/
JXPathContext.java
956 lines (903 loc) · 31.3 KB
/
JXPathContext.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jxpath;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.commons.jxpath.util.KeyManagerUtils;
/**
* JXPathContext provides APIs for the traversal of graphs of JavaBeans using
* the XPath syntax. Using JXPathContext, you can read and write properties of
* JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans
* introspection to enumerate and access JavaBeans properties.
* <p>
* JXPathContext allows alternative implementations. This is why instead of
* allocating JXPathContext directly, you should call a static
* <code>newContext</code> method. This method will utilize the
* {@link JXPathContextFactory} API to locate a suitable implementation of
* JXPath. Bundled with JXPath comes a default implementation called Reference
* Implementation.
* </p>
*
* <h2>JXPath Interprets XPath Syntax on Java Object Graphs</h2>
*
* JXPath uses an intuitive interpretation of the xpath syntax in the context
* of Java object graphs. Here are some examples:
*
* <h3>Example 1: JavaBean Property Access</h3>
*
* JXPath can be used to access properties of a JavaBean.
*
* <pre><blockquote>
* public class Employee {
* public String getFirstName(){
* ...
* }
* }
*
* Employee emp = new Employee();
* ...
*
* JXPathContext context = JXPathContext.newContext(emp);
* String fName = (String)context.getValue("firstName");
* </blockquote></pre>
*
* In this example, we are using JXPath to access a property of the
* <code>emp</code> bean. In this simple case the invocation of JXPath is
* equivalent to invocation of getFirstName() on the bean.
*
* <h3>Example 2: Nested Bean Property Access</h3>
* JXPath can traverse object graphs:
*
* <pre><blockquote>
* public class Employee {
* public Address getHomeAddress(){
* ...
* }
* }
* public class Address {
* public String getStreetNumber(){
* ...
* }
* }
*
* Employee emp = new Employee();
* ...
*
* JXPathContext context = JXPathContext.newContext(emp);
* String sNumber = (String)context.getValue("homeAddress/streetNumber");
* </blockquote></pre>
*
* In this case XPath is used to access a property of a nested bean.
* <p>
* A property identified by the xpath does not have to be a "leaf" property.
* For instance, we can extract the whole Address object in above example:
*
* <pre><blockquote>
* Address addr = (Address)context.getValue("homeAddress");
* </blockquote></pre>
* </p>
*
* <h3>Example 3: Collection Subscripts</h3>
* JXPath can extract elements from arrays and collections.
*
* <pre><blockquote>
* public class Integers {
* public int[] getNumbers(){
* ...
* }
* }
*
* Integers ints = new Integers();
* ...
*
* JXPathContext context = JXPathContext.newContext(ints);
* Integer thirdInt = (Integer)context.getValue("numbers[3]");
* </blockquote></pre>
* A collection can be an arbitrary array or an instance of java.util.
* Collection.
* <p>
* Note: in XPath the first element of a collection has index 1, not 0.<br>
*
* <h3>Example 4: Map Element Access</h3>
*
* JXPath supports maps. To get a value use its key.
*
* <pre><blockquote>
* public class Employee {
* public Map getAddresses(){
* return addressMap;
* }
*
* public void addAddress(String key, Address address){
* addressMap.put(key, address);
* }
* ...
* }
*
* Employee emp = new Employee();
* emp.addAddress("home", new Address(...));
* emp.addAddress("office", new Address(...));
* ...
*
* JXPathContext context = JXPathContext.newContext(emp);
* String homeZipCode = (String)context.getValue("addresses/home/zipCode");
* </blockquote></pre>
*
* Often you will need to use the alternative syntax for accessing Map
* elements:
*
* <pre><blockquote>
* String homeZipCode =
* (String) context.getValue("addresses[@name='home']/zipCode");
* </blockquote></pre>
*
* In this case, the key can be an expression, e.g. a variable.<br>
*
* Note: At this point JXPath only supports Maps that use strings for keys.<br>
* Note: JXPath supports the extended notion of Map: any object with
* dynamic properties can be handled by JXPath provided that its
* class is registered with the {@link JXPathIntrospector}.
*
* <h3>Example 5: Retrieving Multiple Results</h3>
*
* JXPath can retrieve multiple objects from a graph. Note that the method
* called in this case is not <code>getValue</code>, but <code>iterate</code>.
*
* <pre><blockquote>
* public class Author {
* public Book[] getBooks(){
* ...
* }
* }
*
* Author auth = new Author();
* ...
*
* JXPathContext context = JXPathContext.newContext(auth);
* Iterator threeBooks = context.iterate("books[position() < 4]");
* </blockquote></pre>
*
* This returns a list of at most three books from the array of all books
* written by the author.
*
* <h3>Example 6: Setting Properties</h3>
* JXPath can be used to modify property values.
*
* <pre><blockquote>
* public class Employee {
* public Address getAddress() {
* ...
* }
*
* public void setAddress(Address address) {
* ...
* }
* }
*
* Employee emp = new Employee();
* Address addr = new Address();
* ...
*
* JXPathContext context = JXPathContext.newContext(emp);
* context.setValue("address", addr);
* context.setValue("address/zipCode", "90190");
*
* </blockquote></pre>
*
* <h3>Example 7: Creating objects</h3>
* JXPath can be used to create new objects. First, create a subclass of {@link
* AbstractFactory AbstractFactory} and install it on the JXPathContext. Then
* call {@link JXPathContext#createPath createPathAndSetValue()} instead of
* "setValue". JXPathContext will invoke your AbstractFactory when it discovers
* that an intermediate node of the path is <b>null</b>. It will not override
* existing nodes.
*
* <pre><blockquote>
* public class AddressFactory extends AbstractFactory {
* public boolean createObject(JXPathContext context,
* Pointer pointer, Object parent, String name, int index){
* if ((parent instanceof Employee) && name.equals("address"){
* ((Employee)parent).setAddress(new Address());
* return true;
* }
* return false;
* }
* }
*
* JXPathContext context = JXPathContext.newContext(emp);
* context.setFactory(new AddressFactory());
* context.createPathAndSetValue("address/zipCode", "90190");
* </blockquote></pre>
*
* <h3>Example 8: Using Variables</h3>
* JXPath supports the notion of variables. The XPath syntax for accessing
* variables is <i>"$varName"</i>.
*
* <pre><blockquote>
* public class Author {
* public Book[] getBooks(){
* ...
* }
* }
*
* Author auth = new Author();
* ...
*
* JXPathContext context = JXPathContext.newContext(auth);
* context.getVariables().declareVariable("index", new Integer(2));
*
* Book secondBook = (Book)context.getValue("books[$index]");
* </blockquote></pre>
*
* You can also set variables using JXPath:
*
* <pre><blockquote>
* context.setValue("$index", new Integer(3));
* </blockquote></pre>
*
* Note: you can only <i>change</i> the value of an existing variable this
* way, you cannot <i>define</i> a new variable.
*
* <p>
* When a variable contains a JavaBean or a collection, you can
* traverse the bean or collection as well:
* <pre><blockquote>
* ...
* context.getVariables().declareVariable("book", myBook);
* String title = (String)context.getValue("$book/title);
*
* Book array[] = new Book[]{...};
*
* context.getVariables().declareVariable("books", array);
*
* String title = (String)context.getValue("$books[2]/title);
* </blockquote></pre>
*
* <h3>Example 9: Using Nested Contexts</h3>
* If you need to use the same set of variable while interpreting XPaths with
* different beans, it makes sense to put the variables in a separate context
* and specify that context as a parent context every time you allocate a new
* JXPathContext for a JavaBean.
*
* <pre><blockquote>
* JXPathContext varContext = JXPathContext.newContext(null);
* varContext.getVariables().declareVariable("title", "Java");
*
* JXPathContext context = JXPathContext.newContext(varContext, auth);
*
* Iterator javaBooks = context.iterate("books[title = $title]");
* </blockquote></pre>
*
* <h3>Using Custom Variable Pools</h3>
* By default, JXPathContext creates a HashMap of variables. However,
* you can substitute a custom implementation of the Variables
* interface to make JXPath work with an alternative source of variables.
* For example, you can define implementations of Variables that
* cover a servlet context, HTTP request or any similar structure.
*
* <h3>Example 10: Using Standard Extension Functions</h3>
* Using the standard extension functions, you can call methods on objects,
* static methods on classes and create objects using any constructor.
* The class names should be fully qualified.
* <p>
* Here's how you can create new objects:
* <pre><blockquote>
* Book book =
* (Book) context.getValue(
* "org.apache.commons.jxpath.example.Book.new ('John Updike')");
* </blockquote></pre>
*
* Here's how you can call static methods:
* <pre><blockquote>
* Book book =
* (Book) context.getValue(
* "org. apache.commons.jxpath.example.Book.getBestBook('John Updike')");
* </blockquote></pre>
*
* Here's how you can call regular methods:
* <pre><blockquote>
* String firstName = (String)context.getValue("getAuthorsFirstName($book)");
* </blockquote></pre>
* As you can see, the target of the method is specified as the first parameter
* of the function.
*
* <h3>Example 11: Using Custom Extension Functions</h3>
* Collections of custom extension functions can be implemented
* as {@link Functions Functions} objects or as Java classes, whose methods
* become extenstion functions.
* <p>
* Let's say the following class implements various formatting operations:
* <pre><blockquote>
* public class Formats {
* public static String date(Date d, String pattern){
* return new SimpleDateFormat(pattern).format(d);
* }
* ...
* }
* </blockquote></pre>
*
* We can register this class with a JXPathContext:
*
* <pre><blockquote>
* context.setFunctions(new ClassFunctions(Formats.class, "format"));
* ...
*
* context.getVariables().declareVariable("today", new Date());
* String today = (String)context.getValue("format:date($today, 'MM/dd/yyyy')");
*
* </blockquote></pre>
* You can also register whole packages of Java classes using PackageFunctions.
* <p>
* Also, see {@link FunctionLibrary FunctionLibrary}, which is a class
* that allows you to register multiple sets of extension functions with
* the same JXPathContext.
*
* <h2>Configuring JXPath</h2>
*
* JXPath uses JavaBeans introspection to discover properties of JavaBeans.
* You can provide alternative property lists by supplying
* custom JXPathBeanInfo classes (see {@link JXPathBeanInfo JXPathBeanInfo}).
*
* <h2>Notes</h2>
* <ul>
* <li> JXPath does not support DOM attributes for non-DOM objects. Even though
* XPaths like "para[@type='warning']" are legitimate, they will always produce
* empty results. The only attribute supported for JavaBeans is "name". The
* XPath "foo/bar" is equivalent to "foo[@name='bar']".
* </ul>
*
* See <a href="http://www.w3schools.com/xpath">XPath Tutorial by
* W3Schools</a><br>. Also see <a href="http://www.w3.org/TR/xpath">XML Path
* Language (XPath) Version 1.0</a><br><br>
*
* You will also find more information and examples in
* <a href="http://commons.apache.org/jxpath/users-guide.html">
* JXPath User's Guide</a>
*
*
* @author Dmitri Plotnikov
* @version $Revision$ $Date$
*/
public abstract class JXPathContext {
private static volatile JXPathContextFactory contextFactory;
private static volatile JXPathContext compilationContext;
private static final PackageFunctions GENERIC_FUNCTIONS =
new PackageFunctions("", null);
/** parent context */
protected JXPathContext parentContext;
/** context bean */
protected Object contextBean;
/** variables */
protected Variables vars;
/** functions */
protected Functions functions;
/** AbstractFactory */
protected AbstractFactory factory;
/** IdentityManager */
protected IdentityManager idManager;
/** KeyManager */
protected KeyManager keyManager;
/** decimal format map */
protected HashMap decimalFormats;
private Locale locale;
private boolean lenientSet = false;
private boolean lenient = false;
/**
* Creates a new JXPathContext with the specified object as the root node.
* @param contextBean Object
* @return JXPathContext
*/
public static JXPathContext newContext(Object contextBean) {
return getContextFactory().newContext(null, contextBean);
}
/**
* Creates a new JXPathContext with the specified bean as the root node and
* the specified parent context. Variables defined in a parent context can
* be referenced in XPaths passed to the child context.
* @param parentContext parent context
* @param contextBean Object
* @return JXPathContext
*/
public static JXPathContext newContext(
JXPathContext parentContext,
Object contextBean) {
return getContextFactory().newContext(parentContext, contextBean);
}
/**
* Acquires a context factory and caches it.
* @return JXPathContextFactory
*/
private static JXPathContextFactory getContextFactory () {
if (contextFactory == null) {
contextFactory = JXPathContextFactory.newInstance();
}
return contextFactory;
}
/**
* This constructor should remain protected - it is to be overridden by
* subclasses, but never explicitly invoked by clients.
* @param parentContext parent context
* @param contextBean Object
*/
protected JXPathContext(JXPathContext parentContext, Object contextBean) {
this.parentContext = parentContext;
this.contextBean = contextBean;
}
/**
* Returns the parent context of this context or null.
* @return JXPathContext
*/
public JXPathContext getParentContext() {
return parentContext;
}
/**
* Returns the JavaBean associated with this context.
* @return Object
*/
public Object getContextBean() {
return contextBean;
}
/**
* Returns a Pointer for the context bean.
* @return Pointer
*/
public abstract Pointer getContextPointer();
/**
* Returns a JXPathContext that is relative to the current JXPathContext.
* The supplied pointer becomes the context pointer of the new context.
* The relative context inherits variables, extension functions, locale etc
* from the parent context.
* @param pointer Pointer
* @return JXPathContext
*/
public abstract JXPathContext getRelativeContext(Pointer pointer);
/**
* Installs a custom implementation of the Variables interface.
* @param vars Variables
*/
public void setVariables(Variables vars) {
this.vars = vars;
}
/**
* Returns the variable pool associated with the context. If no such
* pool was specified with the {@link #setVariables} method,
* returns the default implementation of Variables,
* {@link BasicVariables BasicVariables}.
* @return Variables
*/
public Variables getVariables() {
if (vars == null) {
vars = new BasicVariables();
}
return vars;
}
/**
* Install a library of extension functions.
* @param functions Functions
* @see FunctionLibrary
*/
public void setFunctions(Functions functions) {
this.functions = functions;
}
/**
* Returns the set of functions installed on the context.
* @return Functions
*/
public Functions getFunctions() {
if (functions != null) {
return functions;
}
if (parentContext == null) {
return GENERIC_FUNCTIONS;
}
return null;
}
/**
* Install an abstract factory that should be used by the
* <code>createPath()</code> and <code>createPathAndSetValue()</code>
* methods.
* @param factory AbstractFactory
*/
public void setFactory(AbstractFactory factory) {
this.factory = factory;
}
/**
* Returns the AbstractFactory installed on this context.
* If none has been installed and this context has a parent context,
* returns the parent's factory. Otherwise returns null.
* @return AbstractFactory
*/
public AbstractFactory getFactory() {
if (factory == null && parentContext != null) {
return parentContext.getFactory();
}
return factory;
}
/**
* Set the locale for this context. The value of the "lang"
* attribute as well as the the lang() function will be
* affected by the locale. By default, JXPath uses
* <code>Locale.getDefault()</code>
* @param locale Locale
*/
public synchronized void setLocale(Locale locale) {
this.locale = locale;
}
/**
* Returns the locale set with setLocale. If none was set and
* the context has a parent, returns the parent's locale.
* Otherwise, returns Locale.getDefault().
* @return Locale
*/
public synchronized Locale getLocale() {
if (locale == null) {
if (parentContext != null) {
return parentContext.getLocale();
}
locale = Locale.getDefault();
}
return locale;
}
/**
* Sets {@link DecimalFormatSymbols} for a given name. The DecimalFormatSymbols
* can be referenced as the third, optional argument in the invocation of
* <code>format-number (number,format,decimal-format-name)</code> function.
* By default, JXPath uses the symbols for the current locale.
*
* @param name the format name or null for default format.
* @param symbols DecimalFormatSymbols
*/
public synchronized void setDecimalFormatSymbols(String name,
DecimalFormatSymbols symbols) {
if (decimalFormats == null) {
decimalFormats = new HashMap();
}
decimalFormats.put(name, symbols);
}
/**
* Get the named DecimalFormatSymbols.
* @param name key
* @return DecimalFormatSymbols
* @see #setDecimalFormatSymbols(String, DecimalFormatSymbols)
*/
public synchronized DecimalFormatSymbols getDecimalFormatSymbols(String name) {
if (decimalFormats == null) {
return parentContext == null ? null : parentContext.getDecimalFormatSymbols(name);
}
return (DecimalFormatSymbols) decimalFormats.get(name);
}
/**
* If the context is in the lenient mode, then getValue() returns null
* for inexistent paths. Otherwise, a path that does not map to
* an existing property will throw an exception. Note that if the
* property exists, but its value is null, the exception is <i>not</i>
* thrown.
* <p>
* By default, lenient = false
* @param lenient flag
*/
public synchronized void setLenient(boolean lenient) {
this.lenient = lenient;
lenientSet = true;
}
/**
* Learn whether this JXPathContext is lenient.
* @return boolean
* @see #setLenient(boolean)
*/
public synchronized boolean isLenient() {
if (!lenientSet && parentContext != null) {
return parentContext.isLenient();
}
return lenient;
}
/**
* Compiles the supplied XPath and returns an internal representation
* of the path that can then be evaluated. Use CompiledExpressions
* when you need to evaluate the same expression multiple times
* and there is a convenient place to cache CompiledExpression
* between invocations.
* @param xpath to compile
* @return CompiledExpression
*/
public static CompiledExpression compile(String xpath) {
if (compilationContext == null) {
compilationContext = JXPathContext.newContext(null);
}
return compilationContext.compilePath(xpath);
}
/**
* Overridden by each concrete implementation of JXPathContext
* to perform compilation. Is called by <code>compile()</code>.
* @param xpath to compile
* @return CompiledExpression
*/
protected abstract CompiledExpression compilePath(String xpath);
/**
* Finds the first object that matches the specified XPath. It is equivalent
* to <code>getPointer(xpath).getNode()</code>. Note that this method
* produces the same result as <code>getValue()</code> on object models
* like JavaBeans, but a different result for DOM/JDOM etc., because it
* returns the Node itself, rather than its textual contents.
*
* @param xpath the xpath to be evaluated
* @return the found object
*/
public Object selectSingleNode(String xpath) {
Pointer pointer = getPointer(xpath);
return pointer == null ? null : pointer.getNode();
}
/**
* Finds all nodes that match the specified XPath.
*
* @param xpath the xpath to be evaluated
* @return a list of found objects
*/
public List selectNodes(String xpath) {
ArrayList list = new ArrayList();
Iterator iterator = iteratePointers(xpath);
while (iterator.hasNext()) {
Pointer pointer = (Pointer) iterator.next();
list.add(pointer.getNode());
}
return list;
}
/**
* Evaluates the xpath and returns the resulting object. Primitive
* types are wrapped into objects.
* @param xpath to evaluate
* @return Object found
*/
public abstract Object getValue(String xpath);
/**
* Evaluates the xpath, converts the result to the specified class and
* returns the resulting object.
* @param xpath to evaluate
* @param requiredType required type
* @return Object found
*/
public abstract Object getValue(String xpath, Class requiredType);
/**
* Modifies the value of the property described by the supplied xpath.
* Will throw an exception if one of the following conditions occurs:
* <ul>
* <li>The xpath does not in fact describe an existing property
* <li>The property is not writable (no public, non-static set method)
* </ul>
* @param xpath indicating position
* @param value to set
*/
public abstract void setValue(String xpath, Object value);
/**
* Creates missing elements of the path by invoking an {@link AbstractFactory},
* which should first be installed on the context by calling {@link #setFactory}.
* <p>
* Will throw an exception if the AbstractFactory fails to create
* an instance for a path element.
* @param xpath indicating destination to create
* @return pointer to new location
*/
public abstract Pointer createPath(String xpath);
/**
* The same as setValue, except it creates intermediate elements of
* the path by invoking an {@link AbstractFactory}, which should first be
* installed on the context by calling {@link #setFactory}.
* <p>
* Will throw an exception if one of the following conditions occurs:
* <ul>
* <li>Elements of the xpath aleady exist, but the path does not in
* fact describe an existing property
* <li>The AbstractFactory fails to create an instance for an intermediate
* element.
* <li>The property is not writable (no public, non-static set method)
* </ul>
* @param xpath indicating position to create
* @param value to set
* @return pointer to new location
*/
public abstract Pointer createPathAndSetValue(String xpath, Object value);
/**
* Removes the element of the object graph described by the xpath.
* @param xpath indicating position to remove
*/
public abstract void removePath(String xpath);
/**
* Removes all elements of the object graph described by the xpath.
* @param xpath indicating positions to remove
*/
public abstract void removeAll(String xpath);
/**
* Traverses the xpath and returns an Iterator of all results found
* for the path. If the xpath matches no properties
* in the graph, the Iterator will be empty, but not null.
* @param xpath to iterate
* @return Iterator<Object>
*/
public abstract Iterator iterate(String xpath);
/**
* Traverses the xpath and returns a Pointer.
* A Pointer provides easy access to a property.
* If the xpath matches no properties
* in the graph, the pointer will be null.
* @param xpath desired
* @return Pointer
*/
public abstract Pointer getPointer(String xpath);
/**
* Traverses the xpath and returns an Iterator of Pointers.
* A Pointer provides easy access to a property.
* If the xpath matches no properties
* in the graph, the Iterator be empty, but not null.
* @param xpath to iterate
* @return Iterator<Pointer>
*/
public abstract Iterator iteratePointers(String xpath);
/**
* Install an identity manager that will be used by the context
* to look up a node by its ID.
* @param idManager IdentityManager to set
*/
public void setIdentityManager(IdentityManager idManager) {
this.idManager = idManager;
}
/**
* Returns this context's identity manager. If none has been installed,
* returns the identity manager of the parent context.
* @return IdentityManager
*/
public IdentityManager getIdentityManager() {
if (idManager == null && parentContext != null) {
return parentContext.getIdentityManager();
}
return idManager;
}
/**
* Locates a Node by its ID.
*
* @param id is the ID of the sought node.
* @return Pointer
*/
public Pointer getPointerByID(String id) {
IdentityManager manager = getIdentityManager();
if (manager != null) {
return manager.getPointerByID(this, id);
}
throw new JXPathException(
"Cannot find an element by ID - "
+ "no IdentityManager has been specified");
}
/**
* Install a key manager that will be used by the context
* to look up a node by a key value.
* @param keyManager KeyManager
*/
public void setKeyManager(KeyManager keyManager) {
this.keyManager = keyManager;
}
/**
* Returns this context's key manager. If none has been installed,
* returns the key manager of the parent context.
* @return KeyManager
*/
public KeyManager getKeyManager() {
if (keyManager == null && parentContext != null) {
return parentContext.getKeyManager();
}
return keyManager;
}
/**
* Locates a Node by a key value.
* @param key string
* @param value string
* @return Pointer found
*/
public Pointer getPointerByKey(String key, String value) {
KeyManager manager = getKeyManager();
if (manager != null) {
return manager.getPointerByKey(this, key, value);
}
throw new JXPathException(
"Cannot find an element by key - "
+ "no KeyManager has been specified");
}
/**
* Locates a NodeSet by key/value.
* @param key string
* @param value object
* @return NodeSet found
*/
public NodeSet getNodeSetByKey(String key, Object value) {
KeyManager manager = getKeyManager();
if (manager != null) {
return KeyManagerUtils.getExtendedKeyManager(manager)
.getNodeSetByKey(this, key, value);
}
throw new JXPathException("Cannot find an element by key - "
+ "no KeyManager has been specified");
}
/**
* Registers a namespace prefix.
*
* @param prefix A namespace prefix
* @param namespaceURI A URI for that prefix
*/
public void registerNamespace(String prefix, String namespaceURI) {
throw new UnsupportedOperationException(
"Namespace registration is not implemented by " + getClass());
}
/**
* Given a prefix, returns a registered namespace URI. If the requested
* prefix was not defined explicitly using the registerNamespace method,
* JXPathContext will then check the context node to see if the prefix is
* defined there. See
* {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer}.
*
* @param prefix The namespace prefix to look up
* @return namespace URI or null if the prefix is undefined.
*/
public String getNamespaceURI(String prefix) {
throw new UnsupportedOperationException(
"Namespace registration is not implemented by " + getClass());
}
/**
* Get the prefix associated with the specifed namespace URI.
* @param namespaceURI the ns URI to check.
* @return String prefix
* @since JXPath 1.3
*/
public String getPrefix(String namespaceURI) {
throw new UnsupportedOperationException(
"Namespace registration is not implemented by " + getClass());
}
/**
* Namespace prefixes can be defined implicitly by specifying a pointer to a
* context where the namespaces are defined. By default,
* NamespaceContextPointer is the same as the Context Pointer, see
* {@link #getContextPointer() getContextPointer()}
*
* @param namespaceContextPointer The pointer to the context where prefixes used in
* XPath expressions should be resolved.
*/
public void setNamespaceContextPointer(Pointer namespaceContextPointer) {
throw new UnsupportedOperationException(
"Namespace registration is not implemented by " + getClass());
}
/**
* Returns the namespace context pointer set with
* {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer()}
* or, if none has been specified, the context pointer otherwise.
*
* @return The namespace context pointer.
*/
public Pointer getNamespaceContextPointer() {
throw new UnsupportedOperationException(
"Namespace registration is not implemented by " + getClass());
}
/**
* Set the ExceptionHandler used by this context, if any.
* @param exceptionHandler to set
* @since 1.4
*/
public void setExceptionHandler(ExceptionHandler exceptionHandler) {
throw new UnsupportedOperationException(
"ExceptionHandler registration is not implemented by " + getClass());
}
}