public void execute()

in src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImports.java [77:221]


    public void execute(final AnalyserTaskContext ctx) throws Exception {
        boolean ignoreAPIRegions = ctx.getConfiguration().getOrDefault(
                IGNORE_API_REGIONS_CONFIG_KEY, "false").equalsIgnoreCase("true");

        final Map<BundleDescriptor, Report> reports = new HashMap<>();

        final SortedMap<Integer, List<BundleDescriptor>> bundlesMap = new TreeMap<>();
        for(final BundleDescriptor bi : ctx.getFeatureDescriptor().getBundleDescriptors()) {
            final List<BundleDescriptor> list = bundlesMap.computeIfAbsent(bi.getArtifact().getStartOrder(), key -> new ArrayList<>());
            list.add(bi);
        }

        // add all system packages
        final List<BundleDescriptor> exportingBundles = new ArrayList<>();
        if ( ctx.getFrameworkDescriptor() != null ) {
            exportingBundles.add(ctx.getFrameworkDescriptor());
        }

        // extract and check the api-regions
        ApiRegions apiRegions = null;
        try {
            apiRegions = ApiRegions.getApiRegions(ctx.getFeature());
        } catch (final IllegalArgumentException e) {
            ctx.reportError("API Region does not represent a valid JSON 'api-regions': " + e.getMessage());
            return;
        }
        if ( apiRegions == null ) {
            apiRegions = new ApiRegions(); // Empty region as default
        }

        for(final Map.Entry<Integer, List<BundleDescriptor>> entry : bundlesMap.entrySet()) {
            // first add all exporting bundles
            for(final BundleDescriptor info : entry.getValue()) {
                if ( !info.getExportedPackages().isEmpty() ) {
                    exportingBundles.add(info);
                }
            }
            // check importing bundles
            for(final BundleDescriptor info : entry.getValue()) {
                for(final PackageInfo pck : info.getImportedPackages() ) {
                    final Map<BundleDescriptor, Set<String>> candidates =
                            getCandidates(exportingBundles, pck, info, apiRegions, ignoreAPIRegions);
                    if ( candidates.isEmpty() ) {
                        if ( pck.isOptional() ) {
                            getReport(reports, info).missingExportsForOptional.add(pck);
                        } else {
                            getReport(reports, info).missingExports.add(pck);
                        }
                    } else {
                        final List<BundleDescriptor> matchingCandidates = new ArrayList<>();

                        Set<String> exportingRegions = new HashSet<>();
                        Set<String> importingRegions = new HashSet<>();
                        for(final Map.Entry<BundleDescriptor, Set<String>> candidate : candidates.entrySet()) {
                            BundleDescriptor bd = candidate.getKey();
                            if (bd.isExportingPackage(pck)) {
                                Set<String> exRegions = candidate.getValue();
                                if (exRegions.contains(NO_REGION)) {
                                    // If an export is defined outside of a region, it always matches
                                    matchingCandidates.add(bd);
                                    continue;
                                }
                                if (exRegions.contains(GLOBAL_REGION)) {
                                    // Everyone can import from the global regin
                                    matchingCandidates.add(bd);
                                    continue;
                                }
                                if (exRegions.contains(OWN_FEATURE)) {
                                    // A feature can always import packages from bundles in itself
                                    matchingCandidates.add(bd);
                                    continue;
                                }

                                // Find out what regions the importing bundle is in
                                Set<String> imRegions =
                                        getBundleRegions(info, apiRegions, ignoreAPIRegions);

                                // Record the exporting and importing regions for diagnostics
                                exportingRegions.addAll(exRegions);

                                Set<String> regions = new HashSet<>();
                                for (String region : imRegions) {
                                    for (ApiRegion r = apiRegions.getRegionByName(region); r != null; r = r.getParent()) {
                                        regions.add(r.getName());
                                    }
                                }

                                importingRegions.addAll(regions);

                                // Only keep the regions that also export the package
                                regions.retainAll(exRegions);

                                if (!regions.isEmpty()) {
                                    // there is an overlapping region
                                    matchingCandidates.add(bd);
                                }
                            }
                        }

                        if ( matchingCandidates.isEmpty() ) {
                            if ( pck.isOptional() ) {
                                getReport(reports, info).missingExportsForOptional.add(pck);
                            } else {
                                getReport(reports, info).missingExportsWithVersion.add(pck);
                                getReport(reports, info).regionInfo.put(pck, new AbstractMap.SimpleEntry<>(exportingRegions, importingRegions));
                            }
                        } else if ( matchingCandidates.size() > 1 ) {
                            getReport(reports, info).exportMatchingSeveral.add(pck);
                        }
                    }
                }
            }
        }

        boolean errorReported = false;
        for(final Map.Entry<BundleDescriptor, Report> entry : reports.entrySet()) {
            final String key = "Bundle " + entry.getKey().getArtifact().getId().getArtifactId() + ":" + entry.getKey().getArtifact().getId().getVersion();

            if ( !entry.getValue().missingExports.isEmpty() ) {
                ctx.reportArtifactError(entry.getKey().getArtifact().getId(),
                        key + " is importing package(s) " + getPackageInfo(entry.getValue().missingExports, false) + " in start level " +
                        String.valueOf(entry.getKey().getArtifact().getStartOrder())
                        + " but no bundle is exporting these for that start level.");
                errorReported = true;
            }
            if ( !entry.getValue().missingExportsWithVersion.isEmpty() ) {
                StringBuilder message = new StringBuilder(key + " is importing package(s) " + getPackageInfo(entry.getValue().missingExportsWithVersion, true) + " in start level " +
                        String.valueOf(entry.getKey().getArtifact().getStartOrder())
                        + " but no visible bundle is exporting these for that start level in the required version range.");

                for (Map.Entry<PackageInfo, Map.Entry<Set<String>, Set<String>>> regionInfoEntry : entry.getValue().regionInfo.entrySet()) {
                    PackageInfo pkg = regionInfoEntry.getKey();
                    Map.Entry<Set<String>, Set<String>> regions = regionInfoEntry.getValue();
                    if (regions.getKey().size() > 0) {
                        message.append("\n" + pkg.getName() + " is exported in regions " + regions.getKey() + " but it is imported in regions " + regions.getValue());
                    }
                }
                ctx.reportArtifactError(entry.getKey().getArtifact().getId(), message.toString());
                errorReported = true;
            }
        }
        if (errorReported && ctx.getFeature().isComplete()) {
            ctx.reportError(ctx.getFeature().getId().toMvnId() + " is marked as 'complete' but has missing imports.");
        }
    }