dmitri 2002/10/12 19:59:02 Modified: jxpath/src/java/org/apache/commons/jxpath/ri/axes SimplePathInterpreter.java jxpath/src/java/org/apache/commons/jxpath/ri/compiler Path.java jxpath/src/java/org/apache/commons/jxpath/ri/model/beans NullPropertyPointer.java BeanAttributeIterator.java jxpath/src/java/org/apache/commons/jxpath/ri/model NodePointer.java jxpath/src/java/org/apache/commons/jxpath/ri/model/jdom JDOMNodePointer.java jxpath/src/java/org/apache/commons/jxpath/ri/model/dom DOMNodePointer.java Log: 1. Now bean's properties can be accessed either with the child:: or the attribute:: axis. 2. Missing attributes can be created with an AbstractFactory Revision Changes Path 1.7 +60 -29 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SimplePathInterpreter.java Index: SimplePathInterpreter.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/axes/SimplePathInterpreter.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- SimplePathInterpreter.java 10 Aug 2002 01:35:47 -0000 1.6 +++ SimplePathInterpreter.java 13 Oct 2002 02:59:01 -0000 1.7 @@ -119,7 +119,7 @@ * starts with the given root, which is the result of evaluation * of the root expression of the expression path, applies the * given predicates to it and then follows the given steps. - * All steps must have the axis "child::" + * All steps must have the axis "child::" or "attribute::" * and a name test. They can also optionally have predicates * of type [@name=...] or simply [...] interpreted as an index. */ @@ -204,17 +204,7 @@ Step[] steps, int current_step) { Step step = steps[current_step]; - NodePointer childPointer; - if (step.getAxis() == Compiler.AXIS_CHILD){ - // Treat the name test as a property name - QName name = ((NodeNameTest)step.getNodeTest()).getNodeName(); - - childPointer = parentPointer.getPropertyPointer(); - ((PropertyPointer)childPointer).setPropertyName(name.toString()); - } - else { - childPointer = parentPointer; - } + NodePointer childPointer = createChildPointerForStep(parentPointer, step); if (!childPointer.isActual()){ // The property does not exist - create a null pointer. @@ -272,10 +262,14 @@ Step[] steps, int current_step) { Step step = steps[current_step]; + + if (step.getAxis() == Compiler.AXIS_SELF){ + return doStep(context, parentPointer, steps, current_step + 1); + } + int bestQuality = 0; NodePointer bestMatch = null; - NodeIterator it = - parentPointer.childIterator(step.getNodeTest(), false, null); + NodeIterator it = getNodeIterator(parentPointer, step); if (it != null){ for (int i = 1; it.setPosition(i); i++){ NodePointer childPointer = it.getNodePointer(); @@ -316,15 +310,8 @@ Step step = steps[current_step]; Expression predicates[] = step.getPredicates(); - NodePointer childPointer; - if (step.getAxis() == Compiler.AXIS_CHILD){ - QName name = ((NodeNameTest)step.getNodeTest()).getNodeName(); - childPointer = parentPointer.getPropertyPointer(); - ((PropertyPointer)childPointer).setPropertyName(name.toString()); - } - else { - childPointer = parentPointer; - } + NodePointer childPointer = + createChildPointerForStep(parentPointer, step); if (!childPointer.isActual()){ // Property does not exist - return a null pointer return createNullPointer( @@ -336,6 +323,28 @@ context, childPointer, steps, current_step, predicates, 0); } + private static NodePointer createChildPointerForStep( + PropertyOwnerPointer parentPointer, Step step) + { + int axis = step.getAxis(); + if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE){ + NodePointer childPointer; + QName name = ((NodeNameTest)step.getNodeTest()).getNodeName(); + if (axis == Compiler.AXIS_ATTRIBUTE && isLangAttribute(name)){ + childPointer = new LangAttributePointer(parentPointer); + } + else { + childPointer = parentPointer.getPropertyPointer(); + ((PropertyPointer)childPointer).setPropertyName(name.toString()); + childPointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE); + } + return childPointer; + } + else { + return parentPointer; + } + } + /** * A path that starts with a standard InfoSet node, e.g. a DOM Node. * The method evaluates the first predicate in a special way and @@ -348,7 +357,8 @@ Step step = steps[current_step]; Expression predicates[] = step.getPredicates(); - if (step.getAxis() == Compiler.AXIS_SELF){ + int axis = step.getAxis(); + if (axis == Compiler.AXIS_SELF){ return doPredicate(context, parent, steps, current_step, predicates, 0); } @@ -361,8 +371,7 @@ // It is a very common use case, so it deserves individual // attention if (predicates.length == 1){ - NodeIterator it = parent.childIterator( - step.getNodeTest(), false, null); + NodeIterator it = getNodeIterator(parent, step); NodePointer pointer = null; if (it != null){ if (predicate instanceof NameAttributeTest){ // [@name = key] @@ -387,8 +396,7 @@ } } else { - NodeIterator it = parent.childIterator( - step.getNodeTest(), false, null); + NodeIterator it = getNodeIterator(parent, step); if (it != null){ List list = new ArrayList(); for (int i = 1; it.setPosition(i); i++){ @@ -686,10 +694,12 @@ Step step = steps[current_step]; - if (step.getAxis() == Compiler.AXIS_CHILD){ + int axis = step.getAxis(); + if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE){ NullPropertyPointer pointer = new NullPropertyPointer(parent); QName name = ((NodeNameTest)step.getNodeTest()).getNodeName(); pointer.setPropertyName(name.toString()); + pointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE); parent = pointer; } // else { it is self::node() } @@ -729,5 +739,26 @@ // Proceed with the remaining steps return createNullPointer( context, parent, steps, current_step + 1); + } + + private static NodeIterator getNodeIterator(NodePointer pointer, Step step){ + if (step.getAxis() == Compiler.AXIS_CHILD){ + return pointer.childIterator(step.getNodeTest(), false, null); + } + else { // Compiler.AXIS_ATTRIBUTE + if (!(step.getNodeTest() instanceof NodeNameTest)){ + throw new UnsupportedOperationException( + "Not supported node test for attributes: " + + step.getNodeTest()); + } + return pointer.attributeIterator( + ((NodeNameTest)step.getNodeTest()).getNodeName()); + } + } + + private static boolean isLangAttribute(QName name){ + return name.getPrefix() != null && + name.getPrefix().equals("xml") && + name.getName().equals("lang"); } } 1.6 +6 -5 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java Index: Path.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/compiler/Path.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Path.java 10 Aug 2002 01:39:29 -0000 1.5 +++ Path.java 13 Oct 2002 02:59:01 -0000 1.6 @@ -115,7 +115,8 @@ Compiler.NODE_TYPE_NODE){ accepted = true; } - else if (steps[i].getAxis() == Compiler.AXIS_CHILD && + else if ((steps[i].getAxis() == Compiler.AXIS_CHILD || + steps[i].getAxis() == Compiler.AXIS_ATTRIBUTE) && (steps[i].getNodeTest() instanceof NodeNameTest) && !((NodeNameTest)steps[i].getNodeTest()). getNodeName().getName().equals("*")){ 1.8 +24 -12 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/beans/NullPropertyPointer.java Index: NullPropertyPointer.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/beans/NullPropertyPointer.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- NullPropertyPointer.java 10 Aug 2002 16:13:04 -0000 1.7 +++ NullPropertyPointer.java 13 Oct 2002 02:59:01 -0000 1.8 @@ -82,7 +82,7 @@ } public QName getName(){ - return new QName(null, propertyName); + return new QName(propertyName); } public void setPropertyIndex(int index){ @@ -101,7 +101,7 @@ } public NodePointer getValuePointer(){ - return new NullPointer(this, new QName(null, getPropertyName())); + return new NullPointer(this, new QName(getPropertyName())); } protected boolean isActualProperty(){ @@ -122,19 +122,31 @@ } public NodePointer createPath(JXPathContext context){ - return parent.createChild(context, getName(), getIndex()); + if (isAttribute()){ + return parent.createAttribute(context, getName()); + } + else { + return parent.createChild(context, getName(), getIndex()); + } } public NodePointer createPath(JXPathContext context, Object value){ - return parent.createChild(context, getName(), getIndex(), value); + if (isAttribute()){ + NodePointer pointer = parent.createAttribute(context, getName()); + pointer.setValue(value); + return pointer; + } + else { + return parent.createChild(context, getName(), getIndex(), value); + } } - public NodePointer createChild(JXPathContext context, + public NodePointer createChild(JXPathContext context, QName name, int index, Object value){ return createPath(context).createChild(context, name, index, value); } - public NodePointer createChild(JXPathContext context, + public NodePointer createChild(JXPathContext context, QName name, int index){ return createPath(context).createChild(context, name, index); } @@ -184,13 +196,13 @@ private String escape(String string){ int index = string.indexOf('\''); while (index != -1){ - string = string.substring(0, index) + "'" + + string = string.substring(0, index) + "'" + string.substring(index + 1); index = string.indexOf('\''); } index = string.indexOf('\"'); while (index != -1){ - string = string.substring(0, index) + """ + + string = string.substring(0, index) + """ + string.substring(index + 1); index = string.indexOf('\"'); } 1.3 +34 -13 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/beans/BeanAttributeIterator.java Index: BeanAttributeIterator.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/beans/BeanAttributeIterator.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- BeanAttributeIterator.java 24 Apr 2002 04:06:46 -0000 1.2 +++ BeanAttributeIterator.java 13 Oct 2002 02:59:01 -0000 1.3 @@ -66,24 +66,35 @@ import org.apache.commons.jxpath.ri.model.NodePointer; /** - * An iterator of attributes of a JavaBean. Currently supports only one - * attribute - "lang". + * An iterator of attributes of a JavaBean. Returns bean properties as + * well as the "xml:lang" attribute. * * @author Dmitri Plotnikov * @version $Revision$ $Date$ */ -public class BeanAttributeIterator implements NodeIterator { +public class BeanAttributeIterator extends PropertyIterator { private NodePointer parent; - private QName name; private int position = 0; + private boolean includeXmlLang; - public BeanAttributeIterator(NodePointer parent, QName name){ + public BeanAttributeIterator(PropertyOwnerPointer parent, QName name){ + super(parent, + (name.getPrefix() == null && + (name.getName() == null || name.getName().equals("*"))) ? + null : name.toString(), false, null); this.parent = parent; - this.name = name; + includeXmlLang = + (name.getPrefix() != null && name.getPrefix().equals("xml")) && + (name.getName().equals("lang") || name.getName().equals("*")); } public NodePointer getNodePointer(){ - return new LangAttributePointer(parent); + if (includeXmlLang && position == 1){ + return new LangAttributePointer(parent); + } + else { + return super.getNodePointer(); + } } public int getPosition(){ @@ -92,7 +103,17 @@ public boolean setPosition(int position){ this.position = position; - return position == 1 && name.getPrefix() != null && name.getPrefix().equals("xml") && - (name.getName().equals("lang") || name.getName().equals("*")); + if (includeXmlLang){ + if (position == 1){ + return true; + } + else { + return super.setPosition(position - 1); + } + } + else { + this.position = position; + return super.setPosition(position); + } } } 1.11 +86 -43 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java Index: NodePointer.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/NodePointer.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- NodePointer.java 10 Aug 2002 16:13:03 -0000 1.10 +++ NodePointer.java 13 Oct 2002 02:59:01 -0000 1.11 @@ -91,45 +91,51 @@ public static int WHOLE_COLLECTION = Integer.MIN_VALUE; protected int index = WHOLE_COLLECTION; public static String UNKNOWN_NAMESPACE = "<<unknown namespace>>"; + private boolean attribute = false; /** * Allocates an entirely new NodePointer by iterating through all installed * NodePointerFactories until it finds one that can create a pointer. */ public static NodePointer newNodePointer( - QName name, - Object bean, - Locale locale) { + QName name, Object bean, Locale locale) + { if (bean == null) { return new NullPointer(name, locale); } NodePointerFactory[] factories = JXPathContextReferenceImpl.getNodePointerFactories(); for (int i = 0; i < factories.length; i++) { - NodePointer pointer = factories[i].createNodePointer(name, bean, locale); + NodePointer pointer = factories[i]. + createNodePointer(name, bean, locale); if (pointer != null) { return pointer; } } throw new JXPathException( - "Could not allocate a NodePointer for object of " + bean.getClass()); + "Could not allocate a NodePointer for object of " + + bean.getClass()); } /** * Allocates an new child NodePointer by iterating through all installed * NodePointerFactories until it finds one that can create a pointer. */ - public static NodePointer newChildNodePointer(NodePointer parent, QName name, Object bean) { + public static NodePointer newChildNodePointer( + NodePointer parent, QName name, Object bean) + { NodePointerFactory[] factories = JXPathContextReferenceImpl.getNodePointerFactories(); for (int i = 0; i < factories.length; i++) { - NodePointer pointer = factories[i].createNodePointer(parent, name, bean); + NodePointer pointer = factories[i]. + createNodePointer(parent, name, bean); if (pointer != null) { return pointer; } } throw new JXPathException( - "Could not allocate a NodePointer for object of " + bean.getClass()); + "Could not allocate a NodePointer for object of " + + bean.getClass()); } protected NodePointer parent; @@ -149,6 +155,20 @@ } /** + * Set to true if the pointer represents the "attribute::" axis. + */ + public void setAttribute(boolean attribute){ + this.attribute = attribute; + } + + /** + * Returns true if the pointer represents the "attribute::" axis. + */ + public boolean isAttribute(){ + return attribute; + } + + /** * Returns true if this Pointer has no parent. */ public boolean isRoot() { @@ -198,7 +218,8 @@ /** * If the pointer represents a collection (or collection element), - * returns the length of the collection. Otherwise returns 1 (even if the value is null). + * returns the length of the collection. + * Otherwise returns 1 (even if the value is null). */ public int getLength() { Object value = getBaseValue(); @@ -232,11 +253,13 @@ * if it is null. A non-actual pointer represents a part that does not exist * at all. * For instance consider the pointer "/address/street". - * If both <em>address</em> and <em>street</em> are not null, the pointer is actual. - * If <em>address</em> is not null, but <em>street</em> is null, the pointer is still actual. + * If both <em>address</em> and <em>street</em> are not null, + * the pointer is actual. + * If <em>address</em> is not null, but <em>street</em> is null, + * the pointer is still actual. * If <em>address</em> is null, the pointer is not actual. - * (In JavaBeans) if <em>address</em> is not a property of the root bean, a Pointer - * for this path cannot be obtained at all - actual or otherwise. + * (In JavaBeans) if <em>address</em> is not a property of the root bean, + * a Pointer for this path cannot be obtained at all - actual or otherwise. */ public boolean isActual() { if (index == WHOLE_COLLECTION) { @@ -262,11 +285,11 @@ /** * Returns the object the pointer points to; does not convert it * to a "canonical" type. - * + * * @deprecated 1.1 Please use getNode() */ public Object getNodeValue(){ - return getNode(); + return getNode(); } /** @@ -274,7 +297,7 @@ * to a "canonical" type. */ public abstract Object getNode(); - + /** * Converts the value to the required type and changes the corresponding * object to that value. @@ -285,7 +308,8 @@ * Compares two child NodePointers and returns a positive number, * zero or a positive number according to the order of the pointers. */ - public abstract int compareChildNodePointers(NodePointer pointer1, NodePointer pointer2); + public abstract int compareChildNodePointers( + NodePointer pointer1, NodePointer pointer2); /** * Checks if this Pointer matches the supplied NodeTest. @@ -316,7 +340,7 @@ return testLocalName.equals(nodeName.getName()); } else if (test instanceof NodeTypeTest) { - if (((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE) { + if (((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE){ return isNode(); } } @@ -371,20 +395,31 @@ int index, Object value) { throw new JXPathException( "Cannot create an object for path " - + asPath() + + asPath() + "/" + name + "[" + (index + 1) + "]" + ", operation is not allowed for this type of node"); } /** * Called by a child pointer when it needs to create a parent object - * for a non-existent collection element. It may have to expand the collection, - * then create an element object and return a new pointer describing the - * newly created element. + * for a non-existent collection element. It may have to expand the + * collection, then create an element object and return a new pointer + * describing the newly created element. */ - public NodePointer createChild(JXPathContext context, QName name, int index) { + public NodePointer createChild( + JXPathContext context, QName name, int index) { throw new JXPathException( "Cannot create an object for path " - + asPath() + + asPath() + "/" + name + "[" + (index + 1) + "]" + + ", operation is not allowed for this type of node"); + } + + /** + * Called to create a non-existing attribute + */ + public NodePointer createAttribute(JXPathContext context, QName name){ + throw new JXPathException( + "Cannot create an attribute for path " + + asPath() + "/@" + name + ", operation is not allowed for this type of node"); } @@ -426,8 +461,8 @@ } /** - * Returns a NodeIterator that iterates over all attributes of the current node - * matching the supplied node name (could have a wildcard). + * Returns a NodeIterator that iterates over all attributes of the current + * node matching the supplied node name (could have a wildcard). * May return null if the object does not support the attributes. */ public NodeIterator attributeIterator(QName qname) { @@ -449,7 +484,8 @@ /** * Returns a NodePointer for the specified namespace. Will return null - * if namespaces are not supported. Will return UNKNOWN_NAMESPACE if there is no such namespace. + * if namespaces are not supported. + * Will return UNKNOWN_NAMESPACE if there is no such namespace. */ public NodePointer namespacePointer(String namespace) { return null; @@ -520,20 +556,27 @@ StringBuffer buffer = new StringBuffer(); if (getParent() != null) { buffer.append(getParent().asPath()); - // TBD: the following needs to be redesigned. What this condition says is - // "if the parent of this node has already appended this node's name, - // don't do it again". However, I would hate to add an ugly API like - // "isResponsibleForAppendingChildName()". - if (getParent().isNode() || (parent instanceof NullElementPointer)) { + // TBD: the following needs to be redesigned. + // What this condition says is + // "if the parent of this node has already appended this node's + // name, don't do it again". However, I would hate to add an ugly + // API like "isResponsibleForAppendingChildName()". + if (getParent().isNode() || (parent instanceof NullElementPointer)){ QName name = getName(); if (name != null) { buffer.append('/'); + if (attribute){ + buffer.append('@'); + } buffer.append(name); } } } else { QName name = getName(); + if (attribute){ + buffer.append('@'); + } buffer.append(name); } if (index != WHOLE_COLLECTION && isCollection()) { @@ -542,9 +585,7 @@ return buffer.toString(); } - public static int count = 0; public Object clone() { - count ++; try { NodePointer ptr = (NodePointer)super.clone(); if (parent != null){ @@ -564,7 +605,8 @@ } public int compareTo(Object object){ - NodePointer pointer = (NodePointer) object; // Let it throw a ClassCastException + // Let it throw a ClassCastException + NodePointer pointer = (NodePointer) object; if (parent == pointer.parent){ if (parent == null){ return 0; @@ -588,9 +630,9 @@ return compareNodePointers(this, depth1, pointer, depth2); } - private int compareNodePointers(NodePointer p1, int depth1, NodePointer p2, int depth2){ -// System.err.println("Comparing " + p1.asPath() + " (" + depth1 + ") ~ " + -// p2.asPath() + " (" + depth2 + ")"); + private int compareNodePointers( + NodePointer p1, int depth1, NodePointer p2, int depth2) + { if (depth1 < depth2){ int r = compareNodePointers(p1, depth1, p2.parent, depth2-1); if (r != 0){ @@ -618,7 +660,8 @@ "Cannot compare pointers that do not belong to the same tree: '" + p1 + "' and '" + p2 + "'"); } - int r = compareNodePointers(p1.parent, depth1 - 1, p2.parent, depth2 - 1); + int r = compareNodePointers( + p1.parent, depth1 - 1, p2.parent, depth2 - 1); if (r != 0){ return r; } 1.2 +32 -4 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/jdom/JDOMNodePointer.java Index: JDOMNodePointer.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/jdom/JDOMNodePointer.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- JDOMNodePointer.java 26 Aug 2002 22:29:48 -0000 1.1 +++ JDOMNodePointer.java 13 Oct 2002 02:59:02 -0000 1.2 @@ -486,6 +486,34 @@ return ptr; } + public NodePointer createAttribute(JXPathContext context, QName name){ + if (!(node instanceof Element)){ + return super.createAttribute(context, name); + } + + Element element = (Element)node; + String prefix = name.getPrefix(); + if (prefix != null){ + Namespace ns = element.getNamespace(prefix); + if (ns == null){ + throw new JXPathException("Unknown namespace prefix: " + prefix); + } + Attribute attr = element.getAttribute(name.getName(), ns); + if (attr == null){ + element.setAttribute(name.getName(), "", ns); + } + } + else { + Attribute attr = element.getAttribute(name.getName()); + if (attr == null){ + element.setAttribute(name.getName(), ""); + } + } + NodeIterator it = attributeIterator(name); + it.setPosition(1); + return it.getNodePointer(); + } + public void remove(){ Element parent = nodeParent(node); if (parent == null){ 1.10 +27 -4 jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/dom/DOMNodePointer.java Index: DOMNodePointer.java =================================================================== RCS file: /home/cvs/jakarta-commons/jxpath/src/java/org/apache/commons/jxpath/ri/model/dom/DOMNodePointer.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- DOMNodePointer.java 26 Aug 2002 22:15:26 -0000 1.9 +++ DOMNodePointer.java 13 Oct 2002 02:59:02 -0000 1.10 @@ -403,6 +403,29 @@ return ptr; } + public NodePointer createAttribute(JXPathContext context, QName name){ + if (!(node instanceof Element)){ + return super.createAttribute(context, name); + } + Element element = (Element)node; + String prefix = name.getPrefix(); + if (prefix != null){ + String ns = getNamespaceURI(prefix); + if (ns == null){ + throw new JXPathException("Unknown namespace prefix: " + prefix); + } + element.setAttributeNS(ns, name.toString(), ""); + } + else { + if (!element.hasAttribute(name.getName())){ + element.setAttribute(name.getName(), ""); + } + } + NodeIterator it = attributeIterator(name); + it.setPosition(1); + return it.getNodePointer(); + } + public void remove(){ Node parent = node.getParentNode(); if (parent == null){
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>