forked from phoedos/pmd
Make Saxon support multival properties as XPath sequences
This commit is contained in:
@ -14,12 +14,9 @@ import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.saxon.DocumentNode;
|
||||
import net.sourceforge.pmd.lang.ast.xpath.saxon.ElementNode;
|
||||
import net.sourceforge.pmd.lang.xpath.Initializer;
|
||||
import net.sourceforge.pmd.properties.BooleanProperty;
|
||||
import net.sourceforge.pmd.properties.EnumeratedProperty;
|
||||
import net.sourceforge.pmd.properties.IntegerProperty;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.properties.StringProperty;
|
||||
|
||||
import net.sf.saxon.om.Item;
|
||||
import net.sf.saxon.om.ValueRepresentation;
|
||||
import net.sf.saxon.sxpath.AbstractStaticContext;
|
||||
import net.sf.saxon.sxpath.IndependentContext;
|
||||
@ -29,9 +26,16 @@ import net.sf.saxon.sxpath.XPathExpression;
|
||||
import net.sf.saxon.sxpath.XPathStaticContext;
|
||||
import net.sf.saxon.sxpath.XPathVariable;
|
||||
import net.sf.saxon.trans.XPathException;
|
||||
import net.sf.saxon.value.BigIntegerValue;
|
||||
import net.sf.saxon.value.BooleanValue;
|
||||
import net.sf.saxon.value.DoubleValue;
|
||||
import net.sf.saxon.value.EmptySequence;
|
||||
import net.sf.saxon.value.FloatValue;
|
||||
import net.sf.saxon.value.Int64Value;
|
||||
import net.sf.saxon.value.SequenceExtent;
|
||||
import net.sf.saxon.value.StringValue;
|
||||
import net.sf.saxon.value.UntypedAtomicValue;
|
||||
|
||||
|
||||
/**
|
||||
* This is a Saxon based XPathRule query.
|
||||
@ -83,31 +87,7 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
|
||||
String name = xpathVariable.getVariableQName().getLocalName();
|
||||
for (Map.Entry<PropertyDescriptor<?>, Object> entry : super.properties.entrySet()) {
|
||||
if (name.equals(entry.getKey().name())) {
|
||||
PropertyDescriptor<?> propertyDescriptor = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
ValueRepresentation valueRepresentation;
|
||||
|
||||
// TODO Need to handle null values?
|
||||
// TODO Need to handle more PropertyDescriptors, is
|
||||
// there an easy factory in Saxon we can use for this?
|
||||
if (propertyDescriptor instanceof StringProperty) {
|
||||
valueRepresentation = new StringValue((String) value);
|
||||
} else if (propertyDescriptor instanceof BooleanProperty) {
|
||||
valueRepresentation = BooleanValue.get((Boolean) value);
|
||||
} else if (propertyDescriptor instanceof IntegerProperty) {
|
||||
valueRepresentation = Int64Value.makeIntegerValue((Integer) value);
|
||||
} else if (propertyDescriptor instanceof EnumeratedProperty) {
|
||||
if (value instanceof String) {
|
||||
valueRepresentation = new StringValue((String) value);
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Unable to create ValueRepresentaton for non-String EnumeratedProperty value: "
|
||||
+ value);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unable to create ValueRepresentaton for PropertyDescriptor: "
|
||||
+ propertyDescriptor);
|
||||
}
|
||||
ValueRepresentation valueRepresentation = getRepresentation(entry.getKey(), entry.getValue());
|
||||
xpathDynamicContext.setVariable(xpathVariable, valueRepresentation);
|
||||
}
|
||||
}
|
||||
@ -123,6 +103,49 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private ValueRepresentation getRepresentation(PropertyDescriptor<?> descriptor, Object value) {
|
||||
if (descriptor.isMultiValue()) {
|
||||
List<?> val = (List<?>) value;
|
||||
if (val.isEmpty()) {
|
||||
return EmptySequence.getInstance();
|
||||
}
|
||||
Item[] converted = new Item[val.size()];
|
||||
for (int i = 0; i < val.size(); i++) {
|
||||
converted[i] = getItemRepresentation(val.get(i));
|
||||
}
|
||||
return new SequenceExtent(converted);
|
||||
} else {
|
||||
return getItemRepresentation(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Item getItemRepresentation(Object value) {
|
||||
if (value == null) {
|
||||
return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
|
||||
} else if (value instanceof String) {
|
||||
return new StringValue((String) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
return BooleanValue.get((Boolean) value);
|
||||
} else if (value instanceof Integer) {
|
||||
return Int64Value.makeIntegerValue((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return new BigIntegerValue((Long) value);
|
||||
} else if (value instanceof Double) {
|
||||
return new DoubleValue((Double) value);
|
||||
} else if (value instanceof Character) {
|
||||
return new StringValue(value.toString());
|
||||
} else if (value instanceof Float) {
|
||||
return new FloatValue((Float) value);
|
||||
} else {
|
||||
// We could maybe use UntypedAtomicValue
|
||||
throw new RuntimeException("Unable to create ValueRepresentation "
|
||||
+ "for value of type: " + (value == null ? null : value.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DocumentNode getDocumentNode(Node node) {
|
||||
// Get the root AST node
|
||||
Node root = node;
|
||||
|
@ -39,7 +39,7 @@ public class PropertyDescriptorUtil {
|
||||
temp.put("List[Float]", FloatMultiProperty.extractor());
|
||||
temp.put("Double", DoubleProperty.extractor());
|
||||
temp.put("List[Double]", DoubleMultiProperty.extractor());
|
||||
// temp.put("Enum", EnumeratedProperty.FACTORY); // TODO:cf implement that
|
||||
// temp.put("Enum", EnumeratedProperty.FACTORY); // TODO:cf we need new syntax in the xml to support that
|
||||
// temp.put("List[Enum]", EnumeratedMultiProperty.FACTORY);
|
||||
|
||||
temp.put("Class", TypeProperty.extractor());
|
||||
|
@ -8,13 +8,16 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.PMDException;
|
||||
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;
|
||||
@ -32,6 +35,7 @@ import net.sourceforge.pmd.lang.rule.xpath.JaxenXPathRuleQuery;
|
||||
import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery;
|
||||
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.properties.StringMultiProperty;
|
||||
import net.sourceforge.pmd.properties.StringProperty;
|
||||
import net.sourceforge.pmd.testframework.RuleTst;
|
||||
|
||||
@ -53,17 +57,36 @@ public class XPathRuleTest extends RuleTst {
|
||||
public void testPluginname() throws Exception {
|
||||
rule.setXPath("//VariableDeclaratorId[string-length(@Image) < 3]");
|
||||
rule.setMessage("{0}");
|
||||
PMD p = new PMD();
|
||||
RuleContext ctx = new RuleContext();
|
||||
Report report = new Report();
|
||||
ctx.setReport(report);
|
||||
ctx.setSourceCodeFilename("n/a");
|
||||
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
|
||||
p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST1), new RuleSets(rules), ctx);
|
||||
Report report = getReportForTestString(rule, TEST1);
|
||||
RuleViolation rv = report.iterator().next();
|
||||
assertEquals("a", rv.getDescription());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testXPathMultiProperty() throws Exception {
|
||||
rule.setXPath("//VariableDeclaratorId[@Image=$forbiddenNames]");
|
||||
rule.setMessage("Avoid vars");
|
||||
rule.setVersion(XPathRuleQuery.XPATH_2_0);
|
||||
StringMultiProperty varDescriptor
|
||||
= StringMultiProperty.named("forbiddenNames")
|
||||
.desc("Forbidden names")
|
||||
.defaultValues("forbid1", "forbid2")
|
||||
.delim('$')
|
||||
.build();
|
||||
|
||||
rule.definePropertyDescriptor(varDescriptor);
|
||||
|
||||
Report report = getReportForTestString(rule, TEST3);
|
||||
Iterator<RuleViolation> rv = report.iterator();
|
||||
int i = 0;
|
||||
for (; rv.hasNext(); ++i) {
|
||||
rv.next();
|
||||
}
|
||||
assertEquals(2, i);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testVariables() throws Exception {
|
||||
rule.setXPath("//VariableDeclaratorId[@Image=$var]");
|
||||
@ -71,17 +94,12 @@ public class XPathRuleTest extends RuleTst {
|
||||
StringProperty varDescriptor = new StringProperty("var", "Test var", null, 1.0f);
|
||||
rule.definePropertyDescriptor(varDescriptor);
|
||||
rule.setProperty(varDescriptor, "fiddle");
|
||||
PMD p = new PMD();
|
||||
RuleContext ctx = new RuleContext();
|
||||
Report report = new Report();
|
||||
ctx.setReport(report);
|
||||
ctx.setSourceCodeFilename("n/a");
|
||||
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule);
|
||||
p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST2), new RuleSets(rules), ctx);
|
||||
Report report = getReportForTestString(rule, TEST2);
|
||||
RuleViolation rv = report.iterator().next();
|
||||
assertEquals(3, rv.getBeginLine());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test for problem reported in bug #1219 PrimarySuffix/@Image does not work
|
||||
* in some cases in xpath 2.0
|
||||
@ -168,8 +186,24 @@ public class XPathRuleTest extends RuleTst {
|
||||
assertEquals(5, nodes.get(1).getBeginLine());
|
||||
}
|
||||
|
||||
private static Report getReportForTestString(Rule r, String test) throws PMDException {
|
||||
PMD p = new PMD();
|
||||
RuleContext ctx = new RuleContext();
|
||||
Report report = new Report();
|
||||
ctx.setReport(report);
|
||||
ctx.setSourceCodeFilename("n/a");
|
||||
RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(r);
|
||||
p.getSourceCodeProcessor().processSourceCode(new StringReader(test), new RuleSets(rules), ctx);
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
private static final String TEST1 = "public class Foo {" + PMD.EOL + " int a;" + PMD.EOL + "}";
|
||||
|
||||
private static final String TEST2 = "public class Foo {" + PMD.EOL + " int faddle;" + PMD.EOL + " int fiddle;"
|
||||
+ PMD.EOL + "}";
|
||||
|
||||
|
||||
private static final String TEST3 = "public class Foo {" + PMD.EOL + " int forbid1; int forbid2; int forbid1$forbid2;" + PMD.EOL + "}";
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user