SLING-10328 : Analyser checking for usage of deprecated api
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApi.java b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApi.java
new file mode 100644
index 0000000..7bc35d2
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApi.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.feature.extension.apiregions.analyser;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.sling.feature.analyser.task.AnalyserTask;
+import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
+import org.apache.sling.feature.extension.apiregions.api.ApiExport;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
+import org.apache.sling.feature.scanner.BundleDescriptor;
+import org.apache.sling.feature.scanner.PackageInfo;
+import org.osgi.framework.Version;
+
+
+public class CheckDeprecatedApi implements AnalyserTask{
+
+    public static final String CFG_REGIONS = "regions";
+
+    private static final String CFG_STRICT = "strict";
+
+    private static final String PROP_VERSION = "version";
+
+    
+    @Override
+    public String getId() {
+        return "region-deprecated-api";
+    }
+
+    @Override
+    public String getName() {
+        return "Region Deprecated API analyser task";
+    }
+
+	@Override
+	public void execute(final AnalyserTaskContext context) throws Exception {
+        final ApiRegions regions = ApiRegions.getApiRegions(context.getFeature());
+        if ( regions == null ) {
+            context.reportExtensionError(ApiRegions.EXTENSION_NAME, "No regions configured");
+        } else {
+            final Map<BundleDescriptor, Set<String>> bundleRegions = this.calculateBundleRegions(context, regions);
+            final boolean strict = Boolean.parseBoolean(context.getConfiguration().getOrDefault(CFG_STRICT, "false"));
+            final String regionNames = context.getConfiguration().getOrDefault(CFG_REGIONS, ApiRegion.GLOBAL);
+            for(final String r : regionNames.split(",")) {
+                final ApiRegion region = regions.getRegionByName(r.trim());
+                if (region == null ) {
+                    context.reportExtensionError(ApiRegions.EXTENSION_NAME, "Region not found:" + r.trim());
+                } else {
+                    checkBundlesForRegion(context, region, bundleRegions, strict);
+                }
+            }
+        }
+	}
+
+    private Map<BundleDescriptor, Set<String>> calculateBundleRegions(AnalyserTaskContext context, ApiRegions regions) {
+        final Map<BundleDescriptor, Set<String>> result = new LinkedHashMap<>();
+        for(final BundleDescriptor bd : context.getFeatureDescriptor().getBundleDescriptors()) {
+            final Set<String> regionNames = getBundleRegions(bd, regions);
+            result.put(bd, regionNames);
+        }
+        return result;
+    }
+
+    private void checkBundlesForRegion(final AnalyserTaskContext context, 
+            final ApiRegion region,
+            final Map<BundleDescriptor, Set<String>> bundleRegions,
+            final boolean strict) {
+        final Set<ApiExport> exports = this.calculateDeprecatedPackages(region, bundleRegions);
+
+        final Set<String> allowedNames = getAllowedRegions(region);
+
+        for(final BundleDescriptor bd : context.getFeatureDescriptor().getBundleDescriptors()) {
+            if ( isInAllowedRegion(bundleRegions.get(bd), region.getName(), allowedNames) ) {
+                for(final PackageInfo pi : bd.getImportedPackages()) {
+                    String imports = null;
+                    for(final ApiExport exp : exports) {
+                        if ( pi.getName().equals(exp.getName()) ) {
+                            String version = exp.getProperties().get(PROP_VERSION);
+                            if ( version == null || pi.getPackageVersionRange().includes(new Version(version)) ) {
+                                imports = exp.getDeprecation().getPackageInfo().getMessage();
+                                break;
+                            }
+                        }
+                    }
+                    if ( imports != null ) {
+                        final String msg = "Usage of deprecated package found : ".concat(pi.getName()).concat(" : ").concat(imports);
+                        if ( strict ) {
+                            context.reportArtifactError(bd.getArtifact().getId(), msg);
+                        } else {
+                            context.reportArtifactWarning(bd.getArtifact().getId(), msg);
+                        }
+                    } 
+                }
+
+            }
+        }
+    }
+
+    boolean isInAllowedRegion(final Set<String> bundleRegions, final String regionName, final Set<String> allowedRegions) {
+        if ( bundleRegions.contains(regionName) ) {
+            for(final String name : bundleRegions) {
+                if ( !allowedRegions.contains(name) ) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    Set<String> getAllowedRegions(final ApiRegion region) {
+        final Set<String> allowedNames = new HashSet<>();
+        ApiRegion r = region;
+        while ( r != null ) {
+            allowedNames.add(r.getName());
+            r = r.getParent();
+        }
+
+        return allowedNames;
+    }
+
+    Set<ApiExport> calculateDeprecatedPackages(final ApiRegion region,
+            final Map<BundleDescriptor, Set<String>> bundleRegions) {
+        final Set<ApiExport> result = new LinkedHashSet<>();
+        for(final ApiExport export : region.listAllExports()) {
+            if ( export.getDeprecation().getPackageInfo() != null ) {
+                final String version = getVersion(bundleRegions, region.getName(), export.getName());
+                // create new ApiExport to add version
+                final ApiExport clone = new ApiExport(export.getName());
+                clone.getDeprecation().setPackageInfo(export.getDeprecation().getPackageInfo());
+                if ( version != null ) {
+                    clone.getProperties().put(PROP_VERSION, version);
+                }
+                result.add(clone);
+            }
+        }
+        return result;
+    }
+
+    String getVersion(final Map<BundleDescriptor, Set<String>> bundleRegions, final String regionName, final String packageName) {
+        String version = null;
+        for(final Map.Entry<BundleDescriptor, Set<String>> entry : bundleRegions.entrySet()) {
+            if ( entry.getValue().contains(regionName)) {
+                for(final PackageInfo info : entry.getKey().getExportedPackages()) {
+                    if ( info.getName().equals(packageName)) {
+                        version = info.getVersion();
+                        break;
+                    }
+                }
+                if ( version != null ) {
+                    break;
+                }
+            }
+        }
+        return version;
+    }
+
+    private Set<String> getBundleRegions(final BundleDescriptor info, final ApiRegions regions) {
+        return Stream.of(info.getArtifact().getFeatureOrigins())
+            .map(regions::getRegionsByFeature).flatMap(Stream::of).map(ApiRegion::getName).collect(Collectors.toSet());
+    }
+}
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
index c2f9f10..e40f213 100644
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask
@@ -4,4 +4,5 @@
 org.apache.sling.feature.extension.apiregions.analyser.CheckApiRegionsOrder
 org.apache.sling.feature.extension.apiregions.analyser.CheckApiRegionsBundleExportsImports
 org.apache.sling.feature.extension.apiregions.analyser.CheckApiRegionsCrossFeatureDups
-org.apache.sling.feature.extension.apiregions.analyser.CheckConfigurationApi
\ No newline at end of file
+org.apache.sling.feature.extension.apiregions.analyser.CheckConfigurationApi
+org.apache.sling.feature.extension.apiregions.analyser.CheckDeprecatedApi
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApiTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApiTest.java
new file mode 100644
index 0000000..d6875f2
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckDeprecatedApiTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.feature.extension.apiregions.analyser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sling.feature.extension.apiregions.api.ApiExport;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
+import org.apache.sling.feature.extension.apiregions.api.DeprecationInfo;
+import org.junit.Test;
+
+public class CheckDeprecatedApiTest {
+    
+    @Test public void testIsInAllowedRegion() {
+        final CheckDeprecatedApi analyser = new CheckDeprecatedApi();
+
+        final Set<String> allowedRegions = new HashSet<>(Arrays.asList("deprecated", "global"));
+
+        assertTrue(analyser.isInAllowedRegion(new HashSet<>(Arrays.asList("deprecated", "global")), "deprecated", allowedRegions));
+        assertFalse(analyser.isInAllowedRegion(new HashSet<>(Arrays.asList("deprecated", "global", "internal")), "deprecated", allowedRegions));
+        assertTrue(analyser.isInAllowedRegion(new HashSet<>(Arrays.asList("deprecated")), "deprecated", allowedRegions));
+        assertFalse(analyser.isInAllowedRegion(new HashSet<>(Arrays.asList("foo")), "deprecated", allowedRegions));
+    }
+
+    @Test public void testGetAllowedRegions() {
+        final CheckDeprecatedApi analyser = new CheckDeprecatedApi();
+
+        final ApiRegions regions = new ApiRegions();
+        regions.add(new ApiRegion("global"));
+        regions.add(new ApiRegion("deprecated"));
+        regions.add(new ApiRegion("internal"));
+        assertEquals(new HashSet<>(Arrays.asList("global")), analyser.getAllowedRegions(regions.getRegionByName("global")));
+        assertEquals(new HashSet<>(Arrays.asList("global", "deprecated")), analyser.getAllowedRegions(regions.getRegionByName("deprecated")));
+        assertEquals(new HashSet<>(Arrays.asList("global", "deprecated", "internal")), analyser.getAllowedRegions(regions.getRegionByName("internal")));
+    }
+
+    @Test public void testCalculateDeprecatedPackages() {
+        final CheckDeprecatedApi analyser = new CheckDeprecatedApi();
+
+        final ApiRegion region = new ApiRegion("global");
+        final ApiExport e1 = new ApiExport("e1");
+        e1.getDeprecation().setPackageInfo(new DeprecationInfo("deprecated-e1"));
+        final ApiExport e2 = new ApiExport("e2");
+        final ApiExport e3 = new ApiExport("e3");
+        e3.getDeprecation().addMemberInfo("Foo", new DeprecationInfo("deprecated-e3"));
+
+        region.add(e1);
+        region.add(e2);
+        region.add(e3);
+        
+        // only e1 should be returned
+        final Set<ApiExport> exports = analyser.calculateDeprecatedPackages(region, Collections.emptyMap());
+        assertEquals(1, exports.size());
+        final ApiExport exp = exports.iterator().next();
+        assertEquals(e1.getName(), exp.getName());
+        assertEquals(e1.getDeprecation().getPackageInfo().getMessage(), exp.getDeprecation().getPackageInfo().getMessage());
+    }
+}