Package hudson.plugins.analysis.util.model

Source Code of hudson.plugins.analysis.util.model.AbstractAnnotation

package hudson.plugins.analysis.util.model;

import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;

import com.google.common.collect.ImmutableList;

import hudson.model.AbstractBuild;
import hudson.model.Item;
import hudson.plugins.analysis.Messages;
import hudson.plugins.analysis.core.AbstractAnnotationParser;
import hudson.plugins.analysis.util.PackageDetectors;
import hudson.plugins.analysis.util.TreeString;
import hudson.plugins.analysis.util.TreeStringBuilder;

/**
*  A base class for annotations.
*
* @author Ulli Hafner
*/
@ExportedBean
@SuppressWarnings("PMD.CyclomaticComplexity")
public abstract class AbstractAnnotation implements FileAnnotation, Serializable { // NOPMD
    private static final String DEFAULT_PACKAGE = "Default Package";
    /** UNIX path separator. */
    private static final String SLASH = "/";
    /** Temporary directory holding the workspace files. */
    public static final String WORKSPACE_FILES = "workspace-files";
    /** Unique identifier of this class. */
    private static final long serialVersionUID = -1092014926477547148L;
    /** Current key of this annotation. */
    private static long currentKey;

    /** The message of this annotation. */
    private /*almost final*/ TreeString message;
    /** The priority of this annotation. */
    private Priority priority;
    /** Unique key of this annotation. */
    private final long key;
    /**
     * The ordered list of line ranges that show the origin of the annotation in
     * the associated file. To save memory consumption, this can be
     * {@link ImmutableList}, in which case updates requires a new copy.
     */
    private final LineRangeList lineRanges;
    /** Primary line number of this warning, i.e., the start line of the first line range. */
    private final int primaryLineNumber;
    /** The filename of the class that contains this annotation. */
    private TreeString fileName;
    /** The name of the maven or ant module that contains this annotation. */
    private TreeString moduleName;
    /** The name of the package (or name space) that contains this annotation. */
    private TreeString packageName;
    /** Bug category. */
    private /*almost final*/ String category;
    /** Bug type. */
    private /*almost final*/ String type;
    /**
     * Context hash code of this annotation. This hash code is used to decide if
     * two annotations are equal even if the equals method returns <code>false</code>.
     */
    private long contextHashCode;
    /** The origin of this warning. */
    private String origin;
    /** Relative path of this duplication. @since 1.10 */
    private TreeString pathName;
    /** Column start of primary line range of warning. @since 1.38 */
    private int primaryColumnStart;
    /** Column end of primary line range of warning. @since 1.38 */
    private int primaryColumnEnd;

    /**
     * Creates a new instance of <code>AbstractAnnotation</code>.
     *
     * @param message
     *            the message of the warning
     * @param start
     *            the first line of the line range
     * @param end
     *            the last line of the line range
     * @param category
     *            the category of the annotation
     * @param type
     *            the type of the annotation
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("ST")
    public AbstractAnnotation(final String message, final int start, final int end, final String category, final String type) {
        this.message = TreeString.of(StringUtils.strip(message));
        this.category = StringUtils.defaultString(category);
        this.type = StringUtils.defaultString(type);

        key = currentKey++;

        lineRanges = new LineRangeList();
        lineRanges.add(new LineRange(start, end));
        primaryLineNumber = start;

        contextHashCode = currentKey;
    }

    /**
     * Creates a new instance of <code>AbstractAnnotation</code>.
     *
     * @param priority
     *            the priority
     * @param message
     *            the message of the warning
     * @param start
     *            the first line of the line range
     * @param end
     *            the last line of the line range
     * @param category
     *            the category of the annotation
     * @param type
     *            the type of the annotation
     */
    public AbstractAnnotation(final Priority priority, final String message, final int start, final int end,
            final String category, final String type) {
        this(message, start, end, category, type);
        this.priority = priority;
    }

    /**
     * Copy constructor: Creates a new instance of {@link AbstractAnnotation}.
     *
     * @param copy
     *            the annotation to copy the values from
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("ST")
    public AbstractAnnotation(final FileAnnotation copy) {
        key = currentKey++;

        message = TreeString.of(copy.getMessage());
        priority = copy.getPriority();
        primaryLineNumber = copy.getPrimaryLineNumber();
        lineRanges = new LineRangeList(copy.getLineRanges());

        contextHashCode = copy.getContextHashCode();

        fileName = TreeString.of(copy.getFileName());
        category = copy.getCategory();
        type = copy.getType();
        moduleName = TreeString.of(copy.getModuleName());
        packageName = TreeString.of(copy.getPackageName());
    }

    /**
     * Called after XStream de-serialization to improve the memory usage.
     * Ideally we'd like this to be protected, so that the subtype can call this
     * method, but some plugins that depends on this (such as findbugs) already
     * define "private Object readResolve()", so defining it as protected will
     * break those subtypes. So instead, we expose "superReadResolve" as the
     * protected entry point for this method.
     *
     * @return this
     */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("SE")
    private Object readResolve() {
        if (origin != null) {
            origin = origin.intern();
        }
        if (category != null) {
            category = category.intern();
        }
        if (type != null) {
            type = type.intern();
        }
        return this;
    }

    /**
     * Simply calls {@link #readResolve()}.
     */
    protected void superReadResolve() {
        readResolve();
    }

    /**
     * {@link AbstractAnnotationParser} can call this method to let
     * {@link AbstractAnnotation}s to reduce their memory footprint by sharing
     * what they can share with other {@link AbstractAnnotation}s.
     *
     * @param builder
     *            caches previously used strings
     * @since 1.43
     */
    public void intern(final TreeStringBuilder builder) {
        lineRanges.trim();
        message = builder.intern(message);
        fileName = builder.intern(fileName);
        moduleName = builder.intern(moduleName);
        packageName = builder.intern(packageName);

        readResolve(); // String.intern some of the data fields
    }


    /**
     * Let {@link FileAnnotation}s share some of their internal data structure
     * to reduce memory footprint.
     *
     * @param annotations
     *            the annotations to compress
     * @return The same object as passed in the 'annotations' parameter to let
     *         this function used as a filter.
     */
    public static Collection<FileAnnotation> intern(final Collection<FileAnnotation> annotations) {
        TreeStringBuilder stringPool = new TreeStringBuilder();
        for (FileAnnotation annotation : annotations) {
            if (annotation instanceof AbstractAnnotation) {
                AbstractAnnotation aa = (AbstractAnnotation) annotation;
                aa.intern(stringPool);
            }
        }
        stringPool.dedup();
        return annotations;
    }

    /**
     * Sets the column position of this warning.
     *
     * @param column
     *            the column of this warning
     */
    public void setColumnPosition(final int column) {
        setColumnPosition(column, column);
    }

    /**
     * Sets the column position of this warning.
     *
     * @param columnStart
     *            starting column
     * @param columnEnd
     *            ending column
     */
    public void setColumnPosition(final int columnStart, final int columnEnd) {
        primaryColumnStart = columnStart;
        primaryColumnEnd = columnEnd;
    }

    @Override
    public String getLinkName() {
        if (hasPackageName()) {
            return getPackageName() + "." + FilenameUtils.getBaseName(getFileName());
        }
        else {
            if (pathName == null || pathName.isBlank()) {
                return getFileName();
            }
            else {
                return pathName + SLASH + getShortFileName();
            }
        }
    }

    @Override
    public boolean hasPackageName() {
        String actualPackageName = StringUtils.trim(TreeString.toString(packageName));

        return StringUtils.isNotBlank(actualPackageName) && !StringUtils.equals(actualPackageName, PackageDetectors.UNDEFINED_PACKAGE);
    }

    /**
     * Sets the pathname for this warning.
     *
     * @param workspacePath
     *            the workspace path
     */
    @Override
    public void setPathName(final String workspacePath) {
        String normalized = workspacePath.replace('\\', '/');

        String s = StringUtils.removeStart(getFileName(), normalized);
        s = StringUtils.remove(s, FilenameUtils.getName(getFileName()));
        s = StringUtils.removeStart(s, SLASH);
        s = StringUtils.removeEnd(s, SLASH);
        pathName = TreeString.of(s);
    }

    @Override
    public String getPathName() {
        return TreeString.toString(pathName);
    }

    @Override
    public String getOrigin() {
        return StringUtils.defaultString(origin);
    }

    @Override
    public int getColumnEnd() {
        return primaryColumnEnd;
    }

    @Override
    public int getColumnStart() {
        return primaryColumnStart;
    }

    /**
     * Sets the origin of this annotation to the specified value.
     *
     * @param origin the value to set
     */
    public void setOrigin(final String origin) {
        this.origin = origin;
    }

    /**
     * Sets the priority to the specified value.
     *
     * @param priority the value to set
     */
    public void setPriority(final Priority priority) {
        this.priority = priority;
    }

    @Override
    @Exported
    public String getMessage() {
        return TreeString.toString(message);
    }

    @Override
    @Exported
    public Priority getPriority() {
        return priority;
    }

    @Override
    @Exported
    public final long getKey() {
        return key;
    }

    @Override
    @Exported
    public final String getFileName() {
        return TreeString.toString(fileName);
    }

    @Override
    public String getTempName(final AbstractBuild<?, ?> owner) {
        if (fileName != null) {
            return owner.getRootDir().getAbsolutePath()
                    + SLASH + WORKSPACE_FILES
                    + SLASH + Integer.toHexString(fileName.hashCode()) + ".tmp";
        }
        return StringUtils.EMPTY;
    }

    @Override
    public String getCategory() {
        return category;
    }

    @Override
    public String getType() {
        return type;
    }

    /**
     * Sets the file name to the specified value.
     *
     * @param fileName the value to set
     */
    @Override
    public final void setFileName(final String fileName) {
        this.fileName = TreeString.of(StringUtils.strip(fileName).replace('\\', '/'));
    }

    @Override
    public final String getModuleName() {
        return StringUtils.defaultIfEmpty(TreeString.toString(moduleName), "Default Module");
    }

    /**
     * Sets the module name to the specified value.
     *
     * @param moduleName the value to set
     */
    @Override
    public final void setModuleName(final String moduleName) {
        this.moduleName = TreeString.of(moduleName);
    }

    @Override
    public final String getPackageName() {
        return StringUtils.defaultIfEmpty(TreeString.toString(packageName), DEFAULT_PACKAGE);
    }

    /**
     * Sets the package name to the specified value.
     *
     * @param packageName the value to set
     */
    public final void setPackageName(final String packageName) {
        this.packageName = TreeString.of(packageName);
    }

    @Override
    public final Collection<LineRange> getLineRanges() {
        return Collections.unmodifiableCollection(lineRanges);
    }

    @Override
    @Exported
    public final int getPrimaryLineNumber() {
        return primaryLineNumber;
    }

    /**
     * Returns the line number that should be shown on top of the source code view.
     *
     * @return the line number
     */
    public final int getLinkLineNumber() {
        return Math.max(1, primaryLineNumber - 10);
    }

    /**
     * Adds another line range to this annotation.
     *
     * @param lineRange
     *            the line range to add
     */
    public void addLineRange(final LineRange lineRange) {
        if (!lineRanges.contains(lineRange)) {
            lineRanges.add(lineRange);
        }
    }

    @Override
    public long getContextHashCode() {
        return contextHashCode;
    }

    @Override
    public void setContextHashCode(final long contextHashCode) {
        this.contextHashCode = contextHashCode;
    }

    // CHECKSTYLE:OFF

    @Override
    @SuppressWarnings("PMD")
    public int hashCode() {
        int prime = 31;
        int result = 1;
        result = prime * result + ((category == null) ? 0 : category.hashCode());
        result = prime * result + ((fileName == null) ? 0 : fileName.hashCode());
        result = prime * result + ((lineRanges == null) ? 0 : lineRanges.hashCode());
        result = prime * result + ((message == null) ? 0 : message.hashCode());
        result = prime * result + ((moduleName == null) ? 0 : moduleName.hashCode());
        result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
        result = prime * result + primaryLineNumber;
        result = prime * result + primaryColumnStart;
        result = prime * result + primaryColumnEnd;
        result = prime * result + ((priority == null) ? 0 : priority.hashCode());
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        return result;
    }

    @Override
    @SuppressWarnings("PMD")
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        AbstractAnnotation other = (AbstractAnnotation)obj;
        if (category == null) {
            if (other.category != null) {
                return false;
            }
        }
        else if (!category.equals(other.category)) {
            return false;
        }
        if (fileName == null) {
            if (other.fileName != null) {
                return false;
            }
        }
        else if (!fileName.toString().equals(other.fileName.toString())) {
            return false;
        }
        if (lineRanges == null) {
            if (other.lineRanges != null) {
                return false;
            }
        }
        else if (!lineRanges.equals(other.lineRanges)) {
            return false;
        }
        if (message == null) {
            if (other.message != null) {
                return false;
            }
        }
        else if (!message.toString().equals(other.message.toString())) {
            return false;
        }
        if (!getModuleName().equals(other.getModuleName())) {
            return false;
        }
        if (!getPackageName().equals(other.getPackageName())) {
            return false;
        }
        if (primaryLineNumber != other.primaryLineNumber) {
            return false;
        }
        if (primaryColumnStart != other.primaryColumnStart) {
            return false;
        }
        if (primaryColumnEnd != other.primaryColumnEnd) {
            return false;
        }
        if (priority == null) {
            if (other.priority != null) {
                return false;
            }
        }
        else if (!priority.equals(other.priority)) {
            return false;
        }
        if (type == null) {
            if (other.type != null) {
                return false;
            }
        }
        else if (!type.equals(other.type)) {
            return false;
        }
        return true;
    }

    /**
     * Gets the associated file name of this bug (without path).
     *
     * @return the short file name
     */
    @Override
    public String getShortFileName() {
        if (isInConsoleLog()) {
            return Messages.ConsoleLog_Name();
        }
        return FilenameUtils.getName(TreeString.toString(fileName));
    }

    /**
     * Checks if the file exists and the user is authorized to see the contents of the file.
     *
     * @return <code>true</code>, if successful
     */
    @Override
    public final boolean canDisplayFile(final AbstractBuild<?, ?> owner) {
        if (owner.hasPermission(Item.WORKSPACE)) {
            return isInConsoleLog() || new File(getFileName()).exists() || new File(getTempName(owner)).exists();
        }
        return false;
    }

    @Override
    public int compareTo(final FileAnnotation other) {
        int result;

        result = getFileName().compareTo(other.getFileName());
        if (result != 0) {
            return result;
        }
        result = getPrimaryLineNumber() - other.getPrimaryLineNumber();
        if (result != 0) {
            return result;
        }
        result = getColumnStart() - other.getColumnStart();
        if (result != 0) {
            return result;
        }

        return hashCode() - other.hashCode(); // fallback
    }

    @Override
    public String toString() {
        return String.format("%s(%s):%s,%s,%s:%s", getFileName(), primaryLineNumber, priority, getCategory(), getType(), getMessage());
    }

    @Override
    public boolean isInConsoleLog() {
        return fileName == null || StringUtils.isBlank(fileName.toString());
    }
}
TOP

Related Classes of hudson.plugins.analysis.util.model.AbstractAnnotation

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.