All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.engine.commons.findings.DetachedFinding Maven / Gradle / Ivy

/*-------------------------------------------------------------------------+
|                                                                          |
| Copyright 2005-2011 the ConQAT Project                                   |
|                                                                          |
| Licensed under the Apache License, Version 2.0 (the "License");          |
| you may not use this file except in compliance with the License.         |
| You may obtain a copy of the License at                                  |
|                                                                          |
|    http://www.apache.org/licenses/LICENSE-2.0                            |
|                                                                          |
| Unless required by applicable law or agreed to in writing, software      |
| distributed under the License is distributed on an "AS IS" BASIS,        |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and      |
| limitations under the License.                                           |
+-------------------------------------------------------------------------*/
package org.conqat.engine.commons.findings;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;

import javax.annotation.Nullable;

import org.conqat.engine.commons.findings.location.ElementLocation;
import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.assessment.ETrafficLightColor;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableMap;
import org.conqat.lib.commons.js_export.ExportAsType;
import org.conqat.lib.commons.js_export.ExportToTypeScript;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * This class describes orphaned finding that is attached to a node or a
 * findings report. This is useful if e.g. findings have been filtered but
 * certain operations should still be carried out on the findings.
 */
@ExportToTypeScript
public class DetachedFinding implements Serializable {

	/** Serial version UID. */
	private static final long serialVersionUID = 1;

	/** The name of the JSON property name for {@link #location}. */
	protected static final String LOCATION_PROPERTY = "location";

	/** The name of the JSON property name for {@link #groupName}. */
	protected static final String GROUP_NAME_PROPERTY = "groupName";

	/** The name of the JSON property name for {@link #categoryName}. */
	protected static final String CATEGORY_NAME_PROPERTY = "categoryName";

	/** The name of the JSON property name for {@link #message}. */
	protected static final String MESSAGE_PROPERTY = "message";

	/** The name of the JSON property name for {@link #assessment}. */
	private static final String ASSESSMENT_PROPERTY = "assessment";

	/** The location. */
	@JsonProperty(LOCATION_PROPERTY)
	@ExportAsType("ElementLocation|TextRegionLocation|QualifiedNameLocation")
	private ElementLocation location;

	/** The group name. */
	@JsonProperty(GROUP_NAME_PROPERTY)
	private String groupName;

	/** The category name. */
	@JsonProperty(CATEGORY_NAME_PROPERTY)
	private String categoryName;

	/** The message. */
	@JsonProperty(MESSAGE_PROPERTY)
	private String message;

	/** The assessment color of the finding (may be null). */
	@JsonProperty(ASSESSMENT_PROPERTY)
	@Nullable
	private ETrafficLightColor assessment;

	/**
	 * The locations of other findings that are considered siblings. This is, e.g.,
	 * used to find other clone instances in the same clone class. As this is
	 * commonly empty, we keep this attribute null in this case to save space for
	 * serialization. The access methods, however, handle this transparently.
	 */
	@JsonProperty("siblingLocations")
	@Nullable
	private List siblingLocations;

	/**
	 * Caches the sibling locations as strings. Used to avoid adding duplicate
	 * sibling locations. Initialized lazily.
	 */
	private transient Set siblingLocationCache = null;

	/**
	 * Next to the primary location {@link #location}, a finding may optionally have
	 * secondary locations. For instance, an architecture finding may contain the
	 * individual source code locations of the violating identifiers.
	 */
	@JsonProperty("secondaryLocations")
	@Nullable
	private List secondaryLocations;

	/**
	 * Properties for this finding. Each finding can be associated with one or more
	 * properties describing details of the finding (e.g. length of long method,
	 * etc.). These properties are also displayed in the UI and can be used for
	 * sorting findings (in which case the value class must be comparable).
	 */
	@JsonProperty("properties")
	@ExportAsType("Record")
	private final Map properties = new HashMap<>();

	/**
	 * @see #getStatementPath()
	 */
	@JsonProperty("statementPath")
	@Nullable
	private List statementPath = null;

	/** Constructor. */
	public DetachedFinding(String groupName, String categoryName, String message, ElementLocation location) {
		this(groupName, categoryName, message, location, null);
	}

	/**
	 * Constructor. Adds the given finding properties to the finding. The assessment
	 * may be null.
	 */
	public DetachedFinding(String groupName, String categoryName, String message, ElementLocation location,
			ETrafficLightColor assessment, Map findingProperties) {
		this(groupName, categoryName, message, location, assessment);
		properties.putAll(findingProperties);
	}

	/** Constructor. The assessment may be null */
	@JsonCreator
	public DetachedFinding(@JsonProperty(GROUP_NAME_PROPERTY) String groupName,
			@JsonProperty(CATEGORY_NAME_PROPERTY) String categoryName, @JsonProperty(MESSAGE_PROPERTY) String message,
			@JsonProperty(LOCATION_PROPERTY) ElementLocation location,
			@JsonProperty(ASSESSMENT_PROPERTY) ETrafficLightColor assessment) {
		CCSMAssert.isNotNull(location);

		this.groupName = groupName;
		this.categoryName = categoryName;
		this.message = message;
		this.location = location;
		this.assessment = assessment;
	}

	/** Copy constructor. */
	protected DetachedFinding(DetachedFinding other) {
		this(other.groupName, other.categoryName, other.message, other.location, other.assessment);

		if (other.hasSiblings()) {
			this.siblingLocations = new ArrayList<>(other.siblingLocations);
		}

		if (other.secondaryLocations != null) {
			this.secondaryLocations = new ArrayList<>(other.secondaryLocations);
		}

		properties.putAll(other.properties);

		this.statementPath = other.statementPath;
	}

	/** Get group name. */
	public String getGroupName() {
		return groupName;
	}

	/** Sets group name. */
	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}

	/** Get category name. */
	public String getCategoryName() {
		return categoryName;
	}

	/** Sets category name. */
	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}

	/** Get location. */
	public ElementLocation getLocation() {
		return location;
	}

	/** Sets location. */
	public void setLocation(ElementLocation location) {
		CCSMAssert.isNotNull(location);
		this.location = location;
	}

	/** Get location string. */
	public String getLocationString() {
		return location.toLocationString();
	}

	/** Get message. */
	public String getMessage() {
		return message;
	}

	/** @see #message */
	public void setMessage(String message) {
		this.message = message;
	}

	/** Returns whether this findings has siblings. */
	public boolean hasSiblings() {
		return siblingLocations != null && !siblingLocations.isEmpty();
	}

	/** Returns the locations of sibling findings. */
	public List getSiblingLocations() {
		if (siblingLocations == null) {
			return CollectionUtils.emptyList();
		}
		return CollectionUtils.asUnmodifiable(siblingLocations);
	}

	/** Removes all sibling locations matching the given filter. */
	public void removeMatchingSiblingLocations(Predicate filter) {
		siblingLocations.removeIf(filter);
	}

	/**
	 * @see #secondaryLocations
	 */
	public List getSecondaryLocations() {
		if (secondaryLocations == null) {
			return CollectionUtils.emptyList();
		}
		return CollectionUtils.asUnmodifiable(secondaryLocations);
	}

	/**
	 * Adds all previously unknown locations of sibling findings (already known
	 * locations are skipped).
	 */
	public void addSiblingLocation(ElementLocation location) {
		if (siblingLocations == null) {
			siblingLocations = new ArrayList<>();
		}
		if (siblingLocationCache == null) {
			siblingLocationCache = new HashSet<>(
					CollectionUtils.map(siblingLocations, ElementLocation::toLocationString));
			// This way, a finding can never be a sibling of itself
			siblingLocationCache.add(getLocationString());
		}
		String locationString = location.toLocationString();
		if (!siblingLocationCache.contains(locationString)) {
			siblingLocations.add(location);
			siblingLocationCache.add(locationString);
		}
	}

	/**
	 * Adds the given list of locations as sibling findings.
	 *
	 * @param findings
	 *            a collection of {@link DetachedFinding} that will be added as
	 *            siblings.
	 */
	public void addSiblingFindings(Collection findings) {
		findings.forEach(this::addSiblingFinding);
	}

	/** Add the given sibling locations */
	public void addSiblingLocations(Collection siblingLocations) {
		siblingLocations.forEach(this::addSiblingLocation);
	}

	/** Adds the given {@link DetachedFinding} to the list of sibling findings. */
	public void addSiblingFinding(DetachedFinding sibling) {
		addSiblingLocation(sibling.getLocation());
	}

	/** Adds a secondary location to this finding. */
	public void addSecondaryLocation(ElementLocation location) {
		if (secondaryLocations == null) {
			secondaryLocations = new ArrayList<>();
		}
		secondaryLocations.add(location);
	}

	/** Adds the given locations as secondary locations. */
	public void addSecondaryLocations(Collection locations) {
		if (secondaryLocations == null) {
			secondaryLocations = new ArrayList<>();
		}
		secondaryLocations.addAll(locations);
	}

	/** Returns the properties of this finding. */
	public UnmodifiableMap getProperties() {
		return CollectionUtils.asUnmodifiable(properties);
	}

	/** Sets a property for this finding. */
	public void setProperty(String name, Object value) {
		properties.put(name, value);
	}

	/**
	 * Sets the properties of this finding, overwriting any previously existing ones
	 */
	public void setProperties(Map properties) {
		this.properties.clear();
		this.properties.putAll(properties);
	}

	/**
	 * Get the property value for a given key. Returns null if no such property
	 * exists.
	 */
	public Object getProperty(String name) {
		return properties.get(name);
	}

	/** Returns assessment. */
	public ETrafficLightColor getAssessment() {
		return assessment;
	}

	/** Sets the assessment. */
	public void setAssessment(ETrafficLightColor assessment) {
		this.assessment = assessment;
	}

	/**
	 * Returns a string representation that contains the, message, the group and and
	 * location hint.
	 */
	@Override
	public String toString() {
		return getMessage() + " (" + getGroupName() + ") @ " + location.toLocationString();
	}

	/**
	 * Returns the statement path if there is one attached to this finding.
	 *
	 * A statementPath is a path through the program that leads to this finding. The
	 * path may not be a simple chain, but could be a DAG.
	 *
	 * Each entry in the list refers to the indices of its predecessor statements.
	 * Contract for the setup of this structure:
	 * 
    *
  • An element might have more than one predecessor.
  • *
  • Multiple elements can have the same predecessor.
  • *
  • There are no cycles in the path.
  • *
*/ public List getStatementPath() { if (statementPath == null) { return CollectionUtils.emptyList(); } return statementPath; } /** @see #getStatementPath() */ public void setStatementPath(List path) { this.statementPath = path; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy