001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jxpath.ri;
018
019 import java.lang.ref.SoftReference;
020 import java.util.ArrayList;
021 import java.util.Arrays;
022 import java.util.Collections;
023 import java.util.Comparator;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.Map;
027 import java.util.Vector;
028 import java.util.Map.Entry;
029
030 import org.apache.commons.jxpath.CompiledExpression;
031 import org.apache.commons.jxpath.Function;
032 import org.apache.commons.jxpath.Functions;
033 import org.apache.commons.jxpath.JXPathContext;
034 import org.apache.commons.jxpath.JXPathException;
035 import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
036 import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
037 import org.apache.commons.jxpath.JXPathNotFoundException;
038 import org.apache.commons.jxpath.JXPathTypeConversionException;
039 import org.apache.commons.jxpath.Pointer;
040 import org.apache.commons.jxpath.ri.axes.InitialContext;
041 import org.apache.commons.jxpath.ri.axes.RootContext;
042 import org.apache.commons.jxpath.ri.compiler.Expression;
043 import org.apache.commons.jxpath.ri.compiler.LocationPath;
044 import org.apache.commons.jxpath.ri.compiler.Path;
045 import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
046 import org.apache.commons.jxpath.ri.model.NodePointer;
047 import org.apache.commons.jxpath.ri.model.NodePointerFactory;
048 import org.apache.commons.jxpath.ri.model.VariablePointerFactory;
049 import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
050 import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
051 import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
052 import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
053 import org.apache.commons.jxpath.util.ReverseComparator;
054 import org.apache.commons.jxpath.util.TypeUtils;
055
056 /**
057 * The reference implementation of JXPathContext.
058 *
059 * @author Dmitri Plotnikov
060 * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
061 */
062 public class JXPathContextReferenceImpl extends JXPathContext {
063
064 /**
065 * Change this to <code>false</code> to disable soft caching of
066 * CompiledExpressions.
067 */
068 public static final boolean USE_SOFT_CACHE = true;
069
070 private static final Compiler COMPILER = new TreeCompiler();
071 private static Map compiled = new HashMap();
072 private static int cleanupCount = 0;
073
074 private static NodePointerFactory[] nodeFactoryArray = null;
075 // The frequency of the cache cleanup
076 private static final int CLEANUP_THRESHOLD = 500;
077 private static final Vector nodeFactories = new Vector();
078
079 static {
080 nodeFactories.add(new CollectionPointerFactory());
081 nodeFactories.add(new BeanPointerFactory());
082 nodeFactories.add(new DynamicPointerFactory());
083 nodeFactories.add(new VariablePointerFactory());
084
085 // DOM factory is only registered if DOM support is on the classpath
086 Object domFactory = allocateConditionally(
087 "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory",
088 "org.w3c.dom.Node");
089 if (domFactory != null) {
090 nodeFactories.add(domFactory);
091 }
092
093 // JDOM factory is only registered if JDOM is on the classpath
094 Object jdomFactory = allocateConditionally(
095 "org.apache.commons.jxpath.ri.model.jdom.JDOMPointerFactory",
096 "org.jdom.Document");
097 if (jdomFactory != null) {
098 nodeFactories.add(jdomFactory);
099 }
100
101 // DynaBean factory is only registered if BeanUtils are on the classpath
102 Object dynaBeanFactory =
103 allocateConditionally(
104 "org.apache.commons.jxpath.ri.model.dynabeans."
105 + "DynaBeanPointerFactory",
106 "org.apache.commons.beanutils.DynaBean");
107 if (dynaBeanFactory != null) {
108 nodeFactories.add(dynaBeanFactory);
109 }
110
111 nodeFactories.add(new ContainerPointerFactory());
112 createNodeFactoryArray();
113 }
114
115 /**
116 * Create the default node factory array.
117 */
118 private static synchronized void createNodeFactoryArray() {
119 if (nodeFactoryArray == null) {
120 nodeFactoryArray =
121 (NodePointerFactory[]) nodeFactories.
122 toArray(new NodePointerFactory[nodeFactories.size()]);
123 Arrays.sort(nodeFactoryArray, new Comparator() {
124 public int compare(Object a, Object b) {
125 int orderA = ((NodePointerFactory) a).getOrder();
126 int orderB = ((NodePointerFactory) b).getOrder();
127 return orderA - orderB;
128 }
129 });
130 }
131 }
132
133 /**
134 * Call this with a custom NodePointerFactory to add support for
135 * additional types of objects. Make sure the factory returns
136 * a name that puts it in the right position on the list of factories.
137 * @param factory NodePointerFactory to add
138 */
139 public static void addNodePointerFactory(NodePointerFactory factory) {
140 synchronized (nodeFactories) {
141 nodeFactories.add(factory);
142 nodeFactoryArray = null;
143 }
144 }
145
146 /**
147 * Get the registered NodePointerFactories.
148 * @return NodePointerFactory[]
149 */
150 public static NodePointerFactory[] getNodePointerFactories() {
151 return nodeFactoryArray;
152 }
153
154 /** Namespace resolver */
155 protected NamespaceResolver namespaceResolver;
156
157 private Pointer rootPointer;
158 private Pointer contextPointer;
159
160 /**
161 * Create a new JXPathContextReferenceImpl.
162 * @param parentContext parent context
163 * @param contextBean Object
164 */
165 protected JXPathContextReferenceImpl(JXPathContext parentContext,
166 Object contextBean) {
167 this(parentContext, contextBean, null);
168 }
169
170 /**
171 * Create a new JXPathContextReferenceImpl.
172 * @param parentContext parent context
173 * @param contextBean Object
174 * @param contextPointer context pointer
175 */
176 public JXPathContextReferenceImpl(JXPathContext parentContext,
177 Object contextBean, Pointer contextPointer) {
178 super(parentContext, contextBean);
179
180 synchronized (nodeFactories) {
181 createNodeFactoryArray();
182 }
183
184 if (contextPointer != null) {
185 this.contextPointer = contextPointer;
186 this.rootPointer =
187 NodePointer.newNodePointer(
188 new QName(null, "root"),
189 contextPointer.getRootNode(),
190 getLocale());
191 }
192 else {
193 this.contextPointer =
194 NodePointer.newNodePointer(
195 new QName(null, "root"),
196 contextBean,
197 getLocale());
198 this.rootPointer = this.contextPointer;
199 }
200
201 NamespaceResolver parentNR = null;
202 if (parentContext instanceof JXPathContextReferenceImpl) {
203 parentNR = ((JXPathContextReferenceImpl) parentContext).getNamespaceResolver();
204 }
205 namespaceResolver = new NamespaceResolver(parentNR);
206 namespaceResolver
207 .setNamespaceContextPointer((NodePointer) this.contextPointer);
208 }
209
210 /**
211 * Returns a static instance of TreeCompiler.
212 *
213 * Override this to return an alternate compiler.
214 * @return Compiler
215 */
216 protected Compiler getCompiler() {
217 return COMPILER;
218 }
219
220 protected CompiledExpression compilePath(String xpath) {
221 return new JXPathCompiledExpression(xpath, compileExpression(xpath));
222 }
223
224 /**
225 * Compile the given expression.
226 * @param xpath to compile
227 * @return Expression
228 */
229 private Expression compileExpression(String xpath) {
230 Expression expr;
231
232 synchronized (compiled) {
233 if (USE_SOFT_CACHE) {
234 expr = null;
235 SoftReference ref = (SoftReference) compiled.get(xpath);
236 if (ref != null) {
237 expr = (Expression) ref.get();
238 }
239 }
240 else {
241 expr = (Expression) compiled.get(xpath);
242 }
243 }
244
245 if (expr != null) {
246 return expr;
247 }
248
249 expr = (Expression) Parser.parseExpression(xpath, getCompiler());
250
251 synchronized (compiled) {
252 if (USE_SOFT_CACHE) {
253 if (cleanupCount++ >= CLEANUP_THRESHOLD) {
254 Iterator it = compiled.entrySet().iterator();
255 while (it.hasNext()) {
256 Entry me = (Entry) it.next();
257 if (((SoftReference) me.getValue()).get() == null) {
258 it.remove();
259 }
260 }
261 cleanupCount = 0;
262 }
263 compiled.put(xpath, new SoftReference(expr));
264 }
265 else {
266 compiled.put(xpath, expr);
267 }
268 }
269
270 return expr;
271 }
272
273 /**
274 * Traverses the xpath and returns the resulting object. Primitive
275 * types are wrapped into objects.
276 * @param xpath expression
277 * @return Object found
278 */
279 public Object getValue(String xpath) {
280 Expression expression = compileExpression(xpath);
281 // TODO: (work in progress) - trying to integrate with Xalan
282 // Object ctxNode = getNativeContextNode(expression);
283 // if (ctxNode != null) {
284 // System.err.println("WILL USE XALAN: " + xpath);
285 // CachedXPathAPI api = new CachedXPathAPI();
286 // try {
287 // if (expression instanceof Path) {
288 // Node node = api.selectSingleNode((Node)ctxNode, xpath);
289 // System.err.println("NODE: " + node);
290 // if (node == null) {
291 // return null;
292 // }
293 // return new DOMNodePointer(node, null).getValue();
294 // }
295 // else {
296 // XObject object = api.eval((Node)ctxNode, xpath);
297 // switch (object.getType()) {
298 // case XObject.CLASS_STRING: return object.str();
299 // case XObject.CLASS_NUMBER: return new Double(object.num());
300 // case XObject.CLASS_BOOLEAN: return new Boolean(object.bool());
301 // default:
302 // System.err.println("OTHER TYPE: " + object.getTypeString());
303 // }
304 // }
305 // }
306 // catch (TransformerException e) {
307 // // TODO Auto-generated catch block
308 // e.printStackTrace();
309 // }
310 // return
311 // }
312
313 return getValue(xpath, expression);
314 }
315
316 // private Object getNativeContextNode(Expression expression) {
317 // Object node = getNativeContextNode(getContextBean());
318 // if (node == null) {
319 // return null;
320 // }
321 //
322 // List vars = expression.getUsedVariables();
323 // if (vars != null) {
324 // return null;
325 // }
326 //
327 // return node;
328 // }
329
330 // private Object getNativeContextNode(Object bean) {
331 // if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
332 // return bean;
333 // }
334 // if (bean instanceof Node) {
335 // return (Node)bean;
336 // }
337 //
338 // if (bean instanceof Container) {
339 // bean = ((Container)bean).getValue();
340 // return getNativeContextNode(bean);
341 // }
342 //
343 // return null;
344 // }
345
346 /**
347 * Get the value indicated.
348 * @param xpath String
349 * @param expr Expression
350 * @return Object
351 */
352 public Object getValue(String xpath, Expression expr) {
353 Object result = expr.computeValue(getEvalContext());
354 if (result == null) {
355 if (expr instanceof Path && !isLenient()) {
356 throw new JXPathNotFoundException("No value for xpath: "
357 + xpath);
358 }
359 return null;
360 }
361 if (result instanceof EvalContext) {
362 EvalContext ctx = (EvalContext) result;
363 result = ctx.getSingleNodePointer();
364 if (!isLenient() && result == null) {
365 throw new JXPathNotFoundException("No value for xpath: "
366 + xpath);
367 }
368 }
369 if (result instanceof NodePointer) {
370 result = ((NodePointer) result).getValuePointer();
371 if (!isLenient() && !((NodePointer) result).isActual()) {
372 // We need to differentiate between pointers representing
373 // a non-existing property and ones representing a property
374 // whose value is null. In the latter case, the pointer
375 // is going to have isActual == false, but its parent,
376 // which is a non-node pointer identifying the bean property,
377 // will return isActual() == true.
378 NodePointer parent =
379 ((NodePointer) result).getImmediateParentPointer();
380 if (parent == null
381 || !parent.isContainer()
382 || !parent.isActual()) {
383 throw new JXPathNotFoundException("No value for xpath: "
384 + xpath);
385 }
386 }
387 result = ((NodePointer) result).getValue();
388 }
389 return result;
390 }
391
392 /**
393 * Calls getValue(xpath), converts the result to the required type
394 * and returns the result of the conversion.
395 * @param xpath expression
396 * @param requiredType Class
397 * @return Object
398 */
399 public Object getValue(String xpath, Class requiredType) {
400 Expression expr = compileExpression(xpath);
401 return getValue(xpath, expr, requiredType);
402 }
403
404 /**
405 * Get the value indicated.
406 * @param xpath expression
407 * @param expr compiled Expression
408 * @param requiredType Class
409 * @return Object
410 */
411 public Object getValue(String xpath, Expression expr, Class requiredType) {
412 Object value = getValue(xpath, expr);
413 if (value != null && requiredType != null) {
414 if (!TypeUtils.canConvert(value, requiredType)) {
415 throw new JXPathTypeConversionException(
416 "Invalid expression type. '"
417 + xpath
418 + "' returns "
419 + value.getClass().getName()
420 + ". It cannot be converted to "
421 + requiredType.getName());
422 }
423 value = TypeUtils.convert(value, requiredType);
424 }
425 return value;
426 }
427
428 /**
429 * Traverses the xpath and returns a Iterator of all results found
430 * for the path. If the xpath matches no properties
431 * in the graph, the Iterator will not be null.
432 * @param xpath expression
433 * @return Iterator
434 */
435 public Iterator iterate(String xpath) {
436 return iterate(xpath, compileExpression(xpath));
437 }
438
439 /**
440 * Traverses the xpath and returns a Iterator of all results found
441 * for the path. If the xpath matches no properties
442 * in the graph, the Iterator will not be null.
443 * @param xpath expression
444 * @param expr compiled Expression
445 * @return Iterator
446 */
447 public Iterator iterate(String xpath, Expression expr) {
448 return expr.iterate(getEvalContext());
449 }
450
451 public Pointer getPointer(String xpath) {
452 return getPointer(xpath, compileExpression(xpath));
453 }
454
455 /**
456 * Get a pointer to the specified path/expression.
457 * @param xpath String
458 * @param expr compiled Expression
459 * @return Pointer
460 */
461 public Pointer getPointer(String xpath, Expression expr) {
462 Object result = expr.computeValue(getEvalContext());
463 if (result instanceof EvalContext) {
464 result = ((EvalContext) result).getSingleNodePointer();
465 }
466 if (result instanceof Pointer) {
467 if (!isLenient() && !((NodePointer) result).isActual()) {
468 throw new JXPathNotFoundException("No pointer for xpath: "
469 + xpath);
470 }
471 return (Pointer) result;
472 }
473 return NodePointer.newNodePointer(null, result, getLocale());
474 }
475
476 public void setValue(String xpath, Object value) {
477 setValue(xpath, compileExpression(xpath), value);
478 }
479
480 /**
481 * Set the value of xpath to value.
482 * @param xpath path
483 * @param expr compiled Expression
484 * @param value Object
485 */
486 public void setValue(String xpath, Expression expr, Object value) {
487 try {
488 setValue(xpath, expr, value, false);
489 }
490 catch (Throwable ex) {
491 throw new JXPathException(
492 "Exception trying to set value with xpath " + xpath, ex);
493 }
494 }
495
496 public Pointer createPath(String xpath) {
497 return createPath(xpath, compileExpression(xpath));
498 }
499
500 /**
501 * Create the given path.
502 * @param xpath String
503 * @param expr compiled Expression
504 * @return resulting Pointer
505 */
506 public Pointer createPath(String xpath, Expression expr) {
507 try {
508 Object result = expr.computeValue(getEvalContext());
509 Pointer pointer = null;
510
511 if (result instanceof Pointer) {
512 pointer = (Pointer) result;
513 }
514 else if (result instanceof EvalContext) {
515 EvalContext ctx = (EvalContext) result;
516 pointer = ctx.getSingleNodePointer();
517 }
518 else {
519 checkSimplePath(expr);
520 // This should never happen
521 throw new JXPathException("Cannot create path:" + xpath);
522 }
523 return ((NodePointer) pointer).createPath(this);
524 }
525 catch (Throwable ex) {
526 throw new JXPathException(
527 "Exception trying to create xpath " + xpath,
528 ex);
529 }
530 }
531
532 public Pointer createPathAndSetValue(String xpath, Object value) {
533 return createPathAndSetValue(xpath, compileExpression(xpath), value);
534 }
535
536 /**
537 * Create the given path setting its value to value.
538 * @param xpath String
539 * @param expr compiled Expression
540 * @param value Object
541 * @return resulting Pointer
542 */
543 public Pointer createPathAndSetValue(String xpath, Expression expr,
544 Object value) {
545 try {
546 return setValue(xpath, expr, value, true);
547 }
548 catch (Throwable ex) {
549 throw new JXPathException(
550 "Exception trying to create xpath " + xpath,
551 ex);
552 }
553 }
554
555 /**
556 * Set the specified value.
557 * @param xpath path
558 * @param expr compiled Expression
559 * @param value destination value
560 * @param create whether to create missing node(s)
561 * @return Pointer created
562 */
563 private Pointer setValue(String xpath, Expression expr, Object value,
564 boolean create) {
565 Object result = expr.computeValue(getEvalContext());
566 Pointer pointer = null;
567
568 if (result instanceof Pointer) {
569 pointer = (Pointer) result;
570 }
571 else if (result instanceof EvalContext) {
572 EvalContext ctx = (EvalContext) result;
573 pointer = ctx.getSingleNodePointer();
574 }
575 else {
576 if (create) {
577 checkSimplePath(expr);
578 }
579
580 // This should never happen
581 throw new JXPathException("Cannot set value for xpath: " + xpath);
582 }
583 if (create) {
584 pointer = ((NodePointer) pointer).createPath(this, value);
585 }
586 else {
587 pointer.setValue(value);
588 }
589 return pointer;
590 }
591
592 /**
593 * Checks if the path follows the JXPath restrictions on the type
594 * of path that can be passed to create... methods.
595 * @param expr Expression to check
596 */
597 private void checkSimplePath(Expression expr) {
598 if (!(expr instanceof LocationPath)
599 || !((LocationPath) expr).isSimplePath()) {
600 throw new JXPathInvalidSyntaxException(
601 "JXPath can only create a path if it uses exclusively "
602 + "the child:: and attribute:: axes and has "
603 + "no context-dependent predicates");
604 }
605 }
606
607 /**
608 * Traverses the xpath and returns an Iterator of Pointers.
609 * A Pointer provides easy access to a property.
610 * If the xpath matches no properties
611 * in the graph, the Iterator be empty, but not null.
612 * @param xpath expression
613 * @return Iterator
614 */
615 public Iterator iteratePointers(String xpath) {
616 return iteratePointers(xpath, compileExpression(xpath));
617 }
618
619 /**
620 * Traverses the xpath and returns an Iterator of Pointers.
621 * A Pointer provides easy access to a property.
622 * If the xpath matches no properties
623 * in the graph, the Iterator be empty, but not null.
624 * @param xpath expression
625 * @param expr compiled Expression
626 * @return Iterator
627 */
628 public Iterator iteratePointers(String xpath, Expression expr) {
629 return expr.iteratePointers(getEvalContext());
630 }
631
632 public void removePath(String xpath) {
633 removePath(xpath, compileExpression(xpath));
634 }
635
636 /**
637 * Remove the specified path.
638 * @param xpath expression
639 * @param expr compiled Expression
640 */
641 public void removePath(String xpath, Expression expr) {
642 try {
643 NodePointer pointer = (NodePointer) getPointer(xpath, expr);
644 if (pointer != null) {
645 ((NodePointer) pointer).remove();
646 }
647 }
648 catch (Throwable ex) {
649 throw new JXPathException(
650 "Exception trying to remove xpath " + xpath,
651 ex);
652 }
653 }
654
655 public void removeAll(String xpath) {
656 removeAll(xpath, compileExpression(xpath));
657 }
658
659 /**
660 * Remove all matching nodes.
661 * @param xpath expression
662 * @param expr compiled Expression
663 */
664 public void removeAll(String xpath, Expression expr) {
665 try {
666 ArrayList list = new ArrayList();
667 Iterator it = expr.iteratePointers(getEvalContext());
668 while (it.hasNext()) {
669 list.add(it.next());
670 }
671 Collections.sort(list, ReverseComparator.INSTANCE);
672 it = list.iterator();
673 if (it.hasNext()) {
674 NodePointer pointer = (NodePointer) it.next();
675 pointer.remove();
676 while (it.hasNext()) {
677 removePath(((NodePointer) it.next()).asPath());
678 }
679 }
680 }
681 catch (Throwable ex) {
682 throw new JXPathException(
683 "Exception trying to remove all for xpath " + xpath,
684 ex);
685 }
686 }
687
688 public JXPathContext getRelativeContext(Pointer pointer) {
689 Object contextBean = pointer.getNode();
690 if (contextBean == null) {
691 throw new JXPathException(
692 "Cannot create a relative context for a non-existent node: "
693 + pointer);
694 }
695 return new JXPathContextReferenceImpl(this, contextBean, pointer);
696 }
697
698 public Pointer getContextPointer() {
699 return contextPointer;
700 }
701
702 /**
703 * Get absolute root pointer.
704 * @return NodePointer
705 */
706 private NodePointer getAbsoluteRootPointer() {
707 return (NodePointer) rootPointer;
708 }
709
710 /**
711 * Get the evaluation context.
712 * @return EvalContext
713 */
714 private EvalContext getEvalContext() {
715 return new InitialContext(new RootContext(this,
716 (NodePointer) getContextPointer()));
717 }
718
719 /**
720 * Get the absolute root context.
721 * @return EvalContext
722 */
723 public EvalContext getAbsoluteRootContext() {
724 return new InitialContext(new RootContext(this,
725 getAbsoluteRootPointer()));
726 }
727
728 /**
729 * Get a VariablePointer for the given variable name.
730 * @param name variable name
731 * @return NodePointer
732 */
733 public NodePointer getVariablePointer(QName name) {
734 return NodePointer.newNodePointer(name, VariablePointerFactory
735 .contextWrapper(this), getLocale());
736 }
737
738 /**
739 * Get the named Function.
740 * @param functionName name
741 * @param parameters function args
742 * @return Function
743 */
744 public Function getFunction(QName functionName, Object[] parameters) {
745 String namespace = functionName.getPrefix();
746 String name = functionName.getName();
747 JXPathContext funcCtx = this;
748 Function func = null;
749 Functions funcs;
750 while (funcCtx != null) {
751 funcs = funcCtx.getFunctions();
752 if (funcs != null) {
753 func = funcs.getFunction(namespace, name, parameters);
754 if (func != null) {
755 return func;
756 }
757 }
758 funcCtx = funcCtx.getParentContext();
759 }
760 throw new JXPathFunctionNotFoundException(
761 "Undefined function: " + functionName.toString());
762 }
763
764 public void registerNamespace(String prefix, String namespaceURI) {
765 if (namespaceResolver.isSealed()) {
766 namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
767 }
768 namespaceResolver.registerNamespace(prefix, namespaceURI);
769 }
770
771 public String getNamespaceURI(String prefix) {
772 return namespaceResolver.getNamespaceURI(prefix);
773 }
774
775 /**
776 * {@inheritDoc}
777 * @see org.apache.commons.jxpath.JXPathContext#getPrefix(java.lang.String)
778 */
779 public String getPrefix(String namespaceURI) {
780 return namespaceResolver.getPrefix(namespaceURI);
781 }
782
783 public void setNamespaceContextPointer(Pointer pointer) {
784 if (namespaceResolver.isSealed()) {
785 namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
786 }
787 namespaceResolver.setNamespaceContextPointer((NodePointer) pointer);
788 }
789
790 public Pointer getNamespaceContextPointer() {
791 return namespaceResolver.getNamespaceContextPointer();
792 }
793
794 /**
795 * Get the namespace resolver.
796 * @return NamespaceResolver
797 */
798 public NamespaceResolver getNamespaceResolver() {
799 namespaceResolver.seal();
800 return namespaceResolver;
801 }
802
803 /**
804 * Checks if existenceCheckClass exists on the class path. If so, allocates
805 * an instance of the specified class, otherwise returns null.
806 * @param className to instantiate
807 * @param existenceCheckClassName guard class
808 * @return className instance
809 */
810 public static Object allocateConditionally(String className,
811 String existenceCheckClassName) {
812 try {
813 try {
814 Class.forName(existenceCheckClassName);
815 }
816 catch (ClassNotFoundException ex) {
817 return null;
818 }
819 Class cls = Class.forName(className);
820 return cls.newInstance();
821 }
822 catch (Exception ex) {
823 throw new JXPathException("Cannot allocate " + className, ex);
824 }
825 }
826 }