diff options
author | Julien Eluard <[email protected]> | 2013-07-07 04:03:29 -0700 |
---|---|---|
committer | Julien Eluard <[email protected]> | 2013-07-07 04:03:29 -0700 |
commit | 941c16dbe976c4acfa5faa24214e351b56b58791 (patch) | |
tree | 6ef0d90f3018b927c0c2a56f6aff0d76a4d1981f | |
parent | e43dd9d0d77698758655524005653bdce6d0ad93 (diff) | |
parent | 95a8f35e037fe8498df3904e47e74d639c1d1a31 (diff) |
Merge pull request #12 from tesla/master
Integrating JarDiff source
-rwxr-xr-x | api/pom.xml | 14 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/AbstractDiffHandler.java | 68 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/AbstractInfo.java | 316 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/ClassInfo.java | 159 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/ClassInfoVisitor.java | 146 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/DOMDiffHandler.java | 722 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/DiffCriteria.java | 92 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/DiffException.java | 57 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/DiffHandler.java | 263 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/FieldInfo.java | 106 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/JarDiff.java | 489 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/MethodInfo.java | 105 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/SimpleDiffCriteria.java | 159 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/StreamDiffHandler.java | 784 | ||||
-rw-r--r-- | api/src/main/java/org/osjava/jardiff/Tools.java | 80 |
15 files changed, 3557 insertions, 3 deletions
diff --git a/api/pom.xml b/api/pom.xml index 53e0855..a800ad6 100755 --- a/api/pom.xml +++ b/api/pom.xml @@ -20,11 +20,19 @@ <version>2.0.0</version> </dependency> <dependency> - <groupId>jardiff</groupId> - <artifactId>jardiff</artifactId> - <version>0.2</version> + <groupId>asm</groupId> + <artifactId>asm</artifactId> </dependency> <dependency> + <groupId>asm</groupId> + <artifactId>asm-commons</artifactId> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>1.0</version> + </dependency> + <dependency> <groupId>de.tototec</groupId> <artifactId>de.tototec.cmdoption</artifactId> <version>0.2.0</version> diff --git a/api/src/main/java/org/osjava/jardiff/AbstractDiffHandler.java b/api/src/main/java/org/osjava/jardiff/AbstractDiffHandler.java new file mode 100644 index 0000000..2c06906 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/AbstractDiffHandler.java @@ -0,0 +1,68 @@ +/* + * org.osjava.jardiff.AbstractDiffHandler + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * An abstract implementation of DiffHandler which provides utility methods. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public abstract class AbstractDiffHandler implements DiffHandler +{ + /** + * Get the java classname given the internal class name internalName. + * + * @return the classname for internalName + */ + protected final String getClassName(String internalName) { + StringBuffer ret = new StringBuffer(internalName.length()); + for (int i = 0; i < internalName.length(); i++) { + char ch = internalName.charAt(i); + switch (ch) { + case '$': + case '/': + ret.append('.'); + break; + default: + ret.append(ch); + } + } + return ret.toString(); + } +} diff --git a/api/src/main/java/org/osjava/jardiff/AbstractInfo.java b/api/src/main/java/org/osjava/jardiff/AbstractInfo.java new file mode 100644 index 0000000..197752c --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/AbstractInfo.java @@ -0,0 +1,316 @@ +/* + * org.osjava.jardiff.AbstractInfo + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +import org.objectweb.asm.Opcodes; + +/** + * An abstract class representing information about a class, method or field. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public abstract class AbstractInfo +{ + /** + * The string used to represent a class, method or field with public + * access. + */ + public final String ACCESS_PUBLIC = "public"; + + /** + * The string used to represent a class, method or field with protected + * access. + */ + public final String ACCESS_PROTECTED = "protected"; + + /** + * The string used to represent a class, method or field with package + * private access. + * Package private access is the default access level used by java when + * you do not specify one of public, protected or private. + */ + public final String ACCESS_PACKAGE = "package"; + + /** + * The string used to represent a class, method or field with private + * access. + */ + public final String ACCESS_PRIVATE = "private"; + + /** + * The access flags for this class, method or field. + */ + private final int access; + + /** + * The internal name of this class, method or field. + */ + private final String name; + + /** + * Construct a new AbstractInfo with the specified access and name. + * + * @param access The access flags for this class, method or field. + * @param name The internal name of this class, method or field. + */ + public AbstractInfo(int access, String name) { + this.access = access; + this.name = name; + } + + /** + * Get the access flags for this class, method or field. + * + * @return the access flags. + */ + public final int getAccess() { + return access; + } + + /** + * Get the internal name of this class, method or field. + * + * @return the name + */ + public final String getName() { + return name; + } + + /** + * Test if this class, method or field is public. + * + * @return true if it is public. + */ + public final boolean isPublic() { + return (access & Opcodes.ACC_PUBLIC) != 0; + } + + /** + * Test if this class, method or field is protected. + * + * @return true if it is protected. + */ + public final boolean isProtected() { + return (access & Opcodes.ACC_PROTECTED) != 0; + } + + /** + * Test if this class, method or field is package private. + * + * @return true if it is package private. + */ + public final boolean isPackagePrivate() { + return (access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | + Opcodes.ACC_PRIVATE)) == 0; + } + + /** + * Test if this class, method or field is private. + * + * @return true if it is private. + */ + public final boolean isPrivate() { + return (access & Opcodes.ACC_PRIVATE) != 0; + } + + /** + * Test if this class, method or field is abstract. + * + * @return true if it is abstract. + */ + public final boolean isAbstract() { + return (access & Opcodes.ACC_ABSTRACT) != 0; + } + + /** + * Test if this class, method or field is annotation + * + * @return true if it is annotation. + */ + public final boolean isAnnotation() { + return (access & Opcodes.ACC_ANNOTATION) != 0; + } + + /** + * Test if this class, method or field is a bridge + * + * @return true if it is a bridge. + */ + public final boolean isBridge() { + return (access & Opcodes.ACC_BRIDGE) != 0; + } + + /** + * Test if this class, method or field is deprecated. + * + * @return true if it is deprecated. + */ + public final boolean isDeprecated() { + return (access & Opcodes.ACC_DEPRECATED) != 0; + } + + /** + * Test if this class, method or field is an enum. + * + * @return true if it is an enum. + */ + public final boolean isEnum() { + return (access & Opcodes.ACC_ENUM) != 0; + } + + /** + * Test if this class, method or field is final. + * + * @return true if it is final. + */ + public final boolean isFinal() { + return (access & Opcodes.ACC_FINAL) != 0; + } + + /** + * Test if this class, method or field is an interface. + * + * @return true if it is an interface. + */ + public final boolean isInterface() { + return (access & Opcodes.ACC_INTERFACE) != 0; + } + + /** + * Test if this class, method or field is native. + * + * @return true if it is native. + */ + public final boolean isNative() { + return (access & Opcodes.ACC_NATIVE) != 0; + } + + /** + * Test if this class, method or field is static. + * + * @return true if it is static. + */ + public final boolean isStatic() { + return (access & Opcodes.ACC_STATIC) != 0; + } + + /** + * Test if this class, method or field is string. + * + * @return true if it is strict. + */ + public final boolean isStrict() { + return (access & Opcodes.ACC_STRICT) != 0; + } + + /** + * Test if this class, method or field is super. + * + * @return true if it is super. + */ + public final boolean isSuper() { + return (access & Opcodes.ACC_SUPER) != 0; + } + + /** + * Test if this class, method or field is synchronized. + * + * @return true if it is synchronized + */ + public final boolean isSynchronized() { + return (access & Opcodes.ACC_SYNCHRONIZED) != 0; + } + + /** + * Test if this class, method or field is synthetic. + * + * @return true if it is synchronized. + */ + public final boolean isSynthetic() { + return (access & Opcodes.ACC_SYNTHETIC) != 0; + } + + /** + * Test if this class or field is transient. + * If this flag is set on a method it means something different. + * + * @return true if it is transient. + */ + public final boolean isTransient() { + return !(this instanceof MethodInfo) && + ((access & Opcodes.ACC_TRANSIENT) != 0); + } + + /** + * Test if this method is varargs. + * If this flag is set on a class or field it means something different. + * Well, it probably shouldn't be set on a class as it would make + * no sense, it only really makes sense on fields and methods. + * + * @return true if it is vargargs. + */ + public final boolean isVarargs() { + return (this instanceof MethodInfo) && + ((access & Opcodes.ACC_VARARGS) != 0); + } + + /** + * Test if this class, method or field is volatile. + * + * @return true if it is volatile. + */ + public final boolean isVolatile() { + return (access & Opcodes.ACC_VOLATILE) != 0; + } + + /** + * Retrivie the access level for this class, method or field. + * + * @return the access level + */ + public final String getAccessType() { + if (isPublic()) + return ACCESS_PUBLIC; + if (isProtected()) + return ACCESS_PROTECTED; + if (isPrivate()) + return ACCESS_PRIVATE; + return ACCESS_PACKAGE; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/ClassInfo.java b/api/src/main/java/org/osjava/jardiff/ClassInfo.java new file mode 100644 index 0000000..d27ccc3 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/ClassInfo.java @@ -0,0 +1,159 @@ +/* + * org.osjava.jardiff.ClassInfo + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; +import java.util.Map; + +/** + * Information about a class file. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public final class ClassInfo extends AbstractInfo +{ + /** + * The classfile version number. + */ + private int version; + + /** + * The class signature. + */ + private String signature; + + /** + * The internal classname of the superclass. + */ + private String supername; + + /** + * An array of names of internal classnames of interfaces implmented + * by the class. + */ + private String[] interfaces; + + /** + * A map of method signature to MethodInfo, for the methods provided + * by this class. + */ + private Map methodMap; + + /** + * A map of field signature to FieldInfo, for the fields provided by + * this class. + */ + private Map fieldMap; + + /** + * Create a new classinfo. + * + * @param version the class file version number. + * @param access the access flags for the class. + * @param name the internal name of the class. + * @param signature the signature of the class. + * @param interfaces an array of internal names of interfaces implemented + * by the class. + * @param methodMap a map of methods provided by this class. + * @param fieldMap a map of fields provided by this class. + */ + public ClassInfo(int version, int access, String name, String signature, + String supername, String[] interfaces, Map methodMap, + Map fieldMap) { + super(access, name); + this.version = version; + this.signature = signature; + this.supername = supername; + this.interfaces = interfaces; + this.methodMap = methodMap; + this.fieldMap = fieldMap; + } + + /** + * Get the class file version. + * + * @return The class file version as specified in the java language spec. + */ + public final int getVersion() { + return version; + } + + /** + * Get the class signature. + * + * @return the class signature + */ + public final String getSignature() { + return signature; + } + + /** + * Get the internal name of the superclass. + * + * @return the internal name of the superclass + */ + public final String getSupername() { + return supername; + } + + /** + * Get the internal names of the interfaces implemented by this class + * + * @return an array of internal names of classes implemented by the class. + */ + public final String[] getInterfaces() { + return interfaces; + } + + /** + * Get the map of method signatures to methods. + * + * @return a map with method signatures as keys, and MethodInfos as values. + */ + public final Map getMethodMap() { + return methodMap; + } + + /** + * Get the map of field signatures to fields. + * + * @return a map with field signatures as keys, and FieldInfos as values. + */ + public final Map getFieldMap() { + return fieldMap; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/ClassInfoVisitor.java b/api/src/main/java/org/osjava/jardiff/ClassInfoVisitor.java new file mode 100644 index 0000000..a97885d --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/ClassInfoVisitor.java @@ -0,0 +1,146 @@ +/* + * org.osjava.jardiff.ClassInfoVisitor + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; +import java.util.HashMap; +import java.util.Map; + +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.EmptyVisitor; + +/** + * A reusable class which uses the ASM to build up ClassInfo about a + * java class file. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public class ClassInfoVisitor extends EmptyVisitor +{ + /** + * The class file version. + */ + private int version; + + /** + * The access flags for the class. + */ + + private int access; + + /** + * The internal name of the class. + */ + private String name; + + /** + * The signature of the class + */ + private String signature; + + /** + * The internal name of the superclass. + */ + private String supername; + + /** + * An array of internal names of interfaces implemented by this class. + */ + private String[] interfaces; + + /** + * A map of method signature to a MethodInfo describing the method. + */ + private Map methodMap; + + /** + * A map of field signature to a FieldInfo describing the field. + */ + private Map fieldMap; + + /** + * Reset this ClassInfoVisitor so that it can be used to visit another + * class. + */ + public void reset() { + methodMap = new HashMap(); + fieldMap = new HashMap(); + } + + /** + * The the classInfo this ClassInfoVisitor has built up about a class + */ + public ClassInfo getClassInfo() { + return new ClassInfo(version, access, name, signature, supername, + interfaces, methodMap, fieldMap); + } + + /** + * Receive notification of information about a class from ASM. + * + * @param version the class file version number. + * @param access the access flags for the class. + * @param name the internal name of the class. + * @param signature the signature of the class. + * @param supername the internal name of the super class. + * @param interfaces the internal names of interfaces implemented. + */ + public void visit(int version, int access, String name, String signature, + String supername, String[] interfaces) { + this.version = version; + this.access = access; + this.name = name; + this.signature = signature; + this.supername = supername; + this.interfaces = interfaces; + } + + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + methodMap.put(name + desc, new MethodInfo(access, name, desc, + signature, exceptions)); + return null; + } + + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + fieldMap.put(name, + new FieldInfo(access, name, desc, signature, value)); + return this; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/DOMDiffHandler.java b/api/src/main/java/org/osjava/jardiff/DOMDiffHandler.java new file mode 100644 index 0000000..1c5b016 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/DOMDiffHandler.java @@ -0,0 +1,722 @@ +/* + * org.osjava.jardiff.DOMDiffHandler + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/jardiff/src/ava/org/osjava/jardiff/DOMDiffHandler.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +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 <a href="mailto:[email protected]">Antony Riley</a> + */ +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; + } + + /** + * 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; + } + + /** + * 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; + } + + /** + * 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; + } + } +} diff --git a/api/src/main/java/org/osjava/jardiff/DiffCriteria.java b/api/src/main/java/org/osjava/jardiff/DiffCriteria.java new file mode 100644 index 0000000..b235405 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/DiffCriteria.java @@ -0,0 +1,92 @@ +/* + * org.osjava.jardiff.DiffCriteria + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * An interface for choosing which API differences are interesting. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public interface DiffCriteria +{ + /** + * Check if the class described by classinfo is interesting. + * + * @return true if classinfo is interesting, false otherwise. + */ + public boolean validClass(ClassInfo classinfo); + + /** + * Check if the method described by methodinfo is interesting. + * + * @return true if methodinfo is interesting, false otherwise. + */ + public boolean validMethod(MethodInfo methodinfo); + + /** + * Check if the method described by fieldinfo is interesting. + * + * @return true if fieldinfo is interesting, false otherwise. + */ + public boolean validField(FieldInfo fieldinfo); + + /** + * Check if the differences between the class described by infoA and + * the class described by infoB are interesting. + * + * @return true if the changes are interesting, false otherwise. + */ + public boolean differs(ClassInfo infoA, ClassInfo infoB); + + /** + * Check if the differences between the method described by infoA and + * the method described by infoB are interesting. + * + * @return true if the changes are interesting, false otherwise. + */ + public boolean differs(MethodInfo methodinfo, MethodInfo methodinfo_1_); + + /** + * Check if the differences between the field described by infoA and the + * field described by infoB are interesting. + * + * @return true if the changes are interesting, false otherwise. + */ + public boolean differs(FieldInfo fieldinfo, FieldInfo fieldinfo_2_); +} diff --git a/api/src/main/java/org/osjava/jardiff/DiffException.java b/api/src/main/java/org/osjava/jardiff/DiffException.java new file mode 100644 index 0000000..8ddf529 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/DiffException.java @@ -0,0 +1,57 @@ +/* + * org.osjava.jardiff.DiffException + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * A wrapper exception classes for various exceptions that can happen + * whilst performing a diff. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public class DiffException extends Exception +{ + /** + * Create a new DiffException wrapping another exception. + * + * @param toWrap the wrapped exception + */ + public DiffException(Exception toWrap) { + super((Throwable) toWrap); + } +} diff --git a/api/src/main/java/org/osjava/jardiff/DiffHandler.java b/api/src/main/java/org/osjava/jardiff/DiffHandler.java new file mode 100644 index 0000000..92eadcd --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/DiffHandler.java @@ -0,0 +1,263 @@ +/* + * org.osjava.jardiff.DiffHandler + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * An interface for classes which wish to receive information about + * differences in class files between two different jar file version to + * implement. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public interface DiffHandler +{ + /** + * Start a diff between two versions, where string a is the old version + * and string b is the new version. + * + * @param a the name of the old version + * @param b the name of the new version + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startDiff(String a, String b) + throws DiffException; + + /** + * 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; + + /** + * Start the list of new contents. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startNewContents() throws DiffException; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * Start the list of removed classes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startRemoved() throws DiffException; + + /** + * Notification that a class was removed. + * + * @param classinfo information about the class that has been removed. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void classRemoved(ClassInfo classinfo) throws DiffException; + + /** + * End of list of removed classes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void endRemoved() throws DiffException; + + /** + * Start of list of added classes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startAdded() throws DiffException; + + /** + * Notification that a class was added. + * + * @param classinfo information about the class that has been removed. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void classAdded(ClassInfo classinfo) throws DiffException; + + /** + * End of list of removed classes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void endAdded() throws DiffException; + + /** + * Start list of changed classes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startChanged() throws DiffException; + + /** + * Start information about class changes for the classname passed. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void startClassChanged(String string) throws DiffException; + + /** + * The field was removed for the current class that has changed. + * + * @param fieldinfo Information about the field removed. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void fieldRemoved(FieldInfo fieldinfo) throws DiffException; + + /** + * The method was removed for the current class that has changed. + * + * @param methodinfo Information about the method removed. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void methodRemoved(MethodInfo methodinfo) throws DiffException; + + /** + * The field was added for the current class that has changed. + * + * @param fieldinfo Information about the field added. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void fieldAdded(FieldInfo fieldinfo) throws DiffException; + + /** + * The method was added for the current class that has changed. + * + * @param methodinfo Information about the method added. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void methodAdded(MethodInfo methodinfo) throws DiffException; + + /** + * The current class has changed. + * This is called when a class's interfaces or superclass or access + * flags have changed. + * + * @param oldClassinfo Information about the old class. + * @param newClassinfo Information 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 oldClassinfo, ClassInfo newClassinfo) + throws DiffException; + + /** + * A field on the current class has changed. + * + * @param oldFieldinfo Information about the old field. + * @param newFieldinfo Information 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 oldFieldinfo, FieldInfo newFieldinfo) + throws DiffException; + + /** + * A method on the current class has changed. + * + * @param oldMethodInfo Information about the old method. + * @param newMethodInfo Information 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 oldMethodInfo, MethodInfo newMethodInfo) throws DiffException; + + /** + * End of changes for the current class. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void endClassChanged() throws DiffException; + + /** + * End of class changes. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void endChanged() throws DiffException; + + /** + * End of the diff. + * + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void endDiff() throws DiffException; +} diff --git a/api/src/main/java/org/osjava/jardiff/FieldInfo.java b/api/src/main/java/org/osjava/jardiff/FieldInfo.java new file mode 100644 index 0000000..d319aea --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/FieldInfo.java @@ -0,0 +1,106 @@ +/* + * org.osjava.jardiff.FieldInfo + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * Information about a field of a class. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public final class FieldInfo extends AbstractInfo +{ + /** + * The field descriptor for this field. + */ + private String desc; + + /** + * The signature for this field. + */ + private String signature; + + /** + * The initial value of this field. + */ + private Object value; + + /** + * Create a new FieldInfo + * + * @param access The access flags. + * @param name The name of the field. + * @param desc The field discriptor. + * @param signature The signature of this field. + * @param value The initial value of the field. + */ + public FieldInfo(int access, String name, String desc, String signature, + Object value) { + super(access, name); + this.desc = desc; + this.signature = signature; + this.value = value; + } + + /** + * Get the descriptor for this FieldInfo. + * + * @return The field descriptor. + */ + public final String getDesc() { + return desc; + } + + /** + * Get the signature for this fieldinfo. + * + * @return The signature. + */ + public final String getSignature() { + return signature; + } + + /** + * Get the initial value for this fieldinfo + * + * @return The initial value. + */ + public final Object getValue() { + return value; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/JarDiff.java b/api/src/main/java/org/osjava/jardiff/JarDiff.java new file mode 100644 index 0000000..21eb333 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/JarDiff.java @@ -0,0 +1,489 @@ +/* + * org.osjava.jardiff.JarDiff + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.TreeMap; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +/* +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +*/ + +import org.objectweb.asm.ClassReader; + +/** + * A class to perform a diff between two jar files. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public class JarDiff +{ + /** + * A map containing information about classes which are dependencies. + * Keys are internal class names. + * Values are instances of ClassInfo. + */ + protected Map depClassInfo = new HashMap(); + + /** + * A map containing information about classes in the old jar file. + * Keys are internal class names. + * Values are instances of ClassInfo. + */ + protected Map oldClassInfo = new TreeMap(); + + /** + * A map containing information about classes in the new jar file. + * Keys are internal class names. + * Values are instances of ClassInfo. + */ + protected Map newClassInfo = new TreeMap(); + + /** + * An array of dependencies which are jar files, or urls. + */ + private URL[] deps; + + /** + * A class loader used for loading dependency classes. + */ + private URLClassLoader depLoader; + + /** + * The name of the old version. + */ + private String oldVersion; + + /** + * The name of the new version. + */ + private String newVersion; + + /** + * Class info visitor, used to load information about classes. + */ + private ClassInfoVisitor infoVisitor = new ClassInfoVisitor(); + + /** + * Create a new JarDiff object. + */ + public JarDiff() { + } + + /** + * Set the name of the old version. + * + * @param oldVersion the name + */ + public void setOldVersion(String oldVersion) { + this.oldVersion = oldVersion; + } + + /** + * Get the name of the old version. + * + * @return the name + */ + public String getOldVersion() { + return oldVersion; + } + + /** + * Set the name of the new version. + * + * @param newVersion + */ + public void setNewVersion(String newVersion) { + this.newVersion = newVersion; + } + + /** + * Get the name of the new version. + * + * @return the name + */ + public String getNewVersion() { + return newVersion; + } + + /** + * Set the dependencies. + * + * @param deps an array of urls pointing to jar files or directories + * containing classes which are required dependencies. + */ + public void setDependencies(URL[] deps) { + this.deps = deps; + } + + /** + * Get the dependencies. + * + * @return the dependencies as an array of URLs + */ + public URL[] getDependencies() { + return deps; + } + + /** + * Load classinfo given a ClassReader. + * + * @param reader the ClassReader + * @return the ClassInfo + */ + private synchronized ClassInfo loadClassInfo(ClassReader reader) + throws IOException + { + infoVisitor.reset(); + reader.accept(infoVisitor, false); + return infoVisitor.getClassInfo(); + } + + /** + * Load all the classes from the specified URL and store information + * about them in the specified map. + * This currently only works for jar files, <b>not</b> directories + * which contain classes in subdirectories or in the current directory. + * + * @param infoMap the map to store the ClassInfo in. + * @throws DiffException if there is an exception reading info about a + * class. + */ + private void loadClasses(Map infoMap, URL path) throws DiffException { + try { + File jarFile = null; + if(!"file".equals(path.getProtocol()) || path.getHost() != null) { + // If it's not a local file, store it as a temporary jar file. + // java.util.jar.JarFile requires a local file handle. + jarFile = File.createTempFile("jardiff","jar"); + // Mark it to be deleted on exit. + jarFile.deleteOnExit(); + InputStream in = path.openStream(); + OutputStream out = new FileOutputStream(jarFile); + byte[] buffer = new byte[4096]; + int i; + while( (i = in.read(buffer,0,buffer.length)) != -1) { + out.write(buffer, 0, i); + } + in.close(); + out.close(); + } else { + // Else it's a local file, nothing special to do. + jarFile = new File(path.getPath()); + } + loadClasses(infoMap, jarFile); + } catch (IOException ioe) { + throw new DiffException(ioe); + } + } + + /** + * Load all the classes from the specified URL and store information + * about them in the specified map. + * This currently only works for jar files, <b>not</b> directories + * which contain classes in subdirectories or in the current directory. + * + * @param infoMap the map to store the ClassInfo in. + * @param file the jarfile to load classes from. + * @throws IOException if there is an IOException reading info about a + * class. + */ + private void loadClasses(Map infoMap, File file) throws DiffException { + try { + JarFile jar = new JarFile(file); + Enumeration e = jar.entries(); + while (e.hasMoreElements()) { + JarEntry entry = (JarEntry) e.nextElement(); + String name = entry.getName(); + if (!entry.isDirectory() && name.endsWith(".class")) { + ClassReader reader + = new ClassReader(jar.getInputStream(entry)); + ClassInfo ci = loadClassInfo(reader); + infoMap.put(ci.getName(), ci); + } + } + } catch (IOException ioe) { + throw new DiffException(ioe); + } + } + + /** + * Load old classes from the specified URL. + * + * @param loc The location of a jar file to load classes from. + * @throws DiffException if there is an IOException. + */ + public void loadOldClasses(URL loc) throws DiffException { + loadClasses(oldClassInfo, loc); + } + + /** + * Load new classes from the specified URL. + * + * @param loc The location of a jar file to load classes from. + * @throws DiffException if there is an IOException. + */ + public void loadNewClasses(URL loc) throws DiffException { + loadClasses(newClassInfo, loc); + } + + /** + * Load old classes from the specified File. + * + * @param file The location of a jar file to load classes from. + * @throws DiffException if there is an IOException + */ + public void loadOldClasses(File file) throws DiffException { + loadClasses(oldClassInfo, file); + } + + /** + * Load new classes from the specified File. + * + * @param file The location of a jar file to load classes from. + * @throws DiffExeption if there is an IOException + */ + public void loadNewClasses(File file) throws DiffException { + loadClasses(newClassInfo, file); + } + + /** + * Perform a diff sending the output to the specified handler, using + * the specified criteria to select diffs. + * + * @param handler The handler to receive and handle differences. + * @param criteria The criteria we use to select differences. + * @throws DiffException when there is an underlying exception, e.g. + * writing to a file caused an IOException + */ + public void diff(DiffHandler handler, DiffCriteria criteria) + throws DiffException + { + // TODO: Build the name from the MANIFEST rather than the filename + handler.startDiff(oldVersion, newVersion); + Iterator i; + + handler.startOldContents(); + i = oldClassInfo.entrySet().iterator(); + while(i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + ClassInfo ci = (ClassInfo) entry.getValue(); + if(criteria.validClass(ci)) { + handler.contains(ci); + } + } + handler.endOldContents(); + + handler.startNewContents(); + i = newClassInfo.entrySet().iterator(); + while(i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + ClassInfo ci = (ClassInfo) entry.getValue(); + if(criteria.validClass(ci)) { + handler.contains(ci); + } + } + handler.endNewContents(); + + java.util.Set onlyOld = new TreeSet(oldClassInfo.keySet()); + java.util.Set onlyNew = new TreeSet(newClassInfo.keySet()); + java.util.Set both = new TreeSet(oldClassInfo.keySet()); + onlyOld.removeAll(newClassInfo.keySet()); + onlyNew.removeAll(oldClassInfo.keySet()); + both.retainAll(newClassInfo.keySet()); + handler.startRemoved(); + i = onlyOld.iterator(); + while (i.hasNext()) { + String s = (String) i.next(); + ClassInfo ci = (ClassInfo) oldClassInfo.get(s); + if (criteria.validClass(ci)) + handler.classRemoved(ci); + } + handler.endRemoved(); + handler.startAdded(); + i = onlyNew.iterator(); + while (i.hasNext()) { + String s = (String) i.next(); + ClassInfo ci = (ClassInfo) newClassInfo.get(s); + if (criteria.validClass(ci)) + handler.classAdded(ci); + } + handler.endAdded(); + java.util.Set removedMethods = new TreeSet(); + java.util.Set removedFields = new TreeSet(); + java.util.Set addedMethods = new TreeSet(); + java.util.Set addedFields = new TreeSet(); + java.util.Set changedMethods = new TreeSet(); + java.util.Set changedFields = new TreeSet(); + handler.startChanged(); + i = both.iterator(); + while (i.hasNext()) { + String s = (String) i.next(); + ClassInfo oci = (ClassInfo) oldClassInfo.get(s); + ClassInfo nci = (ClassInfo) newClassInfo.get(s); + if (criteria.validClass(oci) || criteria.validClass(nci)) { + Map oldMethods = oci.getMethodMap(); + Map oldFields = oci.getFieldMap(); + Map newMethods = nci.getMethodMap(); + Map newFields = nci.getFieldMap(); + Iterator j = oldMethods.entrySet().iterator(); + while (j.hasNext()) { + Map.Entry entry = (Map.Entry) j.next(); + if (criteria.validMethod((MethodInfo) entry.getValue())) + removedMethods.add(entry.getKey()); + } + j = oldFields.entrySet().iterator(); + while (j.hasNext()) { + Map.Entry entry = (Map.Entry) j.next(); + if (criteria.validField((FieldInfo) entry.getValue())) + removedFields.add(entry.getKey()); + } + j = newMethods.entrySet().iterator(); + while (j.hasNext()) { + Map.Entry entry = (Map.Entry) j.next(); + if (criteria.validMethod((MethodInfo) entry.getValue())) + addedMethods.add(entry.getKey()); + } + j = newFields.entrySet().iterator(); + while (j.hasNext()) { + Map.Entry entry = (Map.Entry) j.next(); + if (criteria.validField((FieldInfo) entry.getValue())) + addedFields.add(entry.getKey()); + } + changedMethods.addAll(removedMethods); + changedMethods.retainAll(addedMethods); + removedMethods.removeAll(changedMethods); + addedMethods.removeAll(changedMethods); + changedFields.addAll(removedFields); + changedFields.retainAll(addedFields); + removedFields.removeAll(changedFields); + addedFields.removeAll(changedFields); + j = changedMethods.iterator(); + while (j.hasNext()) { + String desc = (String) j.next(); + MethodInfo oldInfo = (MethodInfo) oldMethods.get(desc); + MethodInfo newInfo = (MethodInfo) newMethods.get(desc); + if (!criteria.differs(oldInfo, newInfo)) + j.remove(); + } + j = changedFields.iterator(); + while (j.hasNext()) { + String desc = (String) j.next(); + FieldInfo oldInfo = (FieldInfo) oldFields.get(desc); + FieldInfo newInfo = (FieldInfo) newFields.get(desc); + if (!criteria.differs(oldInfo, newInfo)) + j.remove(); + } + boolean classchanged = criteria.differs(oci, nci); + if (classchanged || !removedMethods.isEmpty() + || !removedFields.isEmpty() || !addedMethods.isEmpty() + || !addedFields.isEmpty() || !changedMethods.isEmpty() + || !changedFields.isEmpty()) { + handler.startClassChanged(s); + handler.startRemoved(); + j = removedFields.iterator(); + while (j.hasNext()) + handler + .fieldRemoved((FieldInfo) oldFields.get(j.next())); + j = removedMethods.iterator(); + while (j.hasNext()) + handler.methodRemoved((MethodInfo) + oldMethods.get(j.next())); + handler.endRemoved(); + handler.startAdded(); + j = addedFields.iterator(); + while (j.hasNext()) + handler + .fieldAdded((FieldInfo) newFields.get(j.next())); + j = addedMethods.iterator(); + while (j.hasNext()) + handler.methodAdded((MethodInfo) + newMethods.get(j.next())); + handler.endAdded(); + handler.startChanged(); + if (classchanged) + handler.classChanged(oci, nci); + j = changedFields.iterator(); + while (j.hasNext()) { + Object tmp = j.next(); + handler.fieldChanged((FieldInfo) oldFields.get(tmp), + (FieldInfo) newFields.get(tmp)); + } + j = changedMethods.iterator(); + while (j.hasNext()) { + Object tmp = j.next(); + handler.methodChanged((MethodInfo) oldMethods.get(tmp), + ((MethodInfo) + newMethods.get(tmp))); + } + handler.endChanged(); + handler.endClassChanged(); + removedMethods.clear(); + removedFields.clear(); + addedMethods.clear(); + addedFields.clear(); + changedMethods.clear(); + changedFields.clear(); + } + } + } + handler.endChanged(); + handler.endDiff(); + } +} diff --git a/api/src/main/java/org/osjava/jardiff/MethodInfo.java b/api/src/main/java/org/osjava/jardiff/MethodInfo.java new file mode 100644 index 0000000..b83edaa --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/MethodInfo.java @@ -0,0 +1,105 @@ +/* + * org.osjava.jardiff.MethodInfo + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * A class to hold information about a method. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public final class MethodInfo extends AbstractInfo +{ + /** + * The method descriptor. + */ + private String desc; + + /** + * The signature of the method. + */ + private String signature; + + /** + * An array of the exceptions thrown by this method. + */ + private String[] exceptions; + + /** + * Create a new MethodInfo with the specified parameters. + * + * @param access The access flags for the method. + * @param name The name of the method. + * @param signature The signature of the method. + * @param exceptions The exceptions thrown by the method. + */ + public MethodInfo(int access, String name, String desc, String signature, + String[] exceptions) { + super(access, name); + this.desc = desc; + this.signature = signature; + this.exceptions = exceptions; + } + + /** + * Get the descriptor for the method. + * + * @return the descriptor + */ + public final String getDesc() { + return desc; + } + + /** + * Get the signature for the method. + * + * @return the signature + */ + public final String getSignature() { + return signature; + } + + /** + * Get the array of exceptions which can be thrown by the method. + * + * @return the exceptions as a String[] of internal names. + */ + public final String[] getExceptions() { + return exceptions; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/SimpleDiffCriteria.java b/api/src/main/java/org/osjava/jardiff/SimpleDiffCriteria.java new file mode 100644 index 0000000..244b59c --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/SimpleDiffCriteria.java @@ -0,0 +1,159 @@ +/* + * org.osjava.jardiff.SimpleDiffCriteria + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; +import java.util.Arrays; +import java.util.HashSet; + +/** + * A specific type of DiffCriteria which is only true for classes, methods + * and fields which are not synthetic, and are public or protected. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public class SimpleDiffCriteria implements DiffCriteria +{ + /** + * Check if a class is valid. + * If the class is not synthetic and is public or protected, return true. + * + * @param info Info describing the class. + * @return True if the class meets the criteria, false otherwise. + */ + public boolean validClass(ClassInfo info) { + return !info.isSynthetic() && (info.isPublic() || info.isProtected()); + } + + /** + * Check if a method is valid. + * If the method is not synthetic and is public or protected, return true. + * + * @param info Info describing the method. + * @return True if the method meets the criteria, false otherwise. + */ + public boolean validMethod(MethodInfo info) { + return !info.isSynthetic() && (info.isPublic() || info.isProtected()); + } + + /** + * Check if a field is valid. + * If the method is not synthetic and is public or protected, return true. + * + * @param info Info describing the field. + * @return True if the field meets the criteria, false otherwise. + */ + public boolean validField(FieldInfo info) { + return !info.isSynthetic() && (info.isPublic() || info.isProtected()); + } + + /** + * Check if there is a change between two versions of a class. + * Returns true if the access flags differ, or if the superclass differs + * or if the implemented interfaces differ. + * + * @param oldInfo Info about the old version of the class. + * @param newInfo Info about the new version of the class. + * @return True if the classes differ, false otherwise. + */ + public boolean differs(ClassInfo oldInfo, ClassInfo newInfo) { + if (oldInfo.getAccess() != newInfo.getAccess()) + return true; + // Yes classes can have a null supername, e.g. java.lang.Object ! + if(oldInfo.getSupername() == null) { + if(newInfo.getSupername() != null) { + return true; + } + } else if (!oldInfo.getSupername().equals(newInfo.getSupername())) { + return true; + } + java.util.Set oldInterfaces + = new HashSet(Arrays.asList(oldInfo.getInterfaces())); + java.util.Set newInterfaces + = new HashSet(Arrays.asList(newInfo.getInterfaces())); + if (!oldInterfaces.equals(newInterfaces)) + return true; + return false; + } + + /** + * Check if there is a change between two versions of a method. + * Returns true if the access flags differ, or if the thrown + * exceptions differ. + * + * @param oldInfo Info about the old version of the method. + * @param newInfo Info about the new version of the method. + * @return True if the methods differ, false otherwise. + */ + public boolean differs(MethodInfo oldInfo, MethodInfo newInfo) { + if (oldInfo.getAccess() != newInfo.getAccess()) + return true; + if (oldInfo.getExceptions() == null + || newInfo.getExceptions() == null) { + if (oldInfo.getExceptions() != newInfo.getExceptions()) + return true; + } else { + java.util.Set oldExceptions + = new HashSet(Arrays.asList(oldInfo.getExceptions())); + java.util.Set newExceptions + = new HashSet(Arrays.asList(newInfo.getExceptions())); + if (!oldExceptions.equals(newExceptions)) + return true; + } + return false; + } + + /** + * Check if there is a change between two versions of a field. + * Returns true if the access flags differ, or if the inital value + * of the field differs. + * + * @param oldInfo Info about the old version of the field. + * @param newInfo Info about the new version of the field. + * @return True if the fields differ, false otherwise. + */ + public boolean differs(FieldInfo oldInfo, FieldInfo newInfo) { + if (oldInfo.getAccess() != newInfo.getAccess()) + return true; + if (oldInfo.getValue() == null || newInfo.getValue() == null) { + if (oldInfo.getValue() != newInfo.getValue()) + return true; + } else if (!oldInfo.getValue().equals(newInfo.getValue())) + return true; + return false; + } +} diff --git a/api/src/main/java/org/osjava/jardiff/StreamDiffHandler.java b/api/src/main/java/org/osjava/jardiff/StreamDiffHandler.java new file mode 100644 index 0000000..35847c8 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/StreamDiffHandler.java @@ -0,0 +1,784 @@ +/* + * org.osjava.jardiff.StreamDiffHandler + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/jardiff/src/ava/org/osjava/jardiff/DOMDiffHandler.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +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 <a href="mailto:[email protected]">Antony Riley</a> + */ +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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + out.write("<diff xmlns=\""); + out.write(xmlEscape(XML_URI)); + out.write("\" old=\""); + out.write(xmlEscape(oldJar)); + out.write("\" new=\""); + out.write(xmlEscape(newJar)); + 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("<oldcontents>"); + } 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("<newcontents>"); + } 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("<class name=\""); + out.write(xmlEscape(info.getName())); + 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("</oldcontents>"); + } 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("</newcontents>"); + } 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("<removed>"); + } 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("</removed>"); + } 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("<added>"); + } 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("</added>"); + } 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("<changed>"); + } 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("<classchanged name=\""); + out.write(xmlEscape(internalName)); + 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("<classchange><from>"); + writeClassInfo(oldInfo); + out.write("</from><to>"); + writeClassInfo(newInfo); + out.write("</to></classchange>"); + } catch (IOException ioe) { + throw new DiffException(ioe); + } + } + + /** + * 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("<fieldchange><from>"); + writeFieldInfo(oldInfo); + out.write("</from><to>"); + writeFieldInfo(newInfo); + out.write("</to></fieldchange>"); + } catch (IOException ioe) { + throw new DiffException(ioe); + } + } + + /** + * 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("<methodchange><from>"); + writeMethodInfo(oldInfo); + out.write("</from><to>"); + writeMethodInfo(newInfo); + out.write("</to></methodchange>"); + } catch (IOException ioe) { + throw new DiffException(ioe); + } + } + + /** + * 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("</classchanged>"); + } 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("</changed>"); + } 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("</diff>"); + 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("<class"); + addAccessFlags(info); + if(info.getName() != null) { + out.write(" name=\""); + out.write(xmlEscape(info.getName())); + out.write("\""); + } + if(info.getSignature() != null) { + out.write(" signature=\""); + out.write(xmlEscape(info.getSignature())); + out.write("\""); + } + if(info.getSupername() != null) { + out.write(" superclass=\""); + out.write(xmlEscape(info.getSupername())); + out.write("\">"); + } + String[] interfaces = info.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + out.write("<implements name=\""); + out.write(xmlEscape(interfaces[i])); + out.write("\"/>"); + } + out.write("</class>"); + } + + /** + * 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("<method"); + + addAccessFlags(info); + + if (info.getName() != null) { + out.write(" name=\""); + out.write(xmlEscape(info.getName())); + out.write("\""); + } + if (info.getSignature() != null) { + out.write(" signature=\""); + out.write(xmlEscape(info.getSignature())); + out.write("\""); + } + 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("<exception name=\""); + out.write(xmlEscape(exceptions[i])); + out.write("\"/>"); + } + } + out.write("</method>"); + } + + /** + * 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("<field"); + + addAccessFlags(info); + + if(info.getName() != null) { + out.write(" name=\""); + out.write(xmlEscape(info.getName())); + out.write("\""); + } + if (info.getSignature() != null) { + out.write(" signature=\""); + out.write(xmlEscape(info.getSignature())); + out.write("\""); + } + if (info.getValue() != null) { + out.write(" value=\""); + out.write(xmlEscape(info.getValue().toString())); + out.write("\""); + } + out.write(">"); + if (info.getDesc() != null) { + addTypeNode(info.getDesc()); + } + out.write("</field>"); + } + + /** + * 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("<arguments>"); + for (int i = 0; i < args.length; i++) + addTypeNode(args[i]); + out.write("</arguments>"); + out.write("<return>"); + addTypeNode(ret); + out.write("</return>"); + } + + /** + * 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("<type"); + int i = type.getSort(); + if (i == Type.ARRAY) { + out.write(" array=\"yes\" dimensions=\""); + out.write(""+type.getDimensions()); + out.write("\""); + type = type.getElementType(); + i = type.getSort(); + } + switch (i) { + case Type.BOOLEAN: + out.write(" primitive=\"yes\" name=\"boolean\"/>"); + 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<str.length();i++) { + char ch = str.charAt(i); + switch(ch) { + case '<': + ret.append("<"); + break; + case '&': + ret.append("&"); + break; + case '>': + ret.append(">"); + break; + default: + ret.append(ch); + } + } + return ret.toString(); + } +} diff --git a/api/src/main/java/org/osjava/jardiff/Tools.java b/api/src/main/java/org/osjava/jardiff/Tools.java new file mode 100644 index 0000000..23b6445 --- /dev/null +++ b/api/src/main/java/org/osjava/jardiff/Tools.java @@ -0,0 +1,80 @@ +/* + * org.osjava.jardiff.Tools + * + * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $ + * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $ + * $Rev: 1952 $ + * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $ + * $Author: cybertiger $ + * + * Copyright (c) 2005, Antony Riley + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * + Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * + Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * + Neither the name JarDiff nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.osjava.jardiff; + +/** + * A set of Tools which do not belong anywhere else in the API at this time. + * This is nasty, but for now, useful. + * + * @author <a href="mailto:[email protected]">Antony Riley</a> + */ +public final class Tools +{ + /** + * Private constructor so this class can't be instantiated. + */ + private Tools() { + /* empty */ + } + + /** + * Get the java class name given an internal class name. + * This method currently replaces all instances of $ and / with . this + * may not be according to the java language spec, and will almost + * certainly fail for some inner classes. + * + * @param internalName The internal name of the class. + * @return The java class name. + */ + public static final String getClassName(String internalName) { + StringBuffer ret = new StringBuffer(internalName.length()); + for (int i = 0; i < internalName.length(); i++) { + char ch = internalName.charAt(i); + switch (ch) { + case '$': + case '/': + ret.append('.'); + break; + default: + ret.append(ch); + } + } + return ret.toString(); + } +} |