Use local copy of latest bndlib code for pre-release testing purposes
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/DiffPluginImpl.java b/bundleplugin/src/main/java/aQute/bnd/differ/DiffPluginImpl.java
new file mode 100644
index 0000000..e3a1308
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/DiffPluginImpl.java
@@ -0,0 +1,181 @@
+package aQute.bnd.differ;
+
+import static aQute.bnd.service.diff.Delta.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.bnd.service.diff.*;
+import aQute.bnd.service.diff.Tree.Data;
+import aQute.lib.hex.*;
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+import aQute.libg.cryptography.*;
+import aQute.libg.header.*;
+
+
+/**
+ * This Diff Plugin Implementation will compare JARs for their API (based on the
+ * Bundle Class Path and exported packages), the Manifest, and the resources.
+ * The Differences are represented in a {@link Diff} tree.
+ */
+public class DiffPluginImpl implements Differ {
+
+ /**
+ * Headers that are considered major enough to parse according to spec and
+ * compare their constituents
+ */
+ final static Set<String> MAJOR_HEADERS = new TreeSet<String>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ /**
+ * Headers that are considered not major enough to be considered
+ */
+ final static Set<String> IGNORE_HEADERS = new TreeSet<String>(
+ String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ MAJOR_HEADERS.add(Constants.EXPORT_PACKAGE);
+ MAJOR_HEADERS.add(Constants.IMPORT_PACKAGE);
+ MAJOR_HEADERS.add(Constants.REQUIRE_BUNDLE);
+ MAJOR_HEADERS.add(Constants.FRAGMENT_HOST);
+ MAJOR_HEADERS.add(Constants.BUNDLE_SYMBOLICNAME);
+ MAJOR_HEADERS.add(Constants.BUNDLE_LICENSE);
+ MAJOR_HEADERS.add(Constants.BUNDLE_NATIVECODE);
+ MAJOR_HEADERS.add(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ MAJOR_HEADERS.add(Constants.DYNAMICIMPORT_PACKAGE);
+
+ IGNORE_HEADERS.add(Constants.TOOL);
+ IGNORE_HEADERS.add(Constants.BND_LASTMODIFIED);
+ IGNORE_HEADERS.add(Constants.CREATED_BY);
+ }
+
+ /**
+ *
+ * @see aQute.bnd.service.diff.Differ#diff(aQute.lib.resource.Jar,
+ * aQute.lib.resource.Jar)
+ */
+ public Tree tree(File newer) throws Exception {
+ Jar jnewer = new Jar(newer);
+ try {
+ return tree(jnewer);
+ } finally {
+ jnewer.close();
+ }
+ }
+
+ /**
+ *
+ * @see aQute.bnd.service.diff.Differ#diff(aQute.lib.resource.Jar,
+ * aQute.lib.resource.Jar)
+ */
+ public Tree tree(Jar newer) throws Exception {
+ Analyzer anewer = new Analyzer();
+ try {
+ anewer.setJar(newer);
+ return tree(anewer);
+ } finally {
+ anewer.setJar((Jar) null);
+ anewer.close();
+ }
+ }
+
+ public Tree tree(Analyzer newer) throws Exception {
+ return bundleElement(newer);
+ }
+
+ /**
+ * Create an element representing a bundle from the Jar.
+ *
+ * @param infos
+ * @param jar
+ * The Jar to be analyzed
+ * @return the elements that should be compared
+ * @throws Exception
+ */
+ private Element bundleElement(Analyzer analyzer) throws Exception {
+ List<Element> result = new ArrayList<Element>();
+
+ Manifest manifest = analyzer.getJar().getManifest();
+
+ if (manifest != null) {
+ result.add(JavaElement.getAPI(analyzer));
+ result.add(manifestElement(manifest));
+ }
+ result.add(resourcesElement(analyzer.getJar()));
+ return new Element(Type.BUNDLE, analyzer.getJar().getName(), result, CHANGED, CHANGED,
+ null);
+ }
+
+ /**
+ * Create an element representing all resources in the JAR
+ *
+ * @param jar
+ * @return
+ * @throws Exception
+ */
+ private Element resourcesElement(Jar jar) throws Exception {
+ List<Element> resources = new ArrayList<Element>();
+ for (Map.Entry<String, Resource> entry : jar.getResources().entrySet()) {
+
+ InputStream in = entry.getValue().openInputStream();
+ try {
+ Digester<SHA1> digester = SHA1.getDigester();
+ IO.copy(in, digester);
+ String value = Hex.toHexString(digester.digest().digest());
+ resources
+ .add(new Element(Type.RESOURCE, entry.getKey()+"="+value, null, CHANGED, CHANGED, null));
+ } finally {
+ in.close();
+ }
+ }
+ return new Element(Type.RESOURCES, "<resources>", resources, CHANGED, CHANGED, null);
+ }
+
+
+
+
+ /**
+ * Create an element for each manifest header. There are
+ * {@link #IGNORE_HEADERS} and {@link #MAJOR_HEADERS} that will be treated
+ * differently.
+ *
+ * @param manifest
+ * @return
+ */
+
+ private Element manifestElement(Manifest manifest) {
+ List<Element> result = new ArrayList<Element>();
+
+ for (Object key : manifest.getMainAttributes().keySet()) {
+ String header = key.toString();
+ String value = manifest.getMainAttributes().getValue(header);
+ if (IGNORE_HEADERS.contains(header))
+ continue;
+
+ if (MAJOR_HEADERS.contains(header)) {
+ Parameters clauses = OSGiHeader.parseHeader(value);
+ Collection<Element> clausesDef = new ArrayList<Element>();
+ for (Map.Entry<String, Attrs> clause : clauses.entrySet()) {
+ Collection<Element> parameterDef = new ArrayList<Element>();
+ for (Map.Entry<String, String> parameter : clause.getValue().entrySet()) {
+ parameterDef.add(new Element(Type.PARAMETER, parameter.getKey() + ":" + parameter
+ .getValue(), null, CHANGED, CHANGED, null));
+ }
+ clausesDef.add(new Element(Type.CLAUSE, clause.getKey(), parameterDef,
+ CHANGED, CHANGED, null));
+ }
+ result.add(new Element(Type.HEADER, header, clausesDef, CHANGED, CHANGED, null));
+ } else {
+ result.add(new Element(Type.HEADER, header +":"+ value, null,CHANGED, CHANGED, null));
+ }
+ }
+ return new Element(Type.MANIFEST, "<manifest>", result, CHANGED, CHANGED, null);
+ }
+
+ public Tree deserialize(Data data) throws Exception {
+ return new Element(data);
+ }
+
+}