Compare commits

...

92 Commits

Author SHA1 Message Date
Andreas Dangel
46573bb566 [maven-release-plugin] prepare release pmd_releases/5.0.4 2013-05-01 12:21:21 +02:00
Andreas Dangel
da08b0757e pmd: Prepare pmd release 5.0.4 2013-05-01 11:59:42 +02:00
Andreas Dangel
1c0badd429 pmd: fix #1089 When changing priority in a custom ruleset, violations reported twice 2013-05-01 11:40:37 +02:00
Andreas Dangel
e0a9e49560 pmd: fix #794 False positive on PreserveStackTrace with anonymous inner 2013-04-28 10:15:34 +02:00
Andreas Dangel
dba46f2ebd pmd: fix #1063 False+: ArrayIsStoredDirectly 2013-04-28 10:15:26 +02:00
Andreas Dangel
5fb6daea58 pmd: fix #1085 NullPointerException by at net.sourceforge.pmd.lang.java.rule.design.GodClassRule.visit(GodClassRule.java:313) 2013-04-25 19:27:24 +02:00
Andreas Dangel
2d655f4684 pmd: fix #1087 PreserveStackTrace (still) ignores initCause()
one more test, extended documentation example
2013-04-25 18:39:03 +02:00
Andreas Dangel
b77de63950 pmd: fix #1087 PreserveStackTrace (still) ignores initCause() 2013-04-24 21:40:22 +02:00
Andreas Dangel
d347e38622 pmd: fix #1086 Unsupported Element and Attribute in Ant Task Example 2013-04-22 20:31:10 +02:00
Andreas Dangel
7494011999 pmd: fix #254 False+ : UnusedImport with Javadoc @throws 2013-04-15 21:35:51 +02:00
Andreas Dangel
3901ad7ddb pmd: correct documentation for pmd's ant task to select the language
and adding more unit tests.
2013-04-15 21:34:22 +02:00
Andreas Dangel
2e85f1f935 pmd: fix #1080 net.sourceforge.pmd.cpd.CPDTest test failing
The symlinks are simply removed now from the source code and
created by the test, when needed.
The test runs only under unix systems, not under windows.
2013-04-14 17:51:29 +02:00
Andreas Dangel
a5efd66b73 pmd: adding more entries to gitignore and to exclude while creating the source zip file. 2013-04-14 17:49:49 +02:00
Andreas Dangel
a96ccf9741 pmd: fix #1082 CPD performance issue on larger projects 2013-04-14 13:41:31 +02:00
Andreas Dangel
f60128b142 pmd: fix #1081 Regression: CPD skipping all files when using relative paths 2013-04-14 13:41:14 +02:00
Andreas Dangel
89ebc32141 [maven-release-plugin] prepare for next development iteration 2013-04-05 19:31:38 +02:00
Andreas Dangel
b501de3fba [maven-release-plugin] prepare release pmd_releases/5.0.3 2013-04-05 19:31:38 +02:00
Andreas Dangel
79fb7e359d pmd: correct the command line examples 2013-04-05 19:19:14 +02:00
Andreas Dangel
6aa4897e21 pmd: prepare pmd release 5.0.3 2013-04-05 18:52:59 +02:00
Andreas Dangel
ee05fa73cd pmd: use the released pmd-build plugin 0.8 2013-04-05 18:49:11 +02:00
Andreas Dangel
0170a40dec pmd: fix NPE in LooseCouplingRule 2013-04-05 17:29:53 +02:00
Andreas Dangel
4d7b4bb1bc pmd: fix #1078 Package statement introduces false positive UnnecessaryFullyQualifiedName violation 2013-04-05 17:16:52 +02:00
Andreas Dangel
a6c1c1f57c pmd: fix #1077 Missing JavaDocs for Xref-Test Files 2013-03-30 15:46:15 +01:00
Andreas Dangel
9f8246caac pmd: fix #938 False positive on LooseCoupling for overriding methods 2013-03-30 15:46:04 +01:00
Andreas Dangel
f51e41b05c pmd: fix #940 False positive on UnsynchronizedStaticDateFormatter 2013-03-30 15:45:52 +01:00
Andreas Dangel
ad66153a2d pmd: fix #942 CheckResultSet False Positive
* replacing the xpath rule with a java based rule implementation
2013-03-30 13:58:42 +01:00
Andreas Dangel
c8c6aa16dd pmd: fix #943 PreserveStackTrace false positive if a StringBuffer exists 2013-03-30 12:01:42 +01:00
Andreas Dangel
b998e64819 pmd: fix #945 PMD generates RuleSets it cannot read. 2013-03-30 12:01:33 +01:00
Andreas Dangel
62fc70e816 pmd: fix #958 Intermittent NullPointerException while loading XPath node attributes
* synchronizing the method cache
 * adding additional null check
2013-03-30 12:01:14 +01:00
Andreas Dangel
34b868ad14 formatting, white spaces 2013-03-30 12:01:06 +01:00
Andreas Dangel
26a862a51b pmd: update changelog, verify #1076 2013-03-24 20:40:55 +01:00
Philip Graf
4569842965 pmd: fix #1076 Report.treeIterator() does not return all violations
The cause of the bug was the implementation of
ViolationNode.equalsNode(otherNode) which returned true when two
different violations start on the same line.

Note: this change also adds a maven dependency on Mockito 1.9.5 (scope:
test). Mockito helps creating mock objects and makes the test more
readable.
2013-03-24 20:40:20 +01:00
Andreas Dangel
fddccb5030 pmd: update changelog 2013-03-24 20:40:01 +01:00
Peter Kofler
00e27b4a37 fix Nullpointer Exception when using -l jsp 2013-03-24 20:39:52 +01:00
Andreas Dangel
b6c6899741 pmd: fix #1027 PMD Ant: java.lang.ClassCastException 2013-03-17 12:47:45 +01:00
Andreas Dangel
43ecf1e110 pmd: fix #999 Law of Demeter: False positives and negatives 2013-03-17 11:08:16 +01:00
Andreas Dangel
5f45a28621 pmd: fix #975 false positive in ClassCastExceptionWithToArray 2013-03-17 10:20:58 +01:00
Andreas Dangel
41c44e5d8d pmd: fix #968 Issues with JUnit4 @Test annotation with expected exception
Thanks to Yiannis Paschalidis
2013-03-17 10:20:49 +01:00
Andreas Dangel
040d465137 pmd: fix #976 UselessStringValueOf wrong when appending character arrays 2013-03-16 19:55:45 +01:00
Andreas Dangel
4e5ca16990 pmd: fix #977 MisplacedNullCheck makes false positives 2013-03-16 19:21:15 +01:00
Andreas Dangel
30af4f6381 pmd: fix #985 Suppressed methods shouldn't affect avg CyclomaticComplexity 2013-03-16 18:16:29 +01:00
Andreas Dangel
cece698b04 pmd: fix #984 Cyclomatic complexity should treat constructors like methods 2013-03-16 18:16:20 +01:00
Andreas Dangel
13ef401a63 pmd: fix #997 Rule NonThreadSafeSingleton gives analysis problem 2013-03-16 12:26:11 +01:00
Andreas Dangel
589ec3c6da pmd: fix #992 Class java.beans.Statement triggered in CloseResource rule 2013-03-16 12:25:53 +01:00
Andreas Dangel
c00ca9e44c pmd: fix NPE in ConstructorCallsOverridableMethodRule 2013-03-15 21:57:39 +01:00
Andreas Dangel
31f6b9af1a pmd: fix #1032 ImmutableField Rule: Private field in inner class gives false positive
Improved symbol table resolution to check inner classes, too.
2013-03-15 21:37:29 +01:00
Andreas Dangel
8eac048c03 pmd: fix #1005 False + for ConstructorCallsOverridableMethod - overloaded methods
It won't be perfect, but for constructor calls and method calls a
simple check of the argument types is done. Primitive types are
distinguished, reference types not.
In order to support all cases, full symbol resolution is needed.
2013-03-14 20:49:08 +01:00
Andreas Dangel
3b14b3f5cb pmd: #1002 False +: FinalFieldCouldBeStatic on inner class 2013-03-13 21:12:46 +01:00
Andreas Dangel
adcbdc204f pmd: fix #1073 Hard coded violation messages CommentSize 2013-03-10 09:54:34 +01:00
Andreas Dangel
a1b1e3f31c pmd: fix #1074 rule priority doesn't work on group definitions
and verify #1067 Custom Ruleset doesn't import the complete ruleset anymore by reference
2013-03-09 17:56:45 +01:00
Andreas Dangel
3e8c42384b pmd: fixed #1064 Exception running PrematureDeclaration 2013-03-01 22:20:46 +01:00
Andreas Dangel
a00532595a pmd: fixed #1068 CPD fails on broken symbolic links 2013-03-01 20:17:45 +01:00
Andreas Dangel
3feda38cc3 [maven-release-plugin] prepare for next development iteration 2013-02-03 18:35:38 +01:00
Andreas Dangel
3412d55fc6 [maven-release-plugin] prepare release pmd_releases/5.0.2 2013-02-03 18:35:37 +01:00
Andreas Dangel
436b7dcb50 pmd: set release date for 5.0.2 2013-02-03 18:29:37 +01:00
Andreas Dangel
d25dadc4b4 pmd: Prepare pmd release 5.0.2 2013-02-03 15:44:02 +01:00
Andreas Dangel
1c051674a0 pmd: update changelog for 5.0.2 2013-02-03 15:34:58 +01:00
Romain PELISSE
3daca86045 pmd: AntTask support for language
- fix  #1004 targetjdk isn't attribute of PMD task
- introduce a new element, called <language>, that replaces the already existing <version/> element
2013-02-03 15:33:46 +01:00
Romain PELISSE
d95d960de3 pmd: fix #1004 targetjdk isn't attribute of PMD task 2013-02-03 15:33:31 +01:00
Torsten Kleiber
ef7478c350 Fix for bug #1056 "Error while processing" while running on xml file with DOCTYPE reference
Change the initial value of XINCLUDE_AWARE_DESCRIPTOR to false
This does validate an invalid doctype reference as in some ide generated xml files exist.
Any xml rule, which needs this property should overwrite this value with true.
2013-02-03 15:32:54 +01:00
Romain PELISSE
c080fdda08 pmd: fix #1049 Errors in "How to write a rule" 2013-02-03 15:31:21 +01:00
Andreas Dangel
e603844a53 pmd: fix #1030 CPD Java.lang.IndexOutOfBoundsException: Index: 2013-01-26 20:08:54 +01:00
Andreas Dangel
52b8be6601 pmd: fixed #947 CloseResource rule fails if field is marked with annotation 2013-01-26 17:58:31 +01:00
Andreas Dangel
7cbb385dbb pmd: fixed #1011 CloseResource Rule ignores Constructors 2013-01-26 17:57:17 +01:00
Andreas Dangel
61c0b4ea7f pmd: fixed #1007 Parse Exception with annotation 2013-01-26 17:57:11 +01:00
Andreas Dangel
26fa85a9fa pmd: fixed #1046 ant task CPDTask doesn't accept ecmascript 2013-01-26 17:56:51 +01:00
Andreas Dangel
98b903897e pmd: fixed #1039 pmd-nicerhtml.xsl is packaged in wrong location 2013-01-26 17:55:22 +01:00
Andreas Dangel
55b5aa7f27 pmd: update rule-guidelines to changed package names 2013-01-26 17:54:09 +01:00
Andreas Dangel
ffac9894c9 pmd: fixed #1028 False-positive: Compare objects with equals for Enums 2013-01-26 17:54:03 +01:00
Andreas Dangel
d557ed9c01 pmd: verify #1020 Parsing Error 2013-01-26 17:53:56 +01:00
Andreas Dangel
e0b7f6f17c pmd: fixed #913 SignatureDeclareThrowsException is raised twice 2013-01-26 17:53:49 +01:00
Andreas Dangel
cb9861dd1f pmd: fixed #878 False positive: UnusedFormalParameter for abstract methods 2013-01-26 17:53:41 +01:00
Andreas Dangel
7ee96a6b52 pmd: fixed #1055 Please add a colon in the ant output after line,column for Oracle JDeveloper IDE usage 2013-01-26 17:53:33 +01:00
Andreas Dangel
ac8e3314a3 pmd: fixed bug #1012 False positive: Useless parentheses. 2013-01-19 19:13:09 +01:00
Andreas Dangel
7989ae1863 pmd: fixed #1060 GodClassRule >>> wrong method 2013-01-19 19:12:59 +01:00
Andreas Dangel
e3708b5030 pmd: fix #1037 Facing a showstopper issue in PMD Report Class
* wrapping the report listeners in a thread safe (synchronized) class
 * overtaking the listeners for each thread / report
2012-12-17 11:02:24 +01:00
Andreas Dangel
85e493d55f pmd: fixed #1026 PMD doesn't handle 'value =' in SuppressWarnings annotation 2012-12-16 23:12:29 +01:00
Andreas Dangel
6abbea51dd pmd: fix #1043 node.getEndLine() always returns 0 (ECMAscript)
* added commons-io dependency
 * added a SourceCodePositioner to calculate line/column from position
2012-12-16 18:16:39 +01:00
Andreas Dangel
9162917346 pmd: fix #1047 False Positive in 'for' loops for LocalVariableCouldBeFinal in 5.0.1 2012-12-16 12:10:06 +01:00
Andreas Dangel
637006674f pmd: Fixed bug 1048: CommentContent Rule, String Index out of range Exception 2012-12-07 11:17:44 +01:00
Andreas Dangel
63c1a8fe47 pmd: CLI - make sure we display the usage help text
Therefore make the -h option a help option.
Make the auxclasspath option description look nicer - newlines break the layout.
Always display the jcommander usage help text.
And exit with an error code in case there are parameter errors.
2012-12-05 22:02:49 +01:00
Andreas Dangel
063c4228e3 pmd: fix bug #1044 - update suppressing documentation with new cli paramter name 2012-12-05 22:02:40 +01:00
Andreas Dangel
4b0063dfc1 [maven-release-plugin] prepare for next development iteration 2012-11-28 21:43:38 +01:00
Andreas Dangel
13e36974a5 [maven-release-plugin] prepare release pmd_releases/5.0.1 2012-11-28 21:43:36 +01:00
Andreas Dangel
33c7af965c pmd: fix failing unit test 2012-11-28 21:25:56 +01:00
Andreas Dangel
a66bbf2193 pmd: prepare release 5.0.1 2012-11-28 21:02:35 +01:00
Andreas Dangel
38af8f2433 Merge branch 'master' into pmd/5.0.x 2012-11-28 20:42:58 +01:00
Andreas Dangel
a6bcfe2b65 pmd: added missing ssh/scp wagon support for maven3
git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/5.0.x@7669 51baf565-9d33-0410-a72c-fc3788e3496d
2012-05-01 11:41:10 +00:00
Andreas Dangel
6474fde42d [maven-release-plugin] prepare for next development iteration
git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/5.0.x@7668 51baf565-9d33-0410-a72c-fc3788e3496d
2012-05-01 07:43:32 +00:00
Andreas Dangel
8dcde0edb5 [maven-release-plugin] prepare release pmd_release_5_0_0
git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/5.0.x@7666 51baf565-9d33-0410-a72c-fc3788e3496d
2012-05-01 07:42:56 +00:00
Andreas Dangel
70347aedd5 pmd: Prepare pmd release 5.0.0
git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/5.0.x@7665 51baf565-9d33-0410-a72c-fc3788e3496d
2012-05-01 07:38:38 +00:00
Andreas Dangel
eaf1385595 update the version to the usual three components: 5.0.0-SNAPSHOT
git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/5.0.x@7664 51baf565-9d33-0410-a72c-fc3788e3496d
2012-04-30 11:35:40 +00:00
138 changed files with 3468 additions and 703 deletions

2
pmd/.gitignore vendored
View File

@ -3,5 +3,7 @@ bin/
.project
.classpath
.checkstyle
.pmd
.ruleset
.settings/
*.patch

View File

@ -1,4 +1,76 @@
???? ??, 2012 - 5.1.0:
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 +543,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 +607,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.

View File

@ -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.
@ -1914,9 +1922,9 @@ void ForStatement() :
{
"for" "("
(
LOOKAHEAD(Modifiers() Type() <IDENTIFIER> ":")
LOOKAHEAD(LocalVariableDeclaration() ":")
{checkForBadJDK15ForLoopSyntaxArgumentsUsage();}
Modifiers() LocalVariableDeclaration() ":" Expression()
LocalVariableDeclaration() ":" Expression()
|
[ ForInit() ] ";"
[ Expression() ] ";"
@ -1928,7 +1936,7 @@ void ForStatement() :
void ForInit() :
{}
{
LOOKAHEAD( [ "final" ] Type() <IDENTIFIER> )
LOOKAHEAD( LocalVariableDeclaration() )
LocalVariableDeclaration()
|
StatementExpressionList()

View File

@ -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.4</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_releases/5.0.4</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>

View File

@ -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>

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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());
}
/**

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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 {
/*

View File

@ -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 + "\" />";
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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());

View File

@ -16,6 +16,8 @@ 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;
@ -31,6 +33,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 +42,6 @@ public class CPD {
}
public void go() {
TokenEntry.clearImages();
matchAlgorithm = new MatchAlgorithm(
source, tokens,
configuration.minimumTileSize(),
@ -92,11 +95,16 @@ 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);

View File

@ -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;
}
}
}

View File

@ -106,19 +106,22 @@ public class GUI implements CPDListener {
{"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() {
{"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 {

View File

@ -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;
}
}
}
}

View File

@ -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";

View File

@ -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.

View File

@ -119,7 +119,7 @@ public abstract class AbstractNode implements Node {
if (children != null && children.length > 0) {
return children[0].getBeginColumn();
} else {
throw new RuntimeException("Unable to determine begining line of Node.");
throw new RuntimeException("Unable to determine beginning line of Node.");
}
}
}

View File

@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.ast.xpath;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -15,31 +16,32 @@ import net.sourceforge.pmd.lang.ast.Node;
public class AttributeAxisIterator implements Iterator<Attribute> {
private static class MethodWrapper {
public Method method;
public String name;
public Method method;
public String name;
public MethodWrapper(Method m) {
this.method = m;
this.name = truncateMethodName(m.getName());
}
public MethodWrapper(Method m) {
this.method = m;
this.name = truncateMethodName(m.getName());
}
private String truncateMethodName(String n) {
// about 70% of the methods start with 'get', so this case goes first
if (n.startsWith("get")) {
return n.substring("get".length());
}
if (n.startsWith("is")) {
return n.substring("is".length());
}
if (n.startsWith("has")) {
return n.substring("has".length());
}
if (n.startsWith("uses")) {
return n.substring("uses".length());
}
private String truncateMethodName(String n) {
// about 70% of the methods start with 'get', so this case goes
// first
if (n.startsWith("get")) {
return n.substring("get".length());
}
if (n.startsWith("is")) {
return n.substring("is".length());
}
if (n.startsWith("has")) {
return n.substring("has".length());
}
if (n.startsWith("uses")) {
return n.substring("uses".length());
}
return n;
}
return n;
}
}
private Attribute currObj;
@ -47,64 +49,65 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
private int position;
private Node node;
private static Map<Class<?>, MethodWrapper[]> methodCache = new HashMap<Class<?>, MethodWrapper[]>();
private static Map<Class<?>, MethodWrapper[]> methodCache =
Collections.synchronizedMap(new HashMap<Class<?>, MethodWrapper[]>());
public AttributeAxisIterator(Node contextNode) {
this.node = contextNode;
if (!methodCache.containsKey(contextNode.getClass())) {
Method[] preFilter = contextNode.getClass().getMethods();
List<MethodWrapper> postFilter = new ArrayList<MethodWrapper>();
for (Method element : preFilter) {
if (isAttributeAccessor(element)) {
postFilter.add(new MethodWrapper(element));
}
}
methodCache.put(contextNode.getClass(), postFilter.toArray(new MethodWrapper[postFilter.size()]));
}
this.methodWrappers = methodCache.get(contextNode.getClass());
this.node = contextNode;
if (!methodCache.containsKey(contextNode.getClass())) {
Method[] preFilter = contextNode.getClass().getMethods();
List<MethodWrapper> postFilter = new ArrayList<MethodWrapper>();
for (Method element : preFilter) {
if (isAttributeAccessor(element)) {
postFilter.add(new MethodWrapper(element));
}
}
methodCache.put(contextNode.getClass(), postFilter.toArray(new MethodWrapper[postFilter.size()]));
}
this.methodWrappers = methodCache.get(contextNode.getClass());
this.position = 0;
this.currObj = getNextAttribute();
this.position = 0;
this.currObj = getNextAttribute();
}
public Attribute next() {
if (currObj == null) {
throw new IndexOutOfBoundsException();
}
Attribute ret = currObj;
currObj = getNextAttribute();
return ret;
if (currObj == null) {
throw new IndexOutOfBoundsException();
}
Attribute ret = currObj;
currObj = getNextAttribute();
return ret;
}
public boolean hasNext() {
return currObj != null;
return currObj != null;
}
public void remove() {
throw new UnsupportedOperationException();
throw new UnsupportedOperationException();
}
private Attribute getNextAttribute() {
if (position == methodWrappers.length) {
return null;
}
MethodWrapper m = methodWrappers[position++];
return new Attribute(node, m.name, m.method);
if (methodWrappers == null || position == methodWrappers.length) {
return null;
}
MethodWrapper m = methodWrappers[position++];
return new Attribute(node, m.name, m.method);
}
protected boolean isAttributeAccessor(Method method) {
String methodName = method.getName();
String methodName = method.getName();
return (Integer.TYPE == method.getReturnType() || Boolean.TYPE == method.getReturnType()
|| Double.TYPE == method.getReturnType() || String.class == method.getReturnType())
&& method.getParameterTypes().length == 0
&& Void.TYPE != method.getReturnType()
&& !methodName.startsWith("jjt")
&& !methodName.equals("toString")
&& !methodName.equals("getScope")
&& !methodName.equals("getClass")
&& !methodName.equals("getTypeNameNode")
&& !methodName.equals("getImportedNameNode") && !methodName.equals("hashCode");
return (Integer.TYPE == method.getReturnType() || Boolean.TYPE == method.getReturnType()
|| Double.TYPE == method.getReturnType() || String.class == method.getReturnType())
&& method.getParameterTypes().length == 0
&& Void.TYPE != method.getReturnType()
&& !methodName.startsWith("jjt")
&& !methodName.equals("toString")
&& !methodName.equals("getScope")
&& !methodName.equals("getClass")
&& !methodName.equals("getTypeNameNode")
&& !methodName.equals("getImportedNameNode") && !methodName.equals("hashCode");
}
}

View File

@ -1,3 +1,6 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.dfa.report;
import net.sourceforge.pmd.RuleViolation;
@ -23,6 +26,9 @@ public class ViolationNode extends AbstractReportNode {
return rv.getFilename().equals(getRuleViolation().getFilename()) &&
rv.getBeginLine() == getRuleViolation().getBeginLine() &&
rv.getBeginColumn() == getRuleViolation().getBeginColumn() &&
rv.getEndLine() == getRuleViolation().getEndLine() &&
rv.getEndColumn()== getRuleViolation().getEndColumn() &&
rv.getVariableName().equals(getRuleViolation().getVariableName());
}

View File

@ -7,18 +7,27 @@ import net.sourceforge.pmd.lang.ast.AbstractNode;
import org.mozilla.javascript.ast.AstNode;
public abstract class AbstractEcmascriptNode<T extends AstNode> extends AbstractNode implements EcmascriptNode {
public abstract class AbstractEcmascriptNode<T extends AstNode> extends AbstractNode implements EcmascriptNode<T> {
protected final T node;
public AbstractEcmascriptNode(T node) {
super(node.getType());
this.node = node;
this.beginLine = node.getLineno() + 1;
this.beginLine = node.getLineno() + 1;
// TODO Implement positions, or figure out how to do begin/end lines/column
//this.beginPosition = node.getAbsolutePosition();
//this.endPosition = this.beginPosition + node.getLength();
}
/* package private */
void calculateLineNumbers(SourceCodePositioner positioner) {
int startOffset = node.getAbsolutePosition();
int endOffset = startOffset + node.getLength();
this.beginLine = positioner.lineNumberFromOffset(startOffset);
this.beginColumn = positioner.columnFromOffset(startOffset);
this.endLine = positioner.lineNumberFromOffset(endOffset);
this.endColumn = positioner.columnFromOffset(endOffset) - 1; // end column is inclusive
if (this.endColumn < 0) {
this.endColumn = 0;
}
}
/**
@ -34,7 +43,9 @@ public abstract class AbstractEcmascriptNode<T extends AstNode> extends Abstract
public Object childrenAccept(EcmascriptParserVisitor visitor, Object data) {
if (children != null) {
for (int i = 0; i < children.length; ++i) {
((EcmascriptNode) children[i]).jjtAccept(visitor, data);
@SuppressWarnings("unchecked") // we know that the children here are all EcmascriptNodes
EcmascriptNode<T> ecmascriptNode = (EcmascriptNode<T>) children[i];
ecmascriptNode.jjtAccept(visitor, data);
}
}
return data;
@ -52,11 +63,6 @@ public abstract class AbstractEcmascriptNode<T extends AstNode> extends Abstract
return node.hasSideEffects();
}
@Override
public int getBeginColumn() {
return -1;
}
@Override
public String toString() {
return node.shortName();

View File

@ -11,6 +11,7 @@ import java.util.List;
import net.sourceforge.pmd.lang.ast.ParseException;
import net.sourceforge.pmd.lang.ecmascript.EcmascriptParserOptions;
import org.apache.commons.io.IOUtils;
import org.mozilla.javascript.CompilerEnvirons;
import org.mozilla.javascript.Parser;
import org.mozilla.javascript.ast.AstRoot;
@ -24,7 +25,7 @@ public class EcmascriptParser {
this.parserOptions = parserOptions;
}
protected AstRoot parseEcmascript(final Reader reader, final List<ParseProblem> parseProblems) throws ParseException {
protected AstRoot parseEcmascript(final String sourceCode, final List<ParseProblem> parseProblems) throws ParseException {
final CompilerEnvirons compilerEnvirons = new CompilerEnvirons();
compilerEnvirons.setRecordingComments(parserOptions.isRecordingComments());
compilerEnvirons.setRecordingLocalJsDocComments(parserOptions.isRecordingLocalJsDocComments());
@ -35,23 +36,23 @@ public class EcmascriptParser {
// TODO We should do something with Rhino errors...
final ErrorCollector errorCollector = new ErrorCollector();
final Parser parser = new Parser(compilerEnvirons, errorCollector);
// TODO Fix hardcode
final String sourceURI = "unknown";
final int beginLineno = 1;
AstRoot astRoot = parser.parse(sourceCode, sourceURI, beginLineno);
parseProblems.addAll(errorCollector.getErrors());
return astRoot;
}
public EcmascriptNode<AstRoot> parse(final Reader reader) {
try {
// TODO Fix hardcode
final String sourceURI = "unknown";
// TODO Fix hardcode
final int lineno = 0;
AstRoot astRoot = parser.parse(reader, sourceURI, lineno);
parseProblems.addAll(errorCollector.getErrors());
return astRoot;
} catch (final IOException e) {
final List<ParseProblem> parseProblems = new ArrayList<ParseProblem>();
final String sourceCode = IOUtils.toString(reader);
final AstRoot astRoot = parseEcmascript(sourceCode, parseProblems);
final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(sourceCode, parseProblems);
return treeBuilder.build(astRoot);
} catch (IOException e) {
throw new ParseException(e);
}
}
public EcmascriptNode parse(final Reader reader) {
final List<ParseProblem> parseProblems = new ArrayList<ParseProblem>();
final AstRoot astRoot = parseEcmascript(reader, parseProblems);
final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(parseProblems);
return treeBuilder.build(astRoot);
}
}

View File

@ -65,9 +65,9 @@ import org.mozilla.javascript.ast.XmlExpression;
import org.mozilla.javascript.ast.XmlMemberGet;
import org.mozilla.javascript.ast.XmlString;
public class EcmascriptTreeBuilder implements NodeVisitor {
public final class EcmascriptTreeBuilder implements NodeVisitor {
protected static final Map<Class<? extends AstNode>, Constructor<? extends EcmascriptNode>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<Class<? extends AstNode>, Constructor<? extends EcmascriptNode>>();
private static final Map<Class<? extends AstNode>, Constructor<? extends EcmascriptNode<?>>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<Class<? extends AstNode>, Constructor<? extends EcmascriptNode<?>>>();
static {
register(ArrayComprehension.class, ASTArrayComprehension.class);
register(ArrayComprehensionLoop.class, ASTArrayComprehensionLoop.class);
@ -120,7 +120,7 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
register(XmlString.class, ASTXmlString.class);
}
protected static void register(Class<? extends AstNode> nodeType, Class<? extends EcmascriptNode> nodeAdapterType) {
private static <T extends AstNode> void register(Class<T> nodeType, Class<? extends EcmascriptNode<T>> nodeAdapterType) {
try {
NODE_TYPE_TO_NODE_ADAPTER_TYPE.put(nodeType, nodeAdapterType.getConstructor(nodeType));
} catch (SecurityException e) {
@ -139,13 +139,18 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
// The Rhino nodes with children to build.
protected Stack<AstNode> parents = new Stack<AstNode>();
public EcmascriptTreeBuilder(List<ParseProblem> parseProblems) {
private final SourceCodePositioner sourceCodePositioner;
public EcmascriptTreeBuilder(String sourceCode, List<ParseProblem> parseProblems) {
this.sourceCodePositioner = new SourceCodePositioner(sourceCode);
this.parseProblems = parseProblems;
}
protected EcmascriptNode createNodeAdapter(AstNode node) {
private <T extends AstNode> EcmascriptNode<T> createNodeAdapter(T node) {
try {
Constructor<? extends EcmascriptNode> constructor = NODE_TYPE_TO_NODE_ADAPTER_TYPE.get(node.getClass());
@SuppressWarnings("unchecked") // the register function makes sure only EcmascriptNode<T> can be added,
// where T is "T extends AstNode".
Constructor<? extends EcmascriptNode<T>> constructor = (Constructor<? extends EcmascriptNode<T>>) NODE_TYPE_TO_NODE_ADAPTER_TYPE.get(node.getClass());
if (constructor == null) {
throw new IllegalArgumentException("There is no Node adapter class registered for the Node class: "
+ node.getClass());
@ -160,8 +165,10 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
}
}
public EcmascriptNode build(AstNode astNode) {
EcmascriptNode node = buildInternal(astNode);
public <T extends AstNode> EcmascriptNode<T> build(T astNode) {
EcmascriptNode<T> node = buildInternal(astNode);
calculateLineNumbers(node);
// Set all the trailing comma nodes
for (TrailingCommaNode trailingCommaNode : parseProblemToNode.values()) {
@ -171,9 +178,9 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
return node;
}
protected EcmascriptNode buildInternal(AstNode astNode) {
private <T extends AstNode> EcmascriptNode<T> buildInternal(T astNode) {
// Create a Node
EcmascriptNode node = createNodeAdapter(astNode);
EcmascriptNode<T> node = createNodeAdapter(astNode);
// Append to parent
Node parent = nodes.isEmpty() ? null : nodes.peek();
@ -203,7 +210,7 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
}
}
private void handleParseProblems(EcmascriptNode node) {
private void handleParseProblems(EcmascriptNode<? extends AstNode> node) {
if (node instanceof TrailingCommaNode) {
TrailingCommaNode trailingCommaNode = (TrailingCommaNode) node;
int nodeStart = node.getNode().getAbsolutePosition();
@ -216,7 +223,7 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
if ("Trailing comma is not legal in an ECMA-262 object initializer".equals(parseProblem.getMessage())) {
// Report on the shortest code block containing the
// problem (i.e. inner most code in nested structures).
EcmascriptNode currentNode = (EcmascriptNode) parseProblemToNode.get(parseProblem);
EcmascriptNode<? extends AstNode> currentNode = (EcmascriptNode<? extends AstNode>) parseProblemToNode.get(parseProblem);
if (currentNode == null || node.getNode().getLength() < currentNode.getNode().getLength()) {
parseProblemToNode.put(parseProblem, trailingCommaNode);
}
@ -225,4 +232,15 @@ public class EcmascriptTreeBuilder implements NodeVisitor {
}
}
}
private void calculateLineNumbers(EcmascriptNode<?> node) {
EcmascriptParserVisitorAdapter visitor = new EcmascriptParserVisitorAdapter() {
@Override
public Object visit(EcmascriptNode node, Object data) {
((AbstractEcmascriptNode<?>)node).calculateLineNumbers(sourceCodePositioner);
return super.visit(node, data); // also visit the children
}
};
node.jjtAccept(visitor, null);
}
}

View File

@ -0,0 +1,56 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.ecmascript.ast;
import java.util.Arrays;
/**
* Calculates from an absolute offset in the source file the line/column coordinate.
* This is needed as Rhino only offers absolute positions for each node.
*
* Idea from: http://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/javascript/jscomp/SourceFile.java
*/
public class SourceCodePositioner {
private int[] lineOffsets;
public SourceCodePositioner(String sourceCode) {
analyzeLineOffsets(sourceCode);
}
private void analyzeLineOffsets(String sourceCode) {
String[] lines = sourceCode.split("\n");
int startOffset = 0;
int lineNumber = 0;
lineOffsets = new int[lines.length];
for (String line : lines) {
lineOffsets[lineNumber] = startOffset;
lineNumber++;
startOffset += line.length() + 1; // +1 for the "\n" character
}
}
public int lineNumberFromOffset(int offset) {
int search = Arrays.binarySearch(lineOffsets, offset);
int lineNumber;
if (search >= 0) {
lineNumber = search;
} else {
int insertionPoint = search;
insertionPoint += 1;
insertionPoint *= -1;
lineNumber = insertionPoint - 1; // take the insertion point one before
}
return lineNumber + 1; // 1-based line numbers
}
public int columnFromOffset(int offset) {
int lineNumber = lineNumberFromOffset(offset);
int columnOffset = offset - lineOffsets[lineNumber - 1];
return columnOffset + 1; // 1-based column offsets
}
}

Some files were not shown because too many files have changed in this diff Show More