blob: b5daec8f193e72be33db0b995ef977f8eceff8b6 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.lib.jardiff;
2
3import java.io.*;
4import java.util.*;
5import java.util.jar.*;
6
7import aQute.lib.osgi.*;
8
9public class Diff {
10
11 /**
12 * Compare two JAR files with each other.
13 *
14 * @param a
15 * @param b
16 * @param strict
17 * @return
18 * @throws IOException
19 */
20 public Map<String, Object> diff(Jar a, Jar b, boolean strict)
21 throws Exception {
22 Map<String, Object> different = new TreeMap<String, Object>();
23 compareManifest(different, a.getManifest(), b.getManifest(), strict);
24 diff(different, a.getResources().keySet(), b.getResources().keySet());
25
26 Set<String> shared = new HashSet<String>(a.getResources().keySet());
27 shared.retainAll(b.getResources().keySet());
28
29 for (String path : a.getResources().keySet()) {
30 Resource ra = a.getResource(path);
31 Resource rb = a.getResource(path);
32 if (rb != null) {
33 if (ra.getClass() != rb.getClass()) {
34 different.put(path, "Different types: "
35 + a.getClass().getName() + " : "
36 + b.getClass().getName());
37 } else {
38 if (path.endsWith(".jar")) {
39 Jar aa = new Jar(path, ra.openInputStream());
40 Jar bb = new Jar(path, rb.openInputStream());
41 try {
42 Map<String, Object> result = diff(aa, bb, strict);
43 if (!result.isEmpty())
44 different.put(path, result);
45 } finally {
46 aa.close();
47 bb.close();
48 }
49 } else {
50 String cmp = diff(ra.openInputStream(), rb
51 .openInputStream());
52 if (cmp != null)
53 different.put(path, cmp);
54 }
55 }
56 }
57 }
58 return different;
59 }
60
61 String diff(InputStream a, InputStream b) throws IOException {
62 int n = 0;
63 int binary = 0;
64 StringBuffer sb = new StringBuffer();
65 while (true) {
66 int ac = a.read();
67 int bc = b.read();
68 if (ac < 0) {
69 if (bc < 0)
70 return null;
71
72 return "a is smaller";
73 } else if (bc < 0) {
74 return "b is smaller";
75 }
76
77 if (ac != bc) {
78 String s = "Difference at pos: " + n;
79 if (binary == 0 && sb.length() > 5) {
80 s = s + "Context: " + sb.substring(sb.length() - 5);
81 }
82 }
83
84 if (ac >= ' ' && ac <= '~')
85 sb.append((char) ac);
86 else
87 binary++;
88
89 n++;
90 }
91 }
92
93 void diff(Map<String, Object> different, Set<?> a, Set<?> b) {
94 Set<Object> onlyInA = new HashSet<Object>(a);
95 onlyInA.removeAll(b);
96 Set<Object> onlyInB = new HashSet<Object>(b);
97 onlyInB.removeAll(a);
98
99 for (Object element : onlyInA) {
100 different.put(element.toString(), "a");
101 }
102 for (Object element : onlyInB) {
103 different.put(element.toString(), "b");
104 }
105 }
106
107 public void compareManifest(Map<String, Object> different, Manifest a,
108 Manifest b, boolean strict) {
109 if (a == null || b == null) {
110 different.put("Manifest null", (a == null ? "a=null" : "a exists")
111 + " " + (b == null ? "b=null" : "b exists"));
112 return;
113 }
114
115 Attributes attrs = a.getMainAttributes();
116 Attributes bttrs = b.getMainAttributes();
117 diff(different, attrs.keySet(), bttrs.keySet());
118 for (Object element : attrs.keySet()) {
119 Attributes.Name name = (Attributes.Name) element;
120 String av = attrs.getValue(name);
121 String bv = bttrs.getValue(name);
122 if (bv != null) {
123 if (!av.equals(bv))
124 different.put(name.toString(), "M:" + name + ":" + av
125 + "!=" + bv);
126 }
127 }
128 }
129
130 @SuppressWarnings("unchecked")
131 public void print(PrintStream pout, Map<String, Object> map, int indent) {
132 for (Map.Entry<String, Object> entry : map.entrySet()) {
133 for (int j = 0; j < indent; j++) {
134 pout.print(" ");
135 }
136 String key = entry.getKey();
137 pout.print(key);
138 for (int j = 0; j < 70 - indent - key.length(); j++) {
139 pout.print(" ");
140 }
141 if (entry.getValue() instanceof Map) {
142 pout.println();
143 print(pout, (Map<String, Object>) entry.getValue(), indent + 1);
144 } else
145 pout.println(entry.getValue());
146 }
147 }
148
149 public void close() {
150
151 }
152}