
org.osjava.jardiff.DOMDiffHandler Maven / Gradle / Ivy
/**
* Copyright 2012-2014 Julien Eluard and contributors
* This project includes software developed by Julien Eluard: https://github.com/jeluard/
*
* Licensed 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.osjava.jardiff;
/* Not in 1.4.2
import javax.xml.XMLConstants;
*/
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Result;
import org.w3c.dom.*;
import org.objectweb.asm.Type;
/**
* A specific type of DiffHandler which uses DOM to create an XML document
* describing the changes in the diff.
*
* @author Antony Riley
*/
public class DOMDiffHandler implements DiffHandler
{
/**
* The XML namespace used.
*/
public static final String XML_URI = "http://www.osjava.org/jardiff/0.1";
/**
* The javax.xml.transform.sax.Transformer used to convert
* the DOM to text.
*/
private final Transformer transformer;
/**
* Where we write the result to.
*/
private final Result result;
/**
* The document object we're building
*/
private final Document doc;
/**
* The current Node.
*/
private Node currentNode;
/**
* Create a new DOMDiffHandler which writes to System.out
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public DOMDiffHandler() throws DiffException {
try {
TransformerFactory tf = TransformerFactory.newInstance();
this.transformer = tf.newTransformer();
this.result = new StreamResult(System.out);
this.currentNode = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
this.doc = db.newDocument();
} catch (TransformerConfigurationException tce) {
throw new DiffException(tce);
} catch (ParserConfigurationException pce) {
throw new DiffException(pce);
}
}
/**
* Create a new DOMDiffHandler with the specified Transformer and Result.
* This method allows the user to choose what they are going to do with
* the output in a flexible manner, allowing a stylesheet to be specified
* and some result object.
*
* @param transformer The transformer to transform the output with.
* @param result Where to put the result.
*/
public DOMDiffHandler(Transformer transformer, Result result)
throws DiffException
{
try {
this.transformer = transformer;
this.result = result;
this.currentNode = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
this.doc = db.newDocument();
} catch (ParserConfigurationException pce) {
throw new DiffException(pce);
}
}
/**
* Start the diff.
* This writes out the start of a <diff> node.
*
* @param oldJar ignored
* @param newJar ignored
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startDiff(String oldJar, String newJar) throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "diff");
tmp.setAttribute( "old", oldJar);
tmp.setAttribute( "new", newJar);
doc.appendChild(tmp);
currentNode = tmp;
}
/**
* Start the list of old contents.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startOldContents() throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "oldcontents");
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Start the list of old contents.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startNewContents() throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "newcontents");
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Add a contained class.
*
* @param info information about a class
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void contains(ClassInfo info) throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "class");
tmp.setAttribute("name", info.getName());
currentNode.appendChild(tmp);
}
/**
* End the list of old contents.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endOldContents() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* End the list of new contents.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endNewContents() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* Start the removed node.
* This writes out a <removed> node.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startRemoved() throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "removed");
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Write out class info for a removed class.
* This writes out the nodes describing a class
*
* @param info The info to write out.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void classRemoved(ClassInfo info) throws DiffException {
writeClassInfo(info);
}
/**
* End the removed section.
* This closes the <removed> tag.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endRemoved() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* Start the added section.
* This opens the <added> tag.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startAdded() throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "added");
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Write out the class info for an added class.
* This writes out the nodes describing an added class.
*
* @param info The class info describing the added class.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void classAdded(ClassInfo info) throws DiffException {
writeClassInfo(info);
}
/**
* End the added section.
* This closes the <added> tag.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endAdded() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* Start the changed section.
* This writes out the <changed> node.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startChanged() throws DiffException {
Element tmp = doc.createElementNS(XML_URI, "changed");
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Start a changed section for an individual class.
* This writes out an <classchanged> node with the real class
* name as the name attribute.
*
* @param internalName the internal name of the class that has changed.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void startClassChanged(String internalName) throws DiffException
{
Element tmp = doc.createElementNS(XML_URI, "classchanged");
tmp.setAttribute( "name", internalName);
currentNode.appendChild(tmp);
currentNode = tmp;
}
/**
* Write out info about a removed field.
* This just writes out the field info, it will be inside a start/end
* removed section.
*
* @param info Info about the field that's been removed.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void fieldRemoved(FieldInfo info) throws DiffException {
writeFieldInfo(info);
}
/**
* Write out info about a removed method.
* This just writes out the method info, it will be inside a start/end
* removed section.
*
* @param info Info about the method that's been removed.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void methodRemoved(MethodInfo info) throws DiffException {
writeMethodInfo(info);
}
/**
* Write out info about an added field.
* This just writes out the field info, it will be inside a start/end
* added section.
*
* @param info Info about the added field.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void fieldAdded(FieldInfo info) throws DiffException {
writeFieldInfo(info);
}
/**
* Write out info about a added method.
* This just writes out the method info, it will be inside a start/end
* added section.
*
* @param info Info about the added method.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void methodAdded(MethodInfo info) throws DiffException {
writeMethodInfo(info);
}
/**
* Write out info aboout a changed class.
* This writes out a <classchange> node, followed by a
* <from> node, with the old information about the class
* followed by a <to> node with the new information about the
* class.
*
* @param oldInfo Info about the old class.
* @param newInfo Info about the new class.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void classChanged(ClassInfo oldInfo, ClassInfo newInfo)
throws DiffException
{
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "classchange");
Element from = doc.createElementNS(XML_URI, "from");
Element to = doc.createElementNS(XML_URI, "to");
tmp.appendChild(from);
tmp.appendChild(to);
currentNode.appendChild(tmp);
this.currentNode = from;
writeClassInfo(oldInfo);
this.currentNode = to;
writeClassInfo(newInfo);
this.currentNode = currentNode;
}
/**
* Invokes {@link #classChanged(ClassInfo, ClassInfo)}.
*/
public void classDeprecated(ClassInfo oldInfo, ClassInfo newInfo)
throws DiffException
{
classChanged(oldInfo, newInfo);
}
/**
* Write out info aboout a changed field.
* This writes out a <fieldchange> node, followed by a
* <from> node, with the old information about the field
* followed by a <to> node with the new information about the
* field.
*
* @param oldInfo Info about the old field.
* @param newInfo Info about the new field.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void fieldChanged(FieldInfo oldInfo, FieldInfo newInfo)
throws DiffException
{
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "fieldchange");
Element from = doc.createElementNS(XML_URI, "from");
Element to = doc.createElementNS(XML_URI, "to");
tmp.appendChild(from);
tmp.appendChild(to);
currentNode.appendChild(tmp);
this.currentNode = from;
writeFieldInfo(oldInfo);
this.currentNode = to;
writeFieldInfo(newInfo);
this.currentNode = currentNode;
}
/**
* Invokes {@link #fieldChanged(FieldInfo, FieldInfo)}.
*/
public void fieldDeprecated(FieldInfo oldInfo, FieldInfo newInfo)
throws DiffException {
fieldChanged(oldInfo, newInfo);
}
/**
* Write out info aboout a changed method.
* This writes out a <methodchange> node, followed by a
* <from> node, with the old information about the method
* followed by a <to> node with the new information about the
* method.
*
* @param oldInfo Info about the old method.
* @param newInfo Info about the new method.
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void methodChanged(MethodInfo oldInfo, MethodInfo newInfo)
throws DiffException
{
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "methodchange");
Element from = doc.createElementNS(XML_URI, "from");
Element to = doc.createElementNS(XML_URI, "to");
tmp.appendChild(from);
tmp.appendChild(to);
currentNode.appendChild(tmp);
this.currentNode = from;
writeMethodInfo(oldInfo);
this.currentNode = to;
writeMethodInfo(newInfo);
this.currentNode = currentNode;
}
/**
* Invokes {@link #methodChanged(MethodInfo, MethodInfo)}.
*/
public void methodDeprecated(MethodInfo oldInfo, MethodInfo newInfo)
throws DiffException
{
methodChanged(oldInfo, newInfo);
}
/**
* End the changed section for an individual class.
* This closes the <classchanged> node.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endClassChanged() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* End the changed section.
* This closes the <changed> node.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endChanged() throws DiffException {
currentNode = currentNode.getParentNode();
}
/**
* End the diff.
* This closes the <diff> node.
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public void endDiff() throws DiffException {
DOMSource source = new DOMSource(doc);
try {
transformer.transform(source, result);
} catch (TransformerException te) {
throw new DiffException(te);
}
}
/**
* Write out information about a class.
* This writes out a <class> node, which contains information about
* what interfaces are implemented each in a <implements> node.
*
* @param info Info about the class to write out.
*/
protected void writeClassInfo(ClassInfo info) {
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "class");
currentNode.appendChild(tmp);
this.currentNode = tmp;
addAccessFlags(info);
if (info.getName() != null)
tmp.setAttribute( "name",
info.getName());
if (info.getSignature() != null)
tmp.setAttribute( "signature",
info.getSignature());
if (info.getSupername() != null)
tmp.setAttribute( "superclass",
info.getSupername());
String[] interfaces = info.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Element iface = doc.createElementNS(XML_URI, "implements");
tmp.appendChild(iface);
iface.setAttribute( "name",
interfaces[i]);
}
this.currentNode = currentNode;
}
/**
* Write out information about a method.
* This writes out a <method> node which contains information about
* the arguments, the return type, and the exceptions thrown by the
* method.
*
* @param info Info about the method.
*/
protected void writeMethodInfo(MethodInfo info) {
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "method");
currentNode.appendChild(tmp);
this.currentNode = tmp;
addAccessFlags(info);
if (info.getName() != null)
tmp.setAttribute( "name", info.getName());
if (info.getSignature() != null)
tmp.setAttribute( "signature", info.getSignature());
if (info.getDesc() != null)
addMethodNodes(info.getDesc());
String[] exceptions = info.getExceptions();
if (exceptions != null) {
for (int i = 0; i < exceptions.length; i++) {
Element excep = doc.createElementNS(XML_URI, "exception");
excep.setAttribute( "name", exceptions[i]);
tmp.appendChild(excep);
}
}
this.currentNode = currentNode;
}
/**
* Write out information about a field.
* This writes out a <field> node with attributes describing the
* field.
*
* @param info Info about the field.
*/
protected void writeFieldInfo(FieldInfo info) {
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI, "field");
currentNode.appendChild(tmp);
this.currentNode = tmp;
addAccessFlags(info);
if (info.getName() != null)
tmp.setAttribute( "name",
info.getName());
if (info.getSignature() != null)
tmp.setAttribute( "signature",
info.getSignature());
if (info.getValue() != null)
tmp.setAttribute( "value",
info.getValue().toString());
if (info.getDesc() != null)
addTypeNode(info.getDesc());
this.currentNode = currentNode;
}
/**
* Add attributes describing some access flags.
* This adds the attributes to the attr field.
*
* @see #attr
* @param info Info describing the access flags.
*/
protected void addAccessFlags(AbstractInfo info) {
Element currentNode = (Element) this.currentNode;
currentNode.setAttribute( "access", info.getAccessType());
if (info.isAbstract())
currentNode.setAttribute( "abstract", "yes");
if (info.isAnnotation())
currentNode.setAttribute( "annotation", "yes");
if (info.isBridge())
currentNode.setAttribute( "bridge", "yes");
if (info.isDeprecated())
currentNode.setAttribute( "deprecated", "yes");
if (info.isEnum())
currentNode.setAttribute( "enum", "yes");
if (info.isFinal())
currentNode.setAttribute( "final", "yes");
if (info.isInterface())
currentNode.setAttribute( "interface", "yes");
if (info.isNative())
currentNode.setAttribute( "native", "yes");
if (info.isStatic())
currentNode.setAttribute( "static", "yes");
if (info.isStrict())
currentNode.setAttribute( "strict", "yes");
if (info.isSuper())
currentNode.setAttribute( "super", "yes");
if (info.isSynchronized())
currentNode.setAttribute( "synchronized", "yes");
if (info.isSynthetic())
currentNode.setAttribute( "synthetic", "yes");
if (info.isTransient())
currentNode.setAttribute( "transient", "yes");
if (info.isVarargs())
currentNode.setAttribute( "varargs", "yes");
if (info.isVolatile())
currentNode.setAttribute( "volatile", "yes");
}
/**
* Add the method nodes for the method descriptor.
* This writes out an <arguments> node containing the
* argument types for the method, followed by a <return> node
* containing the return type.
*
* @param desc The descriptor for the method to write out.
*/
protected void addMethodNodes(String desc) {
Type[] args = Type.getArgumentTypes(desc);
Type ret = Type.getReturnType(desc);
Node currentNode = this.currentNode;
Element tmp = doc.createElementNS(XML_URI,"arguments");
currentNode.appendChild(tmp);
this.currentNode = tmp;
for (int i = 0; i < args.length; i++)
addTypeNode(args[i]);
tmp = doc.createElementNS(XML_URI,"return");
currentNode.appendChild(tmp);
this.currentNode = tmp;
addTypeNode(ret);
this.currentNode = currentNode;
}
/**
* Add a type node for the specified descriptor.
*
* @param desc A type descriptor.
*/
protected void addTypeNode(String desc) {
addTypeNode(Type.getType(desc));
}
/**
* Add a type node for the specified type.
* This writes out a <type> node with attributes describing
* the type.
*
* @param type The type to describe.
*/
protected void addTypeNode(Type type) {
Element tmp = doc.createElementNS(XML_URI, "type");
currentNode.appendChild(tmp);
int i = type.getSort();
if (i == Type.ARRAY) {
tmp.setAttribute( "array", "yes");
tmp.setAttribute( "dimensions",
"" + type.getDimensions());
type = type.getElementType();
i = type.getSort();
}
switch (i) {
case Type.BOOLEAN:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "boolean");
break;
case Type.BYTE:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "byte");
break;
case Type.CHAR:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "char");
break;
case Type.DOUBLE:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "double");
break;
case Type.FLOAT:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "float");
break;
case Type.INT:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "int");
break;
case Type.LONG:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "long");
break;
case Type.OBJECT:
tmp.setAttribute( "name", type.getInternalName());
break;
case Type.SHORT:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "short");
break;
case Type.VOID:
tmp.setAttribute( "primitive", "yes");
tmp.setAttribute( "name", "void");
break;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy