You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2019/04/30 16:21:30 UTC

svn commit: r1858443 [3/3] - in /felix/trunk/connect: ./ src/main/java/org/apache/felix/connect/ src/main/java/org/apache/felix/connect/felix/framework/ src/main/java/org/apache/felix/connect/felix/framework/capabilityset/ src/main/java/org/apache/feli...

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/SimpleFilter.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/SimpleFilter.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/SimpleFilter.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/capabilityset/SimpleFilter.java Tue Apr 30 16:21:29 2019
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -18,8 +18,12 @@
  */
 package org.apache.felix.connect.felix.framework.capabilityset;
 
+import org.osgi.framework.VersionRange;
+
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 public class SimpleFilter
 {
@@ -60,49 +64,51 @@ public class SimpleFilter
         return m_op;
     }
 
-    @SuppressWarnings("unchecked")
     public String toString()
     {
         String s = null;
         switch (m_op)
         {
-        case AND:
-            s = "(&" + toString((List) m_value) + ")";
-            break;
-        case OR:
-            s = "(|" + toString((List) m_value) + ")";
-            break;
-        case NOT:
-            s = "(!" + toString((List) m_value) + ")";
-            break;
-        case EQ:
-            s = "(" + m_name + "=" + toEncodedString(m_value) + ")";
-            break;
-        case LTE:
-            s = "(" + m_name + "<=" + toEncodedString(m_value) + ")";
-            break;
-        case GTE:
-            s = "(" + m_name + ">=" + toEncodedString(m_value) + ")";
-            break;
-        case SUBSTRING:
-            s = "(" + m_name + "=" + unparseSubstring((List<String>) m_value) + ")";
-            break;
-        case PRESENT:
-            s = "(" + m_name + "=*)";
-            break;
-        case APPROX:
-            s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
-            break;
+            case AND:
+                s = "(&" + toString((List) m_value) + ")";
+                break;
+            case OR:
+                s = "(|" + toString((List) m_value) + ")";
+                break;
+            case NOT:
+                s = "(!" + toString((List) m_value) + ")";
+                break;
+            case EQ:
+                s = "(" + m_name + "=" + toEncodedString(m_value) + ")";
+                break;
+            case LTE:
+                s = "(" + m_name + "<=" + toEncodedString(m_value) + ")";
+                break;
+            case GTE:
+                s = "(" + m_name + ">=" + toEncodedString(m_value) + ")";
+                break;
+            case SUBSTRING:
+                s = "(" + m_name + "=" + unparseSubstring((List<String>) m_value) + ")";
+                break;
+            case PRESENT:
+                s = "(" + m_name + "=*)";
+                break;
+            case APPROX:
+                s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
+                break;
+            case MATCH_ALL:
+                s = "(*)";
+                break;
         }
         return s;
     }
 
-    private static String toString(List<?> list)
+    private static String toString(List list)
     {
         StringBuilder sb = new StringBuilder();
-        for (Object aList : list)
+        for (int i = 0; i < list.size(); i++)
         {
-            sb.append(aList.toString());
+            sb.append(list.get(i).toString());
         }
         return sb.toString();
     }
@@ -150,31 +156,28 @@ public class SimpleFilter
         return o.toString();
     }
 
-    @SuppressWarnings("unchecked")
     public static SimpleFilter parse(String filter)
     {
         int idx = skipWhitespace(filter, 0);
 
-        if ((filter == null) || (filter.length() == 0)
-                || (idx >= filter.length()))
+        if ((filter == null) || (filter.length() == 0) || (idx >= filter.length()))
         {
             throw new IllegalArgumentException("Null or empty filter.");
         }
         else if (filter.charAt(idx) != '(')
         {
-            throw new IllegalArgumentException("Missing opening parenthesis: "
-                    + filter);
+            throw new IllegalArgumentException("Missing opening parenthesis: " + filter);
         }
 
         SimpleFilter sf = null;
-        List<Object> stack = new ArrayList<Object>();
+        List stack = new ArrayList();
         boolean isEscaped = false;
         while (idx < filter.length())
         {
             if (sf != null)
             {
                 throw new IllegalArgumentException(
-                        "Only one top-level operation allowed: " + filter);
+                    "Only one top-level operation allowed: " + filter);
             }
 
             if (!isEscaped && (filter.charAt(idx) == '('))
@@ -188,12 +191,11 @@ public class SimpleFilter
                     if (filter.charAt(peek) == '(')
                     {
                         idx = peek - 1;
-                        stack.add(0, new SimpleFilter(null, new ArrayList(),
-                                SimpleFilter.AND));
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.AND));
                     }
                     else
                     {
-                        stack.add(0, idx);
+                        stack.add(0, new Integer(idx));
                     }
                 }
                 else if (filter.charAt(idx) == '|')
@@ -202,12 +204,11 @@ public class SimpleFilter
                     if (filter.charAt(peek) == '(')
                     {
                         idx = peek - 1;
-                        stack.add(0, new SimpleFilter(null, new ArrayList(),
-                                SimpleFilter.OR));
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.OR));
                     }
                     else
                     {
-                        stack.add(0, idx);
+                        stack.add(0, new Integer(idx));
                     }
                 }
                 else if (filter.charAt(idx) == '!')
@@ -216,17 +217,16 @@ public class SimpleFilter
                     if (filter.charAt(peek) == '(')
                     {
                         idx = peek - 1;
-                        stack.add(0, new SimpleFilter(null, new ArrayList(),
-                                SimpleFilter.NOT));
+                        stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT));
                     }
                     else
                     {
-                        stack.add(0, idx);
+                        stack.add(0, new Integer(idx));
                     }
                 }
                 else
                 {
-                    stack.add(0, idx);
+                    stack.add(0, new Integer(idx));
                 }
             }
             else if (!isEscaped && (filter.charAt(idx) == ')'))
@@ -236,7 +236,7 @@ public class SimpleFilter
                 {
                     if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
                     {
-                        ((List<Object>) ((SimpleFilter) stack.get(0)).m_value).add(top);
+                        ((List) ((SimpleFilter) stack.get(0)).m_value).add(top);
                     }
                     else
                     {
@@ -245,17 +245,21 @@ public class SimpleFilter
                 }
                 else if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
                 {
-                    ((List<Object>) ((SimpleFilter) stack.get(0)).m_value)
-                            .add(SimpleFilter.subfilter(filter, (Integer) top, idx));
+                    ((List) ((SimpleFilter) stack.get(0)).m_value).add(
+                        SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx));
                 }
                 else
                 {
-                    sf = SimpleFilter.subfilter(filter, (Integer) top, idx);
+                    sf = SimpleFilter.subfilter(filter, ((Integer) top).intValue(), idx);
                 }
             }
+            else if (!isEscaped && (filter.charAt(idx) == '\\'))
+            {
+                isEscaped = true;
+            }
             else
             {
-                isEscaped = !isEscaped && (filter.charAt(idx) == '\\');
+                isEscaped = false;
             }
 
             idx = skipWhitespace(filter, idx + 1);
@@ -269,8 +273,7 @@ public class SimpleFilter
         return sf;
     }
 
-    private static SimpleFilter subfilter(String filter, int startIdx,
-                                          int endIdx)
+    private static SimpleFilter subfilter(String filter, int startIdx, int endIdx)
     {
         final String opChars = "=<>~";
 
@@ -290,8 +293,8 @@ public class SimpleFilter
         }
         if (attrEndIdx == startIdx)
         {
-            throw new IllegalArgumentException("Missing attribute name: "
-                    + filter.substring(startIdx, endIdx));
+            throw new IllegalArgumentException(
+                "Missing attribute name: " + filter.substring(startIdx, endIdx));
         }
         String attr = filter.substring(startIdx, attrEndIdx);
 
@@ -299,43 +302,43 @@ public class SimpleFilter
         startIdx = skipWhitespace(filter, attrEndIdx);
 
         // Determine the operator type.
-        int op;
+        int op = -1;
         switch (filter.charAt(startIdx))
         {
-        case '=':
-            op = EQ;
-            startIdx++;
-            break;
-        case '<':
-            if (filter.charAt(startIdx + 1) != '=')
-            {
-                throw new IllegalArgumentException("Unknown operator: "
-                        + filter.substring(startIdx, endIdx));
-            }
-            op = LTE;
-            startIdx += 2;
-            break;
-        case '>':
-            if (filter.charAt(startIdx + 1) != '=')
-            {
-                throw new IllegalArgumentException("Unknown operator: "
-                        + filter.substring(startIdx, endIdx));
-            }
-            op = GTE;
-            startIdx += 2;
-            break;
-        case '~':
-            if (filter.charAt(startIdx + 1) != '=')
-            {
-                throw new IllegalArgumentException("Unknown operator: "
-                        + filter.substring(startIdx, endIdx));
-            }
-            op = APPROX;
-            startIdx += 2;
-            break;
-        default:
-            throw new IllegalArgumentException("Unknown operator: "
-                    + filter.substring(startIdx, endIdx));
+            case '=':
+                op = EQ;
+                startIdx++;
+                break;
+            case '<':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = LTE;
+                startIdx += 2;
+                break;
+            case '>':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = GTE;
+                startIdx += 2;
+                break;
+            case '~':
+                if (filter.charAt(startIdx + 1) != '=')
+                {
+                    throw new IllegalArgumentException(
+                        "Unknown operator: " + filter.substring(startIdx, endIdx));
+                }
+                op = APPROX;
+                startIdx += 2;
+                break;
+            default:
+                throw new IllegalArgumentException(
+                    "Unknown operator: " + filter.substring(startIdx, endIdx));
         }
 
         // Parse value.
@@ -347,8 +350,9 @@ public class SimpleFilter
         {
             String valueStr = filter.substring(startIdx, endIdx);
             List<String> values = parseSubstring(valueStr);
-            if ((values.size() == 2) && (values.get(0).length() == 0)
-                    && (values.get(1).length() == 0))
+            if ((values.size() == 2)
+                && (values.get(0).length() == 0)
+                && (values.get(1).length() == 0))
             {
                 op = PRESENT;
             }
@@ -364,7 +368,7 @@ public class SimpleFilter
 
     public static List<String> parseSubstring(String value)
     {
-        List<String> pieces = new ArrayList<String>();
+        List<String> pieces = new ArrayList();
         StringBuilder ss = new StringBuilder();
         // int kind = SIMPLE; // assume until proven otherwise
         boolean wasStar = false; // indicates last piece was a star
@@ -375,7 +379,7 @@ public class SimpleFilter
 
         // We assume (sub)strings can contain leading and trailing blanks
         boolean escaped = false;
-        for (; ; )
+loop:   for (;;)
         {
             if (idx >= value.length())
             {
@@ -393,36 +397,30 @@ public class SimpleFilter
                     // the string "" (!=null)
                 }
                 ss.setLength(0);
-                break;
+                break loop;
             }
 
             // Read the next character and account for escapes.
             char c = value.charAt(idx++);
-            if (!escaped && ((c == '(') || (c == ')')))
+            if (!escaped && (c == '*'))
             {
-                throw new IllegalArgumentException("Illegal value: " + value);
-            }
-            else if (!escaped && (c == '*'))
-            {
-                if (wasStar)
+                // If we have successive '*' characters, then we can
+                // effectively collapse them by ignoring succeeding ones.
+                if (!wasStar)
                 {
-                    // encountered two successive stars;
-                    // I assume this is illegal
-                    throw new IllegalArgumentException(
-                            "Invalid filter string: " + value);
-                }
-                if (ss.length() > 0)
-                {
-                    pieces.add(ss.toString()); // accumulate the pieces
-                    // between '*' occurrences
-                }
-                ss.setLength(0);
-                // if this is a leading star, then track it
-                if (pieces.size() == 0)
-                {
-                    leftstar = true;
+                    if (ss.length() > 0)
+                    {
+                        pieces.add(ss.toString()); // accumulate the pieces
+                        // between '*' occurrences
+                    }
+                    ss.setLength(0);
+                    // if this is a leading star, then track it
+                    if (pieces.isEmpty())
+                    {
+                        leftstar = true;
+                    }
+                    wasStar = true;
                 }
-                wasStar = true;
             }
             else if (!escaped && (c == '\\'))
             {
@@ -487,7 +485,7 @@ public class SimpleFilter
 
         int index = 0;
 
-        for (int i = 0; i < len; i++)
+loop:   for (int i = 0; i < len; i++)
         {
             String piece = pieces.get(i);
 
@@ -498,16 +496,23 @@ public class SimpleFilter
                 if (!s.startsWith(piece))
                 {
                     result = false;
-                    break;
+                    break loop;
                 }
             }
 
             // If this is the last piece, then make sure the
             // string ends with it.
-            if (i == len - 1)
+            if (i == (len - 1))
             {
-                result = s.endsWith(piece);
-                break;
+                if (s.endsWith(piece) && (s.length() >= (index + piece.length())))
+                {
+                    result = true;
+                }
+                else
+                {
+                    result = false;
+                }
+                break loop;
             }
 
             // If this is neither the first or last piece, then
@@ -518,7 +523,7 @@ public class SimpleFilter
                 if (index < 0)
                 {
                     result = false;
-                    break;
+                    break loop;
                 }
             }
 
@@ -538,4 +543,109 @@ public class SimpleFilter
         }
         return startIdx;
     }
+
+    /**
+     * Converts a attribute map to a filter. The filter is created by iterating
+     * over the map's entry set. If ordering of attributes is important (e.g.,
+     * for hitting attribute indices), then the map's entry set should iterate
+     * in the desired order. Equality testing is assumed for all attribute types
+     * other than version ranges, which are handled appropriated. If the attribute
+     * map is empty, then a filter that matches anything is returned.
+     * @param attrs Map of attributes to convert to a filter.
+     * @return A filter corresponding to the attributes.
+     */
+    public static SimpleFilter convert(Map<String, Object> attrs)
+    {
+        // Rather than building a filter string to be parsed into a SimpleFilter,
+        // we will just create the parsed SimpleFilter directly.
+
+        List<SimpleFilter> filters = new ArrayList<SimpleFilter>();
+
+        for (Entry<String, Object> entry : attrs.entrySet())
+        {
+            if (entry.getValue() instanceof VersionRange)
+            {
+                VersionRange vr = (VersionRange) entry.getValue();
+                if (vr.getLeftType() == VersionRange.LEFT_CLOSED)
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            vr.getLeft().toString(),
+                            SimpleFilter.GTE));
+                }
+                else
+                {
+                    SimpleFilter not =
+                        new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+                    ((List) not.getValue()).add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            vr.getLeft().toString(),
+                            SimpleFilter.LTE));
+                    filters.add(not);
+                }
+
+                if (vr.getRight() != null)
+                {
+                    if (vr.getRightType() == VersionRange.RIGHT_CLOSED)
+                    {
+                        filters.add(
+                            new SimpleFilter(
+                                entry.getKey(),
+                                vr.getRight().toString(),
+                                SimpleFilter.LTE));
+                    }
+                    else
+                    {
+                        SimpleFilter not =
+                            new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT);
+                        ((List) not.getValue()).add(
+                            new SimpleFilter(
+                                entry.getKey(),
+                                vr.getRight().toString(),
+                                SimpleFilter.GTE));
+                        filters.add(not);
+                    }
+                }
+            }
+            else
+            {
+                List<String> values = SimpleFilter.parseSubstring(entry.getValue().toString());
+                if (values.size() > 1)
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            values,
+                            SimpleFilter.SUBSTRING));
+                }
+                else
+                {
+                    filters.add(
+                        new SimpleFilter(
+                            entry.getKey(),
+                            values.get(0),
+                            SimpleFilter.EQ));
+                }
+            }
+        }
+
+        SimpleFilter sf = null;
+
+        if (filters.size() == 1)
+        {
+            sf = filters.get(0);
+        }
+        else if (attrs.size() > 1)
+        {
+            sf = new SimpleFilter(null, filters, SimpleFilter.AND);
+        }
+        else if (filters.isEmpty())
+        {
+            sf = new SimpleFilter(null, null, SimpleFilter.MATCH_ALL);
+        }
+
+        return sf;
+    }
 }
\ No newline at end of file

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/EventDispatcher.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/EventDispatcher.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/EventDispatcher.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/EventDispatcher.java Tue Apr 30 16:21:29 2019
@@ -532,7 +532,7 @@ public class EventDispatcher
         }
         // Create a whitelist of bundle context, if we have hooks.
         Set<BundleContext> whitelist = null;
-        Set<ServiceReference<EventHook>> hooks = m_registry.getHooks(EventHook.class);
+        Set<ServiceReference<EventHook>> hooks = m_registry.getHookRegistry().getHooks(EventHook.class);
         if ((hooks != null) && !hooks.isEmpty())
         {
             whitelist = new HashSet<BundleContext>();
@@ -545,7 +545,7 @@ public class EventDispatcher
             {
                 try
                 {
-                    EventHook eh = m_registry.getService(bundle, sr);
+                    EventHook eh = m_registry.getService(bundle, sr, false);
                     if (eh != null)
                     {
                         try
@@ -559,7 +559,7 @@ public class EventDispatcher
                         }
                         finally
                         {
-                            m_registry.ungetService(bundle, sr);
+                            m_registry.ungetService(bundle, sr, null);
                         }
                     }
                 }
@@ -603,9 +603,9 @@ public class EventDispatcher
         }
 
         Set<ServiceReference<org.osgi.framework.hooks.service.EventHook>> ehs =
-                m_registry.getHooks(org.osgi.framework.hooks.service.EventHook.class);
+                m_registry.getHookRegistry().getHooks(org.osgi.framework.hooks.service.EventHook.class);
         Set<ServiceReference<EventListenerHook>> elhs =
-                m_registry.getHooks(EventListenerHook.class);
+                m_registry.getHookRegistry().getHooks(EventListenerHook.class);
 
         if ((ehs == null || ehs.isEmpty()) && (elhs == null || elhs.isEmpty()))
         {
@@ -631,7 +631,7 @@ public class EventDispatcher
             {
                 try
                 {
-                    org.osgi.framework.hooks.service.EventHook eh = m_registry.getService(framework, sr);
+                    org.osgi.framework.hooks.service.EventHook eh = m_registry.getService(framework, sr, false);
                     if (eh != null)
                     {
                         try
@@ -645,7 +645,7 @@ public class EventDispatcher
                         }
                         finally
                         {
-                            m_registry.ungetService(framework, sr);
+                            m_registry.ungetService(framework, sr, null);
                         }
                     }
                 }
@@ -666,7 +666,7 @@ public class EventDispatcher
             {
                 try
                 {
-                    EventListenerHook elh = m_registry.getService(framework, sr);
+                    EventListenerHook elh = m_registry.getService(framework, sr, false);
                     if (elh != null)
                     {
                         try
@@ -680,7 +680,7 @@ public class EventDispatcher
                         }
                         finally
                         {
-                            m_registry.ungetService(framework, sr);
+                            m_registry.ungetService(framework, sr, null);
                         }
                     }
                 }

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringComparator.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringComparator.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringComparator.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringComparator.java Tue Apr 30 16:21:29 2019
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -22,28 +22,52 @@ import java.util.Comparator;
 
 public class StringComparator implements Comparator<String>
 {
-    private final boolean m_isCaseSensitive;
 
-    public StringComparator(boolean b)
-    {
-        m_isCaseSensitive = b;
-    }
+    public static final StringComparator COMPARATOR = new StringComparator();
 
-    @Override
-    public int compare(String o1, String o2)
+    public int compare(String s1, String s2)
     {
-        if (m_isCaseSensitive)
-        {
-            return o1.compareTo(o2);
-        }
-        else
+        int n1 = s1.length();
+        int n2 = s2.length();
+        int min = n1 < n2 ? n1 : n2;
+        for ( int i = 0; i < min; i++ )
         {
-            return o1.compareToIgnoreCase(o2);
+            char c1 = s1.charAt( i );
+            char c2 = s2.charAt( i );
+            if ( c1 != c2 )
+            {
+                // Fast check for simple ascii codes
+                if ( c1 <= 128 && c2 <= 128 )
+                {
+                    c1 = toLowerCaseFast(c1);
+                    c2 = toLowerCaseFast(c2);
+                    if ( c1 != c2 )
+                    {
+                        return c1 - c2;
+                    }
+                }
+                else
+                {
+                    c1 = Character.toUpperCase( c1 );
+                    c2 = Character.toUpperCase( c2 );
+                    if ( c1 != c2 )
+                    {
+                        c1 = Character.toLowerCase( c1 );
+                        c2 = Character.toLowerCase( c2 );
+                        if ( c1 != c2 )
+                        {
+                            // No overflow because of numeric promotion
+                            return c1 - c2;
+                        }
+                    }
+                }
+            }
         }
+        return n1 - n2;
     }
 
-    public boolean isCaseSensitive()
+    private static char toLowerCaseFast( char ch )
     {
-        return m_isCaseSensitive;
+        return ( ch >= 'A' && ch <= 'Z' ) ? ( char ) ( ch + 'a' - 'A' ) : ch;
     }
 }
\ No newline at end of file

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringMap.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringMap.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringMap.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/StringMap.java Tue Apr 30 16:21:29 2019
@@ -7,7 +7,7 @@
  * "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
+ *   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
@@ -18,130 +18,32 @@
  */
 package org.apache.felix.connect.felix.framework.util;
 
-import java.util.Collection;
 import java.util.Map;
-import java.util.Set;
 import java.util.TreeMap;
 
 /**
- * Simple utility class that creates a map for string-based keys. This map can
- * be set to use case-sensitive or case-insensitive comparison when searching
- * for the key. Any keys put into this map will be converted to a
- * <tt>String</tt> using the <tt>toString()</tt> method, since it is only
- * intended to compare strings.
- */
-public class StringMap<T> implements Map<String, T>
+ * Simple utility class that creates a map for string-based keys.
+ * This map can be set to use case-sensitive or case-insensitive
+ * comparison when searching for the key.  Any keys put into this
+ * map will be converted to a <tt>String</tt> using the
+ * <tt>toString()</tt> method, since it is only intended to
+ * compare strings.
+ **/
+public class StringMap extends TreeMap<String, Object>
 {
-    private TreeMap<String, T> m_map;
 
     public StringMap()
     {
-        this(true);
-    }
-
-    public StringMap(boolean caseSensitive)
-    {
-        m_map = new TreeMap<String, T>(new StringComparator(caseSensitive));
-    }
-
-    public StringMap(Map<? extends String, ? extends T> map, boolean caseSensitive)
-    {
-        this(caseSensitive);
-        putAll(map);
-    }
-
-    public boolean isCaseSensitive()
-    {
-        return ((StringComparator) m_map.comparator()).isCaseSensitive();
-    }
-
-    public void setCaseSensitive(boolean b)
-    {
-        if (isCaseSensitive() != b)
-        {
-            TreeMap<String, T> map = new TreeMap<String, T>(new StringComparator(b));
-            map.putAll(m_map);
-            m_map = map;
-        }
-    }
-
-    @Override
-    public int size()
-    {
-        return m_map.size();
-    }
-
-    @Override
-    public boolean isEmpty()
-    {
-        return m_map.isEmpty();
-    }
-
-    @Override
-    public boolean containsKey(Object arg0)
-    {
-        return m_map.containsKey(arg0);
-    }
-
-    @Override
-    public boolean containsValue(Object arg0)
-    {
-        return m_map.containsValue(arg0);
+        super(StringComparator.COMPARATOR);
     }
 
-    @Override
-    public T get(Object arg0)
+    public StringMap(Map<?, ?> map)
     {
-        return m_map.get(arg0);
-    }
-
-    @Override
-    public T put(String key, T value)
-    {
-        return m_map.put(key, value);
-    }
-
-    @Override
-    public void putAll(Map<? extends String, ? extends T> map)
-    {
-        for (Entry<? extends String, ? extends T> entry : map.entrySet())
+        this();
+        for (Map.Entry<?, ?> e : map.entrySet())
         {
-            put(entry.getKey(), entry.getValue());
+            put(e.getKey().toString(), e.getValue());
         }
     }
 
-    @Override
-    public T remove(Object arg0)
-    {
-        return m_map.remove(arg0);
-    }
-
-    @Override
-    public void clear()
-    {
-        m_map.clear();
-    }
-
-    @Override
-    public Set<String> keySet()
-    {
-        return m_map.keySet();
-    }
-
-    @Override
-    public Collection<T> values()
-    {
-        return m_map.values();
-    }
-
-    @Override
-    public Set<Entry<String, T>> entrySet()
-    {
-        return m_map.entrySet();
-    }
-
-    public String toString()
-    {
-        return m_map.toString();
-    }
-}
\ No newline at end of file
+}

Modified: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/Util.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/Util.java?rev=1858443&r1=1858442&r2=1858443&view=diff
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/Util.java (original)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/util/Util.java Tue Apr 30 16:21:29 2019
@@ -24,7 +24,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -515,4 +518,116 @@ public class Util
         return val;
     }
 
+    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;
+
+        StringBuilder sb = new StringBuilder();
+
+        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;
+    }
+
+
+    private static final List EMPTY_LIST = Collections.unmodifiableList(Collections.EMPTY_LIST);
+    private static final Map EMPTY_MAP = Collections.unmodifiableMap(Collections.EMPTY_MAP);
+
+    public static <T> List<T> newImmutableList(List<T> list)
+    {
+        return list == null || list.isEmpty() ? EMPTY_LIST : Collections.unmodifiableList(list);
+    }
+
+    public static <K,V> Map<K,V> newImmutableMap(Map<K,V> map)
+    {
+        return map == null || map.isEmpty() ? EMPTY_MAP : Collections.unmodifiableMap(map);
+    }
 }
\ No newline at end of file

Added: felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/wiring/BundleCapabilityImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/wiring/BundleCapabilityImpl.java?rev=1858443&view=auto
==============================================================================
--- felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/wiring/BundleCapabilityImpl.java (added)
+++ felix/trunk/connect/src/main/java/org/apache/felix/connect/felix/framework/wiring/BundleCapabilityImpl.java Tue Apr 30 16:21:29 2019
@@ -0,0 +1,203 @@
+/*
+ * 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.felix.connect.felix.framework.wiring;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.apache.felix.connect.felix.framework.capabilityset.SimpleFilter;
+import org.apache.felix.connect.felix.framework.util.Util;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class BundleCapabilityImpl implements BundleCapability
+{
+    public static final String VERSION_ATTR = "version";
+
+    private final BundleRevision m_revision;
+    private final String m_namespace;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+    private final List<String> m_uses;
+    private final List<List<String>> m_includeFilter;
+    private final List<List<String>> m_excludeFilter;
+    private final Set<String> m_mandatory;
+
+    public BundleCapabilityImpl(BundleRevision revision, String namespace,
+        Map<String, String> dirs, Map<String, Object> attrs)
+    {
+        m_namespace = namespace;
+        m_revision = revision;
+        m_dirs = Util.newImmutableMap(dirs);
+        m_attrs = Util.newImmutableMap(attrs);
+
+        // Find all export directives: uses, mandatory, include, and exclude.
+
+        List<String> uses = Collections.EMPTY_LIST;
+        String value = m_dirs.get(Constants.USES_DIRECTIVE);
+        if (value != null)
+        {
+            // Parse these uses directive.
+            StringTokenizer tok = new StringTokenizer(value, ",");
+            uses = new ArrayList(tok.countTokens());
+            while (tok.hasMoreTokens())
+            {
+                uses.add(tok.nextToken().trim());
+            }
+        }
+        m_uses = uses;
+
+        value = m_dirs.get(Constants.INCLUDE_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> filters = Util.parseDelimitedString(value, ",");
+            m_includeFilter = new ArrayList<List<String>>(filters.size());
+            for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
+            {
+                List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
+                m_includeFilter.add(substrings);
+            }
+        }
+        else
+        {
+            m_includeFilter = null;
+        }
+
+        value = m_dirs.get(Constants.EXCLUDE_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> filters = Util.parseDelimitedString(value, ",");
+            m_excludeFilter = new ArrayList<List<String>>(filters.size());
+            for (int filterIdx = 0; filterIdx < filters.size(); filterIdx++)
+            {
+                List<String> substrings = SimpleFilter.parseSubstring(filters.get(filterIdx));
+                m_excludeFilter.add(substrings);
+            }
+        }
+        else
+        {
+            m_excludeFilter = null;
+        }
+
+        Set<String> mandatory = Collections.EMPTY_SET;
+        value = m_dirs.get(Constants.MANDATORY_DIRECTIVE);
+        if (value != null)
+        {
+            List<String> names = Util.parseDelimitedString(value, ",");
+            mandatory = new HashSet<String>(names.size());
+            for (String name : names)
+            {
+                // If attribute exists, then record it as mandatory.
+                if (m_attrs.containsKey(name))
+                {
+                    mandatory.add(name);
+                }
+                // Otherwise, report an error.
+                else
+                {
+                    throw new IllegalArgumentException(
+                        "Mandatory attribute '" + name + "' does not exist.");
+                }
+            }
+        }
+        m_mandatory = mandatory;
+    }
+
+    public BundleRevision getResource()
+    {
+        return m_revision;
+    }
+
+    public BundleRevision getRevision()
+    {
+        return m_revision;
+    }
+
+    public String getNamespace()
+    {
+        return m_namespace;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public boolean isAttributeMandatory(String name)
+    {
+        return !m_mandatory.isEmpty() && m_mandatory.contains(name);
+    }
+
+    public List<String> getUses()
+    {
+        return m_uses;
+    }
+
+    public boolean isIncluded(String name)
+    {
+        if ((m_includeFilter == null) && (m_excludeFilter == null))
+        {
+            return true;
+        }
+
+        // Get the class name portion of the target class.
+        String className = Util.getClassName(name);
+
+        // If there are no include filters then all classes are included
+        // by default, otherwise try to find one match.
+        boolean included = (m_includeFilter == null);
+        for (int i = 0;
+            (!included) && (m_includeFilter != null) && (i < m_includeFilter.size());
+            i++)
+        {
+            included = SimpleFilter.compareSubstring(m_includeFilter.get(i), className);
+        }
+
+        // If there are no exclude filters then no classes are excluded
+        // by default, otherwise try to find one match.
+        boolean excluded = false;
+        for (int i = 0;
+            (!excluded) && (m_excludeFilter != null) && (i < m_excludeFilter.size());
+            i++)
+        {
+            excluded = SimpleFilter.compareSubstring(m_excludeFilter.get(i), className);
+        }
+        return included && !excluded;
+    }
+
+    @Override
+    public String toString()
+    {
+        if (m_revision == null)
+        {
+            return m_attrs.toString();
+        }
+        return "[" + m_revision + "] " + m_namespace + "; " + m_attrs;
+    }
+}
\ No newline at end of file