1/*
2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package sun.tools.pack.verify;
24
25import java.io.*;
26import java.util.*;
27import java.util.jar.*;
28import xmlkit.*;
29
30public class ClassCompare {
31
32    /*
33     * @author ksrini
34     */
35    private static XMLKit.Element getXMLelement(InputStream is,
36            boolean ignoreUnkAttrs,
37            List<String> ignoreElements) throws IOException {
38
39        ClassReader cr = new ClassReader();
40        cr.keepOrder = false;
41        XMLKit.Element e = cr.readFrom(is);
42
43        if (ignoreElements != null) {
44            XMLKit.Filter filter = XMLKit.elementFilter(ignoreElements);
45            e.removeAllInTree(filter);
46        }
47
48        if (ignoreUnkAttrs == true) {
49            // This removes any unknown attributes
50            e.removeAllInTree(XMLKit.elementFilter("Attribute"));
51        }
52        return e;
53    }
54
55    private static String getXMLPrettyString(XMLKit.Element e) throws IOException {
56        StringWriter out = new StringWriter();
57        e.writePrettyTo(out);
58        return out.toString();
59    }
60
61    private static boolean compareClass0(JarFile jf1, JarFile jf2,
62            JarEntry je, boolean ignoreUnkAttrs,
63            List<String> ignoreElements)
64            throws IOException {
65
66        InputStream is1 = jf1.getInputStream(je);
67        InputStream is2 = jf2.getInputStream(je);
68
69        // First we try to compare the bits if they are the same
70        boolean bCompare = JarFileCompare.compareStreams(is1, is2);
71
72        // If they are the same there is nothing more to do.
73        if (bCompare) {
74            Globals.println("+++" + je.getName() + "+++\t"
75                    + "b/b:PASS");
76            return bCompare;
77        }
78        is1.close();
79        is2.close();
80
81        is1 = jf1.getInputStream(je);
82        is2 = jf2.getInputStream(je);
83
84
85        XMLKit.Element e1 = getXMLelement(is1, ignoreUnkAttrs, ignoreElements);
86        XMLKit.Element e2 = getXMLelement(is2, ignoreUnkAttrs, ignoreElements);
87
88        Globals.print("+++" + je.getName() + "+++\t"
89                + e1.size() + "/" + e1.size() + ":");
90
91        boolean result = true;
92
93        if (e1.equals(e2)) {
94            Globals.println("PASS");
95        } else {
96            Globals.println("FAIL");
97            Globals.log("Strings differs");
98            Globals.log(getXMLPrettyString(e1));
99            Globals.log("----------");
100            Globals.log(getXMLPrettyString(e2));
101            result = false;
102        }
103        return result;
104    }
105
106    /*
107     * Given two Class Paths could be jars the first being a reference
108     * will execute a series of comparisons on the classname  specified
109     * The className could be null in which case it will iterate through
110     * all the classes, otherwise it will compare one class and exit.
111     */
112    public static boolean compareClass(String jar1, String jar2,
113            String className, boolean ignoreUnkAttrs,
114            List<String> ignoreElements)
115            throws IOException {
116
117        Globals.println("Unknown attributes ignored:" + ignoreUnkAttrs);
118        if (ignoreElements != null) {
119            Globals.println(ignoreElements.toString());
120        }
121
122        JarFile jf1 = new JarFile(jar1);
123        JarFile jf2 = new JarFile(jar2);
124
125        boolean result = true;
126
127        if (className == null) {
128            for (JarEntry je1 : Collections.list((Enumeration<JarEntry>) jf1.entries())) {
129                if (je1.getName().endsWith(".class")) {
130                    JarEntry je2 = jf2.getJarEntry(je1.getName());
131                    boolean pf = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements);
132                    if (result == true) {
133                        result = pf;
134                    }
135                }
136            }
137        } else {
138            JarEntry je1 = jf1.getJarEntry(className);
139            result = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements);
140        }
141        if (result == false) {
142            throw new RuntimeException("Class structural comparison failure");
143        }
144        return result;
145    }
146
147    public static boolean compareClass(String jar1, String jar2,
148            String className) throws IOException {
149
150        Stack<String> s = new Stack();
151        if (Globals.ignoreDebugAttributes()) {
152            s = new Stack();
153            s.push("LocalVariable");
154            s.push("LocalVariableType");
155            s.push("LineNumber");
156            s.push("SourceFile");
157        }
158        return compareClass(jar1, jar2, className, Globals.ignoreUnknownAttributes(), s);
159    }
160}
161