CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-biz-a-qute-bnd--biz-a-qute-bndlib

bndlib: A Swiss Army Knife for OSGi providing comprehensive bundle manipulation and analysis capabilities

Pending
Overview
Eval results
Files

api-comparison-diffing.mddocs/

API Comparison and Diffing

Semantic versioning analysis based on API changes between bundle versions, enabling automated compatibility assessment and version recommendation.

Capabilities

DiffImpl

Implementation for comparing APIs between JAR versions and detecting breaking changes.

/**
 * Implementation for comparing APIs between JAR versions
 */
public class DiffImpl implements Diff {
    /** Compare two JAR files and generate diff */
    public Diff diff(Jar newer, Jar older) throws Exception;
    
    /** Create API tree from JAR */
    public Tree tree(Jar jar) throws Exception;
    
    /** Get diff instructions for filtering */
    public Instructions getDiffInstructions();
    
    /** Set diff instructions */
    public void setDiffInstructions(Instructions instructions);
    
    /** Check if change is ignored */
    public boolean isIgnored(Element element);
    
    /** Get diff reporter */
    public Reporter getReporter();
    
    /** Set diff reporter */
    public void setReporter(Reporter reporter);
}

/**
 * Represents a difference between API elements
 */
public interface Diff {
    /** Get diff type */
    public Delta getDelta();
    
    /** Get element type */
    public Element getType();
    
    /** Get element name */
    public String getName();
    
    /** Get children diffs */
    public Collection<? extends Diff> getChildren();
    
    /** Get newer element */
    public Element getNewer();
    
    /** Get older element */  
    public Element getOlder();
}

/**
 * Types of changes detected
 */
public enum Delta {
    IGNORE,     // Change should be ignored
    UNCHANGED,  // No change
    MINOR,      // Minor change (backward compatible)
    MAJOR,      // Major change (breaking)
    MICRO,      // Micro change (implementation only)
    ADDED,      // New element added
    REMOVED     // Element removed
}

Usage Examples:

import aQute.bnd.differ.DiffImpl;
import aQute.bnd.differ.Diff;
import aQute.bnd.differ.Delta;

// Compare two versions of a bundle
Jar newer = new Jar(new File("mybundle-2.0.0.jar"));
Jar older = new Jar(new File("mybundle-1.0.0.jar"));

DiffImpl differ = new DiffImpl();
Diff diff = differ.diff(newer, older);

// Analyze changes
analyzeDiff(diff, 0);

// Helper method to analyze diff recursively
private void analyzeDiff(Diff diff, int indent) {
    String prefix = "  ".repeat(indent);
    Delta delta = diff.getDelta();
    
    System.out.println(prefix + delta + ": " + diff.getType() + " " + diff.getName());
    
    // Process child diffs
    for (Diff child : diff.getChildren()) {
        analyzeDiff(child, indent + 1);
    }
}

Baseline

Performs semantic versioning analysis based on API changes and recommends appropriate version increments.

/**
 * Performs semantic versioning analysis based on API changes
 */
public class Baseline {
    /** Create baseline analyzer */
    public Baseline(Reporter reporter, Instructions diffignore);
    
    /** Perform baseline analysis */
    public Set<Info> baseline(Jar jar, Jar baseline, Instructions instructions) throws Exception;
    
    /** Get suggested version */
    public Version getSuggestedVersion();
    
    /** Check if baseline passed */
    public boolean isOk();
    
    /** Get baseline errors */
    public List<String> getErrors();
    
    /** Get baseline warnings */
    public List<String> getWarnings();
}

/**
 * Baseline information for a package
 */
public class Info implements Comparable<Info> {
    /** Get package name */
    public String packageName;
    
    /** Get suggested version */
    public Version suggestedVersion;
    
    /** Get newer version */
    public Version newerVersion;
    
    /** Get older version */
    public Version olderVersion;
    
    /** Get version change type */
    public Delta delta;
    
    /** Get warnings */
    public List<String> warnings;
    
    /** Check if version is too low */
    public boolean mismatch;
    
    /** Get attributes */
    public Attrs attributes;
}

Usage Examples:

import aQute.bnd.differ.Baseline;
import aQute.bnd.differ.Baseline.Info;
import aQute.bnd.build.Instructions;

// Perform baseline analysis
Jar current = new Jar(new File("current.jar"));
Jar previous = new Jar(new File("previous.jar"));

Reporter reporter = new ConsoleReporter();
Instructions diffIgnore = new Instructions();
diffIgnore.put("*impl*", new Attrs()); // Ignore implementation packages

Baseline baseline = new Baseline(reporter, diffIgnore);
Set<Info> results = baseline.baseline(current, previous, new Instructions());

// Analyze results
for (Info info : results) {
    System.out.println("Package: " + info.packageName);
    System.out.println("  Current version: " + info.newerVersion);
    System.out.println("  Previous version: " + info.olderVersion);
    System.out.println("  Suggested version: " + info.suggestedVersion);
    System.out.println("  Change type: " + info.delta);
    
    if (info.mismatch) {
        System.out.println("  WARNING: Version too low for changes detected");
    }
    
    for (String warning : info.warnings) {
        System.out.println("  WARNING: " + warning);
    }
}

// Check overall result
if (baseline.isOk()) {
    System.out.println("Baseline analysis passed");
    System.out.println("Suggested bundle version: " + baseline.getSuggestedVersion());
} else {
    System.err.println("Baseline analysis failed");
    for (String error : baseline.getErrors()) {
        System.err.println("ERROR: " + error);
    }
}

Element Hierarchy

Classes representing different elements in the API tree for diff analysis.

/**
 * Base class for API elements
 */
public abstract class Element {
    /** Get element type */
    public abstract Type getType();
    
    /** Get element name */
    public abstract String getName();
    
    /** Get element key for comparison */
    public String getKey();
    
    /** Get element version */
    public Version getVersion();
    
    /** Get element attributes */
    public Map<String, String> getAttributes();
    
    /** Compare with another element */
    public Delta compare(Element other);
}

/**
 * Types of API elements
 */
public enum Type {
    ROOT,
    BUNDLE,
    PACKAGE,
    CLASS,
    INTERFACE,
    ANNOTATION,
    ENUM,
    METHOD,
    CONSTRUCTOR,
    FIELD,
    CONSTANT
}

/**
 * Java-specific element for class analysis
 */
public class JavaElement extends Element {
    /** Get Java access modifiers */
    public int getAccess();
    
    /** Check if element is public */
    public boolean isPublic();
    
    /** Check if element is protected */
    public boolean isProtected();
    
    /** Check if element is static */
    public boolean isStatic();
    
    /** Check if element is final */
    public boolean isFinal();
    
    /** Check if element is abstract */
    public boolean isAbstract();
    
    /** Get method signature */
    public String getSignature();
    
    /** Get return type */
    public String getReturnType();
    
    /** Get parameter types */
    public String[] getParameterTypes();
    
    /** Get exception types */
    public String[] getExceptionTypes();
}

Diff Instructions

Configuration for controlling diff analysis and ignoring specific changes.

/**
 * Instructions for controlling diff analysis
 */
public class Instructions extends LinkedHashMap<Instruction, Attrs> {
    /** Create empty instructions */
    public Instructions();
    
    /** Create from properties */
    public Instructions(Properties properties);
    
    /** Check if element matches any instruction */
    public boolean matches(String name);
    
    /** Get instruction that matches name */
    public Instruction getMatching(String name);
    
    /** Add instruction */
    public Instruction put(String pattern, Attrs attributes);
    
    /** Get all patterns */
    public Set<String> getPatterns();
}

/**
 * Single instruction with pattern and attributes
 */
public class Instruction {
    /** Get instruction pattern */
    public String getPattern();
    
    /** Get instruction attributes */
    public Attrs getAttributes();
    
    /** Check if pattern matches string */
    public boolean matches(String value);
    
    /** Check if instruction is literal (no wildcards) */
    public boolean isLiteral();
    
    /** Check if instruction is negated */
    public boolean isNegated();
}

Repository Diff Utilities

Utilities for comparing repository contents and tracking changes over time.

/**
 * Repository difference analyzer
 */
public class RepositoryDiff {
    /** Compare two repositories */
    public static Diff compareRepositories(Repository newer, Repository older) throws Exception;
    
    /** Find changed bundles between repositories */
    public static Map<String, Diff> findChangedBundles(Repository newer, Repository older) throws Exception;
    
    /** Generate compatibility report */
    public static CompatibilityReport generateReport(Repository newer, Repository older) throws Exception;
}

/**
 * Compatibility report between repository versions
 */
public class CompatibilityReport {
    /** Get added bundles */
    public Set<String> getAddedBundles();
    
    /** Get removed bundles */
    public Set<String> getRemovedBundles();
    
    /** Get changed bundles */
    public Map<String, BundleChange> getChangedBundles();
    
    /** Get compatibility summary */
    public CompatibilitySummary getSummary();
}

/**
 * Change information for a bundle
 */
public class BundleChange {
    public String bundleSymbolicName;
    public Version oldVersion;
    public Version newVersion;
    public Delta overallChange;
    public Map<String, Delta> packageChanges;
    public List<String> breakingChanges;
    public List<String> warnings;
}

Complete API Comparison Example:

import aQute.bnd.differ.*;
import aQute.bnd.build.Instructions;

// Complete API comparison and baseline workflow
public class APIComparisonWorkflow {
    
    public void performAPIAnalysis(File currentBundle, File previousBundle) throws Exception {
        try (Jar current = new Jar(currentBundle);
             Jar previous = new Jar(previousBundle)) {
             
            // Step 1: Perform detailed diff analysis
            DiffImpl differ = new DiffImpl();
            Diff diff = differ.diff(current, previous);
            
            System.out.println("=== API Diff Analysis ===");
            printDiffTree(diff, 0);
            
            // Step 2: Perform baseline analysis for version recommendations
            Reporter reporter = new Reporter() {
                public void error(String msg, Object... args) {
                    System.err.println("ERROR: " + String.format(msg, args));
                }
                public void warning(String msg, Object... args) {
                    System.err.println("WARNING: " + String.format(msg, args));
                }
                public void progress(float progress, String msg, Object... args) {
                    System.out.println("PROGRESS: " + String.format(msg, args));
                }
            };
            
            // Configure what to ignore in baseline
            Instructions diffIgnore = new Instructions();
            diffIgnore.put("*impl*", new Attrs());        // Ignore impl packages
            diffIgnore.put("*.internal.*", new Attrs());  // Ignore internal packages
            
            Baseline baseline = new Baseline(reporter, diffIgnore);
            Set<Baseline.Info> baselineResults = baseline.baseline(current, previous, new Instructions());
            
            System.out.println("\n=== Baseline Analysis ===");
            for (Baseline.Info info : baselineResults) {
                System.out.println("Package: " + info.packageName);
                System.out.println("  Previous: " + info.olderVersion + " -> Current: " + info.newerVersion);
                System.out.println("  Suggested: " + info.suggestedVersion);
                System.out.println("  Change: " + info.delta);
                
                if (info.mismatch) {
                    System.out.println("  ❌ Version mismatch - version too low for detected changes");
                }
                
                for (String warning : info.warnings) {
                    System.out.println("  ⚠️  " + warning);
                }
            }
            
            // Step 3: Generate summary and recommendations
            System.out.println("\n=== Summary ===");
            if (baseline.isOk()) {
                System.out.println("✅ Baseline analysis passed");
                Version suggested = baseline.getSuggestedVersion();
                if (suggested != null) {
                    System.out.println("📦 Recommended bundle version: " + suggested);
                }
            } else {
                System.out.println("❌ Baseline analysis failed");
                for (String error : baseline.getErrors()) {
                    System.out.println("   " + error);
                }
            }
            
            // Step 4: Generate compatibility report
            generateCompatibilityReport(diff, baselineResults);
        }
    }
    
    private void printDiffTree(Diff diff, int level) {
        String indent = "  ".repeat(level);
        Delta delta = diff.getDelta();
        
        String icon = getChangeIcon(delta);
        System.out.println(indent + icon + " " + diff.getType() + " " + diff.getName());
        
        for (Diff child : diff.getChildren()) {
            printDiffTree(child, level + 1);
        }
    }
    
    private String getChangeIcon(Delta delta) {
        switch (delta) {
            case ADDED: return "➕";
            case REMOVED: return "❌";
            case MAJOR: return "💥";
            case MINOR: return "🔄";
            case MICRO: return "🔧";
            case UNCHANGED: return "✅";
            default: return "❓";
        }
    }
    
    private void generateCompatibilityReport(Diff diff, Set<Baseline.Info> baselineResults) {
        System.out.println("\n=== Compatibility Report ===");
        
        // Count changes by type
        Map<Delta, Integer> changeCounts = new HashMap<>();
        countChanges(diff, changeCounts);
        
        System.out.println("Change Summary:");
        for (Map.Entry<Delta, Integer> entry : changeCounts.entrySet()) {
            if (entry.getValue() > 0) {
                System.out.println("  " + entry.getKey() + ": " + entry.getValue());
            }
        }
        
        // Compatibility assessment
        boolean hasBreaking = changeCounts.getOrDefault(Delta.MAJOR, 0) > 0 ||
                             changeCounts.getOrDefault(Delta.REMOVED, 0) > 0;
        
        if (hasBreaking) {
            System.out.println("⚠️  BREAKING CHANGES DETECTED");
            System.out.println("   This release contains breaking changes that may affect consumers");
        } else {
            System.out.println("✅ BACKWARD COMPATIBLE");
            System.out.println("   This release maintains backward compatibility");
        }
    }
    
    private void countChanges(Diff diff, Map<Delta, Integer> counts) {
        Delta delta = diff.getDelta();
        counts.put(delta, counts.getOrDefault(delta, 0) + 1);
        
        for (Diff child : diff.getChildren()) {
            countChanges(child, counts);
        }
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-biz-a-qute-bnd--biz-a-qute-bndlib

docs

api-comparison-diffing.md

core-osgi-processing.md

header-processing.md

index.md

jar-resource-management.md

plugin-architecture.md

repository-system.md

version-management.md

workspace-project-management.md

tile.json