Package de.tobject.findbugs.builder

Source Code of de.tobject.findbugs.builder.WorkItem

/*
* Contributions to FindBugs
* Copyright (C) 2009, Andrei Loskutov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package de.tobject.findbugs.builder;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

import de.tobject.findbugs.FindbugsPlugin;
import de.tobject.findbugs.reporter.MarkerUtil;
import de.tobject.findbugs.util.Util;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.util.Archive;

/**
* An item to work on for FB analysis - this can be entire project or single
* file or a java element without corresponding resource (like external library)
*
* @author Andrei
*/
public class WorkItem {

    private final IJavaElement javaElt;

    private final IResource resource;

    private final IProject project;

    public WorkItem(IJavaElement javaElt) {
        this(null, javaElt, javaElt.getJavaProject().getProject());
    }

    public WorkItem(IResource resource) {
        this(resource, null, resource.getProject());
    }

    private WorkItem(IResource resource, IJavaElement javaElt, IProject project) {
        this.resource = resource;
        this.javaElt = javaElt;
        this.project = project;
        Assert.isLegal(resource != null || javaElt != null);
    }

    public void addFilesToProject(Project fbProject, Map<IPath, IPath> outputLocations) {
        IResource res = getCorespondingResource();
        if (res instanceof IProject) {
            for (IPath outDir : outputLocations.values()) {
                fbProject.addFile(outDir.toOSString());
            }
        } else if (res instanceof IFolder) {
            // assumption: this is a source folder.
            boolean added = addClassesForFolder((IFolder) res, outputLocations, fbProject);
            if (!added) {
                // What if this is a class folder???
                addJavaElementPath(fbProject);
            }
        } else if (res instanceof IFile) {
            // ID: 2734173: allow to analyze classes inside archives
            if (Util.isClassFile(res) || Util.isJavaArchive(res)) {
                fbProject.addFile(res.getLocation().toOSString());
            } else if (Util.isJavaFile(res)) {
                addClassesForFile((IFile) res, outputLocations, fbProject);
            }
        } else {
            addJavaElementPath(fbProject);
        }
    }

    private void addJavaElementPath(Project fbProject) {
        if (javaElt != null) {
            IPath path = getPath();
            if (path != null) {
                fbProject.addFile(path.toOSString());
            }
        }
    }

    public void clearMarkers() throws CoreException {
        IResource res = getMarkerTarget();
        if (res == null) {
            return;
        }
        if (javaElt == null || !(res instanceof IProject)) {
            MarkerUtil.removeMarkers(res);
        } else {
            // this is the case of external class folders/libraries: if we would
            // cleanup ALL project markers, it would also remove markers from
            // ALL
            // source/class files, not only for the selected one.
            IMarker[] allMarkers = MarkerUtil.getAllMarkers(res);
            Set<IMarker> set = MarkerUtil.findMarkerForJavaElement(javaElt, allMarkers, true);
            // TODO can be very slow. May think about batch operation w/o
            // resource notifications
            // P.S. if executing "clean+build", package explorer first doesn't
            // notice
            // any change because the external class file labels are not
            // refreshed after clean,
            // but after build we trigger an explicit view refresh, see
            // FindBugsAction
            for (IMarker marker : set) {
                marker.delete();
            }
        }
    }

    public IProject getProject() {
        return project;
    }

    public IJavaProject getJavaProject() {
        return JavaCore.create(project);
    }

    /**
     * @return false if no classes was added
     */
    private boolean addClassesForFolder(IFolder folder, Map<IPath, IPath> outLocations, Project fbProject) {
        IPath path = folder.getLocation();
        IPath srcRoot = getMatchingSourceRoot(path, outLocations);
        if (srcRoot == null) {
            return false;
        }
        IPath outputRoot = outLocations.get(srcRoot);
        int firstSegments = path.matchingFirstSegments(srcRoot);
        // add relative path to the output path
        IPath out = outputRoot.append(path.removeFirstSegments(firstSegments));
        File directory = out.toFile();
        return fbProject.addFile(directory.getAbsolutePath());
        // TODO child directories too. Should add preference???
    }

    private void addClassesForFile(IFile file, Map<IPath, IPath> outLocations, Project fbProject) {
        IPath path = file.getLocation();
        IPath srcRoot = getMatchingSourceRoot(path, outLocations);
        if (srcRoot == null) {
            return;
        }
        IPath outputRoot = outLocations.get(srcRoot);
        int firstSegments = path.matchingFirstSegments(srcRoot);
        // add relative path to the output path
        IPath out = outputRoot.append(path.removeFirstSegments(firstSegments));
        String fileName = path.removeFileExtension().lastSegment();
        String namePattern = fileName + "\\.class|" + fileName + "\\$.*\\.class";
        namePattern = addSecondaryTypesToPattern(file, fileName, namePattern);
        File directory = out.removeLastSegments(1).toFile();

        // add parent folder and regexp for file names
        Pattern classNamesPattern = Pattern.compile(namePattern);
        ResourceUtils.addFiles(fbProject, directory, classNamesPattern);
    }

    /**
     * Add secondary types patterns (not nested in the type itself but contained
     * in the java file)
     *
     * @param fileName
     *            java file name (not path!) without .java suffix
     * @param classNamePattern
     *            non null pattern for all matching .class file names
     * @return modified classNamePattern, if there are more then one type
     *         defined in the java file
     */
    private String addSecondaryTypesToPattern(IFile file, String fileName, String classNamePattern) {
        ICompilationUnit cu = JavaCore.createCompilationUnitFrom(file);
        if (cu == null) {
            FindbugsPlugin.getDefault().logError(
                    "NULL compilation unit for " + file + ", FB analysis might  be incomplete for included types");
            return classNamePattern;
        }
        try {
            IType[] types = cu.getTypes();
            if (types.length > 1) {
                for (IType type : types) {
                    if (fileName.equals(type.getElementName())) {
                        // "usual" type with the same name: we have it already
                        continue;
                    }
                    classNamePattern = classNamePattern + "|" + type.getElementName() + "\\.class|" + type.getElementName()
                            + "\\$.*\\.class";
                }
            }
        } catch (JavaModelException e) {
            FindbugsPlugin.getDefault().logException(e, "Cannot get types from compilation unit: " + cu);
        }
        return classNamePattern;
    }

    public @CheckForNull
    IResource getCorespondingResource() {
        if (resource != null) {
            return resource;
        }
        try {
            return javaElt.getCorrespondingResource();
        } catch (JavaModelException e) {
            // ignore, just return nothing
        }
        return null;
    }

    public @CheckForNull
    IJavaElement getCorespondingJavaElement() {
        if (javaElt != null) {
            return javaElt;
        }
        return JavaCore.create(resource);
    }

    /**
     * @return the resource which can be used to attach markers found for this
     *         item. This resource must exist, and the return value can not be
     *         null. The return value can be absolutely unrelated to the
     *         {@link #getCorespondingResource()}.
     */
    public @Nonnull
    IResource getMarkerTarget() {
        IResource res = getCorespondingResource();
        if (res != null) {
            return res;
        }
        if (javaElt != null) {
            IResource resource2 = javaElt.getResource();
            if (resource2 != null) {
                return resource2;
            }
        }
        // probably not the best solution, but this should always work
        return project;
    }

    /**
     * @return number of markers which are <b>already</b> reported for given
     *         work item.
     */
    public int getMarkerCount(boolean recursive) {
        return getMarkers(recursive).size();
    }

    /**
     * @return markers which are <b>already</b> reported for given work item
     */
    public Set<IMarker> getMarkers(boolean recursive) {
        IResource res = getCorespondingResource();
        if (res != null) {
            if (res.getType() == IResource.PROJECT || javaElt instanceof IPackageFragmentRoot) {
                // for project, depth_one does not make any sense here
                recursive = true;
            }
            IMarker[] markers = MarkerUtil.getMarkers(res, recursive ? IResource.DEPTH_INFINITE : IResource.DEPTH_ONE);
            return new HashSet<IMarker>(Arrays.asList(markers));
        }
        IResource markerTarget = getMarkerTarget();
        if(!markerTarget.isAccessible()) {
            return Collections.emptySet();
        }
        if (!recursive
                && ((markerTarget.getType() == IResource.PROJECT && (javaElt instanceof IPackageFragmentRoot) || Util
                        .isClassFile(javaElt)) || (Util.isJavaArchive(markerTarget) && Util.isClassFile(javaElt)))) {
            recursive = true;
        }
        IMarker[] markers = MarkerUtil.getMarkers(markerTarget, recursive ? IResource.DEPTH_INFINITE : IResource.DEPTH_ONE);
        Set<IMarker> forJavaElement = MarkerUtil.findMarkerForJavaElement(javaElt, markers, recursive);
        return forJavaElement;
    }

    /**
     * @param srcPath may be null
     * @param outLocations
     *            key is the source root, value is output folder
     * @return source root folder matching (parent of) given path, or null
     */
    private @Nullable IPath getMatchingSourceRoot(@Nullable IPath srcPath, Map<IPath, IPath> outLocations) {
        if(srcPath == null) {
            return null;
        }
        Set<Entry<IPath, IPath>> outEntries = outLocations.entrySet();
        IPath result = null;
        int maxSegments = 0;
        for (Entry<IPath, IPath> entry : outEntries) {
            IPath srcRoot = entry.getKey();
            int firstSegments = srcPath.matchingFirstSegments(srcRoot);
            if (firstSegments > maxSegments && firstSegments == srcRoot.segmentCount()) {
                maxSegments = firstSegments;
                result = srcRoot;
            }
        }
        return result;
    }

    public String getName() {
        return resource != null ? resource.getName() : javaElt.getElementName();
    }

    /**
     *
     * @return full absolute path corresponding to the work item (file or
     *         directory). If the work item is a part of an achive, it's the
     *         path to the archive file. If the work item is a project, it's the
     *         path to the project root. TODO If the work item is an internal
     *         java element (method, inner class etc), results are undefined
     *         yet.
     */
    public @CheckForNull
    IPath getPath() {
        IResource corespondingResource = getCorespondingResource();
        if (corespondingResource != null) {
            return corespondingResource.getLocation();
        }
        if (javaElt != null) {
            return javaElt.getPath();
        }
        return null;
    }

    public boolean isDirectory() {
        IResource corespondingResource = getCorespondingResource();
        if (corespondingResource != null) {
            return corespondingResource.getType() == IResource.FOLDER || corespondingResource.getType() == IResource.PROJECT;
        }
        return false;
    }

    /**
     *
     * @return true if the given element is contained inside archive
     */
    public boolean isFromArchive() {
        IPath path = getPath();
        if (path == null) {
            return false;
        }
        File file = path.toFile();
        if (file.isDirectory()) {
            return false;
        }
        return Archive.isArchiveFileName(file.getName());
    }

    @Override
    public String toString() {
        return getName();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((javaElt == null) ? 0 : javaElt.hashCode());
        result = prime * result + ((resource == null) ? 0 : resource.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof WorkItem)) {
            return false;
        }
        WorkItem other = (WorkItem) obj;
        if (javaElt == null) {
            if (other.javaElt != null) {
                return false;
            }
        } else if (!javaElt.equals(other.javaElt)) {
            return false;
        }
        if (resource == null) {
            if (other.resource != null) {
                return false;
            }
        } else if (!resource.equals(other.resource)) {
            return false;
        }
        return true;
    }

}
TOP

Related Classes of de.tobject.findbugs.builder.WorkItem

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.