/**
* 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;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import org.objectweb.asm.Type;
/**
* A specific type of DiffHandler which uses an OutputStream to create an
* XML document describing the changes in the diff.
* This is needed for java 1.2 compatibility for the ant task.
*
* @author Antony Riley
*/
public class StreamDiffHandler 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 BufferedWriter out;
/**
* Create a new StreamDiffHandler which writes to System.out
*
* @throws DiffException when there is an underlying exception, e.g.
* writing to a file caused an IOException
*/
public StreamDiffHandler() throws DiffException {
try {
out = new BufferedWriter(
new OutputStreamWriter(System.out, "UTF-8")
);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* Create a new StreamDiffHandler with the specified OutputStream.
*
* @param out Where to write output.
*/
public StreamDiffHandler(OutputStream out)
throws DiffException
{
try {
this.out = new BufferedWriter(
new OutputStreamWriter(out, "UTF-8")
);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* Start the diff.
* This writes out the start of a <diff> node.
*
* @param oldJar name of old jar file.
* @param newJar name of new jar file.
* @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 {
try {
out.write("");
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeClassInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeClassInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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
{
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeFieldInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeMethodInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeFieldInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
writeMethodInfo(info);
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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
{
try {
out.write("");
writeClassInfo(oldInfo);
out.write("");
writeClassInfo(newInfo);
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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
{
try {
out.write("");
writeFieldInfo(oldInfo);
out.write("");
writeFieldInfo(newInfo);
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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
{
try {
out.write("");
writeMethodInfo(oldInfo);
out.write("");
writeMethodInfo(newInfo);
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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 {
try {
out.write("");
out.newLine();
out.close();
} catch (IOException ioe) {
throw new DiffException(ioe);
}
}
/**
* 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.
* @throws IOException when there is an underlying IOException.
*/
protected void writeClassInfo(ClassInfo info) throws IOException {
out.write("");
}
String[] interfaces = info.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
out.write("");
}
out.write("");
}
/**
* 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.
* @throws IOException when there is an underlying IOException.
*/
protected void writeMethodInfo(MethodInfo info) throws IOException {
out.write("");
if (info.getDesc() != null) {
addMethodNodes(info.getDesc());
}
String[] exceptions = info.getExceptions();
if (exceptions != null) {
for (int i = 0; i < exceptions.length; i++) {
out.write("");
}
}
out.write("");
}
/**
* Write out information about a field.
* This writes out a <field> node with attributes describing the
* field.
*
* @param info Info about the field.
* @throws IOException when there is an underlying IOException.
*/
protected void writeFieldInfo(FieldInfo info) throws IOException {
out.write("");
if (info.getDesc() != null) {
addTypeNode(info.getDesc());
}
out.write("");
}
/**
* Add attributes describing some access flags.
* This adds the attributes to the attr field.
*
* @param info Info describing the access flags.
* @throws IOException when there is an underlying IOException.
*/
protected void addAccessFlags(AbstractInfo info) throws IOException {
out.write(" access=\"");
// Doesn't need escaping.
out.write(info.getAccessType());
out.write("\"");
if (info.isAbstract())
out.write(" abstract=\"yes\"");
if (info.isAnnotation())
out.write(" annotation=\"yes\"");
if (info.isBridge())
out.write(" bridge=\"yes\"");
if (info.isDeprecated())
out.write(" deprecated=\"yes\"");
if (info.isEnum())
out.write(" enum=\"yes\"");
if (info.isFinal())
out.write(" final=\"yes\"");
if (info.isInterface())
out.write(" interface=\"yes\"");
if (info.isNative())
out.write(" native=\"yes\"");
if (info.isStatic())
out.write(" static=\"yes\"");
if (info.isStrict())
out.write(" strict=\"yes\"");
if (info.isSuper())
out.write(" super=\"yes\"");
if (info.isSynchronized())
out.write(" synchronized=\"yes\"");
if (info.isSynthetic())
out.write(" synthetic=\"yes\"");
if (info.isTransient())
out.write(" transient=\"yes\"");
if (info.isVarargs())
out.write(" varargs=\"yes\"");
if (info.isVolatile())
out.write(" 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.
* @throws IOException when there is an underlying IOException.
*/
protected void addMethodNodes(String desc) throws IOException {
Type[] args = Type.getArgumentTypes(desc);
Type ret = Type.getReturnType(desc);
out.write("");
for (int i = 0; i < args.length; i++)
addTypeNode(args[i]);
out.write("");
out.write("");
addTypeNode(ret);
out.write("");
}
/**
* Add a type node for the specified descriptor.
*
* @param desc A type descriptor.
* @throws IOException when there is an underlying IOException.
*/
protected void addTypeNode(String desc) throws IOException {
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.
* @throws IOException when there is an underlying IOException.
*/
protected void addTypeNode(Type type) throws IOException {
out.write("");
break;
case Type.BYTE:
out.write(" primitive=\"yes\" name=\"byte\"/>");
break;
case Type.CHAR:
out.write(" primitive=\"yes\" name=\"char\"/>");
break;
case Type.DOUBLE:
out.write(" primitive=\"yes\" name=\"double\"/>");
break;
case Type.FLOAT:
out.write(" primitive=\"yes\" name=\"float\"/>");
break;
case Type.INT:
out.write(" primitive=\"yes\" name=\"int\"/>");
break;
case Type.LONG:
out.write(" primitive=\"yes\" name=\"long\"/>");
break;
case Type.OBJECT:
out.write(" name=\"");
out.write(xmlEscape(type.getInternalName()));
out.write("\"/>");
break;
case Type.SHORT:
out.write(" primitive=\"yes\" name=\"short\"/>");
break;
case Type.VOID:
out.write(" primitive=\"yes\" name=\"void\"/>");
break;
}
}
/**
* Escape some text into a format suitable for output as xml.
*
* @param str the text to format
* @return the formatted text
*/
private final String xmlEscape(final String str) {
StringBuffer ret = new StringBuffer(str.length());
for(int i=0;i':
ret.append(">");
break;
default:
ret.append(ch);
}
}
return ret.toString();
}
}