diff --git a/openide.util/src/org/openide/xml/XMLUtil.java b/openide.util/src/org/openide/xml/XMLUtil.java --- a/openide.util/src/org/openide/xml/XMLUtil.java +++ b/openide.util/src/org/openide/xml/XMLUtil.java @@ -45,8 +45,12 @@ import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; @@ -66,6 +70,7 @@ import javax.xml.validation.Validator; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; import org.w3c.dom.DOMException; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; @@ -842,4 +847,172 @@ } return doc; } + + /** + * Append child element to the correct position according to given + * order. + * @param parent parent to which the child will be added + * @param el element to be added + * @param order order of the elements which must be followed + */ + public static void appendChildElement(Element parent, Element el, String[] order) { + Element insertBefore = null; + List l = Arrays.asList(order); + int index = l.indexOf(el.getLocalName()); + assert index != -1 : el.getLocalName() + " was not found in " + l; // NOI18N + Iterator it = findSubElements(parent).iterator(); + while (it.hasNext()) { + Element e = (Element) it.next(); + int index2 = l.indexOf(e.getLocalName()); + assert index2 != -1 : e.getLocalName() + " was not found in " + l; // NOI18N + if (index2 > index) { + insertBefore = e; + break; + } + } + parent.insertBefore(el, insertBefore); + } + + /** + * Find all direct child elements of an element. + * Children which are all-whitespace text nodes or comments are ignored; others cause + * an exception to be thrown. + * @param parent a parent element in a DOM tree + * @return a list of direct child elements (may be empty) + * @throws IllegalArgumentException if there are non-element children besides whitespace + */ + public static List findSubElements(Element parent) throws IllegalArgumentException { + NodeList l = parent.getChildNodes(); + List elements = new ArrayList(l.getLength()); + for (int i = 0; i < l.getLength(); i++) { + Node n = l.item(i); + if (n.getNodeType() == Node.ELEMENT_NODE) { + elements.add((Element) n); + } else if (n.getNodeType() == Node.TEXT_NODE) { + String text = ((Text) n).getNodeValue(); + if (text.trim().length() > 0) { + throw new IllegalArgumentException("non-ws text encountered in " + parent + ": " + text); // NOI18N + } + } else if (n.getNodeType() == Node.COMMENT_NODE) { + // OK, ignore + } else { + throw new IllegalArgumentException("unexpected non-element child of " + parent + ": " + n); // NOI18N + } + } + return elements; + } + + /** + * Search for an XML element in the direct children of a parent. DOM + * provides a similar method but it does a recursive search which we do not + * want. It also gives a node list and we want only one result. + * + * @param parent a parent element + * @param name the intended local name + * @param namespace the intended namespace (or null) + * @return the one child element with that name, or null if none + * @throws IllegalArgumentException if there is multiple elements of the same name + */ + public static Element findElement(Element parent, String name, String namespace) throws IllegalArgumentException { + Element result = null; + NodeList l = parent.getChildNodes(); + for (int i = 0; i < l.getLength(); i++) { + if (l.item(i).getNodeType() == Node.ELEMENT_NODE) { + Element el = (Element) l.item(i); + if ((namespace == null && name.equals(el.getTagName())) + || (namespace != null && name.equals(el.getLocalName()) && namespace.equals(el.getNamespaceURI()))) { + if (result == null) { + result = el; + } else { + throw new IllegalArgumentException("more than one element with same name found"); + } + } + } + } + return result; + } + + /** + * Extract nested text from a node. + * Currently does not handle coalescing text nodes, CDATA sections, etc. + * @param parent a parent element + * @return the nested text, or null if none was found + */ + public static String findText(Node parent) { + NodeList l = parent.getChildNodes(); + for (int i = 0; i < l.getLength(); i++) { + if (l.item(i).getNodeType() == Node.TEXT_NODE) { + Text text = (Text) l.item(i); + return text.getNodeValue(); + } + } + return null; + } + + /** + * Convert an XML fragment from one namespace to another. + */ + public static Element translateXML(Element from, String namespace) { + Element to = from.getOwnerDocument().createElementNS(namespace, from.getLocalName()); + NodeList nl = from.getChildNodes(); + int length = nl.getLength(); + for (int i = 0; i < length; i++) { + Node node = nl.item(i); + Node newNode; + if (node.getNodeType() == Node.ELEMENT_NODE) { + newNode = translateXML((Element) node, namespace); + } else { + newNode = node.cloneNode(true); + } + to.appendChild(newNode); + } + NamedNodeMap m = from.getAttributes(); + for (int i = 0; i < m.getLength(); i++) { + Node attr = m.item(i); + to.setAttribute(attr.getNodeName(), attr.getNodeValue()); + } + return to; + } + + /** + * Copy elements from one document to another attaching at the specified element + * and translating to the namespace. + * + * @param doc doc to be copied to + * @param from copy the children of this element (exclusive) + * @param to where to attach the copied elements + * @param newNamespace destination namespace + */ + public static void copyDocument(Document doc, Element from, Element to, String newNamespace) { + NodeList nl = from.getChildNodes(); + int length = nl.getLength(); + for (int i = 0; i < length; i++) { + Node node = nl.item(i); + Node newNode = null; + switch (node.getNodeType()) { + case Node.ELEMENT_NODE: + Element oldElement = (Element) node; + newNode = doc.createElementNS(newNamespace, oldElement.getTagName()); + NamedNodeMap m = oldElement.getAttributes(); + Element newElement = (Element) newNode; + for (int index = 0; index < m.getLength(); index++) { + Node attr = m.item(index); + newElement.setAttribute(attr.getNodeName(), attr.getNodeValue()); + } + copyDocument(doc, oldElement, newElement, newNamespace); + break; + case Node.TEXT_NODE: + Text oldText = (Text) node; + newNode = doc.createTextNode(oldText.getData()); + break; + case Node.COMMENT_NODE: + Comment oldComment = (Comment) node; + newNode = doc.createComment(oldComment.getData()); + break; + } + if (newNode != null) { + to.appendChild(newNode); + } + } + } }