aboutsummaryrefslogtreecommitdiffstats
path: root/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
blob: 699736195cf306138813892141b5d4eb05075bc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.classanalysis;

import com.google.common.collect.Sets;
import com.yahoo.container.plugin.util.Maps;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Tony Vaagenes
 * @author ollivir
 */
public class PackageTally {

    private final Map<String, PackageInfo> definedPackages;
    private final Set<String> referencedPackagesUnfiltered;

    PackageTally(Map<String, PackageInfo> definedPackages, Set<String> referencedPackagesUnfiltered) {
        this.definedPackages = definedPackages;
        this.referencedPackagesUnfiltered = referencedPackagesUnfiltered;
    }

    public Set<String> definedPackages() {
        return definedPackages.keySet();
    }

    public Set<String> referencedPackages() {
        return Sets.difference(referencedPackagesUnfiltered, definedPackages());
    }

    public Map<String, ExportPackageAnnotation> exportedPackages() {
        Map<String, ExportPackageAnnotation> ret = new HashMap<>();
        definedPackages.forEach((pkg, pkgInfo) -> {
            pkgInfo.exportPackage().ifPresent(a -> ret.put(pkg, a));
        });
        return ret;
    }

    public Set<String> publicApiPackages() {
        return definedPackages.values().stream()
                .filter(PackageInfo::isPublicApi)
                .map(PackageInfo::name)
                .collect(Collectors.toSet());
    }

    public Set<String> nonPublicApiExportedPackages() {
        return definedPackages.values().stream()
                .filter(pkgInfo -> pkgInfo.exportPackage().isPresent())
                .filter(pkgInfo -> ! pkgInfo.isPublicApi())
                .map(PackageInfo::name)
                .collect(Collectors.toSet());
    }

    /**
     * Returns the set of packages that is referenced from this tally, but not included in the given set of available packages.
     *
     * @param definedAndExportedPackages Set of available packages (usually all packages defined in the generated bundle's project + all exported packages of dependencies)
     * @return The set of missing packages, that may cause problem when the bundle is deployed in an OSGi container runtime.
     */
    public Set<String> referencedPackagesMissingFrom(Set<String> definedAndExportedPackages) {
        return Sets.difference(referencedPackages(), definedAndExportedPackages).stream()
                .filter(pkg -> !pkg.startsWith("java."))
                .filter(pkg -> !pkg.equals(com.yahoo.api.annotations.PublicApi.class.getPackageName()))
                .collect(Collectors.toSet());
    }

    /**
     * Represents the classes for two package tallies that are deployed as a single unit.
     * <p>
     * ExportPackageAnnotations from this has precedence over the other.
     * TODO: Add unit test and try using Map.merge (as in the functions below). Can't see how Maps.combine is any different.
     */
    public PackageTally combine(PackageTally other) {
        var definedPkgs = Maps.combine(this.definedPackages, other.definedPackages, PackageInfo::hasExportPackageOrElse);
        Set<String> referencedPkgs = new HashSet<>(this.referencedPackagesUnfiltered);
        referencedPkgs.addAll(other.referencedPackagesUnfiltered);

        return new PackageTally(definedPkgs, referencedPkgs);
    }

    public static PackageTally combine(Collection<PackageTally> packageTallies) {
        var definedPkgs = new HashMap<String, PackageInfo>();
        Set<String> referencedPkgs = new HashSet<>();

        for (PackageTally tally : packageTallies) {
            tally.definedPackages.forEach((pkg, info) -> definedPkgs.merge(pkg, info, PackageInfo::hasExportPackageOrElse));
            referencedPkgs.addAll(tally.referencedPackagesUnfiltered);
        }
        return new PackageTally(definedPkgs, referencedPkgs);
    }

    public static PackageTally fromAnalyzedClassFiles(Collection<ClassFileMetaData> analyzedClassFiles) {
        var definedPkgs = new HashMap<String, PackageInfo>();
        var referencedPkgs = new HashSet<String>();

        for (ClassFileMetaData classData : analyzedClassFiles) {
            var pkgName = classData.packageInfo().name();
            definedPkgs.merge(pkgName, classData.packageInfo(), PackageInfo::hasExportPackageOrElse);
            classData.getReferencedClasses().forEach(className -> referencedPkgs.add(Packages.packageName(className)));
        }
        return new PackageTally(definedPkgs, referencedPkgs);
    }

}