Skip to content

Commit 5497a5a

Browse files
committed
Fixes #120 - clone of JaxenComplied instance.
Now it does a Copy-Constructor clone instead of changing final instance fields.
1 parent 250d723 commit 5497a5a

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

core/src/java/org/jdom2/xpath/jaxen/JaxenCompiled.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ public JaxenCompiled(String expression, Filter<T> filter,
156156
xPath.setVariableContext(this);
157157
}
158158

159+
/**
160+
* Make a copy-constructor available to the clone() method.
161+
* This is simpler than trying to do a deep clone anyway.
162+
*
163+
* @param toclone The JaxenCompiled instance to clone
164+
*/
165+
private JaxenCompiled(JaxenCompiled<T> toclone) {
166+
this(toclone.getExpression(), toclone.getFilter(), toclone.getVariables(), toclone.getNamespaces());
167+
}
168+
159169
@Override
160170
public String translateNamespacePrefixToUri(String prefix) {
161171
return getNamespace(prefix).getURI();
@@ -201,5 +211,14 @@ protected Object evaluateRawFirst(Object context) {
201211
"Unable to evaluate expression. See cause", e);
202212
}
203213
}
214+
215+
@Override
216+
public JaxenCompiled<T> clone() {
217+
// Use a copy-constructor instead of a deep clone.
218+
// we have a couple of final variables on this class that we cannot share
219+
// between instances, and the Jaxen xpath variable is pretty complicated to reconstruct
220+
// anyway. Easier to just reconstruct it.
221+
return new JaxenCompiled<T>(this);
222+
}
204223

205224
}

core/src/java/org/jdom2/xpath/util/AbstractXPathCompiled.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,21 @@ public int compare(Namespace ns1, Namespace ns2) {
8888

8989
private static final NamespaceComparator NSSORT = new NamespaceComparator();
9090

91+
/**
92+
* Utility method to find a Namespace that has a given URI, and return the prefix.
93+
* @param uri the URI to search for
94+
* @param nsa the array of namespaces to search through
95+
* @return the prefix of the namespace
96+
*/
97+
private static final String getPrefixForURI(final String uri, final Namespace[] nsa) {
98+
for (final Namespace ns : nsa) {
99+
if (ns.getURI().equals(uri)) {
100+
return ns.getPrefix();
101+
}
102+
}
103+
throw new IllegalStateException("No namespace defined with URI " + uri);
104+
}
105+
91106
private final Map<String, Namespace> xnamespaces = new HashMap<String, Namespace>();
92107
// Not final to support cloning.
93108
private Map<String, Map<String, Object>> xvariables = new HashMap<String, Map<String, Object>>();
@@ -309,6 +324,28 @@ public Object setVariable(String qname, Object value) {
309324
return setVariable(qname, Namespace.NO_NAMESPACE, value);
310325
}
311326

327+
/**
328+
* utility method that allows descendant classes to access the variables
329+
* that were set on this expression, in a format that can be used in a constructor (qname/value).
330+
* @return the variables set on this instance.
331+
*/
332+
protected Map<String,Object> getVariables() {
333+
HashMap<String,Object> vars = new HashMap<String, Object>();
334+
Namespace[] nsa = getNamespaces();
335+
for (Map.Entry<String, Map<String,Object>> ue : xvariables.entrySet()) {
336+
final String uri = ue.getKey();
337+
final String pfx = getPrefixForURI(uri, nsa);
338+
for (Map.Entry<String, Object> ve : ue.getValue().entrySet()) {
339+
if ("".equals(pfx)) {
340+
vars.put(ve.getKey(), ve.getValue());
341+
} else {
342+
vars.put(pfx + ":" + ve.getKey(), ve.getValue());
343+
}
344+
}
345+
}
346+
return vars;
347+
}
348+
312349
@Override
313350
public final Filter<T> getFilter() {
314351
return xfilter;

test/src/java/org/jdom2/test/cases/xpath/AbstractTestXPathCompiled.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,42 @@ public void testClone() {
752752

753753
}
754754

755+
@Test
756+
public void testCloneVariables() {
757+
List<Element> lst = null;
758+
HashMap<String,Object> vars = new HashMap<String,Object>();
759+
vars.put("vns:vname", "1");
760+
Namespace vns = Namespace.getNamespace("vns", "http://jdom.org/xpath_variable_namespace");
761+
XPathExpression<Element> xpathhc = XPathFactory.instance().compile(
762+
"/main/child[1]", Filters.element());
763+
lst = xpathhc.evaluate(doc);
764+
assertTrue(1 == lst.size());
765+
assertTrue(child1emt == lst.get(0));
766+
767+
XPathExpression<Element> xpath = XPathFactory.instance().compile(
768+
"/main/child[position() = $vns:vname]", Filters.element(), vars, vns);
769+
lst = xpath.evaluate(doc);
770+
assertTrue(1 == lst.size());
771+
assertTrue(child1emt == lst.get(0));
772+
xpath.setVariable("vns:vname", "2");
773+
assertTrue("2" == xpath.getVariable("vname", vns));
774+
lst = xpath.evaluate(doc);
775+
assertTrue(1 == lst.size());
776+
assertTrue(child2emt == lst.get(0));
777+
778+
XPathExpression<Element> cloned = xpath.clone();
779+
lst = cloned.evaluate(doc);
780+
assertTrue(1 == lst.size());
781+
assertTrue(child2emt == lst.get(0));
782+
783+
cloned.setVariable("vns:vname", "1");
784+
assertTrue("2" == xpath.getVariable("vname", vns));
785+
assertTrue("1" == cloned.getVariable("vname", vns));
786+
lst = cloned.evaluate(doc);
787+
assertTrue(1 == lst.size());
788+
assertTrue(child1emt == lst.get(0));
789+
}
790+
755791
@Test
756792
public void testSelectDocumentDoc() {
757793
checkXPath("/", doc, mainvalue, doc);

0 commit comments

Comments
 (0)