1 /******************************************************************************* 2 * Copyright (c) 2003, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 15 package org.eclipse.osgi.internal.framework; 16 17 import static java.util.Objects.requireNonNull; 18 19 import java.lang.reflect.AccessibleObject; 20 import java.lang.reflect.Constructor; 21 import java.lang.reflect.Method; 22 import java.lang.reflect.Modifier; 23 import java.security.AccessController; 24 import java.security.PrivilegedAction; 25 import java.util.AbstractMap; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.Dictionary; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Set; 35 import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap; 36 import org.eclipse.osgi.internal.debug.Debug; 37 import org.eclipse.osgi.internal.messages.Msg; 38 import org.eclipse.osgi.internal.serviceregistry.ServiceReferenceImpl; 39 import org.eclipse.osgi.util.NLS; 40 import org.osgi.framework.Constants; 41 import org.osgi.framework.Filter; 42 import org.osgi.framework.InvalidSyntaxException; 43 import org.osgi.framework.ServiceReference; 44 import org.osgi.framework.Version; 45 46 /** 47 * RFC 1960-based Filter. Filter objects can be created by calling the 48 * constructor with the desired filter string. A Filter object can be called 49 * numerous times to determine if the match argument matches the filter string 50 * that was used to create the Filter object. 51 * <p> 52 * The syntax of a filter string is the string representation of LDAP search 53 * filters as defined in RFC 1960: <i>A String Representation of LDAP Search 54 * Filters</i> (available at http://www.ietf.org/rfc/rfc1960.txt). It should be 55 * noted that RFC 2254: <i>A String Representation of LDAP Search Filters</i> 56 * (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes RFC 1960 but 57 * only adds extensible matching and is not applicable for this API. 58 * <p> 59 * The string representation of an LDAP search filter is defined by the 60 * following grammar. It uses a prefix format. 61 * 62 * <pre> 63 * <filter> ::= '(' <filtercomp> ')' 64 * <filtercomp> ::= <and> | <or> | <not> | <item> 65 * <and> ::= '&' <filterlist> 66 * <or> ::= '|' <filterlist> 67 * <not> ::= '!' <filter> 68 * <filterlist> ::= <filter> | <filter> <filterlist> 69 * <item> ::= <simple> | <present> | <substring> 70 * <simple> ::= <attr> <filtertype> <value> 71 * <filtertype> ::= <equal> | <approx> | <greater> | <less> 72 * <equal> ::= '=' 73 * <approx> ::= '˜=' 74 * <greater> ::= '>=' 75 * <less> ::= '<=' 76 * <present> ::= <attr> '=*' 77 * <substring> ::= <attr> '=' <initial> <any> <final> 78 * <initial> ::= NULL | <value> 79 * <any> ::= '*' <starval> 80 * <starval> ::= NULL | <value> '*' <starval> 81 * <final> ::= NULL | <value> 82 * </pre> 83 * 84 * {@code <attr>} is a string representing an attribute, or key, in the 85 * properties objects of the registered services. Attribute names are not case 86 * sensitive; that is cn and CN both refer to the same attribute. 87 * {@code <value>} is a string representing the value, or part of one, of 88 * a key in the properties objects of the registered services. If a 89 * {@code <value>} must contain one of the characters ' {@code *}' or 90 * '{@code (}' or '{@code )}', these characters should be escaped by preceding 91 * them with the backslash '{@code \}' character. Note that although both the 92 * {@code <substring>} and {@code <present>} productions can produce 93 * the {@code 'attr=*'} construct, this construct is used only to denote a 94 * presence filter. 95 * <p> 96 * Examples of LDAP filters are: 97 * 98 * <pre> 99 * "(cn=Babs Jensen)" 100 * "(!(cn=Tim Howes))" 101 * "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))" 102 * "(o=univ*of*mich*)" 103 * </pre> 104 * <p> 105 * The approximate match ({@code ~=}) is implementation specific but should at 106 * least ignore case and white space differences. Optional are codes like 107 * soundex or other smart "closeness" comparisons. 108 * <p> 109 * Comparison of values is not straightforward. Strings are compared differently 110 * than numbers and it is possible for a key to have multiple values. Note that 111 * that keys in the match argument must always be strings. The comparison is 112 * defined by the object type of the key's value. The following rules apply for 113 * comparison: <blockquote> 114 * <TABLE BORDER=0> 115 * <TR> 116 * <TD><b>Property Value Type </b></TD> 117 * <TD><b>Comparison Type</b></TD> 118 * </TR> 119 * <TR> 120 * <TD>String</TD> 121 * <TD>String comparison</TD> 122 * </TR> 123 * <TR valign=top> 124 * <TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal</TD> 125 * <TD>numerical comparison</TD> 126 * </TR> 127 * <TR> 128 * <TD>Character</TD> 129 * <TD>character comparison</TD> 130 * </TR> 131 * <TR> 132 * <TD>Boolean</TD> 133 * <TD>equality comparisons only</TD> 134 * </TR> 135 * <TR> 136 * <TD>[] (array)</TD> 137 * <TD>recursively applied to values</TD> 138 * </TR> 139 * <TR> 140 * <TD>Collection</TD> 141 * <TD>recursively applied to values</TD> 142 * </TR> 143 * </TABLE> 144 * Note: arrays of primitives are also supported. </blockquote> A filter matches 145 * a key that has multiple values if it matches at least one of those values. 146 * For example, 147 * 148 * <pre> 149 * Dictionary d = new Hashtable(); 150 * d.put("cn", new String[] { 151 * "a", "b", "c" 152 * }); 153 * </pre> 154 * 155 * d will match {@code (cn=a)} and also {@code (cn=b)} 156 * <p> 157 * A filter component that references a key having an unrecognizable data type 158 * will evaluate to {@code false} . 159 */ 160 public abstract class FilterImpl implements Filter { 161 /* normalized filter string for Filter object */ 162 private transient String filterString; 163 164 /** 165 * Creates a {@link FilterImpl} object. This filter object may be used to 166 * match a {@link ServiceReference} or a Dictionary. 167 * <p> 168 * If the filter cannot be parsed, an {@link InvalidSyntaxException} will be 169 * thrown with a human readable message where the filter became unparsable. 170 * 171 * @param filterString the filter string. 172 * @throws InvalidSyntaxException If the filter parameter contains an 173 * invalid filter string that cannot be parsed. 174 */ newInstance(String filterString)175 public static FilterImpl newInstance(String filterString) throws InvalidSyntaxException { 176 return newInstance(filterString, false); 177 } 178 newInstance(String filterString, boolean debug)179 public static FilterImpl newInstance(String filterString, boolean debug) throws InvalidSyntaxException { 180 return new Parser(filterString, debug).parse(); 181 } 182 FilterImpl()183 FilterImpl() { 184 // empty constructor for subclasses 185 } 186 187 /** 188 * Filter using a service's properties. 189 * <p> 190 * This {@code Filter} is executed using the keys and values of the 191 * referenced service's properties. The keys are looked up in a case 192 * insensitive manner. 193 * 194 * @param reference The reference to the service whose properties are used 195 * in the match. 196 * @return {@code true} if the service's properties match this 197 * {@code Filter}; {@code false} otherwise. 198 */ 199 @Override match(ServiceReference<?> reference)200 public boolean match(ServiceReference<?> reference) { 201 return matches0((reference != null) ? ServiceReferenceMap.asMap(reference) : Collections.<String, Object> emptyMap()); 202 } 203 204 /** 205 * Filter using a {@code Dictionary} with case insensitive key lookup. This 206 * {@code Filter} is executed using the specified {@code Dictionary}'s keys 207 * and values. The keys are looked up in a case insensitive manner. 208 * 209 * @param dictionary The {@code Dictionary} whose key/value pairs are used 210 * in the match. 211 * @return {@code true} if the {@code Dictionary}'s values match this 212 * filter; {@code false} otherwise. 213 * @throws IllegalArgumentException If {@code dictionary} contains case 214 * variants of the same key name. 215 */ 216 @Override match(Dictionary<String, ?> dictionary)217 public boolean match(Dictionary<String, ?> dictionary) { 218 return matches0((dictionary != null) ? new CaseInsensitiveDictionaryMap<>(dictionary) : Collections.<String, Object> emptyMap()); 219 } 220 221 /** 222 * Filter using a {@code Dictionary}. This {@code Filter} is executed using 223 * the specified {@code Dictionary}'s keys and values. The keys are looked 224 * up in a normal manner respecting case. 225 * 226 * @param dictionary The {@code Dictionary} whose key/value pairs are used 227 * in the match. 228 * @return {@code true} if the {@code Dictionary}'s values match this 229 * filter; {@code false} otherwise. 230 * @since 1.3 231 */ 232 @Override matchCase(Dictionary<String, ?> dictionary)233 public boolean matchCase(Dictionary<String, ?> dictionary) { 234 return matches0((dictionary != null) ? DictionaryMap.asMap(dictionary) : Collections.<String, Object> emptyMap()); 235 } 236 237 /** 238 * Filter using a {@code Map}. This {@code Filter} is executed using the 239 * specified {@code Map}'s keys and values. The keys are looked up in a 240 * normal manner respecting case. 241 * 242 * @param map The {@code Map} whose key/value pairs are used in the match. 243 * Maps with {@code null} key or values are not supported. A 244 * {@code null} value is considered not present to the filter. 245 * @return {@code true} if the {@code Map}'s values match this filter; 246 * {@code false} otherwise. 247 * @since 1.6 248 */ 249 @Override matches(Map<String, ?> map)250 public boolean matches(Map<String, ?> map) { 251 return matches0((map != null) ? map : Collections.<String, Object> emptyMap()); 252 } 253 matches0(Map<String, ?> map)254 abstract boolean matches0(Map<String, ?> map); 255 256 /** 257 * Returns this {@code Filter}'s filter string. 258 * <p> 259 * The filter string is normalized by removing whitespace which does not 260 * affect the meaning of the filter. 261 * 262 * @return This {@code Filter}'s filter string. 263 */ 264 @Override toString()265 public String toString() { 266 String result = filterString; 267 if (result == null) { 268 filterString = result = normalize(new StringBuilder()).toString(); 269 } 270 return result; 271 } 272 273 /** 274 * Returns this {@code Filter}'s normalized filter string. 275 * <p> 276 * The filter string is normalized by removing whitespace which does not 277 * affect the meaning of the filter. 278 * 279 * @return This {@code Filter}'s filter string. 280 */ normalize(StringBuilder sb)281 abstract StringBuilder normalize(StringBuilder sb); 282 283 /** 284 * Compares this {@code Filter} to another {@code Filter}. 285 * <p> 286 * This implementation returns the result of calling 287 * {@code this.toString().equals(obj.toString()}. 288 * 289 * @param obj The object to compare against this {@code Filter}. 290 * @return If the other object is a {@code Filter} object, then returns the 291 * result of calling {@code this.toString().equals(obj.toString()}; 292 * {@code false} otherwise. 293 */ 294 @Override equals(Object obj)295 public boolean equals(Object obj) { 296 if (obj == this) { 297 return true; 298 } 299 300 if (!(obj instanceof Filter)) { 301 return false; 302 } 303 304 return this.toString().equals(obj.toString()); 305 } 306 307 /** 308 * Returns the hashCode for this {@code Filter}. 309 * <p> 310 * This implementation returns the result of calling 311 * {@code this.toString().hashCode()}. 312 * 313 * @return The hashCode of this {@code Filter}. 314 */ 315 @Override hashCode()316 public int hashCode() { 317 return this.toString().hashCode(); 318 } 319 320 static final class And extends FilterImpl { 321 private final FilterImpl[] operands; 322 And(FilterImpl[] operands)323 And(FilterImpl[] operands) { 324 this.operands = operands; 325 } 326 327 @Override matches0(Map<String, ?> map)328 boolean matches0(Map<String, ?> map) { 329 for (FilterImpl operand : operands) { 330 if (!operand.matches0(map)) { 331 return false; 332 } 333 } 334 return true; 335 } 336 337 @Override normalize(StringBuilder sb)338 StringBuilder normalize(StringBuilder sb) { 339 sb.append('(').append('&'); 340 for (FilterImpl operand : operands) { 341 operand.normalize(sb); 342 } 343 return sb.append(')'); 344 } 345 346 @Override getPrimaryKeyValue(String primaryKey)347 public String getPrimaryKeyValue(String primaryKey) { 348 // just checking for simple filters here where primaryKey is the only attr or it is one attr of a base '&' clause 349 // (primaryKey=org.acme.BrickService) OK 350 // (&(primaryKey=org.acme.BrickService)(|(vendor=IBM)(vendor=SUN))) OK 351 // (primaryKey=org.acme.*) NOT OK 352 // (|(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) NOT OK 353 // (&(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) OK but only the first primaryKey is returned 354 for (FilterImpl operand : operands) { 355 if (operand instanceof Equal) { 356 String result = operand.getPrimaryKeyValue(primaryKey); 357 if (result != null) { 358 return result; 359 } 360 } 361 } 362 return null; 363 } 364 365 @Override getChildren()366 public List<FilterImpl> getChildren() { 367 return new ArrayList<>(Arrays.asList(operands)); 368 } 369 370 @Override getAttributesInternal(List<String> results)371 void getAttributesInternal(List<String> results) { 372 for (FilterImpl operand : operands) { 373 operand.getAttributesInternal(results); 374 } 375 } 376 377 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)378 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 379 for (FilterImpl operand : operands) { 380 operand.addAttributes(attributes, versionAttrs, false); 381 } 382 } 383 } 384 385 static final class Or extends FilterImpl { 386 private final FilterImpl[] operands; 387 Or(FilterImpl[] operands)388 Or(FilterImpl[] operands) { 389 this.operands = operands; 390 } 391 392 @Override matches0(Map<String, ?> map)393 boolean matches0(Map<String, ?> map) { 394 for (FilterImpl operand : operands) { 395 if (operand.matches0(map)) { 396 return true; 397 } 398 } 399 return false; 400 } 401 402 @Override normalize(StringBuilder sb)403 StringBuilder normalize(StringBuilder sb) { 404 sb.append('(').append('|'); 405 for (FilterImpl operand : operands) { 406 operand.normalize(sb); 407 } 408 return sb.append(')'); 409 } 410 411 @Override getChildren()412 public List<FilterImpl> getChildren() { 413 return new ArrayList<>(Arrays.asList(operands)); 414 } 415 416 @Override getAttributesInternal(List<String> results)417 void getAttributesInternal(List<String> results) { 418 for (FilterImpl operand : operands) { 419 operand.getAttributesInternal(results); 420 } 421 } 422 423 @Override getStandardOSGiAttributes(String... versions)424 public Map<String, String> getStandardOSGiAttributes(String... versions) { 425 throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: OR"); //$NON-NLS-1$ 426 } 427 428 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)429 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 430 throw new IllegalStateException("Invalid filter for standard OSGi requirements: OR"); //$NON-NLS-1$ 431 } 432 } 433 434 static final class Not extends FilterImpl { 435 private final FilterImpl operand; 436 Not(FilterImpl operand)437 Not(FilterImpl operand) { 438 this.operand = operand; 439 } 440 441 @Override matches0(Map<String, ?> map)442 boolean matches0(Map<String, ?> map) { 443 return !operand.matches0(map); 444 } 445 446 @Override normalize(StringBuilder sb)447 StringBuilder normalize(StringBuilder sb) { 448 sb.append('(').append('!'); 449 operand.normalize(sb); 450 return sb.append(')'); 451 } 452 453 @Override getAttributesInternal(List<String> results)454 void getAttributesInternal(List<String> results) { 455 operand.getAttributesInternal(results); 456 } 457 458 @Override getStandardOSGiAttributes(String... versions)459 public Map<String, String> getStandardOSGiAttributes(String... versions) { 460 throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: NOT"); //$NON-NLS-1$ 461 } 462 463 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)464 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 465 operand.addAttributes(attributes, versionAttrs, true); 466 } 467 } 468 469 static abstract class Item extends FilterImpl { 470 /** debug mode */ 471 final boolean debug; 472 final String attr; 473 Item(String attr, boolean debug)474 Item(String attr, boolean debug) { 475 this.attr = attr; 476 this.debug = debug; 477 } 478 479 @Override matches0(Map<String, ?> map)480 boolean matches0(Map<String, ?> map) { 481 return compare(map.get(attr)); 482 } 483 operation()484 abstract String operation(); 485 value()486 abstract String value(); 487 compare(Object value1)488 private boolean compare(Object value1) { 489 if (debug) { 490 if (value1 == null) { 491 Debug.println("compare(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 492 } else if (!(value1.getClass().isArray() || (value1 instanceof Collection<?>))) { 493 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 494 } 495 } 496 if (value1 == null) { 497 return false; 498 } 499 if (value1 instanceof String) { 500 return compare_String((String) value1); 501 } 502 if (value1 instanceof Version) { 503 return compare_Version((Version) value1); 504 } 505 506 Class<?> clazz = value1.getClass(); 507 if (clazz.isArray()) { 508 Class<?> type = clazz.getComponentType(); 509 if (type.isPrimitive()) { 510 return compare_PrimitiveArray(type, value1); 511 } 512 return compare_ObjectArray((Object[]) value1); 513 } 514 if (value1 instanceof Collection<?>) { 515 return compare_Collection((Collection<?>) value1); 516 } 517 if (value1 instanceof Integer) { 518 return compare_Integer(((Integer) value1).intValue()); 519 } 520 if (value1 instanceof Long) { 521 return compare_Long(((Long) value1).longValue()); 522 } 523 if (value1 instanceof Byte) { 524 return compare_Byte(((Byte) value1).byteValue()); 525 } 526 if (value1 instanceof Short) { 527 return compare_Short(((Short) value1).shortValue()); 528 } 529 if (value1 instanceof Character) { 530 return compare_Character(((Character) value1).charValue()); 531 } 532 if (value1 instanceof Float) { 533 return compare_Float(((Float) value1).floatValue()); 534 } 535 if (value1 instanceof Double) { 536 return compare_Double(((Double) value1).doubleValue()); 537 } 538 if (value1 instanceof Boolean) { 539 return compare_Boolean(((Boolean) value1).booleanValue()); 540 } 541 if (value1 instanceof Comparable<?>) { 542 @SuppressWarnings("unchecked") 543 Comparable<Object> comparable = (Comparable<Object>) value1; 544 return compare_Comparable(comparable); 545 } 546 return compare_Unknown(value1); 547 } 548 compare_Collection(Collection<?> collection)549 private boolean compare_Collection(Collection<?> collection) { 550 for (Object value1 : collection) { 551 if (compare(value1)) { 552 return true; 553 } 554 } 555 return false; 556 } 557 compare_ObjectArray(Object[] array)558 private boolean compare_ObjectArray(Object[] array) { 559 for (Object value1 : array) { 560 if (compare(value1)) { 561 return true; 562 } 563 } 564 return false; 565 } 566 compare_PrimitiveArray(Class<?> type, Object primarray)567 private boolean compare_PrimitiveArray(Class<?> type, Object primarray) { 568 if (Integer.TYPE.isAssignableFrom(type)) { 569 int[] array = (int[]) primarray; 570 for (int value1 : array) { 571 if (debug) { 572 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 573 } 574 if (compare_Integer(value1)) { 575 return true; 576 } 577 } 578 return false; 579 } 580 if (Long.TYPE.isAssignableFrom(type)) { 581 long[] array = (long[]) primarray; 582 for (long value1 : array) { 583 if (debug) { 584 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 585 } 586 if (compare_Long(value1)) { 587 return true; 588 } 589 } 590 return false; 591 } 592 if (Byte.TYPE.isAssignableFrom(type)) { 593 byte[] array = (byte[]) primarray; 594 for (byte value1 : array) { 595 if (debug) { 596 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 597 } 598 if (compare_Byte(value1)) { 599 return true; 600 } 601 } 602 return false; 603 } 604 if (Short.TYPE.isAssignableFrom(type)) { 605 short[] array = (short[]) primarray; 606 for (short value1 : array) { 607 if (debug) { 608 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 609 } 610 if (compare_Short(value1)) { 611 return true; 612 } 613 } 614 return false; 615 } 616 if (Character.TYPE.isAssignableFrom(type)) { 617 char[] array = (char[]) primarray; 618 for (char value1 : array) { 619 if (debug) { 620 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 621 } 622 if (compare_Character(value1)) { 623 return true; 624 } 625 } 626 return false; 627 } 628 if (Float.TYPE.isAssignableFrom(type)) { 629 float[] array = (float[]) primarray; 630 for (float value1 : array) { 631 if (debug) { 632 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 633 } 634 if (compare_Float(value1)) { 635 return true; 636 } 637 } 638 return false; 639 } 640 if (Double.TYPE.isAssignableFrom(type)) { 641 double[] array = (double[]) primarray; 642 for (double value1 : array) { 643 if (debug) { 644 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 645 } 646 if (compare_Double(value1)) { 647 return true; 648 } 649 } 650 return false; 651 } 652 if (Boolean.TYPE.isAssignableFrom(type)) { 653 boolean[] array = (boolean[]) primarray; 654 for (boolean value1 : array) { 655 if (debug) { 656 Debug.println(operation() + "(" + value1 + "," + value() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 657 } 658 if (compare_Boolean(value1)) { 659 return true; 660 } 661 } 662 return false; 663 } 664 return false; 665 } 666 compare_String(String string)667 boolean compare_String(String string) { 668 return false; 669 } 670 compare_Version(Version value1)671 boolean compare_Version(Version value1) { 672 return false; 673 } 674 compare_Comparable(Comparable<Object> value1)675 boolean compare_Comparable(Comparable<Object> value1) { 676 return false; 677 } 678 compare_Unknown(Object value1)679 boolean compare_Unknown(Object value1) { 680 return false; 681 } 682 compare_Boolean(boolean boolval)683 boolean compare_Boolean(boolean boolval) { 684 return false; 685 } 686 compare_Byte(byte byteval)687 boolean compare_Byte(byte byteval) { 688 return false; 689 } 690 compare_Character(char charval)691 boolean compare_Character(char charval) { 692 return false; 693 } 694 compare_Double(double doubleval)695 boolean compare_Double(double doubleval) { 696 return false; 697 } 698 compare_Float(float floatval)699 boolean compare_Float(float floatval) { 700 return false; 701 } 702 compare_Integer(int intval)703 boolean compare_Integer(int intval) { 704 return false; 705 } 706 compare_Long(long longval)707 boolean compare_Long(long longval) { 708 return false; 709 } 710 compare_Short(short shortval)711 boolean compare_Short(short shortval) { 712 return false; 713 } 714 715 /** 716 * Encode the value string such that '(', '*', ')' and '\' are escaped. 717 * 718 * @param value unencoded value string. 719 */ encodeValue(StringBuilder sb, String value)720 static StringBuilder encodeValue(StringBuilder sb, String value) { 721 for (int i = 0, len = value.length(); i < len; i++) { 722 char c = value.charAt(i); 723 switch (c) { 724 case '(' : 725 case '*' : 726 case ')' : 727 case '\\' : 728 sb.append('\\'); 729 // FALL-THROUGH 730 default : 731 sb.append(c); 732 break; 733 } 734 } 735 return sb; 736 } 737 738 @Override getAttributesInternal(List<String> results)739 void getAttributesInternal(List<String> results) { 740 results.add(attr); 741 } 742 743 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)744 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 745 attributes.put(attr, value()); 746 } 747 } 748 749 static final class Present extends Item { Present(String attr, boolean debug)750 Present(String attr, boolean debug) { 751 super(attr, debug); 752 } 753 754 @Override matches0(Map<String, ?> map)755 boolean matches0(Map<String, ?> map) { 756 if (debug) { 757 Debug.println("PRESENT(" + attr + ")"); //$NON-NLS-1$ //$NON-NLS-2$ 758 } 759 return map.get(attr) != null; 760 } 761 762 @Override operation()763 String operation() { 764 return "PRESENT"; //$NON-NLS-1$ 765 } 766 767 @Override value()768 String value() { 769 return "*"; //$NON-NLS-1$ 770 } 771 772 @Override normalize(StringBuilder sb)773 StringBuilder normalize(StringBuilder sb) { 774 return sb.append('(').append(attr).append('=').append('*').append(')'); 775 } 776 } 777 778 static final class Substring extends Item { 779 final String[] substrings; 780 Substring(String attr, String[] substrings, boolean debug)781 Substring(String attr, String[] substrings, boolean debug) { 782 super(attr, debug); 783 this.substrings = substrings; 784 } 785 786 @Override operation()787 String operation() { 788 return "SUBSTRING"; //$NON-NLS-1$ 789 } 790 791 @Override value()792 String value() { 793 return value(new StringBuilder()).toString(); 794 } 795 796 @Override compare_String(String string)797 boolean compare_String(String string) { 798 int pos = 0; 799 for (int i = 0, size = substrings.length; i < size; i++) { 800 String substr = substrings[i]; 801 if (i + 1 < size) /* if this is not that last substr */ { 802 if (substr == null) /* * */ { 803 String substr2 = substrings[i + 1]; 804 if (substr2 == null) /* ** */ 805 continue; /* ignore first star */ 806 /* xxx */ 807 if (debug) { 808 Debug.println("indexOf(\"" + substr2 + "\"," + pos + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 809 } 810 int index = string.indexOf(substr2, pos); 811 if (index == -1) { 812 return false; 813 } 814 pos = index + substr2.length(); 815 if (i + 2 < size) // if there are more 816 // substrings, increment 817 // over the string we just 818 // matched; otherwise need 819 // to do the last substr 820 // check 821 i++; 822 } else /* xxx */ { 823 int len = substr.length(); 824 if (debug) { 825 Debug.println("regionMatches(" + pos + ",\"" + substr + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 826 } 827 if (string.regionMatches(pos, substr, 0, len)) { 828 pos += len; 829 } else { 830 return false; 831 } 832 } 833 } else /* last substr */ { 834 if (substr == null) /* * */ { 835 return true; 836 } 837 /* xxx */ 838 if (debug) { 839 Debug.println("regionMatches(" + pos + "," + substr + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 840 } 841 return string.endsWith(substr); 842 } 843 } 844 return true; 845 } 846 847 @Override normalize(StringBuilder sb)848 StringBuilder normalize(StringBuilder sb) { 849 sb.append('(').append(attr).append('='); 850 return value(sb).append(')'); 851 } 852 value(StringBuilder sb)853 private StringBuilder value(StringBuilder sb) { 854 for (String substr : substrings) { 855 if (substr == null) /* * */ { 856 sb.append('*'); 857 } else /* xxx */ { 858 encodeValue(sb, substr); 859 } 860 } 861 return sb; 862 } 863 } 864 865 static class Equal extends Item { 866 final String value; 867 Equal(String attr, String value, boolean debug)868 Equal(String attr, String value, boolean debug) { 869 super(attr, debug); 870 this.value = value; 871 } 872 873 @Override operation()874 String operation() { 875 return "EQUAL"; //$NON-NLS-1$ 876 } 877 878 @Override value()879 String value() { 880 return value; 881 } 882 comparison(int compare)883 boolean comparison(int compare) { 884 return compare == 0; 885 } 886 887 @Override compare_String(String string)888 boolean compare_String(String string) { 889 return comparison((string == value) ? 0 : string.compareTo(value)); 890 } 891 892 @Override compare_Version(Version value1)893 boolean compare_Version(Version value1) { 894 try { 895 Version version2 = Version.valueOf(value); 896 return comparison(value1.compareTo(version2)); 897 } catch (Exception e) { 898 // if the valueOf or compareTo method throws an exception 899 return false; 900 } 901 } 902 903 @Override compare_Boolean(boolean boolval)904 boolean compare_Boolean(boolean boolval) { 905 boolean boolval2 = Boolean.parseBoolean(value.trim()); 906 return comparison(Boolean.compare(boolval, boolval2)); 907 } 908 909 @Override compare_Byte(byte byteval)910 boolean compare_Byte(byte byteval) { 911 byte byteval2; 912 try { 913 byteval2 = Byte.parseByte(value.trim()); 914 } catch (IllegalArgumentException e) { 915 return false; 916 } 917 return comparison(Byte.compare(byteval, byteval2)); 918 } 919 920 @Override compare_Character(char charval)921 boolean compare_Character(char charval) { 922 char charval2; 923 try { 924 charval2 = value.charAt(0); 925 } catch (IndexOutOfBoundsException e) { 926 return false; 927 } 928 return comparison(Character.compare(charval, charval2)); 929 } 930 931 @Override compare_Double(double doubleval)932 boolean compare_Double(double doubleval) { 933 double doubleval2; 934 try { 935 doubleval2 = Double.parseDouble(value.trim()); 936 } catch (IllegalArgumentException e) { 937 return false; 938 } 939 return comparison(Double.compare(doubleval, doubleval2)); 940 } 941 942 @Override compare_Float(float floatval)943 boolean compare_Float(float floatval) { 944 float floatval2; 945 try { 946 floatval2 = Float.parseFloat(value.trim()); 947 } catch (IllegalArgumentException e) { 948 return false; 949 } 950 return comparison(Float.compare(floatval, floatval2)); 951 } 952 953 @Override compare_Integer(int intval)954 boolean compare_Integer(int intval) { 955 int intval2; 956 try { 957 intval2 = Integer.parseInt(value.trim()); 958 } catch (IllegalArgumentException e) { 959 return false; 960 } 961 return comparison(Integer.compare(intval, intval2)); 962 } 963 964 @Override compare_Long(long longval)965 boolean compare_Long(long longval) { 966 long longval2; 967 try { 968 longval2 = Long.parseLong(value.trim()); 969 } catch (IllegalArgumentException e) { 970 return false; 971 } 972 return comparison(Long.compare(longval, longval2)); 973 } 974 975 @Override compare_Short(short shortval)976 boolean compare_Short(short shortval) { 977 short shortval2; 978 try { 979 shortval2 = Short.parseShort(value.trim()); 980 } catch (IllegalArgumentException e) { 981 return false; 982 } 983 return comparison(Short.compare(shortval, shortval2)); 984 } 985 986 @Override compare_Comparable(Comparable<Object> value1)987 boolean compare_Comparable(Comparable<Object> value1) { 988 Object value2 = valueOf(value1.getClass()); 989 if (value2 == null) { 990 return false; 991 } 992 try { 993 return comparison(value1.compareTo(value2)); 994 } catch (Exception e) { 995 // if the compareTo method throws an exception; return false 996 return false; 997 } 998 } 999 1000 @Override compare_Unknown(Object value1)1001 boolean compare_Unknown(Object value1) { 1002 Object value2 = valueOf(value1.getClass()); 1003 if (value2 == null) { 1004 return false; 1005 } 1006 try { 1007 return value1.equals(value2); 1008 } catch (Exception e) { 1009 // if the equals method throws an exception; return false 1010 return false; 1011 } 1012 } 1013 1014 @Override normalize(StringBuilder sb)1015 StringBuilder normalize(StringBuilder sb) { 1016 sb.append('(').append(attr).append('='); 1017 return encodeValue(sb, value).append(')'); 1018 } 1019 valueOf(Class<?> target)1020 Object valueOf(Class<?> target) { 1021 do { 1022 Method method; 1023 try { 1024 method = target.getMethod("valueOf", String.class); //$NON-NLS-1$ 1025 } catch (NoSuchMethodException e) { 1026 break; 1027 } 1028 if (Modifier.isStatic(method.getModifiers()) && target.isAssignableFrom(method.getReturnType())) { 1029 setAccessible(method); 1030 try { 1031 return method.invoke(null, value.trim()); 1032 } catch (Error e) { 1033 throw e; 1034 } catch (Throwable e) { 1035 return null; 1036 } 1037 } 1038 } while (false); 1039 1040 do { 1041 Constructor<?> constructor; 1042 try { 1043 constructor = target.getConstructor(String.class); 1044 } catch (NoSuchMethodException e) { 1045 break; 1046 } 1047 setAccessible(constructor); 1048 try { 1049 return constructor.newInstance(value.trim()); 1050 } catch (Error e) { 1051 throw e; 1052 } catch (Throwable e) { 1053 return null; 1054 } 1055 } while (false); 1056 1057 return null; 1058 } 1059 setAccessible(final AccessibleObject accessible)1060 private static void setAccessible(final AccessibleObject accessible) { 1061 if (!accessible.isAccessible()) { 1062 AccessController.doPrivileged(new PrivilegedAction<Void>() { 1063 @Override 1064 public Void run() { 1065 accessible.setAccessible(true); 1066 return null; 1067 } 1068 }); 1069 } 1070 } 1071 1072 @Override getPrimaryKeyValue(String primaryKey)1073 public String getPrimaryKeyValue(String primaryKey) { 1074 if (attr.equalsIgnoreCase(primaryKey)) { 1075 return value; 1076 } 1077 return null; 1078 } 1079 1080 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)1081 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 1082 if (!versionAttrs.containsKey(attr)) { 1083 attributes.put(attr, value); 1084 } else { 1085 // this is an exact range e.g. [value,value] 1086 Range currentRange = versionAttrs.get(attr); 1087 if (currentRange != null) { 1088 if (not) { 1089 // this is an expanded form of the filter, e.g.: 1090 // [1.0,2.0) -> (&(version>=1.0)(version<=2.0)(!(version=2.0))) 1091 currentRange.addExclude(Version.valueOf(value)); 1092 } else { 1093 throw new IllegalStateException("Invalid range for: " + attr); //$NON-NLS-1$ 1094 } 1095 } else { 1096 currentRange = new Range(); 1097 Version version = Version.valueOf(value); 1098 currentRange.setLeft('[', version); 1099 currentRange.setRight(']', version); 1100 versionAttrs.put(attr, currentRange); 1101 } 1102 } 1103 } 1104 } 1105 1106 static final class LessEqual extends Equal { LessEqual(String attr, String value, boolean debug)1107 LessEqual(String attr, String value, boolean debug) { 1108 super(attr, value, debug); 1109 } 1110 1111 @Override operation()1112 String operation() { 1113 return "LESS"; //$NON-NLS-1$ 1114 } 1115 1116 @Override comparison(int compare)1117 boolean comparison(int compare) { 1118 return compare <= 0; 1119 } 1120 1121 @Override normalize(StringBuilder sb)1122 StringBuilder normalize(StringBuilder sb) { 1123 sb.append('(').append(attr).append('<').append('='); 1124 return encodeValue(sb, value).append(')'); 1125 } 1126 1127 @Override getPrimaryKeyValue(String primaryKey)1128 public String getPrimaryKeyValue(String primaryKey) { 1129 return null; 1130 } 1131 1132 @Override getStandardOSGiAttributes(String... versions)1133 public Map<String, String> getStandardOSGiAttributes(String... versions) { 1134 throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: " + operation()); //$NON-NLS-1$ 1135 } 1136 1137 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)1138 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 1139 if (!versionAttrs.containsKey(attr)) { 1140 throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$ 1141 } 1142 Range currentRange = versionAttrs.get(attr); 1143 if (currentRange == null) { 1144 currentRange = new Range(); 1145 versionAttrs.put(attr, currentRange); 1146 } 1147 if (not) { 1148 // this must be a range start "(value" 1149 if (!currentRange.setLeft('(', Version.valueOf(value))) { 1150 throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$ 1151 } 1152 } else { 1153 // this must be a range end "value]" 1154 if (!currentRange.setRight(']', Version.valueOf(value))) { 1155 throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$ 1156 } 1157 } 1158 } 1159 } 1160 1161 static final class GreaterEqual extends Equal { GreaterEqual(String attr, String value, boolean debug)1162 GreaterEqual(String attr, String value, boolean debug) { 1163 super(attr, value, debug); 1164 } 1165 1166 @Override operation()1167 String operation() { 1168 return "GREATER"; //$NON-NLS-1$ 1169 } 1170 1171 @Override comparison(int compare)1172 boolean comparison(int compare) { 1173 return compare >= 0; 1174 } 1175 1176 @Override normalize(StringBuilder sb)1177 StringBuilder normalize(StringBuilder sb) { 1178 sb.append('(').append(attr).append('>').append('='); 1179 return encodeValue(sb, value).append(')'); 1180 } 1181 1182 @Override getPrimaryKeyValue(String primaryKey)1183 public String getPrimaryKeyValue(String primaryKey) { 1184 return null; 1185 } 1186 1187 @Override getStandardOSGiAttributes(String... versions)1188 public Map<String, String> getStandardOSGiAttributes(String... versions) { 1189 throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: " + operation()); //$NON-NLS-1$ 1190 } 1191 1192 @Override addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)1193 void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not) { 1194 if (!versionAttrs.containsKey(attr)) { 1195 throw new IllegalStateException("Invalid attribute: " + attr); //$NON-NLS-1$ 1196 } 1197 Range currentRange = versionAttrs.get(attr); 1198 if (currentRange == null) { 1199 currentRange = new Range(); 1200 versionAttrs.put(attr, currentRange); 1201 } 1202 if (not) { 1203 // this must be a range end "value)" 1204 if (!currentRange.setRight(')', Version.valueOf(value))) { 1205 throw new IllegalStateException("range end is already processed for attribute: " + attr); //$NON-NLS-1$ 1206 } 1207 } else { 1208 // this must be a range start "[value" 1209 if (!currentRange.setLeft('[', Version.valueOf(value))) { 1210 throw new IllegalStateException("range start is already processed for attribute: " + attr); //$NON-NLS-1$ 1211 } 1212 } 1213 } 1214 } 1215 1216 static final class Approx extends Equal { 1217 final String approx; 1218 Approx(String attr, String value, boolean debug)1219 Approx(String attr, String value, boolean debug) { 1220 super(attr, value, debug); 1221 this.approx = approxString(value); 1222 } 1223 1224 @Override operation()1225 String operation() { 1226 return "APPROX"; //$NON-NLS-1$ 1227 } 1228 1229 @Override value()1230 String value() { 1231 return approx; 1232 } 1233 1234 @Override compare_String(String string)1235 boolean compare_String(String string) { 1236 string = approxString(string); 1237 return string.equalsIgnoreCase(approx); 1238 } 1239 1240 @Override compare_Character(char charval)1241 boolean compare_Character(char charval) { 1242 char charval2; 1243 try { 1244 charval2 = approx.charAt(0); 1245 } catch (IndexOutOfBoundsException e) { 1246 return false; 1247 } 1248 return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2)); 1249 } 1250 1251 @Override normalize(StringBuilder sb)1252 StringBuilder normalize(StringBuilder sb) { 1253 sb.append('(').append(attr).append('~').append('='); 1254 return encodeValue(sb, approx).append(')'); 1255 } 1256 1257 /** 1258 * Map a string for an APPROX (~=) comparison. This implementation 1259 * removes white spaces. This is the minimum implementation allowed by 1260 * the OSGi spec. 1261 * 1262 * @param input Input string. 1263 * @return String ready for APPROX comparison. 1264 */ approxString(String input)1265 static String approxString(String input) { 1266 boolean changed = false; 1267 char[] output = input.toCharArray(); 1268 int cursor = 0; 1269 for (char c : output) { 1270 if (Character.isWhitespace(c)) { 1271 changed = true; 1272 continue; 1273 } 1274 1275 output[cursor] = c; 1276 cursor++; 1277 } 1278 1279 return changed ? new String(output, 0, cursor) : input; 1280 } 1281 1282 @Override getPrimaryKeyValue(String primaryKey)1283 public String getPrimaryKeyValue(String primaryKey) { 1284 return null; 1285 } 1286 1287 @Override getStandardOSGiAttributes(String... versions)1288 public Map<String, String> getStandardOSGiAttributes(String... versions) { 1289 throw new IllegalArgumentException("Invalid filter for standard OSGi Attributes: " + operation()); //$NON-NLS-1$ 1290 } 1291 } 1292 1293 /** 1294 * Returns the leftmost required objectClass value for the filter to evaluate to true. 1295 * 1296 * @return The leftmost required objectClass value or null if none could be determined. 1297 */ getRequiredObjectClass()1298 public String getRequiredObjectClass() { 1299 return getPrimaryKeyValue(Constants.OBJECTCLASS); 1300 } 1301 1302 /** 1303 * Returns the leftmost required primary key value for the filter to evaluate to true. 1304 * This is useful for indexing candidates to match against this filter. 1305 * @param primaryKey the primary key 1306 * @return The leftmost required primary key value or null if none could be determined. 1307 */ getPrimaryKeyValue(String primaryKey)1308 public String getPrimaryKeyValue(String primaryKey) { 1309 // just checking for simple filters here where primaryKey is the only attr or it is one attr of a base '&' clause 1310 // (primaryKey=org.acme.BrickService) OK 1311 // (&(primaryKey=org.acme.BrickService)(|(vendor=IBM)(vendor=SUN))) OK 1312 // (primaryKey=org.acme.*) NOT OK 1313 // (|(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) NOT OK 1314 // (&(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) OK but only the first objectClass is returned 1315 return null; 1316 } 1317 getChildren()1318 public List<FilterImpl> getChildren() { 1319 return Collections.emptyList(); 1320 } 1321 1322 /** 1323 * Returns all the attributes contained within this filter 1324 * @return all the attributes contained within this filter 1325 */ getAttributes()1326 public String[] getAttributes() { 1327 List<String> results = new ArrayList<>(); 1328 getAttributesInternal(results); 1329 return results.toArray(new String[0]); 1330 } 1331 getAttributesInternal(List<String> results)1332 abstract void getAttributesInternal(List<String> results); 1333 getStandardOSGiAttributes(String... versions)1334 public Map<String, String> getStandardOSGiAttributes(String... versions) { 1335 Map<String, String> result = new HashMap<>(); 1336 Map<String, Range> versionAttrs = new HashMap<>(); 1337 if (versions != null) { 1338 for (String versionAttr : versions) { 1339 versionAttrs.put(versionAttr, null); 1340 } 1341 } 1342 addAttributes(result, versionAttrs, false); 1343 for (Map.Entry<String, Range> entry : versionAttrs.entrySet()) { 1344 Range range = entry.getValue(); 1345 if (range != null) { 1346 result.put(entry.getKey(), range.toString()); 1347 } 1348 } 1349 1350 return result; 1351 } 1352 addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not)1353 abstract void addAttributes(Map<String, String> attributes, Map<String, Range> versionAttrs, boolean not); 1354 1355 /** 1356 * Parser class for OSGi filter strings. This class parses the complete 1357 * filter string and builds a tree of FilterImpl objects rooted at the 1358 * parent. 1359 */ 1360 static private final class Parser { 1361 private final boolean debug; 1362 private final String filterstring; 1363 private final char[] filterChars; 1364 private int pos; 1365 Parser(String filterstring, boolean debug)1366 Parser(String filterstring, boolean debug) { 1367 this.debug = debug; 1368 this.filterstring = filterstring; 1369 filterChars = filterstring.toCharArray(); 1370 pos = 0; 1371 } 1372 parse()1373 FilterImpl parse() throws InvalidSyntaxException { 1374 FilterImpl filter; 1375 try { 1376 filter = parse_filter(); 1377 } catch (ArrayIndexOutOfBoundsException e) { 1378 throw new InvalidSyntaxException(Msg.FILTER_TERMINATED_ABRUBTLY, filterstring, e); 1379 } 1380 1381 if (pos != filterChars.length) { 1382 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_TRAILING_CHARACTERS, filterstring.substring(pos)), filterstring); 1383 } 1384 return filter; 1385 } 1386 parse_filter()1387 private FilterImpl parse_filter() throws InvalidSyntaxException { 1388 FilterImpl filter; 1389 skipWhiteSpace(); 1390 1391 if (filterChars[pos] != '(') { 1392 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, filterstring.substring(pos)), filterstring); 1393 } 1394 1395 pos++; 1396 1397 filter = parse_filtercomp(); 1398 1399 skipWhiteSpace(); 1400 1401 if (filterChars[pos] != ')') { 1402 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_RIGHTPAREN, filterstring.substring(pos)), filterstring); 1403 } 1404 1405 pos++; 1406 1407 skipWhiteSpace(); 1408 1409 return filter; 1410 } 1411 parse_filtercomp()1412 private FilterImpl parse_filtercomp() throws InvalidSyntaxException { 1413 skipWhiteSpace(); 1414 1415 char c = filterChars[pos]; 1416 1417 switch (c) { 1418 case '&' : { 1419 pos++; 1420 return parse_and(); 1421 } 1422 case '|' : { 1423 pos++; 1424 return parse_or(); 1425 } 1426 case '!' : { 1427 pos++; 1428 return parse_not(); 1429 } 1430 } 1431 return parse_item(); 1432 } 1433 parse_and()1434 private FilterImpl parse_and() throws InvalidSyntaxException { 1435 int lookahead = pos; 1436 skipWhiteSpace(); 1437 1438 if (filterChars[pos] != '(') { 1439 pos = lookahead - 1; 1440 return parse_item(); 1441 } 1442 1443 List<FilterImpl> operands = new ArrayList<>(10); 1444 1445 while (filterChars[pos] == '(') { 1446 FilterImpl child = parse_filter(); 1447 operands.add(child); 1448 } 1449 1450 return new FilterImpl.And(operands.toArray(new FilterImpl[0])); 1451 } 1452 parse_or()1453 private FilterImpl parse_or() throws InvalidSyntaxException { 1454 int lookahead = pos; 1455 skipWhiteSpace(); 1456 1457 if (filterChars[pos] != '(') { 1458 pos = lookahead - 1; 1459 return parse_item(); 1460 } 1461 1462 List<FilterImpl> operands = new ArrayList<>(10); 1463 1464 while (filterChars[pos] == '(') { 1465 FilterImpl child = parse_filter(); 1466 operands.add(child); 1467 } 1468 1469 return new FilterImpl.Or(operands.toArray(new FilterImpl[0])); 1470 } 1471 parse_not()1472 private FilterImpl parse_not() throws InvalidSyntaxException { 1473 int lookahead = pos; 1474 skipWhiteSpace(); 1475 1476 if (filterChars[pos] != '(') { 1477 pos = lookahead - 1; 1478 return parse_item(); 1479 } 1480 1481 FilterImpl child = parse_filter(); 1482 1483 return new FilterImpl.Not(child); 1484 } 1485 parse_item()1486 private FilterImpl parse_item() throws InvalidSyntaxException { 1487 String attr = parse_attr(); 1488 1489 skipWhiteSpace(); 1490 1491 switch (filterChars[pos]) { 1492 case '~' : { 1493 if (filterChars[pos + 1] == '=') { 1494 pos += 2; 1495 return new FilterImpl.Approx(attr, parse_value(), debug); 1496 } 1497 break; 1498 } 1499 case '>' : { 1500 if (filterChars[pos + 1] == '=') { 1501 pos += 2; 1502 return new FilterImpl.GreaterEqual(attr, parse_value(), debug); 1503 } 1504 break; 1505 } 1506 case '<' : { 1507 if (filterChars[pos + 1] == '=') { 1508 pos += 2; 1509 return new FilterImpl.LessEqual(attr, parse_value(), debug); 1510 } 1511 break; 1512 } 1513 case '=' : { 1514 if (filterChars[pos + 1] == '*') { 1515 int oldpos = pos; 1516 pos += 2; 1517 skipWhiteSpace(); 1518 if (filterChars[pos] == ')') { 1519 return new FilterImpl.Present(attr, debug); 1520 } 1521 pos = oldpos; 1522 } 1523 1524 pos++; 1525 String[] substrings = parse_substring(); 1526 1527 int length = substrings.length; 1528 if (length == 0) { 1529 return new FilterImpl.Equal(attr, "", debug); //$NON-NLS-1$ 1530 } 1531 if (length == 1) { 1532 String single = substrings[0]; 1533 if (single != null) { 1534 return new FilterImpl.Equal(attr, single, debug); 1535 } 1536 } 1537 return new FilterImpl.Substring(attr, substrings, debug); 1538 } 1539 } 1540 1541 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_OPERATOR, filterstring.substring(pos)), filterstring); 1542 } 1543 parse_attr()1544 private String parse_attr() throws InvalidSyntaxException { 1545 skipWhiteSpace(); 1546 1547 int begin = pos; 1548 int end = pos; 1549 1550 char c = filterChars[pos]; 1551 1552 while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') { 1553 pos++; 1554 1555 if (!Character.isWhitespace(c)) { 1556 end = pos; 1557 } 1558 1559 c = filterChars[pos]; 1560 } 1561 1562 int length = end - begin; 1563 1564 if (length == 0) { 1565 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_ATTR, filterstring.substring(pos)), filterstring); 1566 } 1567 1568 return new String(filterChars, begin, length); 1569 } 1570 parse_value()1571 private String parse_value() throws InvalidSyntaxException { 1572 StringBuilder sb = new StringBuilder(filterChars.length - pos); 1573 1574 parseloop: while (true) { 1575 char c = filterChars[pos]; 1576 1577 switch (c) { 1578 case ')' : { 1579 break parseloop; 1580 } 1581 1582 case '(' : { 1583 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, filterstring.substring(pos)), filterstring); 1584 } 1585 1586 case '\\' : { 1587 pos++; 1588 c = filterChars[pos]; 1589 /* fall through into default */ 1590 } 1591 1592 default : { 1593 sb.append(c); 1594 pos++; 1595 break; 1596 } 1597 } 1598 } 1599 1600 if (sb.length() == 0) { 1601 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_VALUE, filterstring.substring(pos)), filterstring); 1602 } 1603 1604 return sb.toString(); 1605 } 1606 parse_substring()1607 private String[] parse_substring() throws InvalidSyntaxException { 1608 StringBuilder sb = new StringBuilder(filterChars.length - pos); 1609 1610 List<String> operands = new ArrayList<>(10); 1611 1612 parseloop: while (true) { 1613 char c = filterChars[pos]; 1614 1615 switch (c) { 1616 case ')' : { 1617 if (sb.length() > 0) { 1618 operands.add(sb.toString()); 1619 } 1620 1621 break parseloop; 1622 } 1623 1624 case '(' : { 1625 throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, filterstring.substring(pos)), filterstring); 1626 } 1627 1628 case '*' : { 1629 if (sb.length() > 0) { 1630 operands.add(sb.toString()); 1631 } 1632 1633 sb.setLength(0); 1634 1635 operands.add(null); 1636 pos++; 1637 1638 break; 1639 } 1640 1641 case '\\' : { 1642 pos++; 1643 c = filterChars[pos]; 1644 /* fall through into default */ 1645 } 1646 1647 default : { 1648 sb.append(c); 1649 pos++; 1650 break; 1651 } 1652 } 1653 } 1654 1655 return operands.toArray(new String[0]); 1656 } 1657 skipWhiteSpace()1658 private void skipWhiteSpace() { 1659 for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) { 1660 pos++; 1661 } 1662 } 1663 } 1664 1665 /** 1666 * This Map is used for key lookup during filter 1667 * evaluation. This Map implementation only supports the get operation using 1668 * a String key as no other operations are used by the Filter 1669 * implementation. 1670 */ 1671 private static final class DictionaryMap extends AbstractMap<String, Object> implements Map<String, Object> { asMap(Dictionary<String, ?> dictionary)1672 static Map<String, ?> asMap(Dictionary<String, ?> dictionary) { 1673 if (dictionary instanceof Map) { 1674 @SuppressWarnings("unchecked") 1675 Map<String, ?> coerced = (Map<String, ?>) dictionary; 1676 return coerced; 1677 } 1678 return new DictionaryMap(dictionary); 1679 } 1680 1681 private final Dictionary<String, ?> dictionary; 1682 1683 /** 1684 * Create a case insensitive map from the specified dictionary. 1685 * 1686 * @param dictionary 1687 * @throws IllegalArgumentException If {@code dictionary} contains case 1688 * variants of the same key name. 1689 */ DictionaryMap(Dictionary<String, ?> dictionary)1690 DictionaryMap(Dictionary<String, ?> dictionary) { 1691 this.dictionary = requireNonNull(dictionary); 1692 } 1693 1694 @Override get(Object key)1695 public Object get(Object key) { 1696 return dictionary.get(key); 1697 } 1698 1699 @Override entrySet()1700 public Set<Entry<String, Object>> entrySet() { 1701 throw new UnsupportedOperationException(); 1702 } 1703 } 1704 1705 /** 1706 * This Map is used for key lookup from a ServiceReference during filter 1707 * evaluation. This Map implementation only supports the get operation using 1708 * a String key as no other operations are used by the Filter 1709 * implementation. 1710 */ 1711 private static final class ServiceReferenceMap extends AbstractMap<String, Object> implements Map<String, Object> { asMap(ServiceReference<?> reference)1712 static Map<String, ?> asMap(ServiceReference<?> reference) { 1713 if (reference instanceof ServiceReferenceImpl) { 1714 return ((ServiceReferenceImpl<?>) reference).getRegistration().getProperties(); 1715 } 1716 return new ServiceReferenceMap(reference); 1717 } 1718 1719 private final ServiceReference<?> reference; 1720 ServiceReferenceMap(ServiceReference<?> reference)1721 ServiceReferenceMap(ServiceReference<?> reference) { 1722 this.reference = requireNonNull(reference); 1723 } 1724 1725 @Override get(Object key)1726 public Object get(Object key) { 1727 return reference.getProperty((String) key); 1728 } 1729 1730 @Override entrySet()1731 public Set<Entry<String, Object>> entrySet() { 1732 throw new UnsupportedOperationException(); 1733 } 1734 } 1735 1736 static class Range { 1737 private char leftRule = 0; 1738 private Version leftVersion; 1739 private Version rightVersion; 1740 private char rightRule = 0; 1741 private Collection<Version> excludes = new ArrayList<>(0); 1742 1743 @Override toString()1744 public String toString() { 1745 if (rightVersion == null) { 1746 return leftVersion.toString(); 1747 } 1748 return leftRule + leftVersion.toString() + ',' + rightVersion.toString() + rightRule; 1749 } 1750 addExclude(Version exclude)1751 void addExclude(Version exclude) { 1752 this.excludes.add(exclude); 1753 setLeft(leftRule, leftVersion); 1754 setRight(rightRule, rightVersion); 1755 } 1756 setLeft(char leftRule, Version leftVersion)1757 boolean setLeft(char leftRule, Version leftVersion) { 1758 if (this.leftVersion != null && this.leftVersion != leftVersion) 1759 return false; 1760 this.leftRule = excludes.contains(leftVersion) ? '(' : leftRule; 1761 this.leftVersion = leftVersion; 1762 return true; 1763 } 1764 setRight(char rightRule, Version rightVersion)1765 boolean setRight(char rightRule, Version rightVersion) { 1766 if (this.rightVersion != null && this.rightVersion != rightVersion) 1767 return false; 1768 this.rightRule = excludes.contains(rightVersion) ? ')' : rightRule; 1769 this.rightVersion = rightVersion; 1770 return true; 1771 } 1772 } 1773 1774 } 1775