Package org.osgi.framework

Source Code of org.osgi.framework.FrameworkUtil$ServiceReferenceDictionary

/*
* Copyright (c) OSGi Alliance (2005, 2009). All Rights Reserved.
*
* 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 org.osgi.framework;

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.Dictionary;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.security.auth.x500.X500Principal;

/**
* Framework Utility class.
*
* <p>
* This class contains utility methods which access Framework functions that may
* be useful to bundles.
*
* @since 1.3
* @ThreadSafe
* @version $Revision: 8080 $
*/
public class FrameworkUtil {
  /**
   * FrameworkUtil objects may not be constructed.
   */
  private FrameworkUtil() {
    // private empty constructor to prevent construction
  }

  /**
   * Creates a <code>Filter</code> object. This <code>Filter</code> object may
   * be used to match a <code>ServiceReference</code> object or a
   * <code>Dictionary</code> object.
   *
   * <p>
   * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be
   * thrown with a human readable message where the filter became unparsable.
   *
   * <p>
   * This method returns a Filter implementation which may not perform as well
   * as the framework implementation-specific Filter implementation returned
   * by {@link BundleContext#createFilter(String)}.
   *
   * @param filter The filter string.
   * @return A <code>Filter</code> object encapsulating the filter string.
   * @throws InvalidSyntaxException If <code>filter</code> contains an invalid
   *         filter string that cannot be parsed.
   * @throws NullPointerException If <code>filter</code> is null.
   *
   * @see Filter
   */
  public static Filter createFilter(String filter)
      throws InvalidSyntaxException {
    return FilterImpl.newInstance(filter);
  }

  /**
   * Match a Distinguished Name (DN) chain against a pattern. DNs can be
   * matched using wildcards. A wildcard ('*' &#92;u002A) replaces all
   * possible values. Due to the structure of the DN, the comparison is more
   * complicated than string-based wildcard matching.
   * <p>
   * A wildcard can stand for zero or more DNs in a chain, a number of
   * relative distinguished names (RDNs) within a DN, or the value of a single
   * RDN. The DNs in the chain and the matching pattern are canonicalized
   * before processing. This means, among other things, that spaces must be
   * ignored, except in values.
   * <p>
   * The format of a wildcard match pattern is:
   *
   * <pre>
   * matchPattern  ::= dn-match ( ';' dn-match ) *
   * dn-match   ::= ( '*' | rdn-match ) ( ',' rdn-match ) * | '-'
   * rdn-match   ::= name '=' value-match
   * value-match   ::= '*' | value-star
   * value-star   ::= &lt; value, requires escaped '*' and '-' &gt;
   * </pre>
   * <p>
   * The most simple case is a single wildcard; it must match any DN. A
   * wildcard can also replace the first list of RDNs of a DN. The first RDNs
   * are the least significant. Such lists of matched RDNs can be empty.
   * <p>
   * For example, a match pattern with a wildcard that matches all all DNs
   * that end with RDNs of o=ACME and c=US would look like this:
   *
   * <pre>
   * *, o=ACME, c=US
   * </pre>
   *
   * This match pattern would match the following DNs:
   *
   * <pre>
   * cn = Bugs Bunny, o = ACME, c = US
   * ou = Carrots, cn=Daffy Duck, o=ACME, c=US
   * street = 9C\, Avenue St. Drézéry, o=ACME, c=US
   * dc=www, dc=acme, dc=com, o=ACME, c=US
   * o=ACME, c=US
   * </pre>
   *
   * The following DNs would not match:
   *
   * <pre>
   * street = 9C\, Avenue St. Drézéry, o=ACME, c=FR
   * dc=www, dc=acme, dc=com, c=US
   * </pre>
   *
   * If a wildcard is used for a value of an RDN, the value must be exactly *.
   * The wildcard must match any value, and no substring matching must be
   * done. For example:
   *
   * <pre>
   * cn=*,o=ACME,c=*
   * </pre>
   *
   * This match pattern with wildcard must match the following DNs:
   *
   * <pre>
   * cn=Bugs Bunny,o=ACME,c=US
   * cn = Daffy Duck , o = ACME , c = US
   * cn=Road Runner, o=ACME, c=NL
   * </pre>
   *
   * But not:
   *
   * <pre>
   * o=ACME, c=NL
   * dc=acme.com, cn=Bugs Bunny, o=ACME, c=US
   * </pre>
   *
   * <p>
   * A match pattern may contain a chain of DN match patterns. The
   * semicolon(';' &#92;u003B) must be used to separate DN match patterns in a
   * chain. Wildcards can also be used to match against a complete DN within a
   * chain.
   * <p>
   * The following example matches a certificate signed by Tweety Inc. in the
   * US.
   * </p>
   *
   * <pre>
   * * ; ou=S &amp; V, o=Tweety Inc., c=US
   * </pre>
   * <p>
   * The wildcard ('*') matches zero or one DN in the chain, however,
   * sometimes it is necessary to match a longer chain. The minus sign ('-'
   * &#92;u002D) represents zero or more DNs, whereas the asterisk only
   * represents a single DN. For example, to match a DN where the Tweety Inc.
   * is in the DN chain, use the following expression:
   * </p>
   *
   * <pre>
   * - ; *, o=Tweety Inc., c=US
   * </pre>
   *
   * @param matchPattern The pattern against which to match the DN chain.
   * @param dnChain The DN chain to match against the specified pattern. Each
   *        element of the chain must be of type <code>String</code> and use
   *        the format defined in RFC 2253.
   * @return <code>true</code> If the pattern matches the DN chain; otherwise
   *         <code>false</code> is returned.
   * @throws IllegalArgumentException If the specified match pattern or DN
   *         chain is invalid.
   * @since 1.5
   */
  public static boolean matchDistinguishedNameChain(String matchPattern,
      List /* <String> */dnChain) {
    return DNChainMatching.match(matchPattern, dnChain);
  }

  /**
   * Return a <code>Bundle</code> for the specified bundle class. The returned
   * <code>Bundle</code> is the bundle associated with the bundle class loader
   * which defined the specified class.
   *
   * @param classFromBundle A class defined by a bundle class loader.
   * @return A <code>Bundle</code> for the specified bundle class or
   *         <code>null</code> if the specified class was not defined by a
   *         bundle class loader.
   * @since 1.5
   */
  public static Bundle getBundle(final Class classFromBundle) {
    // We use doPriv since the caller may not have permission
    // to call getClassLoader.
    Object cl = AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return classFromBundle.getClassLoader();
      }
    });

    if (cl instanceof BundleReference) {
      return ((BundleReference) cl).getBundle();
    }
    return null;
  }

  /**
   * RFC 1960-based Filter. Filter objects can be created by calling the
   * constructor with the desired filter string. A Filter object can be called
   * numerous times to determine if the match argument matches the filter
   * string that was used to create the Filter object.
   *
   * <p>
   * The syntax of a filter string is the string representation of LDAP search
   * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
   * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
   * be noted that RFC 2254: <i>A String Representation of LDAP Search
   * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
   * RFC 1960 but only adds extensible matching and is not applicable for this
   * API.
   *
   * <p>
   * The string representation of an LDAP search filter is defined by the
   * following grammar. It uses a prefix format.
   *
   * <pre>
   *   &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
   *   &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
   *   &lt;and&gt; ::= '&amp;' &lt;filterlist&gt;
   *   &lt;or&gt; ::= '|' &lt;filterlist&gt;
   *   &lt;not&gt; ::= '!' &lt;filter&gt;
   *   &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
   *   &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
   *   &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
   *   &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
   *   &lt;equal&gt; ::= '='
   *   &lt;approx&gt; ::= '&tilde;='
   *   &lt;greater&gt; ::= '&gt;='
   *   &lt;less&gt; ::= '&lt;='
   *   &lt;present&gt; ::= &lt;attr&gt; '=*'
   *   &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
   *   &lt;initial&gt; ::= NULL | &lt;value&gt;
   *   &lt;any&gt; ::= '*' &lt;starval&gt;
   *   &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
   *   &lt;final&gt; ::= NULL | &lt;value&gt;
   * </pre>
   *
   * <code>&lt;attr&gt;</code> is a string representing an attribute, or key,
   * in the properties objects of the registered services. Attribute names are
   * not case sensitive; that is cn and CN both refer to the same attribute.
   * <code>&lt;value&gt;</code> is a string representing the value, or part of
   * one, of a key in the properties objects of the registered services. If a
   * <code>&lt;value&gt;</code> must contain one of the characters '
   * <code>*</code>' or '<code>(</code>' or '<code>)</code>', these characters
   * should be escaped by preceding them with the backslash '<code>\</code>'
   * character. Note that although both the <code>&lt;substring&gt;</code> and
   * <code>&lt;present&gt;</code> productions can produce the <code>'attr=*'</code>
   * construct, this construct is used only to denote a presence filter.
   *
   * <p>
   * Examples of LDAP filters are:
   *
   * <pre>
   *   &quot;(cn=Babs Jensen)&quot;
   *   &quot;(!(cn=Tim Howes))&quot;
   *   &quot;(&amp;(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
   *   &quot;(o=univ*of*mich*)&quot;
   * </pre>
   *
   * <p>
   * The approximate match (<code>~=</code>) is implementation specific but
   * should at least ignore case and white space differences. Optional are
   * codes like soundex or other smart "closeness" comparisons.
   *
   * <p>
   * Comparison of values is not straightforward. Strings are compared
   * differently than numbers and it is possible for a key to have multiple
   * values. Note that that keys in the match argument must always be strings.
   * The comparison is defined by the object type of the key's value. The
   * following rules apply for comparison:
   *
   * <blockquote>
   * <TABLE BORDER=0>
   * <TR>
   * <TD><b>Property Value Type </b></TD>
   * <TD><b>Comparison Type</b></TD>
   * </TR>
   * <TR>
   * <TD>String</TD>
   * <TD>String comparison</TD>
   * </TR>
   * <TR valign=top>
   * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
   * <TD>numerical comparison</TD>
   * </TR>
   * <TR>
   * <TD>Character</TD>
   * <TD>character comparison</TD>
   * </TR>
   * <TR>
   * <TD>Boolean</TD>
   * <TD>equality comparisons only</TD>
   * </TR>
   * <TR>
   * <TD>[] (array)</TD>
   * <TD>recursively applied to values</TD>
   * </TR>
   * <TR>
   * <TD>Collection</TD>
   * <TD>recursively applied to values</TD>
   * </TR>
   * </TABLE>
   * Note: arrays of primitives are also supported. </blockquote>
   *
   * A filter matches a key that has multiple values if it matches at least
   * one of those values. For example,
   *
   * <pre>
   * Dictionary d = new Hashtable();
   * d.put(&quot;cn&quot;, new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
   * </pre>
   *
   * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
   *
   * <p>
   * A filter component that references a key having an unrecognizable data
   * type will evaluate to <code>false</code> .
   */
  private static class FilterImpl implements Filter {
    /* filter operators */
    private static final int      EQUAL    = 1;
    private static final int      APPROX    = 2;
    private static final int      GREATER    = 3;
    private static final int      LESS    = 4;
    private static final int      PRESENT    = 5;
    private static final int      SUBSTRING  = 6;
    private static final int      AND      = 7;
    private static final int      OR      = 8;
    private static final int      NOT      = 9;

    /** filter operation */
    private final int          op;
    /** filter attribute or null if operation AND, OR or NOT */
    private final String        attr;
    /** filter operands */
    private final Object        value;

    /* normalized filter string for Filter object */
    private transient volatile String  filterString;

    /**
     * Constructs a {@link FilterImpl} object. This filter object may be
     * used to match a {@link ServiceReference} or a Dictionary.
     *
     * <p>
     * If the filter cannot be parsed, an {@link 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.
     */
    static FilterImpl newInstance(String filterString)
        throws InvalidSyntaxException {
      return new Parser(filterString).parse();
    }

    FilterImpl(int operation, String attr, Object value) {
      this.op = operation;
      this.attr = attr;
      this.value = value;
    }

    /**
     * Filter using a service's properties.
     * <p>
     * This <code>Filter</code> is executed using the keys and values of the
     * referenced service's properties. The keys are case insensitively
     * matched with this <code>Filter</code>.
     *
     * @param reference The reference to the service whose properties are
     *        used in the match.
     * @return <code>true</code> if the service's properties match this
     *         <code>Filter</code>; <code>false</code> otherwise.
     */
    public boolean match(ServiceReference reference) {
      return match0(new ServiceReferenceDictionary(reference));
    }

    /**
     * Filter using a <code>Dictionary</code>. This <code>Filter</code> is
     * executed using the specified <code>Dictionary</code>'s keys and
     * values. The keys are case insensitively matched with this
     * <code>Filter</code>.
     *
     * @param dictionary The <code>Dictionary</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.
     */
    public boolean match(Dictionary dictionary) {
      return match0(new CaseInsensitiveDictionary(dictionary));
    }

    /**
     * Filter with case sensitivity using a <code>Dictionary</code>. This
     * <code>Filter</code> is executed using the specified
     * <code>Dictionary</code>'s keys and values. The keys are case
     * sensitively matched with this <code>Filter</code>.
     *
     * @param dictionary The <code>Dictionary</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.
     * @since 1.3
     */
    public boolean matchCase(Dictionary dictionary) {
      return match0(dictionary);
    }

    /**
     * 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.
     */
    public String toString() {
      String result = filterString;
      if (result == null) {
        filterString = result = normalize();
      }
      return result;
    }

    /**
     * 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('&');

          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            sb.append(filters[i].normalize());
          }

          break;
        }

        case OR : {
          sb.append('|');

          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            sb.append(filters[i].normalize());
          }

          break;
        }

        case NOT : {
          sb.append('!');
          FilterImpl filter = (FilterImpl) value;
          sb.append(filter.normalize());

          break;
        }

        case SUBSTRING : {
          sb.append(attr);
          sb.append('=');

          String[] substrings = (String[]) value;

          for (int i = 0, size = substrings.length; i < size; i++) {
            String substr = substrings[i];

            if (substr == null) /* * */{
              sb.append('*');
            }
            else /* xxx */{
              sb.append(encodeValue(substr));
            }
          }

          break;
        }
        case EQUAL : {
          sb.append(attr);
          sb.append('=');
          sb.append(encodeValue((String) value));

          break;
        }
        case GREATER : {
          sb.append(attr);
          sb.append(">=");
          sb.append(encodeValue((String) value));

          break;
        }
        case LESS : {
          sb.append(attr);
          sb.append("<=");
          sb.append(encodeValue((String) value));

          break;
        }
        case APPROX : {
          sb.append(attr);
          sb.append("~=");
          sb.append(encodeValue(approxString((String) value)));

          break;
        }

        case PRESENT : {
          sb.append(attr);
          sb.append("=*");

          break;
        }
      }

      sb.append(')');

      return sb.toString();
    }

    /**
     * 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.
     */
    public boolean equals(Object obj) {
      if (obj == this) {
        return true;
      }

      if (!(obj instanceof Filter)) {
        return false;
      }

      return this.toString().equals(obj.toString());
    }

    /**
     * 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>.
     */
    public int hashCode() {
      return this.toString().hashCode();
    }

    /**
     * Internal match routine. Dictionary parameter must support
     * case-insensitive get.
     *
     * @param properties A dictionary whose keys are used in the match.
     * @return If the Dictionary's keys match the filter, return
     *         <code>true</code>. Otherwise, return <code>false</code>.
     */
    private boolean match0(Dictionary properties) {
      switch (op) {
        case AND : {
          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            if (!filters[i].match0(properties)) {
              return false;
            }
          }

          return true;
        }

        case OR : {
          FilterImpl[] filters = (FilterImpl[]) value;
          for (int i = 0, size = filters.length; i < size; i++) {
            if (filters[i].match0(properties)) {
              return true;
            }
          }

          return false;
        }

        case NOT : {
          FilterImpl filter = (FilterImpl) value;

          return !filter.match0(properties);
        }

        case SUBSTRING :
        case EQUAL :
        case GREATER :
        case LESS :
        case APPROX : {
          Object prop = (properties == null) ? null : properties
              .get(attr);

          return compare(op, prop, value);
        }

        case PRESENT : {
          Object prop = (properties == null) ? null : properties
              .get(attr);

          return prop != null;
        }
      }

      return false;
    }

    /**
     * Encode the value string such that '(', '*', ')' and '\' are escaped.
     *
     * @param value unencoded value string.
     * @return encoded value string.
     */
    private 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;
    }

    private boolean compare(int operation, Object value1, Object value2) {
      if (value1 == null) {
        return false;
      }
      if (value1 instanceof String) {
        return compare_String(operation, (String) value1, value2);
      }

      Class clazz = value1.getClass();
      if (clazz.isArray()) {
        Class type = clazz.getComponentType();
        if (type.isPrimitive()) {
          return compare_PrimitiveArray(operation, type, value1,
              value2);
        }
        return compare_ObjectArray(operation, (Object[]) value1, value2);
      }
      if (value1 instanceof Collection) {
        return compare_Collection(operation, (Collection) value1,
            value2);
      }
      if (value1 instanceof Integer) {
        return compare_Integer(operation,
            ((Integer) value1).intValue(), value2);
      }
      if (value1 instanceof Long) {
        return compare_Long(operation, ((Long) value1).longValue(),
            value2);
      }
      if (value1 instanceof Byte) {
        return compare_Byte(operation, ((Byte) value1).byteValue(),
            value2);
      }
      if (value1 instanceof Short) {
        return compare_Short(operation, ((Short) value1).shortValue(),
            value2);
      }
      if (value1 instanceof Character) {
        return compare_Character(operation, ((Character) value1)
            .charValue(), value2);
      }
      if (value1 instanceof Float) {
        return compare_Float(operation, ((Float) value1).floatValue(),
            value2);
      }
      if (value1 instanceof Double) {
        return compare_Double(operation, ((Double) value1)
            .doubleValue(), value2);
      }
      if (value1 instanceof Boolean) {
        return compare_Boolean(operation, ((Boolean) value1)
            .booleanValue(), value2);
      }
      if (value1 instanceof Comparable) {
        return compare_Comparable(operation, (Comparable) value1,
            value2);
      }
      return compare_Unknown(operation, value1, value2); // RFC 59
    }

    private boolean compare_Collection(int operation,
        Collection collection, Object value2) {
      for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
        if (compare(operation, iterator.next(), value2)) {
          return true;
        }
      }
      return false;
    }

    private boolean compare_ObjectArray(int operation, Object[] array,
        Object value2) {
      for (int i = 0, size = array.length; i < size; i++) {
        if (compare(operation, array[i], value2)) {
          return true;
        }
      }
      return false;
    }

    private boolean compare_PrimitiveArray(int operation, Class type,
        Object primarray, Object value2) {
      if (Integer.TYPE.isAssignableFrom(type)) {
        int[] array = (int[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Integer(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Long.TYPE.isAssignableFrom(type)) {
        long[] array = (long[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Long(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Byte.TYPE.isAssignableFrom(type)) {
        byte[] array = (byte[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Byte(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Short.TYPE.isAssignableFrom(type)) {
        short[] array = (short[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Short(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Character.TYPE.isAssignableFrom(type)) {
        char[] array = (char[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Character(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Float.TYPE.isAssignableFrom(type)) {
        float[] array = (float[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Float(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Double.TYPE.isAssignableFrom(type)) {
        double[] array = (double[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Double(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      if (Boolean.TYPE.isAssignableFrom(type)) {
        boolean[] array = (boolean[]) primarray;
        for (int i = 0, size = array.length; i < size; i++) {
          if (compare_Boolean(operation, array[i], value2)) {
            return true;
          }
        }
        return false;
      }
      return false;
    }

    private boolean compare_String(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) // if there are more
                  // 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 = approxString(string);
          String string2 = 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;
    }

    private boolean compare_Integer(int operation, int intval, Object value2) {
      if (operation == 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 compare_Long(int operation, long longval, Object value2) {
      if (operation == 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 compare_Byte(int operation, byte byteval, Object value2) {
      if (operation == 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 compare_Short(int operation, short shortval,
        Object value2) {
      if (operation == 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 compare_Character(int operation, char charval,
        Object value2) {
      if (operation == 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;
    }

    private boolean compare_Boolean(int operation, boolean boolval,
        Object value2) {
      if (operation == 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 compare_Float(int operation, float floatval,
        Object value2) {
      if (operation == 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 compare_Double(int operation, double doubleval,
        Object value2) {
      if (operation == 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 static final Class[]  constructorType  = new Class[] {String.class};

    private boolean compare_Comparable(int operation, Comparable value1,
        Object value2) {
      if (operation == SUBSTRING) {
        return false;
      }
      Constructor constructor;
      try {
        constructor = value1.getClass().getConstructor(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 compare_Unknown(int operation, Object value1,
        Object value2) {
      if (operation == SUBSTRING) {
        return false;
      }
      Constructor constructor;
      try {
        constructor = value1.getClass().getConstructor(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;
    }

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

    /**
     * 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 char[]  filterChars;
      private int        pos;

      Parser(String filterstring) {
        this.filterstring = filterstring;
        filterChars = filterstring.toCharArray();
        pos = 0;
      }

      FilterImpl parse() throws InvalidSyntaxException {
        FilterImpl filter;
        try {
          filter = parse_filter();
        }
        catch (ArrayIndexOutOfBoundsException e) {
          throw new InvalidSyntaxException("Filter ended abruptly",
              filterstring);
        }

        if (pos != filterChars.length) {
          throw new InvalidSyntaxException(
              "Extraneous trailing characters: "
                  + filterstring.substring(pos), filterstring);
        }
        return filter;
      }

      private FilterImpl parse_filter() throws InvalidSyntaxException {
        FilterImpl filter;
        skipWhiteSpace();

        if (filterChars[pos] != '(') {
          throw new InvalidSyntaxException("Missing '(': "
              + filterstring.substring(pos), filterstring);
        }

        pos++;

        filter = parse_filtercomp();

        skipWhiteSpace();

        if (filterChars[pos] != ')') {
          throw new InvalidSyntaxException("Missing ')': "
              + filterstring.substring(pos), filterstring);
        }

        pos++;

        skipWhiteSpace();

        return filter;
      }

      private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
        skipWhiteSpace();

        char c = filterChars[pos];

        switch (c) {
          case '&' : {
            pos++;
            return parse_and();
          }
          case '|' : {
            pos++;
            return parse_or();
          }
          case '!' : {
            pos++;
            return parse_not();
          }
        }
        return parse_item();
      }

      private FilterImpl parse_and() throws InvalidSyntaxException {
        int lookahead = pos;
        skipWhiteSpace();

        if (filterChars[pos] != '(') {
          pos = lookahead - 1;
          return parse_item();
        }

        List operands = new ArrayList(10);

        while (filterChars[pos] == '(') {
          FilterImpl child = parse_filter();
          operands.add(child);
        }

        return new FilterImpl(FilterImpl.AND, null, operands
            .toArray(new FilterImpl[operands.size()]));
      }

      private FilterImpl parse_or() throws InvalidSyntaxException {
        int lookahead = pos;
        skipWhiteSpace();

        if (filterChars[pos] != '(') {
          pos = lookahead - 1;
          return parse_item();
        }

        List operands = new ArrayList(10);

        while (filterChars[pos] == '(') {
          FilterImpl child = parse_filter();
          operands.add(child);
        }

        return new FilterImpl(FilterImpl.OR, null, operands
            .toArray(new FilterImpl[operands.size()]));
      }

      private FilterImpl parse_not() throws InvalidSyntaxException {
        int lookahead = pos;
        skipWhiteSpace();

        if (filterChars[pos] != '(') {
          pos = lookahead - 1;
          return parse_item();
        }

        FilterImpl child = parse_filter();

        return new FilterImpl(FilterImpl.NOT, null, child);
      }

      private FilterImpl parse_item() throws InvalidSyntaxException {
        String attr = parse_attr();

        skipWhiteSpace();

        switch (filterChars[pos]) {
          case '~' : {
            if (filterChars[pos + 1] == '=') {
              pos += 2;
              return new FilterImpl(FilterImpl.APPROX, attr,
                  parse_value());
            }
            break;
          }
          case '>' : {
            if (filterChars[pos + 1] == '=') {
              pos += 2;
              return new FilterImpl(FilterImpl.GREATER, attr,
                  parse_value());
            }
            break;
          }
          case '<' : {
            if (filterChars[pos + 1] == '=') {
              pos += 2;
              return new FilterImpl(FilterImpl.LESS, attr,
                  parse_value());
            }
            break;
          }
          case '=' : {
            if (filterChars[pos + 1] == '*') {
              int oldpos = pos;
              pos += 2;
              skipWhiteSpace();
              if (filterChars[pos] == ')') {
                return new FilterImpl(FilterImpl.PRESENT, attr,
                    null);
              }
              pos = oldpos;
            }

            pos++;
            Object string = parse_substring();

            if (string instanceof String) {
              return new FilterImpl(FilterImpl.EQUAL, attr,
                  string);
            }
            return new FilterImpl(FilterImpl.SUBSTRING, attr,
                string);
          }
        }

        throw new InvalidSyntaxException("Invalid operator: "
            + filterstring.substring(pos), filterstring);
      }

      private String parse_attr() throws InvalidSyntaxException {
        skipWhiteSpace();

        int begin = pos;
        int end = pos;

        char c = filterChars[pos];

        while (c != '~' && c != '<' && c != '>' && c != '=' && c != '('
            && c != ')') {
          pos++;

          if (!Character.isWhitespace(c)) {
            end = pos;
          }

          c = filterChars[pos];
        }

        int length = end - begin;

        if (length == 0) {
          throw new InvalidSyntaxException("Missing attr: "
              + filterstring.substring(pos), filterstring);
        }

        return new String(filterChars, begin, length);
      }

      private String parse_value() throws InvalidSyntaxException {
        StringBuffer sb = new StringBuffer(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);
        }

        return sb.toString();
      }

      private Object parse_substring() throws InvalidSyntaxException {
        StringBuffer sb = new StringBuffer(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 void skipWhiteSpace() {
        for (int length = filterChars.length; (pos < length)
            && Character.isWhitespace(filterChars[pos]);) {
          pos++;
        }
      }
    }
  }

  /**
   * This Dictionary is used for case-insensitive key lookup during filter
   * evaluation. This Dictionary implementation only supports the get
   * operation using a String key as no other operations are used by the
   * Filter implementation.
   */
  private static class CaseInsensitiveDictionary extends Dictionary {
    private final Dictionary  dictionary;
    private final String[]    keys;

    /**
     * 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.
     */
    CaseInsensitiveDictionary(Dictionary dictionary) {
      if (dictionary == null) {
        this.dictionary = null;
        this.keys = new String[0];
        return;
      }
      this.dictionary = dictionary;
      List keyList = new ArrayList(dictionary.size());
      for (Enumeration e = dictionary.keys(); e.hasMoreElements();) {
        Object k = e.nextElement();
        if (k instanceof String) {
          String key = (String) k;
          for (Iterator i = keyList.iterator(); i.hasNext();) {
            if (key.equalsIgnoreCase((String) i.next())) {
              throw new IllegalArgumentException();
            }
          }
          keyList.add(key);
        }
      }
      this.keys = (String[]) keyList.toArray(new String[keyList.size()]);
    }

    public Object get(Object o) {
      String k = (String) o;
      for (int i = 0, length = keys.length; i < length; i++) {
        String key = keys[i];
        if (key.equalsIgnoreCase(k)) {
          return dictionary.get(key);
        }
      }
      return null;
    }

    public boolean isEmpty() {
      throw new UnsupportedOperationException();
    }

    public Enumeration keys() {
      throw new UnsupportedOperationException();
    }

    public Enumeration elements() {
      throw new UnsupportedOperationException();
    }

    public Object put(Object key, Object value) {
      throw new UnsupportedOperationException();
    }

    public Object remove(Object key) {
      throw new UnsupportedOperationException();
    }

    public int size() {
      throw new UnsupportedOperationException();
    }
  }

  /**
   * This Dictionary is used for key lookup from a ServiceReference during
   * filter evaluation. This Dictionary implementation only supports the get
   * operation using a String key as no other operations are used by the
   * Filter implementation.
   */
  private static class ServiceReferenceDictionary extends Dictionary {
    private final ServiceReference  reference;

    ServiceReferenceDictionary(ServiceReference reference) {
      this.reference = reference;
    }

    public Object get(Object key) {
      if (reference == null) {
        return null;
      }
      return reference.getProperty((String) key);
    }

    public boolean isEmpty() {
      throw new UnsupportedOperationException();
    }

    public Enumeration keys() {
      throw new UnsupportedOperationException();
    }

    public Enumeration elements() {
      throw new UnsupportedOperationException();
    }

    public Object put(Object key, Object value) {
      throw new UnsupportedOperationException();
    }

    public Object remove(Object key) {
      throw new UnsupportedOperationException();
    }

    public int size() {
      throw new UnsupportedOperationException();
    }
  }

  private static class SetAccessibleAction implements PrivilegedAction {
    private final AccessibleObject  accessible;

    SetAccessibleAction(AccessibleObject accessible) {
      this.accessible = accessible;
    }

    public Object run() {
      accessible.setAccessible(true);
      return null;
    }
  }

  /**
   * This class contains a method to match a distinguished name (DN) chain
   * against and DN chain pattern.
   * <p>
   * The format of DNs are given in RFC 2253. We represent a signature chain
   * for an X.509 certificate as a semicolon separated list of DNs. This is
   * what we refer to as the DN chain. Each DN is made up of relative
   * distinguished names (RDN) which in turn are made up of key value pairs.
   * For example:
   *
   * <pre>
   *   cn=ben+ou=research,o=ACME,c=us;ou=Super CA,c=CA
   * </pre>
   *
   * is made up of two DNs: "<code>cn=ben+ou=research,o=ACME,c=us</code>
   * " and " <code>ou=Super CA,c=CA</code>
   * ". The first DN is made of of three RDNs: "
   * <code>cn=ben+ou=research</code>" and "<code>o=ACME</code>" and "
   * <code>c=us</code>". The first RDN has two name value pairs: "
   * <code>cn=ben</code>" and "<code>ou=research</code>".
   * <p>
   * A chain pattern makes use of wildcards ('*' or '-') to match against DNs,
   * and wildcards ('*') to match againts DN prefixes, and value. If a DN in a
   * match pattern chain is made up of a wildcard ("*"), that wildcard will
   * match zero or one DNs in the chain. If a DN in a match pattern chain is
   * made up of a wildcard ("-"), that wildcard will match zero or more DNs in
   * the chain. If the first RDN of a DN is the wildcard ("*"), that DN will
   * match any other DN with the same suffix (the DN with the wildcard RDN
   * removed). If a value of a name/value pair is a wildcard ("*"), the value
   * will match any value for that name.
   */
  private static class DNChainMatching {
    private static final String  MINUS_WILDCARD  = "-";
    private static final String  STAR_WILDCARD  = "*";

    /**
     * Check the name/value pairs of the rdn against the pattern.
     *
     * @param rdn List of name value pairs for a given RDN.
     * @param rdnPattern List of name value pattern pairs.
     * @return true if the list of name value pairs match the pattern.
     */
    private static boolean rdnmatch(List rdn, List rdnPattern) {
      if (rdn.size() != rdnPattern.size()) {
        return false;
      }
      for (int i = 0; i < rdn.size(); i++) {
        String rdnNameValue = (String) rdn.get(i);
        String patNameValue = (String) rdnPattern.get(i);
        int rdnNameEnd = rdnNameValue.indexOf('=');
        int patNameEnd = patNameValue.indexOf('=');
        if (rdnNameEnd != patNameEnd
            || !rdnNameValue.regionMatches(0, patNameValue, 0,
                rdnNameEnd)) {
          return false;
        }
        String patValue = patNameValue.substring(patNameEnd);
        String rdnValue = rdnNameValue.substring(rdnNameEnd);
        if (!rdnValue.equals(patValue) && !patValue.equals("=*")
            && !patValue.equals("=#16012a")) {
          return false;
        }
      }
      return true;
    }

    private static boolean dnmatch(List dn, List dnPattern) {
      int dnStart = 0;
      int patStart = 0;
      int patLen = dnPattern.size();
      if (patLen == 0) {
        return false;
      }
      if (dnPattern.get(0).equals(STAR_WILDCARD)) {
        patStart = 1;
        patLen--;
      }
      if (dn.size() < patLen) {
        return false;
      }
      else {
        if (dn.size() > patLen) {
          if (!dnPattern.get(0).equals(STAR_WILDCARD)) {
            // If the number of rdns do not match we must have a
            // prefix map
            return false;
          }
          // The rdnPattern and rdn must have the same number of
          // elements
          dnStart = dn.size() - patLen;
        }
      }
      for (int i = 0; i < patLen; i++) {
        if (!rdnmatch((List) dn.get(i + dnStart), (List) dnPattern
            .get(i + patStart))) {
          return false;
        }
      }
      return true;
    }

    /**
     * Parses a distinguished name chain pattern and returns a List where
     * each element represents a distinguished name (DN) in the chain of
     * DNs. Each element will be either a String, if the element represents
     * a wildcard ("*" or "-"), or a List representing an RDN. Each element
     * in the RDN List will be a String, if the element represents a
     * wildcard ("*"), or a List of Strings, each String representing a
     * name/value pair in the RDN.
     *
     * @param dnChain
     * @return a list of DNs.
     * @throws IllegalArgumentException
     */
    private static List parseDNchainPattern(String dnChain) {
      if (dnChain == null) {
        throw new IllegalArgumentException(
            "The DN chain must not be null.");
      }
      List parsed = new ArrayList();
      int startIndex = 0;
      startIndex = skipSpaces(dnChain, startIndex);
      while (startIndex < dnChain.length()) {
        int endIndex = startIndex;
        int lastNonSpaceIndex = startIndex;
        boolean inQuote = false;
        out: while (endIndex < dnChain.length()) {
          char c = dnChain.charAt(endIndex);
          switch (c) {
            case '"' :
              inQuote = !inQuote;
              break;
            case '\\' :
              endIndex++; // skip the escaped char
              break;
            case ';' :
              if (!inQuote)
                break out;
            case ' ' :
                                                        if (!inQuote) {
                endIndex++;
                continue;
                                                        }
          }
          lastNonSpaceIndex = ++endIndex;
        }
        if (endIndex > dnChain.length()) {
          throw new IllegalArgumentException("unterminated escape");
        }
        parsed.add(dnChain.substring(startIndex, lastNonSpaceIndex));
        startIndex = endIndex + 1;
        startIndex = skipSpaces(dnChain, startIndex);
      }
      return parseDNchain(parsed);
    }

    private static List parseDNchain(List chain) {
      if (chain == null) {
        throw new IllegalArgumentException("DN chain must not be null.");
      }
      chain = new ArrayList(chain);
      // Now we parse is a list of strings, lets make List of rdn out
      // of them
      for (int i = 0; i < chain.size(); i++) {
        String dn = (String) chain.get(i);
        if (dn.equals(STAR_WILDCARD) || dn.equals(MINUS_WILDCARD)) {
          continue;
        }
        List rdns = new ArrayList();
        if (dn.charAt(0) == '*') {
          if (dn.charAt(1) != ',') {
            throw new IllegalArgumentException(
                "invalid wildcard prefix");
          }
          rdns.add(STAR_WILDCARD);
          dn = new X500Principal(dn.substring(2))
              .getName(X500Principal.CANONICAL);
        }
        else {
          dn = new X500Principal(dn).getName(X500Principal.CANONICAL);
        }
        // Now dn is a nice CANONICAL DN
        parseDN(dn, rdns);
        chain.set(i, rdns);
      }
      if (chain.size() == 0) {
        throw new IllegalArgumentException("empty DN chain");
      }
      return chain;
    }

    /**
     * Increment startIndex until the end of dnChain is hit or until it is
     * the index of a non-space character.
     */
    private static int skipSpaces(String dnChain, int startIndex) {
      while (startIndex < dnChain.length()
          && dnChain.charAt(startIndex) == ' ') {
        startIndex++;
      }
      return startIndex;
    }

    /**
     * Takes a distinguished name in canonical form and fills in the
     * rdnArray with the extracted RDNs.
     *
     * @param dn the distinguished name in canonical form.
     * @param rdn the list to fill in with RDNs extracted from the dn
     * @throws IllegalArgumentException if a formatting error is found.
     */
    private static void parseDN(String dn, List rdn) {
      int startIndex = 0;
      char c = '\0';
      List nameValues = new ArrayList();
      while (startIndex < dn.length()) {
        int endIndex;
        for (endIndex = startIndex; endIndex < dn.length(); endIndex++) {
          c = dn.charAt(endIndex);
          if (c == ',' || c == '+') {
            break;
          }
          if (c == '\\') {
            endIndex++; // skip the escaped char
          }
        }
        if (endIndex > dn.length()) {
          throw new IllegalArgumentException("unterminated escape "
              + dn);
        }
        nameValues.add(dn.substring(startIndex, endIndex));
        if (c != '+') {
          rdn.add(nameValues);
          if (endIndex != dn.length()) {
            nameValues = new ArrayList();
          }
          else {
            nameValues = null;
          }
        }
        startIndex = endIndex + 1;
      }
      if (nameValues != null) {
        throw new IllegalArgumentException("improperly terminated DN "
            + dn);
      }
    }

    /**
     * This method will return an 'index' which points to a non-wildcard DN
     * or the end-of-list.
     */
    private static int skipWildCards(List dnChainPattern,
        int dnChainPatternIndex) {
      int i;
      for (i = dnChainPatternIndex; i < dnChainPattern.size(); i++) {
        Object dnPattern = dnChainPattern.get(i);
        if (dnPattern instanceof String) {
          if (!dnPattern.equals(STAR_WILDCARD)
              && !dnPattern.equals(MINUS_WILDCARD)) {
            throw new IllegalArgumentException(
                "expected wildcard in DN pattern");
          }
          // otherwise continue skipping over wild cards
        }
        else {
          if (dnPattern instanceof List) {
            // if its a list then we have our 'non-wildcard' DN
            break;
          }
          else {
            // unknown member of the DNChainPattern
            throw new IllegalArgumentException(
                "expected String or List in DN Pattern");
          }
        }
      }
      // i either points to end-of-list, or to the first
      // non-wildcard pattern after dnChainPatternIndex
      return i;
    }

    /**
     * recursively attempt to match the DNChain, and the DNChainPattern
     * where DNChain is of the format: "DN;DN;DN;" and DNChainPattern is of
     * the format: "DNPattern;*;DNPattern" (or combinations of this)
     */
    private static boolean dnChainMatch(List dnChain, int dnChainIndex,
        List dnChainPattern, int dnChainPatternIndex)
        throws IllegalArgumentException {
      if (dnChainIndex >= dnChain.size()) {
        return false;
      }
      if (dnChainPatternIndex >= dnChainPattern.size()) {
        return false;
      }
      // check to see what the pattern starts with
      Object dnPattern = dnChainPattern.get(dnChainPatternIndex);
      if (dnPattern instanceof String) {
        if (!dnPattern.equals(STAR_WILDCARD)
            && !dnPattern.equals(MINUS_WILDCARD)) {
          throw new IllegalArgumentException(
              "expected wildcard in DN pattern");
        }
        // here we are processing a wild card as the first DN
        // skip all wildcard DN's
        if (dnPattern.equals(MINUS_WILDCARD)) {
          dnChainPatternIndex = skipWildCards(dnChainPattern,
              dnChainPatternIndex);
        }
        else {
          dnChainPatternIndex++; // only skip the '*' wildcard
        }
        if (dnChainPatternIndex >= dnChainPattern.size()) {
          // return true iff the wild card is '-' or if we are at the
          // end of the chain
          return dnPattern.equals(MINUS_WILDCARD) ? true : dnChain
              .size() - 1 == dnChainIndex;
        }
        //
        // we will now recursively call to see if the rest of the
        // DNChainPattern matches increasingly smaller portions of the
        // rest of the DNChain
        //
        if (dnPattern.equals(STAR_WILDCARD)) {
          // '*' option: only wildcard on 0 or 1
          return dnChainMatch(dnChain, dnChainIndex, dnChainPattern,
              dnChainPatternIndex)
              || dnChainMatch(dnChain, dnChainIndex + 1,
                  dnChainPattern, dnChainPatternIndex);
        }
        for (int i = dnChainIndex; i < dnChain.size(); i++) {
          // '-' option: wildcard 0 or more
          if (dnChainMatch(dnChain, i, dnChainPattern,
              dnChainPatternIndex)) {
            return true;
          }
        }
        // if we are here, then we didn't find a match.. fall through to
        // failure
      }
      else {
        if (dnPattern instanceof List) {
          // here we have to do a deeper check for each DN in the
          // pattern until we hit a wild card
          do {
            if (!dnmatch((List) dnChain.get(dnChainIndex),
                (List) dnPattern)) {
              return false;
            }
            // go to the next set of DN's in both chains
            dnChainIndex++;
            dnChainPatternIndex++;
            // if we finished the pattern then it all matched
            if ((dnChainIndex >= dnChain.size())
                && (dnChainPatternIndex >= dnChainPattern
                    .size())) {
              return true;
            }
            // if the DN Chain is finished, but the pattern isn't
            // finished then if the rest of the pattern is not
            // wildcard then we are done
            if (dnChainIndex >= dnChain.size()) {
              dnChainPatternIndex = skipWildCards(dnChainPattern,
                  dnChainPatternIndex);
              // return TRUE iff the pattern index moved past the
              // list-size (implying that the rest of the pattern
              // is all wildcards)
              return dnChainPatternIndex >= dnChainPattern.size();
            }
            // if the pattern finished, but the chain continues then
            // we have a mis-match
            if (dnChainPatternIndex >= dnChainPattern.size()) {
              return false;
            }
            // get the next DN Pattern
            dnPattern = dnChainPattern.get(dnChainPatternIndex);
            if (dnPattern instanceof String) {
              if (!dnPattern.equals(STAR_WILDCARD)
                  && !dnPattern.equals(MINUS_WILDCARD)) {
                throw new IllegalArgumentException(
                    "expected wildcard in DN pattern");
              }
              // if the next DN is a 'wildcard', then we will
              // recurse
              return dnChainMatch(dnChain, dnChainIndex,
                  dnChainPattern, dnChainPatternIndex);
            }
            else {
              if (!(dnPattern instanceof List)) {
                throw new IllegalArgumentException(
                    "expected String or List in DN Pattern");
              }
            }
            // if we are here, then we will just continue to the
            // match the next set of DN's from the DNChain, and the
            // DNChainPattern since both are lists
          } while (true);
          // should never reach here?
        }
        else {
          throw new IllegalArgumentException(
              "expected String or List in DN Pattern");
        }
      }
      // if we get here, the the default return is 'mis-match'
      return false;
    }

    /**
     * Matches a distinguished name chain against a pattern of a
     * distinguished name chain.
     *
     * @param dnChain
     * @param pattern the pattern of distinguished name (DN) chains to match
     *        against the dnChain. Wildcards ("*" or "-") can be used in
     *        three cases:
     *        <ol>
     *        <li>As a DN. In this case, the DN will consist of just the "*"
     *        or "-". When "*" is used it will match zero or one DNs. When
     *        "-" is used it will match zero or more DNs. For example,
     *        "cn=me,c=US;*;cn=you" will match
     *        "cn=me,c=US";cn=you" and "cn=me,c=US;cn=her;cn=you". The
     *        pattern "cn=me,c=US;-;cn=you" will match "cn=me,c=US";cn=you"
     *        and "cn=me,c=US;cn=her;cn=him;cn=you".
     *        <li>As a DN prefix. In this case, the DN must start with "*,".
     *        The wild card will match zero or more RDNs at the start of a
     *        DN. For example, "*,cn=me,c=US;cn=you" will match
     *        "cn=me,c=US";cn=you" and
     *        "ou=my org unit,o=my org,cn=me,c=US;cn=you"</li>
     *        <li>As a value. In this case the value of a name value pair in
     *        an RDN will be a "*". The wildcard will match any value for
     *        the given name. For example, "cn=*,c=US;cn=you" will match
     *        "cn=me,c=US";cn=you" and "cn=her,c=US;cn=you", but it will not
     *        match "ou=my org unit,c=US;cn=you". If the wildcard does not
     *        occur by itself in the value, it will not be used as a
     *        wildcard. In other words, "cn=m*,c=US;cn=you" represents the
     *        common name of "m*" not any common name starting with "m".</li>
     *        </ol>
     * @return true if dnChain matches the pattern.
     * @throws IllegalArgumentException
     */
    static boolean match(String pattern, List/* <String> */dnChain) {
      List parsedDNChain;
      List parsedDNPattern;
      try {
        parsedDNChain = parseDNchain(dnChain);
      }
      catch (RuntimeException e) {
        IllegalArgumentException iae = new IllegalArgumentException(
            "Invalid DN chain: " + toString(dnChain));
        iae.initCause(e);
        throw iae;
      }
      try {
        parsedDNPattern = parseDNchainPattern(pattern);
      }
      catch (RuntimeException e) {
        IllegalArgumentException iae = new IllegalArgumentException(
            "Invalid match pattern: " + pattern);
        iae.initCause(e);
        throw iae;
      }
      return dnChainMatch(parsedDNChain, 0, parsedDNPattern, 0);
    }

    private static String toString(List dnChain) {
      if (dnChain == null) {
        return null;
      }
      StringBuffer sb = new StringBuffer();
      for (Iterator iChain = dnChain.iterator(); iChain.hasNext();) {
        sb.append(iChain.next());
        if (iChain.hasNext()) {
          sb.append("; ");
        }
      }
      return sb.toString();
    }
  }
}
TOP

Related Classes of org.osgi.framework.FrameworkUtil$ServiceReferenceDictionary

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.