Compare commits

...

117 Commits

Author SHA1 Message Date
Pelisse Romain
80148e6b13 Merge pull request #31 from hgschmie/npe-fix
Fix NPE with empty rulesets.
2013-11-26 16:50:11 -08:00
Pelisse Romain
5a07ad8caa Merge pull request #30 from hgschmie/pmd-5.0.x
Add a no args constructor to CPDConfiguration.
2013-11-26 14:22:11 -08:00
Henning Schmiedehausen
957afd6625 Fix NPE with empty rulesets.
This is the fix for bug #1155 (https://sourceforge.net/p/pmd/bugs/1155/)

The whole code is in dire need of revisiting its behaviour when a
value is null or empty. Same goes for e.g. createRules whether it
should return null values (then the caller will have to deal with it)
or empty Rulesets.

Catching and discarding the rule not found exception looks pretty
suspicious.
2013-11-26 11:00:49 -08:00
Henning Schmiedehausen
14e1f8bb81 Add a no args constructor to CPDConfiguration.
This allows API users to build a path forward from the 5.0.x versions
of the API to 5.1.x.
2013-11-26 10:26:14 -08:00
Andreas Dangel
973fd1c4d4 pmd: add changelog and test for #1144 2013-10-29 19:25:02 +01:00
Pelisse Romain
678d3e8a1b Merge pull request #27 from uncletall/pmd/5.0.x
cpd: fix #1144 CPD encoding argument has no effect
2013-10-29 09:35:17 -07:00
Peter Bruin
33413371f6 cpd: fix #1144 CPD encoding argument has no effect
Make sure the encoding is set before creating the renderer
2013-10-28 15:47:59 +08:00
Andreas Dangel
c9da879840 [maven-release-plugin] prepare for next development iteration 2013-08-11 11:03:53 +02:00
Andreas Dangel
030795ea95 [maven-release-plugin] prepare release pmd_releases/5.0.5 2013-08-11 11:03:52 +02:00
Andreas Dangel
1dc6d7e698 pmd: Prepare pmd release 5.0.5 2013-08-11 10:25:41 +02:00
Andreas Dangel
5c92fdd31a pmd: fix #1095 AvoidFinalLocalVariable false positive 2013-08-11 09:29:52 +02:00
Andreas Dangel
9a81bcf7dd pmd: fix #1069 Eclipse plugin does not accept project-local config
This is about the RuleSetReferenceId and how we resolve such a
id to a real ruleset.
It is mostly backwards compatible.
2013-08-07 19:20:48 +02:00
Andreas Dangel
fb426227e4 pmd: fix #1114 CPD - Tokenizer not initialized with requested properties
There was a similar problem with CPD GUI.
Also resetting the properties, so that unchecking the checkboxes has actually an effect.
2013-08-05 08:51:27 +02:00
Andreas Dangel
acf8db926d pmd: fix #1091 file extension for fortran seems to be wrong in cpdgui tools 2013-08-03 22:29:03 +02:00
Andreas Dangel
91f2137679 pmd: fix #1102 False positive: shift operator parenthesis 2013-08-03 22:28:55 +02:00
Andreas Dangel
cc095674b0 pmd: fix #1099 UseArraysAsList false positives 2013-08-03 22:28:43 +02:00
Andreas Dangel
0859285b8b pmd: fixed #1107 PMD 5.0.4 couldn't parse call of parent outer java class method from inner class 2013-08-03 22:28:34 +02:00
Andreas Dangel
d0c103ab62 pmd: fix #1092 Wrong Attribute "excludemarker" in Ant Task Documentation 2013-08-03 22:28:08 +02:00
Andreas Dangel
e76947b297 pmd: fix #1084 NPE at UselessStringValueOfRule.java:36 2013-08-03 22:27:58 +02:00
Andreas Dangel
a417f0003f pmd: fix #1104 IdempotentOperation false positive 2013-08-03 22:27:38 +02:00
Andreas Dangel
dc64e2df77 pmd: fix #1111 False positive: Useless parentheses 2013-08-03 22:26:52 +02:00
Andreas Dangel
829a939421 pmd: fix #1118 ClassCastException in pmd.lang.ecmascript.ast.ASTElementGet 2013-08-03 14:23:43 +02:00
Andreas Dangel
eb57fdc882 pmd: fix #991 AvoidSynchronizedAtMethodLevel for static methods 2013-08-03 13:08:26 +02:00
Andreas Dangel
da1ee1fe72 pmd: fix #1114 CPD - Tokenizer not initialized with requested properties 2013-08-03 12:28:25 +02:00
Andreas Dangel
ee39aa5f3c [maven-release-plugin] prepare for next development iteration 2013-05-01 12:21:21 +02:00
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
161 changed files with 4020 additions and 828 deletions

2
pmd/.gitignore vendored
View File

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

View File

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

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

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

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

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

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

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

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

@ -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"),

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.

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