Package fr.imag.adele.apam.util

Source Code of fr.imag.adele.apam.util.ApamFilter$SetAccessibleAction

/**
* 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;
  }

}
TOP

Related Classes of fr.imag.adele.apam.util.ApamFilter$SetAccessibleAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');