/*
* 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.ri.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.commons.jxpath.AbstractFactory;
import org.apache.commons.jxpath.ClassFunctions;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathTestCase;
import org.apache.commons.jxpath.NestedTestBean;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
import org.apache.commons.jxpath.ri.compiler.TestFunctions;
import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
import org.apache.commons.jxpath.ri.model.dynabeans.DynaBeanModelTest;
/**
* Abstract superclass for Bean access with JXPath.
*
* @author Dmitri Plotnikov
* @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
*/
public abstract class BeanModelTestCase extends JXPathTestCase {
private JXPathContext context;
public void setUp() {
// if (context == null) {
context = JXPathContext.newContext(createContextBean());
context.setLocale(Locale.US);
context.setFactory(getAbstractFactory());
// }
}
protected abstract Object createContextBean();
protected abstract AbstractFactory getAbstractFactory();
/**
* Test property iterators, the core of the graph traversal engine
*/
public void testIndividualIterators() {
testIndividual(+1, 0, true, false, 0);
testIndividual(-1, 0, true, false, 4);
testIndividual(0, -1, true, true, 4);
testIndividual(+1, -1, true, true, 4);
testIndividual(-1, -1, true, true, 0);
testIndividual(0, 1, true, false, 2);
testIndividual(0, 1, true, true, 1);
testIndividual(0, 0, false, false, 4);
testIndividual(0, 0, false, true, 4);
}
private void testIndividual(
int relativePropertyIndex,
int offset,
boolean useStartLocation,
boolean reverse,
int expected)
{
PropertyOwnerPointer root =
(PropertyOwnerPointer) NodePointer.newNodePointer(
new QName(null, "root"),
createContextBean(),
Locale.getDefault());
NodeIterator it;
PropertyPointer start = null;
if (useStartLocation) {
start = root.getPropertyPointer();
start.setPropertyIndex(
relativeProperty(start, relativePropertyIndex));
start.setIndex(offset);
}
it =
root.childIterator(
new NodeNameTest(new QName(null, "integers")),
reverse,
start);
int size = 0;
while (it.setPosition(it.getPosition() + 1)) {
size++;
}
assertEquals(
"ITERATIONS: Individual, relativePropertyIndex="
+ relativePropertyIndex
+ ", offset="
+ offset
+ ", useStartLocation="
+ useStartLocation
+ ", reverse="
+ reverse,
expected,
size);
}
/**
* Test property iterators with multiple properties returned
*/
public void testMultipleIterators() {
testMultiple(0, 0, true, false, 20);
testMultiple(3, 0, true, false, 16);
testMultiple(3, -1, true, true, 8);
testMultiple(3, 0, true, true, 4);
testMultiple(0, 0, false, false, 21);
testMultiple(0, 0, false, true, 21);
testMultiple(3, 1, true, false, 15);
testMultiple(3, 3, true, false, 13);
}
private void testMultiple(
int propertyIndex,
int offset,
boolean useStartLocation,
boolean reverse,
int expected)
{
PropertyOwnerPointer root =
(PropertyOwnerPointer) NodePointer.newNodePointer(
new QName(null, "root"),
createContextBean(),
Locale.getDefault());
NodeIterator it;
PropertyPointer start = null;
if (useStartLocation) {
start = root.getPropertyPointer();
start.setPropertyIndex(propertyIndex);
start.setIndex(offset);
}
it = root.childIterator(null, reverse, start);
int size = 0;
while (it.setPosition(it.getPosition() + 1)) {
// System.err.println("LOC: " + it.getCurrentNodePointer());
size++;
}
assertEquals(
"ITERATIONS: Multiple, propertyIndex="
+ propertyIndex
+ ", offset="
+ offset
+ ", useStartLocation="
+ useStartLocation
+ ", reverse="
+ reverse,
expected,
size);
}
private int relativeProperty(PropertyPointer holder, int offset) {
String[] names = holder.getPropertyNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equals("integers")) {
return i + offset;
}
}
return -1;
}
public void testIteratePropertyArrayWithHasNext() {
JXPathContext context = JXPathContext.newContext(createContextBean());
Iterator it = context.iteratePointers("/integers");
List actual = new ArrayList();
while (it.hasNext()) {
actual.add(((Pointer) it.next()).asPath());
}
assertEquals(
"Iterating 'hasNext'/'next'<" + "/integers" + ">",
list(
"/integers[1]",
"/integers[2]",
"/integers[3]",
"/integers[4]"),
actual);
}
public void testIteratePropertyArrayWithoutHasNext() {
JXPathContext context = JXPathContext.newContext(createContextBean());
Iterator it = context.iteratePointers("/integers");
List actual = new ArrayList();
for (int i = 0; i < 4; i++) {
actual.add(it.next().toString());
}
assertEquals(
"Iterating 'next'<" + "/integers" + ">",
list(
"/integers[1]",
"/integers[2]",
"/integers[3]",
"/integers[4]"),
actual);
}
public void testIterateAndSet() {
JXPathContext context = JXPathContext.newContext(createContextBean());
Iterator it = context.iteratePointers("beans/int");
int i = 5;
while (it.hasNext()) {
NodePointer pointer = (NodePointer) it.next();
pointer.setValue(new Integer(i++));
}
it = context.iteratePointers("beans/int");
List actual = new ArrayList();
while (it.hasNext()) {
actual.add(((Pointer) it.next()).getValue());
}
assertEquals(
"Iterating <" + "beans/int" + ">",
list(new Integer(5), new Integer(6)),
actual);
}
/**
* Test contributed by Kate Dvortsova
*/
public void testIteratePointerSetValue() {
JXPathContext context = JXPathContext.newContext(createContextBean());
assertXPathValue(context, "/beans[1]/name", "Name 1");
assertXPathValue(context, "/beans[2]/name", "Name 2");
// Test setting via context
context.setValue("/beans[2]/name", "Name 2 set");
assertXPathValue(context, "/beans[2]/name", "Name 2 set");
// Restore original value
context.setValue("/beans[2]/name", "Name 2");
assertXPathValue(context, "/beans[2]/name", "Name 2");
int iterCount = 0;
Iterator iter = context.iteratePointers("/beans/name");
while (iter.hasNext()) {
iterCount++;
Pointer pointer = (Pointer) iter.next();
String s = (String) pointer.getValue();
s = s + "suffix";
pointer.setValue(s);
assertEquals("pointer.getValue", s, pointer.getValue());
// fails right here, the value isn't getting set in the bean.
assertEquals(
"context.getValue",
s,
context.getValue(pointer.asPath()));
}
assertEquals("Iteration count", 2, iterCount);
assertXPathValue(context, "/beans[1]/name", "Name 1suffix");
assertXPathValue(context, "/beans[2]/name", "Name 2suffix");
}
public void testRoot() {
assertXPathValueAndPointer(context, "/", context.getContextBean(), "/");
}
public void testAxisAncestor() {
// ancestor::
assertXPathValue(context, "int/ancestor::root = /", Boolean.TRUE);
assertXPathValue(
context,
"count(beans/name/ancestor-or-self::node())",
new Double(5));
assertXPathValue(
context,
"beans/name/ancestor-or-self::node()[3] = /",
Boolean.TRUE);
}
public void testAxisChild() {
assertXPathValue(context, "boolean", Boolean.FALSE);
assertXPathPointer(context, "boolean", "/boolean");
assertXPathPointerIterator(context, "boolean", list("/boolean"));
// Count elements in a child collection
assertXPathValue(context, "count(set)", new Double(3));
// assertXPathValue(context,"boolean/class/name", "java.lang.Boolean");
// Child with namespace - should not find any
assertXPathValueIterator(context, "foo:boolean", list());
// Count all children with a wildcard
assertXPathValue(context, "count(*)", new Double(21));
// Same, constrained by node type = node()
assertXPathValue(context, "count(child::node())", new Double(21));
}
public void testAxisChildNestedBean() {
// Nested bean
assertXPathValue(context, "nestedBean/name", "Name 0");
assertXPathPointer(context, "nestedBean/name", "/nestedBean/name");
assertXPathPointerIterator(
context,
"nestedBean/name",
list("/nestedBean/name"));
}
public void testAxisChildNestedCollection() {
assertXPathValueIterator(
context,
"integers",
list(
new Integer(1),
new Integer(2),
new Integer(3),
new Integer(4)));
assertXPathPointer(context, "integers", "/integers");
assertXPathPointerIterator(
context,
"integers",
list(
"/integers[1]",
"/integers[2]",
"/integers[3]",
"/integers[4]"));
}
public void testIndexPredicate() {
assertXPathValue(context, "integers[2]", new Integer(2));
assertXPathPointer(context, "integers[2]", "/integers[2]");
assertXPathPointerIterator(
context,
"integers[2]",
list("/integers[2]"));
assertXPathValue(context, "beans[1]/name", "Name 1");
assertXPathPointer(context, "beans[1]/name", "/beans[1]/name");
assertXPathValueIterator(
context,
"beans[1]/strings",
list("String 1", "String 2", "String 3"));
assertXPathValueIterator(
context,
"beans/strings[2]",
list("String 2", "String 2"));
// Find the first match
assertXPathValue(context, "beans/strings[2]", "String 2");
// Indexing in a set collected from a UnionContext
assertXPathValue(context, "(beans/strings[2])[1]", "String 2");
}
public void testAxisDescendant() {
// descendant::
assertXPathValue(context, "count(descendant::node())", new Double(65));
// Should not find any descendants with name root
assertXPathValue(context, "count(descendant::root)", new Double(0));
assertXPathValue(context, "count(descendant::name)", new Double(7));
}
public void testAxisDescendantOrSelf() {
// descendant-or-self::
assertXPathValueIterator(
context,
"descendant-or-self::name",
set(
"Name 1",
"Name 2",
"Name 3",
"Name 6",
"Name 0",
"Name 5",
"Name 4"));
// Same - abbreviated syntax
assertXPathValueIterator(
context,
"//name",
set(
"Name 1",
"Name 2",
"Name 3",
"Name 6",
"Name 0",
"Name 5",
"Name 4"));
// See that it actually finds self
assertXPathValue(
context,
"count(descendant-or-self::root)",
new Double(1));
// Combine descendant-or-self:: and and self::
assertXPathValue(context, "count(nestedBean//.)", new Double(7));
// Combine descendant-or-self:: and and self::name
assertXPathValue(context, "count(//self::beans)", new Double(2));
// Count all nodes in the tree
assertXPathValue(
context,
"count(descendant-or-self::node())",
new Double(66));
}
public void testAxisFollowing() {
// following::
assertXPathValue(
context,
"count(nestedBean/strings[2]/following::node())",
new Double(21));
assertXPathValue(
context,
"count(nestedBean/strings[2]/following::strings)",
new Double(7));
}
public void testAxisFollowingSibling() {
// following-sibling::
assertXPathValue(
context,
"count(/nestedBean/following-sibling::node())",
new Double(8));
assertXPathValue(
context,
"count(/nestedBean/following-sibling::object)",
new Double(1));
// Combine parent:: and following-sibling::
assertXPathValue(
context,
"count(/nestedBean/boolean/../following-sibling::node())",
new Double(8));
assertXPathValue(
context,
"count(/nestedBean/boolean/../following-sibling::object)",
new Double(1));
// Combine descendant:: and following-sibling::
assertXPathValue(
context,
"count(/descendant::boolean/following-sibling::node())",
new Double(53));
assertXPathValue(
context,
"count(/descendant::boolean/following-sibling::name)",
new Double(7));
}
public void testAxisParent() {
// parent::
assertXPathValue(context, "count(/beans/..)", new Double(1));
assertXPathValue(context, "count(//..)", new Double(9));
assertXPathValue(context, "count(//../..)", new Double(2));
assertXPathValueIterator(
context,
"//parent::beans/name",
list("Name 1", "Name 2"));
}
public void testAxisPreceding() {
// preceding::
assertXPathValue(
context,
"count(beans[2]/int/preceding::node())",
new Double(8));
assertXPathValue(
context,
"count(beans[2]/int/preceding::boolean)",
new Double(2));
}
public void testAxisPrecedingSibling() {
// preceding-sibling::
assertXPathValue(
context,
"count(/boolean/preceding-sibling::node())",
new Double(2));
assertXPathValue(
context,
"count(/nestedBean/int/../preceding-sibling::node())",
new Double(12));
assertXPathValue(
context,
"count(/descendant::int/preceding-sibling::node())",
new Double(10));
}
public void testAxisSelf() {
// self::
assertXPathValue(context, "self::node() = /", Boolean.TRUE);
assertXPathValue(context, "self::root = /", Boolean.TRUE);
}
public void testUnion() {
// Union - note corrected document order
assertXPathValueIterator(
context,
"integers | beans[1]/strings",
list(
"String 1",
"String 2",
"String 3",
new Integer(1),
new Integer(2),
new Integer(3),
new Integer(4)));
assertXPathValue(
context,
"count((integers | beans[1]/strings)[contains(., '1')])",
new Double(2));
assertXPathValue(
context,
"count((integers | beans[1]/strings)[name(.) = 'strings'])",
new Double(3));
// Note that the following is different from "integer[2]" -
// it is a filter expression
assertXPathValue(context, "(integers)[2]", new Integer(2));
}
public void testAxisAttribute() {
// Attributes are just like children to beans
assertXPathValue(context, "count(@*)", new Double(21.0));
// Unknown attribute
assertXPathValueLenient(context, "@foo", null);
}
/**
* Testing the pseudo-attribute "name" that java beans
* objects appear to have.
*/
public void testAttributeName() {
assertXPathValue(context, "nestedBean[@name = 'int']", new Integer(1));
assertXPathPointer(
context,
"nestedBean[@name = 'int']",
"/nestedBean/int");
}
public void testAttributeLang() {
assertXPathValue(context, "@xml:lang", "en-US");
assertXPathValue(context, "count(@xml:*)", new Double(1));
assertXPathValue(context, "lang('en')", Boolean.TRUE);
assertXPathValue(context, "lang('fr')", Boolean.FALSE);
}
public void testCoreFunctions() {
assertXPathValue(context, "boolean(boolean)", Boolean.TRUE);
assertXPathValue(context, "boolean(boolean = false())", Boolean.TRUE);
assertXPathValue(
context,
"boolean(integers[position() < 3])",
Boolean.TRUE);
assertXPathValue(
context,
"boolean(integers[position() > 4])",
Boolean.FALSE);
assertXPathValue(context, "sum(integers)", new Double(10));
assertXPathValueAndPointer(
context,
"integers[last()]",
new Integer(4),
"/integers[4]");
assertXPathValueAndPointer(
context,
"//strings[last()]",
"String 3",
"/beans[1]/strings[3]");
}
public void testBooleanPredicate() {
// use child axis
// bean[1]/int = 1
// bean[2]/int = 3
assertXPathValue(context, "beans[int > 2]/name", "Name 2");
assertXPathValueIterator(
context,
"beans[int > 2]/name",
list("Name 2"));
assertXPathValueIterator(
context,
"beans[int >= 1]/name",
list("Name 1", "Name 2"));
assertXPathValueIterator(
context,
"beans[int < 2]/name",
list("Name 1"));
assertXPathValueIterator(
context,
"beans[int <= 3]/name",
list("Name 1", "Name 2"));
assertXPathValueIterator(
context,
"beans[1]/strings[string-length() = 8]",
list("String 1", "String 2", "String 3"));
// use some fancy axis and the child axis in the predicate
assertXPathValueIterator(
context,
"//self::node()[name = 'Name 0']/name",
list("Name 0"));
// use context-dependent function in the predicate
assertXPathValue(
context,
"beans/strings[name(.)='strings'][2]",
"String 2");
// use context-independent function in the predicate
assertXPathValueIterator(
context,
"//self::node()[name(.) = concat('n', 'a', 'm', 'e')]",
list(
"Name 1",
"Name 2",
"Name 3",
"Name 6",
"Name 0",
"Name 5",
"Name 4"));
assertXPathValueIterator(
context,
"integers[position()<3]",
list(new Integer(1), new Integer(2)));
context.getVariables().declareVariable(
"temp",
context.getValue("beans"));
assertXPathValueIterator(
context,
"$temp[int < 2]/int",
list(new Integer(1)));
}
public void testDocumentOrder() {
assertDocumentOrder(context, "boolean", "int", -1);
assertDocumentOrder(context, "integers[1]", "integers[2]", -1);
assertDocumentOrder(context, "integers[1]", "integers[1]", 0);
assertDocumentOrder(context, "nestedBean/int", "nestedBean", 1);
assertDocumentOrder(
context,
"nestedBean/int",
"nestedBean/strings",
-1);
assertDocumentOrder(context, "nestedBean/int", "object/int", -1);
}
public void testSetPropertyValue() {
// Simple property
assertXPathSetValue(context, "int", new Integer(2));
// Simple property with conversion from string
assertXPathSetValue(context, "int", "3", new Integer(3));
// Simple property with conversion from array
assertXPathSetValue(context, "int", new int[] { 4 }, new Integer(4));
// Attribute (which is the same as a child for beans
assertXPathSetValue(context, "@int", new Integer(10));
}
public void testSetCollectionElement() {
// Collection element
assertXPathSetValue(context, "integers[2]", new Integer(5));
// Collection element with conversion
assertXPathSetValue(
context,
"integers[2]",
new int[] { 6 },
new Integer(6));
}
public void testSetContextDependentNode() {
// Find node without using SimplePathInterpreter
assertXPathSetValue(
context,
"integers[position() = 1]",
new Integer(8));
// Find node without using SimplePathInterpreter and set its property
assertXPathSetValue(
context,
"beans[name = 'Name 1']/int",
new Integer(9));
}
public void testSetNonPrimitiveValue() {
// First, let's see if we can set a collection element to null
assertXPathSetValue(context, "beans[2]", null);
// Now, assign it a whole bean
context.setValue("beans[2]", new NestedTestBean("Name 9"));
assertEquals(
"Modified <" + "beans[2]/name" + ">",
"Name 9",
context.getValue("beans[2]/name"));
}
public void testCreatePath() {
context.setValue("nestedBean", null);
// Calls factory.createObject(..., TestBean, "nestedBean")
assertXPathCreatePath(
context,
"/nestedBean/int",
new Integer(1),
"/nestedBean/int");
boolean ex = false;
try {
assertXPathCreatePath(
context,
"/nestedBean/beans[last() + 1]",
new Integer(1),
"/nestedBean/beans[last() + 1]");
}
catch (Exception e) {
ex = true;
}
assertTrue("Exception thrown on invalid path for creation", ex);
}
public void testCreatePathAndSetValue() {
context.setValue("nestedBean", null);
// Calls factory.createObject(..., TestBean, "nestedBean")
assertXPathCreatePathAndSetValue(
context,
"/nestedBean/int",
new Integer(2),
"/nestedBean/int");
}
public void testCreatePathExpandNewCollection() {
context.setValue("beans", null);
// Calls factory.createObject(..., testBean, "beans", 2),
// then factory.createObject(..., testBean, "beans", 2)
assertXPathCreatePath(
context,
"/beans[2]/int",
new Integer(1),
"/beans[2]/int");
}
public void testCreatePathAndSetValueExpandNewCollection() {
context.setValue("beans", null);
// Calls factory.createObject(..., testBean, "beans", 2),
// then factory.createObject(..., testBean, "beans", 2)
assertXPathCreatePathAndSetValue(
context,
"/beans[2]/int",
new Integer(2),
"/beans[2]/int");
}
public void testCreatePathExpandExistingCollection() {
// Calls factory.createObject(..., TestBean, "integers", 5)
// to expand collection
assertXPathCreatePathAndSetValue(
context,
"/integers[5]",
new Integer(3),
"/integers[5]");
}
public void testCreatePathExpandExistingCollectionAndSetProperty() {
// Another, but the collection already exists
assertXPathCreatePath(
context,
"/beans[3]/int",
new Integer(1),
"/beans[3]/int");
}
public void testCreatePathAndSetValueExpandExistingCollection() {
// Another, but the collection already exists
assertXPathCreatePathAndSetValue(
context,
"/beans[3]/int",
new Integer(2),
"/beans[3]/int");
}
public void testCreatePathCreateBeanExpandCollection() {
context.setValue("nestedBean", null);
// Calls factory.createObject(..., TestBean, "nestedBean")
// Calls factory.createObject(..., nestedBean, "strings", 2)
assertXPathCreatePath(
context,
"/nestedBean/strings[2]",
"String 2",
"/nestedBean/strings[2]");
}
public void testCreatePathAndSetValueCreateBeanExpandCollection() {
context.setValue("nestedBean", null);
// Calls factory.createObject(..., TestBean, "nestedBean")
// Calls factory.createObject(..., nestedBean, "strings", 2)
assertXPathCreatePathAndSetValue(
context,
"/nestedBean/strings[2]",
"Test",
"/nestedBean/strings[2]");
}
public void testRemovePathPropertyValue() {
// Remove property value
context.removePath("nestedBean/int");
assertEquals(
"Remove property value",
new Integer(0),
context.getValue("nestedBean/int"));
}
public void testRemovePathArrayElement() {
// Assigns a new array to the property
context.removePath("nestedBean/strings[1]");
assertEquals(
"Remove array element",
"String 2",
context.getValue("nestedBean/strings[1]"));
}
public void testRemoveAllArrayElements() {
context.removeAll("nestedBean/strings");
assertXPathValueIterator(
context,
"nestedBean/strings",
list());
}
public void testRemoveAllListElements() {
context.removeAll("list");
assertXPathValueIterator(
context,
"list",
this instanceof DynaBeanModelTest ? list(null, null, null) : list());
}
public void testRemoveAllMapEntries() {
context.removeAll("map/*");
assertXPathValue(
context,
"map",
Collections.EMPTY_MAP);
}
public void testRemovePathBeanValue() {
context.removePath("nestedBean");
assertEquals(
"Remove collection element",
null,
context.getValue("nestedBean"));
}
public void testRelativeContextRelativePath() {
JXPathContext relative =
context.getRelativeContext(context.getPointer("nestedBean"));
assertXPathValueAndPointer(relative,
"int",
new Integer(1),
"/nestedBean/int");
}
public void testRelativeContextAbsolutePath() {
JXPathContext relative =
context.getRelativeContext(context.getPointer("nestedBean"));
assertXPathValueAndPointer(relative,
"/integers[2]",
new Integer(2),
"/integers[2]");
}
public void testRelativeContextParent() {
JXPathContext relative =
context.getRelativeContext(context.getPointer("nestedBean"));
assertXPathValueAndPointer(relative,
"../integers[2]",
new Integer(2),
"/integers[2]");
}
public void testRelativeContextInheritance() {
context.setFunctions(new ClassFunctions(TestFunctions.class, "test"));
JXPathContext relative =
context.getRelativeContext(context.getPointer("nestedBean"));
assertXPathValue(relative,
"test:countPointers(strings)",
new Integer(3));
}
}