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 Vespa.ai. 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);
}
}
|