001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jxpath.ri.model;
017:
018: import org.apache.commons.jxpath.AbstractFactory;
019: import org.apache.commons.jxpath.IdentityManager;
020: import org.apache.commons.jxpath.JXPathContext;
021: import org.apache.commons.jxpath.JXPathException;
022: import org.apache.commons.jxpath.JXPathTestCase;
023: import org.apache.commons.jxpath.Pointer;
024: import org.apache.commons.jxpath.Variables;
025: import org.apache.commons.jxpath.xml.DocumentContainer;
026:
027: /**
028: * Abstract superclass for pure XPath 1.0. Subclasses
029: * apply the same XPaths to contexts using different models:
030: * DOM, JDOM etc.
031: *
032: * @author Dmitri Plotnikov
033: * @version $Revision: 1.23 $ $Date: 2004/06/30 00:29:13 $
034: */
035:
036: public abstract class XMLModelTestCase extends JXPathTestCase {
037: protected JXPathContext context;
038:
039: /**
040: * Construct a new instance of this test case.
041: *
042: * @param name Name of the test case
043: */
044: public XMLModelTestCase(String name) {
045: super (name);
046: }
047:
048: public void setUp() {
049: if (context == null) {
050: DocumentContainer docCtr = createDocumentContainer();
051: context = createContext();
052: Variables vars = context.getVariables();
053: vars.declareVariable("document", docCtr.getValue());
054: vars.declareVariable("container", docCtr);
055: vars.declareVariable("element", context.getPointer(
056: "vendor/location/address/street").getNode());
057: }
058: }
059:
060: protected abstract String getModel();
061:
062: protected DocumentContainer createDocumentContainer() {
063: return new DocumentContainer(JXPathTestCase.class
064: .getResource("Vendor.xml"), getModel());
065: }
066:
067: protected abstract AbstractFactory getAbstractFactory();
068:
069: protected JXPathContext createContext() {
070: JXPathContext context = JXPathContext
071: .newContext(createDocumentContainer());
072: context.setFactory(getAbstractFactory());
073: context.registerNamespace("product", "productNS");
074: return context;
075: }
076:
077: /**
078: * An XML signature is used to determine if we have the right result
079: * after a modification of XML by JXPath. It is basically a piece
080: * of simplified XML.
081: */
082: protected abstract String getXMLSignature(Object node,
083: boolean elements, boolean attributes, boolean text,
084: boolean pi);
085:
086: protected void assertXMLSignature(JXPathContext context,
087: String path, String signature, boolean elements,
088: boolean attributes, boolean text, boolean pi) {
089: Object node = context.getPointer(path).getNode();
090: String sig = getXMLSignature(node, elements, attributes, text,
091: pi);
092: assertEquals("XML Signature mismatch: ", signature, sig);
093: }
094:
095: // ------------------------------------------------ Individual Test Methods
096:
097: public void testDocumentOrder() {
098: assertDocumentOrder(context, "vendor/location",
099: "vendor/location/address/street", -1);
100:
101: assertDocumentOrder(context, "vendor/location[@id = '100']",
102: "vendor/location[@id = '101']", -1);
103:
104: assertDocumentOrder(context, "vendor//price:amount",
105: "vendor/location", 1);
106: }
107:
108: public void testSetValue() {
109: assertXPathSetValue(context, "vendor/location[@id = '100']",
110: "New Text");
111:
112: assertXMLSignature(context, "vendor/location[@id = '100']",
113: "<E>New Text</E>", false, false, true, false);
114:
115: assertXPathSetValue(context, "vendor/location[@id = '101']",
116: "Replacement Text");
117:
118: assertXMLSignature(context, "vendor/location[@id = '101']",
119: "<E>Replacement Text</E>", false, false, true, false);
120: }
121:
122: /**
123: * Test JXPathContext.createPath() with various arguments
124: */
125: public void testCreatePath() {
126: // Create a DOM element
127: assertXPathCreatePath(context, "/vendor[1]/location[3]", "",
128: "/vendor[1]/location[3]");
129:
130: // Create a DOM element with contents
131: assertXPathCreatePath(context,
132: "/vendor[1]/location[3]/address/street", "",
133: "/vendor[1]/location[3]/address[1]/street[1]");
134:
135: // Create a DOM attribute
136: assertXPathCreatePath(context,
137: "/vendor[1]/location[2]/@manager", "",
138: "/vendor[1]/location[2]/@manager");
139:
140: assertXPathCreatePath(context, "/vendor[1]/location[1]/@name",
141: "local", "/vendor[1]/location[1]/@name");
142:
143: assertXPathCreatePathAndSetValue(context,
144: "/vendor[1]/location[4]/@manager", "",
145: "/vendor[1]/location[4]/@manager");
146:
147: context.registerNamespace("price", "priceNS");
148:
149: // Create a DOM element
150: assertXPathCreatePath(context,
151: "/vendor[1]/price:foo/price:bar", "",
152: "/vendor[1]/price:foo[1]/price:bar[1]");
153: }
154:
155: /**
156: * Test JXPath.createPathAndSetValue() with various arguments
157: */
158: public void testCreatePathAndSetValue() {
159: // Create a XML element
160: assertXPathCreatePathAndSetValue(context, "vendor/location[3]",
161: "", "/vendor[1]/location[3]");
162:
163: // Create a DOM element with contents
164: assertXPathCreatePathAndSetValue(context,
165: "vendor/location[3]/address/street", "Lemon Circle",
166: "/vendor[1]/location[3]/address[1]/street[1]");
167:
168: // Create an attribute
169: assertXPathCreatePathAndSetValue(context,
170: "vendor/location[2]/@manager", "John Doe",
171: "/vendor[1]/location[2]/@manager");
172:
173: assertXPathCreatePathAndSetValue(context,
174: "vendor/location[1]/@manager", "John Doe",
175: "/vendor[1]/location[1]/@manager");
176:
177: assertXPathCreatePathAndSetValue(context,
178: "/vendor[1]/location[4]/@manager", "James Dow",
179: "/vendor[1]/location[4]/@manager");
180:
181: assertXPathCreatePathAndSetValue(
182: context,
183: "vendor/product/product:name/attribute::price:language",
184: "English",
185: "/vendor[1]/product[1]/product:name[1]/@price:language");
186:
187: context.registerNamespace("price", "priceNS");
188:
189: // Create a DOM element
190: assertXPathCreatePathAndSetValue(context,
191: "/vendor[1]/price:foo/price:bar", "123.20",
192: "/vendor[1]/price:foo[1]/price:bar[1]");
193: }
194:
195: /**
196: * Test JXPathContext.removePath() with various arguments
197: */
198: public void testRemovePath() {
199: // Remove XML nodes
200: context
201: .removePath("vendor/location[@id = '101']//street/text()");
202: assertEquals("Remove DOM text", "", context
203: .getValue("vendor/location[@id = '101']//street"));
204:
205: context.removePath("vendor/location[@id = '101']//street");
206: assertEquals(
207: "Remove DOM element",
208: new Double(0),
209: context
210: .getValue("count(vendor/location[@id = '101']//street)"));
211:
212: context.removePath("vendor/location[@id = '100']/@name");
213: assertEquals("Remove DOM attribute", new Double(0), context
214: .getValue("count(vendor/location[@id = '100']/@name)"));
215: }
216:
217: public void testID() {
218: context.setIdentityManager(new IdentityManager() {
219: public Pointer getPointerByID(JXPathContext context,
220: String id) {
221: NodePointer ptr = (NodePointer) context.getPointer("/");
222: ptr = ptr.getValuePointer(); // Unwrap the container
223: return ptr.getPointerByID(context, id);
224: }
225: });
226:
227: assertXPathValueAndPointer(context, "id(101)//street",
228: "Tangerine Drive", "id('101')/address[1]/street[1]");
229:
230: assertXPathPointerLenient(context, "id(105)/address/street",
231: "id(105)/address/street");
232: }
233:
234: public void testAxisChild() {
235: assertXPathValue(context, "vendor/location/address/street",
236: "Orchard Road");
237:
238: // child:: - first child does not match, need to search
239: assertXPathValue(context, "vendor/location/address/city",
240: "Fruit Market");
241:
242: // local-name(qualified)
243: assertXPathValue(context,
244: "local-name(vendor/product/price:amount)", "amount");
245:
246: // local-name(non-qualified)
247: assertXPathValue(context, "local-name(vendor/location)",
248: "location");
249:
250: // name (qualified)
251: assertXPathValue(context, "name(vendor/product/price:amount)",
252: "value:amount");
253:
254: // name (non-qualified)
255: assertXPathValue(context, "name(vendor/location)", "location");
256:
257: // namespace-uri (qualified)
258: assertXPathValue(context,
259: "namespace-uri(vendor/product/price:amount)", "priceNS");
260:
261: // default namespace does not affect search
262: assertXPathValue(context, "vendor/product/prix", "934.99");
263:
264: assertXPathValue(context, "/vendor/contact[@name='jim']", "Jim");
265:
266: boolean nsv = false;
267: try {
268: context.setLenient(false);
269: context.getValue("/vendor/contact[@name='jane']");
270: } catch (JXPathException ex) {
271: nsv = true;
272: }
273: assertTrue("No such value: /vendor/contact[@name='jim']", nsv);
274:
275: nsv = false;
276: try {
277: context.setLenient(false);
278: context.getValue("/vendor/contact[@name='jane']/*");
279: } catch (JXPathException ex) {
280: nsv = true;
281: }
282: assertTrue("No such value: /vendor/contact[@name='jane']/*",
283: nsv);
284:
285: // child:: with a wildcard
286: assertXPathValue(context, "count(vendor/product/price:*)",
287: new Double(2));
288:
289: // child:: with the default namespace
290: assertXPathValue(context, "count(vendor/product/*)",
291: new Double(4));
292:
293: // child:: with a qualified name
294: assertXPathValue(context, "vendor/product/price:amount",
295: "45.95");
296: }
297:
298: public void testAxisChildIndexPredicate() {
299: assertXPathValue(context, "vendor/location[2]/address/street",
300: "Tangerine Drive");
301: }
302:
303: public void testAxisDescendant() {
304: // descendant::
305: assertXPathValue(context, "//street", "Orchard Road");
306:
307: // descendent:: with a namespace and wildcard
308: assertXPathValue(context, "count(//price:*)", new Double(2));
309:
310: assertXPathValueIterator(context, "vendor//saleEnds",
311: list("never"));
312:
313: assertXPathValueIterator(context, "vendor//promotion", list(""));
314:
315: assertXPathValueIterator(context,
316: "vendor//saleEnds[../@stores = 'all']", list("never"));
317:
318: assertXPathValueIterator(context,
319: "vendor//promotion[../@stores = 'all']", list(""));
320: }
321:
322: // public void testAxisDescendantDocumentOrder() {
323: // Iterator iter = context.iteratePointers("//*");
324: // while (iter.hasNext()) {
325: // System.err.println(iter.next());
326: // }
327: // }
328:
329: public void testAxisParent() {
330: // parent::
331: assertXPathPointer(context, "//street/..",
332: "/vendor[1]/location[1]/address[1]");
333:
334: // parent:: (note reverse document order)
335: assertXPathPointerIterator(context, "//street/..", list(
336: "/vendor[1]/location[2]/address[1]",
337: "/vendor[1]/location[1]/address[1]"));
338:
339: // parent:: with a namespace and wildcard
340: assertXPathValue(context,
341: "vendor/product/price:sale/saleEnds/parent::price:*"
342: + "/saleEnds", "never");
343: }
344:
345: public void testAxisFollowingSibling() {
346: // following-sibling::
347: assertXPathValue(context,
348: "vendor/location[.//employeeCount = 10]/"
349: + "following-sibling::location//street",
350: "Tangerine Drive");
351:
352: // following-sibling:: produces the correct pointer
353: assertXPathPointer(context,
354: "vendor/location[.//employeeCount = 10]/"
355: + "following-sibling::location//street",
356: "/vendor[1]/location[2]/address[1]/street[1]");
357: }
358:
359: public void testAxisPrecedingSibling() {
360: // preceding-sibling:: produces the correct pointer
361: assertXPathPointer(context,
362: "//location[2]/preceding-sibling::location//street",
363: "/vendor[1]/location[1]/address[1]/street[1]");
364: }
365:
366: public void testAxisAttribute() {
367: // attribute::
368: assertXPathValue(context, "vendor/location/@id", "100");
369:
370: // attribute:: produces the correct pointer
371: assertXPathPointer(context, "vendor/location/@id",
372: "/vendor[1]/location[1]/@id");
373:
374: // iterate over attributes
375: assertXPathValueIterator(context, "vendor/location/@id", list(
376: "100", "101"));
377:
378: // Using different prefixes for the same namespace
379: assertXPathValue(context,
380: "vendor/product/price:amount/@price:discount", "10%");
381:
382: // namespace uri for an attribute
383: assertXPathValue(
384: context,
385: "namespace-uri(vendor/product/price:amount/@price:discount)",
386: "priceNS");
387:
388: // local name of an attribute
389: assertXPathValue(
390: context,
391: "local-name(vendor/product/price:amount/@price:discount)",
392: "discount");
393:
394: // name for an attribute
395: assertXPathValue(context,
396: "name(vendor/product/price:amount/@price:discount)",
397: "price:discount");
398:
399: // attribute:: with the default namespace
400: assertXPathValue(context,
401: "vendor/product/price:amount/@discount", "20%");
402:
403: // namespace uri of an attribute with the default namespace
404: assertXPathValue(context,
405: "namespace-uri(vendor/product/price:amount/@discount)",
406: "");
407:
408: // local name of an attribute with the default namespace
409: assertXPathValue(context,
410: "local-name(vendor/product/price:amount/@discount)",
411: "discount");
412:
413: // name of an attribute with the default namespace
414: assertXPathValue(context,
415: "name(vendor/product/price:amount/@discount)",
416: "discount");
417:
418: // attribute:: with a namespace and wildcard
419: assertXPathValueIterator(context,
420: "vendor/product/price:amount/@price:*", list("10%"));
421:
422: // attribute:: with a wildcard
423: assertXPathValueIterator(context, "vendor/location[1]/@*", set(
424: "100", "", "local"));
425:
426: // attribute:: with default namespace and wildcard
427: assertXPathValueIterator(context,
428: "vendor/product/price:amount/@*", list("20%"));
429:
430: // Empty attribute
431: assertXPathValue(context, "vendor/location/@manager", "");
432:
433: // Missing attribute
434: assertXPathValueLenient(context, "vendor/location/@missing",
435: null);
436:
437: // Missing attribute with namespace
438: assertXPathValueLenient(context,
439: "vendor/location/@miss:missing", null);
440:
441: // Using attribute in a predicate
442: assertXPathValue(context, "vendor/location[@id='101']//street",
443: "Tangerine Drive");
444:
445: assertXPathValueIterator(context,
446: "/vendor/location[1]/@*[name()!= 'manager']", list(
447: "100", "local"));
448: }
449:
450: public void testAxisNamespace() {
451: // namespace::
452: assertXPathValueAndPointer(context,
453: "vendor/product/prix/namespace::price", "priceNS",
454: "/vendor[1]/product[1]/prix[1]/namespace::price");
455:
456: // namespace::*
457: assertXPathValue(context, "count(vendor/product/namespace::*)",
458: new Double(3));
459:
460: // name of namespace
461: assertXPathValue(context,
462: "name(vendor/product/prix/namespace::price)", "price");
463:
464: // local name of namespace
465: assertXPathValue(context,
466: "local-name(vendor/product/prix/namespace::price)",
467: "price");
468: }
469:
470: public void testAxisAncestor() {
471: // ancestor::
472: assertXPathValue(context, "vendor/product/price:sale/saleEnds/"
473: + "ancestor::price:sale/saleEnds", "never");
474:
475: // ancestor:: with a wildcard
476: assertXPathValue(context,
477: "vendor/product/price:sale/saleEnds/ancestor::price:*"
478: + "/saleEnds", "never");
479: }
480:
481: public void testAxisAncestorOrSelf() {
482: // ancestor-or-self::
483: assertXPathValue(context, "vendor/product/price:sale/"
484: + "ancestor-or-self::price:sale/saleEnds", "never");
485: }
486:
487: public void testAxisFollowing() {
488: assertXPathValueIterator(context,
489: "vendor/contact/following::location//street", list(
490: "Orchard Road", "Tangerine Drive"));
491:
492: // following:: with a namespace
493: assertXPathValue(context,
494: "//location/following::price:sale/saleEnds", "never");
495: }
496:
497: public void testAxisSelf() {
498: // self:: with a namespace
499: assertXPathValue(context,
500: "//price:sale/self::price:sale/saleEnds", "never");
501:
502: // self:: with an unmatching name
503: assertXPathValueLenient(context,
504: "//price:sale/self::x/saleEnds", null);
505: }
506:
507: public void testNodeTypeComment() {
508: // comment()
509: assertXPathValue(context, "//product/comment()",
510: "We are not buying this product, ever");
511: }
512:
513: public void testNodeTypeText() {
514: // text()
515: assertXPathValue(context, "//product/text()[. != '']",
516: "We love this product.");
517:
518: // text() pointer
519: assertXPathPointer(context, "//product/text()",
520: "/vendor[1]/product[1]/text()[1]");
521:
522: }
523:
524: public void testNodeTypeProcessingInstruction() {
525: // processing-instruction() without an argument
526: assertXPathValue(context, "//product/processing-instruction()",
527: "do not show anybody");
528:
529: // processing-instruction() with an argument
530: assertXPathValue(context,
531: "//product/processing-instruction('report')",
532: "average only");
533:
534: // processing-instruction() pointer without an argument
535: assertXPathPointer(context,
536: "//product/processing-instruction('report')",
537: "/vendor[1]/product[1]/processing-instruction('report')[1]");
538:
539: // processing-instruction name
540: assertXPathValue(context,
541: "name(//product/processing-instruction()[1])",
542: "security");
543: }
544:
545: public void testLang() {
546: // xml:lang built-in attribute
547: assertXPathValue(context, "//product/prix/@xml:lang", "fr");
548:
549: // lang() used the built-in xml:lang attribute
550: assertXPathValue(context, "//product/prix[lang('fr')]",
551: "934.99");
552:
553: // Default language
554: assertXPathValue(context,
555: "//product/price:sale[lang('en')]/saleEnds", "never");
556: }
557:
558: public void testDocument() {
559: assertXPathValue(context,
560: "$document/vendor/location[1]//street", "Orchard Road");
561:
562: assertXPathPointer(context,
563: "$document/vendor/location[1]//street",
564: "$document/vendor[1]/location[1]/address[1]/street[1]");
565:
566: assertXPathValue(context, "$document/vendor//street",
567: "Orchard Road");
568: }
569:
570: public void testContainer() {
571: assertXPathValue(context, "$container/vendor//street",
572: "Orchard Road");
573:
574: assertXPathValue(context, "$container//street", "Orchard Road");
575:
576: assertXPathPointer(context, "$container//street",
577: "$container/vendor[1]/location[1]/address[1]/street[1]");
578:
579: // Conversion to number
580: assertXPathValue(context,
581: "number(vendor/location/employeeCount)", new Double(10));
582: }
583:
584: public void testElementInVariable() {
585: assertXPathValue(context, "$element", "Orchard Road");
586: }
587:
588: public void testTypeConversions() {
589: // Implicit conversion to number
590: assertXPathValue(context, "vendor/location/employeeCount + 1",
591: new Double(11));
592:
593: // Implicit conversion to boolean
594: assertXPathValue(context,
595: "vendor/location/employeeCount and true()",
596: Boolean.TRUE);
597: }
598:
599: public void testBooleanFunction() {
600: assertXPathValue(context,
601: "boolean(vendor//saleEnds[../@stores = 'all'])",
602: Boolean.TRUE);
603:
604: assertXPathValue(context,
605: "boolean(vendor//promotion[../@stores = 'all'])",
606: Boolean.TRUE);
607:
608: assertXPathValue(context,
609: "boolean(vendor//promotion[../@stores = 'some'])",
610: Boolean.FALSE);
611: }
612:
613: public void testFunctionsLastAndPosition() {
614: assertXPathPointer(context, "vendor//location[last()]",
615: "/vendor[1]/location[2]");
616: }
617:
618: public void testNamespaceMapping() {
619: context.registerNamespace("rate", "priceNS");
620: context.registerNamespace("goods", "productNS");
621:
622: assertEquals("Context node namespace resolution", "priceNS",
623: context.getNamespaceURI("price"));
624:
625: assertEquals("Registered namespace resolution", "priceNS",
626: context.getNamespaceURI("rate"));
627:
628: // child:: with a namespace and wildcard
629: assertXPathValue(context, "count(vendor/product/rate:*)",
630: new Double(2));
631:
632: // Preference for externally registered namespace prefix
633: assertXPathValueAndPointer(context, "//product:name",
634: "Box of oranges", "/vendor[1]/product[1]/goods:name[1]");
635: }
636: }
|