/** * * 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.airavata.workflow.core; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.airavata.common.exception.AiravataException; import org.apache.airavata.common.utils.IOUtil; import org.apache.xmlbeans.XmlError; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; import org.xml.sax.SAXException; import org.xmlpull.infoset.XmlDocument; import org.xmlpull.infoset.XmlElement; import org.xmlpull.infoset.XmlNamespace; import org.xmlpull.mxp1.MXParserFactory; import org.xmlpull.mxp1_serializer.MXSerializer; public class XMLUtil { /** * The XML builder for XPP5 */ public static final org.xmlpull.infoset.XmlInfosetBuilder BUILDER = org.xmlpull.infoset.XmlInfosetBuilder .newInstance(); /** * The XML builder for XPP3. */ public static final org.xmlpull.v1.builder.XmlInfosetBuilder BUILDER3 = org.xmlpull.v1.builder.XmlInfosetBuilder .newInstance(new MXParserFactory()); private static final Logger logger = LoggerFactory.getLogger(XMLUtil.class); private final static String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; private final static String INDENT = " "; /** * Parses a specified string and returns the XmlElement (XPP3). * * @param string * @return The XmlElement (XPP3) parsed. */ public static org.xmlpull.v1.builder.XmlElement stringToXmlElement3(String string) { return BUILDER3.parseFragmentFromReader(new StringReader(string)); } /** * Parses a specified string and returns the XmlElement (XPP5). * * @param string * @return The XmlElement (XPP5) parsed. */ public static org.xmlpull.infoset.XmlElement stringToXmlElement(String string) { XmlDocument document = BUILDER.parseString(string); org.xmlpull.infoset.XmlElement element = document.getDocumentElement(); return element; } /** * Converts a specified XmlElement (XPP3) to the XmlElement (XPP5). * * @param element * @return The XmlElement (XPP5) converted. */ public static org.xmlpull.infoset.XmlElement xmlElement3ToXmlElement5(org.xmlpull.v1.builder.XmlElement element) { String string = xmlElementToString(element); return stringToXmlElement(string); } /** * Converts a specified XmlElement (XPP5) to the XmlElement (XPP3). * * @param element * @return The XmlElement (XPP3) converted. */ public static org.xmlpull.v1.builder.XmlElement xmlElement5ToXmlElement3(org.xmlpull.infoset.XmlElement element) { String string = xmlElementToString(element); return stringToXmlElement3(string); } /** * Returns the XML string of a specified XmlElement. * * @param element * The specified XmlElement * @return The XML string */ public static String xmlElementToString(org.xmlpull.v1.builder.XmlElement element) { MXSerializer serializer = new MXSerializer(); StringWriter writer = new StringWriter(); serializer.setOutput(writer); serializer.setProperty(PROPERTY_SERIALIZER_INDENTATION, INDENT); BUILDER3.serialize(element, serializer); String xmlText = writer.toString(); return xmlText; } /** * Returns the XML string as a specified XmlElement (XPP5). * * @param element * The specified XmlElement * @return The XML string */ public static String xmlElementToString(org.xmlpull.infoset.XmlElement element) { String string; if (element == null) { string = ""; } else { string = BUILDER.serializeToStringPretty(element); } return string; } /** * Converts a specified XPP5 XML element to a DOM element under a specified document. * * @param xppElement * @param document * @return The converted DOM element. */ public static Element xppElementToDomElement(org.xmlpull.infoset.XmlElement xppElement, Document document) { Element domElement = document.createElement(xppElement.getName()); for (org.xmlpull.infoset.XmlNamespace namespace : xppElement.namespaces()) { logger.debug("namespace: " + namespace); } for (org.xmlpull.infoset.XmlAttribute attribute : xppElement.attributes()) { domElement.setAttribute(attribute.getName(), attribute.getValue()); } for (Object object : xppElement.children()) { if (object instanceof org.xmlpull.infoset.XmlElement) { domElement.appendChild(xppElementToDomElement((org.xmlpull.infoset.XmlElement) object, document)); } else if (object instanceof String) { Text text = document.createTextNode((String) object); domElement.appendChild(text); } else { logger.debug("object.getClass(): " + object.getClass()); } } return domElement; } // /** // * @param definitions3 // * @return The WsdlDefinitions (XSUL5) // */ // @Deprecated // public static xsul5.wsdl.WsdlDefinitions wsdlDefinitions3ToWsdlDefintions5(xsul.wsdl.WsdlDefinitions definitions3) { // return WSDLUtil.wsdlDefinitions3ToWsdlDefintions5(definitions3); // } // // /** // * @param definitions5 // * @return The WsdlDefinitions (XSUL3) // */ // @Deprecated // public static xsul.wsdl.WsdlDefinitions wsdlDefinitions5ToWsdlDefintions3(xsul5.wsdl.WsdlDefinitions definitions5) { // return WSDLUtil.wsdlDefinitions5ToWsdlDefintions3(definitions5); // } /** * Converts a specified XPP3 XML element to a DOM element under a specified document. * * @param xppElement * @param document * @return The converted DOM element. */ public static Element xppElementToDomElement(org.xmlpull.v1.builder.XmlElement xppElement, Document document) { Element domElement = document.createElement(xppElement.getName()); Iterator nsIt = xppElement.namespaces(); while (nsIt.hasNext()) { org.xmlpull.v1.builder.XmlNamespace namespace = (org.xmlpull.v1.builder.XmlNamespace) nsIt.next(); logger.debug("namespace: " + namespace); // TODO } Iterator attrIt = xppElement.attributes(); while (attrIt.hasNext()) { org.xmlpull.v1.builder.XmlAttribute attribute = (org.xmlpull.v1.builder.XmlAttribute) attrIt.next(); // TODO namespace domElement.setAttribute(attribute.getName(), attribute.getValue()); } Iterator elementIt = xppElement.children(); while (elementIt.hasNext()) { Object object = elementIt.next(); if (object instanceof org.xmlpull.v1.builder.XmlElement) { domElement.appendChild(xppElementToDomElement((org.xmlpull.v1.builder.XmlElement) object, document)); } else if (object instanceof String) { Text text = document.createTextNode((String) object); domElement.appendChild(text); } else { logger.debug("object.getClass(): " + object.getClass()); } } return domElement; } /** * @param element * @return The cloned XmlElement. */ public static org.xmlpull.infoset.XmlElement deepClone(org.xmlpull.infoset.XmlElement element) throws AiravataException { try { XmlElement clonedElement = element.clone(); clonedElement.setParent(null); return clonedElement; } catch (CloneNotSupportedException e) { // This should not happen because we don't put any special Objects. throw new AiravataException(e.getMessage(), e); } } /** * Saves a specified XmlElement to a specified file. * * @param element * @param file * @throws IOException */ public static void saveXML(org.xmlpull.infoset.XmlElement element, File file) throws IOException { XmlDocument document = BUILDER.newDocument(); document.setDocumentElement(element); String xmlText = BUILDER.serializeToStringPretty(document); IOUtil.writeToFile(xmlText, file); } /** * Saves a specified XmlElement to a specified file. * * @param element * @param file * @throws IOException */ public static void saveXML(org.xmlpull.v1.builder.XmlElement element, File file) throws IOException { saveXML(xmlElement3ToXmlElement5(element), file); } /** * @param file * @return The XmlElement in the document. * @throws IOException */ public static org.xmlpull.infoset.XmlElement loadXML(InputStream stream) throws IOException { String xmlText = IOUtil.readToString(stream); XmlDocument document = BUILDER.parseString(xmlText); return document.getDocumentElement(); } /** * @param file * @return The XmlElement in the document. * @throws IOException */ public static org.xmlpull.infoset.XmlElement loadXML(File file) throws IOException { return loadXML(new FileInputStream(file)); } /** * @param string * @return true if the specified string is XML, false otherwise */ public static boolean isXML(String string) { try { stringToXmlElement(string); return true; } catch (RuntimeException e) { return false; } } /** * Validates a specified XmlObject along with logging errors if any. * * @param xmlObject */ public static void validate(XmlObject xmlObject) throws AiravataException { XmlOptions validateOptions = new XmlOptions(); ArrayList errorList = new ArrayList(); validateOptions.setErrorListener(errorList); boolean isValid = xmlObject.validate(validateOptions); if (isValid) { // Valid return; } // Error StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < errorList.size(); i++) { XmlError error = (XmlError) errorList.get(i); logger.warn("Message: " + error.getMessage()); logger.warn("Location of invalid XML: " + error.getCursorLocation().xmlText()); stringBuilder.append("Message:" + error.getMessage()); stringBuilder.append("Location of invalid XML: " + error.getCursorLocation().xmlText()); } throw new AiravataException(stringBuilder.toString()); } /** * Returns the local part of a specified QName. * * @param qname * the specified QName in string, e.g. ns:value * @return the local part of the QName, e.g. value */ public static String getLocalPartOfQName(String qname) { int index = qname.indexOf(':'); if (index < 0) { return qname; } else { return qname.substring(index + 1); } } /** * Returns the prefix of a specified QName. * * @param qname * the specified QName in string, e.g. ns:value * @return the prefix of the QName, e.g. ns */ public static String getPrefixOfQName(String qname) { int index = qname.indexOf(':'); if (index < 0) { return null; } else { return qname.substring(0, index); } } /** * @param prefixCandidate * @param uri * @param alwaysUseSuffix * @param element * @return The namespace found or declared. */ public static XmlNamespace declareNamespaceIfNecessary(String prefixCandidate, String uri, boolean alwaysUseSuffix, XmlElement element) { XmlNamespace namespace = element.lookupNamespaceByName(uri); if (namespace == null) { return declareNamespace(prefixCandidate, uri, alwaysUseSuffix, element); } else { return namespace; } } /** * @param prefixCandidate * @param uri * @param alwaysUseSuffix * @param element * @return The namespace declared. */ public static XmlNamespace declareNamespace(String prefixCandidate, String uri, boolean alwaysUseSuffix, XmlElement element) { if (prefixCandidate == null || prefixCandidate.length() == 0) { prefixCandidate = "a"; } String prefix = prefixCandidate; if (alwaysUseSuffix) { prefix += "0"; } if (element.lookupNamespaceByPrefix(prefix) != null) { int i = 1; prefix = prefixCandidate + i; while (element.lookupNamespaceByPrefix(prefix) != null) { i++; } } XmlNamespace namespace = element.declareNamespace(prefix, uri); return namespace; } /** * * @param xml * @param name */ public static void removeElements(XmlElement xml, String name) { Iterable<XmlElement> removeElements = xml.elements(null, name); LinkedList<XmlElement> removeList = new LinkedList<XmlElement>(); for (XmlElement xmlElement : removeElements) { removeList.add(xmlElement); } for (XmlElement xmlElement : removeList) { xml.removeChild(xmlElement); } Iterable children = xml.children(); for (Object object : children) { if (object instanceof XmlElement) { XmlElement element = (XmlElement) object; removeElements(element, name); } } } /** * @param url * @return Document */ public static Document retrievalXMLDocFromUrl(String url) { try { URL xmlUrl = new URL(url); InputStream in = xmlUrl.openStream(); Document ret = null; DocumentBuilderFactory domFactory; DocumentBuilder builder; domFactory = DocumentBuilderFactory.newInstance(); domFactory.setValidating(false); domFactory.setNamespaceAware(false); builder = domFactory.newDocumentBuilder(); ret = builder.parse(in); return ret; } catch (MalformedURLException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } catch (ParserConfigurationException e) { logger.error(e.getMessage(), e); } catch (SAXException e) { logger.error(e.getMessage(), e); } return null; } /** * @param url * @return Document */ public static Document retrievalXMLDocForParse(String url) { try { URL xmlUrl = new URL(url); InputStream in = xmlUrl.openStream(); DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance(); xmlFact.setNamespaceAware(true); DocumentBuilder builder = xmlFact.newDocumentBuilder(); Document doc = builder.parse(in); return doc; } catch (MalformedURLException e) { logger.error("Malformed URL", e); } catch (IOException e) { logger.error(e.getMessage(), e); } catch (ParserConfigurationException e) { logger.error(e.getMessage(), e); } catch (SAXException e) { logger.error(e.getMessage(), e); } return null; } public static boolean isEqual(XmlElement elem1, XmlElement elem2) throws Exception { if (elem1 == null && elem2 == null) { return true; } else if (elem1 == null) { return false; } else if (elem2 == null) { return false; } if (!elem1.getName().equals(elem2.getName())) { return false; } else { // now check if children are the same Iterator children1 = elem1.children().iterator(); Iterator children2 = elem2.children().iterator(); //check first ones for string Object child1 = null; Object child2 = null; if (children1.hasNext() && children2.hasNext()) { child1 = children1.next(); child2 = children2.next(); if (!children1.hasNext() && !children2.hasNext()) { //only one node could be string could be xmlelement return compareObjs(child1, child2); } else { //get new iterators List<XmlElement> elemSet1 = getXmlElementsOnly(elem1.children().iterator()); List<XmlElement> elemSet2 = getXmlElementsOnly(elem2.children().iterator()); if(elemSet1.size() != elemSet2.size()){ return false; } for(int i =0; i< elemSet1.size(); ++i){ if(!isEqual(elemSet1.get(i), elemSet2.get(i))){ return false; } } return true; } }else { //no internal element return true; } } } private static List<XmlElement> getXmlElementsOnly(Iterator itr){ LinkedList<XmlElement> list = new LinkedList<XmlElement>(); while(itr.hasNext()){ Object obj = itr.next(); if(obj instanceof XmlElement){ list.add((XmlElement) obj); } } return list; } private static boolean compareObjs(Object child1, Object child2) throws Exception { if (child1 instanceof String && child2 instanceof String) { return child1.equals(child2); } else if (child1 instanceof XmlElement && child2 instanceof XmlElement) { return isEqual((XmlElement) child1, (XmlElement) child2); } else { return false; } } }