/** * Copyright 2007-2010 非也 * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation。 * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses. * */ package org.firesoa.common.jxpath.model.dom4j; import java.text.SimpleDateFormat; import java.util.List; import java.util.Locale; import org.apache.commons.jxpath.JXPathAbstractFactoryException; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.JXPathException; import org.apache.commons.jxpath.ri.Compiler; import org.apache.commons.jxpath.ri.NamespaceResolver; import org.apache.commons.jxpath.ri.QName; import org.apache.commons.jxpath.ri.compiler.NodeNameTest; import org.apache.commons.jxpath.ri.compiler.NodeTest; import org.apache.commons.jxpath.ri.compiler.NodeTypeTest; import org.apache.commons.jxpath.ri.compiler.ProcessingInstructionTest; import org.apache.commons.jxpath.ri.model.NodeIterator; import org.apache.commons.jxpath.ri.model.NodePointer; import org.apache.commons.jxpath.util.TypeUtils; import org.dom4j.Attribute; import org.dom4j.CDATA; import org.dom4j.Comment; import org.dom4j.Document; import org.dom4j.DocumentType; import org.dom4j.Element; import org.dom4j.Entity; import org.dom4j.Namespace; import org.dom4j.Node; import org.dom4j.ProcessingInstruction; import org.dom4j.Text; import org.firesoa.common.jxpath.Constants; /** * @author 非也 nychen2000@163.com * */ public class Dom4JNodePointer extends NodePointer { private Node node; private String id; private NamespaceResolver localNamespaceResolver; public Dom4JNodePointer(Node node, Locale locale) { super(null, locale); this.node = node; } /** * Create a new JDOMNodePointer. * * @param node * pointed * @param locale * Locale * @param id * String id */ public Dom4JNodePointer(Node node, Locale locale, String id) { super(null, locale); this.node = node; this.id = id; } /** * Create a new JDOMNodePointer. * * @param parent * NodePointer * @param node * pointed */ public Dom4JNodePointer(NodePointer parent, Node node) { super(parent); this.node = node; } public String asPath() { if (id != null) { return "id('" + escape(id) + "')"; } StringBuffer buffer = new StringBuffer(); if (parent != null) { buffer.append(parent.asPath()); } if (node instanceof Element) { // If the parent pointer is not a JDOMNodePointer, it is // the parent's responsibility to produce the node test part // of the path if (parent instanceof Dom4JNodePointer) { if (buffer.length() == 0 || buffer.charAt(buffer.length() - 1) != '/') { buffer.append('/'); } String nsURI = getNamespaceURI(); String ln = Dom4JNodePointer.getLocalName((Node)node); if (nsURI == null) { buffer.append(ln); buffer.append('['); buffer.append(getRelativePositionByName()).append(']'); } else { String prefix = getNamespaceResolver().getPrefix(nsURI); if (prefix != null) { if (prefix.equals(Constants.DEFAULT_NS_PREFIX)){ buffer.append(ln); buffer.append('['); buffer.append(getRelativePositionByName()).append(']'); }else{ buffer.append(prefix); buffer.append(':'); buffer.append(ln); buffer.append('['); buffer.append(getRelativePositionByName()); buffer.append(']'); } } else { buffer.append("node()"); buffer.append('['); buffer.append(getRelativePositionOfElement()); buffer.append(']'); } } } } else if (node instanceof Text || node instanceof CDATA) { buffer.append("/text()"); buffer.append('[').append(getRelativePositionOfTextNode()).append( ']'); } else if (node instanceof ProcessingInstruction) { buffer.append("/processing-instruction(\'").append(((ProcessingInstruction) node).getTarget()).append( "')"); buffer.append('[').append(getRelativePositionOfPI()).append( ']'); } return buffer.toString(); } private int getRelativePositionOfPI() { String target = ((ProcessingInstruction) node).getTarget(); Element parent = (Element) ((ProcessingInstruction) node).getParent(); if (parent == null) { return 1; } List children = parent.content(); int count = 0; for (int i = 0; i < children.size(); i++) { Object child = children.get(i); if (child instanceof ProcessingInstruction && (target == null || target.equals( ((ProcessingInstruction) child).getTarget()))) { count++; } if (child == node) { break; } } return count; } private int getRelativePositionOfTextNode() { Element parent; if (node instanceof Text) { parent = (Element) ((Text) node).getParent(); } else { parent = (Element) ((CDATA) node).getParent(); } if (parent == null) { return 1; } List children = parent.content(); int count = 0; for (int i = 0; i < children.size(); i++) { Object child = children.get(i); if (child instanceof Text || child instanceof CDATA) { count++; } if (child == node) { break; } } return count; } private int getRelativePositionOfElement() { Object parent = ((Element) node).getParent(); if (parent == null) { return 1; } List children; if (parent instanceof Element) { children = ((Element) parent).content(); } else { children = ((Document) parent).content(); } int count = 0; for (int i = 0; i < children.size(); i++) { Object child = children.get(i); if (child instanceof Element) { count++; } if (child == node) { break; } } return count; } public synchronized NamespaceResolver getNamespaceResolver() { if (localNamespaceResolver == null) { localNamespaceResolver = new NamespaceResolver(super.getNamespaceResolver()); localNamespaceResolver.setNamespaceContextPointer(this); } return localNamespaceResolver; } protected String getDefaultNamespaceURI() { return getNamespaceResolver().getNamespaceURI(Constants.DEFAULT_NS_PREFIX); } public static String getLocalName(Node node) { return node.getName(); } public String getNamespaceURI(String prefix) { if (prefix.equals("xml")) { return Namespace.XML_NAMESPACE.getURI(); } if (prefix.equals(Constants.DEFAULT_NS_PREFIX)){//采用context注册的默认命名空间 return getDefaultNamespaceURI(); } Element element = null; if (node instanceof Document) { element = ((Document) node).getRootElement(); } if (node instanceof Element) { element = (Element) node; } if (element == null) { return null; } Namespace ns = element.getNamespaceForPrefix(prefix); return ns == null ? null : ns.getURI(); } public String getNamespaceURI() { if (!(node instanceof Node)){ return null; }else{ return getNamespaceURI((Node)node); } } public static String getPrefix(Element node) { String prefix = node.getNamespacePrefix(); return prefix; } /** * Get the ns uri of the specified node. * @param node Node to check * @return String ns uri */ public static String getNamespaceURI(Node node) { if (node==null)return null; Element element = null; if (node instanceof Document) { element = ((Document) node).getRootElement(); }else if (node instanceof Element){ element = (Element)node; } else{ return null; } if (element==null)return null; String uri = element.getNamespaceURI(); if (uri!=null && uri.trim().equals("")){ uri = null; } return uri; } private int getRelativePositionByName() { if (node instanceof Element) { Object parent = ((Element) node).getParent(); if (!(parent instanceof Element)) { return 1; } List children = ((Element) parent).content(); int count = 0; String name = ((Element) node).getQualifiedName(); for (int i = 0; i < children.size(); i++) { Object child = children.get(i); if ((child instanceof Element) && ((Element) child).getQualifiedName().equals(name)) { count++; } if (child == node) { break; } } return count; } return 1; } private static Element nodeParent(Object node) { if (node instanceof Element) { Object parent = ((Element) node).getParent(); return parent instanceof Element ? (Element) parent : null; } if (node instanceof Text) { return (Element) ((Text) node).getParent(); } if (node instanceof CDATA) { return (Element) ((CDATA) node).getParent(); } if (node instanceof ProcessingInstruction) { return (Element) ((ProcessingInstruction) node).getParent(); } if (node instanceof Comment) { return (Element) ((Comment) node).getParent(); } return null; } /** * Find the nearest occurrence of the specified attribute * on the specified and enclosing elements. * @param n current node * @param attrName attribute name * @param ns Namespace * @return attribute value */ protected static String findEnclosingAttribute(Object n, String attrName, Namespace ns) { while (n != null) { if (n instanceof Element) { Element e = (Element) n; String attr = e.attributeValue(new org.dom4j.QName(attrName, ns)); if (attr != null && !attr.equals("")) { return attr; } } n = nodeParent(n); } return null; } /** * Get the language of this element. * @return String language */ protected String getLanguage() { return findEnclosingAttribute(node, "lang", Namespace.XML_NAMESPACE); } public boolean isLanguage(String lang) { String current = getLanguage(); return current == null ? super.isLanguage(lang) : current.toUpperCase( Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH)); } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#isLeaf() */ @Override public boolean isLeaf() { if (node instanceof Element) { return ((Element) node).content().size() == 0; } if (node instanceof Document) { return ((Document) node).content().size() == 0; } return true; } public int hashCode() { return node.hashCode(); } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#isCollection() */ @Override public boolean isCollection() { return false; } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#getLength() */ @Override public int getLength() { return 1; } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#getName() */ @Override public QName getName() { String ns = null; String ln = null; if (node instanceof Element) { ns = ((Element) node).getNamespacePrefix(); if (ns != null && ns.equals("")) { ns = null; } ln = ((Element) node).getName(); } else if (node instanceof ProcessingInstruction) { ln = ((ProcessingInstruction) node).getTarget(); } return new QName(ns, ln); } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#getBaseValue() */ @Override public Object getBaseValue() { return node; } /* * (non-Javadoc) * * @see org.apache.commons.jxpath.ri.model.NodePointer#getImmediateNode() */ @Override public Object getImmediateNode() { return node; } public Object getValue() { if (node instanceof Document){ return node; } if (node instanceof Element) { StringBuffer buf = new StringBuffer(); for (NodeIterator children = childIterator(null, false, null); children.setPosition(children.getPosition() + 1);) { NodePointer ptr = children.getNodePointer(); if (ptr.getImmediateNode() instanceof Element || ptr.getImmediateNode() instanceof Text) { buf.append(ptr.getValue()); } } return buf.toString(); } if (node instanceof Comment) { String text = ((Comment) node).getText(); if (text != null) { text = text.trim(); } return text; } String result = null; if (node instanceof Text) { result = ((Text) node).getText(); } if (node instanceof ProcessingInstruction) { result = ((ProcessingInstruction) node).getStringValue();//TODO ? } boolean trim = !"preserve".equals(findEnclosingAttribute(node, "space", Namespace.XML_NAMESPACE)); return result != null && trim ? result.trim() : result; // if ((node instanceof org.dom4j.CharacterData // || node instanceof Attribute || node instanceof DocumentType // || node instanceof Entity || node instanceof ProcessingInstruction)) { // return ((Node)node).getText(); // }else{ // if (node instanceof Document){ // ((Document)node).getText(); // }else if (node instanceof Element){ // Element elm = (Element)node; // return elm.getText(); // } // } // return node.toString(); } /* * (non-Javadoc) * * @see * org.apache.commons.jxpath.ri.model.NodePointer#setValue(java.lang.Object) */ @Override public void setValue(Object value) { if (value==null) value="";//null当做空字符串处理 if ((node instanceof org.dom4j.CharacterData || node instanceof Attribute || node instanceof DocumentType || node instanceof Entity || node instanceof ProcessingInstruction)) { String string = (String) TypeUtils.convert(value, String.class); if (string != null && !string.equals("")) { ((Node) node).setText(string); } else { ((Node) node).getParent().remove((Node) node); } } else if (node instanceof Document) { Document theOriginalDoc = (Document)node; Element theOrigialRoot = theOriginalDoc.getRootElement(); if (value instanceof Document){//拷贝整个document Document valueDoc = (Document) value; Element valueRoot = valueDoc.getRootElement(); if (theOrigialRoot==null || valueRoot==null || theOrigialRoot.getQName().equals(valueRoot.getQName())){ theOriginalDoc.clearContent(); List content = valueDoc.content(); if (content != null) { for (int i = 0; i < content.size(); i++) { Node dom4jNode = (Node) content.get(i); Node newDom4jNode = (Node) dom4jNode.clone(); theOriginalDoc.add(newDom4jNode); } } }else{ throw new RuntimeException("Can NOT assign "+valueRoot.getQName()+" to "+theOrigialRoot.getQName()); } } else if (value instanceof Element){ Element valueElem = (Element)value; if (valueElem.getQName().equals(theOrigialRoot.getQName())){ theOriginalDoc.clearContent(); Element newValueElem = (Element)valueElem.clone(); theOriginalDoc.setRootElement(newValueElem); } else{ throw new RuntimeException("Can NOT assign "+valueElem.getQName()+" to "+theOrigialRoot.getQName()); } } else{ throw new RuntimeException("Can NOT assign "+value+" to "+theOrigialRoot.getQName()); } // else if (value instanceof Comment){ // Comment cmmt = (Comment)((Comment)value).clone(); // theOriginalDoc.add(cmmt); // // }else if (value instanceof ProcessingInstruction){ // ProcessingInstruction instru = (ProcessingInstruction)((ProcessingInstruction)value).clone(); // theOriginalDoc.add(instru); // } } else if (node instanceof Element) { Element originalElem = ((Element) node); if (value!=null && value instanceof Element){ Element valueElm = (Element) value; if (originalElem.getQName().equals(valueElm.getQName())){ originalElem.clearContent(); List content = valueElm.content(); if (content != null) { for (int i = 0; i < content.size(); i++) { Node dom4jNode = (Node) content.get(i); Node newDom4jNode = (Node) dom4jNode.clone(); originalElem.add(newDom4jNode); } } }else{ throw new RuntimeException("Can NOT assign "+valueElm.getQName()+" to "+originalElem.getQName()); } } else if (value!=null && value instanceof Text ){ originalElem.clearContent(); Text txt = (Text)((Text)value).clone(); originalElem.add(txt); } else if (value!=null && value instanceof CDATA){ originalElem.clearContent(); CDATA cdata = (CDATA)((CDATA)value).clone(); originalElem.add(cdata); } else if (value!=null && value instanceof java.util.Date){ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = format.format((java.util.Date)value); originalElem.clearContent(); originalElem.addText(dateStr); } else { String string = (String) TypeUtils.convert(value, String.class); originalElem.clearContent(); originalElem.addText(string); } } } /* * (non-Javadoc) * * @see * org.apache.commons.jxpath.ri.model.NodePointer#compareChildNodePointers * (org.apache.commons.jxpath.ri.model.NodePointer, * org.apache.commons.jxpath.ri.model.NodePointer) */ @Override public int compareChildNodePointers(NodePointer pointer1, NodePointer pointer2) { Object node1 = pointer1.getBaseValue(); Object node2 = pointer2.getBaseValue(); if (node1 == node2) { return 0; } if ((node1 instanceof Attribute) && !(node2 instanceof Attribute)) { return -1; } if ( !(node1 instanceof Attribute) && (node2 instanceof Attribute)) { return 1; } if ( (node1 instanceof Attribute) && (node2 instanceof Attribute)) { List list = ((Element) getNode()).attributes(); int length = list.size(); for (int i = 0; i < length; i++) { Object n = list.get(i); if (n == node1) { return -1; } else if (n == node2) { return 1; } } return 0; // Should not happen } if (!(node instanceof Element)) { throw new RuntimeException( "JXPath internal error: " + "compareChildNodes called for " + node); } List children = ((Element) node).content(); int length = children.size(); for (int i = 0; i < length; i++) { Object n = children.get(i); if (n == node1) { return -1; } if (n == node2) { return 1; } } return 0; } public NodePointer createChild( JXPathContext context, QName name, int index) { if (index == WHOLE_COLLECTION) { index = 0; } boolean success = getAbstractFactory(context).createObject( context, this, node, name.toString(), index); if (success) { NodeTest nodeTest; String prefix = name.getPrefix(); String namespaceURI = prefix == null ? null : context .getNamespaceURI(prefix); nodeTest = new NodeNameTest(name, namespaceURI); NodeIterator it = childIterator(nodeTest, false, null); if (it != null && it.setPosition(index + 1)) { return it.getNodePointer(); } } throw new JXPathAbstractFactoryException("Factory could not create " + "a child node for path: " + asPath() + "/" + name + "[" + (index + 1) + "]"); } public NodePointer createChild( JXPathContext context, QName name, int index, Object value) { NodePointer ptr = createChild(context, name, index); ptr.setValue(value); return ptr; } public NodeIterator childIterator( NodeTest test, boolean reverse, NodePointer startWith) { return new Dom4JNodeIterator(this, test, reverse, startWith); } /** * 创建一个名字为name的attribute */ 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 nsUri = null; NamespaceResolver nsr = getNamespaceResolver(); if (nsr != null) { nsUri = nsr.getNamespaceURI(prefix); } if (nsUri == null) { throw new JXPathException( "Unknown namespace prefix: " + prefix); } Namespace dom4jNs = Namespace.get(prefix,nsUri); org.dom4j.QName attributeName = new org.dom4j.QName(name.getName(),dom4jNs); Attribute attr = element.attribute(attributeName); if (attr==null){ element.addAttribute(attributeName, ""); } } else { Attribute attr = element.attribute(name.getName()); if (attr==null) { element.addAttribute(name.getName(), ""); } } NodeIterator it = attributeIterator(name); it.setPosition(1); return it.getNodePointer(); } public NodeIterator attributeIterator(QName name) { return new Dom4JAttributeIterator(this, name); } /** * Get any prefix from the specified node. * @param node the node to check * @return String xml prefix */ public static String getPrefix(Node node) { if (node instanceof Element){ String prefix = ((Element)node).getNamespacePrefix(); return (prefix == null || prefix.equals("")) ? null : prefix; } else if (node instanceof Attribute){ String prefix = ((Attribute)node).getNamespacePrefix(); return (prefix == null || prefix.equals("")) ? null : prefix; } return null; } /** * Get the local name of the specified node. * @param node node to check * @return String local name */ public static String getLocalName(Object node) { if (node instanceof Element) { return ((Element) node).getName(); } if (node instanceof Attribute) { return ((Attribute) node).getName(); } return null; } public boolean testNode(NodeTest test) { return testNode(this, node, test); } /** * Get the parent of the specified node. * @param node to check * @return parent Element */ private static Element nodeParent(Node node) { return node.getParent(); } public NodeIterator namespaceIterator() { return new Dom4JNamespaceIterator(this); } public NodePointer namespacePointer(String prefix) { return new Dom4JNamespacePointer(this, prefix); } public void remove() { Element parent = nodeParent(node); if (parent == null) { throw new JXPathException("Cannot remove root Dom4J node"); } parent.remove(node); } /** * Execute test against node on behalf of pointer. * @param pointer Pointer * @param node to test * @param test to execute * @return true if node passes test */ public static boolean testNode( NodePointer pointer, Object node, NodeTest test) { if (test == null) { return true; } if (test instanceof NodeNameTest) { if (!(node instanceof Element)) { return false; } NodeNameTest nodeNameTest = (NodeNameTest) test; QName testName = nodeNameTest.getNodeName(); String namespaceURI = nodeNameTest.getNamespaceURI(); boolean wildcard = nodeNameTest.isWildcard(); String testPrefix = testName.getPrefix(); //如果testPrefix未null则采用默认命名空间 if (testPrefix==null && namespaceURI==null){ namespaceURI = pointer.getNamespaceURI(Constants.DEFAULT_NS_PREFIX); } if (wildcard && testPrefix == null) { return true; } if (wildcard || testName.getName() .equals(Dom4JNodePointer.getLocalName(node))) { String nodeNS = Dom4JNodePointer.getNamespaceURI((Node)node); return equalStrings(namespaceURI, nodeNS) || nodeNS == null && equalStrings(testPrefix, Dom4JNodePointer.getPrefix((Node)node)); } return false; } if (test instanceof NodeTypeTest) { switch (((NodeTypeTest) test).getNodeType()) { case Compiler.NODE_TYPE_NODE : return true; case Compiler.NODE_TYPE_TEXT : return (node instanceof Text) || (node instanceof CDATA); case Compiler.NODE_TYPE_COMMENT : return node instanceof Comment; case Compiler.NODE_TYPE_PI : return node instanceof ProcessingInstruction; default: return false; } } if (test instanceof ProcessingInstructionTest && node instanceof ProcessingInstruction) { String testPI = ((ProcessingInstructionTest) test).getTarget(); String nodePI = ((ProcessingInstruction) node).getTarget(); return testPI.equals(nodePI); } return false; } private static boolean equalStrings(String s1, String s2) { if (s1 == s2) { return true; } s1 = s1 == null ? "" : s1.trim(); s2 = s2 == null ? "" : s2.trim(); return s1.equals(s2); } public boolean equals(Object object) { if (object == this) { return true; } if (!(object instanceof Dom4JNodePointer)) { return false; } Dom4JNodePointer other = (Dom4JNodePointer) object; return node == other.node; } }