You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2018/04/23 15:08:07 UTC

[sling-whiteboard] branch master updated: Pick up CapabilityMatcher functionality from Felix Utils

This is an automated email from the ASF dual-hosted git repository.

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new c1747d1  Pick up CapabilityMatcher functionality from Felix Utils
c1747d1 is described below

commit c1747d144c18b8172dfdfd7ed9dde838d223519a
Author: David Bosschaert <da...@gmail.com>
AuthorDate: Mon Apr 23 16:07:23 2018 +0100

    Pick up CapabilityMatcher functionality from Felix Utils
---
 featuremodel/feature-analyser/pom.xml              |  16 +-
 .../task/impl/CheckRequirementsCapabilities.java   |  19 +-
 .../feature/support/util/CapabilityMatcher.java    | 524 +--------------------
 3 files changed, 25 insertions(+), 534 deletions(-)

diff --git a/featuremodel/feature-analyser/pom.xml b/featuremodel/feature-analyser/pom.xml
index 28bb660..3179891 100644
--- a/featuremodel/feature-analyser/pom.xml
+++ b/featuremodel/feature-analyser/pom.xml
@@ -133,8 +133,8 @@
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
-		    <artifactId>org.apache.felix.converter</artifactId>
-		    <version>0.1.0-SNAPSHOT</version>
+	    <artifactId>org.apache.felix.converter</artifactId>
+	    <version>0.1.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -143,6 +143,12 @@
             <version>0.0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>1.11.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
         
         <!-- Testing -->
         <dependency>
@@ -150,11 +156,5 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.utils</artifactId>
-            <version>1.11.0-SNAPSHOT</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 </project>
diff --git a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
index 77e270b..daa0a91 100644
--- a/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
+++ b/featuremodel/feature-analyser/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckRequirementsCapabilities.java
@@ -16,6 +16,14 @@
  */
 package org.apache.sling.feature.analyser.task.impl;
 
+import org.apache.felix.utils.resource.CapabilitySet;
+import org.apache.felix.utils.resource.RequirementImpl;
+import org.apache.sling.feature.analyser.task.AnalyserTask;
+import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
+import org.apache.sling.feature.scanner.ArtifactDescriptor;
+import org.apache.sling.feature.scanner.BundleDescriptor;
+import org.osgi.resource.Requirement;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -23,13 +31,6 @@ import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 
-import org.apache.sling.feature.analyser.task.AnalyserTask;
-import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
-import org.apache.sling.feature.scanner.ArtifactDescriptor;
-import org.apache.sling.feature.scanner.BundleDescriptor;
-import org.apache.sling.feature.support.util.CapabilityMatcher;
-import org.osgi.resource.Requirement;
-
 public class CheckRequirementsCapabilities implements AnalyserTask {
     private final String format = "Artifact %s:%s requires %s in start level %d but %s";
 
@@ -75,7 +76,7 @@ public class CheckRequirementsCapabilities implements AnalyserTask {
                                 // osgi.service is special - we don't provide errors or warnings in this case
                                 continue;
                             }
-                            if (!CapabilityMatcher.isOptional(requirement)) {
+                            if (!RequirementImpl.isOptional(requirement)) {
                                 ctx.reportError(String.format(format, info.getArtifact().getId().getArtifactId(), info.getArtifact().getId().getVersion(), requirement.toString(), entry.getKey(), "no artifact is providing a matching capability in this start level."));
                             }
                             else {
@@ -94,7 +95,7 @@ public class CheckRequirementsCapabilities implements AnalyserTask {
     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)))
+                .filter(artifactDescriptor -> artifactDescriptor.getCapabilities().stream().anyMatch(capability -> CapabilitySet.matches(capability, requirement)))
                 .collect(Collectors.toList());
     }
 }
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
index e476373..3c574a2 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
+++ b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/util/CapabilityMatcher.java
@@ -16,529 +16,19 @@
  */
 package org.apache.sling.feature.support.util;
 
-import static org.osgi.framework.Constants.RESOLUTION_DIRECTIVE;
-import static org.osgi.framework.Constants.RESOLUTION_OPTIONAL;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.VersionRange;
+import org.apache.felix.utils.resource.CapabilitySet;
 import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
+
 
 public class CapabilityMatcher
 {
     static boolean matches(Capability cap, SimpleFilter sf)
     {
-        return matchesInternal(cap, sf) && matchMandatory(cap, sf);
-    }
-
-    private static boolean matchesInternal(Capability cap, SimpleFilter sf)
-    {
-        boolean matched = true;
-
-        if (sf.getOperation() == SimpleFilter.MATCH_ALL)
-        {
-            matched = true;
-        }
-        else if (sf.getOperation() == SimpleFilter.AND)
-        {
-            // Evaluate each subfilter against the remaining capabilities.
-            // For AND we calculate the intersection of each subfilter.
-            // We can short-circuit the AND operation if there are no
-            // remaining capabilities.
-            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (int i = 0; matched && (i < sfs.size()); i++)
-            {
-                matched = matchesInternal(cap, sfs.get(i));
-            }
-        }
-        else if (sf.getOperation() == SimpleFilter.OR)
-        {
-            // Evaluate each subfilter against the remaining capabilities.
-            // For OR we calculate the union of each subfilter.
-            matched = false;
-            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (int i = 0; !matched && (i < sfs.size()); i++)
-            {
-                matched = matchesInternal(cap, sfs.get(i));
-            }
-        }
-        else if (sf.getOperation() == SimpleFilter.NOT)
-        {
-            // Evaluate each subfilter against the remaining capabilities.
-            // For OR we calculate the union of each subfilter.
-            List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
-            for (int i = 0; i < sfs.size(); i++)
-            {
-                matched = !(matchesInternal(cap, sfs.get(i)));
-            }
-        }
-        else
-        {
-            matched = false;
-            Object lhs = cap.getAttributes().get(sf.getName());
-            if (lhs != null)
-            {
-                matched = compare(lhs, sf.getValue(), sf.getOperation());
-            }
-        }
-
-        return matched;
-    }
-
-    private static boolean matchMandatory(Capability cap, SimpleFilter sf)
-    {
-        Map<String, Object> attrs = cap.getAttributes();
-        for (Entry<String, Object> entry : attrs.entrySet())
-        {
-            if (isAttributeMandatory(cap, entry.getKey())
-                    && !matchMandatoryAttribute(entry.getKey(), sf))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static boolean matchMandatoryAttribute(String attrName, SimpleFilter sf)
-    {
-        if ((sf.getName() != null) && sf.getName().equals(attrName))
-        {
-            return true;
-        }
-        else if (sf.getOperation() == SimpleFilter.AND)
-        {
-            List list = (List) sf.getValue();
-            for (int i = 0; i < list.size(); i++)
-            {
-                SimpleFilter sf2 = (SimpleFilter) list.get(i);
-                if ((sf2.getName() != null)
-                        && sf2.getName().equals(attrName))
-                {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private static final Class<?>[] STRING_CLASS = new Class[] { String.class };
-    private static final String VALUE_OF_METHOD_NAME = "valueOf";
-
-    private static boolean compare(Object lhs, Object rhsUnknown, int op)
-    {
-        if (lhs == null)
-        {
-            return false;
-        }
-
-        // If this is a PRESENT operation, then just return true immediately
-        // since we wouldn't be here if the attribute wasn't present.
-        if (op == SimpleFilter.PRESENT)
-        {
-            return true;
-        }
-
-        //Need a special case here when lhs is a Version and rhs is a VersionRange
-        //Version is comparable so we need to check this first
-        if(lhs instanceof Version && op == SimpleFilter.EQ)
-        {
-            Object rhs = null;
-            try
-            {
-                rhs = coerceType(lhs, (String) rhsUnknown);
-            }
-            catch (Exception ex)
-            {
-                //Do nothing will check later if rhs is null
-            }
-
-            if(rhs != null && rhs instanceof VersionRange)
-            {
-                return ((VersionRange)rhs).includes((Version)lhs);
-            }
-        }
-
-        // If the type is comparable, then we can just return the
-        // result immediately.
-        if (lhs instanceof Comparable)
-        {
-            // Spec says SUBSTRING is false for all types other than string.
-            if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
-            {
-                return false;
-            }
-
-            Object rhs;
-            if (op == SimpleFilter.SUBSTRING)
-            {
-                rhs = rhsUnknown;
-            }
-            else
-            {
-                try
-                {
-                    rhs = coerceType(lhs, (String) rhsUnknown);
-                }
-                catch (Exception ex)
-                {
-                    return false;
-                }
-            }
-
-            switch (op)
-            {
-                case SimpleFilter.EQ :
-                    try
-                    {
-                        return (((Comparable) lhs).compareTo(rhs) == 0);
-                    }
-                    catch (Exception ex)
-                    {
-                        return false;
-                    }
-                case SimpleFilter.GTE :
-                    try
-                    {
-                        return (((Comparable) lhs).compareTo(rhs) >= 0);
-                    }
-                    catch (Exception ex)
-                    {
-                        return false;
-                    }
-                case SimpleFilter.LTE :
-                    try
-                    {
-                        return (((Comparable) lhs).compareTo(rhs) <= 0);
-                    }
-                    catch (Exception ex)
-                    {
-                        return false;
-                    }
-                case SimpleFilter.APPROX :
-                    return compareApproximate(lhs, rhs);
-                case SimpleFilter.SUBSTRING :
-                    return SimpleFilter.compareSubstring((List<String>) rhs, (String) lhs);
-                default:
-                    throw new RuntimeException(
-                            "Unknown comparison operator: " + op);
-            }
-        }
-        // Booleans do not implement comparable, so special case them.
-        else if (lhs instanceof Boolean)
-        {
-            Object rhs;
-            try
-            {
-                rhs = coerceType(lhs, (String) rhsUnknown);
-            }
-            catch (Exception ex)
-            {
-                return false;
-            }
-
-            switch (op)
-            {
-                case SimpleFilter.EQ :
-                case SimpleFilter.GTE :
-                case SimpleFilter.LTE :
-                case SimpleFilter.APPROX :
-                    return (lhs.equals(rhs));
-                default:
-                    throw new RuntimeException(
-                            "Unknown comparison operator: " + op);
-            }
-        }
-
-        // If the LHS is not a comparable or boolean, check if it is an
-        // array. If so, convert it to a list so we can treat it as a
-        // collection.
-        if (lhs.getClass().isArray())
-        {
-            lhs = convertArrayToList(lhs);
-        }
-
-        // If LHS is a collection, then call compare() on each element
-        // of the collection until a match is found.
-        if (lhs instanceof Collection)
-        {
-            for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext(); )
-            {
-                if (compare(iter.next(), rhsUnknown, op))
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        // Spec says SUBSTRING is false for all types other than string.
-        if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
-        {
-            return false;
-        }
-
-        // Since we cannot identify the LHS type, then we can only perform
-        // equality comparison.
-        try
-        {
-            return lhs.equals(coerceType(lhs, (String) rhsUnknown));
-        }
-        catch (Exception ex)
-        {
-            return false;
-        }
-    }
-
-    private static boolean compareApproximate(Object lhs, Object rhs)
-    {
-        if (rhs instanceof String)
-        {
-            return removeWhitespace((String) lhs)
-                    .equalsIgnoreCase(removeWhitespace((String) rhs));
-        }
-        else if (rhs instanceof Character)
-        {
-            return Character.toLowerCase(((Character) lhs))
-                    == Character.toLowerCase(((Character) rhs));
-        }
-        return lhs.equals(rhs);
-    }
-
-    private static String removeWhitespace(String s)
-    {
-        StringBuffer sb = new StringBuffer(s.length());
-        for (int i = 0; i < s.length(); i++)
-        {
-            if (!Character.isWhitespace(s.charAt(i)))
-            {
-                sb.append(s.charAt(i));
-            }
-        }
-        return sb.toString();
-    }
-
-    private static Object coerceType(Object lhs, String rhsString) throws Exception
-    {
-        // If the LHS expects a string, then we can just return
-        // the RHS since it is a string.
-        if (lhs.getClass() == rhsString.getClass())
-        {
-            return rhsString;
-        }
-
-        // Try to convert the RHS type to the LHS type by using
-        // the string constructor of the LHS class, if it has one.
-        Object rhs = null;
-        try
-        {
-            // The Character class is a special case, since its constructor
-            // does not take a string, so handle it separately.
-            if (lhs instanceof Character)
-            {
-                rhs = new Character(rhsString.charAt(0));
-            }
-            else if(lhs instanceof Version && rhsString.indexOf(',') >= 0)
-            {
-                rhs = VersionRange.valueOf(rhsString);
-            }
-            else
-            {
-                // Spec says we should trim number types.
-                if ((lhs instanceof Number) || (lhs instanceof Boolean))
-                {
-                    rhsString = rhsString.trim();
-                }
-
-                try
-                {
-                    // Try to find a suitable static valueOf method
-                    Method valueOfMethod = lhs.getClass().getDeclaredMethod(VALUE_OF_METHOD_NAME, STRING_CLASS);
-                    if (valueOfMethod.getReturnType().isAssignableFrom(lhs.getClass())
-                            && ((valueOfMethod.getModifiers() & Modifier.STATIC) > 0))
-                    {
-                        valueOfMethod.setAccessible(true);
-                        rhs = valueOfMethod.invoke(null, new Object[] { rhsString });
-                    }
-                }
-                catch (Exception ex)
-                {
-                    // Static valueOf fails, try the next conversion mechanism
-                }
-
-                if (rhs == null)
-                {
-                    Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
-                    ctor.setAccessible(true);
-                    rhs = ctor.newInstance(new Object[] { rhsString });
-                }
-            }
-        }
-        catch (Exception ex)
-        {
-            throw new Exception(
-                    "Could not instantiate class "
-                            + lhs.getClass().getName()
-                            + " from string constructor with argument '"
-                            + rhsString + "' because " + ex);
-        }
-
-        return rhs;
-    }
-
-    /**
-     * This is an ugly utility method to convert an array of primitives
-     * to an array of primitive wrapper objects. This method simplifies
-     * processing LDAP filters since the special case of primitive arrays
-     * can be ignored.
-     * @param array An array of primitive types.
-     * @return An corresponding array using pritive wrapper objects.
-     **/
-    private static List convertArrayToList(Object array)
-    {
-        int len = Array.getLength(array);
-        List list = new ArrayList(len);
-        for (int i = 0; i < len; i++)
-        {
-            list.add(Array.get(array, i));
-        }
-        return list;
-    }
-
-
-    public static  boolean matches(Capability capability, Requirement requirement) {
-        if (requirement.getNamespace().equals(capability.getNamespace())) {
-            String filter = requirement.getDirectives().get(Constants.FILTER_DIRECTIVE);
-            if (filter != null) {
-                return matches(capability, SimpleFilter.parse(filter));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public static boolean isOptional(Requirement requirement) {
-        return RESOLUTION_OPTIONAL.equals(requirement. getDirectives().get(RESOLUTION_DIRECTIVE));
-    }
-
-    public static boolean isAttributeMandatory(Capability capability, String name)
-    {
-        String value = capability.getDirectives().get(Constants.MANDATORY_DIRECTIVE);
-        if (value != null)
-        {
-            return parseDelimitedString(value, ",").contains(name);
-        }
-        return false;
-    }
-
-    public static List<String> parseDelimitedString(String value, String delim)
-    {
-        return CapabilityMatcher.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<>();
-
-        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());
-            }
-        }
+        // Translate the SimpleFilter into the one from Felix Utils.
+        // Once the SimpleFilter has moved to Felix Utils, this class can be removed.
+        org.apache.felix.utils.resource.SimpleFilter sf2 =
+                org.apache.felix.utils.resource.SimpleFilter.parse(sf.toString());
 
-        return list;
+        return CapabilitySet.matches(cap, sf2);
     }
 }

-- 
To stop receiving notification emails like this one, please contact
davidb@apache.org.