/* * Copyright (c) 2013 Red Hat, Inc. and/or its affiliates. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Brad Davis - bradsdavis@gmail.com - Initial API and implementation */ package org.jboss.windup.util.xml; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.DefaultHandler2; /** * A SAX ContentHandler which stores the location of each node into that node's <code>setUserData()</code>. */ public class LocationAwareContentHandler extends DefaultHandler2 { final public static String LINE_NUMBER_KEY_NAME = "ln"; final public static String COLUMN_NUMBER_KEY_NAME = "cn"; final public static String DOCTYPE_KEY_NAME = "dt"; final public static String NAMESPACE_KEY_NAME = "nsuri"; private final Set<String> namespaceURIs = new HashSet<>(); private final Document doc; private Locator locator; private Element current; private Doctype doctype; @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException { doctype = new Doctype(name, publicId, systemId, baseURI); doc.setUserData(DOCTYPE_KEY_NAME, doctype, null); return new InputSource(new ByteArrayInputStream(new byte[0])); } public LocationAwareContentHandler(Document d) { this.doc = d; } @Override public void setDocumentLocator(final Locator locator) { this.locator = locator; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attrs) throws SAXException { Element e = null; if (localName != null && !"".equals(localName)) { e = doc.createElementNS(uri, localName); } else { e = doc.createElement(qName); } storeLineInformation(e); if(StringUtils.isNotBlank(uri)) { namespaceURIs.add(uri); } if(doc.getUserData(NAMESPACE_KEY_NAME) == null) { doc.setUserData(NAMESPACE_KEY_NAME, namespaceURIs, null); } if (current == null) { doc.appendChild(e); } else { current.appendChild(e); } current = e; if (attrs != null) { for (int i = 0; i < attrs.getLength(); i++) { Attr attr = null; if (attrs.getLocalName(i) != null && !"".equals(attrs.getLocalName(i))) { attr = doc.createAttributeNS(attrs.getURI(i), attrs.getLocalName(i)); attr.setValue(attrs.getValue(i)); storeLineInformation(attr); current.setAttributeNodeNS(attr); } else { attr = doc.createAttribute(attrs.getQName(i)); attr.setValue(attrs.getValue(i)); storeLineInformation(attr); current.setAttributeNode(attr); } } } } @Override public void endElement(final String uri, final String localName, final String qName) { if (current == null) { return; } Node parent = current.getParentNode(); // If the parent is the document itself, then we're done. if (parent.getParentNode() == null) { current.normalize(); current = null; } else { current = (Element) current.getParentNode(); } } private void storeLineInformation(Node e) { e.setUserData(LINE_NUMBER_KEY_NAME, this.locator.getLineNumber(), null); e.setUserData(COLUMN_NUMBER_KEY_NAME, this.locator.getColumnNumber(), null); } @Override public void characters(char buf[], int offset, int length) { if (current != null) { Node n = doc.createTextNode(new String(buf, offset, length)); storeLineInformation(n); current.appendChild(n); } } public class Doctype { public String name; public String publicId; public String systemId; public String baseURI; public Doctype(String name, String publicId, String systemId, String baseURI) { this.name = name; this.publicId = publicId; this.systemId = systemId; this.baseURI = baseURI; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPublicId() { return publicId; } public void setPublicId(String publicId) { this.publicId = publicId; } public String getSystemId() { return systemId; } public void setSystemId(String systemId) { this.systemId = systemId; } public String getBaseURI() { return baseURI; } public void setBaseURI(String baseURI) { this.baseURI = baseURI; } @Override public String toString() { return "Doctype [name=" + name + ", publicId=" + publicId + ", systemId=" + systemId + ", baseURI=" + baseURI + "]"; } } }