forked from phoedos/pmd
Compare commits
117 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
80148e6b13 | ||
|
5a07ad8caa | ||
|
957afd6625 | ||
|
14e1f8bb81 | ||
|
973fd1c4d4 | ||
|
678d3e8a1b | ||
|
33413371f6 | ||
|
c9da879840 | ||
|
030795ea95 | ||
|
1dc6d7e698 | ||
|
5c92fdd31a | ||
|
9a81bcf7dd | ||
|
fb426227e4 | ||
|
acf8db926d | ||
|
91f2137679 | ||
|
cc095674b0 | ||
|
0859285b8b | ||
|
d0c103ab62 | ||
|
e76947b297 | ||
|
a417f0003f | ||
|
dc64e2df77 | ||
|
829a939421 | ||
|
eb57fdc882 | ||
|
da1ee1fe72 | ||
|
ee39aa5f3c | ||
|
46573bb566 | ||
|
da08b0757e | ||
|
1c0badd429 | ||
|
e0a9e49560 | ||
|
dba46f2ebd | ||
|
5fb6daea58 | ||
|
2d655f4684 | ||
|
b77de63950 | ||
|
d347e38622 | ||
|
7494011999 | ||
|
3901ad7ddb | ||
|
2e85f1f935 | ||
|
a5efd66b73 | ||
|
a96ccf9741 | ||
|
f60128b142 | ||
|
89ebc32141 | ||
|
b501de3fba | ||
|
79fb7e359d | ||
|
6aa4897e21 | ||
|
ee05fa73cd | ||
|
0170a40dec | ||
|
4d7b4bb1bc | ||
|
a6c1c1f57c | ||
|
9f8246caac | ||
|
f51e41b05c | ||
|
ad66153a2d | ||
|
c8c6aa16dd | ||
|
b998e64819 | ||
|
62fc70e816 | ||
|
34b868ad14 | ||
|
26a862a51b | ||
|
4569842965 | ||
|
fddccb5030 | ||
|
00e27b4a37 | ||
|
b6c6899741 | ||
|
43ecf1e110 | ||
|
5f45a28621 | ||
|
41c44e5d8d | ||
|
040d465137 | ||
|
4e5ca16990 | ||
|
30af4f6381 | ||
|
cece698b04 | ||
|
13ef401a63 | ||
|
589ec3c6da | ||
|
c00ca9e44c | ||
|
31f6b9af1a | ||
|
8eac048c03 | ||
|
3b14b3f5cb | ||
|
adcbdc204f | ||
|
a1b1e3f31c | ||
|
3e8c42384b | ||
|
a00532595a | ||
|
3feda38cc3 | ||
|
3412d55fc6 | ||
|
436b7dcb50 | ||
|
d25dadc4b4 | ||
|
1c051674a0 | ||
|
3daca86045 | ||
|
d95d960de3 | ||
|
ef7478c350 | ||
|
c080fdda08 | ||
|
e603844a53 | ||
|
52b8be6601 | ||
|
7cbb385dbb | ||
|
61c0b4ea7f | ||
|
26fa85a9fa | ||
|
98b903897e | ||
|
55b5aa7f27 | ||
|
ffac9894c9 | ||
|
d557ed9c01 | ||
|
e0b7f6f17c | ||
|
cb9861dd1f | ||
|
7ee96a6b52 | ||
|
ac8e3314a3 | ||
|
7989ae1863 | ||
|
e3708b5030 | ||
|
85e493d55f | ||
|
6abbea51dd | ||
|
9162917346 | ||
|
637006674f | ||
|
63c1a8fe47 | ||
|
063c4228e3 | ||
|
4b0063dfc1 | ||
|
13e36974a5 | ||
|
33c7af965c | ||
|
a66bbf2193 | ||
|
38af8f2433 | ||
|
a6bcfe2b65 | ||
|
6474fde42d | ||
|
8dcde0edb5 | ||
|
70347aedd5 | ||
|
eaf1385595 |
2
pmd/.gitignore
vendored
2
pmd/.gitignore
vendored
@ -3,5 +3,7 @@ bin/
|
||||
.project
|
||||
.classpath
|
||||
.checkstyle
|
||||
.pmd
|
||||
.ruleset
|
||||
.settings/
|
||||
*.patch
|
||||
|
@ -1,4 +1,98 @@
|
||||
???? ??, 2012 - 5.1.0:
|
||||
????? - 5.0.6:
|
||||
|
||||
Fixed bug 1144: CPD encoding argument has no effect
|
||||
|
||||
|
||||
August 11, 2013 - 5.0.5:
|
||||
|
||||
Fixed bug 991: AvoidSynchronizedAtMethodLevel for static methods
|
||||
Fixed bug 1084: NPE at UselessStringValueOfRule.java:36
|
||||
Fixed bug 1091: file extension for fortran seems to be wrong in cpdgui tools
|
||||
Fixed bug 1092: Wrong Attribute "excludemarker" in Ant Task Documentation
|
||||
Fixed bug 1095: AvoidFinalLocalVariable false positive
|
||||
Fixed bug 1099: UseArraysAsList false positives
|
||||
Fixed bug 1102: False positive: shift operator parenthesis
|
||||
Fixed bug 1104: IdempotentOperation false positive
|
||||
Fixed bug 1107: PMD 5.0.4 couldn't parse call of parent outer java class method from inner class
|
||||
Fixed bug 1069: Eclipse plugin does not accept project-local config
|
||||
Fixed bug 1111: False positive: Useless parentheses
|
||||
Fixed bug 1114: CPD - Tokenizer not initialized with requested properties
|
||||
Fixed bug 1118: ClassCastException in pmd.lang.ecmascript.ast.ASTElementGet
|
||||
|
||||
|
||||
May 1, 2013 - 5.0.4:
|
||||
|
||||
Fixed bug 254: False+ : UnusedImport with Javadoc @throws
|
||||
Fixed bug 794: False positive on PreserveStackTrace with anonymous inner
|
||||
Fixed bug 1063: False+: ArrayIsStoredDirectly
|
||||
Fixed bug 1080: net.sourceforge.pmd.cpd.CPDTest test failing
|
||||
Fixed bug 1081: Regression: CPD skipping all files when using relative paths
|
||||
Fixed bug 1082: CPD performance issue on larger projects
|
||||
Fixed bug 1085: NullPointerException by at net.sourceforge.pmd.lang.java.rule.design.GodClassRule.visit(GodClassRule.java:313)
|
||||
Fixed bug 1086: Unsupported Element and Attribute in Ant Task Example
|
||||
Fixed bug 1087: PreserveStackTrace (still) ignores initCause()
|
||||
Fixed bug 1089: When changing priority in a custom ruleset, violations reported twice
|
||||
|
||||
|
||||
April 5, 2013 - 5.0.3:
|
||||
|
||||
Fixed bug 938: False positive on LooseCoupling for overriding methods
|
||||
Fixed bug 940: False positive on UnsynchronizedStaticDateFormatter
|
||||
Fixed bug 942: CheckResultSet False Positive and Negative
|
||||
Fixed bug 943: PreserveStackTrace false positive if a StringBuffer exists
|
||||
Fixed bug 945: PMD generates RuleSets it cannot read.
|
||||
Fixed bug 958: Intermittent NullPointerException while loading XPath node attributes
|
||||
Fixed bug 968: Issues with JUnit4 @Test annotation with expected exception (Thanks to Yiannis Paschalidis)
|
||||
Fixed bug 975: false positive in ClassCastExceptionWithToArray
|
||||
Fixed bug 976: UselessStringValueOf wrong when appending character arrays
|
||||
Fixed bug 977: MisplacedNullCheck makes false positives
|
||||
Fixed bug 984: Cyclomatic complexity should treat constructors like methods
|
||||
Fixed bug 985: Suppressed methods shouldn't affect avg CyclomaticComplexity
|
||||
Fixed bug 992: Class java.beans.Statement triggered in CloseResource rule
|
||||
Fixed bug 997: Rule NonThreadSafeSingleton gives analysis problem
|
||||
Fixed bug 999: Law of Demeter: False positives and negatives
|
||||
Fixed bug 1002: False +: FinalFieldCouldBeStatic on inner class
|
||||
Fixed bug 1005: False + for ConstructorCallsOverridableMethod - overloaded methods
|
||||
Fixed bug 1027: PMD Ant: java.lang.ClassCastException
|
||||
Fixed bug 1032: ImmutableField Rule: Private field in inner class gives false positive
|
||||
Fixed bug 1064: Exception running PrematureDeclaration
|
||||
Fixed bug 1068: CPD fails on broken symbolic links
|
||||
Fixed bug 1073: Hard coded violation messages CommentSize
|
||||
Fixed bug 1074: rule priority doesn't work on group definitions
|
||||
Fixed bug 1076: Report.treeIterator() does not return all violations
|
||||
Fixed bug 1077: Missing JavaDocs for Xref-Test Files
|
||||
Fixed bug 1078: Package statement introduces false positive UnnecessaryFullyQualifiedName violation
|
||||
Merged pull request #14: fix Nullpointer Exception when using -l jsp
|
||||
|
||||
|
||||
|
||||
February 3, 2013 - 5.0.2:
|
||||
|
||||
Fixed bug 878: False positive: UnusedFormalParameter for abstract methods
|
||||
Fixed bug 913: SignatureDeclareThrowsException is raised twice
|
||||
Fixed bug 947: CloseResource rule fails if field is marked with annotation
|
||||
Fixed bug 1004: targetjdk isn't attribute of PMD task
|
||||
Fixed bug 1007: Parse Exception with annotation
|
||||
Fixed bug 1011: CloseResource Rule ignores Constructors
|
||||
Fixed bug 1012: False positive: Useless parentheses.
|
||||
Fixed bug 1020: Parsing Error
|
||||
Fixed bug 1026: PMD doesn't handle 'value =' in SuppressWarnings annotation
|
||||
Fixed bug 1028: False-positive: Compare objects with equals for Enums
|
||||
Fixed bug 1030: CPD Java.lang.IndexOutOfBoundsException: Index:
|
||||
Fixed bug 1037: Facing a showstopper issue in PMD Report Class (report listeners)
|
||||
Fixed bug 1039: pmd-nicerhtml.xsl is packaged in wrong location
|
||||
Fixed bug 1043: node.getEndLine() always returns 0 (ECMAscript)
|
||||
Fixed bug 1044: Unknown option: -excludemarker
|
||||
Fixed bug 1046: ant task CPDTask doesn't accept ecmascript
|
||||
Fixed bug 1047: False Positive in 'for' loops for LocalVariableCouldBeFinal in 5.0.1
|
||||
Fixed bug 1048: CommentContent Rule, String Index out of range Exception
|
||||
Fixed bug 1049: Errors in "How to write a rule"
|
||||
Fixed bug 1055: Please add a colon in the ant output after line,column for Oracle JDeveloper IDE usage
|
||||
Fixed bug 1056: "Error while processing" while running on xml file with DOCTYPE reference
|
||||
Fixed bug 1060: GodClassRule >>> wrong method
|
||||
|
||||
|
||||
November 28, 2012 - 5.0.1:
|
||||
|
||||
Fixed bug 820: False+ AvoidReassigningParameters
|
||||
Fixed bug 1008: pmd-5.0.0: ImmutableField false positive on self-inc/dec
|
||||
@ -471,7 +565,7 @@ The RuleSet XML Schema namespace is now: http://pmd.sourceforge.net/ruleset/2.0.
|
||||
The RuleSet XML Schema is located in the source at: etc/ruleset_2_0_0.xsd
|
||||
The RuleSet DTD is located in the source at: etc/ruleset_2_0_0.dtd
|
||||
Improved include/exclude pattern matching performance for ends-with type patterns.
|
||||
Modify (and hopefully fixed) CPD algorithm thanks to a patch from Juan Jesús García de Soria.
|
||||
Modify (and hopefully fixed) CPD algorithm thanks to a patch from Juan Jesús GarcÃa de Soria.
|
||||
Fixed character reference in xml report - thanks to Seko
|
||||
Enhanced SuspiciousEqualsMethodName rule - thanks to Andy Throgmorton
|
||||
Add a script to launch CPDGUI on Unix system - thanks to Tom Wheeler
|
||||
@ -535,7 +629,7 @@ Fixed bug 2835074 - False -: DoubleCheckedLocking with reversed null check
|
||||
Fixed bug 2826119 - False +: DoubleCheckedLocking warning with volatile field
|
||||
Fixed bug 2904832 - Type resolution not working for ASTType when using an inner class
|
||||
|
||||
Modify (and hopefully fixed) CPD algorithm thanks to a patch from Juan Jesús García de Soria.
|
||||
Modify (and hopefully fixed) CPD algorithm thanks to a patch from Juan Jesús GarcÃa de Soria.
|
||||
Correct -benchmark reporting of Rule visits via the RuleChain
|
||||
Fix issue with Type Resolution incorrectly handling of Classes with same name as a java.lang Class.
|
||||
The JSP/JSF parser can now parse Unicode input.
|
||||
|
@ -1,4 +1,12 @@
|
||||
/**
|
||||
* Fix ForStatement to allow Annotations within the initializer.
|
||||
*
|
||||
* Andreas Dangel 01/2013
|
||||
* ===================================================================
|
||||
* Fix wrong consumption of modifiers (e.g. "final") in a for-each loop.
|
||||
*
|
||||
* Andreas Dangel 12/2012
|
||||
* ===================================================================
|
||||
* Enhance grammar to use LocalVariableDeclaration in a for-each loop.
|
||||
* This enhances the symbol table to recognize variables declared in such
|
||||
* a for-each loop.
|
||||
@ -1711,6 +1719,7 @@ void PrimaryPrefix() :
|
||||
void PrimarySuffix() :
|
||||
{Token t;}
|
||||
{ LOOKAHEAD(2) "." "this"
|
||||
| LOOKAHEAD(2) "." "super"
|
||||
| LOOKAHEAD(2) "." AllocationExpression()
|
||||
| LOOKAHEAD(3) MemberSelector()
|
||||
| "[" Expression() "]" {jjtThis.setIsArrayDereference();}
|
||||
@ -1914,9 +1923,9 @@ void ForStatement() :
|
||||
{
|
||||
"for" "("
|
||||
(
|
||||
LOOKAHEAD(Modifiers() Type() <IDENTIFIER> ":")
|
||||
LOOKAHEAD(LocalVariableDeclaration() ":")
|
||||
{checkForBadJDK15ForLoopSyntaxArgumentsUsage();}
|
||||
Modifiers() LocalVariableDeclaration() ":" Expression()
|
||||
LocalVariableDeclaration() ":" Expression()
|
||||
|
|
||||
[ ForInit() ] ";"
|
||||
[ Expression() ] ";"
|
||||
@ -1928,7 +1937,7 @@ void ForStatement() :
|
||||
void ForInit() :
|
||||
{}
|
||||
{
|
||||
LOOKAHEAD( [ "final" ] Type() <IDENTIFIER> )
|
||||
LOOKAHEAD( LocalVariableDeclaration() )
|
||||
LocalVariableDeclaration()
|
||||
|
|
||||
StatementExpressionList()
|
||||
|
70
pmd/pom.xml
70
pmd/pom.xml
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd</artifactId>
|
||||
<name>PMD</name>
|
||||
<version>5.1.0-SNAPSHOT</version>
|
||||
<version>5.0.6-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
@ -233,6 +232,7 @@
|
||||
<connection>scm:git:git://github.com/pmd/pmd.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/pmd/pmd.git</developerConnection>
|
||||
<url>https://github.com/pmd/pmd</url>
|
||||
<tag>pmd/5.0.x</tag>
|
||||
</scm>
|
||||
<organization>
|
||||
<name>InfoEther</name>
|
||||
@ -246,7 +246,7 @@
|
||||
If the xdocs files stay in src/site/xdoc/, mvn tries to copy over the generated
|
||||
one, and complains... -->
|
||||
<src.xdocs.dir>src/site/xdocs</src.xdocs.dir>
|
||||
<pmd.website.baseurl>http://pmd.sourceforge.net/snapshot</pmd.website.baseurl>
|
||||
<pmd.website.baseurl>http://pmd.sourceforge.net/pmd-${project.version}</pmd.website.baseurl>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@ -333,10 +333,8 @@
|
||||
<configuration>
|
||||
<target>
|
||||
<ant antfile="src/main/ant/alljavacc.xml">
|
||||
<property name="target"
|
||||
value="${project.build.directory}/generated-sources/javacc" />
|
||||
<property name="javacc.jar"
|
||||
value="${settings.localRepository}/net/java/dev/javacc/javacc/${javacc.version}/javacc-${javacc.version}.jar" />
|
||||
<property name="target" value="${project.build.directory}/generated-sources/javacc" />
|
||||
<property name="javacc.jar" value="${settings.localRepository}/net/java/dev/javacc/javacc/${javacc.version}/javacc-${javacc.version}.jar" />
|
||||
</ant>
|
||||
</target>
|
||||
</configuration>
|
||||
@ -350,15 +348,11 @@
|
||||
<configuration>
|
||||
<target>
|
||||
<echo>PMD specific tasks: generating xdocs from rulesets</echo>
|
||||
<mkdir
|
||||
dir="${project.build.directory}/generated-xdocs/" />
|
||||
<copy
|
||||
toDir="${project.build.directory}/generated-xdocs/"
|
||||
overwrite="true" verbose="true">
|
||||
<mkdir dir="${project.build.directory}/generated-xdocs/" />
|
||||
<copy toDir="${project.build.directory}/generated-xdocs/" overwrite="true" verbose="true">
|
||||
<fileset dir="${src.xdocs.dir}" />
|
||||
<filterset>
|
||||
<filter token="VERSION"
|
||||
value="${project.version}" />
|
||||
<filter token="VERSION" value="${project.version}" />
|
||||
</filterset>
|
||||
</copy>
|
||||
</target>
|
||||
@ -374,25 +368,18 @@
|
||||
<target>
|
||||
<echo>PMD site specific tasks</echo>
|
||||
<echo>1. Copying missing images to site directory.</echo>
|
||||
<copy
|
||||
todir="${project.build.directory}/site/images/">
|
||||
<fileset dir="${src.xdocs.dir}/images/"
|
||||
includes="**/*.*" />
|
||||
<copy todir="${project.build.directory}/site/images/">
|
||||
<fileset dir="${src.xdocs.dir}/images/" includes="**/*.*" />
|
||||
</copy>
|
||||
<echo>2. Adding missing text files to site.</echo>
|
||||
<copy todir="${project.build.directory}/site/">
|
||||
<fileset dir="${src.xdocs.dir}/"
|
||||
includes="**/*.txt" />
|
||||
<fileset dir="${src.xdocs.dir}/" includes="**/*.txt" />
|
||||
</copy>
|
||||
<echo>3. Deleting useless generated files.</echo>
|
||||
<delete quiet="true">
|
||||
<fileset dir="${src.xdocs.dir}/rules"
|
||||
includes="**/*.xml" />
|
||||
<fileset dir="${src.xdocs.dir}/"
|
||||
includes="mergedruleset.xml" />
|
||||
<fileset
|
||||
dir="${project.build.directory}"
|
||||
includes="site/mergedruleset.html" />
|
||||
<fileset dir="${src.xdocs.dir}/rules" includes="**/*.xml" />
|
||||
<fileset dir="${src.xdocs.dir}/" includes="mergedruleset.xml" />
|
||||
<fileset dir="${project.build.directory}" includes="site/mergedruleset.html" />
|
||||
</delete>
|
||||
</target>
|
||||
</configuration>
|
||||
@ -407,10 +394,8 @@
|
||||
<target>
|
||||
<echo>PMD specific tasks: cleaning generated xdocs</echo>
|
||||
<delete quiet="true">
|
||||
<fileset dir="${src.xdocs.dir}/rules"
|
||||
includes="**/*.xml" />
|
||||
<fileset dir="${src.xdocs.dir}/"
|
||||
includes="mergedruleset.xml" />
|
||||
<fileset dir="${src.xdocs.dir}/rules" includes="**/*.xml" />
|
||||
<fileset dir="${src.xdocs.dir}/" includes="mergedruleset.xml" />
|
||||
</delete>
|
||||
</target>
|
||||
</configuration>
|
||||
@ -492,7 +477,7 @@
|
||||
<plugin>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-build</artifactId>
|
||||
<version>0.7</version>
|
||||
<version>0.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>pre-site</phase>
|
||||
@ -620,6 +605,23 @@
|
||||
<artifactId>javacc</artifactId>
|
||||
<version>${javacc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<reporting>
|
||||
@ -638,9 +640,7 @@
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>javadoc</report>
|
||||
<!-- FIXME: The following report fails
|
||||
<report>test-javadoc</report>
|
||||
-->
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
|
@ -18,9 +18,13 @@
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
<excludes>
|
||||
<exclude>${project.build.directory}/**</exclude>
|
||||
<exclude>bin/**</exclude>
|
||||
<exclude>.settings/**</exclude>
|
||||
<exclude>.project</exclude>
|
||||
<exclude>.classpath</exclude>
|
||||
<exclude>.checkstyle</exclude>
|
||||
<exclude>.pmd</exclude>
|
||||
<exclude>.ruleset</exclude>
|
||||
</excludes>
|
||||
<directoryMode>0755</directoryMode>
|
||||
<fileMode>0644</fileMode>
|
||||
|
@ -335,8 +335,6 @@ public class PMD {
|
||||
return languages;
|
||||
}
|
||||
|
||||
private static final int ERROR_STATUS = 1;
|
||||
|
||||
/**
|
||||
* Entry to invoke PMD as command line tool
|
||||
*
|
||||
@ -367,7 +365,7 @@ public class PMD {
|
||||
} catch (Exception e) {
|
||||
PMDCommandLineInterface.buildUsageText();
|
||||
System.out.println(e.getMessage());
|
||||
status = ERROR_STATUS;
|
||||
status = PMDCommandLineInterface.ERROR_STATUS;
|
||||
} finally {
|
||||
logHandlerManager.close();
|
||||
LOG.setLevel(oldLogLevel);
|
||||
|
@ -22,15 +22,18 @@ import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
public class Report {
|
||||
|
||||
public static Report createReport(RuleContext ctx, String fileName) {
|
||||
|
||||
Report report = new Report();
|
||||
ctx.setReport(report);
|
||||
ctx.setSourceCodeFilename(fileName);
|
||||
ctx.setSourceCodeFile(new File(fileName));
|
||||
return report;
|
||||
}
|
||||
|
||||
public static Report createReport(RuleContext ctx, String fileName) {
|
||||
Report report = new Report();
|
||||
|
||||
// overtake the listener
|
||||
report.addSynchronizedListeners(ctx.getReport().getSynchronizedListeners());
|
||||
|
||||
ctx.setReport(report);
|
||||
ctx.setSourceCodeFilename(fileName);
|
||||
ctx.setSourceCodeFile(new File(fileName));
|
||||
return report;
|
||||
}
|
||||
|
||||
public static class ReadableDuration {
|
||||
private final long duration;
|
||||
|
||||
@ -112,7 +115,7 @@ public class Report {
|
||||
// Note that this and the above data structure are both being maintained for a bit
|
||||
private final List<RuleViolation> violations = new ArrayList<RuleViolation>();
|
||||
private final Set<Metric> metrics = new HashSet<Metric>();
|
||||
private final List<ReportListener> listeners = new ArrayList<ReportListener>();
|
||||
private final List<SynchronizedReportListener> listeners = new ArrayList<SynchronizedReportListener>();
|
||||
private List<ProcessingError> errors;
|
||||
private List<RuleConfigurationError> configErrors;
|
||||
private Map<Integer, String> linesToSuppress = new HashMap<Integer, String>();
|
||||
@ -164,7 +167,7 @@ public class Report {
|
||||
}
|
||||
|
||||
public void addListener(ReportListener listener) {
|
||||
listeners.add(listener);
|
||||
listeners.add(new SynchronizedReportListener(listener));
|
||||
}
|
||||
|
||||
public List<SuppressedViolation> getSuppressedRuleViolations() {
|
||||
@ -266,13 +269,13 @@ public class Report {
|
||||
}
|
||||
|
||||
public Iterator<ProcessingError> errors() {
|
||||
return errors == null ? EmptyIterator.instance : errors.iterator();
|
||||
return errors == null ? EmptyIterator.<ProcessingError> instance() : errors.iterator();
|
||||
}
|
||||
|
||||
public Iterator<RuleConfigurationError> configErrors() {
|
||||
return configErrors == null ? EmptyIterator.instance : errors.iterator();
|
||||
return configErrors == null ? EmptyIterator.<RuleConfigurationError> instance() : configErrors.iterator();
|
||||
}
|
||||
|
||||
|
||||
public int treeSize() {
|
||||
return violationTree.size();
|
||||
}
|
||||
@ -292,4 +295,12 @@ public class Report {
|
||||
public long getElapsedTimeInMillis() {
|
||||
return end - start;
|
||||
}
|
||||
|
||||
public List<SynchronizedReportListener> getSynchronizedListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
public void addSynchronizedListeners(List<SynchronizedReportListener> synchronizedListeners) {
|
||||
listeners.addAll(synchronizedListeners);
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +42,11 @@ public class RuleContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which shares attributes with the given RuleContext.
|
||||
* Constructor which shares attributes and report listeners with the given RuleContext.
|
||||
*/
|
||||
public RuleContext(RuleContext ruleContext) {
|
||||
this.attributes = ruleContext.attributes;
|
||||
this.report.addSynchronizedListeners(ruleContext.getReport().getSynchronizedListeners());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,9 @@ package net.sourceforge.pmd;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@ -71,7 +73,7 @@ public class RuleSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new rule to this ruleset
|
||||
* Add a new rule to this ruleset. Note that this method does not check for duplicates.
|
||||
*
|
||||
* @param rule the rule to be added
|
||||
*/
|
||||
@ -82,6 +84,53 @@ public class RuleSet {
|
||||
rules.add(rule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a rule. If a rule with the same name and language already existed before in the ruleset,
|
||||
* then the new rule will replace it. This makes sure that the rule configured is overridden.
|
||||
* @param rule
|
||||
* @return <code>true</code> if the new rule replaced an existing one, otherwise <code>false</code>.
|
||||
*/
|
||||
public boolean addRuleReplaceIfExists(Rule rule) {
|
||||
if (rule == null) {
|
||||
throw new IllegalArgumentException("Missing rule");
|
||||
}
|
||||
|
||||
boolean replaced = false;
|
||||
for (Iterator<Rule> it = rules.iterator(); it.hasNext(); ) {
|
||||
Rule r = it.next();
|
||||
if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) {
|
||||
it.remove();
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
addRule(rule);
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only adds a rule to the ruleset if no rule with the same name for the same language was added
|
||||
* before, so that the existent rule configuration won't be overridden.
|
||||
* @param rule
|
||||
* @return <code>true</code> if the rule was added, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean addRuleIfNotExists(Rule rule) {
|
||||
if (rule == null) {
|
||||
throw new IllegalArgumentException("Missing rule");
|
||||
}
|
||||
|
||||
boolean exists = false;
|
||||
for (Rule r : rules) {
|
||||
if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
addRule(rule);
|
||||
}
|
||||
return !exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new rule by reference to this ruleset.
|
||||
*
|
||||
@ -144,8 +193,7 @@ public class RuleSet {
|
||||
*/
|
||||
public Rule getRuleByName(String ruleName) {
|
||||
|
||||
for (Iterator<Rule> i = rules.iterator(); i.hasNext();) {
|
||||
Rule r = i.next();
|
||||
for (Rule r : rules) {
|
||||
if (r.getName().equals(ruleName)) {
|
||||
return r;
|
||||
}
|
||||
@ -162,6 +210,18 @@ public class RuleSet {
|
||||
rules.addAll(rules.size(), ruleSet.getRules());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all rules by reference from one RuleSet to this RuleSet. The rules
|
||||
* can be added as individual references, or collectively as an all rule
|
||||
* reference.
|
||||
*
|
||||
* @param ruleSet the RuleSet to add
|
||||
* @param allRules
|
||||
*/
|
||||
public void addRuleSetByReference(RuleSet ruleSet, boolean allRules) {
|
||||
addRuleSetByReference(ruleSet, allRules, (String[])null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all rules by reference from one RuleSet to this RuleSet. The rules
|
||||
* can be added as individual references, or collectively as an all rule
|
||||
@ -169,13 +229,17 @@ public class RuleSet {
|
||||
*
|
||||
* @param ruleSet the RuleSet to add
|
||||
* @param allRules
|
||||
* @param excludes names of the rules that should be excluded.
|
||||
*/
|
||||
public void addRuleSetByReference(RuleSet ruleSet, boolean allRules) {
|
||||
public void addRuleSetByReference(RuleSet ruleSet, boolean allRules, String ... excludes) {
|
||||
if (StringUtil.isEmpty(ruleSet.getFileName())) {
|
||||
throw new RuntimeException("Adding a rule by reference is not allowed with an empty rule set file name.");
|
||||
}
|
||||
RuleSetReference ruleSetReference = new RuleSetReference(ruleSet.getFileName());
|
||||
ruleSetReference.setAllRules(allRules);
|
||||
if (excludes != null) {
|
||||
ruleSetReference.setExcludes(new HashSet<String>(Arrays.asList(excludes)));
|
||||
}
|
||||
for (Rule rule : ruleSet.getRules()) {
|
||||
RuleReference ruleReference = new RuleReference(rule, ruleSetReference);
|
||||
rules.add(ruleReference);
|
||||
|
@ -280,11 +280,15 @@ public class RuleSetFactory {
|
||||
RuleSetReference ruleSetReference = new RuleSetReference();
|
||||
ruleSetReference.setAllRules(true);
|
||||
ruleSetReference.setRuleSetFileName(ref);
|
||||
NodeList excludeNodes = ruleElement.getChildNodes();
|
||||
for (int i = 0; i < excludeNodes.getLength(); i++) {
|
||||
if (isElementNode(excludeNodes.item(i),"exclude")) {
|
||||
Element excludeElement = (Element) excludeNodes.item(i);
|
||||
String priority = null;
|
||||
NodeList childNodes = ruleElement.getChildNodes();
|
||||
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||
Node child = childNodes.item(i);
|
||||
if (isElementNode(child,"exclude")) {
|
||||
Element excludeElement = (Element) child;
|
||||
ruleSetReference.addExclude(excludeElement.getAttribute("name"));
|
||||
} else if (isElementNode(child, "priority")) {
|
||||
priority = parseTextNode(child).trim();
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +301,12 @@ public class RuleSetFactory {
|
||||
RuleReference ruleReference = new RuleReference();
|
||||
ruleReference.setRuleSetReference(ruleSetReference);
|
||||
ruleReference.setRule(rule);
|
||||
ruleSet.addRule(ruleReference);
|
||||
ruleSet.addRuleIfNotExists(ruleReference);
|
||||
|
||||
// override the priority
|
||||
if (priority != null) {
|
||||
ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(priority)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -508,7 +517,7 @@ public class RuleSetFactory {
|
||||
|
||||
if (StringUtil.isNotEmpty(ruleSetReferenceId.getRuleName())
|
||||
|| referencedRule.getPriority().compareTo(minimumPriority) <= 0) {
|
||||
ruleSet.addRule(ruleReference);
|
||||
ruleSet.addRuleReplaceIfExists(ruleReference);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,13 @@
|
||||
*/
|
||||
package net.sourceforge.pmd;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import net.sourceforge.pmd.util.ResourceLoader;
|
||||
import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
@ -101,88 +104,143 @@ public class RuleSetReferenceId {
|
||||
* @throws IllegalArgumentException If the ID is not Rule reference when there is an external RuleSetReferenceId.
|
||||
*/
|
||||
public RuleSetReferenceId(final String id, final RuleSetReferenceId externalRuleSetReferenceId) {
|
||||
if (externalRuleSetReferenceId != null && !externalRuleSetReferenceId.isExternal()) {
|
||||
throw new IllegalArgumentException("Cannot pair with non-external <" + externalRuleSetReferenceId + ">.");
|
||||
}
|
||||
if (id != null && id.indexOf(',') >= 0) {
|
||||
throw new IllegalArgumentException("A single RuleSetReferenceId cannot contain ',' (comma) characters: "
|
||||
+ id);
|
||||
}
|
||||
if (externalRuleSetReferenceId != null && !externalRuleSetReferenceId.isExternal()) {
|
||||
throw new IllegalArgumentException("Cannot pair with non-external <" + externalRuleSetReferenceId + ">.");
|
||||
}
|
||||
if (id != null && id.indexOf(',') >= 0) {
|
||||
throw new IllegalArgumentException("A single RuleSetReferenceId cannot contain ',' (comma) characters: "
|
||||
+ id);
|
||||
}
|
||||
|
||||
// Damn this parsing sucks, but my brain is just not working to let me write a simpler scheme.
|
||||
if (StringUtil.isEmpty(id) || isFullRuleSetName(id)) {
|
||||
// A full RuleSet name
|
||||
external = true;
|
||||
ruleSetFileName = id;
|
||||
allRules = true;
|
||||
ruleName = null;
|
||||
} else {
|
||||
// Find last path separator if it exists...
|
||||
final int separatorIndex = Math.max(id.lastIndexOf('/'), id.lastIndexOf('\\'));
|
||||
if (separatorIndex >= 0 && separatorIndex != id.length() - 1) {
|
||||
final String name = id.substring(0, separatorIndex);
|
||||
external = true;
|
||||
if (isFullRuleSetName(name)) {
|
||||
// A full RuleSet name
|
||||
ruleSetFileName = name;
|
||||
} else {
|
||||
// Likely a simple RuleSet name
|
||||
int index = name.indexOf('-');
|
||||
if (index >= 0) {
|
||||
// Standard short name
|
||||
ruleSetFileName = "rulesets/" + name.substring(0, index) + "/" + name.substring(index + 1)
|
||||
+ ".xml";
|
||||
} else {
|
||||
// A release RuleSet?
|
||||
if (name.matches("[0-9]+.*")) {
|
||||
ruleSetFileName = "rulesets/releases/" + name + ".xml";
|
||||
} else {
|
||||
// Appears to be a non-standard RuleSet name
|
||||
ruleSetFileName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Damn this parsing sucks, but my brain is just not working to let me
|
||||
// write a simpler scheme.
|
||||
|
||||
// Everything left should be a Rule name
|
||||
allRules = false;
|
||||
ruleName = id.substring(separatorIndex + 1);
|
||||
} else {
|
||||
// Likely a simple RuleSet name
|
||||
int index = id.indexOf('-');
|
||||
if (index >= 0) {
|
||||
// Standard short name
|
||||
external = true;
|
||||
ruleSetFileName = "rulesets/" + id.substring(0, index) + "/" + id.substring(index + 1) + ".xml";
|
||||
allRules = true;
|
||||
ruleName = null;
|
||||
} else {
|
||||
// A release RuleSet?
|
||||
if (id.matches("[0-9]+.*")) {
|
||||
external = true;
|
||||
ruleSetFileName = "rulesets/releases/" + id + ".xml";
|
||||
allRules = true;
|
||||
ruleName = null;
|
||||
} else {
|
||||
// Must be a Rule name
|
||||
external = externalRuleSetReferenceId != null ? true : false;
|
||||
ruleSetFileName = externalRuleSetReferenceId != null ? externalRuleSetReferenceId
|
||||
.getRuleSetFileName() : null;
|
||||
allRules = false;
|
||||
ruleName = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isFullRuleSetName(id)) {
|
||||
// A full RuleSet name
|
||||
external = true;
|
||||
ruleSetFileName = id;
|
||||
allRules = true;
|
||||
ruleName = null;
|
||||
} else {
|
||||
String tempRuleName = getRuleName(id);
|
||||
String tempRuleSetFileName = tempRuleName != null && id != null ?
|
||||
id.substring(0, id.length() - tempRuleName.length() - 1) : id;
|
||||
|
||||
if (this.external && this.ruleName != null && !this.ruleName.equals(id) && externalRuleSetReferenceId != null) {
|
||||
throw new IllegalArgumentException("Cannot pair external <" + this + "> with external <"
|
||||
+ externalRuleSetReferenceId + ">.");
|
||||
}
|
||||
this.externalRuleSetReferenceId = externalRuleSetReferenceId;
|
||||
if (isFullRuleSetName(tempRuleSetFileName)) {
|
||||
// remaining part is a xml ruleset file, so the tempRuleName is probably a real rule name
|
||||
external = true;
|
||||
ruleSetFileName = tempRuleSetFileName;
|
||||
ruleName = tempRuleName;
|
||||
allRules = tempRuleName == null;
|
||||
} else {
|
||||
// resolve the ruleset name - it's maybe a built in ruleset
|
||||
String builtinRuleSet = resolveBuiltInRuleset(tempRuleSetFileName);
|
||||
if (checkRulesetExists(builtinRuleSet)) {
|
||||
external = true;
|
||||
ruleSetFileName = builtinRuleSet;
|
||||
ruleName = tempRuleName;
|
||||
allRules = tempRuleName == null;
|
||||
} else {
|
||||
// well, we didn't find the ruleset, so it's probably not a internal ruleset.
|
||||
// at this time, we don't know, whether the tempRuleName is a name of the rule
|
||||
// or the file name of the ruleset file.
|
||||
// It is assumed, that tempRuleName is actually the filename of the ruleset,
|
||||
// if there are more separator characters in the remaining ruleset filename (tempRuleSetFileName).
|
||||
// This means, the only reliable way to specify single rules within a custom rulesest file is
|
||||
// only possible, if the ruleset file has a .xml file extension.
|
||||
if (tempRuleSetFileName == null || tempRuleSetFileName.contains(File.separator)) {
|
||||
external = true;
|
||||
ruleSetFileName = id;
|
||||
ruleName = null;
|
||||
allRules = true;
|
||||
} else {
|
||||
external = externalRuleSetReferenceId != null ? externalRuleSetReferenceId.isExternal() : false;
|
||||
ruleSetFileName = externalRuleSetReferenceId != null ? externalRuleSetReferenceId.getRuleSetFileName() : null;
|
||||
ruleName = id;
|
||||
allRules = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.external && this.ruleName != null && !this.ruleName.equals(id) && externalRuleSetReferenceId != null) {
|
||||
throw new IllegalArgumentException("Cannot pair external <" + this + "> with external <"
|
||||
+ externalRuleSetReferenceId + ">.");
|
||||
}
|
||||
this.externalRuleSetReferenceId = externalRuleSetReferenceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the given ruleset.
|
||||
* @param name the ruleset name
|
||||
* @return <code>true</code> if the ruleset could be loaded, <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean checkRulesetExists(String name) {
|
||||
boolean resourceFound = false;
|
||||
if (name != null) {
|
||||
try {
|
||||
InputStream resource = ResourceLoader.loadResourceAsStream(name, RuleSetReferenceId.class.getClassLoader());
|
||||
if (resource != null) {
|
||||
resourceFound = true;
|
||||
IOUtils.closeQuietly(resource);
|
||||
}
|
||||
} catch (RuleSetNotFoundException e) {
|
||||
resourceFound = false;
|
||||
}
|
||||
}
|
||||
return resourceFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes that the ruleset name given is e.g. "java-basic". Then
|
||||
* it will return the full classpath name for the ruleset, in this example
|
||||
* it would return "rulesets/java/basic.xml".
|
||||
*
|
||||
* @param name the ruleset name
|
||||
* @return the full classpath to the ruleset
|
||||
*/
|
||||
private String resolveBuiltInRuleset(final String name) {
|
||||
String result = null;
|
||||
if (name != null) {
|
||||
// Likely a simple RuleSet name
|
||||
int index = name.indexOf('-');
|
||||
if (index >= 0) {
|
||||
// Standard short name
|
||||
result = "rulesets/" + name.substring(0, index) + "/" + name.substring(index + 1)
|
||||
+ ".xml";
|
||||
} else {
|
||||
// A release RuleSet?
|
||||
if (name.matches("[0-9]+.*")) {
|
||||
result = "rulesets/releases/" + name + ".xml";
|
||||
} else {
|
||||
// Appears to be a non-standard RuleSet name
|
||||
result = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the rule name out of a ruleset path. E.g. for "/my/ruleset.xml/MyRule" it
|
||||
* would return "MyRule". If no single rule is specified, <code>null</code> is returned.
|
||||
* @param rulesetName the full rule set path
|
||||
* @return the rule name or <code>null</code>.
|
||||
*/
|
||||
private String getRuleName(final String rulesetName) {
|
||||
String result = null;
|
||||
if (rulesetName != null) {
|
||||
// Find last path separator if it exists... this might be a rule name
|
||||
final int separatorIndex = Math.max(rulesetName.lastIndexOf('/'), rulesetName.lastIndexOf('\\'));
|
||||
if (separatorIndex >= 0 && separatorIndex != rulesetName.length() - 1) {
|
||||
result = rulesetName.substring(separatorIndex + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isFullRuleSetName(String name) {
|
||||
return name.endsWith(".xml");
|
||||
return name != null && name.endsWith(".xml");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,11 +251,13 @@ public class RuleSetReferenceId {
|
||||
*/
|
||||
public static List<RuleSetReferenceId> parse(String referenceString) {
|
||||
List<RuleSetReferenceId> references = new ArrayList<RuleSetReferenceId>();
|
||||
if (referenceString.indexOf(',') == -1) {
|
||||
references.add(new RuleSetReferenceId(referenceString));
|
||||
} else {
|
||||
for (String name : referenceString.split(",")) {
|
||||
references.add(new RuleSetReferenceId(name));
|
||||
if (referenceString != null && referenceString.trim().length() > 0) {
|
||||
if (referenceString.indexOf(',') == -1) {
|
||||
references.add(new RuleSetReferenceId(referenceString));
|
||||
} else {
|
||||
for (String name : referenceString.split(",")) {
|
||||
references.add(new RuleSetReferenceId(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return references;
|
||||
@ -247,20 +307,20 @@ public class RuleSetReferenceId {
|
||||
* @throws RuleSetNotFoundException if unable to find a resource.
|
||||
*/
|
||||
public InputStream getInputStream(ClassLoader classLoader) throws RuleSetNotFoundException {
|
||||
if (externalRuleSetReferenceId == null) {
|
||||
InputStream in = StringUtil.isEmpty(ruleSetFileName) ? null : ResourceLoader.loadResourceAsStream(
|
||||
ruleSetFileName, classLoader);
|
||||
if (in == null) {
|
||||
throw new RuleSetNotFoundException(
|
||||
"Can't find resource "
|
||||
+ ruleSetFileName
|
||||
+ ". Make sure the resource is a valid file or URL or is on the CLASSPATH. Here's the current classpath: "
|
||||
+ System.getProperty("java.class.path"));
|
||||
}
|
||||
return in;
|
||||
} else {
|
||||
return externalRuleSetReferenceId.getInputStream(classLoader);
|
||||
}
|
||||
if (externalRuleSetReferenceId == null) {
|
||||
InputStream in = StringUtil.isEmpty(ruleSetFileName) ? null : ResourceLoader.loadResourceAsStream(
|
||||
ruleSetFileName, classLoader);
|
||||
if (in == null) {
|
||||
throw new RuleSetNotFoundException(
|
||||
"Can't find resource " + ruleSetFileName
|
||||
+ ". Make sure the resource is a valid file or URL and is on the CLASSPATH. "
|
||||
+ "Here's the current classpath: "
|
||||
+ System.getProperty("java.class.path"));
|
||||
}
|
||||
return in;
|
||||
} else {
|
||||
return externalRuleSetReferenceId.getInputStream(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +129,9 @@ public class RuleSetWriter {
|
||||
}
|
||||
|
||||
private Element createExcludeElement(String exclude) {
|
||||
return createTextElement("exclude", exclude);
|
||||
Element element = document.createElementNS(RULESET_NS_URI, "exclude");
|
||||
element.setAttribute("name", exclude);
|
||||
return element;
|
||||
}
|
||||
|
||||
private Element createExampleElement(String example) {
|
||||
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd;
|
||||
|
||||
import net.sourceforge.pmd.stat.Metric;
|
||||
|
||||
/**
|
||||
* Wraps a report listener in order to synchronize calls to it.
|
||||
*/
|
||||
public final class SynchronizedReportListener implements ReportListener {
|
||||
|
||||
private final ReportListener wrapped;
|
||||
|
||||
public SynchronizedReportListener(ReportListener listener) {
|
||||
this.wrapped = listener;
|
||||
}
|
||||
|
||||
public synchronized void ruleViolationAdded(RuleViolation ruleViolation) {
|
||||
wrapped.ruleViolationAdded(ruleViolation);
|
||||
}
|
||||
|
||||
public synchronized void metricAdded(Metric metric) {
|
||||
wrapped.metricAdded(metric);
|
||||
}
|
||||
|
||||
}
|
@ -26,7 +26,6 @@ import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
import net.sourceforge.pmd.RuleSets;
|
||||
import net.sourceforge.pmd.lang.Language;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.renderers.AbstractRenderer;
|
||||
import net.sourceforge.pmd.renderers.Renderer;
|
||||
@ -110,24 +109,10 @@ public class PMDTask extends Task {
|
||||
formatters.add(f);
|
||||
}
|
||||
|
||||
public void addConfiguredVersion(Version version) {
|
||||
LanguageVersion languageVersion = LanguageVersion.findByTerseName(version.getTerseName());
|
||||
public void addConfiguredSourceLanguage(SourceLanguage version) {
|
||||
LanguageVersion languageVersion = LanguageVersion.findVersionsForLanguageTerseName(version.getName(), version.getVersion());
|
||||
if (languageVersion == null) {
|
||||
StringBuilder buf = new StringBuilder("The <version> element, if used, must be one of ");
|
||||
boolean first = true;
|
||||
for (Language language : Language.values()) {
|
||||
if (language.getVersions().size() > 2) {
|
||||
for (LanguageVersion v : language.getVersions()) {
|
||||
if (!first) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append('\'').append(v.getTerseName()).append('\'');
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.append('.');
|
||||
throw new BuildException(buf.toString());
|
||||
throw new BuildException("The following language is not supported:" + version + ".");
|
||||
}
|
||||
configuration.setDefaultLanguageVersion(languageVersion);
|
||||
}
|
||||
@ -304,7 +289,12 @@ public class PMDTask extends Task {
|
||||
log("Using the normal ClassLoader", Project.MSG_VERBOSE);
|
||||
} else {
|
||||
log("Using the AntClassLoader", Project.MSG_VERBOSE);
|
||||
configuration.setClassLoader(new AntClassLoader(getProject(), classpath));
|
||||
// must be true, otherwise you'll get ClassCastExceptions as classes are loaded twice
|
||||
// and exist in multiple class loaders
|
||||
boolean parentFirst = true;
|
||||
configuration.setClassLoader(
|
||||
new AntClassLoader(Thread.currentThread().getContextClassLoader(), getProject(),
|
||||
classpath, parentFirst));
|
||||
}
|
||||
try {
|
||||
/*
|
||||
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd.ant;
|
||||
|
||||
/**
|
||||
* Stores LanguageVersion terse name value.
|
||||
*/
|
||||
public class SourceLanguage {
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "<language name=\"" + this.name + "\" version=\"" + this.version + "\" />";
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
package net.sourceforge.pmd.ant;
|
||||
|
||||
/**
|
||||
* Stores LanguageVersion terse name value.
|
||||
*/
|
||||
public class Version {
|
||||
private String terseName;
|
||||
|
||||
public void addText(String text) {
|
||||
this.terseName = text;
|
||||
}
|
||||
|
||||
public String getTerseName() {
|
||||
return terseName;
|
||||
}
|
||||
}
|
@ -27,17 +27,24 @@ public class PMDCommandLineInterface {
|
||||
public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit";
|
||||
public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status";
|
||||
|
||||
public static final int ERROR_STATUS = 1;
|
||||
|
||||
public static PMDParameters extractParameters(PMDParameters arguments, String[] args, String progName) {
|
||||
jcommander = new JCommander(arguments);
|
||||
jcommander.setProgramName(progName);
|
||||
|
||||
try {
|
||||
jcommander = new JCommander(arguments, args);
|
||||
jcommander.setProgramName(progName);
|
||||
jcommander.parse(args);
|
||||
if (arguments.isHelp()) {
|
||||
jcommander.usage();
|
||||
System.exit(0);
|
||||
System.out.println(buildUsageText());
|
||||
setStatusCodeOrExit(0);
|
||||
}
|
||||
} catch (ParameterException e) {
|
||||
jcommander.usage();
|
||||
System.out.println(buildUsageText());
|
||||
System.out.println(e.getMessage());
|
||||
setStatusCodeOrExit(ERROR_STATUS);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
@ -61,7 +68,7 @@ public class PMDCommandLineInterface {
|
||||
+ "3) A ruleset filename or a comma-delimited string of ruleset filenames" + PMD.EOL
|
||||
+ PMD.EOL
|
||||
+ "For example: " + PMD.EOL
|
||||
+ "c:\\> " + launchCmd + "-d c:\\my\\source\\code -f html -R java-unusedcode" + PMD.EOL
|
||||
+ "c:\\> " + launchCmd + " -d c:\\my\\source\\code -f html -R java-unusedcode" + PMD.EOL
|
||||
+ PMD.EOL;
|
||||
|
||||
fullText += supportedVersions() + PMD.EOL;
|
||||
@ -91,16 +98,16 @@ public class PMDCommandLineInterface {
|
||||
+ WINDOWS_PROMPT + launchCmd + " -dir" + WINDOWS_PATH_TO_CODE + "-format text -R java-unusedcode,java-imports -version 1.5 -language java -debug" + PMD.EOL
|
||||
+ WINDOWS_PROMPT + launchCmd + " -dir" + WINDOWS_PATH_TO_CODE + "-f xml -rulesets java-basic,java-design -encoding UTF-8" + PMD.EOL
|
||||
+ WINDOWS_PROMPT + launchCmd + " -d" + WINDOWS_PATH_TO_CODE + "-rulesets java-typeresolution -auxclasspath commons-collections.jar;derby.jar" + PMD.EOL
|
||||
+ WINDOWS_PROMPT + launchCmd + " -d" + WINDOWS_PATH_TO_CODE + "-f html -R java-typeresolution -auxclasspath -d file:///C:/my/classpathfile" + PMD.EOL
|
||||
+ WINDOWS_PROMPT + launchCmd + " -d" + WINDOWS_PATH_TO_CODE + "-f html -R java-typeresolution -auxclasspath file:///C:/my/classpathfile" + PMD.EOL
|
||||
+ PMD.EOL;
|
||||
}
|
||||
|
||||
private static String getUnixExample(String launchCmd) {
|
||||
final String UNIX_PROMPT = "$ ";
|
||||
return "For example on *nix: " + PMD.EOL
|
||||
+ UNIX_PROMPT + launchCmd + " -dir /home/workspace/src/main/java/code -f nicehtml -rulesets java-basic,java-design" + PMD.EOL
|
||||
+ UNIX_PROMPT + launchCmd + " -d ./src/main/java/code -f nicehtml -r java-basic,java-design -xslt my-own.xsl" + PMD.EOL
|
||||
+ UNIX_PROMPT + launchCmd + " -d ./src/main/java/code -f nicehtml -r java-typeresolution -auxclasspath commons-collections.jar:derby.jar"
|
||||
+ UNIX_PROMPT + launchCmd + " -dir /home/workspace/src/main/java/code -f html -rulesets java-basic,java-design" + PMD.EOL
|
||||
+ UNIX_PROMPT + launchCmd + " -d ./src/main/java/code -f xslt -R java-basic,java-design -property xsltFilename=my-own.xsl" + PMD.EOL
|
||||
+ UNIX_PROMPT + launchCmd + " -d ./src/main/java/code -f html -R java-typeresolution -auxclasspath commons-collections.jar:derby.jar"
|
||||
+ PMD.EOL;
|
||||
}
|
||||
|
||||
@ -148,12 +155,16 @@ public class PMDCommandLineInterface {
|
||||
}
|
||||
|
||||
public static void run(String[] args) {
|
||||
if ( isExitAfterRunSet() )
|
||||
System.exit(PMD.run(args));
|
||||
else
|
||||
setStatusCode(PMD.run(args));
|
||||
setStatusCodeOrExit(PMD.run(args));
|
||||
}
|
||||
|
||||
public static void setStatusCodeOrExit(int status) {
|
||||
if ( isExitAfterRunSet() )
|
||||
System.exit(status);
|
||||
else
|
||||
setStatusCode(status);
|
||||
}
|
||||
|
||||
private static boolean isExitAfterRunSet() {
|
||||
return (System.getenv(NO_EXIT_AFTER_RUN) == null ? false : true);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public class PMDParameters {
|
||||
@Parameter(names = {"-debug", "-verbose", "-D", "-V"}, description = "Debug mode")
|
||||
private boolean debug = false;
|
||||
|
||||
@Parameter(names = {"-help","-h","-H"}, description = "Display help on usage")
|
||||
@Parameter(names = {"-help","-h","-H"}, description = "Display help on usage", help = true)
|
||||
private boolean help = false;
|
||||
|
||||
@Parameter(names= {"-encoding", "-e"} , description = "specifies the character set encoding of the source code files PMD is reading (i.e., UTF-8)")
|
||||
@ -67,7 +67,7 @@ public class PMDParameters {
|
||||
@Parameter(names = {"-language", "-l"}, description = "specify version of a language PMD should use")
|
||||
private String language = Language.getDefaultLanguage().getTerseName();
|
||||
|
||||
@Parameter(names = "-auxclasspath", description = "specifies the classpath for libraries used by the source code (used by type resolution)\n(alternatively, a 'file://' URL to a text file containing path elements on consecutive lines")
|
||||
@Parameter(names = "-auxclasspath", description = "specifies the classpath for libraries used by the source code. This is used by the type resolution. Alternatively, a 'file://' URL to a text file containing path elements on consecutive lines can be specified.")
|
||||
private String auxclasspath;
|
||||
|
||||
class PropertyConverter implements IStringConverter<Properties> {
|
||||
@ -118,7 +118,11 @@ public class PMDParameters {
|
||||
configuration.setSuppressMarker(params.getSuppressmarker());
|
||||
configuration.setThreads(params.getThreads());
|
||||
for ( LanguageVersion language : LanguageVersion.findVersionsForLanguageTerseName( params.getLanguage() ) ) {
|
||||
configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(language.getLanguage().getVersion(params.getVersion()));
|
||||
LanguageVersion languageVersion = language.getLanguage().getVersion(params.getVersion());
|
||||
if (languageVersion == null) {
|
||||
languageVersion = language.getLanguage().getDefaultVersion();
|
||||
}
|
||||
configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(languageVersion);
|
||||
}
|
||||
try {
|
||||
configuration.prependClasspath(params.getAuxclasspath());
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.sourceforge.pmd.util.filter.Filters;
|
||||
|
||||
@ -20,4 +21,8 @@ public abstract class AbstractLanguage implements Language {
|
||||
public Tokenizer getTokenizer() {
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
// needs to be implemented by subclasses.
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,16 @@ import java.util.TreeMap;
|
||||
|
||||
import net.sourceforge.pmd.util.FileFinder;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
public class CPD {
|
||||
|
||||
private static final int MISSING_FILES = 1;
|
||||
private static final int MISSING_ARGS = 2;
|
||||
private static final int DUPLICATE_CODE_FOUND = 4;
|
||||
|
||||
static boolean dontExitForTests = false;
|
||||
|
||||
private CPDConfiguration configuration;
|
||||
|
||||
private Map<String, SourceCode> source = new TreeMap<String, SourceCode>();
|
||||
@ -31,6 +35,8 @@ public class CPD {
|
||||
|
||||
public CPD(CPDConfiguration theConfiguration) {
|
||||
configuration = theConfiguration;
|
||||
// before we start any tokenizing (add(File...)), we need to reset the static TokenEntry status
|
||||
TokenEntry.clearImages();
|
||||
}
|
||||
|
||||
public void setCpdListener(CPDListener cpdListener) {
|
||||
@ -38,7 +44,6 @@ public class CPD {
|
||||
}
|
||||
|
||||
public void go() {
|
||||
TokenEntry.clearImages();
|
||||
matchAlgorithm = new MatchAlgorithm(
|
||||
source, tokens,
|
||||
configuration.minimumTileSize(),
|
||||
@ -92,18 +97,23 @@ public class CPD {
|
||||
current.add(signature);
|
||||
}
|
||||
|
||||
if (!file.getCanonicalPath().equals(new File(file.getAbsolutePath()).getCanonicalPath())) {
|
||||
if (!FilenameUtils.equalsNormalizedOnSystem(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) {
|
||||
System.err.println("Skipping " + file + " since it appears to be a symlink");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)");
|
||||
return;
|
||||
}
|
||||
|
||||
listener.addedFile(fileCount, file);
|
||||
SourceCode sourceCode = configuration.sourceCodeFor(file);
|
||||
configuration.tokenizer().tokenize(sourceCode, tokens);
|
||||
source.put(sourceCode.getFileName(), sourceCode);
|
||||
}
|
||||
|
||||
private static void setSystemProperties(String[] args) {
|
||||
private static void setSystemProperties(String[] args, CPDConfiguration config) {
|
||||
boolean ignoreLiterals = CPDConfiguration.findBooleanSwitch(args, "--ignore-literals");
|
||||
boolean ignoreIdentifiers = CPDConfiguration.findBooleanSwitch(args, "--ignore-identifiers");
|
||||
boolean ignoreAnnotations = CPDConfiguration.findBooleanSwitch(args, "--ignore-annotations");
|
||||
@ -118,6 +128,7 @@ public class CPD {
|
||||
properties.setProperty(JavaTokenizer.IGNORE_ANNOTATIONS, "true");
|
||||
}
|
||||
System.setProperties(properties);
|
||||
config.language().setProperties(properties);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@ -131,7 +142,7 @@ public class CPD {
|
||||
|
||||
// Pass extra parameters as System properties to allow language
|
||||
// implementation to retrieve their associate values...
|
||||
setSystemProperties(args);
|
||||
setSystemProperties(args, config);
|
||||
|
||||
CPD cpd = new CPD(config);
|
||||
|
||||
@ -155,7 +166,9 @@ public class CPD {
|
||||
cpd.go();
|
||||
if (cpd.getMatches().hasNext()) {
|
||||
System.out.println(config.renderer().render(cpd.getMatches()));
|
||||
System.exit(DUPLICATE_CODE_FOUND);
|
||||
if (!dontExitForTests) {
|
||||
System.exit(DUPLICATE_CODE_FOUND);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -23,17 +23,21 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
String languageString = findOptionalStringValue(args, "--language", "java");
|
||||
language = new LanguageFactory().createLanguage(languageString);
|
||||
|
||||
String formatString = findOptionalStringValue(args, "--format", "text");
|
||||
renderer = getRendererFromString(formatString);
|
||||
|
||||
final String systemDefaultEncoding = System.getProperty("file.encoding");
|
||||
setEncoding( findOptionalStringValue(args, "--encoding", systemDefaultEncoding) );
|
||||
|
||||
String formatString = findOptionalStringValue(args, "--format", "text");
|
||||
renderer = getRendererFromString(formatString);
|
||||
|
||||
minimumTileSize = Integer.parseInt(findRequiredStringValue(args, "--minimum-tokens"));
|
||||
|
||||
|
||||
skipDuplicates = findBooleanSwitch(args, "--skip-duplicate-files");
|
||||
}
|
||||
|
||||
|
||||
public CPDConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
public CPDConfiguration(int theMinTileSize, Language theLanguage, String theEncoding) {
|
||||
minimumTileSize = theMinTileSize;
|
||||
language = theLanguage;
|
||||
|
@ -188,16 +188,9 @@ public class CPDTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME Can't we do something cleaner and
|
||||
* more dynamic ? Maybe externalise to a properties files that will
|
||||
* be generated when building pmd ? This will not have to add manually
|
||||
* new language here ?
|
||||
*/
|
||||
public static class LanguageAttribute extends EnumeratedAttribute {
|
||||
private static final String[] LANGUAGES = new String[]{"java","jsp","cpp", "c","php", "ruby", "fortran", "cs"};
|
||||
public String[] getValues() {
|
||||
return LANGUAGES;
|
||||
return LanguageFactory.supportedLanguages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,6 @@ package net.sourceforge.pmd.cpd;
|
||||
|
||||
public class FortranLanguage extends AbstractLanguage {
|
||||
public FortranLanguage() {
|
||||
super(new FortranTokenizer(), ".for");
|
||||
super(new FortranTokenizer(), ".for", ".f", ".f66", ".f77", ".f90");
|
||||
}
|
||||
}
|
||||
|
@ -105,20 +105,23 @@ public class GUI implements CPDListener {
|
||||
public String[] extensions() { return new String[] {".rb" }; }; } },
|
||||
{"Fortran", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage("fortran"); }
|
||||
public String[] extensions() { return new String[] {".rb" }; }; } },
|
||||
{"by extension...", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage(LanguageFactory.BY_EXTENSION, p); }
|
||||
public String[] extensions() { return new String[] {"" }; }; } },
|
||||
{"PHP", new LanguageConfig() {
|
||||
public String[] extensions() { return new String[] {".for", ".f", ".f66", ".f77", ".f90" }; }; } },
|
||||
{"PHP", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage("php"); }
|
||||
public String[] extensions() { return new String[] {".php" }; }; } },
|
||||
{"C#", new LanguageConfig() {
|
||||
{"C#", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage("cs"); }
|
||||
public String[] extensions() { return new String[] {".cs" }; }; } },
|
||||
{"Ecmascript", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage("js"); }
|
||||
public String[] extensions() { return new String[] {".js" }; }; } },
|
||||
{"by extension...", new LanguageConfig() {
|
||||
public Language languageFor(LanguageFactory lf, Properties p) { return lf.createLanguage(LanguageFactory.BY_EXTENSION, p); }
|
||||
public String[] extensions() { return new String[] {"" }; }; } },
|
||||
};
|
||||
|
||||
private static final int DEFAULT_CPD_MINIMUM_LENGTH = 75;
|
||||
private static final Map LANGUAGE_CONFIGS_BY_LABEL = new HashMap(LANGUAGE_SETS.length);
|
||||
private static final Map<String, LanguageConfig> LANGUAGE_CONFIGS_BY_LABEL = new HashMap<String, LanguageConfig>(LANGUAGE_SETS.length);
|
||||
private static final KeyStroke COPY_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_C,ActionEvent.CTRL_MASK,false);
|
||||
private static final KeyStroke DELETE_KEY_STROKE = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
|
||||
|
||||
@ -148,12 +151,12 @@ public class GUI implements CPDListener {
|
||||
|
||||
static {
|
||||
for (int i=0; i<LANGUAGE_SETS.length; i++) {
|
||||
LANGUAGE_CONFIGS_BY_LABEL.put(LANGUAGE_SETS[i][0], LANGUAGE_SETS[i][1]);
|
||||
LANGUAGE_CONFIGS_BY_LABEL.put((String)LANGUAGE_SETS[i][0], (LanguageConfig)LANGUAGE_SETS[i][1]);
|
||||
}
|
||||
}
|
||||
|
||||
private static LanguageConfig languageConfigFor(String label) {
|
||||
return (LanguageConfig)LANGUAGE_CONFIGS_BY_LABEL.get(label);
|
||||
return LANGUAGE_CONFIGS_BY_LABEL.get(label);
|
||||
}
|
||||
|
||||
private static class CancelListener implements ActionListener {
|
||||
@ -571,6 +574,7 @@ public class GUI implements CPDListener {
|
||||
p.setProperty(LanguageFactory.EXTENSION, extensionField.getText());
|
||||
LanguageConfig conf = languageConfigFor((String)languageBox.getSelectedItem());
|
||||
Language language = conf.languageFor(new LanguageFactory(), p);
|
||||
language.setProperties(p);
|
||||
CPDConfiguration config = new CPDConfiguration(
|
||||
Integer.parseInt(minimumLengthField.getText()),
|
||||
language, encodingField.getText()
|
||||
@ -594,21 +598,19 @@ public class GUI implements CPDListener {
|
||||
t.stop();
|
||||
|
||||
matches = new ArrayList<Match>();
|
||||
Match match;
|
||||
for (Iterator<Match> i = cpd.getMatches(); i.hasNext();) {
|
||||
match = i.next();
|
||||
Match match = i.next();
|
||||
setLabelFor(match);
|
||||
matches.add(match);
|
||||
}
|
||||
|
||||
setListDataFrom(cpd.getMatches());
|
||||
String report = new SimpleRenderer().render(cpd.getMatches());
|
||||
if (report.length() == 0) {
|
||||
JOptionPane.showMessageDialog(frame,
|
||||
"Done; couldn't find any duplicates longer than " + minimumLengthField.getText() + " tokens");
|
||||
"Done. Couldn't find any duplicates longer than " + minimumLengthField.getText() + " tokens");
|
||||
} else {
|
||||
resultsTextArea.setText(report);
|
||||
setListDataFrom(cpd.getMatches());
|
||||
|
||||
}
|
||||
} catch (IOException t) {
|
||||
t.printStackTrace();
|
||||
|
@ -12,7 +12,11 @@ public class JavaLanguage extends AbstractLanguage {
|
||||
|
||||
public JavaLanguage(Properties properties) {
|
||||
super(new JavaTokenizer(), ".java");
|
||||
JavaTokenizer tokenizer = (JavaTokenizer)getTokenizer();
|
||||
tokenizer.setProperties(properties);
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
public final void setProperties(Properties properties) {
|
||||
JavaTokenizer tokenizer = (JavaTokenizer)getTokenizer();
|
||||
tokenizer.setProperties(properties);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.LanguageVersionHandler;
|
||||
@ -23,8 +23,6 @@ public class JavaTokenizer implements Tokenizer {
|
||||
private boolean ignoreAnnotations;
|
||||
private boolean ignoreLiterals;
|
||||
private boolean ignoreIdentifiers;
|
||||
List<Discarder> discarders = new ArrayList<Discarder>();
|
||||
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
ignoreAnnotations = Boolean.parseBoolean(properties.getProperty(IGNORE_ANNOTATIONS, "false"));
|
||||
@ -42,22 +40,17 @@ public class JavaTokenizer implements Tokenizer {
|
||||
fileName, new StringReader(stringBuilder.toString()));
|
||||
Token currentToken = (Token) tokenMgr.getNextToken();
|
||||
|
||||
initDiscarders();
|
||||
TokenDiscarder discarder = new TokenDiscarder(ignoreAnnotations);
|
||||
|
||||
while (currentToken.image.length() > 0) {
|
||||
for (Discarder discarder : discarders) {
|
||||
discarder.add(currentToken);
|
||||
}
|
||||
discarder.updateState(currentToken);
|
||||
|
||||
if (inDiscardingState()) {
|
||||
if (discarder.isDiscarding()) {
|
||||
currentToken = (Token) tokenMgr.getNextToken();
|
||||
continue;
|
||||
}
|
||||
|
||||
//skip semicolons
|
||||
if (currentToken.kind != JavaParserConstants.SEMICOLON) {
|
||||
processToken(tokenEntries, fileName, currentToken);
|
||||
}
|
||||
processToken(tokenEntries, fileName, currentToken);
|
||||
currentToken = (Token) tokenMgr.getNextToken();
|
||||
}
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
@ -68,7 +61,8 @@ public class JavaTokenizer implements Tokenizer {
|
||||
if (ignoreLiterals
|
||||
&& (currentToken.kind == JavaParserConstants.STRING_LITERAL
|
||||
|| currentToken.kind == JavaParserConstants.CHARACTER_LITERAL
|
||||
|| currentToken.kind == JavaParserConstants.DECIMAL_LITERAL || currentToken.kind == JavaParserConstants.FLOATING_POINT_LITERAL)) {
|
||||
|| currentToken.kind == JavaParserConstants.DECIMAL_LITERAL
|
||||
|| currentToken.kind == JavaParserConstants.FLOATING_POINT_LITERAL)) {
|
||||
image = String.valueOf(currentToken.kind);
|
||||
}
|
||||
if (ignoreIdentifiers && currentToken.kind == JavaParserConstants.IDENTIFIER) {
|
||||
@ -77,23 +71,6 @@ public class JavaTokenizer implements Tokenizer {
|
||||
tokenEntries.add(new TokenEntry(image, fileName, currentToken.beginLine));
|
||||
}
|
||||
|
||||
private void initDiscarders() {
|
||||
if (ignoreAnnotations)
|
||||
discarders.add(new AnnotationStateDiscarder());
|
||||
discarders.add(new SuppressCPDDiscarder());
|
||||
discarders.add(new KeyWordToSemiColonStateDiscarder(JavaParserConstants.IMPORT));
|
||||
discarders.add(new KeyWordToSemiColonStateDiscarder(JavaParserConstants.PACKAGE));
|
||||
}
|
||||
|
||||
private boolean inDiscardingState() {
|
||||
boolean discarding = false;
|
||||
for (Discarder discarder : discarders) {
|
||||
if (discarder.isDiscarding())
|
||||
discarding = true;
|
||||
}
|
||||
return discarding;
|
||||
}
|
||||
|
||||
public void setIgnoreLiterals(boolean ignore) {
|
||||
this.ignoreLiterals = ignore;
|
||||
}
|
||||
@ -106,83 +83,101 @@ public class JavaTokenizer implements Tokenizer {
|
||||
this.ignoreAnnotations = ignoreAnnotations;
|
||||
}
|
||||
|
||||
static public interface Discarder {
|
||||
public void add(Token token);
|
||||
/**
|
||||
* The {@link TokenDiscarder} consumes token by token and maintains state.
|
||||
* It can detect, whether the current token belongs to an annotation and whether
|
||||
* the current token should be discarded by CPD.
|
||||
* <p>
|
||||
* By default, it discards semicolons, package and import statements, and enables CPD suppression.
|
||||
* Optionally, all annotations can be ignored, too.
|
||||
* </p>
|
||||
*/
|
||||
private static class TokenDiscarder {
|
||||
private boolean isAnnotation = false;
|
||||
private boolean nextTokenEndsAnnotation = false;
|
||||
private int annotationStack = 0;
|
||||
|
||||
public boolean isDiscarding();
|
||||
}
|
||||
private boolean discardingSemicolon = false;
|
||||
private boolean discardingKeywords = false;
|
||||
private boolean discardingSuppressing = false;
|
||||
private boolean discardingAnnotations = false;
|
||||
private boolean ignoreAnnotations = false;
|
||||
|
||||
static public class AnnotationStateDiscarder implements Discarder {
|
||||
public TokenDiscarder(boolean ignoreAnnotations) {
|
||||
this.ignoreAnnotations = ignoreAnnotations;
|
||||
}
|
||||
|
||||
Stack<Token> tokenStack = new Stack<Token>();
|
||||
public void updateState(Token currentToken) {
|
||||
detectAnnotations(currentToken);
|
||||
|
||||
public void add(Token token) {
|
||||
if (isDiscarding() && tokenStack.size() == 2 && token.kind != JavaParserConstants.LPAREN) {
|
||||
tokenStack.clear();
|
||||
}
|
||||
|
||||
if (token.kind == JavaParserConstants.AT && !isDiscarding()) {
|
||||
tokenStack.push(token);
|
||||
return;
|
||||
}
|
||||
if (token.kind == JavaParserConstants.RPAREN && isDiscarding()) {
|
||||
Token popped = null;
|
||||
while ((popped = tokenStack.pop()).kind != JavaParserConstants.LPAREN) ;
|
||||
return;
|
||||
|
||||
} else {
|
||||
if (isDiscarding())
|
||||
tokenStack.push(token);
|
||||
skipSemicolon(currentToken);
|
||||
skipPackageAndImport(currentToken);
|
||||
skipCPDSuppression(currentToken);
|
||||
if (ignoreAnnotations) {
|
||||
skipAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDiscarding() {
|
||||
return !tokenStack.isEmpty();
|
||||
public void skipPackageAndImport(Token currentToken) {
|
||||
if (currentToken.kind == JavaParserConstants.PACKAGE || currentToken.kind == JavaParserConstants.IMPORT) {
|
||||
discardingKeywords = true;
|
||||
} else if (discardingKeywords && currentToken.kind == JavaParserConstants.SEMICOLON) {
|
||||
discardingKeywords = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public class KeyWordToSemiColonStateDiscarder implements Discarder {
|
||||
|
||||
private final int keyword;
|
||||
Stack<Token> tokenStack = new Stack<Token>();
|
||||
|
||||
public KeyWordToSemiColonStateDiscarder(int keyword) {
|
||||
this.keyword = keyword;
|
||||
public void skipSemicolon(Token currentToken) {
|
||||
if (currentToken.kind == JavaParserConstants.SEMICOLON) {
|
||||
discardingSemicolon = true;
|
||||
} else if (discardingSemicolon && currentToken.kind != JavaParserConstants.SEMICOLON) {
|
||||
discardingSemicolon = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void add(Token token) {
|
||||
if (token.kind == keyword)
|
||||
tokenStack.add(token);
|
||||
if (token.kind == JavaParserConstants.SEMICOLON && isDiscarding())
|
||||
tokenStack.clear();
|
||||
}
|
||||
|
||||
public boolean isDiscarding() {
|
||||
return !tokenStack.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public class SuppressCPDDiscarder implements Discarder {
|
||||
AnnotationStateDiscarder asm = new AnnotationStateDiscarder();
|
||||
Boolean discarding = false;
|
||||
|
||||
public void add(Token token) {
|
||||
asm.add(token);
|
||||
public void skipCPDSuppression(Token currentToken) {
|
||||
//if processing an annotation, look for a CPD-START or CPD-END
|
||||
if (asm.isDiscarding()) {
|
||||
if (CPD_START.equals(token.image))
|
||||
discarding = true;
|
||||
if (CPD_END.equals(token.image) && discarding)
|
||||
discarding = false;
|
||||
if (isAnnotation) {
|
||||
if (!discardingSuppressing && currentToken.kind == JavaParserConstants.STRING_LITERAL && CPD_START.equals(currentToken.image)) {
|
||||
discardingSuppressing = true;
|
||||
} else if (discardingSuppressing && currentToken.kind == JavaParserConstants.STRING_LITERAL && CPD_END.equals(currentToken.image)) {
|
||||
discardingSuppressing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skipAnnotations() {
|
||||
if (!discardingAnnotations && isAnnotation) {
|
||||
discardingAnnotations = true;
|
||||
} else if (discardingAnnotations && !isAnnotation) {
|
||||
discardingAnnotations = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDiscarding() {
|
||||
return discarding;
|
||||
boolean result = discardingSemicolon || discardingKeywords || discardingAnnotations || discardingSuppressing;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void detectAnnotations(Token currentToken) {
|
||||
if (isAnnotation && nextTokenEndsAnnotation) {
|
||||
isAnnotation = false;
|
||||
nextTokenEndsAnnotation = false;
|
||||
}
|
||||
if (isAnnotation) {
|
||||
if (currentToken.kind == JavaParserConstants.LPAREN) {
|
||||
annotationStack++;
|
||||
} else if (currentToken.kind == JavaParserConstants.RPAREN) {
|
||||
annotationStack--;
|
||||
if (annotationStack == 0) {
|
||||
nextTokenEndsAnnotation = true;
|
||||
}
|
||||
} else if (annotationStack == 0 && currentToken.kind != JavaParserConstants.IDENTIFIER && currentToken.kind != JavaParserConstants.LPAREN) {
|
||||
isAnnotation = false;
|
||||
}
|
||||
}
|
||||
if (currentToken.kind == JavaParserConstants.AT) {
|
||||
isAnnotation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,13 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.Properties;
|
||||
|
||||
public interface Language {
|
||||
|
||||
Tokenizer getTokenizer();
|
||||
|
||||
FilenameFilter getFileFilter();
|
||||
|
||||
void setProperties(Properties properties);
|
||||
}
|
||||
|
@ -7,9 +7,16 @@ import java.util.Properties;
|
||||
|
||||
public class LanguageFactory {
|
||||
|
||||
// TODO derive and provide this at runtime instead, used by outside IDEs
|
||||
public static String[] supportedLanguages = new String[]{"java", "jsp", "cpp", "c", "php", "ruby","fortran", "ecmascript","cs" };
|
||||
|
||||
/*
|
||||
* TODO derive and provide this at runtime instead, used by outside IDEs
|
||||
* FIXME Can't we do something cleaner and
|
||||
* more dynamic ? Maybe externalise to a properties files that will
|
||||
* be generated when building pmd ? This will not have to add manually
|
||||
* new language here ?
|
||||
*/
|
||||
public static String[] supportedLanguages =
|
||||
new String[]{"java", "jsp", "cpp", "c", "php", "ruby", "fortran", "ecmascript", "cs"};
|
||||
|
||||
private static final String SUFFIX = "Language";
|
||||
public static final String EXTENSION = "extension";
|
||||
public static final String BY_EXTENSION = "by_extension";
|
||||
|
@ -39,7 +39,7 @@ public enum Language {
|
||||
//ANY("Any", null, null, null, (String)null),
|
||||
//UNKNOWN("Unknown", null, "unknown", null, (String)null),
|
||||
CPP("C++", null, "cpp", null, "h", "c", "cpp", "cxx", "cc", "C"),
|
||||
FORTRAN("Fortran", null, "fortran", null, "for"),
|
||||
FORTRAN("Fortran", null, "fortran", null, "for", "f", "f66", "f77", "f90"),
|
||||
ECMASCRIPT("Ecmascript", null, "ecmascript", EcmascriptRuleChainVisitor.class, "js"),
|
||||
JAVA("Java", null, "java", JavaRuleChainVisitor.class, "java"),
|
||||
JSP("Java Server Pages", "JSP", "jsp", JspRuleChainVisitor.class, "jsp"),
|
||||
|
@ -200,6 +200,23 @@ public enum LanguageVersion {
|
||||
return versionsAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to retrieve the appropriate enum, given the provided parameters
|
||||
*
|
||||
* @param languageTerseName The LanguageVersion terse name.
|
||||
* @param languageVersion The version of the language requested.
|
||||
* @return A list of versions associated with the terse name.
|
||||
*/
|
||||
public static LanguageVersion findVersionsForLanguageTerseName(String languageTerseName, String languageVersion) {
|
||||
List<LanguageVersion> versionsAvailable = findVersionsForLanguageTerseName(languageTerseName);
|
||||
for ( LanguageVersion version : versionsAvailable ) {
|
||||
if ( version.getVersion().equalsIgnoreCase(languageVersion) )
|
||||
return version;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a comma-separated list of LanguageVersion terse names.
|
||||
* @param languageVersions The language versions.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user