Add approximate operator and fix a couple bugs. (FELIX-2039)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@926976 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
index 466af81..910cb01 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -19,6 +19,7 @@
 package org.apache.felix.framework.capabilityset;
 
 import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -338,19 +339,19 @@
             return true;
         }
 
+        // The substring operator only works on string values, so if the
+        // lhs is not a string, then do an equality comparison using the
+        // original string containing wildcards.
+        if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+        {
+            op = SimpleFilter.EQ;
+            rhsUnknown = SimpleFilter.unparseSubstring((List<String>) rhsUnknown);
+        }
+
         // If the type is comparable, then we can just return the
         // result immediately.
         if (lhs instanceof Comparable)
         {
-            // The substring operator only works on string values, so if the
-            // lhs is not a string, then do an equality comparison using the
-            // original string containing wildcards.
-            if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
-            {
-                op = SimpleFilter.EQ;
-                rhsUnknown = SimpleFilter.unparseSubstring((List<String>) rhsUnknown);
-            }
-
             Object rhs;
             if (op == SimpleFilter.SUBSTRING)
             {
@@ -376,8 +377,8 @@
                     return (((Comparable) lhs).compareTo(rhs) >= 0);
                 case SimpleFilter.LTE :
                     return (((Comparable) lhs).compareTo(rhs) <= 0);
-//                case SimpleFilter.APPROX :
-//                    return compareToApprox(((Comparable) lhs), rhs);
+                case SimpleFilter.APPROX :
+                    return compareApproximate(((Comparable) lhs), rhs);
                 case SimpleFilter.SUBSTRING :
                     return SimpleFilter.compareSubstring((String) lhs, (List<String>) rhs);
                 default:
@@ -403,7 +404,7 @@
                 case SimpleFilter.EQ :
                 case SimpleFilter.GTE :
                 case SimpleFilter.LTE :
-//                case SimpleFilter.APPROX:
+                case SimpleFilter.APPROX :
                     return (lhs.equals(rhs));
                 default:
                     throw new RuntimeException(
@@ -446,6 +447,30 @@
         }
     }
 
+    private static boolean compareApproximate(Object lhs, Object rhs)
+    {
+        if (rhs instanceof String)
+        {
+            String s = (String) rhs;
+            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));
+                }
+            }
+            s = sb.toString();
+            return s.equalsIgnoreCase((String) lhs);
+        }
+        else if (rhs instanceof Character)
+        {
+            return Character.toLowerCase(((Character) lhs))
+                == Character.toLowerCase(((Character) rhs));
+        }
+        return lhs.equals(rhs);
+    }
+
     private static Object coerceType(Object lhs, String rhsString) throws Exception
     {
         // If the LHS expects a string, then we can just return
@@ -468,9 +493,10 @@
             }
             else
             {
-                rhs = lhs.getClass()
-                    .getConstructor(STRING_CLASS)
-                        .newInstance(new Object[] { rhsString });
+// TODO: FELIX3 - Use SecureAction.
+                Constructor ctor = lhs.getClass().getConstructor(STRING_CLASS);
+                ctor.setAccessible(true);
+                rhs = ctor.newInstance(new Object[] { rhsString });
             }
         }
         catch (Exception ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
index 42205e9..1e0ae63 100644
--- a/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -33,6 +33,7 @@
 //       special case of string equality comparison?
     public static final int SUBSTRING = 7;
     public static final int PRESENT = 8;
+    public static final int APPROX = 9;
 
     private final String m_name;
     private final Object m_value;
@@ -89,6 +90,9 @@
             case PRESENT:
                 s = "(" + m_name + "=*)";
                 break;
+            case APPROX:
+                s = "(" + m_name + "~=" + toEncodedString(m_value) + ")";
+                break;
         }
         return s;
     }
@@ -249,7 +253,7 @@
 
     private static SimpleFilter subfilter(String filter, int startIdx, int endIdx)
     {
-        final String opChars = "=<>";
+        final String opChars = "=<>~";
 
         // Determine the ending index of the attribute name.
         int attrEndIdx = startIdx;
@@ -301,6 +305,15 @@
                 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));