blob: b565b8651aafbed11fbb594857905a546d1e0802 [file] [log] [blame]
Richard S. Hallc88fca32006-10-18 22:01:22 +00001/*
Richard S. Hallf28d6762009-06-08 19:31:06 +00002 * Copyright (c) OSGi Alliance (2005, 2009). All Rights Reserved.
Richard S. Hallc88fca32006-10-18 22:01:22 +00003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.osgi.framework;
18
Richard S. Hallf28d6762009-06-08 19:31:06 +000019import java.lang.reflect.AccessibleObject;
20import java.lang.reflect.Constructor;
21import java.lang.reflect.InvocationTargetException;
Richard S. Hallc88fca32006-10-18 22:01:22 +000022import java.security.AccessController;
23import java.security.PrivilegedAction;
Richard S. Hallf28d6762009-06-08 19:31:06 +000024import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Dictionary;
27import java.util.Enumeration;
28import java.util.Iterator;
29import java.util.List;
30
31import javax.security.auth.x500.X500Principal;
Richard S. Hallc88fca32006-10-18 22:01:22 +000032
33/**
34 * Framework Utility class.
35 *
36 * <p>
37 * This class contains utility methods which access Framework functions that may
38 * be useful to bundles.
39 *
Richard S. Hallc88fca32006-10-18 22:01:22 +000040 * @since 1.3
Richard S. Hall53e70d32008-08-01 19:31:32 +000041 * @ThreadSafe
Richard S. Hall2ff3db92009-08-25 14:31:32 +000042 * @version $Revision: 7761 $
Richard S. Hallc88fca32006-10-18 22:01:22 +000043 */
44public class FrameworkUtil {
Richard S. Hallf28d6762009-06-08 19:31:06 +000045 /**
46 * FrameworkUtil objects may not be constructed.
Richard S. Hallc88fca32006-10-18 22:01:22 +000047 */
Richard S. Hallf28d6762009-06-08 19:31:06 +000048 private FrameworkUtil() {
49 // private empty constructor to prevent construction
Richard S. Hallc88fca32006-10-18 22:01:22 +000050 }
51
Richard S. Hallc88fca32006-10-18 22:01:22 +000052 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +000053 * Creates a <code>Filter</code> object. This <code>Filter</code> object may
54 * be used to match a <code>ServiceReference</code> object or a
Richard S. Hallc88fca32006-10-18 22:01:22 +000055 * <code>Dictionary</code> object.
56 *
57 * <p>
58 * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be
59 * thrown with a human readable message where the filter became unparsable.
60 *
Richard S. Hallf28d6762009-06-08 19:31:06 +000061 * <p>
62 * This method returns a Filter implementation which may not perform as well
63 * as the framework implementation-specific Filter implementation returned
64 * by {@link BundleContext#createFilter(String)}.
65 *
Richard S. Hallc88fca32006-10-18 22:01:22 +000066 * @param filter The filter string.
67 * @return A <code>Filter</code> object encapsulating the filter string.
Richard S. Hallf28d6762009-06-08 19:31:06 +000068 * @throws InvalidSyntaxException If <code>filter</code> contains an invalid
69 * filter string that cannot be parsed.
Richard S. Hallc88fca32006-10-18 22:01:22 +000070 * @throws NullPointerException If <code>filter</code> is null.
71 *
72 * @see Filter
73 */
74 public static Filter createFilter(String filter)
75 throws InvalidSyntaxException {
Richard S. Hallf28d6762009-06-08 19:31:06 +000076 return FilterImpl.newInstance(filter);
77 }
78
79 /**
80 * Match a Distinguished Name (DN) chain against a pattern. DNs can be
81 * matched using wildcards. A wildcard ('*' &#92;u002A) replaces all
82 * possible values. Due to the structure of the DN, the comparison is more
83 * complicated than string-based wildcard matching.
84 * <p>
85 * A wildcard can stand for zero or more DNs in a chain, a number of
86 * relative distinguished names (RDNs) within a DN, or the value of a single
87 * RDN. The DNs in the chain and the matching pattern are canonicalized
88 * before processing. This means, among other things, that spaces must be
89 * ignored, except in values.
90 * <p>
91 * The format of a wildcard match pattern is:
92 *
93 * <pre>
94 * matchPattern ::= dn-match ( ';' dn-match ) *
95 * dn-match ::= ( '*' | rdn-match ) ( ',' rdn-match ) * | '-'
96 * rdn-match ::= name '=' value-match
97 * value-match ::= '*' | value-star
98 * value-star ::= &lt; value, requires escaped '*' and '-' &gt;
99 * </pre>
100 * <p>
101 * The most simple case is a single wildcard; it must match any DN. A
102 * wildcard can also replace the first list of RDNs of a DN. The first RDNs
103 * are the least significant. Such lists of matched RDNs can be empty.
104 * <p>
105 * For example, a match pattern with a wildcard that matches all all DNs
106 * that end with RDNs of o=ACME and c=US would look like this:
107 *
108 * <pre>
109 * *, o=ACME, c=US
110 * </pre>
111 *
112 * This match pattern would match the following DNs:
113 *
114 * <pre>
115 * cn = Bugs Bunny, o = ACME, c = US
116 * ou = Carrots, cn=Daffy Duck, o=ACME, c=US
117 * street = 9C\, Avenue St. Drézéry, o=ACME, c=US
118 * dc=www, dc=acme, dc=com, o=ACME, c=US
119 * o=ACME, c=US
120 * </pre>
121 *
122 * The following DNs would not match:
123 *
124 * <pre>
125 * street = 9C\, Avenue St. Drézéry, o=ACME, c=FR
126 * dc=www, dc=acme, dc=com, c=US
127 * </pre>
128 *
129 * If a wildcard is used for a value of an RDN, the value must be exactly *.
130 * The wildcard must match any value, and no substring matching must be
131 * done. For example:
132 *
133 * <pre>
134 * cn=*,o=ACME,c=*
135 * </pre>
136 *
137 * This match pattern with wildcard must match the following DNs:
138 *
139 * <pre>
140 * cn=Bugs Bunny,o=ACME,c=US
141 * cn = Daffy Duck , o = ACME , c = US
142 * cn=Road Runner, o=ACME, c=NL
143 * </pre>
144 *
145 * But not:
146 *
147 * <pre>
148 * o=ACME, c=NL
149 * dc=acme.com, cn=Bugs Bunny, o=ACME, c=US
150 * </pre>
151 *
152 * <p>
153 * A match pattern may contain a chain of DN match patterns. The
154 * semicolon(';' &#92;u003B) must be used to separate DN match patterns in a
155 * chain. Wildcards can also be used to match against a complete DN within a
156 * chain.
157 * <p>
158 * The following example matches a certificate signed by Tweety Inc. in the
159 * US.
160 * </p>
161 *
162 * <pre>
163 * * ; ou=S &amp; V, o=Tweety Inc., c=US
164 * </pre>
165 * <p>
166 * The wildcard ('*') matches zero or one DN in the chain, however,
167 * sometimes it is necessary to match a longer chain. The minus sign ('-'
168 * &#92;u002D) represents zero or more DNs, whereas the asterisk only
169 * represents a single DN. For example, to match a DN where the Tweety Inc.
170 * is in the DN chain, use the following expression:
171 * </p>
172 *
173 * <pre>
174 * - ; *, o=Tweety Inc., c=US
175 * </pre>
176 *
177 * @param matchPattern The pattern against which to match the DN chain.
178 * @param dnChain The DN chain to match against the specified pattern. Each
179 * element of the chain must be of type <code>String</code> and use
180 * the format defined in RFC 2253.
181 * @return <code>true</code> If the pattern matches the DN chain; otherwise
182 * <code>false</code> is returned.
183 * @throws IllegalArgumentException If the specified match pattern or DN
184 * chain is invalid.
185 * @since 1.5
186 */
187 public static boolean matchDistinguishedNameChain(String matchPattern,
188 List /* <String> */dnChain) {
Richard S. Hall2ff3db92009-08-25 14:31:32 +0000189 return DNChainMatching.match(matchPattern, dnChain);
Richard S. Hallf28d6762009-06-08 19:31:06 +0000190 }
191
192 /**
193 * Return a <code>Bundle</code> for the specified bundle class. The returned
194 * <code>Bundle</code> is the bundle associated with the bundle class loader
195 * which defined the specified class.
196 *
197 * @param classFromBundle A class defined by a bundle class loader.
198 * @return A <code>Bundle</code> for the specified bundle class or
199 * <code>null</code> if the specified class was not defined by a
200 * bundle class loader.
201 * @since 1.5
202 */
203 public static Bundle getBundle(final Class classFromBundle) {
204 // We use doPriv since the caller may not have permission
205 // to call getClassLoader.
206 Object cl = AccessController.doPrivileged(new PrivilegedAction() {
207 public Object run() {
208 return classFromBundle.getClassLoader();
209 }
210 });
211
212 if (cl instanceof BundleReference) {
213 return ((BundleReference) cl).getBundle();
214 }
215 return null;
216 }
217
218 /**
219 * RFC 1960-based Filter. Filter objects can be created by calling the
220 * constructor with the desired filter string. A Filter object can be called
221 * numerous times to determine if the match argument matches the filter
222 * string that was used to create the Filter object.
223 *
224 * <p>
225 * The syntax of a filter string is the string representation of LDAP search
226 * filters as defined in RFC 1960: <i>A String Representation of LDAP Search
227 * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should
228 * be noted that RFC 2254: <i>A String Representation of LDAP Search
229 * Filters</i> (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
230 * RFC 1960 but only adds extensible matching and is not applicable for this
231 * API.
232 *
233 * <p>
234 * The string representation of an LDAP search filter is defined by the
235 * following grammar. It uses a prefix format.
236 *
237 * <pre>
238 * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
239 * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
240 * &lt;and&gt; ::= '&amp;' &lt;filterlist&gt;
241 * &lt;or&gt; ::= '|' &lt;filterlist&gt;
242 * &lt;not&gt; ::= '!' &lt;filter&gt;
243 * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
244 * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
245 * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
246 * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
247 * &lt;equal&gt; ::= '='
248 * &lt;approx&gt; ::= '&tilde;='
249 * &lt;greater&gt; ::= '&gt;='
250 * &lt;less&gt; ::= '&lt;='
251 * &lt;present&gt; ::= &lt;attr&gt; '=*'
252 * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
253 * &lt;initial&gt; ::= NULL | &lt;value&gt;
254 * &lt;any&gt; ::= '*' &lt;starval&gt;
255 * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
256 * &lt;final&gt; ::= NULL | &lt;value&gt;
257 * </pre>
258 *
259 * <code>&lt;attr&gt;</code> is a string representing an attribute, or key,
260 * in the properties objects of the registered services. Attribute names are
261 * not case sensitive; that is cn and CN both refer to the same attribute.
262 * <code>&lt;value&gt;</code> is a string representing the value, or part of
263 * one, of a key in the properties objects of the registered services. If a
264 * <code>&lt;value&gt;</code> must contain one of the characters '
265 * <code>*</code>' or '<code>(</code>' or '<code>)</code>', these characters
266 * should be escaped by preceding them with the backslash '<code>\</code>'
267 * character. Note that although both the <code>&lt;substring&gt;</code> and
268 * <code>&lt;present&gt;</code> productions can produce the <code>'attr=*'</code>
269 * construct, this construct is used only to denote a presence filter.
270 *
271 * <p>
272 * Examples of LDAP filters are:
273 *
274 * <pre>
275 * &quot;(cn=Babs Jensen)&quot;
276 * &quot;(!(cn=Tim Howes))&quot;
277 * &quot;(&amp;(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
278 * &quot;(o=univ*of*mich*)&quot;
279 * </pre>
280 *
281 * <p>
282 * The approximate match (<code>~=</code>) is implementation specific but
283 * should at least ignore case and white space differences. Optional are
284 * codes like soundex or other smart "closeness" comparisons.
285 *
286 * <p>
287 * Comparison of values is not straightforward. Strings are compared
288 * differently than numbers and it is possible for a key to have multiple
289 * values. Note that that keys in the match argument must always be strings.
290 * The comparison is defined by the object type of the key's value. The
291 * following rules apply for comparison:
292 *
293 * <blockquote>
294 * <TABLE BORDER=0>
295 * <TR>
296 * <TD><b>Property Value Type </b></TD>
297 * <TD><b>Comparison Type</b></TD>
298 * </TR>
299 * <TR>
300 * <TD>String</TD>
301 * <TD>String comparison</TD>
302 * </TR>
303 * <TR valign=top>
304 * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD>
305 * <TD>numerical comparison</TD>
306 * </TR>
307 * <TR>
308 * <TD>Character</TD>
309 * <TD>character comparison</TD>
310 * </TR>
311 * <TR>
312 * <TD>Boolean</TD>
313 * <TD>equality comparisons only</TD>
314 * </TR>
315 * <TR>
316 * <TD>[] (array)</TD>
317 * <TD>recursively applied to values</TD>
318 * </TR>
319 * <TR>
320 * <TD>Collection</TD>
321 * <TD>recursively applied to values</TD>
322 * </TR>
323 * </TABLE>
324 * Note: arrays of primitives are also supported. </blockquote>
325 *
326 * A filter matches a key that has multiple values if it matches at least
327 * one of those values. For example,
328 *
329 * <pre>
330 * Dictionary d = new Hashtable();
331 * d.put(&quot;cn&quot;, new String[] {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;});
332 * </pre>
333 *
334 * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
335 *
336 * <p>
337 * A filter component that references a key having an unrecognizable data
338 * type will evaluate to <code>false</code> .
339 */
340 private static class FilterImpl implements Filter {
341 /* filter operators */
342 private static final int EQUAL = 1;
343 private static final int APPROX = 2;
344 private static final int GREATER = 3;
345 private static final int LESS = 4;
346 private static final int PRESENT = 5;
347 private static final int SUBSTRING = 6;
348 private static final int AND = 7;
349 private static final int OR = 8;
350 private static final int NOT = 9;
351
352 /** filter operation */
353 private final int op;
354 /** filter attribute or null if operation AND, OR or NOT */
355 private final String attr;
356 /** filter operands */
357 private final Object value;
358
359 /* normalized filter string for Filter object */
360 private transient volatile String filterString;
361
362 /**
363 * Constructs a {@link FilterImpl} object. This filter object may be
364 * used to match a {@link ServiceReference} or a Dictionary.
365 *
366 * <p>
367 * If the filter cannot be parsed, an {@link InvalidSyntaxException}
368 * will be thrown with a human readable message where the filter became
369 * unparsable.
370 *
371 * @param filterString the filter string.
372 * @exception InvalidSyntaxException If the filter parameter contains an
373 * invalid filter string that cannot be parsed.
374 */
375 static FilterImpl newInstance(String filterString)
376 throws InvalidSyntaxException {
377 return new Parser(filterString).parse();
378 }
379
380 FilterImpl(int operation, String attr, Object value) {
381 this.op = operation;
382 this.attr = attr;
383 this.value = value;
384 }
385
386 /**
387 * Filter using a service's properties.
388 * <p>
389 * This <code>Filter</code> is executed using the keys and values of the
390 * referenced service's properties. The keys are case insensitively
391 * matched with this <code>Filter</code>.
392 *
393 * @param reference The reference to the service whose properties are
394 * used in the match.
395 * @return <code>true</code> if the service's properties match this
396 * <code>Filter</code>; <code>false</code> otherwise.
397 */
398 public boolean match(ServiceReference reference) {
399 return match0(new ServiceReferenceDictionary(reference));
400 }
401
402 /**
403 * Filter using a <code>Dictionary</code>. This <code>Filter</code> is
404 * executed using the specified <code>Dictionary</code>'s keys and
405 * values. The keys are case insensitively matched with this
406 * <code>Filter</code>.
407 *
408 * @param dictionary The <code>Dictionary</code> whose keys are used in
409 * the match.
410 * @return <code>true</code> if the <code>Dictionary</code>'s keys and
411 * values match this filter; <code>false</code> otherwise.
412 * @throws IllegalArgumentException If <code>dictionary</code> contains
413 * case variants of the same key name.
414 */
415 public boolean match(Dictionary dictionary) {
416 return match0(new CaseInsensitiveDictionary(dictionary));
417 }
418
419 /**
420 * Filter with case sensitivity using a <code>Dictionary</code>. This
421 * <code>Filter</code> is executed using the specified
422 * <code>Dictionary</code>'s keys and values. The keys are case
423 * sensitively matched with this <code>Filter</code>.
424 *
425 * @param dictionary The <code>Dictionary</code> whose keys are used in
426 * the match.
427 * @return <code>true</code> if the <code>Dictionary</code>'s keys and
428 * values match this filter; <code>false</code> otherwise.
429 * @since 1.3
430 */
431 public boolean matchCase(Dictionary dictionary) {
432 return match0(dictionary);
433 }
434
435 /**
436 * Returns this <code>Filter</code>'s filter string.
437 * <p>
438 * The filter string is normalized by removing whitespace which does not
439 * affect the meaning of the filter.
440 *
441 * @return This <code>Filter</code>'s filter string.
442 */
443 public String toString() {
444 String result = filterString;
445 if (result == null) {
446 filterString = result = normalize();
447 }
448 return result;
449 }
450
451 /**
452 * Returns this <code>Filter</code>'s normalized filter string.
453 * <p>
454 * The filter string is normalized by removing whitespace which does not
455 * affect the meaning of the filter.
456 *
457 * @return This <code>Filter</code>'s filter string.
458 */
459 private String normalize() {
460 StringBuffer sb = new StringBuffer();
461 sb.append('(');
462
463 switch (op) {
464 case AND : {
465 sb.append('&');
466
467 FilterImpl[] filters = (FilterImpl[]) value;
468 for (int i = 0, size = filters.length; i < size; i++) {
469 sb.append(filters[i].normalize());
470 }
471
472 break;
473 }
474
475 case OR : {
476 sb.append('|');
477
478 FilterImpl[] filters = (FilterImpl[]) value;
479 for (int i = 0, size = filters.length; i < size; i++) {
480 sb.append(filters[i].normalize());
481 }
482
483 break;
484 }
485
486 case NOT : {
487 sb.append('!');
488 FilterImpl filter = (FilterImpl) value;
489 sb.append(filter.normalize());
490
491 break;
492 }
493
494 case SUBSTRING : {
495 sb.append(attr);
496 sb.append('=');
497
498 String[] substrings = (String[]) value;
499
500 for (int i = 0, size = substrings.length; i < size; i++) {
501 String substr = substrings[i];
502
503 if (substr == null) /* * */{
504 sb.append('*');
505 }
506 else /* xxx */{
507 sb.append(encodeValue(substr));
508 }
509 }
510
511 break;
512 }
513 case EQUAL : {
514 sb.append(attr);
515 sb.append('=');
516 sb.append(encodeValue((String) value));
517
518 break;
519 }
520 case GREATER : {
521 sb.append(attr);
522 sb.append(">=");
523 sb.append(encodeValue((String) value));
524
525 break;
526 }
527 case LESS : {
528 sb.append(attr);
529 sb.append("<=");
530 sb.append(encodeValue((String) value));
531
532 break;
533 }
534 case APPROX : {
535 sb.append(attr);
536 sb.append("~=");
537 sb.append(encodeValue(approxString((String) value)));
538
539 break;
540 }
541
542 case PRESENT : {
543 sb.append(attr);
544 sb.append("=*");
545
546 break;
547 }
548 }
549
550 sb.append(')');
551
552 return sb.toString();
553 }
554
555 /**
556 * Compares this <code>Filter</code> to another <code>Filter</code>.
557 *
558 * <p>
559 * This implementation returns the result of calling
560 * <code>this.toString().equals(obj.toString()</code>.
561 *
562 * @param obj The object to compare against this <code>Filter</code>.
563 * @return If the other object is a <code>Filter</code> object, then
564 * returns the result of calling
565 * <code>this.toString().equals(obj.toString()</code>;
566 * <code>false</code> otherwise.
567 */
568 public boolean equals(Object obj) {
569 if (obj == this) {
570 return true;
571 }
572
573 if (!(obj instanceof Filter)) {
574 return false;
575 }
576
577 return this.toString().equals(obj.toString());
578 }
579
580 /**
581 * Returns the hashCode for this <code>Filter</code>.
582 *
583 * <p>
584 * This implementation returns the result of calling
585 * <code>this.toString().hashCode()</code>.
586 *
587 * @return The hashCode of this <code>Filter</code>.
588 */
589 public int hashCode() {
590 return this.toString().hashCode();
591 }
592
593 /**
594 * Internal match routine. Dictionary parameter must support
595 * case-insensitive get.
596 *
597 * @param properties A dictionary whose keys are used in the match.
598 * @return If the Dictionary's keys match the filter, return
599 * <code>true</code>. Otherwise, return <code>false</code>.
600 */
601 private boolean match0(Dictionary properties) {
602 switch (op) {
603 case AND : {
604 FilterImpl[] filters = (FilterImpl[]) value;
605 for (int i = 0, size = filters.length; i < size; i++) {
606 if (!filters[i].match0(properties)) {
607 return false;
608 }
609 }
610
611 return true;
612 }
613
614 case OR : {
615 FilterImpl[] filters = (FilterImpl[]) value;
616 for (int i = 0, size = filters.length; i < size; i++) {
617 if (filters[i].match0(properties)) {
618 return true;
619 }
620 }
621
622 return false;
623 }
624
625 case NOT : {
626 FilterImpl filter = (FilterImpl) value;
627
628 return !filter.match0(properties);
629 }
630
631 case SUBSTRING :
632 case EQUAL :
633 case GREATER :
634 case LESS :
635 case APPROX : {
636 Object prop = (properties == null) ? null : properties
637 .get(attr);
638
639 return compare(op, prop, value);
640 }
641
642 case PRESENT : {
643 Object prop = (properties == null) ? null : properties
644 .get(attr);
645
646 return prop != null;
647 }
648 }
649
650 return false;
651 }
652
653 /**
654 * Encode the value string such that '(', '*', ')' and '\' are escaped.
655 *
656 * @param value unencoded value string.
657 * @return encoded value string.
658 */
659 private static String encodeValue(String value) {
660 boolean encoded = false;
661 int inlen = value.length();
662 int outlen = inlen << 1; /* inlen 2 */
663
664 char[] output = new char[outlen];
665 value.getChars(0, inlen, output, inlen);
666
667 int cursor = 0;
668 for (int i = inlen; i < outlen; i++) {
669 char c = output[i];
670
671 switch (c) {
672 case '(' :
673 case '*' :
674 case ')' :
675 case '\\' : {
676 output[cursor] = '\\';
677 cursor++;
678 encoded = true;
679
680 break;
681 }
682 }
683
684 output[cursor] = c;
685 cursor++;
686 }
687
688 return encoded ? new String(output, 0, cursor) : value;
689 }
690
691 private boolean compare(int operation, Object value1, Object value2) {
692 if (value1 == null) {
693 return false;
694 }
695 if (value1 instanceof String) {
696 return compare_String(operation, (String) value1, value2);
697 }
698
699 Class clazz = value1.getClass();
700 if (clazz.isArray()) {
701 Class type = clazz.getComponentType();
702 if (type.isPrimitive()) {
703 return compare_PrimitiveArray(operation, type, value1,
704 value2);
705 }
706 return compare_ObjectArray(operation, (Object[]) value1, value2);
707 }
708 if (value1 instanceof Collection) {
709 return compare_Collection(operation, (Collection) value1,
710 value2);
711 }
712 if (value1 instanceof Integer) {
713 return compare_Integer(operation,
714 ((Integer) value1).intValue(), value2);
715 }
716 if (value1 instanceof Long) {
717 return compare_Long(operation, ((Long) value1).longValue(),
718 value2);
719 }
720 if (value1 instanceof Byte) {
721 return compare_Byte(operation, ((Byte) value1).byteValue(),
722 value2);
723 }
724 if (value1 instanceof Short) {
725 return compare_Short(operation, ((Short) value1).shortValue(),
726 value2);
727 }
728 if (value1 instanceof Character) {
729 return compare_Character(operation, ((Character) value1)
730 .charValue(), value2);
731 }
732 if (value1 instanceof Float) {
733 return compare_Float(operation, ((Float) value1).floatValue(),
734 value2);
735 }
736 if (value1 instanceof Double) {
737 return compare_Double(operation, ((Double) value1)
738 .doubleValue(), value2);
739 }
740 if (value1 instanceof Boolean) {
741 return compare_Boolean(operation, ((Boolean) value1)
742 .booleanValue(), value2);
743 }
744 if (value1 instanceof Comparable) {
745 return compare_Comparable(operation, (Comparable) value1,
746 value2);
747 }
748 return compare_Unknown(operation, value1, value2); // RFC 59
749 }
750
751 private boolean compare_Collection(int operation,
752 Collection collection, Object value2) {
753 for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
754 if (compare(operation, iterator.next(), value2)) {
755 return true;
756 }
757 }
758 return false;
759 }
760
761 private boolean compare_ObjectArray(int operation, Object[] array,
762 Object value2) {
763 for (int i = 0, size = array.length; i < size; i++) {
764 if (compare(operation, array[i], value2)) {
765 return true;
766 }
767 }
768 return false;
769 }
770
771 private boolean compare_PrimitiveArray(int operation, Class type,
772 Object primarray, Object value2) {
773 if (Integer.TYPE.isAssignableFrom(type)) {
774 int[] array = (int[]) primarray;
775 for (int i = 0, size = array.length; i < size; i++) {
776 if (compare_Integer(operation, array[i], value2)) {
777 return true;
778 }
779 }
780 return false;
781 }
782 if (Long.TYPE.isAssignableFrom(type)) {
783 long[] array = (long[]) primarray;
784 for (int i = 0, size = array.length; i < size; i++) {
785 if (compare_Long(operation, array[i], value2)) {
786 return true;
787 }
788 }
789 return false;
790 }
791 if (Byte.TYPE.isAssignableFrom(type)) {
792 byte[] array = (byte[]) primarray;
793 for (int i = 0, size = array.length; i < size; i++) {
794 if (compare_Byte(operation, array[i], value2)) {
795 return true;
796 }
797 }
798 return false;
799 }
800 if (Short.TYPE.isAssignableFrom(type)) {
801 short[] array = (short[]) primarray;
802 for (int i = 0, size = array.length; i < size; i++) {
803 if (compare_Short(operation, array[i], value2)) {
804 return true;
805 }
806 }
807 return false;
808 }
809 if (Character.TYPE.isAssignableFrom(type)) {
810 char[] array = (char[]) primarray;
811 for (int i = 0, size = array.length; i < size; i++) {
812 if (compare_Character(operation, array[i], value2)) {
813 return true;
814 }
815 }
816 return false;
817 }
818 if (Float.TYPE.isAssignableFrom(type)) {
819 float[] array = (float[]) primarray;
820 for (int i = 0, size = array.length; i < size; i++) {
821 if (compare_Float(operation, array[i], value2)) {
822 return true;
823 }
824 }
825 return false;
826 }
827 if (Double.TYPE.isAssignableFrom(type)) {
828 double[] array = (double[]) primarray;
829 for (int i = 0, size = array.length; i < size; i++) {
830 if (compare_Double(operation, array[i], value2)) {
831 return true;
832 }
833 }
834 return false;
835 }
836 if (Boolean.TYPE.isAssignableFrom(type)) {
837 boolean[] array = (boolean[]) primarray;
838 for (int i = 0, size = array.length; i < size; i++) {
839 if (compare_Boolean(operation, array[i], value2)) {
840 return true;
841 }
842 }
843 return false;
844 }
845 return false;
846 }
847
848 private boolean compare_String(int operation, String string,
849 Object value2) {
850 switch (operation) {
851 case SUBSTRING : {
852 String[] substrings = (String[]) value2;
853 int pos = 0;
854 for (int i = 0, size = substrings.length; i < size; i++) {
855 String substr = substrings[i];
856
857 if (i + 1 < size) /* if this is not that last substr */{
858 if (substr == null) /* * */{
859 String substr2 = substrings[i + 1];
860
861 if (substr2 == null) /* ** */
862 continue; /* ignore first star */
863 /* xxx */
864 int index = string.indexOf(substr2, pos);
865 if (index == -1) {
866 return false;
867 }
868
869 pos = index + substr2.length();
870 if (i + 2 < size) // if there are more
871 // substrings, increment
872 // over the string we just
873 // matched; otherwise need
874 // to do the last substr
875 // check
876 i++;
877 }
878 else /* xxx */{
879 int len = substr.length();
880 if (string.regionMatches(pos, substr, 0, len)) {
881 pos += len;
882 }
883 else {
884 return false;
885 }
886 }
887 }
888 else /* last substr */{
889 if (substr == null) /* * */{
890 return true;
891 }
892 /* xxx */
893 return string.endsWith(substr);
894 }
895 }
896
897 return true;
898 }
899 case EQUAL : {
900 return string.equals(value2);
901 }
902 case APPROX : {
903 string = approxString(string);
904 String string2 = approxString((String) value2);
905
906 return string.equalsIgnoreCase(string2);
907 }
908 case GREATER : {
909 return string.compareTo((String) value2) >= 0;
910 }
911 case LESS : {
912 return string.compareTo((String) value2) <= 0;
913 }
914 }
915 return false;
916 }
917
918 private boolean compare_Integer(int operation, int intval, Object value2) {
919 if (operation == SUBSTRING) {
920 return false;
921 }
922 int intval2 = Integer.parseInt(((String) value2).trim());
923 switch (operation) {
924 case APPROX :
925 case EQUAL : {
926 return intval == intval2;
927 }
928 case GREATER : {
929 return intval >= intval2;
930 }
931 case LESS : {
932 return intval <= intval2;
933 }
934 }
935 return false;
936 }
937
938 private boolean compare_Long(int operation, long longval, Object value2) {
939 if (operation == SUBSTRING) {
940 return false;
941 }
942 long longval2 = Long.parseLong(((String) value2).trim());
943 switch (operation) {
944 case APPROX :
945 case EQUAL : {
946 return longval == longval2;
947 }
948 case GREATER : {
949 return longval >= longval2;
950 }
951 case LESS : {
952 return longval <= longval2;
953 }
954 }
955 return false;
956 }
957
958 private boolean compare_Byte(int operation, byte byteval, Object value2) {
959 if (operation == SUBSTRING) {
960 return false;
961 }
962 byte byteval2 = Byte.parseByte(((String) value2).trim());
963 switch (operation) {
964 case APPROX :
965 case EQUAL : {
966 return byteval == byteval2;
967 }
968 case GREATER : {
969 return byteval >= byteval2;
970 }
971 case LESS : {
972 return byteval <= byteval2;
973 }
974 }
975 return false;
976 }
977
978 private boolean compare_Short(int operation, short shortval,
979 Object value2) {
980 if (operation == SUBSTRING) {
981 return false;
982 }
983 short shortval2 = Short.parseShort(((String) value2).trim());
984 switch (operation) {
985 case APPROX :
986 case EQUAL : {
987 return shortval == shortval2;
988 }
989 case GREATER : {
990 return shortval >= shortval2;
991 }
992 case LESS : {
993 return shortval <= shortval2;
994 }
995 }
996 return false;
997 }
998
999 private boolean compare_Character(int operation, char charval,
1000 Object value2) {
1001 if (operation == SUBSTRING) {
1002 return false;
1003 }
1004 char charval2 = (((String) value2).trim()).charAt(0);
1005 switch (operation) {
1006 case EQUAL : {
1007 return charval == charval2;
1008 }
1009 case APPROX : {
1010 return (charval == charval2)
1011 || (Character.toUpperCase(charval) == Character
1012 .toUpperCase(charval2))
1013 || (Character.toLowerCase(charval) == Character
1014 .toLowerCase(charval2));
1015 }
1016 case GREATER : {
1017 return charval >= charval2;
1018 }
1019 case LESS : {
1020 return charval <= charval2;
1021 }
1022 }
1023 return false;
1024 }
1025
1026 private boolean compare_Boolean(int operation, boolean boolval,
1027 Object value2) {
1028 if (operation == SUBSTRING) {
1029 return false;
1030 }
1031 boolean boolval2 = Boolean.valueOf(((String) value2).trim())
1032 .booleanValue();
1033 switch (operation) {
1034 case APPROX :
1035 case EQUAL :
1036 case GREATER :
1037 case LESS : {
1038 return boolval == boolval2;
1039 }
1040 }
1041 return false;
1042 }
1043
1044 private boolean compare_Float(int operation, float floatval,
1045 Object value2) {
1046 if (operation == SUBSTRING) {
1047 return false;
1048 }
1049 float floatval2 = Float.parseFloat(((String) value2).trim());
1050 switch (operation) {
1051 case APPROX :
1052 case EQUAL : {
1053 return Float.compare(floatval, floatval2) == 0;
1054 }
1055 case GREATER : {
1056 return Float.compare(floatval, floatval2) >= 0;
1057 }
1058 case LESS : {
1059 return Float.compare(floatval, floatval2) <= 0;
1060 }
1061 }
1062 return false;
1063 }
1064
1065 private boolean compare_Double(int operation, double doubleval,
1066 Object value2) {
1067 if (operation == SUBSTRING) {
1068 return false;
1069 }
1070 double doubleval2 = Double.parseDouble(((String) value2).trim());
1071 switch (operation) {
1072 case APPROX :
1073 case EQUAL : {
1074 return Double.compare(doubleval, doubleval2) == 0;
1075 }
1076 case GREATER : {
1077 return Double.compare(doubleval, doubleval2) >= 0;
1078 }
1079 case LESS : {
1080 return Double.compare(doubleval, doubleval2) <= 0;
1081 }
1082 }
1083 return false;
1084 }
1085
1086 private static final Class[] constructorType = new Class[] {String.class};
1087
1088 private boolean compare_Comparable(int operation, Comparable value1,
1089 Object value2) {
1090 if (operation == SUBSTRING) {
1091 return false;
1092 }
1093 Constructor constructor;
Richard S. Hallc88fca32006-10-18 22:01:22 +00001094 try {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001095 constructor = value1.getClass().getConstructor(constructorType);
1096 }
1097 catch (NoSuchMethodException e) {
1098 return false;
1099 }
1100 try {
1101 if (!constructor.isAccessible())
1102 AccessController.doPrivileged(new SetAccessibleAction(
1103 constructor));
1104 value2 = constructor
1105 .newInstance(new Object[] {((String) value2).trim()});
1106 }
1107 catch (IllegalAccessException e) {
1108 return false;
Richard S. Hallc88fca32006-10-18 22:01:22 +00001109 }
1110 catch (InvocationTargetException e) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001111 return false;
1112 }
1113 catch (InstantiationException e) {
1114 return false;
1115 }
1116
1117 switch (operation) {
1118 case APPROX :
1119 case EQUAL : {
1120 return value1.compareTo(value2) == 0;
1121 }
1122 case GREATER : {
1123 return value1.compareTo(value2) >= 0;
1124 }
1125 case LESS : {
1126 return value1.compareTo(value2) <= 0;
1127 }
1128 }
1129 return false;
1130 }
1131
1132 private boolean compare_Unknown(int operation, Object value1,
1133 Object value2) {
1134 if (operation == SUBSTRING) {
1135 return false;
1136 }
1137 Constructor constructor;
1138 try {
1139 constructor = value1.getClass().getConstructor(constructorType);
1140 }
1141 catch (NoSuchMethodException e) {
1142 return false;
1143 }
1144 try {
1145 if (!constructor.isAccessible())
1146 AccessController.doPrivileged(new SetAccessibleAction(
1147 constructor));
1148 value2 = constructor
1149 .newInstance(new Object[] {((String) value2).trim()});
1150 }
1151 catch (IllegalAccessException e) {
1152 return false;
1153 }
1154 catch (InvocationTargetException e) {
1155 return false;
1156 }
1157 catch (InstantiationException e) {
1158 return false;
1159 }
1160
1161 switch (operation) {
1162 case APPROX :
1163 case EQUAL :
1164 case GREATER :
1165 case LESS : {
1166 return value1.equals(value2);
1167 }
1168 }
1169 return false;
1170 }
1171
1172 /**
1173 * Map a string for an APPROX (~=) comparison.
1174 *
1175 * This implementation removes white spaces. This is the minimum
1176 * implementation allowed by the OSGi spec.
1177 *
1178 * @param input Input string.
1179 * @return String ready for APPROX comparison.
1180 */
1181 private static String approxString(String input) {
1182 boolean changed = false;
1183 char[] output = input.toCharArray();
1184 int cursor = 0;
1185 for (int i = 0, length = output.length; i < length; i++) {
1186 char c = output[i];
1187
1188 if (Character.isWhitespace(c)) {
1189 changed = true;
1190 continue;
1191 }
1192
1193 output[cursor] = c;
1194 cursor++;
1195 }
1196
1197 return changed ? new String(output, 0, cursor) : input;
1198 }
1199
1200 /**
1201 * Parser class for OSGi filter strings. This class parses the complete
1202 * filter string and builds a tree of Filter objects rooted at the
1203 * parent.
1204 */
1205 private static class Parser {
1206 private final String filterstring;
1207 private final char[] filterChars;
1208 private int pos;
1209
1210 Parser(String filterstring) {
1211 this.filterstring = filterstring;
1212 filterChars = filterstring.toCharArray();
1213 pos = 0;
1214 }
1215
1216 FilterImpl parse() throws InvalidSyntaxException {
1217 FilterImpl filter;
1218 try {
1219 filter = parse_filter();
1220 }
1221 catch (ArrayIndexOutOfBoundsException e) {
1222 throw new InvalidSyntaxException("Filter ended abruptly",
1223 filterstring);
1224 }
1225
1226 if (pos != filterChars.length) {
1227 throw new InvalidSyntaxException(
1228 "Extraneous trailing characters: "
1229 + filterstring.substring(pos), filterstring);
1230 }
1231 return filter;
1232 }
1233
1234 private FilterImpl parse_filter() throws InvalidSyntaxException {
1235 FilterImpl filter;
1236 skipWhiteSpace();
1237
1238 if (filterChars[pos] != '(') {
1239 throw new InvalidSyntaxException("Missing '(': "
1240 + filterstring.substring(pos), filterstring);
1241 }
1242
1243 pos++;
1244
1245 filter = parse_filtercomp();
1246
1247 skipWhiteSpace();
1248
1249 if (filterChars[pos] != ')') {
1250 throw new InvalidSyntaxException("Missing ')': "
1251 + filterstring.substring(pos), filterstring);
1252 }
1253
1254 pos++;
1255
1256 skipWhiteSpace();
1257
1258 return filter;
1259 }
1260
1261 private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
1262 skipWhiteSpace();
1263
1264 char c = filterChars[pos];
1265
1266 switch (c) {
1267 case '&' : {
1268 pos++;
1269 return parse_and();
1270 }
1271 case '|' : {
1272 pos++;
1273 return parse_or();
1274 }
1275 case '!' : {
1276 pos++;
1277 return parse_not();
1278 }
1279 }
1280 return parse_item();
1281 }
1282
1283 private FilterImpl parse_and() throws InvalidSyntaxException {
1284 skipWhiteSpace();
1285
1286 if (filterChars[pos] != '(') {
1287 throw new InvalidSyntaxException("Missing '(': "
1288 + filterstring.substring(pos), filterstring);
1289 }
1290
1291 List operands = new ArrayList(10);
1292
1293 while (filterChars[pos] == '(') {
1294 FilterImpl child = parse_filter();
1295 operands.add(child);
1296 }
1297
1298 return new FilterImpl(FilterImpl.AND, null, operands
1299 .toArray(new FilterImpl[operands.size()]));
1300 }
1301
1302 private FilterImpl parse_or() throws InvalidSyntaxException {
1303 skipWhiteSpace();
1304
1305 if (filterChars[pos] != '(') {
1306 throw new InvalidSyntaxException("Missing '(': "
1307 + filterstring.substring(pos), filterstring);
1308 }
1309
1310 List operands = new ArrayList(10);
1311
1312 while (filterChars[pos] == '(') {
1313 FilterImpl child = parse_filter();
1314 operands.add(child);
1315 }
1316
1317 return new FilterImpl(FilterImpl.OR, null, operands
1318 .toArray(new FilterImpl[operands.size()]));
1319 }
1320
1321 private FilterImpl parse_not() throws InvalidSyntaxException {
1322 skipWhiteSpace();
1323
1324 if (filterChars[pos] != '(') {
1325 throw new InvalidSyntaxException("Missing '(': "
1326 + filterstring.substring(pos), filterstring);
1327 }
1328
1329 FilterImpl child = parse_filter();
1330
1331 return new FilterImpl(FilterImpl.NOT, null, child);
1332 }
1333
1334 private FilterImpl parse_item() throws InvalidSyntaxException {
1335 String attr = parse_attr();
1336
1337 skipWhiteSpace();
1338
1339 switch (filterChars[pos]) {
1340 case '~' : {
1341 if (filterChars[pos + 1] == '=') {
1342 pos += 2;
1343 return new FilterImpl(FilterImpl.APPROX, attr,
1344 parse_value());
1345 }
1346 break;
1347 }
1348 case '>' : {
1349 if (filterChars[pos + 1] == '=') {
1350 pos += 2;
1351 return new FilterImpl(FilterImpl.GREATER, attr,
1352 parse_value());
1353 }
1354 break;
1355 }
1356 case '<' : {
1357 if (filterChars[pos + 1] == '=') {
1358 pos += 2;
1359 return new FilterImpl(FilterImpl.LESS, attr,
1360 parse_value());
1361 }
1362 break;
1363 }
1364 case '=' : {
1365 if (filterChars[pos + 1] == '*') {
1366 int oldpos = pos;
1367 pos += 2;
1368 skipWhiteSpace();
1369 if (filterChars[pos] == ')') {
1370 return new FilterImpl(FilterImpl.PRESENT, attr,
1371 null);
1372 }
1373 pos = oldpos;
1374 }
1375
1376 pos++;
1377 Object string = parse_substring();
1378
1379 if (string instanceof String) {
1380 return new FilterImpl(FilterImpl.EQUAL, attr,
1381 string);
1382 }
1383 return new FilterImpl(FilterImpl.SUBSTRING, attr,
1384 string);
1385 }
1386 }
1387
1388 throw new InvalidSyntaxException("Invalid operator: "
1389 + filterstring.substring(pos), filterstring);
1390 }
1391
1392 private String parse_attr() throws InvalidSyntaxException {
1393 skipWhiteSpace();
1394
1395 int begin = pos;
1396 int end = pos;
1397
1398 char c = filterChars[pos];
1399
1400 while (c != '~' && c != '<' && c != '>' && c != '=' && c != '('
1401 && c != ')') {
1402 pos++;
1403
1404 if (!Character.isWhitespace(c)) {
1405 end = pos;
1406 }
1407
1408 c = filterChars[pos];
1409 }
1410
1411 int length = end - begin;
1412
1413 if (length == 0) {
1414 throw new InvalidSyntaxException("Missing attr: "
1415 + filterstring.substring(pos), filterstring);
1416 }
1417
1418 return new String(filterChars, begin, length);
1419 }
1420
1421 private String parse_value() throws InvalidSyntaxException {
1422 StringBuffer sb = new StringBuffer(filterChars.length - pos);
1423
1424 parseloop: while (true) {
1425 char c = filterChars[pos];
1426
1427 switch (c) {
1428 case ')' : {
1429 break parseloop;
1430 }
1431
1432 case '(' : {
1433 throw new InvalidSyntaxException("Invalid value: "
1434 + filterstring.substring(pos), filterstring);
1435 }
1436
1437 case '\\' : {
1438 pos++;
1439 c = filterChars[pos];
1440 /* fall through into default */
1441 }
1442
1443 default : {
1444 sb.append(c);
1445 pos++;
1446 break;
1447 }
1448 }
1449 }
1450
1451 if (sb.length() == 0) {
1452 throw new InvalidSyntaxException("Missing value: "
1453 + filterstring.substring(pos), filterstring);
1454 }
1455
1456 return sb.toString();
1457 }
1458
1459 private Object parse_substring() throws InvalidSyntaxException {
1460 StringBuffer sb = new StringBuffer(filterChars.length - pos);
1461
1462 List operands = new ArrayList(10);
1463
1464 parseloop: while (true) {
1465 char c = filterChars[pos];
1466
1467 switch (c) {
1468 case ')' : {
1469 if (sb.length() > 0) {
1470 operands.add(sb.toString());
1471 }
1472
1473 break parseloop;
1474 }
1475
1476 case '(' : {
1477 throw new InvalidSyntaxException("Invalid value: "
1478 + filterstring.substring(pos), filterstring);
1479 }
1480
1481 case '*' : {
1482 if (sb.length() > 0) {
1483 operands.add(sb.toString());
1484 }
1485
1486 sb.setLength(0);
1487
1488 operands.add(null);
1489 pos++;
1490
1491 break;
1492 }
1493
1494 case '\\' : {
1495 pos++;
1496 c = filterChars[pos];
1497 /* fall through into default */
1498 }
1499
1500 default : {
1501 sb.append(c);
1502 pos++;
1503 break;
1504 }
1505 }
1506 }
1507
1508 int size = operands.size();
1509
1510 if (size == 0) {
1511 throw new InvalidSyntaxException("Missing value: "
1512 + filterstring.substring(pos), filterstring);
1513 }
1514
1515 if (size == 1) {
1516 Object single = operands.get(0);
1517
1518 if (single != null) {
1519 return single;
1520 }
1521 }
1522
1523 return operands.toArray(new String[size]);
1524 }
1525
1526 private void skipWhiteSpace() {
1527 for (int length = filterChars.length; (pos < length)
1528 && Character.isWhitespace(filterChars[pos]);) {
1529 pos++;
1530 }
Richard S. Hallc88fca32006-10-18 22:01:22 +00001531 }
1532 }
Richard S. Hallf28d6762009-06-08 19:31:06 +00001533 }
1534
1535 /**
1536 * This Dictionary is used for case-insensitive key lookup during filter
1537 * evaluation. This Dictionary implementation only supports the get
1538 * operation using a String key as no other operations are used by the
1539 * Filter implementation.
1540 */
1541 private static class CaseInsensitiveDictionary extends Dictionary {
1542 private final Dictionary dictionary;
1543 private final String[] keys;
1544
1545 /**
1546 * Create a case insensitive dictionary from the specified dictionary.
1547 *
1548 * @param dictionary
1549 * @throws IllegalArgumentException If <code>dictionary</code> contains
1550 * case variants of the same key name.
1551 */
1552 CaseInsensitiveDictionary(Dictionary dictionary) {
1553 if (dictionary == null) {
1554 this.dictionary = null;
1555 this.keys = new String[0];
1556 return;
1557 }
1558 this.dictionary = dictionary;
1559 List keyList = new ArrayList(dictionary.size());
1560 for (Enumeration e = dictionary.keys(); e.hasMoreElements();) {
1561 Object k = e.nextElement();
1562 if (k instanceof String) {
1563 String key = (String) k;
1564 for (Iterator i = keyList.iterator(); i.hasNext();) {
1565 if (key.equalsIgnoreCase((String) i.next())) {
1566 throw new IllegalArgumentException();
1567 }
1568 }
1569 keyList.add(key);
1570 }
1571 }
1572 this.keys = (String[]) keyList.toArray(new String[keyList.size()]);
Richard S. Hallc88fca32006-10-18 22:01:22 +00001573 }
Richard S. Hallf28d6762009-06-08 19:31:06 +00001574
1575 public Object get(Object o) {
1576 String k = (String) o;
1577 for (int i = 0, length = keys.length; i < length; i++) {
1578 String key = keys[i];
1579 if (key.equalsIgnoreCase(k)) {
1580 return dictionary.get(key);
1581 }
1582 }
1583 return null;
Richard S. Hallc88fca32006-10-18 22:01:22 +00001584 }
Richard S. Hallf28d6762009-06-08 19:31:06 +00001585
1586 public boolean isEmpty() {
1587 throw new UnsupportedOperationException();
Richard S. Hallc88fca32006-10-18 22:01:22 +00001588 }
Richard S. Hallf28d6762009-06-08 19:31:06 +00001589
1590 public Enumeration keys() {
1591 throw new UnsupportedOperationException();
1592 }
1593
1594 public Enumeration elements() {
1595 throw new UnsupportedOperationException();
1596 }
1597
1598 public Object put(Object key, Object value) {
1599 throw new UnsupportedOperationException();
1600 }
1601
1602 public Object remove(Object key) {
1603 throw new UnsupportedOperationException();
1604 }
1605
1606 public int size() {
1607 throw new UnsupportedOperationException();
1608 }
1609 }
1610
1611 /**
1612 * This Dictionary is used for key lookup from a ServiceReference during
1613 * filter evaluation. This Dictionary implementation only supports the get
1614 * operation using a String key as no other operations are used by the
1615 * Filter implementation.
1616 */
1617 private static class ServiceReferenceDictionary extends Dictionary {
1618 private final ServiceReference reference;
1619
1620 ServiceReferenceDictionary(ServiceReference reference) {
1621 this.reference = reference;
1622 }
1623
1624 public Object get(Object key) {
1625 if (reference == null) {
1626 return null;
1627 }
1628 return reference.getProperty((String) key);
1629 }
1630
1631 public boolean isEmpty() {
1632 throw new UnsupportedOperationException();
1633 }
1634
1635 public Enumeration keys() {
1636 throw new UnsupportedOperationException();
1637 }
1638
1639 public Enumeration elements() {
1640 throw new UnsupportedOperationException();
1641 }
1642
1643 public Object put(Object key, Object value) {
1644 throw new UnsupportedOperationException();
1645 }
1646
1647 public Object remove(Object key) {
1648 throw new UnsupportedOperationException();
1649 }
1650
1651 public int size() {
1652 throw new UnsupportedOperationException();
1653 }
1654 }
1655
1656 private static class SetAccessibleAction implements PrivilegedAction {
1657 private final AccessibleObject accessible;
1658
1659 SetAccessibleAction(AccessibleObject accessible) {
1660 this.accessible = accessible;
1661 }
1662
1663 public Object run() {
1664 accessible.setAccessible(true);
1665 return null;
1666 }
1667 }
1668
1669 /**
1670 * This class contains a method to match a distinguished name (DN) chain
1671 * against and DN chain pattern.
1672 * <p>
1673 * The format of DNs are given in RFC 2253. We represent a signature chain
1674 * for an X.509 certificate as a semicolon separated list of DNs. This is
1675 * what we refer to as the DN chain. Each DN is made up of relative
1676 * distinguished names (RDN) which in turn are made up of key value pairs.
1677 * For example:
1678 *
1679 * <pre>
1680 * cn=ben+ou=research,o=ACME,c=us;ou=Super CA,c=CA
1681 * </pre>
1682 *
1683 * is made up of two DNs: "<code>cn=ben+ou=research,o=ACME,c=us</code>
1684 * " and " <code>ou=Super CA,c=CA</code>
1685 * ". The first DN is made of of three RDNs: "
1686 * <code>cn=ben+ou=research</code>" and "<code>o=ACME</code>" and "
1687 * <code>c=us</code>". The first RDN has two name value pairs: "
1688 * <code>cn=ben</code>" and "<code>ou=research</code>".
1689 * <p>
1690 * A chain pattern makes use of wildcards ('*' or '-') to match against DNs,
1691 * and wildcards ('*') to match againts DN prefixes, and value. If a DN in a
1692 * match pattern chain is made up of a wildcard ("*"), that wildcard will
1693 * match zero or one DNs in the chain. If a DN in a match pattern chain is
1694 * made up of a wildcard ("-"), that wildcard will match zero or more DNs in
1695 * the chain. If the first RDN of a DN is the wildcard ("*"), that DN will
1696 * match any other DN with the same suffix (the DN with the wildcard RDN
1697 * removed). If a value of a name/value pair is a wildcard ("*"), the value
1698 * will match any value for that name.
1699 */
1700 private static class DNChainMatching {
1701 private static final String MINUS_WILDCARD = "-";
1702 private static final String STAR_WILDCARD = "*";
1703
1704 /**
1705 * Check the name/value pairs of the rdn against the pattern.
1706 *
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001707 * @param rdn List of name value pairs for a given RDN.
1708 * @param rdnPattern List of name value pattern pairs.
Richard S. Hallf28d6762009-06-08 19:31:06 +00001709 * @return true if the list of name value pairs match the pattern.
1710 */
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001711 private static boolean rdnmatch(List rdn, List rdnPattern) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001712 if (rdn.size() != rdnPattern.size()) {
1713 return false;
1714 }
1715 for (int i = 0; i < rdn.size(); i++) {
1716 String rdnNameValue = (String) rdn.get(i);
1717 String patNameValue = (String) rdnPattern.get(i);
1718 int rdnNameEnd = rdnNameValue.indexOf('=');
1719 int patNameEnd = patNameValue.indexOf('=');
1720 if (rdnNameEnd != patNameEnd
1721 || !rdnNameValue.regionMatches(0, patNameValue, 0,
1722 rdnNameEnd)) {
1723 return false;
1724 }
1725 String patValue = patNameValue.substring(patNameEnd);
1726 String rdnValue = rdnNameValue.substring(rdnNameEnd);
1727 if (!rdnValue.equals(patValue) && !patValue.equals("=*")
1728 && !patValue.equals("=#16012a")) {
1729 return false;
1730 }
1731 }
1732 return true;
1733 }
1734
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001735 private static boolean dnmatch(List dn, List dnPattern) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001736 int dnStart = 0;
1737 int patStart = 0;
1738 int patLen = dnPattern.size();
1739 if (patLen == 0) {
1740 return false;
1741 }
1742 if (dnPattern.get(0).equals(STAR_WILDCARD)) {
1743 patStart = 1;
1744 patLen--;
1745 }
1746 if (dn.size() < patLen) {
1747 return false;
1748 }
1749 else {
1750 if (dn.size() > patLen) {
1751 if (!dnPattern.get(0).equals(STAR_WILDCARD)) {
1752 // If the number of rdns do not match we must have a
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001753 // prefix map
Richard S. Hallf28d6762009-06-08 19:31:06 +00001754 return false;
1755 }
1756 // The rdnPattern and rdn must have the same number of
1757 // elements
1758 dnStart = dn.size() - patLen;
1759 }
1760 }
1761 for (int i = 0; i < patLen; i++) {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001762 if (!rdnmatch((List) dn.get(i + dnStart), (List) dnPattern
1763 .get(i + patStart))) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001764 return false;
1765 }
1766 }
1767 return true;
1768 }
1769
1770 /**
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001771 * Parses a distinguished name chain pattern and returns a List where
1772 * each element represents a distinguished name (DN) in the chain of
1773 * DNs. Each element will be either a String, if the element represents
1774 * a wildcard ("*" or "-"), or a List representing an RDN. Each element
1775 * in the RDN List will be a String, if the element represents a
1776 * wildcard ("*"), or a List of Strings, each String representing a
1777 * name/value pair in the RDN.
Richard S. Hallf28d6762009-06-08 19:31:06 +00001778 *
1779 * @param dnChain
1780 * @return a list of DNs.
1781 * @throws IllegalArgumentException
1782 */
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001783 private static List parseDNchainPattern(String dnChain) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001784 if (dnChain == null) {
1785 throw new IllegalArgumentException(
1786 "The DN chain must not be null.");
1787 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001788 List parsed = new ArrayList();
Richard S. Hallf28d6762009-06-08 19:31:06 +00001789 int startIndex = 0;
1790 startIndex = skipSpaces(dnChain, startIndex);
1791 while (startIndex < dnChain.length()) {
1792 int endIndex = startIndex;
1793 boolean inQuote = false;
1794 out: while (endIndex < dnChain.length()) {
1795 char c = dnChain.charAt(endIndex);
1796 switch (c) {
1797 case '"' :
1798 inQuote = !inQuote;
1799 break;
1800 case '\\' :
1801 endIndex++; // skip the escaped char
1802 break;
1803 case ';' :
1804 if (!inQuote)
1805 break out;
1806 }
1807 endIndex++;
1808 }
1809 if (endIndex > dnChain.length()) {
1810 throw new IllegalArgumentException("unterminated escape");
1811 }
1812 parsed.add(dnChain.substring(startIndex, endIndex));
1813 startIndex = endIndex + 1;
1814 startIndex = skipSpaces(dnChain, startIndex);
1815 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001816 return parseDNchain(parsed);
Richard S. Hallf28d6762009-06-08 19:31:06 +00001817 }
1818
1819 private static List parseDNchain(List chain) {
1820 if (chain == null) {
1821 throw new IllegalArgumentException("DN chain must not be null.");
1822 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001823 chain = new ArrayList(chain);
1824 // Now we parse is a list of strings, lets make List of rdn out
1825 // of them
Richard S. Hallf28d6762009-06-08 19:31:06 +00001826 for (int i = 0; i < chain.size(); i++) {
1827 String dn = (String) chain.get(i);
1828 if (dn.equals(STAR_WILDCARD) || dn.equals(MINUS_WILDCARD)) {
1829 continue;
1830 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001831 List rdns = new ArrayList();
Richard S. Hallf28d6762009-06-08 19:31:06 +00001832 if (dn.charAt(0) == '*') {
1833 if (dn.charAt(1) != ',') {
1834 throw new IllegalArgumentException(
1835 "invalid wildcard prefix");
1836 }
1837 rdns.add(STAR_WILDCARD);
1838 dn = new X500Principal(dn.substring(2))
1839 .getName(X500Principal.CANONICAL);
1840 }
1841 else {
1842 dn = new X500Principal(dn).getName(X500Principal.CANONICAL);
1843 }
1844 // Now dn is a nice CANONICAL DN
1845 parseDN(dn, rdns);
1846 chain.set(i, rdns);
1847 }
1848 if (chain.size() == 0) {
1849 throw new IllegalArgumentException("empty DN chain");
1850 }
1851 return chain;
1852 }
1853
1854 /**
1855 * Increment startIndex until the end of dnChain is hit or until it is
1856 * the index of a non-space character.
1857 */
1858 private static int skipSpaces(String dnChain, int startIndex) {
1859 while (startIndex < dnChain.length()
1860 && dnChain.charAt(startIndex) == ' ') {
1861 startIndex++;
1862 }
1863 return startIndex;
1864 }
1865
1866 /**
1867 * Takes a distinguished name in canonical form and fills in the
1868 * rdnArray with the extracted RDNs.
1869 *
1870 * @param dn the distinguished name in canonical form.
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001871 * @param rdn the list to fill in with RDNs extracted from the dn
Richard S. Hallf28d6762009-06-08 19:31:06 +00001872 * @throws IllegalArgumentException if a formatting error is found.
1873 */
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001874 private static void parseDN(String dn, List rdn) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00001875 int startIndex = 0;
1876 char c = '\0';
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001877 List nameValues = new ArrayList();
Richard S. Hallf28d6762009-06-08 19:31:06 +00001878 while (startIndex < dn.length()) {
1879 int endIndex;
1880 for (endIndex = startIndex; endIndex < dn.length(); endIndex++) {
1881 c = dn.charAt(endIndex);
1882 if (c == ',' || c == '+') {
1883 break;
1884 }
1885 if (c == '\\') {
1886 endIndex++; // skip the escaped char
1887 }
1888 }
1889 if (endIndex > dn.length()) {
1890 throw new IllegalArgumentException("unterminated escape "
1891 + dn);
1892 }
1893 nameValues.add(dn.substring(startIndex, endIndex));
1894 if (c != '+') {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001895 rdn.add(nameValues);
Richard S. Hallf28d6762009-06-08 19:31:06 +00001896 if (endIndex != dn.length()) {
1897 nameValues = new ArrayList();
1898 }
1899 else {
1900 nameValues = null;
1901 }
1902 }
1903 startIndex = endIndex + 1;
1904 }
1905 if (nameValues != null) {
1906 throw new IllegalArgumentException("improperly terminated DN "
1907 + dn);
1908 }
1909 }
1910
1911 /**
1912 * This method will return an 'index' which points to a non-wildcard DN
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001913 * or the end-of-list.
Richard S. Hallf28d6762009-06-08 19:31:06 +00001914 */
1915 private static int skipWildCards(List dnChainPattern,
1916 int dnChainPatternIndex) {
1917 int i;
1918 for (i = dnChainPatternIndex; i < dnChainPattern.size(); i++) {
1919 Object dnPattern = dnChainPattern.get(i);
1920 if (dnPattern instanceof String) {
1921 if (!dnPattern.equals(STAR_WILDCARD)
1922 && !dnPattern.equals(MINUS_WILDCARD)) {
1923 throw new IllegalArgumentException(
1924 "expected wildcard in DN pattern");
1925 }
1926 // otherwise continue skipping over wild cards
1927 }
1928 else {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001929 if (dnPattern instanceof List) {
1930 // if its a list then we have our 'non-wildcard' DN
Richard S. Hallf28d6762009-06-08 19:31:06 +00001931 break;
1932 }
1933 else {
1934 // unknown member of the DNChainPattern
1935 throw new IllegalArgumentException(
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001936 "expected String or List in DN Pattern");
Richard S. Hallf28d6762009-06-08 19:31:06 +00001937 }
1938 }
1939 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001940 // i either points to end-of-list, or to the first
1941 // non-wildcard pattern after dnChainPatternIndex
Richard S. Hallf28d6762009-06-08 19:31:06 +00001942 return i;
1943 }
1944
1945 /**
1946 * recursively attempt to match the DNChain, and the DNChainPattern
1947 * where DNChain is of the format: "DN;DN;DN;" and DNChainPattern is of
1948 * the format: "DNPattern;*;DNPattern" (or combinations of this)
1949 */
1950 private static boolean dnChainMatch(List dnChain, int dnChainIndex,
1951 List dnChainPattern, int dnChainPatternIndex)
1952 throws IllegalArgumentException {
1953 if (dnChainIndex >= dnChain.size()) {
1954 return false;
1955 }
1956 if (dnChainPatternIndex >= dnChainPattern.size()) {
1957 return false;
1958 }
1959 // check to see what the pattern starts with
1960 Object dnPattern = dnChainPattern.get(dnChainPatternIndex);
1961 if (dnPattern instanceof String) {
1962 if (!dnPattern.equals(STAR_WILDCARD)
1963 && !dnPattern.equals(MINUS_WILDCARD)) {
1964 throw new IllegalArgumentException(
1965 "expected wildcard in DN pattern");
1966 }
1967 // here we are processing a wild card as the first DN
1968 // skip all wildcard DN's
1969 if (dnPattern.equals(MINUS_WILDCARD)) {
1970 dnChainPatternIndex = skipWildCards(dnChainPattern,
1971 dnChainPatternIndex);
1972 }
1973 else {
1974 dnChainPatternIndex++; // only skip the '*' wildcard
1975 }
1976 if (dnChainPatternIndex >= dnChainPattern.size()) {
1977 // return true iff the wild card is '-' or if we are at the
1978 // end of the chain
1979 return dnPattern.equals(MINUS_WILDCARD) ? true : dnChain
1980 .size() - 1 == dnChainIndex;
1981 }
1982 //
1983 // we will now recursively call to see if the rest of the
Richard S. Hall2ff3db92009-08-25 14:31:32 +00001984 // DNChainPattern matches increasingly smaller portions of the
1985 // rest of the DNChain
Richard S. Hallf28d6762009-06-08 19:31:06 +00001986 //
1987 if (dnPattern.equals(STAR_WILDCARD)) {
1988 // '*' option: only wildcard on 0 or 1
1989 return dnChainMatch(dnChain, dnChainIndex, dnChainPattern,
1990 dnChainPatternIndex)
1991 || dnChainMatch(dnChain, dnChainIndex + 1,
1992 dnChainPattern, dnChainPatternIndex);
1993 }
1994 for (int i = dnChainIndex; i < dnChain.size(); i++) {
1995 // '-' option: wildcard 0 or more
1996 if (dnChainMatch(dnChain, i, dnChainPattern,
1997 dnChainPatternIndex)) {
1998 return true;
1999 }
2000 }
2001 // if we are here, then we didn't find a match.. fall through to
2002 // failure
2003 }
2004 else {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002005 if (dnPattern instanceof List) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00002006 // here we have to do a deeper check for each DN in the
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002007 // pattern until we hit a wild card
Richard S. Hallf28d6762009-06-08 19:31:06 +00002008 do {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002009 if (!dnmatch((List) dnChain.get(dnChainIndex),
2010 (List) dnPattern)) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00002011 return false;
2012 }
2013 // go to the next set of DN's in both chains
2014 dnChainIndex++;
2015 dnChainPatternIndex++;
2016 // if we finished the pattern then it all matched
2017 if ((dnChainIndex >= dnChain.size())
2018 && (dnChainPatternIndex >= dnChainPattern
2019 .size())) {
2020 return true;
2021 }
2022 // if the DN Chain is finished, but the pattern isn't
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002023 // finished then if the rest of the pattern is not
2024 // wildcard then we are done
Richard S. Hallf28d6762009-06-08 19:31:06 +00002025 if (dnChainIndex >= dnChain.size()) {
2026 dnChainPatternIndex = skipWildCards(dnChainPattern,
2027 dnChainPatternIndex);
2028 // return TRUE iff the pattern index moved past the
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002029 // list-size (implying that the rest of the pattern
2030 // is all wildcards)
Richard S. Hallf28d6762009-06-08 19:31:06 +00002031 return dnChainPatternIndex >= dnChainPattern.size();
2032 }
2033 // if the pattern finished, but the chain continues then
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002034 // we have a mis-match
Richard S. Hallf28d6762009-06-08 19:31:06 +00002035 if (dnChainPatternIndex >= dnChainPattern.size()) {
2036 return false;
2037 }
2038 // get the next DN Pattern
2039 dnPattern = dnChainPattern.get(dnChainPatternIndex);
2040 if (dnPattern instanceof String) {
2041 if (!dnPattern.equals(STAR_WILDCARD)
2042 && !dnPattern.equals(MINUS_WILDCARD)) {
2043 throw new IllegalArgumentException(
2044 "expected wildcard in DN pattern");
2045 }
2046 // if the next DN is a 'wildcard', then we will
2047 // recurse
2048 return dnChainMatch(dnChain, dnChainIndex,
2049 dnChainPattern, dnChainPatternIndex);
2050 }
2051 else {
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002052 if (!(dnPattern instanceof List)) {
Richard S. Hallf28d6762009-06-08 19:31:06 +00002053 throw new IllegalArgumentException(
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002054 "expected String or List in DN Pattern");
Richard S. Hallf28d6762009-06-08 19:31:06 +00002055 }
2056 }
2057 // if we are here, then we will just continue to the
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002058 // match the next set of DN's from the DNChain, and the
2059 // DNChainPattern since both are lists
Richard S. Hallf28d6762009-06-08 19:31:06 +00002060 } while (true);
2061 // should never reach here?
2062 }
2063 else {
2064 throw new IllegalArgumentException(
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002065 "expected String or List in DN Pattern");
Richard S. Hallf28d6762009-06-08 19:31:06 +00002066 }
2067 }
2068 // if we get here, the the default return is 'mis-match'
2069 return false;
2070 }
2071
2072 /**
2073 * Matches a distinguished name chain against a pattern of a
2074 * distinguished name chain.
2075 *
2076 * @param dnChain
2077 * @param pattern the pattern of distinguished name (DN) chains to match
2078 * against the dnChain. Wildcards ("*" or "-") can be used in
2079 * three cases:
2080 * <ol>
2081 * <li>As a DN. In this case, the DN will consist of just the "*"
2082 * or "-". When "*" is used it will match zero or one DNs. When
2083 * "-" is used it will match zero or more DNs. For example,
2084 * "cn=me,c=US;*;cn=you" will match
2085 * "cn=me,c=US";cn=you" and "cn=me,c=US;cn=her;cn=you". The
2086 * pattern "cn=me,c=US;-;cn=you" will match "cn=me,c=US";cn=you"
2087 * and "cn=me,c=US;cn=her;cn=him;cn=you".
2088 * <li>As a DN prefix. In this case, the DN must start with "*,".
2089 * The wild card will match zero or more RDNs at the start of a
2090 * DN. For example, "*,cn=me,c=US;cn=you" will match
2091 * "cn=me,c=US";cn=you" and
2092 * "ou=my org unit,o=my org,cn=me,c=US;cn=you"</li>
2093 * <li>As a value. In this case the value of a name value pair in
2094 * an RDN will be a "*". The wildcard will match any value for
2095 * the given name. For example, "cn=*,c=US;cn=you" will match
2096 * "cn=me,c=US";cn=you" and "cn=her,c=US;cn=you", but it will not
2097 * match "ou=my org unit,c=US;cn=you". If the wildcard does not
2098 * occur by itself in the value, it will not be used as a
2099 * wildcard. In other words, "cn=m*,c=US;cn=you" represents the
2100 * common name of "m*" not any common name starting with "m".</li>
2101 * </ol>
2102 * @return true if dnChain matches the pattern.
2103 * @throws IllegalArgumentException
2104 */
2105 static boolean match(String pattern, List/* <String> */dnChain) {
2106 List parsedDNChain;
2107 List parsedDNPattern;
2108 try {
2109 parsedDNChain = parseDNchain(dnChain);
2110 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002111 catch (RuntimeException e) {
2112 IllegalArgumentException iae = new IllegalArgumentException(
2113 "Invalid DN chain: " + toString(dnChain));
2114 iae.initCause(e);
2115 throw iae;
Richard S. Hallf28d6762009-06-08 19:31:06 +00002116 }
2117 try {
2118 parsedDNPattern = parseDNchainPattern(pattern);
2119 }
Richard S. Hall2ff3db92009-08-25 14:31:32 +00002120 catch (RuntimeException e) {
2121 IllegalArgumentException iae = new IllegalArgumentException(
2122 "Invalid match pattern: " + pattern);
2123 iae.initCause(e);
2124 throw iae;
Richard S. Hallf28d6762009-06-08 19:31:06 +00002125 }
2126 return dnChainMatch(parsedDNChain, 0, parsedDNPattern, 0);
2127 }
2128
2129 private static String toString(List dnChain) {
2130 if (dnChain == null) {
2131 return null;
2132 }
2133 StringBuffer sb = new StringBuffer();
2134 for (Iterator iChain = dnChain.iterator(); iChain.hasNext();) {
2135 sb.append(iChain.next());
2136 if (iChain.hasNext()) {
2137 sb.append("; ");
2138 }
2139 }
2140 return sb.toString();
Richard S. Hallc88fca32006-10-18 22:01:22 +00002141 }
2142 }
2143}