0
# API Comparison and Diffing
1
2
Semantic versioning analysis based on API changes between bundle versions, enabling automated compatibility assessment and version recommendation.
3
4
## Capabilities
5
6
### DiffImpl
7
8
Implementation for comparing APIs between JAR versions and detecting breaking changes.
9
10
```java { .api }
11
/**
12
* Implementation for comparing APIs between JAR versions
13
*/
14
public class DiffImpl implements Diff {
15
/** Compare two JAR files and generate diff */
16
public Diff diff(Jar newer, Jar older) throws Exception;
17
18
/** Create API tree from JAR */
19
public Tree tree(Jar jar) throws Exception;
20
21
/** Get diff instructions for filtering */
22
public Instructions getDiffInstructions();
23
24
/** Set diff instructions */
25
public void setDiffInstructions(Instructions instructions);
26
27
/** Check if change is ignored */
28
public boolean isIgnored(Element element);
29
30
/** Get diff reporter */
31
public Reporter getReporter();
32
33
/** Set diff reporter */
34
public void setReporter(Reporter reporter);
35
}
36
37
/**
38
* Represents a difference between API elements
39
*/
40
public interface Diff {
41
/** Get diff type */
42
public Delta getDelta();
43
44
/** Get element type */
45
public Element getType();
46
47
/** Get element name */
48
public String getName();
49
50
/** Get children diffs */
51
public Collection<? extends Diff> getChildren();
52
53
/** Get newer element */
54
public Element getNewer();
55
56
/** Get older element */
57
public Element getOlder();
58
}
59
60
/**
61
* Types of changes detected
62
*/
63
public enum Delta {
64
IGNORE, // Change should be ignored
65
UNCHANGED, // No change
66
MINOR, // Minor change (backward compatible)
67
MAJOR, // Major change (breaking)
68
MICRO, // Micro change (implementation only)
69
ADDED, // New element added
70
REMOVED // Element removed
71
}
72
```
73
74
**Usage Examples:**
75
76
```java
77
import aQute.bnd.differ.DiffImpl;
78
import aQute.bnd.differ.Diff;
79
import aQute.bnd.differ.Delta;
80
81
// Compare two versions of a bundle
82
Jar newer = new Jar(new File("mybundle-2.0.0.jar"));
83
Jar older = new Jar(new File("mybundle-1.0.0.jar"));
84
85
DiffImpl differ = new DiffImpl();
86
Diff diff = differ.diff(newer, older);
87
88
// Analyze changes
89
analyzeDiff(diff, 0);
90
91
// Helper method to analyze diff recursively
92
private void analyzeDiff(Diff diff, int indent) {
93
String prefix = " ".repeat(indent);
94
Delta delta = diff.getDelta();
95
96
System.out.println(prefix + delta + ": " + diff.getType() + " " + diff.getName());
97
98
// Process child diffs
99
for (Diff child : diff.getChildren()) {
100
analyzeDiff(child, indent + 1);
101
}
102
}
103
```
104
105
### Baseline
106
107
Performs semantic versioning analysis based on API changes and recommends appropriate version increments.
108
109
```java { .api }
110
/**
111
* Performs semantic versioning analysis based on API changes
112
*/
113
public class Baseline {
114
/** Create baseline analyzer */
115
public Baseline(Reporter reporter, Instructions diffignore);
116
117
/** Perform baseline analysis */
118
public Set<Info> baseline(Jar jar, Jar baseline, Instructions instructions) throws Exception;
119
120
/** Get suggested version */
121
public Version getSuggestedVersion();
122
123
/** Check if baseline passed */
124
public boolean isOk();
125
126
/** Get baseline errors */
127
public List<String> getErrors();
128
129
/** Get baseline warnings */
130
public List<String> getWarnings();
131
}
132
133
/**
134
* Baseline information for a package
135
*/
136
public class Info implements Comparable<Info> {
137
/** Get package name */
138
public String packageName;
139
140
/** Get suggested version */
141
public Version suggestedVersion;
142
143
/** Get newer version */
144
public Version newerVersion;
145
146
/** Get older version */
147
public Version olderVersion;
148
149
/** Get version change type */
150
public Delta delta;
151
152
/** Get warnings */
153
public List<String> warnings;
154
155
/** Check if version is too low */
156
public boolean mismatch;
157
158
/** Get attributes */
159
public Attrs attributes;
160
}
161
```
162
163
**Usage Examples:**
164
165
```java
166
import aQute.bnd.differ.Baseline;
167
import aQute.bnd.differ.Baseline.Info;
168
import aQute.bnd.build.Instructions;
169
170
// Perform baseline analysis
171
Jar current = new Jar(new File("current.jar"));
172
Jar previous = new Jar(new File("previous.jar"));
173
174
Reporter reporter = new ConsoleReporter();
175
Instructions diffIgnore = new Instructions();
176
diffIgnore.put("*impl*", new Attrs()); // Ignore implementation packages
177
178
Baseline baseline = new Baseline(reporter, diffIgnore);
179
Set<Info> results = baseline.baseline(current, previous, new Instructions());
180
181
// Analyze results
182
for (Info info : results) {
183
System.out.println("Package: " + info.packageName);
184
System.out.println(" Current version: " + info.newerVersion);
185
System.out.println(" Previous version: " + info.olderVersion);
186
System.out.println(" Suggested version: " + info.suggestedVersion);
187
System.out.println(" Change type: " + info.delta);
188
189
if (info.mismatch) {
190
System.out.println(" WARNING: Version too low for changes detected");
191
}
192
193
for (String warning : info.warnings) {
194
System.out.println(" WARNING: " + warning);
195
}
196
}
197
198
// Check overall result
199
if (baseline.isOk()) {
200
System.out.println("Baseline analysis passed");
201
System.out.println("Suggested bundle version: " + baseline.getSuggestedVersion());
202
} else {
203
System.err.println("Baseline analysis failed");
204
for (String error : baseline.getErrors()) {
205
System.err.println("ERROR: " + error);
206
}
207
}
208
```
209
210
### Element Hierarchy
211
212
Classes representing different elements in the API tree for diff analysis.
213
214
```java { .api }
215
/**
216
* Base class for API elements
217
*/
218
public abstract class Element {
219
/** Get element type */
220
public abstract Type getType();
221
222
/** Get element name */
223
public abstract String getName();
224
225
/** Get element key for comparison */
226
public String getKey();
227
228
/** Get element version */
229
public Version getVersion();
230
231
/** Get element attributes */
232
public Map<String, String> getAttributes();
233
234
/** Compare with another element */
235
public Delta compare(Element other);
236
}
237
238
/**
239
* Types of API elements
240
*/
241
public enum Type {
242
ROOT,
243
BUNDLE,
244
PACKAGE,
245
CLASS,
246
INTERFACE,
247
ANNOTATION,
248
ENUM,
249
METHOD,
250
CONSTRUCTOR,
251
FIELD,
252
CONSTANT
253
}
254
255
/**
256
* Java-specific element for class analysis
257
*/
258
public class JavaElement extends Element {
259
/** Get Java access modifiers */
260
public int getAccess();
261
262
/** Check if element is public */
263
public boolean isPublic();
264
265
/** Check if element is protected */
266
public boolean isProtected();
267
268
/** Check if element is static */
269
public boolean isStatic();
270
271
/** Check if element is final */
272
public boolean isFinal();
273
274
/** Check if element is abstract */
275
public boolean isAbstract();
276
277
/** Get method signature */
278
public String getSignature();
279
280
/** Get return type */
281
public String getReturnType();
282
283
/** Get parameter types */
284
public String[] getParameterTypes();
285
286
/** Get exception types */
287
public String[] getExceptionTypes();
288
}
289
```
290
291
### Diff Instructions
292
293
Configuration for controlling diff analysis and ignoring specific changes.
294
295
```java { .api }
296
/**
297
* Instructions for controlling diff analysis
298
*/
299
public class Instructions extends LinkedHashMap<Instruction, Attrs> {
300
/** Create empty instructions */
301
public Instructions();
302
303
/** Create from properties */
304
public Instructions(Properties properties);
305
306
/** Check if element matches any instruction */
307
public boolean matches(String name);
308
309
/** Get instruction that matches name */
310
public Instruction getMatching(String name);
311
312
/** Add instruction */
313
public Instruction put(String pattern, Attrs attributes);
314
315
/** Get all patterns */
316
public Set<String> getPatterns();
317
}
318
319
/**
320
* Single instruction with pattern and attributes
321
*/
322
public class Instruction {
323
/** Get instruction pattern */
324
public String getPattern();
325
326
/** Get instruction attributes */
327
public Attrs getAttributes();
328
329
/** Check if pattern matches string */
330
public boolean matches(String value);
331
332
/** Check if instruction is literal (no wildcards) */
333
public boolean isLiteral();
334
335
/** Check if instruction is negated */
336
public boolean isNegated();
337
}
338
```
339
340
### Repository Diff Utilities
341
342
Utilities for comparing repository contents and tracking changes over time.
343
344
```java { .api }
345
/**
346
* Repository difference analyzer
347
*/
348
public class RepositoryDiff {
349
/** Compare two repositories */
350
public static Diff compareRepositories(Repository newer, Repository older) throws Exception;
351
352
/** Find changed bundles between repositories */
353
public static Map<String, Diff> findChangedBundles(Repository newer, Repository older) throws Exception;
354
355
/** Generate compatibility report */
356
public static CompatibilityReport generateReport(Repository newer, Repository older) throws Exception;
357
}
358
359
/**
360
* Compatibility report between repository versions
361
*/
362
public class CompatibilityReport {
363
/** Get added bundles */
364
public Set<String> getAddedBundles();
365
366
/** Get removed bundles */
367
public Set<String> getRemovedBundles();
368
369
/** Get changed bundles */
370
public Map<String, BundleChange> getChangedBundles();
371
372
/** Get compatibility summary */
373
public CompatibilitySummary getSummary();
374
}
375
376
/**
377
* Change information for a bundle
378
*/
379
public class BundleChange {
380
public String bundleSymbolicName;
381
public Version oldVersion;
382
public Version newVersion;
383
public Delta overallChange;
384
public Map<String, Delta> packageChanges;
385
public List<String> breakingChanges;
386
public List<String> warnings;
387
}
388
```
389
390
**Complete API Comparison Example:**
391
392
```java
393
import aQute.bnd.differ.*;
394
import aQute.bnd.build.Instructions;
395
396
// Complete API comparison and baseline workflow
397
public class APIComparisonWorkflow {
398
399
public void performAPIAnalysis(File currentBundle, File previousBundle) throws Exception {
400
try (Jar current = new Jar(currentBundle);
401
Jar previous = new Jar(previousBundle)) {
402
403
// Step 1: Perform detailed diff analysis
404
DiffImpl differ = new DiffImpl();
405
Diff diff = differ.diff(current, previous);
406
407
System.out.println("=== API Diff Analysis ===");
408
printDiffTree(diff, 0);
409
410
// Step 2: Perform baseline analysis for version recommendations
411
Reporter reporter = new Reporter() {
412
public void error(String msg, Object... args) {
413
System.err.println("ERROR: " + String.format(msg, args));
414
}
415
public void warning(String msg, Object... args) {
416
System.err.println("WARNING: " + String.format(msg, args));
417
}
418
public void progress(float progress, String msg, Object... args) {
419
System.out.println("PROGRESS: " + String.format(msg, args));
420
}
421
};
422
423
// Configure what to ignore in baseline
424
Instructions diffIgnore = new Instructions();
425
diffIgnore.put("*impl*", new Attrs()); // Ignore impl packages
426
diffIgnore.put("*.internal.*", new Attrs()); // Ignore internal packages
427
428
Baseline baseline = new Baseline(reporter, diffIgnore);
429
Set<Baseline.Info> baselineResults = baseline.baseline(current, previous, new Instructions());
430
431
System.out.println("\n=== Baseline Analysis ===");
432
for (Baseline.Info info : baselineResults) {
433
System.out.println("Package: " + info.packageName);
434
System.out.println(" Previous: " + info.olderVersion + " -> Current: " + info.newerVersion);
435
System.out.println(" Suggested: " + info.suggestedVersion);
436
System.out.println(" Change: " + info.delta);
437
438
if (info.mismatch) {
439
System.out.println(" β Version mismatch - version too low for detected changes");
440
}
441
442
for (String warning : info.warnings) {
443
System.out.println(" β οΈ " + warning);
444
}
445
}
446
447
// Step 3: Generate summary and recommendations
448
System.out.println("\n=== Summary ===");
449
if (baseline.isOk()) {
450
System.out.println("β Baseline analysis passed");
451
Version suggested = baseline.getSuggestedVersion();
452
if (suggested != null) {
453
System.out.println("π¦ Recommended bundle version: " + suggested);
454
}
455
} else {
456
System.out.println("β Baseline analysis failed");
457
for (String error : baseline.getErrors()) {
458
System.out.println(" " + error);
459
}
460
}
461
462
// Step 4: Generate compatibility report
463
generateCompatibilityReport(diff, baselineResults);
464
}
465
}
466
467
private void printDiffTree(Diff diff, int level) {
468
String indent = " ".repeat(level);
469
Delta delta = diff.getDelta();
470
471
String icon = getChangeIcon(delta);
472
System.out.println(indent + icon + " " + diff.getType() + " " + diff.getName());
473
474
for (Diff child : diff.getChildren()) {
475
printDiffTree(child, level + 1);
476
}
477
}
478
479
private String getChangeIcon(Delta delta) {
480
switch (delta) {
481
case ADDED: return "β";
482
case REMOVED: return "β";
483
case MAJOR: return "π₯";
484
case MINOR: return "π";
485
case MICRO: return "π§";
486
case UNCHANGED: return "β ";
487
default: return "β";
488
}
489
}
490
491
private void generateCompatibilityReport(Diff diff, Set<Baseline.Info> baselineResults) {
492
System.out.println("\n=== Compatibility Report ===");
493
494
// Count changes by type
495
Map<Delta, Integer> changeCounts = new HashMap<>();
496
countChanges(diff, changeCounts);
497
498
System.out.println("Change Summary:");
499
for (Map.Entry<Delta, Integer> entry : changeCounts.entrySet()) {
500
if (entry.getValue() > 0) {
501
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
502
}
503
}
504
505
// Compatibility assessment
506
boolean hasBreaking = changeCounts.getOrDefault(Delta.MAJOR, 0) > 0 ||
507
changeCounts.getOrDefault(Delta.REMOVED, 0) > 0;
508
509
if (hasBreaking) {
510
System.out.println("β οΈ BREAKING CHANGES DETECTED");
511
System.out.println(" This release contains breaking changes that may affect consumers");
512
} else {
513
System.out.println("β BACKWARD COMPATIBLE");
514
System.out.println(" This release maintains backward compatibility");
515
}
516
}
517
518
private void countChanges(Diff diff, Map<Delta, Integer> counts) {
519
Delta delta = diff.getDelta();
520
counts.put(delta, counts.getOrDefault(delta, 0) + 1);
521
522
for (Diff child : diff.getChildren()) {
523
countChanges(child, counts);
524
}
525
}
526
}
527
```