com.github.javaparser.printer.XmlPrinter Maven / Gradle / Ivy
Show all versions of javaparser-core Show documentation
/*
* Copyright (C) 2007-2010 Júlio Vilmar Gesser.
* Copyright (C) 2011, 2013-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser 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.
*/
package com.github.javaparser.printer;
import static com.github.javaparser.utils.Utils.assertNonEmpty;
import static com.github.javaparser.utils.Utils.assertNotNull;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.metamodel.NodeMetaModel;
import com.github.javaparser.metamodel.PropertyMetaModel;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.function.Predicate;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* Outputs an XML file containing the AST meant for inspecting it.
*/
public class XmlPrinter {
private final boolean outputNodeType;
private static final Class> TYPE_CLASS = Type.class;
public XmlPrinter(boolean outputNodeType) {
this.outputNodeType = outputNodeType;
}
/**
* Generate a xml string for given AST Node. Tag name of root element in the result document will be "root".
*
* @param node AST node to be converted to XML
* @return XML document corresponding to node
*/
public String output(Node node) {
return stringWriterOutput(node, "root").toString();
}
/**
* Output XML data from an AST node to a String Builder. This method is kept for backward compatilibity only and
* should be removed in future releases.
*
* @param node AST node to be converted to XML
* @param name Tag name of root element in the resulting document
* @param level Nesting level of node in tree. Not used.
* @param builder Target object to receive the generated XML
*/
@Deprecated
public void output(Node node, String name, int level, StringBuilder builder) {
builder.append(stringWriterOutput(node, name).toString());
}
/**
* Create a string writer filled with XML document representing an AST node.
*
* Returned stringWriter is not closed upon return because doing so {@link StringWriter#close() has no effect}.
* So users of this method are not required to close it.
*
* @param node AST node to be converted to XML
* @param name Tag name of root element in the resulting document
* @return Stringwriter filled with XML document
* @throws RuntimeXMLStreamException Unchecked exception wrapping checked {@link XMLStreamException}, when any
* error on producing XML output occours
*/
public StringWriter stringWriterOutput(Node node, String name) {
StringWriter stringWriter = new StringWriter();
try {
outputDocument(node, name, stringWriter);
} catch (XMLStreamException ex) {
throw new RuntimeXMLStreamException(ex);
}
return stringWriter;
}
/**
* Output the XML Document representing given AST node to given writer.
*
* This method creates a {@link XMLStreamWriter} that writes to given writer and delegates execution to
* {@link #outputDocument(Node, String, XMLStreamWriter)}
*
*
* Provided writer is NOT closed at the end of execution of this method.
*
*
* @param node AST node to be converted to XML
* @param name Tag name of root element of document
* @param writer Target to get the document writen to
* @throws XMLStreamException When any error on outputting XML occours
*/
public void outputDocument(Node node, String name, Writer writer) throws XMLStreamException {
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xmlWriter = outputFactory.createXMLStreamWriter(writer);
try {
outputDocument(node, name, xmlWriter);
} finally {
xmlWriter.close();
}
}
/**
* Output the XML Document representing an AST node to given XMLStreamWriter.
*
* This method outputs the starting of XML document, then delegates to
* {@link #outputNode(Node, String, XMLStreamWriter) for writing the root element of XML document, and finally
* outputs the ending of XML document.
*
*
* This method is used when the root element of an XML document corresponds to an AST node. Would an element
* corresponding to an AST node be written as child of another element, then
* {@link #outputNode(String, Node, XMLStreamWriter)} should be used instead. Actually, outputNode is used
* recursively for outputting nested elements from AST.
*
*
* Provided xmlWriter is NOT closed at the end of execution of this method.
*
*
* @param node AST node to be converted to XML
* @param name Tag name of root element of document
* @param xmlWriter Target to get document written to
* @throws XMLStreamException When any error on outputting XML occours
* @see outputNode(String, Node, XMLStreamWriter)
*/
public void outputDocument(Node node, String name, XMLStreamWriter xmlWriter) throws XMLStreamException {
xmlWriter.writeStartDocument();
outputNode(node, name, xmlWriter);
xmlWriter.writeEndDocument();
}
/**
* Output the XML Element representing an AST node to given writer.
*
* This method outputs an XML Element with given tag name to writer. It is used recursively for generating nested
* elements corresponding to AST.
*
*
* For generating a complete XML document from an AST node, {@link outputDocument(String, Node, XMLStreamWriter)}
* should be used instead.
*
*
* Provided xmlWriter is NOT closed at the end of execution of this method.
*
*
* @param node AST node to be converted to XML
* @param name Tag name of element corresponding to node
* @param xmlWriter Target to get XML written to
* @throws XMLStreamException When any error on outputting XML occours
* @see outputDocument(String, Node, XMLStreamWriter)
*/
public void outputNode(Node node, String name, XMLStreamWriter xmlWriter) throws XMLStreamException {
assertNotNull(node);
assertNonEmpty(name);
assertNotNull(xmlWriter);
NodeMetaModel metaModel = node.getMetaModel();
List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
Predicate nonNullNode = propertyMetaModel -> propertyMetaModel.getValue(node) != null;
Predicate nonEmptyList =
propertyMetaModel -> ((NodeList) propertyMetaModel.getValue(node)).isNonEmpty();
Predicate typeList = propertyMetaModel -> TYPE_CLASS == propertyMetaModel.getType();
xmlWriter.writeStartElement(name);
// Output node type attribute
if (outputNodeType) {
xmlWriter.writeAttribute("type", metaModel.getTypeName());
}
try {
// Output attributes
allPropertyMetaModels.stream()
.filter(PropertyMetaModel::isAttribute)
.filter(PropertyMetaModel::isSingular)
.forEach(attributeMetaModel -> {
try {
final String attributeName = attributeMetaModel.getName();
final String attributeValue =
attributeMetaModel.getValue(node).toString();
xmlWriter.writeAttribute(attributeName, attributeValue);
} catch (XMLStreamException ex) {
throw new RuntimeXMLStreamException(ex);
}
});
// Output singular subNodes
allPropertyMetaModels.stream()
.filter(PropertyMetaModel::isNode)
.filter(PropertyMetaModel::isSingular)
.filter(nonNullNode)
.forEach(subNodeMetaModel -> {
try {
final Node subNode = (Node) subNodeMetaModel.getValue(node);
final String subNodeName = subNodeMetaModel.getName();
outputNode(subNode, subNodeName, xmlWriter);
} catch (XMLStreamException ex) {
throw new RuntimeXMLStreamException(ex);
}
});
// Output list subNodes
allPropertyMetaModels.stream()
.filter(PropertyMetaModel::isNodeList)
.filter(nonNullNode)
.filter(nonEmptyList.or(typeList))
.forEach(listMetaModel -> {
try {
String listName = listMetaModel.getName();
String singular = listName.substring(0, listName.length() - 1);
NodeList extends Node> nodeList = (NodeList) listMetaModel.getValue(node);
xmlWriter.writeStartElement(listName);
for (Node subNode : nodeList) {
outputNode(subNode, singular, xmlWriter);
}
xmlWriter.writeEndElement();
} catch (XMLStreamException ex) {
throw new RuntimeXMLStreamException(ex);
}
});
} catch (RuntimeXMLStreamException ex) {
throw ex.getXMLStreamCause();
}
xmlWriter.writeEndElement();
}
public static void print(Node node) {
System.out.println(new XmlPrinter(true).output(node));
}
}
/**
* RuntimeException subclass encapsulationg XMLStreamException.
*
* Used for generating methods without checked exceptions, but allowing to selectively capture of XMLStreamException at
* higher levels.
*
*/
class RuntimeXMLStreamException extends RuntimeException {
public RuntimeXMLStreamException(XMLStreamException cause) {
super(cause);
}
public XMLStreamException getXMLStreamCause() {
return (XMLStreamException) super.getCause();
}
}