/* * 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.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import net.sourceforge.pmd.eclipse.plugin.PMDPlugin; import net.sourceforge.pmd.eclipse.runtime.PMDRuntimeConstants; import net.sourceforge.pmd.eclipse.runtime.builder.MarkerUtil; import net.sourceforge.pmd.eclipse.ui.nls.StringKeys; import net.sourceforge.pmd.eclipse.util.IOUtil; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; /** * AbstractPMDRecord for Files * * @author SebastianRaffel ( 16.05.2005 ), Philippe Herlin, Sven Jacob * */ public class FileRecord extends AbstractPMDRecord { private AbstractPMDRecord[] children; private final IResource resource; private final AbstractPMDRecord parent; private int numberOfLOC; private int numberOfMethods; /** * Constructor (not for use with the Model, no PackageRecord is provided here) * * @param javaResource the given File */ public FileRecord(IResource javaResource) { super(); if (javaResource == null) { throw new IllegalArgumentException("javaResource cannot be null"); } this.resource = javaResource; this.parent = null; this.numberOfLOC = 0; this.numberOfMethods = 0; this.children = createChildren(); } /** * Constructor (for use with the Model) * * @param javaResource * @param record */ public FileRecord(IResource javaResource, PackageRecord record) { super(); if (javaResource == null) { throw new IllegalArgumentException("javaResource cannot be null"); } this.resource = javaResource; this.parent = record; this.numberOfLOC = 0; this.numberOfMethods = 0; this.children = createChildren(); } /** * Constructor (for use with the Model) * * @param javaResource * @param record */ public FileRecord(IResource resource, FolderRecord record) { super(); if (resource == null) { throw new IllegalArgumentException("resource cannot be null"); } this.resource = resource; this.parent = record; this.numberOfLOC = 0; this.numberOfMethods = 0; this.children = createChildren(); } public long getTimestamp() { return resource.getLocalTimeStamp(); } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getParent() */ @Override public AbstractPMDRecord getParent() { return parent; } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getChildren() */ @Override public AbstractPMDRecord[] getChildren() { return children; // NOPMD by Sven on 13.11.06 11:57 } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getResource() */ @Override public IResource getResource() { return resource; } /** * Updates all children. * */ public void updateChildren() { children = createChildren(); } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#createChildren() */ @Override protected final AbstractPMDRecord[] createChildren() { AbstractPMDRecord[] children = EMPTY_RECORDS; try { // get all markers final List<IMarker> markers = Arrays.asList(findMarkers()); if (markers.isEmpty()) return EMPTY_RECORDS; final Iterator<IMarker> markerIterator = markers.iterator(); // put all markers in a map with key = rulename final Map<String, MarkerRecord> allMarkerMap = new HashMap<String, MarkerRecord>(); while (markerIterator.hasNext()) { final IMarker marker = markerIterator.next(); MarkerRecord markerRecord = allMarkerMap.get(MarkerUtil.ruleNameFor(marker)); if (markerRecord == null) { String ruleName = MarkerUtil.ruleNameFor(marker); markerRecord = new MarkerRecord(this, // NOPMD by Sven on 13.11.06 11:57 ruleName, MarkerUtil.rulePriorityFor(marker) ); markerRecord.addViolation(marker); allMarkerMap.put(ruleName, markerRecord); } else { markerRecord.addViolation(marker); } } children = allMarkerMap.values().toArray(new MarkerRecord[allMarkerMap.size()]); } catch (CoreException e) { PMDPlugin.getDefault().logError(StringKeys.ERROR_CORE_EXCEPTION + this.toString(), e); } return children; // no children so return an empty array, not null! } /** * Checks the File for PMD-Markers * * @return true if the File has PMD-Markers, false otherwise */ @Override public boolean hasMarkers() { final IMarker[] markers = findMarkers(); return markers != null && markers.length > 0; } /** * Finds PMD-Markers in the File * * @return an Array of markers */ @Override public final IMarker[] findMarkers() { try { // this is the overwritten Function from AbstractPMDRecord // we simply call the IResource-function to find Markers if (resource.isAccessible()) { return MarkerUtil.findMarkers(resource, PMDRuntimeConstants.RULE_MARKER_TYPES); } } catch (CoreException ce) { PMDPlugin.getDefault().logError(StringKeys.ERROR_FIND_MARKER + this.toString(), ce); } return MarkerUtil.EMPTY_MARKERS; } /** * Finds PMD PDFA Markers in the File * * @return an Array of markers */ public IMarker[] findDFAMarkers() { try { // we can only find Markers for a file // we use the DFA-Marker-ID set for Dataflow Anomalies if (resource.isAccessible()) { return MarkerUtil.findMarkers(resource, PMDRuntimeConstants.PMD_DFA_MARKER); } } catch (CoreException ce) { PMDPlugin.getDefault().logError(StringKeys.ERROR_FIND_MARKER + this.toString(), ce); } return MarkerUtil.EMPTY_MARKERS; } /** * Finds Markers, that have a given Attribute with a given Value * * @param attributeName * @param value * @return an Array of markers matching these Attribute and Value */ @Override public IMarker[] findMarkersByAttribute(String attributeName, Object value) { final IMarker[] markers = findMarkers(); final List<IMarker> attributeMarkers = new ArrayList<IMarker>(); try { // we get all Markers and catch the ones that matches our criteria for (IMarker marker : markers) { final Object val = marker.getAttribute(attributeName); // if the value is null, the Attribute doesn't exist if (val != null && val.equals(value)) { attributeMarkers.add(marker); } } } catch (CoreException ce) { PMDPlugin.getDefault().logError(StringKeys.ERROR_FIND_MARKER + this.toString(), ce); } // return an Array of the Markers return attributeMarkers.toArray(new IMarker[attributeMarkers.size()]); } /** * Calculates the Number of Code-Lines this File has. * The Function is adapted from the Eclipse Metrics-Plugin available at: * http://www.sourceforge.net/projects/metrics * */ public void calculateLinesOfCode() { if (resource.isAccessible()) { // the whole while has to be a String for this operation // so we read the File line-wise into a String final String source = resourceToString(resource); numberOfLOC = linesOfCodeIn(source, true); } } // TODO migrate to utility class public static int linesOfCodeIn(final String source, boolean ignoreSingleBrackets) { int loc = 0; int ignoredLines = 0; final int firstCurly = source.indexOf('{'); if (firstCurly != -1) { final String body = source.substring(firstCurly+1, source.length()-1).trim(); final StringTokenizer lines = new StringTokenizer(body, "\n"); while (lines.hasMoreTokens()) { String trimmed = lines.nextToken().trim(); if (trimmed.length() > 0 && trimmed.startsWith("/*")) { while (trimmed.indexOf("*/") == -1) { trimmed = lines.nextToken().trim(); } if (lines.hasMoreTokens()) { // NOPMD by Sven on 13.11.06 12:04 trimmed = lines.nextToken().trim(); } } if (ignoreSingleBrackets) { if ("{".equals(trimmed) || "}".equals(trimmed)) { ignoredLines++; continue; } } if (!trimmed.startsWith("//")) { loc++; } } } // System.out.println("ignored lines: " + ignoredLines); return loc; } /** * Gets the Number of Code-Lines this File has. * * @return the Lines of Code */ @Override public int getLOC() { return numberOfLOC; } /** * Reads a Resource's File and return the Code as String. * * @param resource a resource to read ; the resource must be accessible. * @return a String which is the Files Content */ protected String resourceToString(IResource resource) { final StringBuilder fileContents = new StringBuilder(); BufferedReader bReader = null; try { // we create a FileReader bReader = new BufferedReader(new FileReader(resource.getRawLocation().toFile())); // ... and read the File line by line while (bReader.ready()) { fileContents.append(bReader.readLine()).append('\n'); } } catch (FileNotFoundException fnfe) { PMDPlugin.getDefault().logError( StringKeys.ERROR_FILE_NOT_FOUND + resource.toString() + " in " + this.toString(), fnfe); } catch (IOException ioe) { PMDPlugin.getDefault().logError(StringKeys.ERROR_IO_EXCEPTION + this.toString(), ioe); } finally { IOUtil.closeQuietly(bReader); } return fileContents.toString(); } /** * Calculate the number of methods. */ public void calculateNumberOfMethods() { if (resource.isAccessible()) { // we need to change the Resource into a Java-File final IJavaElement element = JavaCore.create(resource); final List<Object> methods = new ArrayList<Object>(); if (element instanceof ICompilationUnit) { try { // ITypes can be Package Declarations or other Java Stuff too IType[] types = ((ICompilationUnit) element).getTypes(); for (IType type : types) { // only if it is an IType itself, it's a Class // from which we can get its Methods methods.addAll( Arrays.asList(type.getMethods() )); } } catch (JavaModelException jme) { PMDPlugin.getDefault().logError( StringKeys.ERROR_JAVAMODEL_EXCEPTION + toString(), jme); } } if (!methods.isEmpty()) { numberOfMethods = methods.size(); } } } /** * Gets the Number of Methods, this class contains. * * @return the Number of Methods */ @Override public int getNumberOfMethods() { return numberOfMethods; // deactivate this method for now } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#addResource(org.eclipse.core.resources.IResource) */ @Override public AbstractPMDRecord addResource(IResource resource) { return null; } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#removeResource(org.eclipse.core.resources.IResource) */ @Override public AbstractPMDRecord removeResource(IResource resource) { return null; } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getName() */ @Override public String getName() { return resource.getName(); } public String authorName() { return RepositoryUtil.hasRepositoryAccess() ? RepositoryUtil.authorNameFor(resource) : null; } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getResourceType() */ @Override public int getResourceType() { return TYPE_FILE; } /** * @see net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord#getNumberOfViolationsToPriority(int) */ @Override public int getNumberOfViolationsToPriority(int prio, boolean invertMarkerAndFileRecords) { int number = 0; for (AbstractPMDRecord element : children) { number += element.getNumberOfViolationsToPriority(prio, false); } return number; } }