Initial version of new filter implementation. (FELIX-2039)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@918492 13f79535-47bb-0310-9956-ffa450edef68
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
new file mode 100644
index 0000000..ba2e88f
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/SimpleFilter.java
@@ -0,0 +1,388 @@
+/*
+ * 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.framework.capabilityset;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SimpleFilter
+{
+ public static final int AND = 1;
+ public static final int OR = 2;
+ public static final int NOT = 3;
+ public static final int EQ = 4;
+ public static final int LTE = 5;
+ public static final int GTE = 6;
+// TODO: FELIX3 - Should we handle substring as a separate operator or as a
+// special case of string equality comparison?
+ public static final int SUBSTRING = 7;
+
+ private final String m_name;
+ private final Object m_value;
+ private final int m_op;
+
+ public SimpleFilter(String attr, Object value, int op)
+ {
+ m_name = attr;
+ m_value = value;
+ m_op = op;
+ }
+
+ public String getName()
+ {
+ return m_name;
+ }
+
+ public Object getValue()
+ {
+ return m_value;
+ }
+
+ public int getOperation()
+ {
+ return m_op;
+ }
+
+ 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 + "=" + m_value + ")";
+ break;
+ case LTE:
+ s = "(" + m_name + "<=" + m_value + ")";
+ break;
+ case GTE:
+ s = "(" + m_name + ">=" + m_value + ")";
+ break;
+ case SUBSTRING:
+ s = "(" + m_name + "=" + unparseSubstring((List<String>) m_value) + ")";
+ break;
+ }
+ return s;
+ }
+
+ private String toString(List list)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < list.size(); i++)
+ {
+ sb.append(list.get(i).toString());
+ }
+ return sb.toString();
+ }
+
+ public static SimpleFilter parse(String filter)
+ {
+ filter = filter.trim();
+
+ if ((filter == null) || (filter.length() == 0))
+ {
+ throw new IllegalArgumentException("Null or empty filter.");
+ }
+ else if (filter.charAt(0) != '(')
+ {
+ throw new IllegalArgumentException("Missing opening parenthesis: " + filter);
+ }
+
+ SimpleFilter sf = null;
+ List stack = new ArrayList();
+ for (int i = 0; i < filter.length(); i++)
+ {
+ if (sf != null)
+ {
+ throw new IllegalArgumentException(
+ "Only one top-level operation allowed: " + filter);
+ }
+ if (filter.charAt(i) == '(')
+ {
+ if (filter.charAt(i+1) == '&')
+ {
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.AND));
+ }
+ else if (filter.charAt(i+1) == '|')
+ {
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.OR));
+ }
+ else if (filter.charAt(i+1) == '!')
+ {
+ stack.add(0, new SimpleFilter(null, new ArrayList(), SimpleFilter.NOT));
+ }
+ else
+ {
+ stack.add(0, new Integer(i));
+ }
+ }
+ else if (filter.charAt(i) == ')')
+ {
+ Object top = stack.remove(0);
+ if (top instanceof SimpleFilter)
+ {
+ if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+ {
+ ((List) ((SimpleFilter) stack.get(0)).m_value).add(top);
+ }
+ else
+ {
+ sf = (SimpleFilter) top;
+ }
+ }
+ else if (!stack.isEmpty() && (stack.get(0) instanceof SimpleFilter))
+ {
+ ((List) ((SimpleFilter) stack.get(0)).m_value).add(
+ SimpleFilter.subfilter(filter, ((Integer) top).intValue() + 1, i));
+ }
+ else
+ {
+ sf = SimpleFilter.subfilter(filter, ((Integer) top).intValue() + 1, i);
+ }
+ }
+ }
+
+ if (sf == null)
+ {
+ throw new IllegalArgumentException("Missing closing parenthesis: " + filter);
+ }
+
+ return sf;
+ }
+
+ private static SimpleFilter subfilter(String filter, int startIdx, int endIdx)
+ {
+ final String opChars = "=<>";
+ String attr = null;
+ Object value = null;
+ int op = -1;
+ for (int i = 0; i < (endIdx - startIdx); i++)
+ {
+ if (opChars.indexOf(filter.charAt(startIdx + i)) >= 0)
+ {
+ switch (filter.charAt(startIdx + i))
+ {
+ case '=':
+ attr = filter.substring(startIdx, startIdx + i);
+ List<String> values =
+ parseSubstring(filter.substring(startIdx + i + 1, endIdx));
+ if (values.size() > 1)
+ {
+ op = SUBSTRING;
+ value = values;
+ }
+ else
+ {
+ op = EQ;
+ value = values.get(0);
+ }
+ break;
+ case '<':
+ if (filter.charAt(startIdx + i + 1) != '=')
+ {
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ attr = filter.substring(startIdx, startIdx + i);
+ op = LTE;
+ value = filter.substring(startIdx + i + 2, endIdx);
+ break;
+ case '>':
+ if (filter.charAt(startIdx + i + 1) != '=')
+ {
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ attr = filter.substring(startIdx, startIdx + i);
+ op = GTE;
+ value = filter.substring(startIdx + i + 2, endIdx);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown operator: " + filter.substring(startIdx, endIdx));
+ }
+ break;
+ }
+ }
+
+ return new SimpleFilter(attr, value, op);
+ }
+
+// TODO: FELIX3 - Merge with Util class.
+ public static List<String> parseSubstring(String value)
+ {
+ List<String> pieces = new ArrayList();
+ StringBuffer ss = new StringBuffer();
+ // int kind = SIMPLE; // assume until proven otherwise
+ boolean wasStar = false; // indicates last piece was a star
+ boolean leftstar = false; // track if the initial piece is a star
+ boolean rightstar = false; // track if the final piece is a star
+
+ int idx = 0;
+
+ // We assume (sub)strings can contain leading and trailing blanks
+loop: for (;;)
+ {
+ if (idx >= value.length())
+ {
+ if (wasStar)
+ {
+ // insert last piece as "" to handle trailing star
+ rightstar = true;
+ }
+ else
+ {
+ pieces.add(ss.toString());
+ // accumulate the last piece
+ // note that in the case of
+ // (cn=); this might be
+ // the string "" (!=null)
+ }
+ ss.setLength(0);
+ break loop;
+ }
+
+ char c = value.charAt(idx++);
+ if (c == '*')
+ {
+ 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;
+ }
+ wasStar = true;
+ }
+ else
+ {
+ wasStar = false;
+ ss.append(c);
+ }
+ }
+ if (leftstar || rightstar || pieces.size() > 1)
+ {
+ // insert leading and/or trailing "" to anchor ends
+ if (rightstar)
+ {
+ pieces.add("");
+ }
+ if (leftstar)
+ {
+ pieces.add(0, "");
+ }
+ }
+ return pieces;
+ }
+
+ public static String unparseSubstring(List<String> pieces)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < pieces.size(); i++)
+ {
+ if (i > 0)
+ {
+ sb.append("*");
+ }
+ sb.append(pieces.get(i));
+ }
+ return sb.toString();
+ }
+
+// TODO: FELIX3 - Merge with Util class.
+ public static boolean compareSubstring(String s, List<String> pieces)
+ {
+ // Walk the pieces to match the string
+ // There are implicit stars between each piece,
+ // and the first and last pieces might be "" to anchor the match.
+ // assert (pieces.length > 1)
+ // minimal case is <string>*<string>
+
+ boolean result = true;
+ int len = pieces.size();
+
+ int index = 0;
+
+loop: for (int i = 0; i < len; i++)
+ {
+ String piece = pieces.get(i);
+
+ // If this is the first piece, then make sure the
+ // string starts with it.
+ if (i == 0)
+ {
+ if (!s.startsWith(piece))
+ {
+ result = false;
+ break loop;
+ }
+ }
+
+ // If this is the last piece, then make sure the
+ // string ends with it.
+ if (i == len - 1)
+ {
+ if (s.endsWith(piece))
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ break loop;
+ }
+
+ // If this is neither the first or last piece, then
+ // make sure the string contains it.
+ if ((i > 0) && (i < (len - 1)))
+ {
+ index = s.indexOf(piece, index);
+ if (index < 0)
+ {
+ result = false;
+ break loop;
+ }
+ }
+
+ // Move string index beyond the matching piece.
+ index += piece.length();
+ }
+
+ return result;
+ }
+}
\ No newline at end of file