blob: eee3e276f484d6368fa57de36671e2c27ee1bf64 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.differ;
2
3import java.io.*;
4import java.util.*;
5import java.util.jar.*;
6
7import aQute.bnd.service.diff.*;
8import aQute.bnd.service.diff.Diff.Ignore;
9import aQute.lib.osgi.*;
10import aQute.libg.generics.*;
11import aQute.libg.header.*;
12import aQute.libg.reporter.*;
13import aQute.libg.version.*;
14
15/**
16 * This class maintains
17 *
18 */
19public class Baseline {
20
21 public static class Info {
22 public String packageName;
23 public Diff packageDiff;
24 public Collection<String> providers;
25 public Map<String, String> attributes;
26 public Version newerVersion;
27 public Version olderVersion;
28 public Version suggestedVersion;
29 public Version suggestedIfProviders;
30 public boolean mismatch;
31 public String warning="";
32
33 }
34
35 final Differ differ;
36 final Reporter bnd;
37
38 public Baseline(Reporter bnd, Differ differ) throws IOException {
39 this.differ = differ;
40 this.bnd = bnd;
41 }
42
43 /**
44 * This method compares a jar to a baseline jar and returns version
45 * suggestions if the baseline does not agree with the newer jar. The
46 * returned set contains all the exported packages.
47 *
48 * @param newer
49 * @param older
50 * @return null if ok, otherwise a set of suggested versions for all
51 * packages (also the ones that were ok).
52 * @throws Exception
53 */
54 public Set<Info> baseline(Jar newer, Jar older, Instructions packageFilters)
55 throws Exception {
56 Tree n = differ.tree(newer);
57 Parameters nExports = getExports(newer);
58 Tree o = differ.tree(older);
59 Parameters oExports = getExports(older);
60 if ( packageFilters == null)
61 packageFilters = new Instructions();
62
63 return baseline(n, nExports, o, oExports, packageFilters);
64 }
65
66 public Set<Info> baseline(Tree n, Parameters nExports, Tree o,
67 Parameters oExports, Instructions packageFilters)
68 throws Exception {
69 Diff diff = n.diff(o).get("<api>");
70 Set<Info> infos = Create.set();
71
72 for (Diff pdiff : diff.getChildren()) {
73 if (pdiff.getType() != Type.PACKAGE) // Just packages
74 continue;
75
76 if (pdiff.getName().startsWith("java."))
77 continue;
78
79 if (!packageFilters.matches(pdiff.getName()))
80 continue;
81
82 final Info info = new Info();
83 infos.add(info);
84
85 info.packageDiff = pdiff;
86 info.packageName = pdiff.getName();
87 info.attributes = nExports.get(info.packageName);
88 bnd.trace("attrs for %s %s", info.packageName,info.attributes);
89
90 info.newerVersion = getVersion(info.attributes);
91 info.olderVersion = getVersion(oExports.get(info.packageName));
92 if (pdiff.getDelta() == Delta.UNCHANGED) {
93 info.suggestedVersion = info.olderVersion;
94 if( !info.newerVersion.equals(info.olderVersion)) {
95 info.warning += "No difference but versions are equal";
96 }
97 } else if (pdiff.getDelta() == Delta.REMOVED) {
98 info.suggestedVersion = null;
99 } else if (pdiff.getDelta() == Delta.ADDED) {
100 info.suggestedVersion = Version.ONE;
101 } else {
102 // We have an API change
103 info.suggestedVersion = bump(pdiff.getDelta(), info.olderVersion, 1, 0);
104
105 if (info.newerVersion.compareTo(info.suggestedVersion) < 0) {
106 info.mismatch = true; // our suggested version is smaller
107 // than
108 // the
109 // old version!
110
111 // We can fix some major problems by assuming
112 // that an interface is a provider interface
113 if (pdiff.getDelta() == Delta.MAJOR) {
114
115 info.providers = Create.set();
116 if (info.attributes != null)
117 info.providers.addAll(Processor.split(info.attributes
118 .get(Constants.PROVIDER_TYPE_DIRECTIVE)));
119
120 // Calculate the new delta assuming we fix all the major
121 // interfaces
122 // by making them providers
123 Delta tryDelta = pdiff.getDelta(new Ignore() {
124 public boolean contains(Diff diff) {
125 if (diff.getType() == Type.INTERFACE
126 && diff.getDelta() == Delta.MAJOR) {
127 info.providers.add(Descriptors.getShortName(diff.getName()));
128 return true;
129 }
130 return false;
131 }
132 });
133
134 if (tryDelta != Delta.MAJOR) {
135 info.suggestedIfProviders = bump(tryDelta, info.olderVersion, 1, 0);
136 }
137 }
138 }
139 }
140 }
141 return infos;
142 }
143
144 private Version bump(Delta delta, Version last, int offset, int base) {
145 switch (delta) {
146 case UNCHANGED:
147 return last;
148 case MINOR:
149 return new Version(last.getMajor(), last.getMinor() + offset, base);
150 case MAJOR:
151 return new Version(last.getMajor() + 1, base, base);
152 case ADDED:
153 return last;
154 default:
155 return new Version(last.getMajor(), last.getMinor(), last.getMicro() + offset);
156 }
157 }
158
159 private Version getVersion(Map<String, String> map) {
160 if (map == null)
161 return Version.LOWEST;
162
163 return Version.parseVersion(map.get(Constants.VERSION_ATTRIBUTE));
164 }
165
166 private Parameters getExports(Jar jar) throws Exception {
167 Manifest m = jar.getManifest();
168 if (m == null)
169 return new Parameters();
170
171 return OSGiHeader.parseHeader(m.getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
172 }
173
174}