/******************************************************************************* * Copyright (c) 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Jan S. Rellermeyer, IBM Research - initial API and implementation *******************************************************************************/ package org.eclipse.concierge; import java.lang.reflect.AccessibleObject; 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.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.EmptyStackException; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; /** * The RFC1960 LDAP Filter implementation class. * * @author Jan S. Rellermeyer */ public final class RFC1960Filter implements Filter { /** * AND operator. */ private static final int AND_OPERATOR = 1; /** * OR operator. */ private static final int OR_OPERATOR = 2; /** * NOT operator. */ private static final int NOT_OPERATOR = 3; /** * EQUALS (=) operator. */ private static final int EQUALS = 0; /** * PRESENT (=*) operator. */ private static final int PRESENT = 1; /** * APPROX (=~) operator. */ private static final int APPROX = 2; /** * GREATER (>=) operator. */ private static final int GREATER = 3; /** * LESS (<=) operator. */ private static final int LESS = 4; /** * EQUALS with wildcard */ private static final int SUBSTRING = 5; /** * the string presentations of the operators. */ protected static final String[] OP = { "=", "=*", "~=", ">=", "<=", "=" }; /** * the empty "null filter" is generated from null filter strings and matches * everything. */ private static final Filter NULL_FILTER = new Filter() { public final boolean match(final ServiceReference<?> reference) { return true; } public final boolean match(final Dictionary<String, ?> dictionary) { return true; } public final boolean matchCase(final Dictionary<String, ?> dictionary) { return true; } public boolean matches(final Map<String, ?> map) { return true; } }; // fields /** * the operands. */ private final List<Filter> operands = new ArrayList<Filter>(1); /** * the operator. */ private final int operator; /** * create a new filter instance. * * @param operator * the operator of the node */ private RFC1960Filter(final int operator) { this.operator = operator; } /** * get a filter instance from filter string. * * @param filterString * the filter string. * @return a filter instance. * @throws InvalidSyntaxException * is the string is invalid. */ public static Filter fromString(final String str) throws InvalidSyntaxException { if (str == null) { return NULL_FILTER; } final String filterString = str.trim(); if (filterString.length() == 1) { throw new InvalidSyntaxException("Malformed filter", filterString); } final Stack<Filter> stack = new Stack<Filter>(); try { final int len = filterString.length(); int last = -1; int oper = 0; String id = null; int comparator = -1; final char[] chars = filterString.toCharArray(); // stack.clear(); final BitSet esc = new BitSet(chars.length); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '\\': // escaped character if (i < chars.length) { if (chars[i + 1] == '(' || chars[i + 1] == ')' || chars[i + 1] == '\\' || chars[i + 1] == '*') { esc.set(i); } } i++; continue; case '(': // lookahead ... char nextChar = chars[i + 1]; while (Character.isWhitespace(nextChar)) { i++; nextChar = chars[i + 1]; } if (nextChar == ')') { throw new InvalidSyntaxException("Empty filter", filterString); } // lookahead int x = i; char nextnextChar = chars[x + 2]; while (Character.isWhitespace(nextnextChar)) { x++; nextnextChar = chars[x + 2]; } if (nextChar == '&' && nextnextChar == '(') { stack.push(new RFC1960Filter(AND_OPERATOR)); continue; } else if (nextChar == '|' && nextnextChar == '(') { stack.push(new RFC1960Filter(OR_OPERATOR)); continue; } else if (nextChar == '!' && nextnextChar == '(') { stack.push(new RFC1960Filter(NOT_OPERATOR)); continue; } else { if (last == -1) { last = i; } else { throw new InvalidSyntaxException( "Surplus left paranthesis at: " + filterString.substring(i), filterString); } } continue; case ')': if (last == -1) { final RFC1960Filter filter = (RFC1960Filter) stack .pop(); if (stack.isEmpty()) { return filter; } final RFC1960Filter parent = (RFC1960Filter) stack .peek(); if (parent.operator == NOT_OPERATOR && !parent.operands.isEmpty()) { throw new InvalidSyntaxException( "Unexpected literal: " + filterString.substring(i), filterString); } parent.operands.add(filter); if (i == len - 1) { throw new InvalidSyntaxException( "Missing right paranthesis at the end.", filterString); } } else { if (oper == 0) { throw new InvalidSyntaxException( "Missing operator.", filterString); } if (stack.isEmpty()) { if (i == len - 1) { // just a single simple filter // String value = filterString.substring(++oper, // len - 1); String value = value(filterString, ++oper, len - 1, esc); if (value.equals("*") && comparator == SUBSTRING) { comparator = PRESENT; value = null; } return new RFC1960SimpleFilter(id, comparator, value); } else { throw new InvalidSyntaxException( "Unexpected literal: " + filterString.substring(i), filterString); } } // get the parent from stack final RFC1960Filter parent = (RFC1960Filter) stack .peek(); // String value = filterString.substring(++oper, i); String value = value(filterString, ++oper, i, esc); if (value.equals("*") && comparator == SUBSTRING) { comparator = PRESENT; value = null; } // link current element to parent parent.operands.add( new RFC1960SimpleFilter(id, comparator, value)); oper = 0; last = -1; id = null; comparator = -1; } continue; case '~': if (oper == 0 && chars[i + 1] == '=') { id = filterString.substring(last + 1, i).trim(); comparator = APPROX; oper = ++i; continue; } else { throw new InvalidSyntaxException( "Unexpected character " + chars[i + 1], filterString); } case '>': if (oper == 0 && chars[i + 1] == '=') { id = filterString.substring(last + 1, i).trim(); comparator = GREATER; oper = ++i; continue; } else { throw new InvalidSyntaxException( "Unexpected character " + chars[i + 1], filterString); } case '<': if (oper == 0 && chars[i + 1] == '=') { id = filterString.substring(last + 1, i).trim(); comparator = LESS; oper = ++i; continue; } else { throw new InvalidSyntaxException( "Unexpected character " + chars[i + 1], filterString); } case '=': if (oper == 0) { if (last + 1 == i) { throw new InvalidSyntaxException( "Missing identifier", filterString); } // could also be a "=*" present production. // if this is the case, it is fixed later, because // value=* and value=*key would require a lookahead of // at // least two. (the symbol "=*" alone is ambiguous). id = filterString.substring(last + 1, i).trim(); comparator = EQUALS; oper = i; } continue; case '*': if (comparator == EQUALS) { comparator = SUBSTRING; } continue; } } return stack.pop(); } catch (final EmptyStackException e) { throw new InvalidSyntaxException( "Filter expression not well-formed.", filterString); } } private static final String value(final String str, final int start, final int end, final BitSet esc) { if (esc.isEmpty()) { return str.substring(start, end); } final StringBuilder b = new StringBuilder(); int i = start; int j; while ((j = esc.nextSetBit(i)) > -1) { b.append(str.substring(i, j)); i = j + 1; } b.append(str.substring(i, end)); return b.toString(); } protected final static Map<String, ?> dict2map( final Dictionary<String, ?> dict) { if (dict == null) { return null; } if (dict instanceof Hashtable) { return (Hashtable<String, ?>) dict; } // legacy dictionary... // don't care about the performance... final HashMap<String, Object> map = new HashMap<String, Object>(); for (final Enumeration<String> e = dict.keys(); e.hasMoreElements();) { final String key = e.nextElement(); map.put(key, dict.get(key)); } return map; } /** * check if the filter matches a service reference. * * @param reference * the service reference. * @return true if the filter matches, false otherwise. * @see org.osgi.framework.Filter#match(org.osgi.framework.ServiceReference) * @category Filter */ public boolean match(final ServiceReference<?> reference) { try { return matches(((ServiceReferenceImpl<?>) reference).properties); } catch (final ClassCastException ce) { // so this was not instance of ServiceReferenceImpl. Someone // must have created an own implementation. final HashMap<String, Object> dict = new HashMap<String, Object>(); final String[] keys = reference.getPropertyKeys(); for (int i = 0; i < keys.length; i++) { dict.put(keys[i], reference.getProperty(keys[i])); } return matches(dict); } } /** * check if the filter matches a dictionary of attributes. * * @param values * the attributes. * @return true, if the filter matches, false otherwise. * @see org.osgi.framework.Filter#match(java.util.Dictionary) * @category Filter */ public boolean match(final Dictionary<String, ?> values) { return matches(dict2map(values)); } public boolean matches(final Map<String, ?> map) { if (operator == AND_OPERATOR) { final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { if (!operandArray[i].matches(map)) { return false; } } return true; } else if (operator == OR_OPERATOR) { final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { if (operandArray[i].matches(map)) { return true; } } return false; } else if (operator == NOT_OPERATOR) { return !operands.get(0).matches(map); } throw new IllegalStateException("PARSER ERROR"); } /** * check if the filter matches a dictionary of attributes. This method is * case sensitive. * * @param values * the attributes. * @return true, if the filter matches, false otherwise. * @see org.osgi.framework.Filter#matchCase(Dictionary) * @category Filter */ public boolean matchCase(final Dictionary<String, ?> values) { if (operator == AND_OPERATOR) { final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { if (!operandArray[i].matchCase(values)) { return false; } } return true; } else if (operator == OR_OPERATOR) { final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { if (operandArray[i].matchCase(values)) { return true; } } return false; } else if (operator == NOT_OPERATOR) { return !operands.get(0).matchCase(values); } throw new IllegalStateException("PARSER ERROR"); } /** * get a string representation of the filter. * * @return the string. * @category Object */ public String toString() { if (operator == NOT_OPERATOR) { return "(!" + operands.get(0) + ")"; } final StringBuffer buffer = new StringBuffer( operator == AND_OPERATOR ? "(&" : "(|"); final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { buffer.append(operandArray[i]); } buffer.append(")"); return buffer.toString(); } /** * check if the filter equals another object. * * @param obj * the other object. * @return true if the object is an instance of RFC1960Filter and the * filters are equal. * @see java.lang.Object#equals(java.lang.Object) * @category Object */ public boolean equals(final Object obj) { if (obj instanceof RFC1960Filter) { final RFC1960Filter filter = (RFC1960Filter) obj; if (operands.size() != filter.operands.size()) { return false; } final Filter[] operandArray = operands .toArray(new Filter[operands.size()]); final Filter[] operandArray2 = filter.operands .toArray(new Filter[operands.size()]); for (int i = 0; i < operandArray.length; i++) { if (!operandArray[i].equals(operandArray2[i])) { return false; } } return true; } return false; } /** * get the hash code. * * @return the hash code. * @category Object */ public int hashCode() { return toString().hashCode(); } /** * check, if a value matches a wildcard expression. * * @param c1 * the value. * @param p1 * the value index. * @param c2 * the attribute. * @param p2 * the attribute index. * @return integer with the same semantics as compareTo. */ static int stringCompare(final char[] c1, int p1, final char[] c2, int p2) { if (p1 == c1.length) { return 0; } final int l1 = c1.length; final int l2 = c2.length; while (p1 < l1 && p2 < l2) { boolean escaped = false; if (c1[p1] == c2[p2]) { p1++; p2++; continue; } if (c1[p1] == '\\') { p1++; escaped = true; } if (c1[p1] == '*' && !escaped) { p1++; do { if (stringCompare(c1, p1, c2, p2) == 0) { return 0; } p2++; } while (l2 - p2 > -1); return 1; } else { if (c1[p1] < c2[p2]) { return -1; } else if (c1[p1] > c2[p2]) { return 1; } } } if (p2 == l2 && c1[p1 - 1] == c2[p2 - 1] && (p1 == l1 || p1 == l1 - 1 && c1[p1] == '*')) { return 0; } if (p1 > 0 && c1[p1 - 1] == '*' && p1 == l1 && p2 == l2) { return 0; } final int min = l1 < l2 ? l1 : l2; return l1 == min ? -1 : 1; } /** * A simple filter. That is a filter of the form <tt>key operand value</tt>. * A general filter consists of one or more simple filter literals connected * by boolean operators. * * @author Jan S. Rellermeyer */ final private static class RFC1960SimpleFilter implements Filter { /** * the id. */ protected final String id; /** * the comparator. */ protected final int comparator; /** * the value. */ protected final String value; /** * create a new filter. * * @param id * the key * @param comparator * the comparator */ protected RFC1960SimpleFilter(final String id, final int comparator, final String value) { this.id = id; this.comparator = comparator; this.value = value; } /** * check, if the filter matches a service reference. * * @param reference * the service reference. * @return true, iff it matches. * @see org.osgi.framework.Filter#match(org.osgi.framework.ServiceReference) * @category Filter */ public boolean match(final ServiceReference<?> reference) { try { return matches( ((ServiceReferenceImpl<?>) reference).properties); } catch (final ClassCastException e) { // so this was not instance of ServiceReferenceImpl. Someone // must have created an own implementation. final Map<String, Object> dict = new HashMap<String, Object>(); final String[] keys = reference.getPropertyKeys(); for (int i = 0; i < keys.length; i++) { dict.put(keys[i], reference.getProperty(keys[i])); } return matches(dict); } } /** * check if the filter matches a dictionary of attributes. * * @param map * the attributes. * @return true if the filter matches, false otherwise. * @see org.osgi.framework.Filter#match(java.util.Dictionary) * @category Filter */ public boolean match(final Dictionary<String, ?> dict) { return matches(dict2map(dict), false); } /** * check if the filter matches a dictionary of attributes. This method * is case sensitive. * * @param map * the attributes. * @return true if the filter matches, false otherwise. * @see org.osgi.framework.Filter#matchCase(Dictionary) * @category Filter */ public boolean matchCase(final Dictionary<String, ?> dict) { return matches(dict2map(dict), true); } public boolean matches(final Map<String, ?> map) { return matches(map, false); } private boolean matches(final Map<String, ?> map, final boolean caseSensitive) { if (map == null) { return false; } Object temp = null; // just by chance, try if the case sensitive matching returns a // result. temp = map.get(id); if (temp == null) { if (caseSensitive) { return false; } // no ? Then try lower case. temp = map.get(id.toLowerCase()); } if (temp == null) { // bad luck, try case insensitive matching of all keys for (final String key : map.keySet()) { if (key.equalsIgnoreCase(id)) { temp = map.get(key); break; } } } if (temp == null) { return false; } // are we just checking for presence ? Then we are done ... if (comparator == PRESENT) { return true; } final Object attr = temp; try { if (attr instanceof String) { return compareString(value, comparator, (String) attr); } else if (attr instanceof Number) { // all the numbers checkings run a lot faster when compared // in a primitive typed way return compareNumber(value.trim(), comparator, (Number) attr); } else if (attr instanceof String[]) { final String[] array = (String[]) attr; if (array.length == 0) { return false; } final String val = comparator == APPROX ? stripWhitespaces(value) : value; for (int i = 0; i < array.length; i++) { if (compareString(val, comparator, array[i])) { return true; } } return false; } else if (attr instanceof Boolean) { return (comparator == EQUALS || comparator == APPROX) && ((Boolean) attr) .equals(Boolean.valueOf(value.trim())); } else if (attr instanceof Character) { final String trimmed = value.trim(); return trimmed.length() == 1 ? compareTyped(new Character(trimmed.charAt(0)), comparator, (Character) attr) : trimmed.length() == 0 && Character.isWhitespace( ((Character) attr).charValue()); } else if (attr instanceof Collection) { final Collection<?> col = (Collection<?>) attr; final Object[] obj = col.toArray(); return compareArray(value, comparator, obj); } else if (attr instanceof Object[]) { return compareArray(value, comparator, (Object[]) attr); } else if (attr.getClass().isArray()) { for (int i = 0; i < Array.getLength(attr); i++) { final Object obj = Array.get(attr, i); if (obj instanceof Number && compareNumber(value.trim(), comparator, (Number) obj) || obj instanceof Character && compareTyped( new Character(value.trim().charAt(0)), comparator, (Character) obj) || compareReflective(value, comparator, obj)) { return true; } } return false; } else { return compareReflective(value, comparator, attr); } } catch (final Throwable t) { return false; } } /** * compare a string. * * @param val * the filter value. * @param comparator * the comparator. * @param attr * the attribute. * @return true, iff matches. */ private static boolean compareString(final String val, final int comparator, final String attr) { final String value = comparator == APPROX ? stripWhitespaces(val).toLowerCase() : val; final String attribute = comparator == APPROX ? stripWhitespaces(attr).toLowerCase() : attr; switch (comparator) { case APPROX: case EQUALS: case SUBSTRING: return RFC1960Filter.stringCompare(value.toCharArray(), 0, attribute.toCharArray(), 0) == 0; case GREATER: return RFC1960Filter.stringCompare(value.toCharArray(), 0, attribute.toCharArray(), 0) <= 0; case LESS: return RFC1960Filter.stringCompare(value.toCharArray(), 0, attribute.toCharArray(), 0) >= 0; default: throw new IllegalStateException("Found illegal comparator."); } } /** * compare numbers. * * @param value * the filter value. * @param comparator * the comparator. * @param attr * the number. * @return true, iff matches. */ private static boolean compareNumber(final String value, final int comparator, final Number attr) { if (attr instanceof Integer) { final int intAttr = ((Integer) attr).intValue(); final int intValue = Integer.parseInt(value); switch (comparator) { case GREATER: return intAttr >= intValue; case LESS: return intAttr <= intValue; default: return intAttr == intValue; } } else if (attr instanceof Long) { final long longAttr = ((Long) attr).longValue(); final long longValue = Long.parseLong(value); switch (comparator) { case GREATER: return longAttr >= longValue; case LESS: return longAttr <= longValue; default: return longAttr == longValue; } } else if (attr instanceof Byte) { final byte byteAttr = ((Byte) attr).byteValue(); final byte byteValue = Byte.parseByte(value); switch (comparator) { case GREATER: return byteAttr >= byteValue; case LESS: return byteAttr <= byteValue; default: return byteAttr == byteValue; } } else if (attr instanceof Short) { final short shortAttr = ((Short) attr).shortValue(); final short shortValue = Short.parseShort(value); switch (comparator) { case GREATER: return shortAttr >= shortValue; case LESS: return shortAttr <= shortValue; default: return shortAttr == shortValue; } } else if (attr instanceof Double) { final double doubleAttr = ((Double) attr).doubleValue(); final double doubleValue = Double.parseDouble(value); switch (comparator) { case GREATER: return doubleAttr >= doubleValue; case LESS: return doubleAttr <= doubleValue; default: return doubleAttr == doubleValue; } } else if (attr instanceof Float) { final float floatAttr = ((Float) attr).floatValue(); final float floatValue = Float.parseFloat(value); switch (comparator) { case GREATER: return floatAttr >= floatValue; case LESS: return floatAttr <= floatValue; default: return floatAttr == floatValue; } } // all other are less frequent and are handled as // Comparables or objects. return compareReflective(value, comparator, attr); } /** * compare in a typed way. * * @param typedVal * the typed filter value. * @param comparator * the comparator. * @param attr * the attribute. * @return true, iff matches. */ @SuppressWarnings("unchecked") private static boolean compareTyped(final Object typedVal, final int comparator, @SuppressWarnings("rawtypes") final Comparable attr) { switch (comparator) { case APPROX: if (typedVal instanceof Character) { return compareString( String.valueOf(((Character) typedVal).toString()), comparator, ((Character) attr).toString()); } case EQUALS: return attr.compareTo(typedVal) == 0; case GREATER: return attr.compareTo(typedVal) >= 0; case LESS: return attr.compareTo(typedVal) <= 0; default: throw new IllegalStateException("Found illegal comparator."); } } /** * compare arrays. * * @param value * the filter value. * @param comparator * the comparator. * @param array * the array. * @return true, iff matches. */ private static boolean compareArray(final String value, final int comparator, final Object[] array) { for (int i = 0; i < array.length; i++) { final Object obj = array[i]; if (obj instanceof String) { if (compareString(value.trim(), comparator, ((String) obj).trim())) { return true; } } else if (obj instanceof Number) { if (compareNumber(value.trim(), comparator, (Number) obj)) { return true; } } else { if (compareReflective(value, comparator, obj)) { return true; } } } return false; } /** * compare in a generic way by using reflection to create a * corresponding object from the filter values string and compare this * object with the attribute. * * @param val * the filter value. * @param comparator * the comparator. * @param attr * the attribute. * @return true, iff matches. */ private static boolean compareReflective(final String val, final int comparator, final Object attr) { if (comparator == SUBSTRING) { return false; } Object typedVal = null; // check if there is a valueOf... try { final Method m = attr.getClass().getDeclaredMethod("valueOf", String.class); if (Modifier.isStatic(m.getModifiers()) && attr.getClass() .isAssignableFrom(m.getReturnType())) { if (!((AccessibleObject) m).isAccessible()) { m.setAccessible(true); } typedVal = m.invoke(null, val); } } catch (final Exception e) { // ignore and move on } try { if (typedVal == null) { // check for constructor... final Constructor<?> constr = attr.getClass() .getConstructor(String.class); if (!((AccessibleObject) constr).isAccessible()) { constr.setAccessible(true); } typedVal = constr.newInstance(new Object[] { val }); } return (attr instanceof Comparable) ? compareTyped(typedVal, comparator, (Comparable<?>) attr) : typedVal.equals(attr); } catch (final Exception didNotWork) { return false; } } /** * strip whitespaces from a string. * * @param s * the string. * @return the stripped string. */ private static String stripWhitespaces(final String s) { return s.replaceAll(" ", ""); } /** * get a string representation of the SimpleFilter. * * @return the string. * @category Object */ public String toString() { return "(" + id + OP[comparator] + (value == null ? "" : escape(value)) + ")"; } private final static String escape(final String s) { final StringBuilder b = new StringBuilder(); final char[] chars = s.toCharArray(); for (int i = 0; i < chars.length; i++) { switch (chars[i]) { case '(': case ')': case '\\': b.append('\\'); default: b.append(chars[i]); } } return b.toString(); } /** * check, if the instance matches another object. * * @param obj * the other object. * @return true, iff the other object is an instance of * RFC1960SimpleFilter and the filter expressions are equal. * @category Object */ public boolean equals(final Object obj) { if (obj instanceof RFC1960SimpleFilter) { final RFC1960SimpleFilter filter = (RFC1960SimpleFilter) obj; return comparator == filter.comparator && id.equals(filter.id) && value.equals(filter.value); } return false; } /** * get the hash code. * * @return the hash code. * @category Object */ public int hashCode() { return toString().hashCode(); } } private static short INSUFFICIENT = 0; private static short NECESSARY = 1; private static short REQUIRED = 3; static List<Capability> filterWithIndex(final Requirement requirement, final String filterStr, final Concierge.CapabilityRegistry capabilityIndex) throws InvalidSyntaxException { final Set<String> values = new HashSet<String>(); final String namespace = requirement.getNamespace(); final Filter filter = fromString(filterStr); final int prefilterResult = prefilter(namespace, filter, capabilityIndex, INSUFFICIENT, false, values); final List<Capability> candidates; if (prefilterResult == REQUIRED) { if (values.size() != 1) { return Collections.emptyList(); } return capabilityIndex.getByValue(namespace, values.iterator().next()); } else if (prefilterResult == NECESSARY) { // FIXME: check if (values.size() != 1) { candidates = capabilityIndex.getAll(namespace); } else { candidates = capabilityIndex.getByKey(namespace, values.iterator().next()); } } else { candidates = capabilityIndex.getAll(namespace); } if (candidates == null) { return Collections.emptyList(); } final ArrayList<Capability> matches = new ArrayList<Capability>(); for (final Capability cap : candidates) { if (filter.matches(cap.getAttributes()) && Concierge .matches0(namespace, requirement, cap, filterStr)) { matches.add(cap); } } return matches; } private static int prefilter(final String namespace, final Filter filter, final Concierge.CapabilityRegistry capabilities, final int state, final boolean inNegation, final Set<String> values) { int newState = state; if (filter instanceof RFC1960Filter) { final RFC1960Filter f = (RFC1960Filter) filter; final int operator = f.operator; if (f.operands.size() == 1) { return prefilter(namespace, f.operands.get(0), capabilities, state, operator == NOT_OPERATOR ? !inNegation : inNegation, values); } for (final Filter next : f.operands) { final int childState = prefilter(namespace, next, capabilities, state, inNegation, values); if (f.operator == AND_OPERATOR) { newState |= childState; } else { // if (f.operator == OR_OPERATOR) newState &= childState; } if (newState == INSUFFICIENT) { // early termination return INSUFFICIENT; } // as soon as we have more than one clause, REQUIRED degrades to // NECESSARY newState ^= 2; } return newState == 3 ? NECESSARY : newState; } else if (filter instanceof RFC1960SimpleFilter) { final RFC1960SimpleFilter f = (RFC1960SimpleFilter) filter; if (namespace.equals(f.id)) { if (f.comparator == EQUALS) { values.add(f.value); return REQUIRED; } else if (f.comparator == PRESENT && inNegation) { return INSUFFICIENT; } else { return NECESSARY; } } else { return INSUFFICIENT; } } else { return INSUFFICIENT; } } }