sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject svn commit: r1803557 [1/2] - in /sling/whiteboard/cziegeler: feature-analyser/src/main/java/org/apache/sling/feature/analyser/ feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ feature-analyser/src/main/java/org/apache/sling/featur...
Date Mon, 31 Jul 2017 17:22:59 GMT
Author: pauls
Date: Mon Jul 31 17:22:58 2017
New Revision: 1803557

URL: http://svn.apache.org/viewvc?rev=1803557&view=rev
Log:
Move parsing into feature-support, add Requirement/Capability matching and parsing to feature-support, and create basic check task for Req/Cap in analyser (still needs testing).

Added:
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckRequirementsCapabilities.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/
      - copied from r1803556, sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/json/
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/LambdaUtil.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestUtil.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/PackageInfo.java
    sling/whiteboard/cziegeler/feature-support/src/test/java/org/apache/sling/feature/support/json/
    sling/whiteboard/cziegeler/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONReaderTest.java
    sling/whiteboard/cziegeler/feature-support/src/test/java/org/apache/sling/feature/support/json/FeatureJSONWriterTest.java
    sling/whiteboard/cziegeler/feature-support/src/test/java/org/apache/sling/feature/support/json/U.java
    sling/whiteboard/cziegeler/feature-support/src/test/resources/
    sling/whiteboard/cziegeler/feature-support/src/test/resources/features/
    sling/whiteboard/cziegeler/feature-support/src/test/resources/features/test.json
Removed:
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/PackageInfo.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ManifestUtil.java
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/json/
    sling/whiteboard/cziegeler/feature/src/test/java/org/apache/sling/feature/json/
    sling/whiteboard/cziegeler/feature/src/test/resources/features/
Modified:
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/BundleDescriptor.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckBundleExportsImports.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ContainerDescriptorImpl.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/main/Main.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/AnalyserTask.java
    sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
    sling/whiteboard/cziegeler/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
    sling/whiteboard/cziegeler/feature-support/pom.xml
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/ConfigurationUtil.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONReader.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONWriter.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONReader.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONWriter.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONConstants.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONReaderBase.java
    sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java
    sling/whiteboard/cziegeler/feature/pom.xml
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/Capability.java
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/Requirement.java
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
    sling/whiteboard/cziegeler/osgifeature-maven-plugin/src/main/java/org/apache/sling/feature/maven/Preprocessor.java
    sling/whiteboard/cziegeler/osgifeature-maven-plugin/src/main/java/org/apache/sling/feature/maven/ProjectHelper.java
    sling/whiteboard/cziegeler/osgifeature-maven-plugin/src/main/java/org/apache/sling/feature/maven/mojos/AttachFeature.java

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/BundleDescriptor.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/BundleDescriptor.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/BundleDescriptor.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/BundleDescriptor.java Mon Jul 31 17:22:58 2017
@@ -16,6 +16,8 @@
  */
 package org.apache.sling.feature.analyser;
 
+import org.apache.sling.feature.support.util.PackageInfo;
+
 /**
  * Information about a bundle
  */

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/Descriptor.java Mon Jul 31 17:22:58 2017
@@ -21,6 +21,7 @@ import java.util.Set;
 
 import org.apache.sling.feature.Capability;
 import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.support.util.PackageInfo;
 
 /**
  * A descriptor holds information about requirements and capabilities

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/BundleDescriptorImpl.java Mon Jul 31 17:22:58 2017
@@ -24,8 +24,12 @@ import java.util.Set;
 import java.util.jar.Manifest;
 
 import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Capability;
+import org.apache.sling.feature.Requirement;
 import org.apache.sling.feature.analyser.BundleDescriptor;
-import org.apache.sling.feature.analyser.PackageInfo;
+import org.apache.sling.feature.support.util.PackageInfo;
+import org.apache.sling.feature.support.util.ManifestParser;
+import org.apache.sling.feature.support.util.ManifestUtil;
 import org.osgi.framework.Constants;
 
 /**
@@ -61,6 +65,10 @@ public class BundleDescriptorImpl
     /** The corresponding artifact from the feature. */
     private final Artifact artifact;
 
+    private final Set<Capability> capabilities = new HashSet<>();
+
+    private final Set<Requirement> requirements = new HashSet<>();
+
     public BundleDescriptorImpl(final Artifact a,
             final File file,
             final int startLevel) throws IOException  {
@@ -169,6 +177,16 @@ public class BundleDescriptorImpl
         return Collections.unmodifiableSet(this.dynamicImportedPackages);
     }
 
+    @Override
+    public Set<Capability> getCapabilities() {
+        return Collections.unmodifiableSet(capabilities);
+    }
+
+    @Override
+    public Set<Requirement> getRequirements() {
+        return Collections.unmodifiableSet(requirements);
+    }
+
     protected void analyze() throws IOException {
         final String name = this.manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
         if ( name != null ) {
@@ -186,7 +204,13 @@ public class BundleDescriptorImpl
             this.exportedPackages.addAll(ManifestUtil.extractExportedPackages(this.manifest));
             this.importedPackages.addAll(ManifestUtil.extractImportedPackages(this.manifest));
             this.dynamicImportedPackages.addAll(ManifestUtil.extractDynamicImportedPackages(this.manifest));
-
+            try {
+                ManifestParser parser = new ManifestParser(this.manifest);
+                this.capabilities.addAll(ManifestUtil.extractCapabilities(parser));
+                this.requirements.addAll(ManifestUtil.extractRequirements(parser));
+            } catch (Exception ex) {
+                throw new IOException(ex);
+            }
         } else {
             throw new IOException("Unable to get bundle symbolic name from artifact " + getArtifact().getId().toMvnId());
         }

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckBundleExportsImports.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckBundleExportsImports.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckBundleExportsImports.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckBundleExportsImports.java Mon Jul 31 17:22:58 2017
@@ -28,7 +28,7 @@ import java.util.TreeMap;
 
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.analyser.BundleDescriptor;
-import org.apache.sling.feature.analyser.PackageInfo;
+import org.apache.sling.feature.support.util.PackageInfo;
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
 import org.osgi.framework.Version;

Added: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckRequirementsCapabilities.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckRequirementsCapabilities.java?rev=1803557&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckRequirementsCapabilities.java (added)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/CheckRequirementsCapabilities.java Mon Jul 31 17:22:58 2017
@@ -0,0 +1,108 @@
+/*
+ * 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.analyser.impl;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Capability;
+import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.analyser.ArtifactDescriptor;
+import org.apache.sling.feature.analyser.BundleDescriptor;
+import org.apache.sling.feature.analyser.task.AnalyserTask;
+import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
+import org.apache.sling.feature.support.impl.CapabilityMatcher;
+import org.osgi.framework.Constants;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class CheckRequirementsCapabilities implements AnalyserTask {
+    private final String format = "Artifact %s:%s requires %s in start level %d but %s";
+
+    @Override
+    public void execute(AnalyserTaskContext ctx) throws Exception {
+        final SortedMap<Integer, List<ArtifactDescriptor>> artifactsMap = new TreeMap<>();
+        for(final BundleDescriptor bi : ctx.getDescriptor().getBundleDescriptors()) {
+            List<ArtifactDescriptor> list = artifactsMap.get(bi.getBundleStartLevel());
+            if ( list == null ) {
+                list = new ArrayList<>();
+                artifactsMap.put(bi.getBundleStartLevel(), list);
+            }
+            list.add(bi);
+        }
+
+        if (!ctx.getDescriptor().getArtifactDescriptors().isEmpty()) {
+            artifactsMap.put(
+                    (artifactsMap.isEmpty() ? 0 : artifactsMap.lastKey()) + 1,
+                    new ArrayList<>(ctx.getDescriptor().getArtifactDescriptors())
+                    );
+        }
+
+        // create a synthetic bundle info for the system bundle
+        final ArtifactDescriptor system = new BundleDescriptorImpl(new Artifact(ctx.getApplication().getFramework()), ctx.getDescriptor().getFrameworkDescriptor().getExportedPackages()) {
+            @Override
+            public Set<Requirement> getRequirements() {
+                return  ctx.getDescriptor().getFrameworkDescriptor().getRequirements();
+            }
+
+            @Override
+            public Set<Capability> getCapabilities() {
+                return  ctx.getDescriptor().getFrameworkDescriptor().getCapabilities();
+            }
+        };
+
+        // add system artifact
+        final List<ArtifactDescriptor> artifacts = new ArrayList<>();
+        artifacts.add(system);
+
+        for(final Map.Entry<Integer, List<ArtifactDescriptor>> entry : artifactsMap.entrySet()) {
+            // first add all providing artifacts
+            for (final ArtifactDescriptor info : entry.getValue()) {
+                if (info.getCapabilities() != null) {
+                    artifacts.add(info);
+                }
+            }
+            // check requiring artifacts
+            for (final ArtifactDescriptor info : entry.getValue()) {
+                if (info.getRequirements() != null)
+                {
+                    for (Requirement requirement : info.getRequirements()) {
+                        List<ArtifactDescriptor> candidates = getCandidates(artifacts, requirement);
+
+                        if (candidates.isEmpty()) {
+                            if (!CapabilityMatcher.isOptional(requirement)) {
+                                ctx.reportError(String.format(format, info.toString(), entry.getKey(), "no artifact is providing a matching capability in this start level."));
+                            }
+                            else {
+                                ctx.reportWarning(String.format(format, info.toString(), entry.getKey(), "while the requirement is optional no artifact is providing a matching capability in this start level."));
+                            }
+                        }
+                        else if ( candidates.size() > 1 ) {
+                            ctx.reportWarning(String.format(format, info.toString(), entry.getKey(), "there is more than one matching capability in this start level."));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private List<ArtifactDescriptor> getCandidates(List<ArtifactDescriptor> artifactDescriptors, Requirement requirement) {
+        return artifactDescriptors.stream()
+                .filter(artifactDescriptor -> artifactDescriptor.getCapabilities() != null)
+                .filter(artifactDescriptor -> artifactDescriptor.getCapabilities().stream().anyMatch(capability -> CapabilityMatcher.matches(capability, requirement)))
+                .collect(Collectors.toList());
+    }
+}

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ContainerDescriptorImpl.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ContainerDescriptorImpl.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ContainerDescriptorImpl.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/impl/ContainerDescriptorImpl.java Mon Jul 31 17:22:58 2017
@@ -24,7 +24,7 @@ import org.apache.sling.feature.Requirem
 import org.apache.sling.feature.analyser.ArtifactDescriptor;
 import org.apache.sling.feature.analyser.BundleDescriptor;
 import org.apache.sling.feature.analyser.ContainerDescriptor;
-import org.apache.sling.feature.analyser.PackageInfo;
+import org.apache.sling.feature.support.util.PackageInfo;
 
 /**
  * Information about a container (feature/application).

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/main/Main.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/main/Main.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/main/Main.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/main/Main.java Mon Jul 31 17:22:58 2017
@@ -23,7 +23,7 @@ import java.io.IOException;
 import org.apache.sling.feature.Application;
 import org.apache.sling.feature.analyser.Analyser;
 import org.apache.sling.feature.analyser.Scanner;
-import org.apache.sling.feature.json.ApplicationJSONReader;
+import org.apache.sling.feature.support.json.ApplicationJSONReader;
 import org.apache.sling.feature.support.ArtifactManagerConfig;
 import org.apache.sling.feature.support.FeatureUtil;
 import org.slf4j.Logger;

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/AnalyserTask.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/AnalyserTask.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/AnalyserTask.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/AnalyserTask.java Mon Jul 31 17:22:58 2017
@@ -23,10 +23,14 @@ package org.apache.sling.feature.analyse
 public interface AnalyserTask {
 
     /** A unique (short) id. */
-    String getId();
+    default String getId() {
+        return getClass().getName();
+    };
 
     /** A human readable name to identify the task. */
-    String getName();
+    default String getName() {
+        return getClass().getSimpleName();
+    };
 
     /** Execute the task. */
     void execute(AnalyserTaskContext ctx) throws Exception;

Modified: sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java (original)
+++ sling/whiteboard/cziegeler/feature-analyser/src/main/java/org/apache/sling/feature/scanner/impl/FelixFrameworkScanner.java Mon Jul 31 17:22:58 2017
@@ -31,7 +31,7 @@ import org.apache.sling.commons.osgi.Man
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.KeyValueMap;
 import org.apache.sling.feature.analyser.Descriptor;
-import org.apache.sling.feature.analyser.PackageInfo;
+import org.apache.sling.feature.support.util.PackageInfo;
 import org.apache.sling.feature.scanner.FrameworkScanner;
 import org.osgi.framework.Constants;
 

Modified: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java (original)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java Mon Jul 31 17:22:58 2017
@@ -28,8 +28,8 @@ import org.apache.sling.feature.Artifact
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.json.ApplicationJSONReader;
-import org.apache.sling.feature.json.ApplicationJSONWriter;
+import org.apache.sling.feature.support.json.ApplicationJSONReader;
+import org.apache.sling.feature.support.json.ApplicationJSONWriter;
 import org.apache.sling.feature.launcher.impl.LauncherConfig.StartupMode;
 import org.apache.sling.feature.support.ArtifactHandler;
 import org.apache.sling.feature.support.ArtifactManager;

Modified: sling/whiteboard/cziegeler/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java (original)
+++ sling/whiteboard/cziegeler/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java Mon Jul 31 17:22:58 2017
@@ -43,8 +43,8 @@ import org.apache.sling.feature.Extensio
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Extensions;
 import org.apache.sling.feature.KeyValueMap;
-import org.apache.sling.feature.json.ApplicationJSONWriter;
-import org.apache.sling.feature.json.FeatureJSONWriter;
+import org.apache.sling.feature.support.json.ApplicationJSONWriter;
+import org.apache.sling.feature.support.json.FeatureJSONWriter;
 import org.apache.sling.feature.support.ArtifactHandler;
 import org.apache.sling.feature.support.ArtifactManager;
 import org.apache.sling.feature.support.ArtifactManagerConfig;

Modified: sling/whiteboard/cziegeler/feature-support/pom.xml
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/pom.xml?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/pom.xml (original)
+++ sling/whiteboard/cziegeler/feature-support/pom.xml Mon Jul 31 17:22:58 2017
@@ -65,6 +65,32 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.4.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.converter</artifactId>
+            <version>0.1.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configurator</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-json_1.0_spec</artifactId>
+            <version>1.0-alpha-1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Testing -->
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
         </dependency>
@@ -74,5 +100,11 @@
             <version>2.8.9</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.johnzon</groupId>
+            <artifactId>johnzon-core</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/ConfigurationUtil.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/ConfigurationUtil.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/ConfigurationUtil.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/ConfigurationUtil.java Mon Jul 31 17:22:58 2017
@@ -26,7 +26,7 @@ import java.util.jar.Manifest;
 import java.util.zip.ZipEntry;
 
 import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.json.ConfigurationJSONWriter;
+import org.apache.sling.feature.support.json.ConfigurationJSONWriter;
 import org.osgi.framework.Constants;
 
 

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/FeatureUtil.java Mon Jul 31 17:22:58 2017
@@ -28,7 +28,7 @@ import java.util.List;
 import org.apache.sling.feature.Application;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.json.FeatureJSONReader;
+import org.apache.sling.feature.support.json.FeatureJSONReader;
 import org.apache.sling.feature.process.ApplicationBuilder;
 import org.apache.sling.feature.process.FeatureProvider;
 

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONReader.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONReader.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONReader.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONReader.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Reader;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONWriter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONWriter.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONWriter.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ApplicationJSONWriter.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Writer;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONReader.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONReader.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONReader.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONReader.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Reader;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONWriter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONWriter.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONWriter.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/ConfigurationJSONWriter.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Writer;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Reader;
@@ -32,6 +32,7 @@ import org.apache.sling.feature.Capabili
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
 import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.support.util.ManifestUtil;
 
 /**
  * This class offers a method to read a {@code Feature} using a {@code Reader} instance.
@@ -283,18 +284,14 @@ public class FeatureJSONReader extends J
                     checkType("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    for(final Map.Entry<String, Object> entry : attrs.entrySet()) {
-                        r.getAttributes().put(entry.getKey(), entry.getValue().toString());
-                    }
+                    attrs.forEach((key, value) -> ManifestUtil.unmarshallAttribute(key, value, r.getAttributes()::put));
                 }
 
                 if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
                     checkType("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    for(final Map.Entry<String, Object> entry : dirs.entrySet()) {
-                        r.getDirectives().put(entry.getKey(), entry.getValue().toString());
-                    }
+                    dirs.forEach((key, value) -> ManifestUtil.unmarshallDirective(key, value, r.getDirectives()::put));
                 }
             }
         }
@@ -324,18 +321,14 @@ public class FeatureJSONReader extends J
                     checkType("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
                     @SuppressWarnings("unchecked")
                     final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    for(final Map.Entry<String, Object> entry : attrs.entrySet()) {
-                        c.getAttributes().put(entry.getKey(), entry.getValue().toString());
-                    }
+                    attrs.forEach((key, value) -> ManifestUtil.unmarshallAttribute(key, value, c.getAttributes()::put));
                 }
 
                 if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
                     checkType("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
                     @SuppressWarnings("unchecked")
-                    final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    for(final Map.Entry<String, Object> entry : dirs.entrySet()) {
-                        c.getDirectives().put(entry.getKey(), entry.getValue().toString());
-                    }
+                    final Map<String, Object> dirs = (Map<String, Object>) obj.get(JSONConstants.REQCAP_DIRECTIVES);
+                    dirs.forEach((key, value) -> ManifestUtil.unmarshallDirective(key, value, c.getDirectives()::put));
                 }
             }
         }

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java Mon Jul 31 17:22:58 2017
@@ -14,12 +14,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Writer;
 import java.util.List;
 import java.util.Map;
+import java.util.function.BiFunction;
 
 import javax.json.Json;
 import javax.json.stream.JsonGenerator;
@@ -31,6 +32,7 @@ import org.apache.sling.feature.Configur
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
 import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.support.util.ManifestUtil;
 
 
 /**
@@ -152,16 +154,12 @@ public class FeatureJSONWriter extends J
                 w.write(JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
                 if ( !req.getAttributes().isEmpty() ) {
                     w.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                    for(final Map.Entry<String, String> entry : req.getAttributes()) {
-                        w.write(entry.getKey(), entry.getValue());
-                    }
+                    req.getAttributes().forEach((key, value) -> ManifestUtil.marshallAttribute(key, value, w::write));
                     w.writeEnd();
                 }
                 if ( !req.getDirectives().isEmpty() ) {
                     w.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                    for(final Map.Entry<String, String> entry : req.getDirectives()) {
-                        w.write(entry.getKey(), entry.getValue());
-                    }
+                    req.getDirectives().forEach((key, value) -> ManifestUtil.marshallDirective(key, value, w::write));
                     w.writeEnd();
                 }
                 w.writeEnd();
@@ -178,16 +176,12 @@ public class FeatureJSONWriter extends J
                 w.write(JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
                 if ( !cap.getAttributes().isEmpty() ) {
                     w.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                    for(final Map.Entry<String, String> entry : cap.getAttributes()) {
-                        w.write(entry.getKey(), entry.getValue());
-                    }
+                    cap.getAttributes().forEach((key, value) -> ManifestUtil.marshallAttribute(key, value, w::write));
                     w.writeEnd();
                 }
                 if ( !cap.getDirectives().isEmpty() ) {
                     w.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                    for(final Map.Entry<String, String> entry : cap.getDirectives()) {
-                        w.write(entry.getKey(), entry.getValue());
-                    }
+                    cap.getDirectives().forEach((key, value) -> ManifestUtil.marshallDirective(key, value, w::write));
                     w.writeEnd();
                 }
                 w.writeEnd();

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONConstants.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONConstants.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONConstants.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONConstants.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.util.Arrays;
 import java.util.List;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONReaderBase.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONReaderBase.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONReaderBase.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONReaderBase.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.IOException;
 import java.io.Reader;

Modified: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java?rev=1803557&r1=1803556&r2=1803557&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java (original)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java Mon Jul 31 17:22:58 2017
@@ -14,7 +14,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.sling.feature.json;
+package org.apache.sling.feature.support.json;
 
 import java.io.StringReader;
 import java.lang.reflect.Array;

Added: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/LambdaUtil.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/LambdaUtil.java?rev=1803557&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/LambdaUtil.java (added)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/LambdaUtil.java Mon Jul 31 17:22:58 2017
@@ -0,0 +1,99 @@
+/*
+ * 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.support.util;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+
+public final class LambdaUtil {
+
+    @FunctionalInterface
+    public interface Consumer_WithExceptions<T, E extends Exception> {
+        void accept(T t) throws E;
+    }
+
+    @FunctionalInterface
+    public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
+        void accept(T t, U u) throws E;
+    }
+
+    @FunctionalInterface
+    public interface Function_WithExceptions<T, R, E extends Exception> {
+        R apply(T t) throws E;
+    }
+
+    @FunctionalInterface
+    public interface Supplier_WithExceptions<T, E extends Exception> {
+        T get() throws E;
+    }
+
+    @FunctionalInterface
+    public interface Runnable_WithExceptions<E extends Exception> {
+        void run() throws E;
+    }
+
+    public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
+        return t -> {
+            try { consumer.accept(t); }
+            catch (Exception exception) { throwAsUnchecked(exception); }
+        };
+    }
+
+    public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) {
+        return (t, u) -> {
+            try { biConsumer.accept(t, u); }
+            catch (Exception exception) { throwAsUnchecked(exception); }
+        };
+    }
+
+    public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) {
+        return t -> {
+            try { return function.apply(t); }
+            catch (Exception exception) { throwAsUnchecked(exception); return null; }
+        };
+    }
+
+    public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) {
+        return () -> {
+            try { return function.get(); }
+            catch (Exception exception) { throwAsUnchecked(exception); return null; }
+        };
+    }
+
+    public static void uncheck(Runnable_WithExceptions t)
+    {
+        try { t.run(); }
+        catch (Exception exception) { throwAsUnchecked(exception); }
+    }
+
+    public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
+    {
+        try { return supplier.get(); }
+        catch (Exception exception) { throwAsUnchecked(exception); return null; }
+    }
+
+    public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
+        try { return function.apply(t); }
+        catch (Exception exception) { throwAsUnchecked(exception); return null; }
+    }
+
+    @SuppressWarnings ("unchecked")
+    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
+}

Added: sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java?rev=1803557&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java (added)
+++ sling/whiteboard/cziegeler/feature-support/src/main/java/org/apache/sling/feature/support/util/ManifestParser.java Mon Jul 31 17:22:58 2017
@@ -0,0 +1,1038 @@
+/*
+ * 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.support.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.jar.Manifest;
+
+import org.apache.sling.feature.Capability;
+import org.apache.sling.feature.Requirement;
+import org.apache.sling.feature.support.impl.SimpleFilter;
+import org.apache.sling.feature.support.impl.VersionRange;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class ManifestParser
+{
+    private static final String BUNDLE_LICENSE_HEADER = "Bundle-License"; // No constant defined by OSGi...
+
+    private final Manifest m_headerMap;
+    private volatile String m_bundleSymbolicName;
+    private volatile Version m_bundleVersion;
+    private volatile List<Capability> m_capabilities;
+    private volatile List<Requirement> m_requirements;
+
+    public ManifestParser(Manifest m)
+            throws BundleException
+    {
+        m_headerMap = m;
+
+        // Verify that only manifest version 2 is specified.
+        String manifestVersion = getManifestVersion(m_headerMap);
+        if ((manifestVersion != null) && !manifestVersion.equals("2"))
+        {
+            throw new BundleException(
+                    "Unknown 'Bundle-ManifestVersion' value: " + manifestVersion);
+        }
+
+        List<Capability> capList = new ArrayList<>();
+
+        //
+        // Parse bundle version.
+        //
+
+        m_bundleVersion = Version.emptyVersion;
+        if (m_headerMap.getMainAttributes().getValue(Constants.BUNDLE_VERSION) != null)
+        {
+            try
+            {
+                m_bundleVersion = Version.parseVersion(m_headerMap.getMainAttributes().getValue(Constants.BUNDLE_VERSION));
+            }
+            catch (RuntimeException ex)
+            {
+                // R4 bundle versions must parse, R3 bundle version may not.
+                if (getManifestVersion().equals("2"))
+                {
+                    throw ex;
+                }
+                m_bundleVersion = Version.emptyVersion;
+            }
+        }
+
+        //
+        // Parse bundle symbolic name.
+        //
+
+        Capability bundleCap = parseBundleSymbolicName(m_headerMap);
+        if (bundleCap != null)
+        {
+            m_bundleSymbolicName = (String)
+                    bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE);
+
+            // Add a bundle capability and a host capability to all
+            // non-fragment bundles. A host capability is the same
+            // as a require capability, but with a different capability
+            // namespace. Bundle capabilities resolve required-bundle
+            // dependencies, while host capabilities resolve fragment-host
+            // dependencies.
+            if (m_headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST) == null)
+            {
+                // All non-fragment bundles have host capabilities.
+                capList.add(bundleCap);
+                // A non-fragment bundle can choose to not have a host capability.
+                String attachment =
+                        (String) bundleCap.getDirectives().get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
+                attachment = (attachment == null)
+                        ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME
+                        : attachment;
+                if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER))
+                {
+                    Map<String, Object> hostAttrs =
+                            new HashMap<String, Object>(bundleCap.getAttributes());
+                    Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+                    hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
+                    Capability cap = new Capability(BundleRevision.HOST_NAMESPACE);
+                    cap.getAttributes().putAll(hostAttrs);
+                    cap.getDirectives().putAll(bundleCap.getDirectives());
+                    capList.add(cap);
+                }
+            }
+
+            //
+            // Add the osgi.identity capability.
+            //
+            capList.add(addIdentityCapability(m_headerMap, bundleCap));
+        }
+
+        // Verify that bundle symbolic name is specified.
+        if (getManifestVersion().equals("2") && (m_bundleSymbolicName == null))
+        {
+            throw new BundleException(
+                    "R4 bundle manifests must include bundle symbolic name.");
+        }
+
+        //
+        // Parse Fragment-Host.
+        //
+
+        List<Requirement> hostReqs = parseFragmentHost(m_headerMap);
+
+        //
+        // Parse Require-Bundle
+        //
+
+        List<ParsedHeaderClause> rbClauses =
+                parseStandardHeader(m_headerMap.getMainAttributes().getValue(Constants.REQUIRE_BUNDLE));
+        rbClauses = normalizeRequireClauses(rbClauses, getManifestVersion());
+        List<Requirement> rbReqs = convertRequires(rbClauses);
+
+
+        //
+        // Parse Require-Capability.
+        //
+
+        List<ParsedHeaderClause> requireClauses =
+                parseStandardHeader(m_headerMap.getMainAttributes().getValue(Constants.REQUIRE_CAPABILITY));
+        List<Requirement> requireReqs = convertRequireCapabilities(normalizeCapabilityClauses( requireClauses, getManifestVersion()));
+
+        //
+        // Parse Provide-Capability.
+        //
+
+        List<ParsedHeaderClause> provideClauses =
+                parseStandardHeader(m_headerMap.getMainAttributes().getValue(Constants.PROVIDE_CAPABILITY));
+
+        List<Capability> provideCaps = convertProvideCapabilities(normalizeCapabilityClauses(provideClauses, getManifestVersion()));
+
+        // Combine all requirements.
+        m_requirements = new ArrayList<>();
+        m_requirements.addAll(hostReqs);
+        m_requirements.addAll(rbReqs);
+        m_requirements.addAll(requireReqs);
+
+        // Combine all capabilities.
+        m_capabilities = new ArrayList<>();
+        m_capabilities.addAll(capList);
+        m_capabilities.addAll(provideCaps);
+    }
+
+
+    private static List<Requirement> convertRequireCapabilities(
+            List<ParsedHeaderClause> clauses)
+            throws BundleException
+    {
+        // Now convert generic header clauses into requirements.
+        List<Requirement> reqList = new ArrayList<>();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                if (path.startsWith("osgi.wiring."))
+                {
+                    throw new BundleException("Manifest cannot use Require-Capability for '"
+                            + path
+                            + "' namespace.");
+                }
+
+                Requirement req = new Requirement(path);
+                req.getAttributes().putAll(clause.m_attrs);
+                req.getDirectives().putAll(clause.m_dirs);
+                // Create requirement and add to requirement list.
+                reqList.add(req);
+            }
+        }
+
+        return reqList;
+    }
+
+    private static List<ParsedHeaderClause> normalizeCapabilityClauses(
+            List<ParsedHeaderClause> clauses, String mv)
+            throws BundleException
+    {
+
+        if (!mv.equals("2") && !clauses.isEmpty())
+        {
+            // Should we error here if we are not an R4 bundle?
+        }
+
+        // Convert attributes into specified types.
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (Entry<String, String> entry : clause.m_types.entrySet())
+            {
+                String type = entry.getValue();
+                if (!type.equals("String"))
+                {
+                    if (type.equals("Double"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Double(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Version"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Version(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.equals("Long"))
+                    {
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                new Long(clause.m_attrs.get(entry.getKey()).toString().trim()));
+                    }
+                    else if (type.startsWith("List"))
+                    {
+                        int startIdx = type.indexOf('<');
+                        int endIdx = type.indexOf('>');
+                        if (((startIdx > 0) && (endIdx <= startIdx))
+                                || ((startIdx < 0) && (endIdx > 0)))
+                        {
+                            throw new BundleException(
+                                    "Invalid Provide-Capability attribute list type for '"
+                                            + entry.getKey()
+                                            + "' : "
+                                            + type);
+                        }
+
+                        String listType = "String";
+                        if (endIdx > startIdx)
+                        {
+                            listType = type.substring(startIdx + 1, endIdx).trim();
+                        }
+
+                        List<String> tokens = parseDelimitedString(
+                                clause.m_attrs.get(entry.getKey()).toString(), ",", false);
+                        List<Object> values = new ArrayList<Object>(tokens.size());
+                        for (String token : tokens)
+                        {
+                            if (listType.equals("String"))
+                            {
+                                values.add(token);
+                            }
+                            else if (listType.equals("Double"))
+                            {
+                                values.add(new Double(token.trim()));
+                            }
+                            else if (listType.equals("Version"))
+                            {
+                                values.add(new Version(token.trim()));
+                            }
+                            else if (listType.equals("Long"))
+                            {
+                                values.add(new Long(token.trim()));
+                            }
+                            else
+                            {
+                                throw new BundleException(
+                                        "Unknown Provide-Capability attribute list type for '"
+                                                + entry.getKey()
+                                                + "' : "
+                                                + type);
+                            }
+                        }
+                        clause.m_attrs.put(
+                                entry.getKey(),
+                                values);
+                    }
+                    else
+                    {
+                        throw new BundleException(
+                                "Unknown Provide-Capability attribute type for '"
+                                        + entry.getKey()
+                                        + "' : "
+                                        + type);
+                    }
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<Capability> convertProvideCapabilities(
+            List<ParsedHeaderClause> clauses)
+            throws BundleException
+    {
+        List<Capability> capList = new ArrayList<Capability>();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                if (path.startsWith("osgi.wiring."))
+                {
+                    throw new BundleException("Manifest cannot use Provide-Capability for '"
+                            + path
+                            + "' namespace.");
+                }
+
+                Capability capability = new Capability(path);
+                capability.getAttributes().putAll(clause.m_attrs);
+                capability.getDirectives().putAll(clause.m_dirs);
+                // Create package capability and add to capability list.
+                capList.add(capability);
+            }
+        }
+
+        return capList;
+    }
+
+
+    public String getManifestVersion()
+    {
+        String manifestVersion = getManifestVersion(m_headerMap);
+        return (manifestVersion == null) ? "1" : manifestVersion;
+    }
+
+    private static String getManifestVersion(Manifest headerMap)
+    {
+        String manifestVersion = headerMap.getMainAttributes().getValue(Constants.BUNDLE_MANIFESTVERSION);
+        return (manifestVersion == null) ? null : manifestVersion.trim();
+    }
+
+    public String getSymbolicName()
+    {
+        return m_bundleSymbolicName;
+    }
+
+    public Version getBundleVersion()
+    {
+        return m_bundleVersion;
+    }
+
+    public List<Capability> getCapabilities()
+    {
+        return m_capabilities;
+    }
+
+    public List<Requirement> getRequirements()
+    {
+        return m_requirements;
+    }
+
+
+    static class ParsedHeaderClause
+    {
+        public final List<String> m_paths;
+        public final Map<String, String> m_dirs;
+        public final Map<String, Object> m_attrs;
+        public final Map<String, String> m_types;
+
+        public ParsedHeaderClause(
+                List<String> paths, Map<String, String> dirs, Map<String, Object> attrs,
+                Map<String, String> types)
+        {
+            m_paths = paths;
+            m_dirs = dirs;
+            m_attrs = attrs;
+            m_types = types;
+        }
+    }
+
+    private static Capability parseBundleSymbolicName(
+            Manifest headerMap)
+            throws BundleException
+    {
+        List<ParsedHeaderClause> clauses = parseStandardHeader(headerMap.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME));
+        if (clauses.size() > 0)
+        {
+            if (clauses.size() > 1)
+            {
+                throw new BundleException(
+                        "Cannot have multiple symbolic names: "
+                                + headerMap.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME));
+            }
+            else if (clauses.get(0).m_paths.size() > 1)
+            {
+                throw new BundleException(
+                        "Cannot have multiple symbolic names: "
+                                + headerMap.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME));
+            }
+
+            // Get bundle version.
+            Version bundleVersion = Version.emptyVersion;
+            if (headerMap.getMainAttributes().getValue(Constants.BUNDLE_VERSION) != null)
+            {
+                try
+                {
+                    bundleVersion = Version.parseVersion(
+                            headerMap.getMainAttributes().getValue(Constants.BUNDLE_VERSION));
+                }
+                catch (RuntimeException ex)
+                {
+                    // R4 bundle versions must parse, R3 bundle version may not.
+                    String mv = getManifestVersion(headerMap);
+                    if (mv != null)
+                    {
+                        throw ex;
+                    }
+                    bundleVersion = Version.emptyVersion;
+                }
+            }
+
+            // Create a require capability and return it.
+            String symName = clauses.get(0).m_paths.get(0);
+            clauses.get(0).m_attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
+            clauses.get(0).m_attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
+            Capability cap = new Capability(BundleRevision.BUNDLE_NAMESPACE);
+            cap.getAttributes().putAll(clauses.get(0).m_attrs);
+            cap.getAttributes().putAll(clauses.get(0).m_dirs);
+        }
+
+        return null;
+    }
+
+    private static Capability addIdentityCapability(Manifest headerMap, Capability bundleCap)
+    {
+        Map<String, Object> attrs = new HashMap<String, Object>();
+
+        attrs.put(IdentityNamespace.IDENTITY_NAMESPACE,
+                bundleCap.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE));
+        attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE,
+                headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST) == null
+                        ? IdentityNamespace.TYPE_BUNDLE
+                        : IdentityNamespace.TYPE_FRAGMENT);
+        attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+                bundleCap.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE));
+
+        if (headerMap.getMainAttributes().getValue(Constants.BUNDLE_COPYRIGHT) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_COPYRIGHT_ATTRIBUTE,
+                    headerMap.getMainAttributes().getValue(Constants.BUNDLE_COPYRIGHT));
+        }
+
+        if (headerMap.getMainAttributes().getValue(Constants.BUNDLE_DESCRIPTION) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_DESCRIPTION_ATTRIBUTE,
+                    headerMap.getMainAttributes().getValue(Constants.BUNDLE_DESCRIPTION));
+        }
+        if (headerMap.getMainAttributes().getValue(Constants.BUNDLE_DOCURL) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_DOCUMENTATION_ATTRIBUTE,
+                    headerMap.getMainAttributes().getValue(Constants.BUNDLE_DOCURL));
+        }
+        if (headerMap.getMainAttributes().getValue(BUNDLE_LICENSE_HEADER) != null)
+        {
+            attrs.put(IdentityNamespace.CAPABILITY_LICENSE_ATTRIBUTE,
+                    headerMap.getMainAttributes().getValue(BUNDLE_LICENSE_HEADER));
+        }
+
+        Map<String, String> dirs;
+        if (bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE) != null)
+        {
+            dirs = Collections.singletonMap(IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE,
+                    (String) bundleCap.getDirectives().get(Constants.SINGLETON_DIRECTIVE));
+        }
+        else
+        {
+            dirs = Collections.emptyMap();
+        }
+        Capability cap = new Capability(IdentityNamespace.IDENTITY_NAMESPACE);
+        cap.getAttributes().putAll(attrs);
+        cap.getDirectives().putAll(dirs);
+        return cap;
+    }
+
+    private static List<Requirement> parseFragmentHost(
+            Manifest headerMap)
+            throws BundleException
+    {
+        List<Requirement> reqs = new ArrayList<>();
+
+        String mv = getManifestVersion(headerMap);
+        if ((mv != null) && mv.equals("2"))
+        {
+            List<ParsedHeaderClause> clauses = parseStandardHeader(
+                    headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST));
+            if (clauses.size() > 0)
+            {
+                // Make sure that only one fragment host symbolic name is specified.
+                if (clauses.size() > 1)
+                {
+                    throw new BundleException(
+                            "Fragments cannot have multiple hosts: "
+                                    + headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST));
+                }
+                else if (clauses.get(0).m_paths.size() > 1)
+                {
+                    throw new BundleException(
+                            "Fragments cannot have multiple hosts: "
+                                    + headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST));
+                }
+
+                // If the bundle-version attribute is specified, then convert
+                // it to the proper type.
+                Object value = clauses.get(0).m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                value = (value == null) ? "0.0.0" : value;
+                if (value != null)
+                {
+                    clauses.get(0).m_attrs.put(
+                            Constants.BUNDLE_VERSION_ATTRIBUTE,
+                            VersionRange.parse(value.toString()));
+                }
+
+                // Note that we use a linked hash map here to ensure the
+                // host symbolic name is first, which will make indexing
+                // more efficient.
+// TODO: OSGi R4.3 - This is ordering is kind of hacky.
+                // Prepend the host symbolic name to the map of attributes.
+                Map<String, Object> attrs = clauses.get(0).m_attrs;
+                Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+                // We want this first from an indexing perspective.
+                newAttrs.put(
+                        BundleRevision.HOST_NAMESPACE,
+                        clauses.get(0).m_paths.get(0));
+                newAttrs.putAll(attrs);
+                // But we need to put it again to make sure it wasn't overwritten.
+                newAttrs.put(
+                        BundleRevision.HOST_NAMESPACE,
+                        clauses.get(0).m_paths.get(0));
+
+                // Create filter now so we can inject filter directive.
+                SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+                // Inject filter directive.
+// TODO: OSGi R4.3 - Can we insert this on demand somehow?
+                Map<String, String> dirs = clauses.get(0).m_dirs;
+                Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+                newDirs.putAll(dirs);
+                newDirs.put(
+                        Constants.FILTER_DIRECTIVE,
+                        sf.toString());
+
+                Requirement req = new Requirement(BundleRevision.HOST_NAMESPACE);
+                req.getAttributes().putAll(newAttrs);
+                req.getDirectives().putAll(newDirs);
+                reqs.add(req);
+            }
+        }
+        else if (headerMap.getMainAttributes().getValue(Constants.FRAGMENT_HOST) != null)
+        {
+            String s = headerMap.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+            s = (s == null) ? headerMap.getMainAttributes().getValue(Constants.BUNDLE_NAME) : s;
+            s = (s == null) ? headerMap.toString() : s;
+        }
+
+        return reqs;
+    }
+
+
+    private static List<Requirement> parseBreeHeader(String header)
+    {
+        List<String> filters = new ArrayList<String>();
+        for (String entry : parseDelimitedString(header, ","))
+        {
+            List<String> names = parseDelimitedString(entry, "/");
+            List<String> left = parseDelimitedString(names.get(0), "-");
+
+            String lName = left.get(0);
+            Version lVer;
+            try
+            {
+                lVer = Version.parseVersion(left.get(1));
+            }
+            catch (Exception ex)
+            {
+                // Version doesn't parse. Make it part of the name.
+                lName = names.get(0);
+                lVer = null;
+            }
+
+            String rName = null;
+            Version rVer = null;
+            if (names.size() > 1)
+            {
+                List<String> right = parseDelimitedString(names.get(1), "-");
+                rName = right.get(0);
+                try
+                {
+                    rVer = Version.parseVersion(right.get(1));
+                }
+                catch (Exception ex)
+                {
+                    rName = names.get(1);
+                    rVer = null;
+                }
+            }
+
+            String versionClause;
+            if (lVer != null)
+            {
+                if ((rVer != null) && (!rVer.equals(lVer)))
+                {
+                    // Both versions are defined, but different. Make each of them part of the name
+                    lName = names.get(0);
+                    rName = names.get(1);
+                    versionClause = null;
+                }
+                else
+                {
+                    versionClause = getBreeVersionClause(lVer);
+                }
+            }
+            else
+            {
+                versionClause = getBreeVersionClause(rVer);
+            }
+
+            if ("J2SE".equals(lName))
+            {
+                // J2SE is not used in the Capability variant of BREE, use JavaSE here
+                // This can only happen with the lName part...
+                lName = "JavaSE";
+            }
+
+            String nameClause;
+            if (rName != null)
+                nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + "/" + rName + ")";
+            else
+                nameClause = "(" + ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE + "=" + lName + ")";
+
+            String filter;
+            if (versionClause != null)
+                filter = "(&" + nameClause + versionClause + ")";
+            else
+                filter = nameClause;
+
+            filters.add(filter);
+        }
+
+        if (filters.size() == 0)
+        {
+            return Collections.emptyList();
+        }
+        else
+        {
+            String reqFilter;
+            if (filters.size() == 1)
+            {
+                reqFilter = filters.get(0);
+            }
+            else
+            {
+                // If there are more BREE filters, we need to or them together
+                StringBuilder sb = new StringBuilder("(|");
+                for (String f : filters)
+                {
+                    sb.append(f);
+                }
+                sb.append(")");
+                reqFilter = sb.toString();
+            }
+
+            SimpleFilter sf = SimpleFilter.parse(reqFilter);
+            Requirement req = new Requirement(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE);
+            req.getDirectives().put(ExecutionEnvironmentNamespace.REQUIREMENT_FILTER_DIRECTIVE, reqFilter);
+            return Collections.<Requirement>singletonList(req);
+        }
+    }
+
+    private static String getBreeVersionClause(Version ver) {
+        if (ver == null)
+            return null;
+
+        return "(" + ExecutionEnvironmentNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + ver + ")";
+    }
+
+    private static List<ParsedHeaderClause> normalizeRequireClauses(List<ParsedHeaderClause> clauses, String mv)
+    {
+        // R3 bundles cannot require other bundles.
+        if (!mv.equals("2"))
+        {
+            clauses.clear();
+        }
+        else
+        {
+            // Convert bundle version attribute to VersionRange type.
+            for (ParsedHeaderClause clause : clauses)
+            {
+                Object value = clause.m_attrs.get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+                if (value != null)
+                {
+                    clause.m_attrs.put(
+                            Constants.BUNDLE_VERSION_ATTRIBUTE,
+                            VersionRange.parse(value.toString()));
+                }
+            }
+        }
+
+        return clauses;
+    }
+
+    private static List<Requirement> convertRequires(
+            List<ParsedHeaderClause> clauses)
+    {
+        List<Requirement> reqList = new ArrayList<>();
+        for (ParsedHeaderClause clause : clauses)
+        {
+            for (String path : clause.m_paths)
+            {
+                // Prepend the bundle symbolic name to the array of attributes.
+                Map<String, Object> attrs = clause.m_attrs;
+                // Note that we use a linked hash map here to ensure the
+                // symbolic name attribute is first, which will make indexing
+                // more efficient.
+// TODO: OSGi R4.3 - This is ordering is kind of hacky.
+                // Prepend the symbolic name to the array of attributes.
+                Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
+                // We want this first from an indexing perspective.
+                newAttrs.put(
+                        BundleRevision.BUNDLE_NAMESPACE,
+                        path);
+                newAttrs.putAll(attrs);
+                // But we need to put it again to make sure it wasn't overwritten.
+                newAttrs.put(
+                        BundleRevision.BUNDLE_NAMESPACE,
+                        path);
+
+                // Create filter now so we can inject filter directive.
+                SimpleFilter sf = SimpleFilter.convert(newAttrs);
+
+                // Inject filter directive.
+// TODO: OSGi R4.3 - Can we insert this on demand somehow?
+                Map<String, String> dirs = clause.m_dirs;
+                Map<String, String> newDirs = new HashMap<String, String>(dirs.size() + 1);
+                newDirs.putAll(dirs);
+                newDirs.put(
+                        Constants.FILTER_DIRECTIVE,
+                        sf.toString());
+
+                Requirement req = new Requirement(BundleRevision.BUNDLE_NAMESPACE);
+                req.getAttributes().putAll(newAttrs);
+                req.getDirectives().putAll(newDirs);
+                reqList.add(req);
+            }
+        }
+
+        return reqList;
+    }
+
+    private static final char EOF = (char) -1;
+
+    private static char charAt(int pos, String headers, int length)
+    {
+        if (pos >= length)
+        {
+            return EOF;
+        }
+        return headers.charAt(pos);
+    }
+
+    private static final int CLAUSE_START = 0;
+    private static final int PARAMETER_START = 1;
+    private static final int KEY = 2;
+    private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+    private static final int ARGUMENT = 8;
+    private static final int VALUE = 16;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static List<ParsedHeaderClause> parseStandardHeader(String header)
+    {
+        List<ParsedHeaderClause> clauses = new ArrayList<ParsedHeaderClause>();
+        if (header == null)
+        {
+            return clauses;
+        }
+        ParsedHeaderClause clause = null;
+        String key = null;
+        Map targetMap = null;
+        int state = CLAUSE_START;
+        int currentPosition = 0;
+        int startPosition = 0;
+        int length = header.length();
+        boolean quoted = false;
+        boolean escaped = false;
+
+        char currentChar = EOF;
+        do
+        {
+            currentChar = charAt(currentPosition, header, length);
+            switch (state)
+            {
+                case CLAUSE_START:
+                    clause = new ParsedHeaderClause(
+                            new ArrayList<>(),
+                            new HashMap<>(),
+                            new HashMap<>(),
+                            new HashMap<>());
+                    clauses.add(clause);
+                    state = PARAMETER_START;
+                case PARAMETER_START:
+                    startPosition = currentPosition;
+                    state = KEY;
+                case KEY:
+                    switch (currentChar)
+                    {
+                        case ':':
+                        case '=':
+                            key = header.substring(startPosition, currentPosition).trim();
+                            startPosition = currentPosition + 1;
+                            targetMap = clause.m_attrs;
+                            state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT;
+                            break;
+                        case EOF:
+                        case ',':
+                        case ';':
+                            clause.m_paths.add(header.substring(startPosition, currentPosition).trim());
+                            state = currentChar == ',' ? CLAUSE_START : PARAMETER_START;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case DIRECTIVE_OR_TYPEDATTRIBUTE:
+                    switch(currentChar)
+                    {
+                        case '=':
+                            if (startPosition != currentPosition)
+                            {
+                                clause.m_types.put(key, header.substring(startPosition, currentPosition).trim());
+                            }
+                            else
+                            {
+                                targetMap = clause.m_dirs;
+                            }
+                            state = ARGUMENT;
+                            startPosition = currentPosition + 1;
+                            break;
+                        default:
+                            break;
+                    }
+                    currentPosition++;
+                    break;
+                case ARGUMENT:
+                    if (currentChar == '\"')
+                    {
+                        quoted = true;
+                        currentPosition++;
+                    }
+                    else
+                    {
+                        quoted = false;
+                    }
+                    if (!Character.isWhitespace(currentChar)) {
+                        state = VALUE;
+                    }
+                    else {
+                        currentPosition++;
+                    }
+                    break;
+                case VALUE:
+                    if (escaped)
+                    {
+                        escaped = false;
+                    }
+                    else
+                    {
+                        if (currentChar == '\\' )
+                        {
+                            escaped = true;
+                        }
+                        else if (quoted && currentChar == '\"')
+                        {
+                            quoted = false;
+                        }
+                        else if (!quoted)
+                        {
+                            String value = null;
+                            switch(currentChar)
+                            {
+                                case EOF:
+                                case ';':
+                                case ',':
+                                    value = header.substring(startPosition, currentPosition).trim();
+                                    if (value.startsWith("\"") && value.endsWith("\""))
+                                    {
+                                        value = value.substring(1, value.length() - 1);
+                                    }
+                                    if (targetMap.put(key, value) != null)
+                                    {
+                                        throw new IllegalArgumentException(
+                                                "Duplicate '" + key + "' in: " + header);
+                                    }
+                                    state = currentChar == ';' ? PARAMETER_START : CLAUSE_START;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                    currentPosition++;
+                    break;
+                default:
+                    break;
+            }
+        } while ( currentChar != EOF);
+
+        if (state > PARAMETER_START)
+        {
+            throw new IllegalArgumentException("Unable to parse header: " + header);
+        }
+        return clauses;
+    }
+
+    public static List<String> parseDelimitedString(String value, String delim)
+    {
+        return parseDelimitedString(value, delim, true);
+    }
+
+    /**
+     * Parses delimited string and returns an array containing the tokens. This
+     * parser obeys quotes, so the delimiter character will be ignored if it is
+     * inside of a quote. This method assumes that the quote character is not
+     * included in the set of delimiter characters.
+     * @param value the delimited string to parse.
+     * @param delim the characters delimiting the tokens.
+     * @return a list of string or an empty list if there are none.
+     **/
+    public static List<String> parseDelimitedString(String value, String delim, boolean trim)
+    {
+        if (value == null)
+        {
+            value = "";
+        }
+
+        List<String> list = new ArrayList<String>();
+
+        int CHAR = 1;
+        int DELIMITER = 2;
+        int STARTQUOTE = 4;
+        int ENDQUOTE = 8;
+
+        StringBuffer sb = new StringBuffer();
+
+        int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+        boolean isEscaped = false;
+        for (int i = 0; i < value.length(); i++)
+        {
+            char c = value.charAt(i);
+
+            boolean isDelimiter = (delim.indexOf(c) >= 0);
+
+            if (!isEscaped && (c == '\\'))
+            {
+                isEscaped = true;
+                continue;
+            }
+
+            if (isEscaped)
+            {
+                sb.append(c);
+            }
+            else if (isDelimiter && ((expecting & DELIMITER) > 0))
+            {
+                if (trim)
+                {
+                    list.add(sb.toString().trim());
+                }
+                else
+                {
+                    list.add(sb.toString());
+                }
+                sb.delete(0, sb.length());
+                expecting = (CHAR | DELIMITER | STARTQUOTE);
+            }
+            else if ((c == '"') && ((expecting & STARTQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = CHAR | ENDQUOTE;
+            }
+            else if ((c == '"') && ((expecting & ENDQUOTE) > 0))
+            {
+                sb.append(c);
+                expecting = (CHAR | STARTQUOTE | DELIMITER);
+            }
+            else if ((expecting & CHAR) > 0)
+            {
+                sb.append(c);
+            }
+            else
+            {
+                throw new IllegalArgumentException("Invalid delimited string: " + value);
+            }
+
+            isEscaped = false;
+        }
+
+        if (sb.length() > 0)
+        {
+            if (trim)
+            {
+                list.add(sb.toString().trim());
+            }
+            else
+            {
+                list.add(sb.toString());
+            }
+        }
+
+        return list;
+    }
+}



Mime
View raw message