/* * aitools utilities * Copyright (C) 2006 Noel Bush * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.aitools.util.xml; import java.io.IOException; import java.net.URL; import java.util.List; import org.aitools.util.resource.URLTools; import org.aitools.util.runtime.DeveloperError; import org.aitools.util.runtime.UserError; import org.apache.log4j.Logger; import org.apache.xerces.util.XMLCatalogResolver; import org.jdom.Attribute; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import org.xml.sax.Attributes; /** * XML utilities specific to JDOM. * * @author <a href="mailto:noel@aitools.org">Noel Bush</a> */ public class JDOM { /** JDOM relies on an underlying SAX parser; this is the implementation we want to use. */ static String SAX_PARSER_IMPLEMENTATION = "org.apache.xerces.parsers.SAXParser"; /** * Contextualizes the given path within the context of the given element's document. * * @param path * @param element * @return the contextualized path */ public static URL contextualize(String path, Element element) { return URLTools.contextualize(element.getDocument().getBaseURI(), path); } /** * Converts the given type as returned by {@link Attributes#getType(int)} into a type meaningful to {@link Attribute}. * * @param typeName * @return the type */ public static int getAttributeType(String typeName) { int type = Attribute.UNDECLARED_TYPE; if (typeName.equals("CDATA")) { type = Attribute.CDATA_TYPE; } else if (typeName.equals("ID")) { type = Attribute.ID_TYPE; } else if (typeName.equals("IDREF")) { type = Attribute.IDREF_TYPE; } else if (typeName.equals("IDREFS")) { type = Attribute.IDREFS_TYPE; } else if (typeName.equals("NMTOKEN")) { type = Attribute.NMTOKEN_TYPE; } else if (typeName.equals("NMTOKENS")) { type = Attribute.NMTOKENS_TYPE; } else if (typeName.equals("ENTITY")) { type = Attribute.ENTITY_TYPE; } else if (typeName.equals("ENTITIES")) { type = Attribute.ENTITIES_TYPE; } else if (typeName.equals("NOTATION")) { type = Attribute.NOTATION_TYPE; } return type; } /** * Converts the given namespace uri String to a {@link Namespace}, or {@link Namespace#NO_NAMESPACE} if the argument * is empty or null. * * @param uri * @return the namespace */ public static Namespace getNamespace(String uri) { Namespace namespace; if ("".equals(uri) || uri == null) { namespace = Namespace.NO_NAMESPACE; } else { namespace = Namespace.getNamespace(uri); } return namespace; } /** * Renders a set of attributes. * * @param attributes the attributes to render * @return the rendered attributes */ public static String renderAttributes(List<Attribute> attributes) { StringBuilder result = new StringBuilder(); if (attributes != null) { for (Attribute attribute : attributes) { String attributeName = attribute.getName(); if (attributeName != null && !"xmlns".equals(attributeName)) { result.append(String.format(" %s=\"%s\"", attributeName, attribute.getValue())); } } } return result.toString(); } /** * Renders a given element as an empty element, including a namespace declaration, if requested. * * @param element the element to render * @param includeNamespaceAttribute whether to include the namespace attribute * @return the result of the rendering */ @SuppressWarnings("unchecked") public static String renderEmptyElement(Element element, boolean includeNamespaceAttribute) { StringBuilder result = new StringBuilder(); result.append(String.format("<%s", element.getName())); if (includeNamespaceAttribute) { result.append(String.format(" xmlns=\"%s\"", element.getNamespaceURI())); } result.append(String.format("%s/>", renderAttributes(element.getAttributes()))); return result.toString(); } /** * Renders a given element as an end tag. * * @param element the element to render * @return the result of the rendering */ public static String renderEndTag(Element element) { return String.format("</%s>", element.getName()); } /** * Renders a given element as a start tag, including a namespace declaration, if requested. * * @param element the element to render * @param includeNamespaceAttribute whether to include the namespace attribute * @return the rendering of the element */ @SuppressWarnings("unchecked") public static String renderStartTag(Element element, boolean includeNamespaceAttribute) { StringBuilder result = new StringBuilder(); result.append(String.format("<%s", element.getName())); if (includeNamespaceAttribute) { result.append(String.format(" xmlns=\"%s\"", element.getNamespaceURI())); } result.append(String.format("%s>", renderAttributes(element.getAttributes()))); return result.toString(); } /** * Renders a given element name and set of attributes as a start tag, including a namespace declaration, if requested. * * @param elementName the name of the element to render * @param attributes the attributes to include * @param includeNamespaceAttribute whether or not to include the namespace attribute * @param namespaceURI the namespace URI * @return the rendering result */ public static String renderStartTag(String elementName, List<Attribute> attributes, boolean includeNamespaceAttribute, String namespaceURI) { StringBuilder result = new StringBuilder(); result.append(String.format("<%s", elementName)); if (includeNamespaceAttribute) { result.append(String.format(" xmlns=\"%s\"", namespaceURI)); } result.append(String.format("%s>", renderAttributes(attributes))); return result.toString(); } /** * Loads the given path and creates a JDOM Document. Also explicitly sets the base URI for the * Document object to the <code>path</code>, since JDOM doesn't seem to do this for us. * * @param location * @param catalogPath * @param logger * @return a DOM Document object */ public static Document getDocument(URL location, String catalogPath, Logger logger) { // Get a SAXBuilder, so we can configure the underlying parser. SAXBuilder builder = new SAXBuilder(SAX_PARSER_IMPLEMENTATION, true); builder.setFeature("http://apache.org/xml/features/validation/schema", true); // Get a resolver, and attach it to the builder. XMLCatalogResolver resolver = Resolvers.newXMLCatalogResolver(catalogPath); builder.setProperty("http://apache.org/xml/properties/internal/entity-resolver", resolver); // Finally, attach an error handler to the builder. builder.setErrorHandler(new SimpleSAXErrorHandler(logger)); // Parse the document using the document builder. Document document = null; try { document = builder.build(location.toExternalForm()); } catch (JDOMException e) { throw new DeveloperError(String.format("Error while parsing XML at %s", location), e); } catch (IOException e) { throw new UserError(String.format("I/O error when parsing XML at %s", location), e); } document.setBaseURI(location.toExternalForm()); return document; } }