/**
* 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.ode.spi.compiler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.ode.spi.exec.executable.xml.ContextMode;
import org.apache.ode.spi.exec.executable.xml.SourceId;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
public class ParserUtils {
public static final String LOCATION_NS = "http://ode.apache.org/compiler/location";
public static final String LOCATION_NS_PREFIX = "ol";
public static final String LOCATION_START_LINE_ATTR = "sl";
public static final String LOCATION_START_COL_ATTR = "sc";
public static final String LOCATION_END_LINE_ATTR = "el";
public static final String LOCATION_END_COL_ATTR = "ec";
public static final String LOCATION_START_LINE_NSATTR = LOCATION_NS_PREFIX + ":" + LOCATION_START_LINE_ATTR;
public static final String LOCATION_START_COL_NSATTR = LOCATION_NS_PREFIX + ":" + LOCATION_START_COL_ATTR;
public static final String LOCATION_END_LINE_NSATTR = LOCATION_NS_PREFIX + ":" + LOCATION_END_LINE_ATTR;
public static final String LOCATION_END_COL_NSATTR = LOCATION_NS_PREFIX + ":" + LOCATION_END_COL_ATTR;
public static boolean isContextual(XMLStreamReader input) throws XMLStreamException {
return input.getAttributeValue(LOCATION_NS, LOCATION_END_LINE_ATTR) != null ? true : false;
}
public static void setLocation(XMLStreamReader input, org.apache.ode.spi.exec.executable.xml.Source src, Instructional ins) throws XMLStreamException {
ins.instruction().setSref(new SourceId(src.getSrc().id()));
ins.instruction().setLine(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_START_LINE_ATTR)));
ins.instruction().setColumn(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_START_COL_ATTR)));
}
public static void setLocation(XMLStreamReader input, org.apache.ode.spi.exec.executable.xml.Source src, Contextual ctx) throws XMLStreamException {
ctx.beginContext().setSref(new SourceId(src.getSrc().id()));
ctx.endContext().setMode(ContextMode.START);
ctx.beginContext().setLine(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_START_LINE_ATTR)));
ctx.beginContext().setColumn(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_START_COL_ATTR)));
ctx.endContext().setSref(new SourceId(src.getSrc().id()));
ctx.endContext().setCtx(ctx.beginContext().getCtx());
ctx.endContext().setMode(ContextMode.END);
ctx.endContext().setLine(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_END_LINE_ATTR)));
ctx.endContext().setColumn(Integer.parseInt(input.getAttributeValue(LOCATION_NS, LOCATION_END_COL_ATTR)));
}
/**
* We do not want to track compiler related directives in sources so we will
* not add in line information for them.
*
* @param content
* @param pragma
* @return
* @throws ParserException
*/
public static Document inlineLocation(byte[] content, Set<String> pragma) throws ParserException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
DOMImplementation impl = db.getDOMImplementation();
Document doc = null;
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLStreamReader reader = inputFactory.createXMLStreamReader(new ByteArrayInputStream(content));
Stack<Element> stack = new Stack<Element>();
StringBuilder textBuffer = new StringBuilder();
while (reader.hasNext()) {
int type = reader.next();
switch (type) {
case XMLStreamConstants.START_ELEMENT:
if (textBuffer.length() > 0) {
if (!stack.isEmpty()) {// ignore text/whitespaces before
// root
Element top = stack.peek();
Text text = doc.createTextNode(textBuffer.toString());
top.appendChild(text);
}
textBuffer.delete(0, textBuffer.length());
}
Element el = null;
if (doc == null) {
doc = impl.createDocument(reader.getNamespaceURI(), reader.getLocalName(), null);
el = doc.getDocumentElement();
} else {
if (reader.getPrefix() != null && reader.getPrefix().length() > 0) {
el = doc.createElementNS(reader.getNamespaceURI(), reader.getPrefix() + ":" + reader.getLocalName());
} else {
el = doc.createElementNS(reader.getNamespaceURI(),reader.getLocalName());
}
}
for (int i = 0; i < reader.getNamespaceCount(); i++) {
if (reader.getNamespacePrefix(i) != null && reader.getNamespacePrefix(i).length() > 0) {
el.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns:" + reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
} else {
el.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, "xmlns", reader.getNamespaceURI(i));
}
}
for (int i = 0; i < reader.getAttributeCount(); i++) {
if (reader.getAttributePrefix(i) != null && reader.getAttributePrefix(i).length() > 0) {
el.setAttributeNS(reader.getAttributeNamespace(i), reader.getAttributePrefix(i) + ":" + reader.getAttributeLocalName(i),
reader.getAttributeValue(i));
} else {
el.setAttributeNS(reader.getAttributeNamespace(i),reader.getAttributeLocalName(i), reader.getAttributeValue(i));
}
}
if (el.getNamespaceURI() == null || !pragma.contains(el.getNamespaceURI())) {
if (!stack.empty()) {
stack.peek().setUserData("IS_CONTEXTUAL", "true", null);
}
el.setAttributeNS(LOCATION_NS, LOCATION_START_LINE_NSATTR, String.valueOf(reader.getLocation().getLineNumber()));
el.setAttributeNS(LOCATION_NS, LOCATION_START_COL_NSATTR, String.valueOf(reader.getLocation().getColumnNumber()));
}
stack.push(el);
break;
case XMLStreamConstants.END_ELEMENT:
if (textBuffer.length() > 0) {
if (!stack.isEmpty()) {// ignore text/whitespaces before
// root
Element top = stack.peek();
Text text = doc.createTextNode(textBuffer.toString());
top.appendChild(text);
}
textBuffer.delete(0, textBuffer.length());
}
Element top = stack.pop();
if (top.getNamespaceURI() == null || !pragma.contains(top.getNamespaceURI())) {
String endLine = String.valueOf(reader.getLocation().getLineNumber());
String endColumn = String.valueOf(reader.getLocation().getColumnNumber());
String startLine = top.getAttributeNS(LOCATION_NS, LOCATION_START_LINE_ATTR);
String startColumn = top.getAttributeNS(LOCATION_NS, LOCATION_START_COL_ATTR);
if ("true".equals(top.getUserData("IS_CONTEXTUAL")) && !startLine.equals(endLine) || !startColumn.equals(endColumn)) {
top.setAttributeNS(LOCATION_NS, LOCATION_END_LINE_NSATTR, endLine);
top.setAttributeNS(LOCATION_NS, LOCATION_END_COL_NSATTR, endColumn);
}
}
if (!stack.isEmpty()) {
Element parent = stack.peek();
parent.appendChild(top);
}
break;
case XMLStreamConstants.CHARACTERS:
textBuffer.append(reader.getText());
break;
}
}
return doc;
/* transform to new namespace aware doc so XPath pre-process operations will work
TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer tform = transformFactory.newTransformer();
dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
db = dbf.newDocumentBuilder();
DOMResult dr = new DOMResult(db.newDocument());
tform.transform(new DOMSource(doc), dr);
return (Document) dr.getNode();*/
} catch (Exception e) {
throw new ParserException(e);
}
}
public static void assertStart(XMLStreamReader input, QName element) throws XMLStreamException {
input.require(XMLStreamConstants.START_ELEMENT, element.getNamespaceURI(), element.getLocalPart());
}
public static void assertEnd(XMLStreamReader input, QName element) throws XMLStreamException {
input.require(XMLStreamConstants.END_ELEMENT, element.getNamespaceURI(), element.getLocalPart());
}
public static void skipChildren(XMLStreamReader input) throws XMLStreamException, ParserException {
skip(1, input);
}
public static void skipSiblings(XMLStreamReader input) throws XMLStreamException, ParserException {
skip(0, input);
}
public static void skip(int level, XMLStreamReader input) throws XMLStreamException, ParserException {
if (input.getEventType() != XMLStreamConstants.START_ELEMENT) {
throw new ParserException("reader must be in the start element state");
}
while (input.hasNext()) {
int type = input.next();
if (type == XMLStreamConstants.START_ELEMENT) {
level++;
} else if (type == XMLStreamConstants.END_ELEMENT) {
if (--level == 0) {
return;
}
}
}
}
public static void skipTo(XMLStreamReader input, Location location) throws XMLStreamException {
while (input.hasNext()) {
if (input.getLocation().getCharacterOffset() == location.getCharacterOffset()) {
break;
}
input.next();
}
}
public static void skipTo(XMLStreamReader input, org.apache.ode.spi.compiler.Location location) throws XMLStreamException {
if (location.getOffset() != -1) {
while (input.hasNext()) {
if (input.getLocation().getCharacterOffset() == location.getOffset()) {
break;
}
input.next();
}
} else {
while (input.hasNext()) {
if (input.getLocation().getLineNumber() == location.getLine() && input.getLocation().getColumnNumber() == location.getColumn()) {
break;
}
input.next();
}
}
}
// TODO need a skip to location so that location can be preserved on inlined
// content
public static Element extract(XMLStreamReader input) throws ParserException {
try {
TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer tform = transformFactory.newTransformer();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
DOMResult result = new DOMResult(doc);
tform.transform(new StAXSource(input), result);
return (Element) result.getNode();
} catch (Exception e) {
throw new ParserException(e);
}
}
public static String domToString(Document doc) throws ParserException {
try {
TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer tform = transformFactory.newTransformer();
tform.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
tform.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();
} catch (Exception e) {
throw new ParserException(e);
}
}
public static byte[] domToContent(Document doc) throws ParserException {
try {
TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer tform = transformFactory.newTransformer();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
tform.transform(new DOMSource(doc), new StreamResult(bos));
return bos.toByteArray();
} catch (Exception e) {
throw new ParserException(e);
}
}
public static Document contentToDom(byte[] content) throws ParserException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
return db.parse(new ByteArrayInputStream(content));
} catch (Exception e) {
throw new ParserException(e);
}
}
public static NamespaceContext buildNSContext(final Map<String, String> prefixMappings) {
final Map<String, String> nsMappings = new HashMap<String, String>();
for (Map.Entry<String, String> e : prefixMappings.entrySet()) {
nsMappings.put(e.getValue(), e.getKey());
}
return new NamespaceContext() {
@Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
@Override
public String getPrefix(String namespaceURI) {
return nsMappings.get(namespaceURI);
}
@Override
public String getNamespaceURI(String prefix) {
return prefixMappings.get(prefix);
}
};
}
}