/*
* Created on 7 mai 2005
*
* Copyright (c) 2006, PMD for Eclipse Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed in part by support from
* the Defense Advanced Research Project Agency (DARPA)"
* * Neither the name of "PMD for Eclipse Development Team" nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.sourceforge.pmd.eclipse.ui.model;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.StringUtil;
import net.sourceforge.pmd.eclipse.runtime.PMDRuntimeConstants;
import net.sourceforge.pmd.eclipse.runtime.builder.MarkerUtil;
import net.sourceforge.pmd.eclipse.ui.PMDUiConstants;
import net.sourceforge.pmd.eclipse.plugin.PMDPlugin;
import net.sourceforge.pmd.eclipse.ui.nls.StringKeys;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IMethod;
/**
* This class holds information for use with the dataflow view. It contains a
* Java-Method and the corresponding PMD-Method (SimpleNode) and can return
* Dataflow Anomalies for it.
*
* @author SebastianRaffel ( 07.06.2005 ), Philippe Herlin, Sven Jacob
*
*/
public class DataflowMethodRecord {
private final IMethod method;
private final Node node;
/**
* Constructor
*
* @param javaMethod, the Method of the JavaModel
* @param pmdMethod, the corresponding PMD-SimpleNode / ASTMethodDeclaration
*/
public DataflowMethodRecord(IMethod javaMethod, Node pmdMethod) {
if (javaMethod == null) {
throw new IllegalArgumentException("javaMethod cannot be null");
}
if (pmdMethod == null) {
throw new IllegalArgumentException("pmdMethod cannot be null");
}
this.method = javaMethod;
this.node = pmdMethod;
}
/**
* @return the PMD-Method
*/
public Node getPMDMethod() {
return this.node;
}
/**
* @return the Java-Method
*/
public IMethod getJavaMethod() {
return this.method;
}
/**
* @return the Resource (File) that contains this Method
*/
public IResource getResource() {
return this.method.getResource();
}
/**
* Finds Dataflow-Anomalies for a Method
*
* @return a List of Anomalies
*/
public IMarker[] getMarkers() {
// TODO optimize this to avoid creation when no results found
final List<IMarker> markers = new ArrayList<IMarker>();
try {
if (method.getResource().isAccessible()) {
// we can only find Markers for a file
// we use the DFA-Marker-ID set for Dataflow Anomalies
final IMarker[] allMarkers = MarkerUtil.findMarkers(method.getResource(), PMDRuntimeConstants.PMD_DFA_MARKER);
// we only want to get the Markers for this Method,
// so we need to "extract" them from the whole List
for (IMarker marker : allMarkers) {
// the Marker should have valid Information in it
// ... and we don't want it twice, so we check,
// if the Marker already exists
if (markerIsValid(marker) && !markerIsInList(marker, markers)) {
markers.add(marker);
}
}
}
} catch (CoreException ce) {
PMDPlugin.getDefault().logError(StringKeys.ERROR_FIND_MARKER + this.toString(), ce);
}
// return the Arraylist-Markers as an IMarker-Array
final IMarker[] markerArray = new IMarker[markers.size()];
markers.toArray(markerArray);
return markerArray;
}
/**
* Returns a list of Attributes for a Dataflow Marker, (1.) the Error
* Message, (2.) the beginning Line of the Error, (3.) the ending Line and
* (4.) the Variable (Marker Attribute)
*
* @param marker
* @return an Array of Attributes
*/
private Object[] getMarkerAttributes(IMarker marker) {
final List<Object> values = new ArrayList<Object>();
// add Message, default ""
values.add(marker.getAttribute(IMarker.MESSAGE, ""));
// add the Lines, default 0
// the default-Values help preventing an Exception
int line1 = marker.getAttribute(IMarker.LINE_NUMBER, 0);
int line2 = marker.getAttribute(PMDUiConstants.KEY_MARKERATT_LINE2, 0);
// exchange the Lines if begin > end
if (line2 < line1) {
final int temp = line1;
line1 = line2;
line2 = temp;
}
values.add(Integer.valueOf(line1));
values.add(Integer.valueOf(line2));
// add the Variable
values.add(marker.getAttribute(PMDUiConstants.KEY_MARKERATT_VARIABLE, ""));
return values.toArray();
}
/**
* Checks, if a Marker is valid, meaning that it (1.) is set for this Method
* (between Begin and End-Line) and (2.) has a Variable and Message set
*
* @param marker
* @return true if the Marker is valid, false otherwise
*/
private boolean markerIsValid(IMarker marker) {
boolean isValid = false;
// get the Markers attributes
final Object[] values = getMarkerAttributes(marker);
final int line1 = ((Integer) values[1]).intValue();
final int line2 = ((Integer) values[2]).intValue();
// the Marker has to be in this Method
if (line1 >= this.node.getBeginLine() && line2 <= this.node.getEndLine()) {
isValid = true;
for (int k = 0; k < values.length && isValid; k++) {
// if it is a String, it has to be the Variable
// or Message, which shouldn't be empty
if (values[k] instanceof String && StringUtil.isEmpty((String)values[k])) {
isValid = false;
}
// else it is one of the Lines (Line, Line2)
// and they also should not be 0
else if (values[k] instanceof Number && ((Number) values[k]).intValue() == 0) {
isValid = false;
}
}
}
// check the Values
return isValid;
}
/**
* Checks if a Marker is already in a List
*
* @param marker
* @param list
* @return true, is the marker exists in the list, false otherwise
*/
private boolean markerIsInList(IMarker marker, List<IMarker> list) {
boolean inList = false;
if (list != null && !list.isEmpty()) {
// here we can't simply compare Objects, because the Dataflow
// Anomaly Calculation sets different Markers for the same Error
// get the Markers Attributes and compare with all other Markers
final Object[] markerAttr = getMarkerAttributes(marker);
for (int i = 0; i < list.size() && !inList; i++) {
// get the Marker from the List and its Attributes
final Object[] listAttr = getMarkerAttributes(list.get(i));
boolean markersAreEqual = true;
for (int j = 0; j < listAttr.length; j++) {
// compare the String- and Integer-Values
if (markerAttr[j] instanceof String && !((String) markerAttr[j]).equalsIgnoreCase((String) listAttr[j])) {
markersAreEqual = false;
}
else if (markerAttr[j] instanceof Integer && !((Integer) markerAttr[j]).equals(listAttr[j])) {
markersAreEqual = false;
}
}
// markersAreEqual only stays true, when all Checks above fail
// we need to do that to compare _all_ Attributes; if they all
// are equal, the Marker exists, if not we check the next one
if (markersAreEqual) {
inList = true;
}
}
}
return inList;
}
}