Refactor RuleSet creation

- RuleSet is now immutable
 - RuleSets are created through a RuleSetBuilder
 - RuleSetBuilder is accessed solely from RuleSetFactory
 - RuleSetFactory can now either parse XMLs for rule set creation,
    or create single rule rulesets
This commit is contained in:
Juan Martín Sotuyo Dodero
2016-11-30 15:25:19 -03:00
parent 555266b1b1
commit 1d6c9327a0
14 changed files with 664 additions and 706 deletions

File diff suppressed because it is too large Load Diff

View File

@ -29,11 +29,13 @@ import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import net.sourceforge.pmd.RuleSet.RuleSetBuilder;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.rule.MockRule;
import net.sourceforge.pmd.lang.rule.RuleReference;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.properties.PropertyDescriptorWrapper;
import net.sourceforge.pmd.lang.rule.properties.factories.PropertyDescriptorUtil;
import net.sourceforge.pmd.util.ResourceLoader;
@ -194,6 +196,25 @@ public class RuleSetFactory {
return parseRuleSetNode(ruleSetReferenceId, withDeprecatedRuleReferences);
}
/**
* Creates a new RuleSet for a single rule
* @param rule The rule being created
* @return The newly created RuleSet
*/
public RuleSet createSingleRuleRuleSet(final Rule rule) {
final long checksum;
if (rule instanceof XPathRule) {
checksum = rule.getProperty(XPathRule.XPATH_DESCRIPTOR).hashCode();
} else {
// TODO : Is this good enough? all properties' values + rule name
checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode();
}
final RuleSetBuilder builder = new RuleSetBuilder(checksum);
builder.addRule(rule);
return builder.build();
}
/**
* Create a Rule from a RuleSet created from a file name resource. The
* currently configured ClassLoader is used.
@ -248,9 +269,9 @@ public class RuleSetFactory {
Document document = builder.parse(inputSource);
Element ruleSetElement = document.getDocumentElement();
RuleSet ruleSet = new RuleSet(inputStream.getChecksum().getValue());
ruleSet.setFileName(ruleSetReferenceId.getRuleSetFileName());
ruleSet.setName(ruleSetElement.getAttribute("name"));
RuleSetBuilder ruleSetBuilder = new RuleSetBuilder(inputStream.getChecksum().getValue())
.withFileName(ruleSetReferenceId.getRuleSetFileName())
.withName(ruleSetElement.getAttribute("name"));
NodeList nodeList = ruleSetElement.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
@ -258,13 +279,13 @@ public class RuleSetFactory {
if (node.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = node.getNodeName();
if (DESCRIPTION.equals(nodeName)) {
ruleSet.setDescription(parseTextNode(node));
ruleSetBuilder.withDescription(parseTextNode(node));
} else if ("include-pattern".equals(nodeName)) {
ruleSet.addIncludePattern(parseTextNode(node));
ruleSetBuilder.addIncludePattern(parseTextNode(node));
} else if ("exclude-pattern".equals(nodeName)) {
ruleSet.addExcludePattern(parseTextNode(node));
ruleSetBuilder.addExcludePattern(parseTextNode(node));
} else if ("rule".equals(nodeName)) {
parseRuleNode(ruleSetReferenceId, ruleSet, node, withDeprecatedRuleReferences);
parseRuleNode(ruleSetReferenceId, ruleSetBuilder, node, withDeprecatedRuleReferences);
} else {
throw new IllegalArgumentException(UNEXPECTED_ELEMENT + node.getNodeName()
+ "> encountered as child of <ruleset> element.");
@ -272,7 +293,7 @@ public class RuleSetFactory {
}
}
return ruleSet;
return ruleSetBuilder.build();
} catch (ClassNotFoundException cnfe) {
return classNotFoundProblem(cnfe);
} catch (InstantiationException ie) {
@ -298,22 +319,22 @@ public class RuleSetFactory {
*
* @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being
* parsed.
* @param ruleSet The RuleSet being constructed.
* @param ruleSetBuilder The RuleSet being constructed.
* @param ruleNode Must be a rule element node.
* @param withDeprecatedRuleReferences whether rule references that are
* deprecated should be ignored or not
*/
private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode,
private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode,
boolean withDeprecatedRuleReferences) throws ClassNotFoundException, InstantiationException,
IllegalAccessException, RuleSetNotFoundException {
Element ruleElement = (Element) ruleNode;
String ref = ruleElement.getAttribute("ref");
if (ref.endsWith("xml")) {
parseRuleSetReferenceNode(ruleSetReferenceId, ruleSet, ruleElement, ref);
parseRuleSetReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleElement, ref);
} else if (StringUtil.isEmpty(ref)) {
parseSingleRuleNode(ruleSetReferenceId, ruleSet, ruleNode);
parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode);
} else {
parseRuleReferenceNode(ruleSetReferenceId, ruleSet, ruleNode, ref, withDeprecatedRuleReferences);
parseRuleReferenceNode(ruleSetReferenceId, ruleSetBuilder, ruleNode, ref, withDeprecatedRuleReferences);
}
}
@ -325,11 +346,11 @@ public class RuleSetFactory {
*
* @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being
* parsed.
* @param ruleSet The RuleSet being constructed.
* @param ruleSetBuilder The RuleSet being constructed.
* @param ruleElement Must be a rule element node.
* @param ref The RuleSet reference.
*/
private void parseRuleSetReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Element ruleElement,
private void parseRuleSetReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Element ruleElement,
String ref) throws RuleSetNotFoundException {
RuleSetReference ruleSetReference = new RuleSetReference();
ruleSetReference.setAllRules(true);
@ -357,7 +378,7 @@ public class RuleSetFactory {
RuleReference ruleReference = new RuleReference();
ruleReference.setRuleSetReference(ruleSetReference);
ruleReference.setRule(rule);
ruleSet.addRuleIfNotExists(ruleReference);
ruleSetBuilder.addRuleIfNotExists(ruleReference);
// override the priority
if (priority != null) {
@ -377,10 +398,10 @@ public class RuleSetFactory {
*
* @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being
* parsed.
* @param ruleSet The RuleSet being constructed.
* @param ruleSetBuilder The RuleSet being constructed.
* @param ruleNode Must be a rule element node.
*/
private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode)
private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Element ruleElement = (Element) ruleNode;
@ -452,7 +473,7 @@ public class RuleSetFactory {
rule.setSince(since);
}
rule.setMessage(ruleElement.getAttribute(MESSAGE));
rule.setRuleSetName(ruleSet.getName());
rule.setRuleSetName(ruleSetBuilder.getName());
rule.setExternalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL));
if (hasAttributeSetTrue(ruleElement, "dfa")) {
@ -486,7 +507,7 @@ public class RuleSetFactory {
}
if (StringUtil.isNotEmpty(ruleSetReferenceId.getRuleName())
|| rule.getPriority().compareTo(minimumPriority) <= 0) {
ruleSet.addRule(rule);
ruleSetBuilder.addRule(rule);
}
}
@ -501,13 +522,13 @@ public class RuleSetFactory {
*
* @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being
* parsed.
* @param ruleSet The RuleSet being constructed.
* @param ruleSetBuilder The RuleSet being constructed.
* @param ruleNode Must be a rule element node.
* @param ref A reference to a Rule.
* @param withDeprecatedRuleReferences whether rule references that are
* deprecated should be ignored or not
*/
private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode,
private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode,
String ref, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException {
Element ruleElement = (Element) ruleNode;
@ -603,7 +624,7 @@ public class RuleSetFactory {
if (StringUtil.isNotEmpty(ruleSetReferenceId.getRuleName())
|| referencedRule.getPriority().compareTo(minimumPriority) <= 0) {
if (withDeprecatedRuleReferences || !isSameRuleSet || !ruleReference.isDeprecated()) {
ruleSet.addRuleReplaceIfExists(ruleReference);
ruleSetBuilder.addRuleReplaceIfExists(ruleReference);
}
}
}

View File

@ -159,13 +159,13 @@ public class Benchmarker {
*/
private static void stress(LanguageVersion languageVersion, RuleSet ruleSet, List<DataSource> dataSources, Set<RuleDuration> results, boolean debug) throws PMDException, IOException {
final RuleSetFactory factory = new RuleSetFactory();
for (Rule rule: ruleSet.getRules()) {
if (debug) {
System.out.println("Starting " + rule.getName());
}
RuleSet working = new RuleSet();
working.addRule(rule);
final RuleSet working = factory.createSingleRuleRuleSet(rule);
RuleSets ruleSets = new RuleSets(working);
PMDConfiguration config = new PMDConfiguration();

View File

@ -0,0 +1,19 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cache;
/**
* Interface defining an object that has a checksum
* The checksum is a fingerprint of the object's configuration,
* and *MUST* change if anything changed on the object.
* It differs from a {@code hashCode()} in that a {@code hashCode()} may not
* take all fields into account, but a checksum must do so.
*/
public interface ChecksumAware {
/**
* Retrieves the current instance checksum
* @return The current checksum
*/
long getChecksum();
}

View File

@ -1,79 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cli;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.util.StringUtil;
/**
* To use this, do this:
*
* $ cat ~/tmp/Test.java
* package foo;
* public class Test {
* private int x;
* }
* $ java net.sourceforge.pmd.util.XPathTest -xpath "//FieldDeclaration" -filename "/home/tom/tmp/Test.java"
* Match at line 3 column 11; package name 'foo'; variable name 'x'
*/
public class XPathCLI {
private static final Language LANGUAGE = LanguageRegistry.getLanguage("Java");
public static void main(String[] args) throws Exception {
if (args.length != 4) {
System.err.println("Wrong arguments.\n");
System.err.println("Example:");
System.err.println("java " + XPathCLI.class.getName() + " -xpath \"//FieldDeclaration\" -filename \"/home/user/Test.java\"");
System.exit(1);
}
String xpath = args[0].equals("-xpath") ? args[1] : args[3];
String filename = args[0].equals("-file") ? args[1] : args[3];
Rule rule = new XPathRule(xpath);
rule.setMessage("Got one!");
rule.setLanguage(LANGUAGE);
RuleSet ruleSet = RuleSet.createFor("", rule);
RuleContext ctx = PMD.newRuleContext(filename, new File(filename));
ctx.setLanguageVersion(LANGUAGE.getDefaultVersion());
PMDConfiguration config = new PMDConfiguration();
config.setDefaultLanguageVersion(LANGUAGE.getDefaultVersion());
new SourceCodeProcessor(config).processSourceCode(new FileReader(filename), new RuleSets(ruleSet), ctx);
for (Iterator<RuleViolation> i = ctx.getReport().iterator(); i.hasNext();) {
RuleViolation rv = i.next();
StringBuilder sb = new StringBuilder(60)
.append("Match at line ").append(rv.getBeginLine())
.append(" column ").append(rv.getBeginColumn());
if (StringUtil.isNotEmpty(rv.getPackageName())) {
sb.append("; package name '" + rv.getPackageName() + "'");
}
if (StringUtil.isNotEmpty(rv.getMethodName())) {
sb.append("; method name '" + rv.getMethodName() + "'");
}
if (StringUtil.isNotEmpty(rv.getVariableName())) {
sb.append("; variable name '" + rv.getVariableName() + "'");
}
System.out.println(sb.toString());
}
}
}

View File

@ -101,6 +101,7 @@ import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.lang.LanguageRegistry;
@ -560,25 +561,22 @@ public class Designer implements ClipboardOwner {
LanguageVersion languageVersion = getLanguageVersion();
DFAGraphRule dfaGraphRule = languageVersion.getLanguageVersionHandler().getDFAGraphRule();
RuleSet rs = new RuleSet();
if (dfaGraphRule != null) {
rs.addRule(dfaGraphRule);
}
RuleContext ctx = new RuleContext();
ctx.setSourceCodeFilename("[no filename]." + languageVersion.getLanguage().getExtensions().get(0));
StringReader reader = new StringReader(codeEditorPane.getText());
PMDConfiguration config = new PMDConfiguration();
config.setDefaultLanguageVersion(languageVersion);
final RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(dfaGraphRule);
RuleContext ctx = new RuleContext();
ctx.setSourceCodeFilename("[no filename]." + languageVersion.getLanguage().getExtensions().get(0));
StringReader reader = new StringReader(codeEditorPane.getText());
PMDConfiguration config = new PMDConfiguration();
config.setDefaultLanguageVersion(languageVersion);
try {
new SourceCodeProcessor(config).processSourceCode(reader, new RuleSets(rs), ctx);
// } catch (PMDException pmde) {
// loadTreeData(new ExceptionNode(pmde));
} catch (Exception e) {
e.printStackTrace();
}
try {
new SourceCodeProcessor(config).processSourceCode(reader, new RuleSets(rs), ctx);
// } catch (PMDException pmde) {
// loadTreeData(new ExceptionNode(pmde));
} catch (Exception e) {
e.printStackTrace();
}
if (dfaGraphRule != null) {
List<DFAGraphMethod> methods = dfaGraphRule.getMethods();
if (methods != null && !methods.isEmpty()) {
dfaPanel.resetTo(methods, codeEditorPane);
@ -611,8 +609,7 @@ public class Designer implements ClipboardOwner {
xpathRule.setXPath(xpathQueryArea.getText());
xpathRule.setVersion(xpathVersionButtonGroup.getSelection().getActionCommand());
RuleSet ruleSet = new RuleSet();
ruleSet.addRule(xpathRule);
final RuleSet ruleSet = new RuleSetFactory().createSingleRuleRuleSet(xpathRule);
RuleSets ruleSets = new RuleSets(ruleSet);

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,10 @@
package net.sourceforge.pmd;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import net.sourceforge.pmd.lang.rule.RuleReference;
import net.sourceforge.pmd.RuleSet.RuleSetBuilder;
import org.junit.After;
import org.junit.Assert;
@ -46,9 +48,10 @@ public class RuleSetWriterTest {
*/
@Test
public void testWrite() throws Exception {
RuleSet ruleSet = new RuleSet();
RuleSet braces = new RuleSetFactory().createRuleSet("net/sourceforge/pmd/TestRuleset1.xml");
ruleSet.addRuleSetByReference(braces, true, "MockRule2");
RuleSet ruleSet = new RuleSetBuilder(new Random().nextLong())
.addRuleSetByReference(braces, true, "MockRule2")
.build();
writer.write(ruleSet);
@ -72,8 +75,7 @@ public class RuleSetWriterTest {
ruleRef.setRuleSetReference(ruleSetReference);
ruleRef.setName("Foo"); // override the name
RuleSet ruleSet = new RuleSet();
ruleSet.addRule(ruleRef);
RuleSet ruleSet = ruleSetFactory.createSingleRuleRuleSet(ruleRef);
writer.write(ruleSet);

View File

@ -8,15 +8,15 @@ import static org.junit.Assert.assertTrue;
import java.io.StringReader;
import org.junit.Before;
import org.junit.Test;
import junit.framework.JUnit4TestAdapter;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
import net.sourceforge.pmd.testframework.RuleTst;
import net.sourceforge.pmd.testframework.TestDescriptor;
import org.junit.Before;
import org.junit.Test;
public class ExcludeLinesTest extends RuleTst {
private Rule rule;
@ -41,8 +41,7 @@ import org.junit.Test;
ctx.setReport(r);
ctx.setSourceCodeFilename("n/a");
ctx.setLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion());
RuleSet rules = new RuleSet();
rules.addRule(rule);
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST3), new RuleSets(rules), ctx);
assertTrue(r.isEmpty());
assertEquals(r.getSuppressedRuleViolations().size(), 1);

View File

@ -1,34 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cli;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.Assert;
import org.junit.Test;
public class XPathCLITest {
@Test
public void runXPath() throws Exception {
PrintStream oldOut = System.out;
ByteArrayOutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output));
try {
XPathCLI.main(new String[] {
"-xpath",
"//ClassOrInterfaceDeclaration",
"-filename",
"src/test/java/net/sourceforge/pmd/cli/XPathCLITest.java"
});
System.out.flush();
} finally {
System.setOut(oldOut);
}
Assert.assertTrue(output.toString("UTF-8").startsWith("Match at line "));
}
}

View File

@ -9,11 +9,15 @@ import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PropertyDescriptor;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.LanguageRegistry;
@ -30,9 +34,6 @@ import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
import net.sourceforge.pmd.testframework.RuleTst;
import org.junit.Before;
import org.junit.Test;
/**
* @author daniels
*/
@ -56,8 +57,7 @@ import org.junit.Test;
Report report = new Report();
ctx.setReport(report);
ctx.setSourceCodeFilename("n/a");
RuleSet rules = new RuleSet();
rules.addRule(rule);
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST1), new RuleSets(rules), ctx);
RuleViolation rv = report.iterator().next();
assertEquals("a", rv.getDescription());
@ -75,8 +75,7 @@ import org.junit.Test;
Report report = new Report();
ctx.setReport(report);
ctx.setSourceCodeFilename("n/a");
RuleSet rules = new RuleSet();
rules.addRule(rule);
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST2), new RuleSets(rules), ctx);
RuleViolation rv = report.iterator().next();
assertEquals(3, rv.getBeginLine());

View File

@ -12,6 +12,7 @@ import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.LanguageRegistry;
@ -34,8 +35,7 @@ public class XPathJspRuleTest extends RuleTst {
Rule rule = new XPathRule(XPATH_EXPRESSION);
rule.setMessage("Test");
rule.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME));
RuleSet rules = new RuleSet();
rules.addRule(rule);
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
RuleContext ctx = new RuleContext();
Report report = new Report();

View File

@ -214,8 +214,7 @@ public abstract class RuleTst {
ctx.setSourceCodeFilename("n/a");
ctx.setLanguageVersion(languageVersion);
ctx.setIgnoreExceptions(false);
RuleSet rules = new RuleSet();
rules.addRule(rule);
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
rules.start(ctx);
p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx);
rules.end(ctx);

View File

@ -41,7 +41,8 @@ public class RuleTstTest {
verify(rule).getMinimumLanguageVersion();
verify(rule).getMaximumLanguageVersion();
verify(rule).apply(anyList(), any(RuleContext.class));
verify(rule).getName();
verify(rule, times(2)).getName();
verify(rule).getPropertiesByPropertyDescriptor();
verifyNoMoreInteractions(rule);
}
}