/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
* Licensed 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 fr.imag.adele.apam.util;
/*
* 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.
*/
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.imag.adele.apam.Component;
/**
* This filter implementation is based on the official OSGi filter with
* additional support for the SUPERSET (>*) and SUBSET (<*) operators.
* This filter also has a few optimizations (cached transformation).
*/
@SuppressWarnings("rawtypes")
public class ApamFilter /* implements Filter */{
private static final String UNCHECKED = "unchecked";
/**
* This Map is used for case-insensitive key lookup during filter
* evaluation. This Map implementation only supports the get operation using
* a String key as no other operations are used by the Filter
* implementation.
*/
private static class CaseInsensitiveMap<E> implements Map<String, E> {
private final Map<String, ? extends E> delegate;
/**
* Create a case insensitive dictionary from the specified dictionary.
*
* @param dictionary
* @throws IllegalArgumentException
* If <code>dictionary</code> contains case variants of the
* same key name.
*/
CaseInsensitiveMap(Map<String, ? extends E> delegate) {
this.delegate = delegate;
/*
* verify duplicate case-insensitive keys
*/
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Set<java.util.Map.Entry<String, E>> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public E get(Object key) {
for (String delegateKey : delegate.keySet()) {
if (delegateKey.equalsIgnoreCase((String) key)) {
return delegate.get(delegateKey);
}
}
return null;
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> keySet() {
throw new UnsupportedOperationException();
}
@Override
public E put(String key, E value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends String, ? extends E> m) {
throw new UnsupportedOperationException();
}
@Override
public E remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public Collection<E> values() {
throw new UnsupportedOperationException();
}
}
/**
* Parser class for OSGi filter strings. This class parses the complete
* filter string and builds a tree of Filter objects rooted at the parent.
*/
private static class Parser {
private final String filterstring;
private final boolean ignoreCase;
private final char[] filterChars;
private int pos;
private Component component = null;
Parser(String filterstring, boolean ignoreCase, Component component) {
this.filterstring = filterstring;
this.ignoreCase = ignoreCase;
this.component = component;
filterChars = filterstring.toCharArray();
pos = 0;
}
ApamFilter parse() throws InvalidSyntaxException {
ApamFilter filter;
try {
filter = parseFilter();
} catch (ArrayIndexOutOfBoundsException e) {
throw new InvalidSyntaxException("Filter ended abruptly. Filter : " + filterstring, filterstring, e);
}
if (pos != filterChars.length) {
throw new InvalidSyntaxException("Extraneous trailing characters: " + filterstring.substring(pos), filterstring);
}
return filter;
}
@SuppressWarnings(UNCHECKED)
private ApamFilter parseAnd() throws InvalidSyntaxException {
int lookahead = pos;
skipWhiteSpace();
if (filterChars[pos] != '(') {
pos = lookahead - 1;
return parseItem();
}
List operands = new ArrayList(10);
while (filterChars[pos] == '(') {
ApamFilter child = parseFilter();
operands.add(child);
}
return new ApamFilter(ApamFilter.AND, null, operands.toArray(new ApamFilter[operands.size()]));
}
private String parseAttr() throws InvalidSyntaxException {
skipWhiteSpace();
int begin = pos;
int end = pos;
char c = filterChars[pos];
while ((c != '~') && (c != '<') && (c != '>') && (c != '=') && (c != '(') && (c != ')')) {
if ((c == '<') && (filterChars[pos + 1] == '*')) {
break;
}
if ((c == '*') && (filterChars[pos + 1] == '>')) {
break;
}
pos++;
if (!Character.isWhitespace(c)) {
end = pos;
}
c = filterChars[pos];
}
int length = end - begin;
if (length == 0) {
throw new InvalidSyntaxException("Invalid syntax in filter: " + filterstring + " Missing attr: " + filterstring.substring(pos), filterstring);
}
String str = new String(filterChars, begin, length);
if (ignoreCase) {
str = str.toLowerCase();
}
return str;
}
private ApamFilter parseFilter() throws InvalidSyntaxException {
ApamFilter filter;
skipWhiteSpace();
if (filterChars[pos] != '(') {
throw new InvalidSyntaxException("Missing '(': " + filterstring.substring(pos), filterstring);
}
pos++;
filter = parseFiltercomp();
skipWhiteSpace();
if (filterChars[pos] != ')') {
throw new InvalidSyntaxException("Missing ')': " + filterstring.substring(pos), filterstring);
}
pos++;
skipWhiteSpace();
return filter;
}
private ApamFilter parseFiltercomp() throws InvalidSyntaxException {
skipWhiteSpace();
char c = filterChars[pos];
switch (c) {
case '&': {
pos++;
return parseAnd();
}
case '|': {
pos++;
return parseOr();
}
case '!': {
pos++;
return parseNot();
}
}
return parseItem();
}
private ApamFilter parseItem() throws InvalidSyntaxException {
String attr = parseAttr();
skipWhiteSpace();
switch (filterChars[pos]) {
case '*': {
if (filterChars[pos + 1] == '>') {
pos += 2;
return new ApamFilter(ApamFilter.SUPERSET, attr, parseValue());
}
break;
}
case '~': {
if (filterChars[pos + 1] == '=') {
pos += 2;
return new ApamFilter(ApamFilter.APPROX, attr, parseValue());
}
break;
}
case '>': {
if (filterChars[pos + 1] == '=') {
pos += 2;
return new ApamFilter(ApamFilter.GREATER, attr, parseValue());
}
break;
}
case '<': {
if (filterChars[pos + 1] == '=') {
pos += 2;
return new ApamFilter(ApamFilter.LESS, attr, parseValue());
}
if (filterChars[pos + 1] == '*') {
pos += 2;
return new ApamFilter(ApamFilter.SUBSET, attr, parseValue());
}
break;
}
case '=': {
if (filterChars[pos + 1] == '*') {
int oldpos = pos;
pos += 2;
skipWhiteSpace();
if (filterChars[pos] == ')') {
return new ApamFilter(ApamFilter.PRESENT, attr, null);
}
pos = oldpos;
}
pos++;
Object string = parseSubstring();
if (string instanceof String) {
/*
* TODO If it is a substitution, substitute. If component is
* null, we are only checking that it has substitutions or
* not.
*/
string = Util.toStringAttrValue(Substitute.substitute(null, string, component));
if (string == null) {
logger.debug("Substitution failed. Attribute not set: " + filterstring);
string = "Null";
}
return new ApamFilter(ApamFilter.EQUAL, attr, ((String) string).trim());
}
return new ApamFilter(ApamFilter.SUBSTRING, attr, string);
}
}
throw new InvalidSyntaxException("Invalid operator: " + filterstring.substring(pos), filterstring);
}
private ApamFilter parseNot() throws InvalidSyntaxException {
int lookahead = pos;
skipWhiteSpace();
if (filterChars[pos] != '(') {
pos = lookahead - 1;
return parseItem();
}
ApamFilter child = parseFilter();
return new ApamFilter(ApamFilter.NOT, null, child);
}
@SuppressWarnings(UNCHECKED)
private ApamFilter parseOr() throws InvalidSyntaxException {
int lookahead = pos;
skipWhiteSpace();
if (filterChars[pos] != '(') {
pos = lookahead - 1;
return parseItem();
}
List operands = new ArrayList(10);
while (filterChars[pos] == '(') {
ApamFilter child = parseFilter();
operands.add(child);
}
return new ApamFilter(ApamFilter.OR, null, operands.toArray(new ApamFilter[operands.size()]));
}
@SuppressWarnings(UNCHECKED)
private Object parseSubstring() throws InvalidSyntaxException {
StringBuffer sb = new StringBuffer((int) (filterChars.length - pos));
List operands = new ArrayList(10);
parseloop: while (true) {
char c = filterChars[pos];
switch (c) {
case ')': {
if (sb.length() > 0) {
operands.add(sb.toString());
}
break parseloop;
}
case '(': {
throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
}
case '*': {
if (sb.length() > 0) {
operands.add(sb.toString());
}
sb.setLength(0);
operands.add(null);
pos++;
break;
}
case '\\': {
pos++;
c = filterChars[pos];
/* fall through into default */
}
default: {
sb.append(c);
pos++;
break;
}
}
}
int size = operands.size();
if (size == 0) {
return "";
}
if (size == 1) {
Object single = operands.get(0);
if (single != null) {
return single;
}
}
return operands.toArray(new String[size]);
}
private String parseValue() throws InvalidSyntaxException {
StringBuffer sb = new StringBuffer((int) (filterChars.length - pos));
parseloop: while (true) {
char c = filterChars[pos];
switch (c) {
case ')': {
break parseloop;
}
case '(': {
throw new InvalidSyntaxException("Invalid value: " + filterstring.substring(pos), filterstring);
}
case '\\': {
pos++;
c = filterChars[pos];
/* fall through into default */
}
default: {
sb.append(c);
pos++;
break;
}
}
}
if (sb.length() == 0) {
throw new InvalidSyntaxException("Missing value: " + filterstring.substring(pos), filterstring);
}
// TODO Substitute filter
String ret = Util.toStringAttrValue(Substitute.substitute(null, sb.toString(), component));
if (ret == null) {
throw new InvalidSyntaxException("Substitution failed. Missing value: " + filterstring.substring(pos), filterstring);
}
return ret;
}
private void skipWhiteSpace() {
for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
pos++;
}
}
}
private static class SetAccessibleAction implements PrivilegedAction {
private final AccessibleObject accessible;
SetAccessibleAction(AccessibleObject accessible) {
this.accessible = accessible;
}
@Override
public Object run() {
accessible.setAccessible(true);
return null;
}
}
private static Logger logger = LoggerFactory.getLogger(ApamFilter.class);
/* filter operators */
public static final int EQUAL = 1;
public static final int APPROX = 2;
public static final int GREATER = 3;
public static final int LESS = 4;
public static final int PRESENT = 5;
public static final int SUBSTRING = 6;
public static final int AND = 7;
public static final int OR = 8;
public static final int NOT = 9;
public static final int SUBSET = 10;
public static final int SUPERSET = 11;
private static final Class[] ConstructorType = new Class[] { String.class };
/**
* Map a string for an APPROX (~=) comparison.
*
* This implementation removes white spaces. This is the minimum
* implementation allowed by the OSGi spec.
*
* @param input
* Input string.
* @return String ready for APPROX comparison.
*/
public static String approxString(String input) {
boolean changed = false;
char[] output = input.toCharArray();
int cursor = 0;
for (int i = 0, length = output.length; i < length; i++) {
char c = output[i];
if (Character.isWhitespace(c)) {
changed = true;
continue;
}
output[cursor] = c;
cursor++;
}
return changed ? new String(output, 0, cursor) : input;
}
/**
* Encode the value string such that '(', '*', ')' and '\' are escaped.
*
* @param value
* unencoded value string.
* @return encoded value string.
*/
public static String encodeValue(String value) {
boolean encoded = false;
int inlen = value.length();
int outlen = inlen << 1; /* inlen 2 */
char[] output = new char[outlen];
value.getChars(0, inlen, output, inlen);
int cursor = 0;
for (int i = inlen; i < outlen; i++) {
char c = output[i];
switch (c) {
case '(':
case '*':
case ')':
case '\\': {
output[cursor] = '\\';
cursor++;
encoded = true;
break;
}
}
output[cursor] = c;
cursor++;
}
return encoded ? new String(output, 0, cursor) : value;
}
public static boolean isSubstituteFilter(String filterString, Component component) {
// Inefficient, but simple. Done once.
// ApamFilter f = newInstanceApam(filterString, component) ;
// ApamFilter f2 = newInstance(filterString);
// if (f==null || f2==null) return false ;
// return !f.equals(f2);
return (filterString.indexOf("\"$") == -1 && filterString.indexOf("\"@") == -1);
}
/**
* Constructs a {@link ApamFilter} object. This filter object may be used to
* match a {@link org.osgi.framework.ServiceReference} or a Dictionary.
*
* <p>
* If the filter cannot be parsed, an
* {@link org.osgi.framework.InvalidSyntaxException} will be thrown with a
* human readable message where the filter became unparsable.
*
* @param filterString
* the filter string.
* @exception InvalidSyntaxException
* If the filter parameter contains an invalid filter string
* that cannot be parsed.
*/
public static ApamFilter newInstance(String filterString) {
try {
return ApamFilter.newInstance(filterString, true);
} catch (InvalidSyntaxException e) {
logger.error(e.getMessage());
}
return null;
}
private static ApamFilter newInstance(String filterString, boolean ignoreCase) throws InvalidSyntaxException {
return new Parser(filterString, ignoreCase, null).parse();
}
/**
* For component null, only checks syntax and returns a result different
* than newInstance.
*
* @param filterString
* @param component
* @return
*/
public static ApamFilter newInstanceApam(String filterString, Component component) {
try {
return new Parser(filterString, false, component).parse();
} catch (Exception e) {
logger.error("invalid filter syntax " + filterString);
return null;
}
}
/** filter operation */
public final int op;
/** filter attribute or null if operation AND, OR or NOT */
public final String attr;
/** filter operands */
public final Object value;
/** optim in case of version */
private final Object converted;
/* normalized filter string for Filter object */
private transient volatile String filterString;
ApamFilter(int operation, String attr, Object value) {
op = operation;
this.attr = attr;
this.value = value;
Object conv = null;
try {
if ((op == ApamFilter.SUBSET) || (op == ApamFilter.SUPERSET)) {
conv = getSet(value);
} else if ("version".equalsIgnoreCase(attr)) {
if (value instanceof String) {
conv = Version.parseVersion((String) value);
} else if (value instanceof Version) {
conv = value;
}
}
} catch (Exception exc) {
// Ignore any conversion issue
}
converted = conv;
}
@SuppressWarnings(UNCHECKED)
private boolean compare(int operation, Object value1, Object value2) {
if ((op == ApamFilter.SUPERSET) || (op == ApamFilter.SUBSET)) {
Collection s1 = getSet(value1);
Collection s2 = converted instanceof Collection ? (Collection) converted : getSet(value2);
if (op == ApamFilter.SUPERSET) {
return s1.containsAll(s2);
} else {
return s2.containsAll(s1);
}
}
if (value1 == null) {
return false;
}
if (value1 instanceof String) {
return compareString(operation, (String) value1, value2);
}
Class clazz = value1.getClass();
if (clazz.isArray()) {
Class type = clazz.getComponentType();
if (type.isPrimitive()) {
return comparePrimitiveArray(operation, type, value1, value2);
}
return compareObjectArray(operation, (Object[]) value1, value2);
}
if (value1 instanceof Version) {
if (converted != null) {
switch (operation) {
case APPROX:
case EQUAL: {
return ((Version) value1).compareTo(converted) == 0;
}
case GREATER: {
return ((Version) value1).compareTo(converted) >= 0;
}
case LESS: {
return ((Version) value1).compareTo(converted) <= 0;
}
}
} else {
return compareComparable(operation, (Version) value1, value2);
}
}
if (value1 instanceof Collection) {
return compareCollection(operation, (Collection) value1, value2);
}
if (value1 instanceof Integer) {
return compareInteger(operation, ((Integer) value1).intValue(), value2);
}
if (value1 instanceof Long) {
return compareLong(operation, ((Long) value1).longValue(), value2);
}
if (value1 instanceof Byte) {
return compareByte(operation, ((Byte) value1).byteValue(), value2);
}
if (value1 instanceof Short) {
return compareShort(operation, ((Short) value1).shortValue(), value2);
}
if (value1 instanceof Character) {
return compareCharacter(operation, ((Character) value1).charValue(), value2);
}
if (value1 instanceof Float) {
return compareFloat(operation, ((Float) value1).floatValue(), value2);
}
if (value1 instanceof Double) {
return compareDouble(operation, ((Double) value1).doubleValue(), value2);
}
if (value1 instanceof Boolean) {
return compareBoolean(operation, ((Boolean) value1).booleanValue(), value2);
}
if (value1 instanceof Comparable) {
return compareComparable(operation, (Comparable) value1, value2);
}
return compareUnknown(operation, value1, value2); // RFC 59
}
private boolean compareBoolean(int operation, boolean boolval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
boolean boolval2 = Boolean.valueOf(((String) value2).trim()).booleanValue();
switch (operation) {
case APPROX:
case EQUAL:
case GREATER:
case LESS: {
return boolval == boolval2;
}
}
return false;
}
private boolean compareByte(int operation, byte byteval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
byte byteval2;
try {
byteval2 = Byte.parseByte(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return byteval == byteval2;
}
case GREATER: {
return byteval >= byteval2;
}
case LESS: {
return byteval <= byteval2;
}
}
return false;
}
private boolean compareCharacter(int operation, char charval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
char charval2;
try {
charval2 = ((String) value2).charAt(0);
} catch (IndexOutOfBoundsException e) {
return false;
}
switch (operation) {
case EQUAL: {
return charval == charval2;
}
case APPROX: {
return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
}
case GREATER: {
return charval >= charval2;
}
case LESS: {
return charval <= charval2;
}
}
return false;
}
@SuppressWarnings(UNCHECKED)
private boolean compareCollection(int operation, Collection collection, Object value2) {
if ((op == ApamFilter.SUBSET) || (op == ApamFilter.SUPERSET)) {
Set set = new HashSet();
if (value2 != null) {
StringTokenizer st = new StringTokenizer(value2.toString(), ",");
while (st.hasMoreTokens()) {
set.add(st.nextToken().trim());
}
}
if (op == ApamFilter.SUBSET) {
return set.containsAll(collection);
} else {
return collection.containsAll(set);
}
}
for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
if (compare(operation, iterator.next(), value2)) {
return true;
}
}
return false;
}
@SuppressWarnings(UNCHECKED)
private boolean compareComparable(int operation, Comparable value1, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
Constructor constructor;
try {
constructor = value1.getClass().getConstructor(ApamFilter.ConstructorType);
} catch (NoSuchMethodException e) {
return false;
}
try {
if (!constructor.isAccessible()) {
AccessController.doPrivileged(new SetAccessibleAction(constructor));
}
value2 = constructor.newInstance(new Object[] { ((String) value2).trim() });
} catch (IllegalAccessException e) {
return false;
} catch (InvocationTargetException e) {
return false;
} catch (InstantiationException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return value1.compareTo(value2) == 0;
}
case GREATER: {
return value1.compareTo(value2) >= 0;
}
case LESS: {
return value1.compareTo(value2) <= 0;
}
}
return false;
}
private boolean compareDouble(int operation, double doubleval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
double doubleval2;
try {
doubleval2 = Double.parseDouble(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return Double.compare(doubleval, doubleval2) == 0;
}
case GREATER: {
return Double.compare(doubleval, doubleval2) >= 0;
}
case LESS: {
return Double.compare(doubleval, doubleval2) <= 0;
}
}
return false;
}
private boolean compareFloat(int operation, float floatval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
float floatval2;
try {
floatval2 = Float.parseFloat(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return Float.compare(floatval, floatval2) == 0;
}
case GREATER: {
return Float.compare(floatval, floatval2) >= 0;
}
case LESS: {
return Float.compare(floatval, floatval2) <= 0;
}
}
return false;
}
private boolean compareInteger(int operation, int intval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
int intval2;
try {
intval2 = Integer.parseInt(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return intval == intval2;
}
case GREATER: {
return intval >= intval2;
}
case LESS: {
return intval <= intval2;
}
}
return false;
}
private boolean compareLong(int operation, long longval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
long longval2;
try {
longval2 = Long.parseLong(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return longval == longval2;
}
case GREATER: {
return longval >= longval2;
}
case LESS: {
return longval <= longval2;
}
}
return false;
}
private boolean compareObjectArray(int operation, Object[] array, Object value2) {
for (Object element : array) {
if (compare(operation, element, value2)) {
return true;
}
}
return false;
}
private boolean comparePrimitiveArray(int operation, Class type, Object primarray, Object value2) {
if (Integer.TYPE.isAssignableFrom(type)) {
int[] array = (int[]) primarray;
for (int element : array) {
if (compareInteger(operation, element, value2)) {
return true;
}
}
return false;
}
if (Long.TYPE.isAssignableFrom(type)) {
long[] array = (long[]) primarray;
for (long element : array) {
if (compareLong(operation, element, value2)) {
return true;
}
}
return false;
}
if (Byte.TYPE.isAssignableFrom(type)) {
byte[] array = (byte[]) primarray;
for (byte element : array) {
if (compareByte(operation, element, value2)) {
return true;
}
}
return false;
}
if (Short.TYPE.isAssignableFrom(type)) {
short[] array = (short[]) primarray;
for (short element : array) {
if (compareShort(operation, element, value2)) {
return true;
}
}
return false;
}
if (Character.TYPE.isAssignableFrom(type)) {
char[] array = (char[]) primarray;
for (char element : array) {
if (compareCharacter(operation, element, value2)) {
return true;
}
}
return false;
}
if (Float.TYPE.isAssignableFrom(type)) {
float[] array = (float[]) primarray;
for (float element : array) {
if (compareFloat(operation, element, value2)) {
return true;
}
}
return false;
}
if (Double.TYPE.isAssignableFrom(type)) {
double[] array = (double[]) primarray;
for (double element : array) {
if (compareDouble(operation, element, value2)) {
return true;
}
}
return false;
}
if (Boolean.TYPE.isAssignableFrom(type)) {
boolean[] array = (boolean[]) primarray;
for (boolean element : array) {
if (compareBoolean(operation, element, value2)) {
return true;
}
}
return false;
}
return false;
}
private boolean compareShort(int operation, short shortval, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
short shortval2;
try {
shortval2 = Short.parseShort(((String) value2).trim());
} catch (IllegalArgumentException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL: {
return shortval == shortval2;
}
case GREATER: {
return shortval >= shortval2;
}
case LESS: {
return shortval <= shortval2;
}
}
return false;
}
private boolean compareString(int operation, String string, Object value2) {
switch (operation) {
case SUBSTRING: {
String[] substrings = (String[]) value2;
int pos = 0;
for (int i = 0, size = substrings.length; i < size; i++) {
String substr = substrings[i];
if (i + 1 < size) /* if this is not that last substr */{
if (substr == null) /* * */{
String substr2 = substrings[i + 1];
if (substr2 == null) {
continue; /* ignore first star */
}
/* xxx */
int index = string.indexOf(substr2, pos);
if (index == -1) {
return false;
}
pos = index + substr2.length();
if (i + 2 < size) {
// substrings, increment
// over the string we just
// matched; otherwise need
// to do the last substr
// check
i++;
}
} else /* xxx */{
int len = substr.length();
if (string.regionMatches(pos, substr, 0, len)) {
pos += len;
} else {
return false;
}
}
} else /* last substr */{
if (substr == null) /* * */{
return true;
}
/* xxx */
return string.endsWith(substr);
}
}
return true;
}
case EQUAL: {
return string.equals(value2);
}
case APPROX: {
string = ApamFilter.approxString(string);
String string2 = ApamFilter.approxString((String) value2);
return string.equalsIgnoreCase(string2);
}
case GREATER: {
return string.compareTo((String) value2) >= 0;
}
case LESS: {
return string.compareTo((String) value2) <= 0;
}
}
return false;
}
@SuppressWarnings(UNCHECKED)
private boolean compareUnknown(int operation, Object value1, Object value2) {
if (operation == ApamFilter.SUBSTRING) {
return false;
}
Constructor constructor;
try {
constructor = value1.getClass().getConstructor(ApamFilter.ConstructorType);
} catch (NoSuchMethodException e) {
return false;
}
try {
if (!constructor.isAccessible()) {
AccessController.doPrivileged(new SetAccessibleAction(constructor));
}
value2 = constructor.newInstance(new Object[] { ((String) value2).trim() });
} catch (IllegalAccessException e) {
return false;
} catch (InvocationTargetException e) {
return false;
} catch (InstantiationException e) {
return false;
}
switch (operation) {
case APPROX:
case EQUAL:
case GREATER:
case LESS: {
return value1.equals(value2);
}
}
return false;
}
/**
* Compares this <code>Filter</code> to another <code>Filter</code>.
*
* <p>
* This implementation returns the result of calling
* <code>this.toString().equals(obj.toString()</code>.
*
* @param obj
* The object to compare against this <code>Filter</code>.
* @return If the other object is a <code>Filter</code> object, then returns
* the result of calling
* <code>this.toString().equals(obj.toString()</code>;
* <code>false</code> otherwise.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof ApamFilter)) {
return false;
}
return toString().equals(obj.toString());
}
@SuppressWarnings(UNCHECKED)
private Collection getSet(Object value) {
Collection s;
if (value instanceof Set) {
s = (Set) value;
} else if (value instanceof Collection) {
s = (Collection) value;
if (s.size() > 1) {
s = new HashSet(s);
}
} else if (value != null) {
String v = value.toString();
if (v.indexOf(',') < 0) {
s = Collections.singleton(v);
} else {
// Knowing this is a collection, they have '{' and '}' as prolog
// and epilog. Thus they should be removed in order to tokenize
// it
StringTokenizer st = new StringTokenizer(value.toString().replaceAll("[\\{\\}]", ""), ",");
s = new HashSet();
while (st.hasMoreTokens()) {
s.add(st.nextToken().trim());
}
}
} else {
s = Collections.emptySet();
}
return s;
}
/**
* Returns the hashCode for this <code>Filter</code>.
*
* <p>
* This implementation returns the result of calling
* <code>this.toString().hashCode()</code>.
*
* @return The hashCode of this <code>Filter</code>.
*/
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* Filter using a <code>Map</code>. This <code>Filter</code> is executed
* using the specified <code>Map</code>'s keys and values. The keys are case
* insensitively matched with this <code>Filter</code>.
*
* @param dictionary
* The <code>Map</code> whose keys are used in the match.
* @return <code>true</code> if the <code>Dictionary</code>'s keys and
* values match this filter; <code>false</code> otherwise.
* @throws IllegalArgumentException
* If <code>dictionary</code> contains case variants of the same
* key name.
*
*/
@SuppressWarnings(UNCHECKED)
public boolean match(Map map) {
return match0(new CaseInsensitiveMap<Object>(map));
}
private boolean match0(Map properties) {
switch (op) {
case AND: {
ApamFilter[] filters = (ApamFilter[]) value;
for (int i = 0, size = filters.length; i < size; i++) {
if (!filters[i].match0(properties)) {
return false;
}
}
return true;
}
case OR: {
ApamFilter[] filters = (ApamFilter[]) value;
for (ApamFilter filter : filters) {
if (filter.match0(properties)) {
return true;
}
}
return false;
}
case NOT: {
ApamFilter filter = (ApamFilter) value;
return !filter.match0(properties);
}
case SUBSTRING:
case EQUAL:
case GREATER:
case LESS:
case APPROX:
case SUBSET:
case SUPERSET: {
Object prop = (properties == null) ? null : properties.get(attr);
if (Substitute.isSubstitution(value)) {
// TODO : checking that it is not a substitution. Only to be
// sure. Should not happen.
// if ((value instanceof String) &&
// (((String)value).charAt(0)=='$' ||
// ((String)value).charAt(0)=='@')) {
logger.error("Filter attribute " + attr + " is a substitution: " + (String) value + ". in Filter : " + filterString);
}
return compare(op, prop, value);
}
case PRESENT: {
Object prop = (properties == null) ? null : properties.get(attr);
return prop != null;
}
}
return false;
}
/**
* Filter with case sensitivity using a <code>Map</code>. This
* <code>Filter</code> is executed using the specified <code>Map</code>'s
* keys and values. The keys are case sensitively matched with this
* <code>Filter</code>.
*
* @param map
* The <code>Map</code> whose keys are used in the match.
* @return <code>true</code> if the <code>Map</code>'s keys and values match
* this filter; <code>false</code> otherwise.
* @throws IllegalArgumentException
* If <code>map</code> contains case variants of the same key
* name.
*/
public boolean matchCase(Map map) {
return match0(map);
}
/**
* Returns this <code>Filter</code>'s normalized filter string.
* <p>
* The filter string is normalized by removing whitespace which does not
* affect the meaning of the filter.
*
* @return This <code>Filter</code>'s filter string.
*/
private String normalize() {
StringBuffer sb = new StringBuffer();
sb.append('(');
switch (op) {
case AND: {
sb.append('&');
ApamFilter[] filters = (ApamFilter[]) value;
for (ApamFilter filter : filters) {
sb.append(filter.normalize());
}
break;
}
case OR: {
sb.append('|');
ApamFilter[] filters = (ApamFilter[]) value;
for (ApamFilter filter : filters) {
sb.append(filter.normalize());
}
break;
}
case NOT: {
sb.append('!');
ApamFilter filter = (ApamFilter) value;
sb.append(filter.normalize());
break;
}
case SUBSTRING: {
sb.append(attr);
sb.append('=');
String[] substrings = (String[]) value;
for (String substr : substrings) {
if (substr == null) /* * */{
sb.append('*');
} else /* xxx */{
sb.append(ApamFilter.encodeValue(substr));
}
}
break;
}
case EQUAL: {
sb.append(attr);
sb.append('=');
sb.append(ApamFilter.encodeValue((String) value));
break;
}
case GREATER: {
sb.append(attr);
sb.append(">=");
sb.append(ApamFilter.encodeValue((String) value));
break;
}
case LESS: {
sb.append(attr);
sb.append("<=");
sb.append(ApamFilter.encodeValue((String) value));
break;
}
case APPROX: {
sb.append(attr);
sb.append("~=");
sb.append(ApamFilter.encodeValue(ApamFilter.approxString((String) value)));
break;
}
case PRESENT: {
sb.append(attr);
sb.append("=*");
break;
}
case SUBSET: {
sb.append(attr);
sb.append("<*");
sb.append(ApamFilter.encodeValue(ApamFilter.approxString((String) value)));
break;
}
case SUPERSET: {
sb.append(attr);
sb.append("*>");
sb.append(ApamFilter.encodeValue(ApamFilter.approxString((String) value)));
break;
}
}
sb.append(')');
return sb.toString();
}
/**
* Returns this <code>Filter</code>'s filter string.
* <p>
* The filter string is normalized by removing whitespace which does not
* affect the meaning of the filter.
*
* @return This <code>Filter</code>'s filter string.
*/
@Override
public String toString() {
String result = filterString;
if (result == null) {
filterString = result = normalize();
}
return result;
}
}