001 /* 002 * Copyright 2007-2016 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2008-2016 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021 package com.unboundid.ldap.sdk; 022 023 024 025 import java.io.Serializable; 026 import java.util.ArrayList; 027 import java.util.Arrays; 028 import java.util.Collection; 029 import java.util.HashSet; 030 import java.util.LinkedHashSet; 031 import java.util.List; 032 import java.util.TreeMap; 033 034 import com.unboundid.asn1.ASN1Boolean; 035 import com.unboundid.asn1.ASN1Buffer; 036 import com.unboundid.asn1.ASN1BufferSequence; 037 import com.unboundid.asn1.ASN1BufferSet; 038 import com.unboundid.asn1.ASN1Element; 039 import com.unboundid.asn1.ASN1Exception; 040 import com.unboundid.asn1.ASN1OctetString; 041 import com.unboundid.asn1.ASN1Sequence; 042 import com.unboundid.asn1.ASN1Set; 043 import com.unboundid.asn1.ASN1StreamReader; 044 import com.unboundid.asn1.ASN1StreamReaderSequence; 045 import com.unboundid.asn1.ASN1StreamReaderSet; 046 import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule; 047 import com.unboundid.ldap.matchingrules.MatchingRule; 048 import com.unboundid.ldap.sdk.schema.Schema; 049 import com.unboundid.util.ByteStringBuffer; 050 051 import static com.unboundid.ldap.sdk.LDAPMessages.*; 052 import static com.unboundid.util.Debug.*; 053 import static com.unboundid.util.StaticUtils.*; 054 import static com.unboundid.util.Validator.*; 055 056 057 058 /** 059 * This class provides a data structure that represents an LDAP search filter. 060 * It provides methods for creating various types of filters, as well as parsing 061 * a filter from a string. See 062 * <A HREF="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</A> for more 063 * information about representing search filters as strings. 064 * <BR><BR> 065 * The following filter types are defined: 066 * <UL> 067 * <LI><B>AND</B> -- This is used to indicate that a filter should match an 068 * entry only if all of the embedded filter components match that entry. 069 * An AND filter with zero embedded filter components is considered an 070 * LDAP TRUE filter as defined in 071 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will 072 * match any entry. AND filters contain only a set of embedded filter 073 * components, and each of those embedded components can itself be any 074 * type of filter, including an AND, OR, or NOT filter with additional 075 * embedded components.</LI> 076 * <LI><B>OR</B> -- This is used to indicate that a filter should match an 077 * entry only if at least one of the embedded filter components matches 078 * that entry. An OR filter with zero embedded filter components is 079 * considered an LDAP FALSE filter as defined in 080 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will 081 * never match any entry. OR filters contain only a set of embedded 082 * filter components, and each of those embedded components can itself be 083 * any type of filter, including an AND, OR, or NOT filter with additional 084 * embedded components.</LI> 085 * <LI><B>NOT</B> -- This is used to indicate that a filter should match an 086 * entry only if the embedded NOT component does not match the entry. A 087 * NOT filter contains only a single embedded NOT filter component, but 088 * that embedded component can itself be any type of filter, including an 089 * AND, OR, or NOT filter with additional embedded components.</LI> 090 * <LI><B>EQUALITY</B> -- This is used to indicate that a filter should match 091 * an entry only if the entry contains a value for the specified attribute 092 * that is equal to the provided assertion value. An equality filter 093 * contains only an attribute name and an assertion value.</LI> 094 * <LI><B>SUBSTRING</B> -- This is used to indicate that a filter should match 095 * an entry only if the entry contains at least one value for the 096 * specified attribute that matches the provided substring assertion. The 097 * substring assertion must contain at least one element of the following 098 * types: 099 * <UL> 100 * <LI>subInitial -- This indicates that the specified string must 101 * appear at the beginning of the attribute value. There can be at 102 * most one subInitial element in a substring assertion.</LI> 103 * <LI>subAny -- This indicates that the specified string may appear 104 * anywhere in the attribute value. There can be any number of 105 * substring subAny elements in a substring assertion. If there are 106 * multiple subAny elements, then they must match in the order that 107 * they are provided.</LI> 108 * <LI>subFinal -- This indicates that the specified string must appear 109 * at the end of the attribute value. There can be at most one 110 * subFinal element in a substring assertion.</LI> 111 * </UL> 112 * A substring filter contains only an attribute name and subInitial, 113 * subAny, and subFinal elements.</LI> 114 * <LI><B>GREATER-OR-EQUAL</B> -- This is used to indicate that a filter 115 * should match an entry only if that entry contains at least one value 116 * for the specified attribute that is greater than or equal to the 117 * provided assertion value. A greater-or-equal filter contains only an 118 * attribute name and an assertion value.</LI> 119 * <LI><B>LESS-OR-EQUAL</B> -- This is used to indicate that a filter should 120 * match an entry only if that entry contains at least one value for the 121 * specified attribute that is less than or equal to the provided 122 * assertion value. A less-or-equal filter contains only an attribute 123 * name and an assertion value.</LI> 124 * <LI><B>PRESENCE</B> -- This is used to indicate that a filter should match 125 * an entry only if the entry contains at least one value for the 126 * specified attribute. A presence filter contains only an attribute 127 * name.</LI> 128 * <LI><B>APPROXIMATE-MATCH</B> -- This is used to indicate that a filter 129 * should match an entry only if the entry contains at least one value for 130 * the specified attribute that is approximately equal to the provided 131 * assertion value. The definition of "approximately equal to" may vary 132 * from one server to another, and from one attribute to another, but it 133 * is often implemented as a "sounds like" match using a variant of the 134 * metaphone or double-metaphone algorithm. An approximate-match filter 135 * contains only an attribute name and an assertion value.</LI> 136 * <LI><B>EXTENSIBLE-MATCH</B> -- This is used to perform advanced types of 137 * matching against entries, according to the following criteria: 138 * <UL> 139 * <LI>If an attribute name is provided, then the assertion value must 140 * match one of the values for that attribute (potentially including 141 * values contained in the entry's DN). If a matching rule ID is 142 * also provided, then the associated matching rule will be used to 143 * determine whether there is a match; otherwise the default 144 * equality matching rule for that attribute will be used.</LI> 145 * <LI>If no attribute name is provided, then a matching rule ID must be 146 * given, and the corresponding matching rule will be used to 147 * determine whether any attribute in the target entry (potentially 148 * including attributes contained in the entry's DN) has at least 149 * one value that matches the provided assertion value.</LI> 150 * <LI>If the dnAttributes flag is set, then attributes contained in the 151 * entry's DN will also be evaluated to determine if they match the 152 * filter criteria. If it is not set, then attributes contained in 153 * the entry's DN (other than those contained in its RDN which are 154 * also present as separate attributes in the entry) will not be 155 * examined.</LI> 156 * </UL> 157 * An extensible match filter contains only an attribute name, matching 158 * rule ID, dnAttributes flag, and an assertion value.</LI> 159 * </UL> 160 * <BR><BR> 161 * There are two primary ways to create a search filter. The first is to create 162 * a filter from its string representation with the 163 * {@code Filter#create(String)} method, using the syntax described in RFC 4515. 164 * For example: 165 * <PRE> 166 * Filter f1 = Filter.create("(objectClass=*)"); 167 * Filter f2 = Filter.create("(uid=john.doe)"); 168 * Filter f3 = Filter.create("(|(givenName=John)(givenName=Johnathan))"); 169 * </PRE> 170 * <BR><BR> 171 * Creating a filter from its string representation is a common approach and 172 * seems to be relatively straightforward, but it does have some hidden dangers. 173 * This primarily comes from the potential for special characters in the filter 174 * string which need to be properly escaped. If this isn't done, then the 175 * search may fail or behave unexpectedly, or worse it could lead to a 176 * vulnerability in the application in which a malicious user could trick the 177 * application into retrieving more information than it should have. To avoid 178 * these problems, it may be better to construct filters from their individual 179 * components rather than their string representations, like: 180 * <PRE> 181 * Filter f1 = Filter.createPresenceFilter("objectClass"); 182 * Filter f2 = Filter.createEqualityFilter("uid", "john.doe"); 183 * Filter f3 = Filter.createORFilter( 184 * Filter.createEqualityFilter("givenName", "John"), 185 * Filter.createEqualityFilter("givenName", "Johnathan")); 186 * </PRE> 187 * In general, it is recommended to avoid creating filters from their string 188 * representations if any of that string representation may include 189 * user-provided data or special characters including non-ASCII characters, 190 * parentheses, asterisks, or backslashes. 191 */ 192 public final class Filter 193 implements Serializable 194 { 195 /** 196 * The BER type for AND search filters. 197 */ 198 public static final byte FILTER_TYPE_AND = (byte) 0xA0; 199 200 201 202 /** 203 * The BER type for OR search filters. 204 */ 205 public static final byte FILTER_TYPE_OR = (byte) 0xA1; 206 207 208 209 /** 210 * The BER type for NOT search filters. 211 */ 212 public static final byte FILTER_TYPE_NOT = (byte) 0xA2; 213 214 215 216 /** 217 * The BER type for equality search filters. 218 */ 219 public static final byte FILTER_TYPE_EQUALITY = (byte) 0xA3; 220 221 222 223 /** 224 * The BER type for substring search filters. 225 */ 226 public static final byte FILTER_TYPE_SUBSTRING = (byte) 0xA4; 227 228 229 230 /** 231 * The BER type for greaterOrEqual search filters. 232 */ 233 public static final byte FILTER_TYPE_GREATER_OR_EQUAL = (byte) 0xA5; 234 235 236 237 /** 238 * The BER type for lessOrEqual search filters. 239 */ 240 public static final byte FILTER_TYPE_LESS_OR_EQUAL = (byte) 0xA6; 241 242 243 244 /** 245 * The BER type for presence search filters. 246 */ 247 public static final byte FILTER_TYPE_PRESENCE = (byte) 0x87; 248 249 250 251 /** 252 * The BER type for approximate match search filters. 253 */ 254 public static final byte FILTER_TYPE_APPROXIMATE_MATCH = (byte) 0xA8; 255 256 257 258 /** 259 * The BER type for extensible match search filters. 260 */ 261 public static final byte FILTER_TYPE_EXTENSIBLE_MATCH = (byte) 0xA9; 262 263 264 265 /** 266 * The BER type for the subInitial substring filter element. 267 */ 268 private static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80; 269 270 271 272 /** 273 * The BER type for the subAny substring filter element. 274 */ 275 private static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81; 276 277 278 279 /** 280 * The BER type for the subFinal substring filter element. 281 */ 282 private static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82; 283 284 285 286 /** 287 * The BER type for the matching rule ID extensible match filter element. 288 */ 289 private static final byte EXTENSIBLE_TYPE_MATCHING_RULE_ID = (byte) 0x81; 290 291 292 293 /** 294 * The BER type for the attribute name extensible match filter element. 295 */ 296 private static final byte EXTENSIBLE_TYPE_ATTRIBUTE_NAME = (byte) 0x82; 297 298 299 300 /** 301 * The BER type for the match value extensible match filter element. 302 */ 303 private static final byte EXTENSIBLE_TYPE_MATCH_VALUE = (byte) 0x83; 304 305 306 307 /** 308 * The BER type for the DN attributes extensible match filter element. 309 */ 310 private static final byte EXTENSIBLE_TYPE_DN_ATTRIBUTES = (byte) 0x84; 311 312 313 314 /** 315 * The set of filters that will be used if there are no subordinate filters. 316 */ 317 private static final Filter[] NO_FILTERS = new Filter[0]; 318 319 320 321 /** 322 * The set of subAny components that will be used if there are no subAny 323 * components. 324 */ 325 private static final ASN1OctetString[] NO_SUB_ANY = new ASN1OctetString[0]; 326 327 328 329 /** 330 * The serial version UID for this serializable class. 331 */ 332 private static final long serialVersionUID = -2734184402804691970L; 333 334 335 336 // The assertion value for this filter. 337 private final ASN1OctetString assertionValue; 338 339 // The subFinal component for this filter. 340 private final ASN1OctetString subFinal; 341 342 // The subInitial component for this filter. 343 private final ASN1OctetString subInitial; 344 345 // The subAny components for this filter. 346 private final ASN1OctetString[] subAny; 347 348 // The dnAttrs element for this filter. 349 private final boolean dnAttributes; 350 351 // The filter component to include in a NOT filter. 352 private final Filter notComp; 353 354 // The set of filter components to include in an AND or OR filter. 355 private final Filter[] filterComps; 356 357 // The filter type for this search filter. 358 private final byte filterType; 359 360 // The attribute name for this filter. 361 private final String attrName; 362 363 // The string representation of this search filter. 364 private volatile String filterString; 365 366 // The matching rule ID for this filter. 367 private final String matchingRuleID; 368 369 // The normalized string representation of this search filter. 370 private volatile String normalizedString; 371 372 373 374 /** 375 * Creates a new filter with the appropriate subset of the provided 376 * information. 377 * 378 * @param filterString The string representation of this search filter. 379 * It may be {@code null} if it is not yet known. 380 * @param filterType The filter type for this filter. 381 * @param filterComps The set of filter components for this filter. 382 * @param notComp The filter component for this NOT filter. 383 * @param attrName The name of the target attribute for this filter. 384 * @param assertionValue Then assertion value for this filter. 385 * @param subInitial The subInitial component for this filter. 386 * @param subAny The set of subAny components for this filter. 387 * @param subFinal The subFinal component for this filter. 388 * @param matchingRuleID The matching rule ID for this filter. 389 * @param dnAttributes The dnAttributes flag. 390 */ 391 private Filter(final String filterString, final byte filterType, 392 final Filter[] filterComps, final Filter notComp, 393 final String attrName, final ASN1OctetString assertionValue, 394 final ASN1OctetString subInitial, 395 final ASN1OctetString[] subAny, final ASN1OctetString subFinal, 396 final String matchingRuleID, final boolean dnAttributes) 397 { 398 this.filterString = filterString; 399 this.filterType = filterType; 400 this.filterComps = filterComps; 401 this.notComp = notComp; 402 this.attrName = attrName; 403 this.assertionValue = assertionValue; 404 this.subInitial = subInitial; 405 this.subAny = subAny; 406 this.subFinal = subFinal; 407 this.matchingRuleID = matchingRuleID; 408 this.dnAttributes = dnAttributes; 409 } 410 411 412 413 /** 414 * Creates a new AND search filter with the provided components. 415 * 416 * @param andComponents The set of filter components to include in the AND 417 * filter. It must not be {@code null}. 418 * 419 * @return The created AND search filter. 420 */ 421 public static Filter createANDFilter(final Filter... andComponents) 422 { 423 ensureNotNull(andComponents); 424 425 return new Filter(null, FILTER_TYPE_AND, andComponents, null, null, null, 426 null, NO_SUB_ANY, null, null, false); 427 } 428 429 430 431 /** 432 * Creates a new AND search filter with the provided components. 433 * 434 * @param andComponents The set of filter components to include in the AND 435 * filter. It must not be {@code null}. 436 * 437 * @return The created AND search filter. 438 */ 439 public static Filter createANDFilter(final List<Filter> andComponents) 440 { 441 ensureNotNull(andComponents); 442 443 return new Filter(null, FILTER_TYPE_AND, 444 andComponents.toArray(new Filter[andComponents.size()]), 445 null, null, null, null, NO_SUB_ANY, null, null, false); 446 } 447 448 449 450 /** 451 * Creates a new AND search filter with the provided components. 452 * 453 * @param andComponents The set of filter components to include in the AND 454 * filter. It must not be {@code null}. 455 * 456 * @return The created AND search filter. 457 */ 458 public static Filter createANDFilter(final Collection<Filter> andComponents) 459 { 460 ensureNotNull(andComponents); 461 462 return new Filter(null, FILTER_TYPE_AND, 463 andComponents.toArray(new Filter[andComponents.size()]), 464 null, null, null, null, NO_SUB_ANY, null, null, false); 465 } 466 467 468 469 /** 470 * Creates a new OR search filter with the provided components. 471 * 472 * @param orComponents The set of filter components to include in the OR 473 * filter. It must not be {@code null}. 474 * 475 * @return The created OR search filter. 476 */ 477 public static Filter createORFilter(final Filter... orComponents) 478 { 479 ensureNotNull(orComponents); 480 481 return new Filter(null, FILTER_TYPE_OR, orComponents, null, null, null, 482 null, NO_SUB_ANY, null, null, false); 483 } 484 485 486 487 /** 488 * Creates a new OR search filter with the provided components. 489 * 490 * @param orComponents The set of filter components to include in the OR 491 * filter. It must not be {@code null}. 492 * 493 * @return The created OR search filter. 494 */ 495 public static Filter createORFilter(final List<Filter> orComponents) 496 { 497 ensureNotNull(orComponents); 498 499 return new Filter(null, FILTER_TYPE_OR, 500 orComponents.toArray(new Filter[orComponents.size()]), 501 null, null, null, null, NO_SUB_ANY, null, null, false); 502 } 503 504 505 506 /** 507 * Creates a new OR search filter with the provided components. 508 * 509 * @param orComponents The set of filter components to include in the OR 510 * filter. It must not be {@code null}. 511 * 512 * @return The created OR search filter. 513 */ 514 public static Filter createORFilter(final Collection<Filter> orComponents) 515 { 516 ensureNotNull(orComponents); 517 518 return new Filter(null, FILTER_TYPE_OR, 519 orComponents.toArray(new Filter[orComponents.size()]), 520 null, null, null, null, NO_SUB_ANY, null, null, false); 521 } 522 523 524 525 /** 526 * Creates a new NOT search filter with the provided component. 527 * 528 * @param notComponent The filter component to include in this NOT filter. 529 * It must not be {@code null}. 530 * 531 * @return The created NOT search filter. 532 */ 533 public static Filter createNOTFilter(final Filter notComponent) 534 { 535 ensureNotNull(notComponent); 536 537 return new Filter(null, FILTER_TYPE_NOT, NO_FILTERS, notComponent, null, 538 null, null, NO_SUB_ANY, null, null, false); 539 } 540 541 542 543 /** 544 * Creates a new equality search filter with the provided information. 545 * 546 * @param attributeName The attribute name for this equality filter. It 547 * must not be {@code null}. 548 * @param assertionValue The assertion value for this equality filter. It 549 * must not be {@code null}. 550 * 551 * @return The created equality search filter. 552 */ 553 public static Filter createEqualityFilter(final String attributeName, 554 final String assertionValue) 555 { 556 ensureNotNull(attributeName, assertionValue); 557 558 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 559 attributeName, new ASN1OctetString(assertionValue), null, 560 NO_SUB_ANY, null, null, false); 561 } 562 563 564 565 /** 566 * Creates a new equality search filter with the provided information. 567 * 568 * @param attributeName The attribute name for this equality filter. It 569 * must not be {@code null}. 570 * @param assertionValue The assertion value for this equality filter. It 571 * must not be {@code null}. 572 * 573 * @return The created equality search filter. 574 */ 575 public static Filter createEqualityFilter(final String attributeName, 576 final byte[] assertionValue) 577 { 578 ensureNotNull(attributeName, assertionValue); 579 580 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 581 attributeName, new ASN1OctetString(assertionValue), null, 582 NO_SUB_ANY, null, null, false); 583 } 584 585 586 587 /** 588 * Creates a new equality search filter with the provided information. 589 * 590 * @param attributeName The attribute name for this equality filter. It 591 * must not be {@code null}. 592 * @param assertionValue The assertion value for this equality filter. It 593 * must not be {@code null}. 594 * 595 * @return The created equality search filter. 596 */ 597 static Filter createEqualityFilter(final String attributeName, 598 final ASN1OctetString assertionValue) 599 { 600 ensureNotNull(attributeName, assertionValue); 601 602 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 603 attributeName, assertionValue, null, NO_SUB_ANY, null, 604 null, false); 605 } 606 607 608 609 /** 610 * Creates a new substring search filter with the provided information. At 611 * least one of the subInitial, subAny, and subFinal components must not be 612 * {@code null}. 613 * 614 * @param attributeName The attribute name for this substring filter. It 615 * must not be {@code null}. 616 * @param subInitial The subInitial component for this substring filter. 617 * @param subAny The set of subAny components for this substring 618 * filter. 619 * @param subFinal The subFinal component for this substring filter. 620 * 621 * @return The created substring search filter. 622 */ 623 public static Filter createSubstringFilter(final String attributeName, 624 final String subInitial, 625 final String[] subAny, 626 final String subFinal) 627 { 628 ensureNotNull(attributeName); 629 ensureTrue((subInitial != null) || 630 ((subAny != null) && (subAny.length > 0)) || 631 (subFinal != null)); 632 633 final ASN1OctetString subInitialOS; 634 if (subInitial == null) 635 { 636 subInitialOS = null; 637 } 638 else 639 { 640 subInitialOS = new ASN1OctetString(subInitial); 641 } 642 643 final ASN1OctetString[] subAnyArray; 644 if (subAny == null) 645 { 646 subAnyArray = NO_SUB_ANY; 647 } 648 else 649 { 650 subAnyArray = new ASN1OctetString[subAny.length]; 651 for (int i=0; i < subAny.length; i++) 652 { 653 subAnyArray[i] = new ASN1OctetString(subAny[i]); 654 } 655 } 656 657 final ASN1OctetString subFinalOS; 658 if (subFinal == null) 659 { 660 subFinalOS = null; 661 } 662 else 663 { 664 subFinalOS = new ASN1OctetString(subFinal); 665 } 666 667 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 668 attributeName, null, subInitialOS, subAnyArray, 669 subFinalOS, null, false); 670 } 671 672 673 674 /** 675 * Creates a new substring search filter with the provided information. At 676 * least one of the subInitial, subAny, and subFinal components must not be 677 * {@code null}. 678 * 679 * @param attributeName The attribute name for this substring filter. It 680 * must not be {@code null}. 681 * @param subInitial The subInitial component for this substring filter. 682 * @param subAny The set of subAny components for this substring 683 * filter. 684 * @param subFinal The subFinal component for this substring filter. 685 * 686 * @return The created substring search filter. 687 */ 688 public static Filter createSubstringFilter(final String attributeName, 689 final byte[] subInitial, 690 final byte[][] subAny, 691 final byte[] subFinal) 692 { 693 ensureNotNull(attributeName); 694 ensureTrue((subInitial != null) || 695 ((subAny != null) && (subAny.length > 0)) || 696 (subFinal != null)); 697 698 final ASN1OctetString subInitialOS; 699 if (subInitial == null) 700 { 701 subInitialOS = null; 702 } 703 else 704 { 705 subInitialOS = new ASN1OctetString(subInitial); 706 } 707 708 final ASN1OctetString[] subAnyArray; 709 if (subAny == null) 710 { 711 subAnyArray = NO_SUB_ANY; 712 } 713 else 714 { 715 subAnyArray = new ASN1OctetString[subAny.length]; 716 for (int i=0; i < subAny.length; i++) 717 { 718 subAnyArray[i] = new ASN1OctetString(subAny[i]); 719 } 720 } 721 722 final ASN1OctetString subFinalOS; 723 if (subFinal == null) 724 { 725 subFinalOS = null; 726 } 727 else 728 { 729 subFinalOS = new ASN1OctetString(subFinal); 730 } 731 732 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 733 attributeName, null, subInitialOS, subAnyArray, 734 subFinalOS, null, false); 735 } 736 737 738 739 /** 740 * Creates a new substring search filter with the provided information. At 741 * least one of the subInitial, subAny, and subFinal components must not be 742 * {@code null}. 743 * 744 * @param attributeName The attribute name for this substring filter. It 745 * must not be {@code null}. 746 * @param subInitial The subInitial component for this substring filter. 747 * @param subAny The set of subAny components for this substring 748 * filter. 749 * @param subFinal The subFinal component for this substring filter. 750 * 751 * @return The created substring search filter. 752 */ 753 static Filter createSubstringFilter(final String attributeName, 754 final ASN1OctetString subInitial, 755 final ASN1OctetString[] subAny, 756 final ASN1OctetString subFinal) 757 { 758 ensureNotNull(attributeName); 759 ensureTrue((subInitial != null) || 760 ((subAny != null) && (subAny.length > 0)) || 761 (subFinal != null)); 762 763 if (subAny == null) 764 { 765 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 766 attributeName, null, subInitial, NO_SUB_ANY, subFinal, 767 null, false); 768 } 769 else 770 { 771 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 772 attributeName, null, subInitial, subAny, subFinal, null, 773 false); 774 } 775 } 776 777 778 779 /** 780 * Creates a new greater-or-equal search filter with the provided information. 781 * 782 * @param attributeName The attribute name for this greater-or-equal 783 * filter. It must not be {@code null}. 784 * @param assertionValue The assertion value for this greater-or-equal 785 * filter. It must not be {@code null}. 786 * 787 * @return The created greater-or-equal search filter. 788 */ 789 public static Filter createGreaterOrEqualFilter(final String attributeName, 790 final String assertionValue) 791 { 792 ensureNotNull(attributeName, assertionValue); 793 794 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 795 attributeName, new ASN1OctetString(assertionValue), null, 796 NO_SUB_ANY, null, null, false); 797 } 798 799 800 801 /** 802 * Creates a new greater-or-equal search filter with the provided information. 803 * 804 * @param attributeName The attribute name for this greater-or-equal 805 * filter. It must not be {@code null}. 806 * @param assertionValue The assertion value for this greater-or-equal 807 * filter. It must not be {@code null}. 808 * 809 * @return The created greater-or-equal search filter. 810 */ 811 public static Filter createGreaterOrEqualFilter(final String attributeName, 812 final byte[] assertionValue) 813 { 814 ensureNotNull(attributeName, assertionValue); 815 816 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 817 attributeName, new ASN1OctetString(assertionValue), null, 818 NO_SUB_ANY, null, null, false); 819 } 820 821 822 823 /** 824 * Creates a new greater-or-equal search filter with the provided information. 825 * 826 * @param attributeName The attribute name for this greater-or-equal 827 * filter. It must not be {@code null}. 828 * @param assertionValue The assertion value for this greater-or-equal 829 * filter. It must not be {@code null}. 830 * 831 * @return The created greater-or-equal search filter. 832 */ 833 static Filter createGreaterOrEqualFilter(final String attributeName, 834 final ASN1OctetString assertionValue) 835 { 836 ensureNotNull(attributeName, assertionValue); 837 838 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 839 attributeName, assertionValue, null, NO_SUB_ANY, null, 840 null, false); 841 } 842 843 844 845 /** 846 * Creates a new less-or-equal search filter with the provided information. 847 * 848 * @param attributeName The attribute name for this less-or-equal 849 * filter. It must not be {@code null}. 850 * @param assertionValue The assertion value for this less-or-equal 851 * filter. It must not be {@code null}. 852 * 853 * @return The created less-or-equal search filter. 854 */ 855 public static Filter createLessOrEqualFilter(final String attributeName, 856 final String assertionValue) 857 { 858 ensureNotNull(attributeName, assertionValue); 859 860 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 861 attributeName, new ASN1OctetString(assertionValue), null, 862 NO_SUB_ANY, null, null, false); 863 } 864 865 866 867 /** 868 * Creates a new less-or-equal search filter with the provided information. 869 * 870 * @param attributeName The attribute name for this less-or-equal 871 * filter. It must not be {@code null}. 872 * @param assertionValue The assertion value for this less-or-equal 873 * filter. It must not be {@code null}. 874 * 875 * @return The created less-or-equal search filter. 876 */ 877 public static Filter createLessOrEqualFilter(final String attributeName, 878 final byte[] assertionValue) 879 { 880 ensureNotNull(attributeName, assertionValue); 881 882 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 883 attributeName, new ASN1OctetString(assertionValue), null, 884 NO_SUB_ANY, null, null, false); 885 } 886 887 888 889 /** 890 * Creates a new less-or-equal search filter with the provided information. 891 * 892 * @param attributeName The attribute name for this less-or-equal 893 * filter. It must not be {@code null}. 894 * @param assertionValue The assertion value for this less-or-equal 895 * filter. It must not be {@code null}. 896 * 897 * @return The created less-or-equal search filter. 898 */ 899 static Filter createLessOrEqualFilter(final String attributeName, 900 final ASN1OctetString assertionValue) 901 { 902 ensureNotNull(attributeName, assertionValue); 903 904 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 905 attributeName, assertionValue, null, NO_SUB_ANY, null, 906 null, false); 907 } 908 909 910 911 /** 912 * Creates a new presence search filter with the provided information. 913 * 914 * @param attributeName The attribute name for this presence filter. It 915 * must not be {@code null}. 916 * 917 * @return The created presence search filter. 918 */ 919 public static Filter createPresenceFilter(final String attributeName) 920 { 921 ensureNotNull(attributeName); 922 923 return new Filter(null, FILTER_TYPE_PRESENCE, NO_FILTERS, null, 924 attributeName, null, null, NO_SUB_ANY, null, null, false); 925 } 926 927 928 929 /** 930 * Creates a new approximate match search filter with the provided 931 * information. 932 * 933 * @param attributeName The attribute name for this approximate match 934 * filter. It must not be {@code null}. 935 * @param assertionValue The assertion value for this approximate match 936 * filter. It must not be {@code null}. 937 * 938 * @return The created approximate match search filter. 939 */ 940 public static Filter createApproximateMatchFilter(final String attributeName, 941 final String assertionValue) 942 { 943 ensureNotNull(attributeName, assertionValue); 944 945 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 946 attributeName, new ASN1OctetString(assertionValue), null, 947 NO_SUB_ANY, null, null, false); 948 } 949 950 951 952 /** 953 * Creates a new approximate match search filter with the provided 954 * information. 955 * 956 * @param attributeName The attribute name for this approximate match 957 * filter. It must not be {@code null}. 958 * @param assertionValue The assertion value for this approximate match 959 * filter. It must not be {@code null}. 960 * 961 * @return The created approximate match search filter. 962 */ 963 public static Filter createApproximateMatchFilter(final String attributeName, 964 final byte[] assertionValue) 965 { 966 ensureNotNull(attributeName, assertionValue); 967 968 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 969 attributeName, new ASN1OctetString(assertionValue), null, 970 NO_SUB_ANY, null, null, false); 971 } 972 973 974 975 /** 976 * Creates a new approximate match search filter with the provided 977 * information. 978 * 979 * @param attributeName The attribute name for this approximate match 980 * filter. It must not be {@code null}. 981 * @param assertionValue The assertion value for this approximate match 982 * filter. It must not be {@code null}. 983 * 984 * @return The created approximate match search filter. 985 */ 986 static Filter createApproximateMatchFilter(final String attributeName, 987 final ASN1OctetString assertionValue) 988 { 989 ensureNotNull(attributeName, assertionValue); 990 991 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 992 attributeName, assertionValue, null, NO_SUB_ANY, null, 993 null, false); 994 } 995 996 997 998 /** 999 * Creates a new extensible match search filter with the provided 1000 * information. At least one of the attribute name and matching rule ID must 1001 * be specified, and the assertion value must always be present. 1002 * 1003 * @param attributeName The attribute name for this extensible match 1004 * filter. 1005 * @param matchingRuleID The matching rule ID for this extensible match 1006 * filter. 1007 * @param dnAttributes Indicates whether the match should be performed 1008 * against attributes in the target entry's DN. 1009 * @param assertionValue The assertion value for this extensible match 1010 * filter. It must not be {@code null}. 1011 * 1012 * @return The created extensible match search filter. 1013 */ 1014 public static Filter createExtensibleMatchFilter(final String attributeName, 1015 final String matchingRuleID, 1016 final boolean dnAttributes, 1017 final String assertionValue) 1018 { 1019 ensureNotNull(assertionValue); 1020 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1021 1022 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1023 attributeName, new ASN1OctetString(assertionValue), null, 1024 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1025 } 1026 1027 1028 1029 /** 1030 * Creates a new extensible match search filter with the provided 1031 * information. At least one of the attribute name and matching rule ID must 1032 * be specified, and the assertion value must always be present. 1033 * 1034 * @param attributeName The attribute name for this extensible match 1035 * filter. 1036 * @param matchingRuleID The matching rule ID for this extensible match 1037 * filter. 1038 * @param dnAttributes Indicates whether the match should be performed 1039 * against attributes in the target entry's DN. 1040 * @param assertionValue The assertion value for this extensible match 1041 * filter. It must not be {@code null}. 1042 * 1043 * @return The created extensible match search filter. 1044 */ 1045 public static Filter createExtensibleMatchFilter(final String attributeName, 1046 final String matchingRuleID, 1047 final boolean dnAttributes, 1048 final byte[] assertionValue) 1049 { 1050 ensureNotNull(assertionValue); 1051 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1052 1053 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1054 attributeName, new ASN1OctetString(assertionValue), null, 1055 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1056 } 1057 1058 1059 1060 /** 1061 * Creates a new extensible match search filter with the provided 1062 * information. At least one of the attribute name and matching rule ID must 1063 * be specified, and the assertion value must always be present. 1064 * 1065 * @param attributeName The attribute name for this extensible match 1066 * filter. 1067 * @param matchingRuleID The matching rule ID for this extensible match 1068 * filter. 1069 * @param dnAttributes Indicates whether the match should be performed 1070 * against attributes in the target entry's DN. 1071 * @param assertionValue The assertion value for this extensible match 1072 * filter. It must not be {@code null}. 1073 * 1074 * @return The created approximate match search filter. 1075 */ 1076 static Filter createExtensibleMatchFilter(final String attributeName, 1077 final String matchingRuleID, final boolean dnAttributes, 1078 final ASN1OctetString assertionValue) 1079 { 1080 ensureNotNull(assertionValue); 1081 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1082 1083 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1084 attributeName, assertionValue, null, NO_SUB_ANY, null, 1085 matchingRuleID, dnAttributes); 1086 } 1087 1088 1089 1090 /** 1091 * Creates a new search filter from the provided string representation. 1092 * 1093 * @param filterString The string representation of the filter to create. 1094 * It must not be {@code null}. 1095 * 1096 * @return The search filter decoded from the provided filter string. 1097 * 1098 * @throws LDAPException If the provided string cannot be decoded as a valid 1099 * LDAP search filter. 1100 */ 1101 public static Filter create(final String filterString) 1102 throws LDAPException 1103 { 1104 ensureNotNull(filterString); 1105 1106 return create(filterString, 0, (filterString.length() - 1), 0); 1107 } 1108 1109 1110 1111 /** 1112 * Creates a new search filter from the specified portion of the provided 1113 * string representation. 1114 * 1115 * @param filterString The string representation of the filter to create. 1116 * @param startPos The position of the first character to consider as 1117 * part of the filter. 1118 * @param endPos The position of the last character to consider as 1119 * part of the filter. 1120 * @param depth The current nesting depth for this filter. It should 1121 * be increased by one for each AND, OR, or NOT filter 1122 * encountered, in order to prevent stack overflow 1123 * errors from excessive recursion. 1124 * 1125 * @return The decoded search filter. 1126 * 1127 * @throws LDAPException If the provided string cannot be decoded as a valid 1128 * LDAP search filter. 1129 */ 1130 private static Filter create(final String filterString, final int startPos, 1131 final int endPos, final int depth) 1132 throws LDAPException 1133 { 1134 if (depth > 100) 1135 { 1136 throw new LDAPException(ResultCode.FILTER_ERROR, 1137 ERR_FILTER_TOO_DEEP.get()); 1138 } 1139 1140 final byte filterType; 1141 final Filter[] filterComps; 1142 final Filter notComp; 1143 final String attrName; 1144 final ASN1OctetString assertionValue; 1145 final ASN1OctetString subInitial; 1146 final ASN1OctetString[] subAny; 1147 final ASN1OctetString subFinal; 1148 final String matchingRuleID; 1149 final boolean dnAttributes; 1150 1151 if (startPos >= endPos) 1152 { 1153 throw new LDAPException(ResultCode.FILTER_ERROR, 1154 ERR_FILTER_TOO_SHORT.get()); 1155 } 1156 1157 int l = startPos; 1158 int r = endPos; 1159 1160 // First, see if the provided filter string is enclosed in parentheses, like 1161 // it should be. If so, then strip off the outer parentheses. 1162 if (filterString.charAt(l) == '(') 1163 { 1164 if (filterString.charAt(r) == ')') 1165 { 1166 l++; 1167 r--; 1168 } 1169 else 1170 { 1171 throw new LDAPException(ResultCode.FILTER_ERROR, 1172 ERR_FILTER_OPEN_WITHOUT_CLOSE.get(l, r)); 1173 } 1174 } 1175 else 1176 { 1177 // This is technically an error, and it's a bad practice. If we're 1178 // working on the complete filter string then we'll let it slide, but 1179 // otherwise we'll raise an error. 1180 if (l != 0) 1181 { 1182 throw new LDAPException(ResultCode.FILTER_ERROR, 1183 ERR_FILTER_MISSING_PARENTHESES.get( 1184 filterString.substring(l, r+1))); 1185 } 1186 } 1187 1188 1189 // Look at the first character of the filter to see if it's an '&', '|', or 1190 // '!'. If we find a parenthesis, then that's an error. 1191 switch (filterString.charAt(l)) 1192 { 1193 case '&': 1194 filterType = FILTER_TYPE_AND; 1195 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1196 notComp = null; 1197 attrName = null; 1198 assertionValue = null; 1199 subInitial = null; 1200 subAny = NO_SUB_ANY; 1201 subFinal = null; 1202 matchingRuleID = null; 1203 dnAttributes = false; 1204 break; 1205 1206 case '|': 1207 filterType = FILTER_TYPE_OR; 1208 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1209 notComp = null; 1210 attrName = null; 1211 assertionValue = null; 1212 subInitial = null; 1213 subAny = NO_SUB_ANY; 1214 subFinal = null; 1215 matchingRuleID = null; 1216 dnAttributes = false; 1217 break; 1218 1219 case '!': 1220 filterType = FILTER_TYPE_NOT; 1221 filterComps = NO_FILTERS; 1222 notComp = create(filterString, l+1, r, depth+1); 1223 attrName = null; 1224 assertionValue = null; 1225 subInitial = null; 1226 subAny = NO_SUB_ANY; 1227 subFinal = null; 1228 matchingRuleID = null; 1229 dnAttributes = false; 1230 break; 1231 1232 case '(': 1233 throw new LDAPException(ResultCode.FILTER_ERROR, 1234 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(l)); 1235 1236 case ':': 1237 // This must be an extensible matching filter that starts with a 1238 // dnAttributes flag and/or matching rule ID, and we should parse it 1239 // accordingly. 1240 filterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1241 filterComps = NO_FILTERS; 1242 notComp = null; 1243 attrName = null; 1244 subInitial = null; 1245 subAny = NO_SUB_ANY; 1246 subFinal = null; 1247 1248 // The next element must be either the "dn:{matchingruleid}" or just 1249 // "{matchingruleid}", and it must be followed by a colon. 1250 final int dnMRIDStart = ++l; 1251 while ((l <= r) && (filterString.charAt(l) != ':')) 1252 { 1253 l++; 1254 } 1255 1256 if (l > r) 1257 { 1258 throw new LDAPException(ResultCode.FILTER_ERROR, 1259 ERR_FILTER_NO_COLON_AFTER_MRID.get( 1260 startPos)); 1261 } 1262 else if (l == dnMRIDStart) 1263 { 1264 throw new LDAPException(ResultCode.FILTER_ERROR, 1265 ERR_FILTER_EMPTY_MRID.get(startPos)); 1266 } 1267 final String s = filterString.substring(dnMRIDStart, l++); 1268 if (s.equalsIgnoreCase("dn")) 1269 { 1270 dnAttributes = true; 1271 1272 // The colon must be followed by the matching rule ID and another 1273 // colon. 1274 final int mrIDStart = l; 1275 while ((l < r) && (filterString.charAt(l) != ':')) 1276 { 1277 l++; 1278 } 1279 1280 if (l >= r) 1281 { 1282 throw new LDAPException(ResultCode.FILTER_ERROR, 1283 ERR_FILTER_NO_COLON_AFTER_MRID.get( 1284 startPos)); 1285 } 1286 1287 matchingRuleID = filterString.substring(mrIDStart, l); 1288 if (matchingRuleID.length() == 0) 1289 { 1290 throw new LDAPException(ResultCode.FILTER_ERROR, 1291 ERR_FILTER_EMPTY_MRID.get(startPos)); 1292 } 1293 1294 if ((++l > r) || (filterString.charAt(l) != '=')) 1295 { 1296 throw new LDAPException(ResultCode.FILTER_ERROR, 1297 ERR_FILTER_UNEXPECTED_CHAR_AFTER_MRID.get( 1298 filterString.charAt(l), startPos)); 1299 } 1300 } 1301 else 1302 { 1303 matchingRuleID = s; 1304 dnAttributes = false; 1305 1306 // The colon must be followed by an equal sign. 1307 if ((l > r) || (filterString.charAt(l) != '=')) 1308 { 1309 throw new LDAPException(ResultCode.FILTER_ERROR, 1310 ERR_FILTER_NO_EQUAL_AFTER_MRID.get( 1311 startPos)); 1312 } 1313 } 1314 1315 // Now we should be able to read the value, handling any escape 1316 // characters as we go. 1317 l++; 1318 final ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1); 1319 while (l <= r) 1320 { 1321 final char c = filterString.charAt(l); 1322 if (c == '\\') 1323 { 1324 l = readEscapedHexString(filterString, ++l, valueBuffer); 1325 } 1326 else if (c == '(') 1327 { 1328 throw new LDAPException(ResultCode.FILTER_ERROR, 1329 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(l)); 1330 } 1331 else if (c == ')') 1332 { 1333 throw new LDAPException(ResultCode.FILTER_ERROR, 1334 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(l)); 1335 } 1336 else 1337 { 1338 valueBuffer.append(c); 1339 l++; 1340 } 1341 } 1342 assertionValue = new ASN1OctetString(valueBuffer.toByteArray()); 1343 break; 1344 1345 1346 default: 1347 // We know that it's not an AND, OR, or NOT filter, so we can eliminate 1348 // the variables used only for them. 1349 filterComps = NO_FILTERS; 1350 notComp = null; 1351 1352 1353 // We should now be able to read a non-empty attribute name. 1354 final int attrStartPos = l; 1355 int attrEndPos = -1; 1356 byte tempFilterType = 0x00; 1357 boolean filterTypeKnown = false; 1358 boolean equalFound = false; 1359 attrNameLoop: 1360 while (l <= r) 1361 { 1362 final char c = filterString.charAt(l++); 1363 switch (c) 1364 { 1365 case ':': 1366 tempFilterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1367 filterTypeKnown = true; 1368 attrEndPos = l - 1; 1369 break attrNameLoop; 1370 1371 case '>': 1372 tempFilterType = FILTER_TYPE_GREATER_OR_EQUAL; 1373 filterTypeKnown = true; 1374 attrEndPos = l - 1; 1375 1376 if (l <= r) 1377 { 1378 if (filterString.charAt(l++) != '=') 1379 { 1380 throw new LDAPException(ResultCode.FILTER_ERROR, 1381 ERR_FILTER_UNEXPECTED_CHAR_AFTER_GT.get( 1382 startPos, filterString.charAt(l-1))); 1383 } 1384 } 1385 else 1386 { 1387 throw new LDAPException(ResultCode.FILTER_ERROR, 1388 ERR_FILTER_END_AFTER_GT.get(startPos)); 1389 } 1390 break attrNameLoop; 1391 1392 case '<': 1393 tempFilterType = FILTER_TYPE_LESS_OR_EQUAL; 1394 filterTypeKnown = true; 1395 attrEndPos = l - 1; 1396 1397 if (l <= r) 1398 { 1399 if (filterString.charAt(l++) != '=') 1400 { 1401 throw new LDAPException(ResultCode.FILTER_ERROR, 1402 ERR_FILTER_UNEXPECTED_CHAR_AFTER_LT.get( 1403 startPos, filterString.charAt(l-1))); 1404 } 1405 } 1406 else 1407 { 1408 throw new LDAPException(ResultCode.FILTER_ERROR, 1409 ERR_FILTER_END_AFTER_LT.get(startPos)); 1410 } 1411 break attrNameLoop; 1412 1413 case '~': 1414 tempFilterType = FILTER_TYPE_APPROXIMATE_MATCH; 1415 filterTypeKnown = true; 1416 attrEndPos = l - 1; 1417 1418 if (l <= r) 1419 { 1420 if (filterString.charAt(l++) != '=') 1421 { 1422 throw new LDAPException(ResultCode.FILTER_ERROR, 1423 ERR_FILTER_UNEXPECTED_CHAR_AFTER_TILDE.get( 1424 startPos, filterString.charAt(l-1))); 1425 } 1426 } 1427 else 1428 { 1429 throw new LDAPException(ResultCode.FILTER_ERROR, 1430 ERR_FILTER_END_AFTER_TILDE.get( 1431 startPos)); 1432 } 1433 break attrNameLoop; 1434 1435 case '=': 1436 // It could be either an equality, presence, or substring filter. 1437 // We'll need to look at the value to determine that. 1438 attrEndPos = l - 1; 1439 equalFound = true; 1440 break attrNameLoop; 1441 } 1442 } 1443 1444 if (attrEndPos <= attrStartPos) 1445 { 1446 if (equalFound) 1447 { 1448 throw new LDAPException(ResultCode.FILTER_ERROR, 1449 ERR_FILTER_EMPTY_ATTR_NAME.get(startPos)); 1450 } 1451 else 1452 { 1453 throw new LDAPException(ResultCode.FILTER_ERROR, 1454 ERR_FILTER_NO_EQUAL_SIGN.get(startPos)); 1455 } 1456 } 1457 attrName = filterString.substring(attrStartPos, attrEndPos); 1458 1459 1460 // See if we're dealing with an extensible match filter. If so, then 1461 // we may still need to do additional parsing to get the matching rule 1462 // ID and/or the dnAttributes flag. Otherwise, we can rule out any 1463 // variables that are specific to extensible matching filters. 1464 if (filterTypeKnown && (tempFilterType == FILTER_TYPE_EXTENSIBLE_MATCH)) 1465 { 1466 if (l > r) 1467 { 1468 throw new LDAPException(ResultCode.FILTER_ERROR, 1469 ERR_FILTER_NO_EQUALS.get(startPos)); 1470 } 1471 1472 final char c = filterString.charAt(l++); 1473 if (c == '=') 1474 { 1475 matchingRuleID = null; 1476 dnAttributes = false; 1477 } 1478 else 1479 { 1480 // We have either a matching rule ID or a dnAttributes flag, or 1481 // both. Iterate through the filter until we find the equal sign, 1482 // and then figure out what we have from that. 1483 equalFound = false; 1484 final int substrStartPos = l - 1; 1485 while (l <= r) 1486 { 1487 if (filterString.charAt(l++) == '=') 1488 { 1489 equalFound = true; 1490 break; 1491 } 1492 } 1493 1494 if (! equalFound) 1495 { 1496 throw new LDAPException(ResultCode.FILTER_ERROR, 1497 ERR_FILTER_NO_EQUALS.get(startPos)); 1498 } 1499 1500 final String substr = filterString.substring(substrStartPos, l-1); 1501 final String lowerSubstr = toLowerCase(substr); 1502 if (! substr.endsWith(":")) 1503 { 1504 throw new LDAPException(ResultCode.FILTER_ERROR, 1505 ERR_FILTER_CANNOT_PARSE_MRID.get( 1506 startPos)); 1507 } 1508 1509 if (lowerSubstr.equals("dn:")) 1510 { 1511 matchingRuleID = null; 1512 dnAttributes = true; 1513 } 1514 else if (lowerSubstr.startsWith("dn:")) 1515 { 1516 matchingRuleID = substr.substring(3, substr.length() - 1); 1517 if (matchingRuleID.length() == 0) 1518 { 1519 throw new LDAPException(ResultCode.FILTER_ERROR, 1520 ERR_FILTER_EMPTY_MRID.get(startPos)); 1521 } 1522 1523 dnAttributes = true; 1524 } 1525 else 1526 { 1527 matchingRuleID = substr.substring(0, substr.length() - 1); 1528 dnAttributes = false; 1529 1530 if (matchingRuleID.length() == 0) 1531 { 1532 throw new LDAPException(ResultCode.FILTER_ERROR, 1533 ERR_FILTER_EMPTY_MRID.get(startPos)); 1534 } 1535 } 1536 } 1537 } 1538 else 1539 { 1540 matchingRuleID = null; 1541 dnAttributes = false; 1542 } 1543 1544 1545 // At this point, we're ready to read the value. If we still don't 1546 // know what type of filter we're dealing with, then we can tell that 1547 // based on asterisks in the value. 1548 if (l > r) 1549 { 1550 assertionValue = new ASN1OctetString(); 1551 if (! filterTypeKnown) 1552 { 1553 tempFilterType = FILTER_TYPE_EQUALITY; 1554 } 1555 1556 subInitial = null; 1557 subAny = NO_SUB_ANY; 1558 subFinal = null; 1559 } 1560 else if (l == r) 1561 { 1562 if (filterTypeKnown) 1563 { 1564 switch (filterString.charAt(l)) 1565 { 1566 case '*': 1567 case '(': 1568 case ')': 1569 case '\\': 1570 throw new LDAPException(ResultCode.FILTER_ERROR, 1571 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get( 1572 filterString.charAt(l), startPos)); 1573 } 1574 1575 assertionValue = 1576 new ASN1OctetString(filterString.substring(l, l+1)); 1577 } 1578 else 1579 { 1580 final char c = filterString.charAt(l); 1581 switch (c) 1582 { 1583 case '*': 1584 tempFilterType = FILTER_TYPE_PRESENCE; 1585 assertionValue = null; 1586 break; 1587 1588 case '\\': 1589 case '(': 1590 case ')': 1591 throw new LDAPException(ResultCode.FILTER_ERROR, 1592 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get( 1593 filterString.charAt(l), startPos)); 1594 1595 default: 1596 tempFilterType = FILTER_TYPE_EQUALITY; 1597 assertionValue = 1598 new ASN1OctetString(filterString.substring(l, l+1)); 1599 break; 1600 } 1601 } 1602 1603 subInitial = null; 1604 subAny = NO_SUB_ANY; 1605 subFinal = null; 1606 } 1607 else 1608 { 1609 if (! filterTypeKnown) 1610 { 1611 tempFilterType = FILTER_TYPE_EQUALITY; 1612 } 1613 1614 final int valueStartPos = l; 1615 ASN1OctetString tempSubInitial = null; 1616 ASN1OctetString tempSubFinal = null; 1617 final ArrayList<ASN1OctetString> subAnyList = 1618 new ArrayList<ASN1OctetString>(1); 1619 ByteStringBuffer buffer = new ByteStringBuffer(r - l + 1); 1620 while (l <= r) 1621 { 1622 final char c = filterString.charAt(l++); 1623 switch (c) 1624 { 1625 case '*': 1626 if (filterTypeKnown) 1627 { 1628 throw new LDAPException(ResultCode.FILTER_ERROR, 1629 ERR_FILTER_UNEXPECTED_ASTERISK.get( 1630 startPos)); 1631 } 1632 else 1633 { 1634 if ((l-1) == valueStartPos) 1635 { 1636 // The first character is an asterisk, so there is no 1637 // subInitial. 1638 } 1639 else 1640 { 1641 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1642 { 1643 // We already know that it's a substring filter, so this 1644 // must be a subAny portion. However, if the buffer is 1645 // empty, then that means that there were two asterisks 1646 // right next to each other, which is invalid. 1647 if (buffer.length() == 0) 1648 { 1649 throw new LDAPException(ResultCode.FILTER_ERROR, 1650 ERR_FILTER_UNEXPECTED_DOUBLE_ASTERISK.get( 1651 startPos)); 1652 } 1653 else 1654 { 1655 subAnyList.add( 1656 new ASN1OctetString(buffer.toByteArray())); 1657 buffer = new ByteStringBuffer(r - l + 1); 1658 } 1659 } 1660 else 1661 { 1662 // We haven't yet set the filter type, so the buffer must 1663 // contain the subInitial portion. We also know it's not 1664 // empty because of an earlier check. 1665 tempSubInitial = 1666 new ASN1OctetString(buffer.toByteArray()); 1667 buffer = new ByteStringBuffer(r - l + 1); 1668 } 1669 } 1670 1671 tempFilterType = FILTER_TYPE_SUBSTRING; 1672 } 1673 break; 1674 1675 case '\\': 1676 l = readEscapedHexString(filterString, l, buffer); 1677 break; 1678 1679 case '(': 1680 throw new LDAPException(ResultCode.FILTER_ERROR, 1681 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get( 1682 l)); 1683 1684 case ')': 1685 throw new LDAPException(ResultCode.FILTER_ERROR, 1686 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get( 1687 l)); 1688 1689 default: 1690 buffer.append(c); 1691 break; 1692 } 1693 } 1694 1695 if ((tempFilterType == FILTER_TYPE_SUBSTRING) && 1696 (buffer.length() > 0)) 1697 { 1698 // The buffer must contain the subFinal portion. 1699 tempSubFinal = new ASN1OctetString(buffer.toByteArray()); 1700 } 1701 1702 subInitial = tempSubInitial; 1703 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 1704 subFinal = tempSubFinal; 1705 1706 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1707 { 1708 assertionValue = null; 1709 } 1710 else 1711 { 1712 assertionValue = new ASN1OctetString(buffer.toByteArray()); 1713 } 1714 } 1715 1716 filterType = tempFilterType; 1717 break; 1718 } 1719 1720 1721 if (startPos == 0) 1722 { 1723 return new Filter(filterString, filterType, filterComps, notComp, 1724 attrName, assertionValue, subInitial, subAny, subFinal, 1725 matchingRuleID, dnAttributes); 1726 } 1727 else 1728 { 1729 return new Filter(filterString.substring(startPos, endPos+1), filterType, 1730 filterComps, notComp, attrName, assertionValue, 1731 subInitial, subAny, subFinal, matchingRuleID, 1732 dnAttributes); 1733 } 1734 } 1735 1736 1737 1738 /** 1739 * Parses the specified portion of the provided filter string to obtain a set 1740 * of filter components for use in an AND or OR filter. 1741 * 1742 * @param filterString The string representation for the set of filters. 1743 * @param startPos The position of the first character to consider as 1744 * part of the first filter. 1745 * @param endPos The position of the last character to consider as 1746 * part of the last filter. 1747 * @param depth The current nesting depth for this filter. It should 1748 * be increased by one for each AND, OR, or NOT filter 1749 * encountered, in order to prevent stack overflow 1750 * errors from excessive recursion. 1751 * 1752 * @return The decoded set of search filters. 1753 * 1754 * @throws LDAPException If the provided string cannot be decoded as a set 1755 * of LDAP search filters. 1756 */ 1757 private static Filter[] parseFilterComps(final String filterString, 1758 final int startPos, final int endPos, 1759 final int depth) 1760 throws LDAPException 1761 { 1762 if (startPos > endPos) 1763 { 1764 // This is acceptable, since it can represent an LDAP TRUE or FALSE filter 1765 // as described in RFC 4526. 1766 return NO_FILTERS; 1767 } 1768 1769 1770 // The set of filters must start with an opening parenthesis, and end with a 1771 // closing parenthesis. 1772 if (filterString.charAt(startPos) != '(') 1773 { 1774 throw new LDAPException(ResultCode.FILTER_ERROR, 1775 ERR_FILTER_EXPECTED_OPEN_PAREN.get(startPos)); 1776 } 1777 if (filterString.charAt(endPos) != ')') 1778 { 1779 throw new LDAPException(ResultCode.FILTER_ERROR, 1780 ERR_FILTER_EXPECTED_CLOSE_PAREN.get(startPos)); 1781 } 1782 1783 1784 // Iterate through the specified portion of the filter string and count 1785 // opening and closing parentheses to figure out where one filter ends and 1786 // another begins. 1787 final ArrayList<Filter> filterList = new ArrayList<Filter>(5); 1788 int filterStartPos = startPos; 1789 int pos = startPos; 1790 int numOpen = 0; 1791 while (pos <= endPos) 1792 { 1793 final char c = filterString.charAt(pos++); 1794 if (c == '(') 1795 { 1796 numOpen++; 1797 } 1798 else if (c == ')') 1799 { 1800 numOpen--; 1801 if (numOpen == 0) 1802 { 1803 filterList.add(create(filterString, filterStartPos, pos-1, depth)); 1804 filterStartPos = pos; 1805 } 1806 } 1807 } 1808 1809 if (numOpen != 0) 1810 { 1811 throw new LDAPException(ResultCode.FILTER_ERROR, 1812 ERR_FILTER_MISMATCHED_PARENS.get(startPos, 1813 endPos)); 1814 } 1815 1816 return filterList.toArray(new Filter[filterList.size()]); 1817 } 1818 1819 1820 1821 /** 1822 * Reads one or more hex-encoded bytes from the specified portion of the 1823 * filter string. 1824 * 1825 * @param filterString The string from which the data is to be read. 1826 * @param startPos The position at which to start reading. This should 1827 * be the position of first hex character immediately 1828 * after the initial backslash. 1829 * @param buffer The buffer to which the decoded string portion should 1830 * be appended. 1831 * 1832 * @return The position at which the caller may resume parsing. 1833 * 1834 * @throws LDAPException If a problem occurs while reading hex-encoded 1835 * bytes. 1836 */ 1837 private static int readEscapedHexString(final String filterString, 1838 final int startPos, 1839 final ByteStringBuffer buffer) 1840 throws LDAPException 1841 { 1842 byte b; 1843 switch (filterString.charAt(startPos)) 1844 { 1845 case '0': 1846 b = 0x00; 1847 break; 1848 case '1': 1849 b = 0x10; 1850 break; 1851 case '2': 1852 b = 0x20; 1853 break; 1854 case '3': 1855 b = 0x30; 1856 break; 1857 case '4': 1858 b = 0x40; 1859 break; 1860 case '5': 1861 b = 0x50; 1862 break; 1863 case '6': 1864 b = 0x60; 1865 break; 1866 case '7': 1867 b = 0x70; 1868 break; 1869 case '8': 1870 b = (byte) 0x80; 1871 break; 1872 case '9': 1873 b = (byte) 0x90; 1874 break; 1875 case 'a': 1876 case 'A': 1877 b = (byte) 0xA0; 1878 break; 1879 case 'b': 1880 case 'B': 1881 b = (byte) 0xB0; 1882 break; 1883 case 'c': 1884 case 'C': 1885 b = (byte) 0xC0; 1886 break; 1887 case 'd': 1888 case 'D': 1889 b = (byte) 0xD0; 1890 break; 1891 case 'e': 1892 case 'E': 1893 b = (byte) 0xE0; 1894 break; 1895 case 'f': 1896 case 'F': 1897 b = (byte) 0xF0; 1898 break; 1899 default: 1900 throw new LDAPException(ResultCode.FILTER_ERROR, 1901 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos), 1902 startPos)); 1903 } 1904 1905 switch (filterString.charAt(startPos+1)) 1906 { 1907 case '0': 1908 // No action is required. 1909 break; 1910 case '1': 1911 b |= 0x01; 1912 break; 1913 case '2': 1914 b |= 0x02; 1915 break; 1916 case '3': 1917 b |= 0x03; 1918 break; 1919 case '4': 1920 b |= 0x04; 1921 break; 1922 case '5': 1923 b |= 0x05; 1924 break; 1925 case '6': 1926 b |= 0x06; 1927 break; 1928 case '7': 1929 b |= 0x07; 1930 break; 1931 case '8': 1932 b |= 0x08; 1933 break; 1934 case '9': 1935 b |= 0x09; 1936 break; 1937 case 'a': 1938 case 'A': 1939 b |= 0x0A; 1940 break; 1941 case 'b': 1942 case 'B': 1943 b |= 0x0B; 1944 break; 1945 case 'c': 1946 case 'C': 1947 b |= 0x0C; 1948 break; 1949 case 'd': 1950 case 'D': 1951 b |= 0x0D; 1952 break; 1953 case 'e': 1954 case 'E': 1955 b |= 0x0E; 1956 break; 1957 case 'f': 1958 case 'F': 1959 b |= 0x0F; 1960 break; 1961 default: 1962 throw new LDAPException(ResultCode.FILTER_ERROR, 1963 ERR_FILTER_INVALID_HEX_CHAR.get(filterString.charAt(startPos+1), 1964 (startPos+1))); 1965 } 1966 1967 buffer.append(b); 1968 return startPos+2; 1969 } 1970 1971 1972 1973 /** 1974 * Writes an ASN.1-encoded representation of this filter to the provided ASN.1 1975 * buffer. 1976 * 1977 * @param buffer The ASN.1 buffer to which the encoded representation should 1978 * be written. 1979 */ 1980 public void writeTo(final ASN1Buffer buffer) 1981 { 1982 switch (filterType) 1983 { 1984 case FILTER_TYPE_AND: 1985 case FILTER_TYPE_OR: 1986 final ASN1BufferSet compSet = buffer.beginSet(filterType); 1987 for (final Filter f : filterComps) 1988 { 1989 f.writeTo(buffer); 1990 } 1991 compSet.end(); 1992 break; 1993 1994 case FILTER_TYPE_NOT: 1995 buffer.addElement( 1996 new ASN1Element(filterType, notComp.encode().encode())); 1997 break; 1998 1999 case FILTER_TYPE_EQUALITY: 2000 case FILTER_TYPE_GREATER_OR_EQUAL: 2001 case FILTER_TYPE_LESS_OR_EQUAL: 2002 case FILTER_TYPE_APPROXIMATE_MATCH: 2003 final ASN1BufferSequence avaSequence = buffer.beginSequence(filterType); 2004 buffer.addOctetString(attrName); 2005 buffer.addElement(assertionValue); 2006 avaSequence.end(); 2007 break; 2008 2009 case FILTER_TYPE_SUBSTRING: 2010 final ASN1BufferSequence subFilterSequence = 2011 buffer.beginSequence(filterType); 2012 buffer.addOctetString(attrName); 2013 2014 final ASN1BufferSequence valueSequence = buffer.beginSequence(); 2015 if (subInitial != null) 2016 { 2017 buffer.addOctetString(SUBSTRING_TYPE_SUBINITIAL, 2018 subInitial.getValue()); 2019 } 2020 2021 for (final ASN1OctetString s : subAny) 2022 { 2023 buffer.addOctetString(SUBSTRING_TYPE_SUBANY, s.getValue()); 2024 } 2025 2026 if (subFinal != null) 2027 { 2028 buffer.addOctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue()); 2029 } 2030 valueSequence.end(); 2031 subFilterSequence.end(); 2032 break; 2033 2034 case FILTER_TYPE_PRESENCE: 2035 buffer.addOctetString(filterType, attrName); 2036 break; 2037 2038 case FILTER_TYPE_EXTENSIBLE_MATCH: 2039 final ASN1BufferSequence mrSequence = buffer.beginSequence(filterType); 2040 if (matchingRuleID != null) 2041 { 2042 buffer.addOctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID, 2043 matchingRuleID); 2044 } 2045 2046 if (attrName != null) 2047 { 2048 buffer.addOctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName); 2049 } 2050 2051 buffer.addOctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2052 assertionValue.getValue()); 2053 2054 if (dnAttributes) 2055 { 2056 buffer.addBoolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true); 2057 } 2058 mrSequence.end(); 2059 break; 2060 } 2061 } 2062 2063 2064 2065 /** 2066 * Encodes this search filter to an ASN.1 element suitable for inclusion in an 2067 * LDAP search request protocol op. 2068 * 2069 * @return An ASN.1 element containing the encoded search filter. 2070 */ 2071 public ASN1Element encode() 2072 { 2073 switch (filterType) 2074 { 2075 case FILTER_TYPE_AND: 2076 case FILTER_TYPE_OR: 2077 final ASN1Element[] filterElements = 2078 new ASN1Element[filterComps.length]; 2079 for (int i=0; i < filterComps.length; i++) 2080 { 2081 filterElements[i] = filterComps[i].encode(); 2082 } 2083 return new ASN1Set(filterType, filterElements); 2084 2085 2086 case FILTER_TYPE_NOT: 2087 return new ASN1Element(filterType, notComp.encode().encode()); 2088 2089 2090 case FILTER_TYPE_EQUALITY: 2091 case FILTER_TYPE_GREATER_OR_EQUAL: 2092 case FILTER_TYPE_LESS_OR_EQUAL: 2093 case FILTER_TYPE_APPROXIMATE_MATCH: 2094 final ASN1OctetString[] attrValueAssertionElements = 2095 { 2096 new ASN1OctetString(attrName), 2097 assertionValue 2098 }; 2099 return new ASN1Sequence(filterType, attrValueAssertionElements); 2100 2101 2102 case FILTER_TYPE_SUBSTRING: 2103 final ArrayList<ASN1OctetString> subList = 2104 new ArrayList<ASN1OctetString>(2 + subAny.length); 2105 if (subInitial != null) 2106 { 2107 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, 2108 subInitial.getValue())); 2109 } 2110 2111 for (final ASN1Element subAnyElement : subAny) 2112 { 2113 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY, 2114 subAnyElement.getValue())); 2115 } 2116 2117 2118 if (subFinal != null) 2119 { 2120 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, 2121 subFinal.getValue())); 2122 } 2123 2124 final ASN1Element[] subFilterElements = 2125 { 2126 new ASN1OctetString(attrName), 2127 new ASN1Sequence(subList) 2128 }; 2129 return new ASN1Sequence(filterType, subFilterElements); 2130 2131 2132 case FILTER_TYPE_PRESENCE: 2133 return new ASN1OctetString(filterType, attrName); 2134 2135 2136 case FILTER_TYPE_EXTENSIBLE_MATCH: 2137 final ArrayList<ASN1Element> emElementList = 2138 new ArrayList<ASN1Element>(4); 2139 if (matchingRuleID != null) 2140 { 2141 emElementList.add(new ASN1OctetString( 2142 EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID)); 2143 } 2144 2145 if (attrName != null) 2146 { 2147 emElementList.add(new ASN1OctetString( 2148 EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName)); 2149 } 2150 2151 emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2152 assertionValue.getValue())); 2153 2154 if (dnAttributes) 2155 { 2156 emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, 2157 true)); 2158 } 2159 2160 return new ASN1Sequence(filterType, emElementList); 2161 2162 2163 default: 2164 throw new AssertionError(ERR_FILTER_INVALID_TYPE.get( 2165 toHex(filterType))); 2166 } 2167 } 2168 2169 2170 2171 /** 2172 * Reads and decodes a search filter from the provided ASN.1 stream reader. 2173 * 2174 * @param reader The ASN.1 stream reader from which to read the filter. 2175 * 2176 * @return The decoded search filter. 2177 * 2178 * @throws LDAPException If an error occurs while reading or parsing the 2179 * search filter. 2180 */ 2181 public static Filter readFrom(final ASN1StreamReader reader) 2182 throws LDAPException 2183 { 2184 try 2185 { 2186 final Filter[] filterComps; 2187 final Filter notComp; 2188 final String attrName; 2189 final ASN1OctetString assertionValue; 2190 final ASN1OctetString subInitial; 2191 final ASN1OctetString[] subAny; 2192 final ASN1OctetString subFinal; 2193 final String matchingRuleID; 2194 final boolean dnAttributes; 2195 2196 final byte filterType = (byte) reader.peek(); 2197 2198 switch (filterType) 2199 { 2200 case FILTER_TYPE_AND: 2201 case FILTER_TYPE_OR: 2202 final ArrayList<Filter> comps = new ArrayList<Filter>(5); 2203 final ASN1StreamReaderSet elementSet = reader.beginSet(); 2204 while (elementSet.hasMoreElements()) 2205 { 2206 comps.add(readFrom(reader)); 2207 } 2208 2209 filterComps = new Filter[comps.size()]; 2210 comps.toArray(filterComps); 2211 2212 notComp = null; 2213 attrName = null; 2214 assertionValue = null; 2215 subInitial = null; 2216 subAny = NO_SUB_ANY; 2217 subFinal = null; 2218 matchingRuleID = null; 2219 dnAttributes = false; 2220 break; 2221 2222 2223 case FILTER_TYPE_NOT: 2224 final ASN1Element notFilterElement; 2225 try 2226 { 2227 final ASN1Element e = reader.readElement(); 2228 notFilterElement = ASN1Element.decode(e.getValue()); 2229 } 2230 catch (final ASN1Exception ae) 2231 { 2232 debugException(ae); 2233 throw new LDAPException(ResultCode.DECODING_ERROR, 2234 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)), 2235 ae); 2236 } 2237 notComp = decode(notFilterElement); 2238 2239 filterComps = NO_FILTERS; 2240 attrName = null; 2241 assertionValue = null; 2242 subInitial = null; 2243 subAny = NO_SUB_ANY; 2244 subFinal = null; 2245 matchingRuleID = null; 2246 dnAttributes = false; 2247 break; 2248 2249 2250 case FILTER_TYPE_EQUALITY: 2251 case FILTER_TYPE_GREATER_OR_EQUAL: 2252 case FILTER_TYPE_LESS_OR_EQUAL: 2253 case FILTER_TYPE_APPROXIMATE_MATCH: 2254 reader.beginSequence(); 2255 attrName = reader.readString(); 2256 assertionValue = new ASN1OctetString(reader.readBytes()); 2257 2258 filterComps = NO_FILTERS; 2259 notComp = null; 2260 subInitial = null; 2261 subAny = NO_SUB_ANY; 2262 subFinal = null; 2263 matchingRuleID = null; 2264 dnAttributes = false; 2265 break; 2266 2267 2268 case FILTER_TYPE_SUBSTRING: 2269 reader.beginSequence(); 2270 attrName = reader.readString(); 2271 2272 ASN1OctetString tempSubInitial = null; 2273 ASN1OctetString tempSubFinal = null; 2274 final ArrayList<ASN1OctetString> subAnyList = 2275 new ArrayList<ASN1OctetString>(1); 2276 final ASN1StreamReaderSequence subSequence = reader.beginSequence(); 2277 while (subSequence.hasMoreElements()) 2278 { 2279 final byte type = (byte) reader.peek(); 2280 final ASN1OctetString s = 2281 new ASN1OctetString(type, reader.readBytes()); 2282 switch (type) 2283 { 2284 case SUBSTRING_TYPE_SUBINITIAL: 2285 tempSubInitial = s; 2286 break; 2287 case SUBSTRING_TYPE_SUBANY: 2288 subAnyList.add(s); 2289 break; 2290 case SUBSTRING_TYPE_SUBFINAL: 2291 tempSubFinal = s; 2292 break; 2293 default: 2294 throw new LDAPException(ResultCode.DECODING_ERROR, 2295 ERR_FILTER_INVALID_SUBSTR_TYPE.get(toHex(type))); 2296 } 2297 } 2298 2299 subInitial = tempSubInitial; 2300 subFinal = tempSubFinal; 2301 2302 subAny = new ASN1OctetString[subAnyList.size()]; 2303 subAnyList.toArray(subAny); 2304 2305 filterComps = NO_FILTERS; 2306 notComp = null; 2307 assertionValue = null; 2308 matchingRuleID = null; 2309 dnAttributes = false; 2310 break; 2311 2312 2313 case FILTER_TYPE_PRESENCE: 2314 attrName = reader.readString(); 2315 2316 filterComps = NO_FILTERS; 2317 notComp = null; 2318 assertionValue = null; 2319 subInitial = null; 2320 subAny = NO_SUB_ANY; 2321 subFinal = null; 2322 matchingRuleID = null; 2323 dnAttributes = false; 2324 break; 2325 2326 2327 case FILTER_TYPE_EXTENSIBLE_MATCH: 2328 String tempAttrName = null; 2329 ASN1OctetString tempAssertionValue = null; 2330 String tempMatchingRuleID = null; 2331 boolean tempDNAttributes = false; 2332 2333 final ASN1StreamReaderSequence emSequence = reader.beginSequence(); 2334 while (emSequence.hasMoreElements()) 2335 { 2336 final byte type = (byte) reader.peek(); 2337 switch (type) 2338 { 2339 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2340 tempAttrName = reader.readString(); 2341 break; 2342 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2343 tempMatchingRuleID = reader.readString(); 2344 break; 2345 case EXTENSIBLE_TYPE_MATCH_VALUE: 2346 tempAssertionValue = 2347 new ASN1OctetString(type, reader.readBytes()); 2348 break; 2349 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2350 tempDNAttributes = reader.readBoolean(); 2351 break; 2352 default: 2353 throw new LDAPException(ResultCode.DECODING_ERROR, 2354 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(toHex(type))); 2355 } 2356 } 2357 2358 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2359 { 2360 throw new LDAPException(ResultCode.DECODING_ERROR, 2361 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2362 } 2363 2364 if (tempAssertionValue == null) 2365 { 2366 throw new LDAPException(ResultCode.DECODING_ERROR, 2367 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2368 } 2369 2370 attrName = tempAttrName; 2371 assertionValue = tempAssertionValue; 2372 matchingRuleID = tempMatchingRuleID; 2373 dnAttributes = tempDNAttributes; 2374 2375 filterComps = NO_FILTERS; 2376 notComp = null; 2377 subInitial = null; 2378 subAny = NO_SUB_ANY; 2379 subFinal = null; 2380 break; 2381 2382 2383 default: 2384 throw new LDAPException(ResultCode.DECODING_ERROR, 2385 ERR_FILTER_ELEMENT_INVALID_TYPE.get(toHex(filterType))); 2386 } 2387 2388 return new Filter(null, filterType, filterComps, notComp, attrName, 2389 assertionValue, subInitial, subAny, subFinal, 2390 matchingRuleID, dnAttributes); 2391 } 2392 catch (LDAPException le) 2393 { 2394 debugException(le); 2395 throw le; 2396 } 2397 catch (Exception e) 2398 { 2399 debugException(e); 2400 throw new LDAPException(ResultCode.DECODING_ERROR, 2401 ERR_FILTER_CANNOT_DECODE.get(getExceptionMessage(e)), e); 2402 } 2403 } 2404 2405 2406 2407 /** 2408 * Decodes the provided ASN.1 element as a search filter. 2409 * 2410 * @param filterElement The ASN.1 element containing the encoded search 2411 * filter. 2412 * 2413 * @return The decoded search filter. 2414 * 2415 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 2416 * a search filter. 2417 */ 2418 public static Filter decode(final ASN1Element filterElement) 2419 throws LDAPException 2420 { 2421 final byte filterType = filterElement.getType(); 2422 final Filter[] filterComps; 2423 final Filter notComp; 2424 final String attrName; 2425 final ASN1OctetString assertionValue; 2426 final ASN1OctetString subInitial; 2427 final ASN1OctetString[] subAny; 2428 final ASN1OctetString subFinal; 2429 final String matchingRuleID; 2430 final boolean dnAttributes; 2431 2432 switch (filterType) 2433 { 2434 case FILTER_TYPE_AND: 2435 case FILTER_TYPE_OR: 2436 notComp = null; 2437 attrName = null; 2438 assertionValue = null; 2439 subInitial = null; 2440 subAny = NO_SUB_ANY; 2441 subFinal = null; 2442 matchingRuleID = null; 2443 dnAttributes = false; 2444 2445 final ASN1Set compSet; 2446 try 2447 { 2448 compSet = ASN1Set.decodeAsSet(filterElement); 2449 } 2450 catch (final ASN1Exception ae) 2451 { 2452 debugException(ae); 2453 throw new LDAPException(ResultCode.DECODING_ERROR, 2454 ERR_FILTER_CANNOT_DECODE_COMPS.get(getExceptionMessage(ae)), ae); 2455 } 2456 2457 final ASN1Element[] compElements = compSet.elements(); 2458 filterComps = new Filter[compElements.length]; 2459 for (int i=0; i < compElements.length; i++) 2460 { 2461 filterComps[i] = decode(compElements[i]); 2462 } 2463 break; 2464 2465 2466 case FILTER_TYPE_NOT: 2467 filterComps = NO_FILTERS; 2468 attrName = null; 2469 assertionValue = null; 2470 subInitial = null; 2471 subAny = NO_SUB_ANY; 2472 subFinal = null; 2473 matchingRuleID = null; 2474 dnAttributes = false; 2475 2476 final ASN1Element notFilterElement; 2477 try 2478 { 2479 notFilterElement = ASN1Element.decode(filterElement.getValue()); 2480 } 2481 catch (final ASN1Exception ae) 2482 { 2483 debugException(ae); 2484 throw new LDAPException(ResultCode.DECODING_ERROR, 2485 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)), 2486 ae); 2487 } 2488 notComp = decode(notFilterElement); 2489 break; 2490 2491 2492 2493 case FILTER_TYPE_EQUALITY: 2494 case FILTER_TYPE_GREATER_OR_EQUAL: 2495 case FILTER_TYPE_LESS_OR_EQUAL: 2496 case FILTER_TYPE_APPROXIMATE_MATCH: 2497 filterComps = NO_FILTERS; 2498 notComp = null; 2499 subInitial = null; 2500 subAny = NO_SUB_ANY; 2501 subFinal = null; 2502 matchingRuleID = null; 2503 dnAttributes = false; 2504 2505 final ASN1Sequence avaSequence; 2506 try 2507 { 2508 avaSequence = ASN1Sequence.decodeAsSequence(filterElement); 2509 } 2510 catch (final ASN1Exception ae) 2511 { 2512 debugException(ae); 2513 throw new LDAPException(ResultCode.DECODING_ERROR, 2514 ERR_FILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(ae)), ae); 2515 } 2516 2517 final ASN1Element[] avaElements = avaSequence.elements(); 2518 if (avaElements.length != 2) 2519 { 2520 throw new LDAPException(ResultCode.DECODING_ERROR, 2521 ERR_FILTER_INVALID_AVA_ELEMENT_COUNT.get( 2522 avaElements.length)); 2523 } 2524 2525 attrName = 2526 ASN1OctetString.decodeAsOctetString(avaElements[0]).stringValue(); 2527 assertionValue = ASN1OctetString.decodeAsOctetString(avaElements[1]); 2528 break; 2529 2530 2531 case FILTER_TYPE_SUBSTRING: 2532 filterComps = NO_FILTERS; 2533 notComp = null; 2534 assertionValue = null; 2535 matchingRuleID = null; 2536 dnAttributes = false; 2537 2538 final ASN1Sequence subFilterSequence; 2539 try 2540 { 2541 subFilterSequence = ASN1Sequence.decodeAsSequence(filterElement); 2542 } 2543 catch (final ASN1Exception ae) 2544 { 2545 debugException(ae); 2546 throw new LDAPException(ResultCode.DECODING_ERROR, 2547 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)), 2548 ae); 2549 } 2550 2551 final ASN1Element[] subFilterElements = subFilterSequence.elements(); 2552 if (subFilterElements.length != 2) 2553 { 2554 throw new LDAPException(ResultCode.DECODING_ERROR, 2555 ERR_FILTER_INVALID_SUBSTR_ASSERTION_COUNT.get( 2556 subFilterElements.length)); 2557 } 2558 2559 attrName = ASN1OctetString.decodeAsOctetString( 2560 subFilterElements[0]).stringValue(); 2561 2562 final ASN1Sequence subSequence; 2563 try 2564 { 2565 subSequence = ASN1Sequence.decodeAsSequence(subFilterElements[1]); 2566 } 2567 catch (ASN1Exception ae) 2568 { 2569 debugException(ae); 2570 throw new LDAPException(ResultCode.DECODING_ERROR, 2571 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)), 2572 ae); 2573 } 2574 2575 ASN1OctetString tempSubInitial = null; 2576 ASN1OctetString tempSubFinal = null; 2577 final ArrayList<ASN1OctetString> subAnyList = 2578 new ArrayList<ASN1OctetString>(1); 2579 2580 final ASN1Element[] subElements = subSequence.elements(); 2581 for (final ASN1Element subElement : subElements) 2582 { 2583 switch (subElement.getType()) 2584 { 2585 case SUBSTRING_TYPE_SUBINITIAL: 2586 if (tempSubInitial == null) 2587 { 2588 tempSubInitial = 2589 ASN1OctetString.decodeAsOctetString(subElement); 2590 } 2591 else 2592 { 2593 throw new LDAPException(ResultCode.DECODING_ERROR, 2594 ERR_FILTER_MULTIPLE_SUBINITIAL.get()); 2595 } 2596 break; 2597 2598 case SUBSTRING_TYPE_SUBANY: 2599 subAnyList.add(ASN1OctetString.decodeAsOctetString(subElement)); 2600 break; 2601 2602 case SUBSTRING_TYPE_SUBFINAL: 2603 if (tempSubFinal == null) 2604 { 2605 tempSubFinal = ASN1OctetString.decodeAsOctetString(subElement); 2606 } 2607 else 2608 { 2609 throw new LDAPException(ResultCode.DECODING_ERROR, 2610 ERR_FILTER_MULTIPLE_SUBFINAL.get()); 2611 } 2612 break; 2613 2614 default: 2615 throw new LDAPException(ResultCode.DECODING_ERROR, 2616 ERR_FILTER_INVALID_SUBSTR_TYPE.get( 2617 toHex(subElement.getType()))); 2618 } 2619 } 2620 2621 subInitial = tempSubInitial; 2622 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 2623 subFinal = tempSubFinal; 2624 break; 2625 2626 2627 case FILTER_TYPE_PRESENCE: 2628 filterComps = NO_FILTERS; 2629 notComp = null; 2630 assertionValue = null; 2631 subInitial = null; 2632 subAny = NO_SUB_ANY; 2633 subFinal = null; 2634 matchingRuleID = null; 2635 dnAttributes = false; 2636 attrName = 2637 ASN1OctetString.decodeAsOctetString(filterElement).stringValue(); 2638 break; 2639 2640 2641 case FILTER_TYPE_EXTENSIBLE_MATCH: 2642 filterComps = NO_FILTERS; 2643 notComp = null; 2644 subInitial = null; 2645 subAny = NO_SUB_ANY; 2646 subFinal = null; 2647 2648 final ASN1Sequence emSequence; 2649 try 2650 { 2651 emSequence = ASN1Sequence.decodeAsSequence(filterElement); 2652 } 2653 catch (ASN1Exception ae) 2654 { 2655 debugException(ae); 2656 throw new LDAPException(ResultCode.DECODING_ERROR, 2657 ERR_FILTER_CANNOT_DECODE_EXTMATCH.get(getExceptionMessage(ae)), 2658 ae); 2659 } 2660 2661 String tempAttrName = null; 2662 ASN1OctetString tempAssertionValue = null; 2663 String tempMatchingRuleID = null; 2664 boolean tempDNAttributes = false; 2665 for (final ASN1Element e : emSequence.elements()) 2666 { 2667 switch (e.getType()) 2668 { 2669 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2670 if (tempAttrName == null) 2671 { 2672 tempAttrName = 2673 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2674 } 2675 else 2676 { 2677 throw new LDAPException(ResultCode.DECODING_ERROR, 2678 ERR_FILTER_EXTMATCH_MULTIPLE_ATTRS.get()); 2679 } 2680 break; 2681 2682 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2683 if (tempMatchingRuleID == null) 2684 { 2685 tempMatchingRuleID = 2686 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2687 } 2688 else 2689 { 2690 throw new LDAPException(ResultCode.DECODING_ERROR, 2691 ERR_FILTER_EXTMATCH_MULTIPLE_MRIDS.get()); 2692 } 2693 break; 2694 2695 case EXTENSIBLE_TYPE_MATCH_VALUE: 2696 if (tempAssertionValue == null) 2697 { 2698 tempAssertionValue = ASN1OctetString.decodeAsOctetString(e); 2699 } 2700 else 2701 { 2702 throw new LDAPException(ResultCode.DECODING_ERROR, 2703 ERR_FILTER_EXTMATCH_MULTIPLE_VALUES.get()); 2704 } 2705 break; 2706 2707 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2708 try 2709 { 2710 if (tempDNAttributes) 2711 { 2712 throw new LDAPException(ResultCode.DECODING_ERROR, 2713 ERR_FILTER_EXTMATCH_MULTIPLE_DNATTRS.get()); 2714 } 2715 else 2716 { 2717 tempDNAttributes = 2718 ASN1Boolean.decodeAsBoolean(e).booleanValue(); 2719 } 2720 } 2721 catch (ASN1Exception ae) 2722 { 2723 debugException(ae); 2724 throw new LDAPException(ResultCode.DECODING_ERROR, 2725 ERR_FILTER_EXTMATCH_DNATTRS_NOT_BOOLEAN.get( 2726 getExceptionMessage(ae)), 2727 ae); 2728 } 2729 break; 2730 2731 default: 2732 throw new LDAPException(ResultCode.DECODING_ERROR, 2733 ERR_FILTER_EXTMATCH_INVALID_TYPE.get( 2734 toHex(e.getType()))); 2735 } 2736 } 2737 2738 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2739 { 2740 throw new LDAPException(ResultCode.DECODING_ERROR, 2741 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2742 } 2743 2744 if (tempAssertionValue == null) 2745 { 2746 throw new LDAPException(ResultCode.DECODING_ERROR, 2747 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2748 } 2749 2750 attrName = tempAttrName; 2751 assertionValue = tempAssertionValue; 2752 matchingRuleID = tempMatchingRuleID; 2753 dnAttributes = tempDNAttributes; 2754 break; 2755 2756 2757 default: 2758 throw new LDAPException(ResultCode.DECODING_ERROR, 2759 ERR_FILTER_ELEMENT_INVALID_TYPE.get( 2760 toHex(filterElement.getType()))); 2761 } 2762 2763 2764 return new Filter(null, filterType, filterComps, notComp, attrName, 2765 assertionValue, subInitial, subAny, subFinal, 2766 matchingRuleID, dnAttributes); 2767 } 2768 2769 2770 2771 /** 2772 * Retrieves the filter type for this filter. 2773 * 2774 * @return The filter type for this filter. 2775 */ 2776 public byte getFilterType() 2777 { 2778 return filterType; 2779 } 2780 2781 2782 2783 /** 2784 * Retrieves the set of filter components used in this AND or OR filter. This 2785 * is not applicable for any other filter type. 2786 * 2787 * @return The set of filter components used in this AND or OR filter, or an 2788 * empty array if this is some other type of filter or if there are 2789 * no components (i.e., as in an LDAP TRUE or LDAP FALSE filter). 2790 */ 2791 public Filter[] getComponents() 2792 { 2793 return filterComps; 2794 } 2795 2796 2797 2798 /** 2799 * Retrieves the filter component used in this NOT filter. This is not 2800 * applicable for any other filter type. 2801 * 2802 * @return The filter component used in this NOT filter, or {@code null} if 2803 * this is some other type of filter. 2804 */ 2805 public Filter getNOTComponent() 2806 { 2807 return notComp; 2808 } 2809 2810 2811 2812 /** 2813 * Retrieves the name of the attribute type for this search filter. This is 2814 * applicable for the following types of filters: 2815 * <UL> 2816 * <LI>Equality</LI> 2817 * <LI>Substring</LI> 2818 * <LI>Greater or Equal</LI> 2819 * <LI>Less or Equal</LI> 2820 * <LI>Presence</LI> 2821 * <LI>Approximate Match</LI> 2822 * <LI>Extensible Match</LI> 2823 * </UL> 2824 * 2825 * @return The name of the attribute type for this search filter, or 2826 * {@code null} if it is not applicable for this type of filter. 2827 */ 2828 public String getAttributeName() 2829 { 2830 return attrName; 2831 } 2832 2833 2834 2835 /** 2836 * Retrieves the string representation of the assertion value for this search 2837 * filter. This is applicable for the following types of filters: 2838 * <UL> 2839 * <LI>Equality</LI> 2840 * <LI>Greater or Equal</LI> 2841 * <LI>Less or Equal</LI> 2842 * <LI>Approximate Match</LI> 2843 * <LI>Extensible Match</LI> 2844 * </UL> 2845 * 2846 * @return The string representation of the assertion value for this search 2847 * filter, or {@code null} if it is not applicable for this type of 2848 * filter. 2849 */ 2850 public String getAssertionValue() 2851 { 2852 if (assertionValue == null) 2853 { 2854 return null; 2855 } 2856 else 2857 { 2858 return assertionValue.stringValue(); 2859 } 2860 } 2861 2862 2863 2864 /** 2865 * Retrieves the binary representation of the assertion value for this search 2866 * filter. This is applicable for the following types of filters: 2867 * <UL> 2868 * <LI>Equality</LI> 2869 * <LI>Greater or Equal</LI> 2870 * <LI>Less or Equal</LI> 2871 * <LI>Approximate Match</LI> 2872 * <LI>Extensible Match</LI> 2873 * </UL> 2874 * 2875 * @return The binary representation of the assertion value for this search 2876 * filter, or {@code null} if it is not applicable for this type of 2877 * filter. 2878 */ 2879 public byte[] getAssertionValueBytes() 2880 { 2881 if (assertionValue == null) 2882 { 2883 return null; 2884 } 2885 else 2886 { 2887 return assertionValue.getValue(); 2888 } 2889 } 2890 2891 2892 2893 /** 2894 * Retrieves the raw assertion value for this search filter as an ASN.1 2895 * octet string. This is applicable for the following types of filters: 2896 * <UL> 2897 * <LI>Equality</LI> 2898 * <LI>Greater or Equal</LI> 2899 * <LI>Less or Equal</LI> 2900 * <LI>Approximate Match</LI> 2901 * <LI>Extensible Match</LI> 2902 * </UL> 2903 * 2904 * @return The raw assertion value for this search filter as an ASN.1 octet 2905 * string, or {@code null} if it is not applicable for this type of 2906 * filter. 2907 */ 2908 public ASN1OctetString getRawAssertionValue() 2909 { 2910 return assertionValue; 2911 } 2912 2913 2914 2915 /** 2916 * Retrieves the string representation of the subInitial element for this 2917 * substring filter. This is not applicable for any other filter type. 2918 * 2919 * @return The string representation of the subInitial element for this 2920 * substring filter, or {@code null} if this is some other type of 2921 * filter, or if it is a substring filter with no subInitial element. 2922 */ 2923 public String getSubInitialString() 2924 { 2925 if (subInitial == null) 2926 { 2927 return null; 2928 } 2929 else 2930 { 2931 return subInitial.stringValue(); 2932 } 2933 } 2934 2935 2936 2937 /** 2938 * Retrieves the binary representation of the subInitial element for this 2939 * substring filter. This is not applicable for any other filter type. 2940 * 2941 * @return The binary representation of the subInitial element for this 2942 * substring filter, or {@code null} if this is some other type of 2943 * filter, or if it is a substring filter with no subInitial element. 2944 */ 2945 public byte[] getSubInitialBytes() 2946 { 2947 if (subInitial == null) 2948 { 2949 return null; 2950 } 2951 else 2952 { 2953 return subInitial.getValue(); 2954 } 2955 } 2956 2957 2958 2959 /** 2960 * Retrieves the raw subInitial element for this filter as an ASN.1 octet 2961 * string. This is not applicable for any other filter type. 2962 * 2963 * @return The raw subInitial element for this filter as an ASN.1 octet 2964 * string, or {@code null} if this is not a substring filter, or if 2965 * it is a substring filter with no subInitial element. 2966 */ 2967 public ASN1OctetString getRawSubInitialValue() 2968 { 2969 return subInitial; 2970 } 2971 2972 2973 2974 /** 2975 * Retrieves the string representations of the subAny elements for this 2976 * substring filter. This is not applicable for any other filter type. 2977 * 2978 * @return The string representations of the subAny elements for this 2979 * substring filter, or an empty array if this is some other type of 2980 * filter, or if it is a substring filter with no subFinal element. 2981 */ 2982 public String[] getSubAnyStrings() 2983 { 2984 final String[] subAnyStrings = new String[subAny.length]; 2985 for (int i=0; i < subAny.length; i++) 2986 { 2987 subAnyStrings[i] = subAny[i].stringValue(); 2988 } 2989 2990 return subAnyStrings; 2991 } 2992 2993 2994 2995 /** 2996 * Retrieves the binary representations of the subAny elements for this 2997 * substring filter. This is not applicable for any other filter type. 2998 * 2999 * @return The binary representations of the subAny elements for this 3000 * substring filter, or an empty array if this is some other type of 3001 * filter, or if it is a substring filter with no subFinal element. 3002 */ 3003 public byte[][] getSubAnyBytes() 3004 { 3005 final byte[][] subAnyBytes = new byte[subAny.length][]; 3006 for (int i=0; i < subAny.length; i++) 3007 { 3008 subAnyBytes[i] = subAny[i].getValue(); 3009 } 3010 3011 return subAnyBytes; 3012 } 3013 3014 3015 3016 /** 3017 * Retrieves the raw subAny values for this substring filter. This is not 3018 * applicable for any other filter type. 3019 * 3020 * @return The raw subAny values for this substring filter, or an empty array 3021 * if this is some other type of filter, or if it is a substring 3022 * filter with no subFinal element. 3023 */ 3024 public ASN1OctetString[] getRawSubAnyValues() 3025 { 3026 return subAny; 3027 } 3028 3029 3030 3031 /** 3032 * Retrieves the string representation of the subFinal element for this 3033 * substring filter. This is not applicable for any other filter type. 3034 * 3035 * @return The string representation of the subFinal element for this 3036 * substring filter, or {@code null} if this is some other type of 3037 * filter, or if it is a substring filter with no subFinal element. 3038 */ 3039 public String getSubFinalString() 3040 { 3041 if (subFinal == null) 3042 { 3043 return null; 3044 } 3045 else 3046 { 3047 return subFinal.stringValue(); 3048 } 3049 } 3050 3051 3052 3053 /** 3054 * Retrieves the binary representation of the subFinal element for this 3055 * substring filter. This is not applicable for any other filter type. 3056 * 3057 * @return The binary representation of the subFinal element for this 3058 * substring filter, or {@code null} if this is some other type of 3059 * filter, or if it is a substring filter with no subFinal element. 3060 */ 3061 public byte[] getSubFinalBytes() 3062 { 3063 if (subFinal == null) 3064 { 3065 return null; 3066 } 3067 else 3068 { 3069 return subFinal.getValue(); 3070 } 3071 } 3072 3073 3074 3075 /** 3076 * Retrieves the raw subFinal element for this filter as an ASN.1 octet 3077 * string. This is not applicable for any other filter type. 3078 * 3079 * @return The raw subFinal element for this filter as an ASN.1 octet 3080 * string, or {@code null} if this is not a substring filter, or if 3081 * it is a substring filter with no subFinal element. 3082 */ 3083 public ASN1OctetString getRawSubFinalValue() 3084 { 3085 return subFinal; 3086 } 3087 3088 3089 3090 /** 3091 * Retrieves the matching rule ID for this extensible match filter. This is 3092 * not applicable for any other filter type. 3093 * 3094 * @return The matching rule ID for this extensible match filter, or 3095 * {@code null} if this is some other type of filter, or if this 3096 * extensible match filter does not have a matching rule ID. 3097 */ 3098 public String getMatchingRuleID() 3099 { 3100 return matchingRuleID; 3101 } 3102 3103 3104 3105 /** 3106 * Retrieves the dnAttributes flag for this extensible match filter. This is 3107 * not applicable for any other filter type. 3108 * 3109 * @return The dnAttributes flag for this extensible match filter. 3110 */ 3111 public boolean getDNAttributes() 3112 { 3113 return dnAttributes; 3114 } 3115 3116 3117 3118 /** 3119 * Indicates whether this filter matches the provided entry. Note that this 3120 * is a best-guess effort and may not be completely accurate in all cases. 3121 * All matching will be performed using case-ignore string matching, which may 3122 * yield an unexpected result for values that should not be treated as simple 3123 * strings. For example: 3124 * <UL> 3125 * <LI>Two DN values which are logically equivalent may not be considered 3126 * matches if they have different spacing.</LI> 3127 * <LI>Ordering comparisons against numeric values may yield unexpected 3128 * results (e.g., "2" will be considered greater than "10" because the 3129 * character "2" has a larger ASCII value than the character "1").</LI> 3130 * </UL> 3131 * <BR> 3132 * In addition to the above constraints, it should be noted that neither 3133 * approximate matching nor extensible matching are currently supported. 3134 * 3135 * @param entry The entry for which to make the determination. It must not 3136 * be {@code null}. 3137 * 3138 * @return {@code true} if this filter appears to match the provided entry, 3139 * or {@code false} if not. 3140 * 3141 * @throws LDAPException If a problem occurs while trying to make the 3142 * determination. 3143 */ 3144 public boolean matchesEntry(final Entry entry) 3145 throws LDAPException 3146 { 3147 return matchesEntry(entry, entry.getSchema()); 3148 } 3149 3150 3151 3152 /** 3153 * Indicates whether this filter matches the provided entry. Note that this 3154 * is a best-guess effort and may not be completely accurate in all cases. 3155 * If provided, the given schema will be used in an attempt to determine the 3156 * appropriate matching rule for making the determinations, but some corner 3157 * cases may not be handled accurately. Neither approximate matching nor 3158 * extensible matching are currently supported. 3159 * 3160 * @param entry The entry for which to make the determination. It must not 3161 * be {@code null}. 3162 * @param schema The schema to use when making the determination. If this 3163 * is {@code null}, then all matching will be performed using 3164 * a case-ignore matching rule. 3165 * 3166 * @return {@code true} if this filter appears to match the provided entry, 3167 * or {@code false} if not. 3168 * 3169 * @throws LDAPException If a problem occurs while trying to make the 3170 * determination. 3171 */ 3172 public boolean matchesEntry(final Entry entry, final Schema schema) 3173 throws LDAPException 3174 { 3175 ensureNotNull(entry); 3176 3177 switch (filterType) 3178 { 3179 case FILTER_TYPE_AND: 3180 for (final Filter f : filterComps) 3181 { 3182 if (! f.matchesEntry(entry, schema)) 3183 { 3184 return false; 3185 } 3186 } 3187 return true; 3188 3189 case FILTER_TYPE_OR: 3190 for (final Filter f : filterComps) 3191 { 3192 if (f.matchesEntry(entry, schema)) 3193 { 3194 return true; 3195 } 3196 } 3197 return false; 3198 3199 case FILTER_TYPE_NOT: 3200 return (! notComp.matchesEntry(entry, schema)); 3201 3202 case FILTER_TYPE_EQUALITY: 3203 Attribute a = entry.getAttribute(attrName, schema); 3204 if (a == null) 3205 { 3206 return false; 3207 } 3208 3209 MatchingRule matchingRule = 3210 MatchingRule.selectEqualityMatchingRule(attrName, schema); 3211 for (final ASN1OctetString v : a.getRawValues()) 3212 { 3213 if (matchingRule.valuesMatch(v, assertionValue)) 3214 { 3215 return true; 3216 } 3217 } 3218 return false; 3219 3220 case FILTER_TYPE_SUBSTRING: 3221 a = entry.getAttribute(attrName, schema); 3222 if (a == null) 3223 { 3224 return false; 3225 } 3226 3227 matchingRule = 3228 MatchingRule.selectSubstringMatchingRule(attrName, schema); 3229 for (final ASN1OctetString v : a.getRawValues()) 3230 { 3231 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal)) 3232 { 3233 return true; 3234 } 3235 } 3236 return false; 3237 3238 case FILTER_TYPE_GREATER_OR_EQUAL: 3239 a = entry.getAttribute(attrName, schema); 3240 if (a == null) 3241 { 3242 return false; 3243 } 3244 3245 matchingRule = 3246 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3247 for (final ASN1OctetString v : a.getRawValues()) 3248 { 3249 if (matchingRule.compareValues(v, assertionValue) >= 0) 3250 { 3251 return true; 3252 } 3253 } 3254 return false; 3255 3256 case FILTER_TYPE_LESS_OR_EQUAL: 3257 a = entry.getAttribute(attrName, schema); 3258 if (a == null) 3259 { 3260 return false; 3261 } 3262 3263 matchingRule = 3264 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3265 for (final ASN1OctetString v : a.getRawValues()) 3266 { 3267 if (matchingRule.compareValues(v, assertionValue) <= 0) 3268 { 3269 return true; 3270 } 3271 } 3272 return false; 3273 3274 case FILTER_TYPE_PRESENCE: 3275 return (entry.hasAttribute(attrName)); 3276 3277 case FILTER_TYPE_APPROXIMATE_MATCH: 3278 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3279 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get()); 3280 3281 case FILTER_TYPE_EXTENSIBLE_MATCH: 3282 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3283 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get()); 3284 3285 default: 3286 throw new LDAPException(ResultCode.PARAM_ERROR, 3287 ERR_FILTER_INVALID_TYPE.get()); 3288 } 3289 } 3290 3291 3292 3293 /** 3294 * Attempts to simplify the provided filter to allow it to be more efficiently 3295 * processed by the server. The simplifications it will make include: 3296 * <UL> 3297 * <LI>Any AND or OR filter that contains only a single filter component 3298 * will be converted to just that embedded filter component to eliminate 3299 * the unnecessary AND or OR wrapper. For example, the filter 3300 * "(&(uid=john.doe))" will be converted to just 3301 * "(uid=john.doe)".</LI> 3302 * <LI>Any AND components inside of an AND filter will be merged into the 3303 * outer AND filter. Any OR components inside of an OR filter will be 3304 * merged into the outer OR filter. For example, the filter 3305 * "(&(objectClass=person)(&(givenName=John)(sn=Doe)))" will be 3306 * converted to 3307 * "(&(objectClass=person)(givenName=John)(sn=Doe))".</LI> 3308 * <LI>If {@code reOrderElements} is true, then this method will attempt to 3309 * re-order the elements inside AND and OR filters in an attempt to 3310 * ensure that the components which are likely to be the most efficient 3311 * come earlier than those which are likely to be the least efficient. 3312 * This can speed up processing in servers that process filter 3313 * components in a left-to-right order.</LI> 3314 * </UL> 3315 * <BR><BR> 3316 * The simplification will happen recursively, in an attempt to generate a 3317 * filter that is as simple and efficient as possible. 3318 * 3319 * @param filter The filter to attempt to simplify. 3320 * @param reOrderElements Indicates whether this method may re-order the 3321 * elements in the filter so that, in a server that 3322 * evaluates the components in a left-to-right order, 3323 * the components which are likely to be more 3324 * efficient to process will be listed before those 3325 * which are likely to be less efficient. 3326 * 3327 * @return The simplified filter, or the original filter if the provided 3328 * filter is not one that can be simplified any further. 3329 */ 3330 public static Filter simplifyFilter(final Filter filter, 3331 final boolean reOrderElements) 3332 { 3333 final byte filterType = filter.filterType; 3334 switch (filterType) 3335 { 3336 case FILTER_TYPE_AND: 3337 case FILTER_TYPE_OR: 3338 // These will be handled below. 3339 break; 3340 3341 case FILTER_TYPE_NOT: 3342 // We may be able to simplify the filter component contained inside the 3343 // NOT. 3344 return createNOTFilter(simplifyFilter(filter.notComp, reOrderElements)); 3345 3346 default: 3347 // We can't simplify this filter, so just return what was provided. 3348 return filter; 3349 } 3350 3351 3352 // An AND filter with zero components is an LDAP true filter, and we can't 3353 // simplify that. An OR filter with zero components is an LDAP false 3354 // filter, and we can't simplify that either. The set of components 3355 // should never be null for an AND or OR filter, but if that happens to be 3356 // the case, then we'll return the original filter. 3357 final Filter[] components = filter.filterComps; 3358 if ((components == null) || (components.length == 0)) 3359 { 3360 return filter; 3361 } 3362 3363 3364 // For either an AND or an OR filter with just a single component, then just 3365 // return that embedded component. But simplify it first. 3366 if (components.length == 1) 3367 { 3368 return simplifyFilter(components[0], reOrderElements); 3369 } 3370 3371 3372 // If we've gotten here, then we have a filter with multiple components. 3373 // Simplify each of them to the extent possible, un-embed any ANDs 3374 // contained inside an AND or ORs contained inside an OR, and eliminate any 3375 // duplicate components in the resulting top-level filter. 3376 final LinkedHashSet<Filter> componentSet = new LinkedHashSet<Filter>(10); 3377 for (final Filter f : components) 3378 { 3379 final Filter simplifiedFilter = simplifyFilter(f, reOrderElements); 3380 if (simplifiedFilter.filterType == FILTER_TYPE_AND) 3381 { 3382 if (filterType == FILTER_TYPE_AND) 3383 { 3384 // This is an AND nested inside an AND. In that case, we'll just put 3385 // all the nested components inside the outer AND. 3386 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3387 } 3388 else 3389 { 3390 componentSet.add(simplifiedFilter); 3391 } 3392 } 3393 else if (simplifiedFilter.filterType == FILTER_TYPE_OR) 3394 { 3395 if (filterType == FILTER_TYPE_OR) 3396 { 3397 // This is an OR nested inside an OR. In that case, we'll just put 3398 // all the nested components inside the outer OR. 3399 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3400 } 3401 else 3402 { 3403 componentSet.add(simplifiedFilter); 3404 } 3405 } 3406 else 3407 { 3408 componentSet.add(simplifiedFilter); 3409 } 3410 } 3411 3412 3413 // It's possible at this point that we are down to just a single component. 3414 // That can happen if the filter was an AND or an OR with a duplicate 3415 // element, like "(&(a=b)(a=b))". In that case, just return that one 3416 // component. 3417 if (componentSet.size() == 1) 3418 { 3419 return componentSet.iterator().next(); 3420 } 3421 3422 3423 // If we should re-order the components, then use the following priority 3424 // list: 3425 // 3426 // 1. Equality components that target an attribute other than objectClass. 3427 // These are most likely to require only a single database lookup to get 3428 // the candidate list, and that candidate list will frequently be small. 3429 // 2. Equality components that target the objectClass attribute. These are 3430 // likely to require only a single database lookup to get the candidate 3431 // list, but the candidate list is more likely to be larger. 3432 // 3. Approximate match components. These are also likely to require only 3433 // a single database lookup to get the candidate list, but that 3434 // candidate list is likely to have a larger number of candidates. 3435 // 4. Presence components that target an attribute other than objectClass. 3436 // These are also likely to require only a single database lookup to get 3437 // the candidate list, but are likely to have a large number of 3438 // candidates. 3439 // 5. Substring components that have a subInitial element. These are 3440 // generally the most efficient substring filters to process, requiring 3441 // access to fewer database keys than substring filters with only subAny 3442 // and/or subFinal components. 3443 // 6. Substring components that only have subAny and/or subFinal elements. 3444 // These will probably require a number of database lookups and will 3445 // probably result in large candidate lists. 3446 // 7. Greater-or-equal components and less-or-equal components. These 3447 // will probably require a number of database lookups and will probably 3448 // result in large candidate lists. 3449 // 8. Extensible match components. Even if these are indexed, there isn't 3450 // any good way to know how expensive they might be to process or how 3451 // big the candidate list might be. 3452 // 9. Presence components that target the objectClass attribute. This is 3453 // likely to require only a single database lookup to get the candidate 3454 // list, but the candidate list will also be extremely large (if it's 3455 // indexed at all) since it will match every entry. 3456 // 10. NOT components. These are generally not possible to index and 3457 // therefore cannot be used to create a candidate list. 3458 // 3459 // AND and OR components will be ordered according to the first of their 3460 // embedded components Since the filter has already been simplified, then 3461 // the first element in the list will be the one we think will be the most 3462 // efficient to process. 3463 if (reOrderElements) 3464 { 3465 final TreeMap<Integer,LinkedHashSet<Filter>> m = 3466 new TreeMap<Integer,LinkedHashSet<Filter>>(); 3467 for (final Filter f : componentSet) 3468 { 3469 final Filter prioritizeComp; 3470 if ((f.filterType == FILTER_TYPE_AND) || 3471 (f.filterType == FILTER_TYPE_OR)) 3472 { 3473 if (f.filterComps.length > 0) 3474 { 3475 prioritizeComp = f.filterComps[0]; 3476 } 3477 else 3478 { 3479 prioritizeComp = f; 3480 } 3481 } 3482 else 3483 { 3484 prioritizeComp = f; 3485 } 3486 3487 final Integer slot; 3488 switch (prioritizeComp.filterType) 3489 { 3490 case FILTER_TYPE_EQUALITY: 3491 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3492 { 3493 slot = 2; 3494 } 3495 else 3496 { 3497 slot = 1; 3498 } 3499 break; 3500 3501 case FILTER_TYPE_APPROXIMATE_MATCH: 3502 slot = 3; 3503 break; 3504 3505 case FILTER_TYPE_PRESENCE: 3506 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3507 { 3508 slot = 9; 3509 } 3510 else 3511 { 3512 slot = 4; 3513 } 3514 break; 3515 3516 case FILTER_TYPE_SUBSTRING: 3517 if (prioritizeComp.subInitial == null) 3518 { 3519 slot = 6; 3520 } 3521 else 3522 { 3523 slot = 5; 3524 } 3525 break; 3526 3527 case FILTER_TYPE_GREATER_OR_EQUAL: 3528 case FILTER_TYPE_LESS_OR_EQUAL: 3529 slot = 7; 3530 break; 3531 3532 case FILTER_TYPE_EXTENSIBLE_MATCH: 3533 slot = 8; 3534 break; 3535 3536 case FILTER_TYPE_NOT: 3537 default: 3538 slot = 10; 3539 break; 3540 } 3541 3542 LinkedHashSet<Filter> filterSet = m.get(slot-1); 3543 if (filterSet == null) 3544 { 3545 filterSet = new LinkedHashSet<Filter>(10); 3546 m.put(slot-1, filterSet); 3547 } 3548 filterSet.add(f); 3549 } 3550 3551 componentSet.clear(); 3552 for (final LinkedHashSet<Filter> filterSet : m.values()) 3553 { 3554 componentSet.addAll(filterSet); 3555 } 3556 } 3557 3558 3559 // Return the new, possibly simplified filter. 3560 if (filterType == FILTER_TYPE_AND) 3561 { 3562 return createANDFilter(componentSet); 3563 } 3564 else 3565 { 3566 return createORFilter(componentSet); 3567 } 3568 } 3569 3570 3571 3572 /** 3573 * Generates a hash code for this search filter. 3574 * 3575 * @return The generated hash code for this search filter. 3576 */ 3577 @Override() 3578 public int hashCode() 3579 { 3580 final CaseIgnoreStringMatchingRule matchingRule = 3581 CaseIgnoreStringMatchingRule.getInstance(); 3582 int hashCode = filterType; 3583 3584 switch (filterType) 3585 { 3586 case FILTER_TYPE_AND: 3587 case FILTER_TYPE_OR: 3588 for (final Filter f : filterComps) 3589 { 3590 hashCode += f.hashCode(); 3591 } 3592 break; 3593 3594 case FILTER_TYPE_NOT: 3595 hashCode += notComp.hashCode(); 3596 break; 3597 3598 case FILTER_TYPE_EQUALITY: 3599 case FILTER_TYPE_GREATER_OR_EQUAL: 3600 case FILTER_TYPE_LESS_OR_EQUAL: 3601 case FILTER_TYPE_APPROXIMATE_MATCH: 3602 hashCode += toLowerCase(attrName).hashCode(); 3603 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3604 break; 3605 3606 case FILTER_TYPE_SUBSTRING: 3607 hashCode += toLowerCase(attrName).hashCode(); 3608 if (subInitial != null) 3609 { 3610 hashCode += matchingRule.normalizeSubstring(subInitial, 3611 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode(); 3612 } 3613 for (final ASN1OctetString s : subAny) 3614 { 3615 hashCode += matchingRule.normalizeSubstring(s, 3616 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode(); 3617 } 3618 if (subFinal != null) 3619 { 3620 hashCode += matchingRule.normalizeSubstring(subFinal, 3621 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode(); 3622 } 3623 break; 3624 3625 case FILTER_TYPE_PRESENCE: 3626 hashCode += toLowerCase(attrName).hashCode(); 3627 break; 3628 3629 case FILTER_TYPE_EXTENSIBLE_MATCH: 3630 if (attrName != null) 3631 { 3632 hashCode += toLowerCase(attrName).hashCode(); 3633 } 3634 3635 if (matchingRuleID != null) 3636 { 3637 hashCode += toLowerCase(matchingRuleID).hashCode(); 3638 } 3639 3640 if (dnAttributes) 3641 { 3642 hashCode++; 3643 } 3644 3645 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3646 break; 3647 } 3648 3649 return hashCode; 3650 } 3651 3652 3653 3654 /** 3655 * Indicates whether the provided object is equal to this search filter. 3656 * 3657 * @param o The object for which to make the determination. 3658 * 3659 * @return {@code true} if the provided object can be considered equal to 3660 * this search filter, or {@code false} if not. 3661 */ 3662 @Override() 3663 public boolean equals(final Object o) 3664 { 3665 if (o == null) 3666 { 3667 return false; 3668 } 3669 3670 if (o == this) 3671 { 3672 return true; 3673 } 3674 3675 if (! (o instanceof Filter)) 3676 { 3677 return false; 3678 } 3679 3680 final Filter f = (Filter) o; 3681 if (filterType != f.filterType) 3682 { 3683 return false; 3684 } 3685 3686 final CaseIgnoreStringMatchingRule matchingRule = 3687 CaseIgnoreStringMatchingRule.getInstance(); 3688 3689 switch (filterType) 3690 { 3691 case FILTER_TYPE_AND: 3692 case FILTER_TYPE_OR: 3693 if (filterComps.length != f.filterComps.length) 3694 { 3695 return false; 3696 } 3697 3698 final HashSet<Filter> compSet = new HashSet<Filter>(); 3699 compSet.addAll(Arrays.asList(filterComps)); 3700 3701 for (final Filter filterComp : f.filterComps) 3702 { 3703 if (! compSet.remove(filterComp)) 3704 { 3705 return false; 3706 } 3707 } 3708 3709 return true; 3710 3711 3712 case FILTER_TYPE_NOT: 3713 return notComp.equals(f.notComp); 3714 3715 3716 case FILTER_TYPE_EQUALITY: 3717 case FILTER_TYPE_GREATER_OR_EQUAL: 3718 case FILTER_TYPE_LESS_OR_EQUAL: 3719 case FILTER_TYPE_APPROXIMATE_MATCH: 3720 return (attrName.equalsIgnoreCase(f.attrName) && 3721 matchingRule.valuesMatch(assertionValue, f.assertionValue)); 3722 3723 3724 case FILTER_TYPE_SUBSTRING: 3725 if (! attrName.equalsIgnoreCase(f.attrName)) 3726 { 3727 return false; 3728 } 3729 3730 if (subAny.length != f.subAny.length) 3731 { 3732 return false; 3733 } 3734 3735 if (subInitial == null) 3736 { 3737 if (f.subInitial != null) 3738 { 3739 return false; 3740 } 3741 } 3742 else 3743 { 3744 if (f.subInitial == null) 3745 { 3746 return false; 3747 } 3748 3749 final ASN1OctetString si1 = matchingRule.normalizeSubstring( 3750 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3751 final ASN1OctetString si2 = matchingRule.normalizeSubstring( 3752 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3753 if (! si1.equals(si2)) 3754 { 3755 return false; 3756 } 3757 } 3758 3759 for (int i=0; i < subAny.length; i++) 3760 { 3761 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i], 3762 MatchingRule.SUBSTRING_TYPE_SUBANY); 3763 final ASN1OctetString sa2 = matchingRule.normalizeSubstring( 3764 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY); 3765 if (! sa1.equals(sa2)) 3766 { 3767 return false; 3768 } 3769 } 3770 3771 if (subFinal == null) 3772 { 3773 if (f.subFinal != null) 3774 { 3775 return false; 3776 } 3777 } 3778 else 3779 { 3780 if (f.subFinal == null) 3781 { 3782 return false; 3783 } 3784 3785 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal, 3786 MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3787 final ASN1OctetString sf2 = matchingRule.normalizeSubstring( 3788 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3789 if (! sf1.equals(sf2)) 3790 { 3791 return false; 3792 } 3793 } 3794 3795 return true; 3796 3797 3798 case FILTER_TYPE_PRESENCE: 3799 return (attrName.equalsIgnoreCase(f.attrName)); 3800 3801 3802 case FILTER_TYPE_EXTENSIBLE_MATCH: 3803 if (attrName == null) 3804 { 3805 if (f.attrName != null) 3806 { 3807 return false; 3808 } 3809 } 3810 else 3811 { 3812 if (f.attrName == null) 3813 { 3814 return false; 3815 } 3816 else 3817 { 3818 if (! attrName.equalsIgnoreCase(f.attrName)) 3819 { 3820 return false; 3821 } 3822 } 3823 } 3824 3825 if (matchingRuleID == null) 3826 { 3827 if (f.matchingRuleID != null) 3828 { 3829 return false; 3830 } 3831 } 3832 else 3833 { 3834 if (f.matchingRuleID == null) 3835 { 3836 return false; 3837 } 3838 else 3839 { 3840 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID)) 3841 { 3842 return false; 3843 } 3844 } 3845 } 3846 3847 if (dnAttributes != f.dnAttributes) 3848 { 3849 return false; 3850 } 3851 3852 return matchingRule.valuesMatch(assertionValue, f.assertionValue); 3853 3854 3855 default: 3856 return false; 3857 } 3858 } 3859 3860 3861 3862 /** 3863 * Retrieves a string representation of this search filter. 3864 * 3865 * @return A string representation of this search filter. 3866 */ 3867 @Override() 3868 public String toString() 3869 { 3870 if (filterString == null) 3871 { 3872 final StringBuilder buffer = new StringBuilder(); 3873 toString(buffer); 3874 filterString = buffer.toString(); 3875 } 3876 3877 return filterString; 3878 } 3879 3880 3881 3882 /** 3883 * Appends a string representation of this search filter to the provided 3884 * buffer. 3885 * 3886 * @param buffer The buffer to which to append a string representation of 3887 * this search filter. 3888 */ 3889 public void toString(final StringBuilder buffer) 3890 { 3891 switch (filterType) 3892 { 3893 case FILTER_TYPE_AND: 3894 buffer.append("(&"); 3895 for (final Filter f : filterComps) 3896 { 3897 f.toString(buffer); 3898 } 3899 buffer.append(')'); 3900 break; 3901 3902 case FILTER_TYPE_OR: 3903 buffer.append("(|"); 3904 for (final Filter f : filterComps) 3905 { 3906 f.toString(buffer); 3907 } 3908 buffer.append(')'); 3909 break; 3910 3911 case FILTER_TYPE_NOT: 3912 buffer.append("(!"); 3913 notComp.toString(buffer); 3914 buffer.append(')'); 3915 break; 3916 3917 case FILTER_TYPE_EQUALITY: 3918 buffer.append('('); 3919 buffer.append(attrName); 3920 buffer.append('='); 3921 encodeValue(assertionValue, buffer); 3922 buffer.append(')'); 3923 break; 3924 3925 case FILTER_TYPE_SUBSTRING: 3926 buffer.append('('); 3927 buffer.append(attrName); 3928 buffer.append('='); 3929 if (subInitial != null) 3930 { 3931 encodeValue(subInitial, buffer); 3932 } 3933 buffer.append('*'); 3934 for (final ASN1OctetString s : subAny) 3935 { 3936 encodeValue(s, buffer); 3937 buffer.append('*'); 3938 } 3939 if (subFinal != null) 3940 { 3941 encodeValue(subFinal, buffer); 3942 } 3943 buffer.append(')'); 3944 break; 3945 3946 case FILTER_TYPE_GREATER_OR_EQUAL: 3947 buffer.append('('); 3948 buffer.append(attrName); 3949 buffer.append(">="); 3950 encodeValue(assertionValue, buffer); 3951 buffer.append(')'); 3952 break; 3953 3954 case FILTER_TYPE_LESS_OR_EQUAL: 3955 buffer.append('('); 3956 buffer.append(attrName); 3957 buffer.append("<="); 3958 encodeValue(assertionValue, buffer); 3959 buffer.append(')'); 3960 break; 3961 3962 case FILTER_TYPE_PRESENCE: 3963 buffer.append('('); 3964 buffer.append(attrName); 3965 buffer.append("=*)"); 3966 break; 3967 3968 case FILTER_TYPE_APPROXIMATE_MATCH: 3969 buffer.append('('); 3970 buffer.append(attrName); 3971 buffer.append("~="); 3972 encodeValue(assertionValue, buffer); 3973 buffer.append(')'); 3974 break; 3975 3976 case FILTER_TYPE_EXTENSIBLE_MATCH: 3977 buffer.append('('); 3978 if (attrName != null) 3979 { 3980 buffer.append(attrName); 3981 } 3982 3983 if (dnAttributes) 3984 { 3985 buffer.append(":dn"); 3986 } 3987 3988 if (matchingRuleID != null) 3989 { 3990 buffer.append(':'); 3991 buffer.append(matchingRuleID); 3992 } 3993 3994 buffer.append(":="); 3995 encodeValue(assertionValue, buffer); 3996 buffer.append(')'); 3997 break; 3998 } 3999 } 4000 4001 4002 4003 /** 4004 * Retrieves a normalized string representation of this search filter. 4005 * 4006 * @return A normalized string representation of this search filter. 4007 */ 4008 public String toNormalizedString() 4009 { 4010 if (normalizedString == null) 4011 { 4012 final StringBuilder buffer = new StringBuilder(); 4013 toNormalizedString(buffer); 4014 normalizedString = buffer.toString(); 4015 } 4016 4017 return normalizedString; 4018 } 4019 4020 4021 4022 /** 4023 * Appends a normalized string representation of this search filter to the 4024 * provided buffer. 4025 * 4026 * @param buffer The buffer to which to append a normalized string 4027 * representation of this search filter. 4028 */ 4029 public void toNormalizedString(final StringBuilder buffer) 4030 { 4031 final CaseIgnoreStringMatchingRule mr = 4032 CaseIgnoreStringMatchingRule.getInstance(); 4033 4034 switch (filterType) 4035 { 4036 case FILTER_TYPE_AND: 4037 buffer.append("(&"); 4038 for (final Filter f : filterComps) 4039 { 4040 f.toNormalizedString(buffer); 4041 } 4042 buffer.append(')'); 4043 break; 4044 4045 case FILTER_TYPE_OR: 4046 buffer.append("(|"); 4047 for (final Filter f : filterComps) 4048 { 4049 f.toNormalizedString(buffer); 4050 } 4051 buffer.append(')'); 4052 break; 4053 4054 case FILTER_TYPE_NOT: 4055 buffer.append("(!"); 4056 notComp.toNormalizedString(buffer); 4057 buffer.append(')'); 4058 break; 4059 4060 case FILTER_TYPE_EQUALITY: 4061 buffer.append('('); 4062 buffer.append(toLowerCase(attrName)); 4063 buffer.append('='); 4064 encodeValue(mr.normalize(assertionValue), buffer); 4065 buffer.append(')'); 4066 break; 4067 4068 case FILTER_TYPE_SUBSTRING: 4069 buffer.append('('); 4070 buffer.append(toLowerCase(attrName)); 4071 buffer.append('='); 4072 if (subInitial != null) 4073 { 4074 encodeValue(mr.normalizeSubstring(subInitial, 4075 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer); 4076 } 4077 buffer.append('*'); 4078 for (final ASN1OctetString s : subAny) 4079 { 4080 encodeValue(mr.normalizeSubstring(s, 4081 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer); 4082 buffer.append('*'); 4083 } 4084 if (subFinal != null) 4085 { 4086 encodeValue(mr.normalizeSubstring(subFinal, 4087 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer); 4088 } 4089 buffer.append(')'); 4090 break; 4091 4092 case FILTER_TYPE_GREATER_OR_EQUAL: 4093 buffer.append('('); 4094 buffer.append(toLowerCase(attrName)); 4095 buffer.append(">="); 4096 encodeValue(mr.normalize(assertionValue), buffer); 4097 buffer.append(')'); 4098 break; 4099 4100 case FILTER_TYPE_LESS_OR_EQUAL: 4101 buffer.append('('); 4102 buffer.append(toLowerCase(attrName)); 4103 buffer.append("<="); 4104 encodeValue(mr.normalize(assertionValue), buffer); 4105 buffer.append(')'); 4106 break; 4107 4108 case FILTER_TYPE_PRESENCE: 4109 buffer.append('('); 4110 buffer.append(toLowerCase(attrName)); 4111 buffer.append("=*)"); 4112 break; 4113 4114 case FILTER_TYPE_APPROXIMATE_MATCH: 4115 buffer.append('('); 4116 buffer.append(toLowerCase(attrName)); 4117 buffer.append("~="); 4118 encodeValue(mr.normalize(assertionValue), buffer); 4119 buffer.append(')'); 4120 break; 4121 4122 case FILTER_TYPE_EXTENSIBLE_MATCH: 4123 buffer.append('('); 4124 if (attrName != null) 4125 { 4126 buffer.append(toLowerCase(attrName)); 4127 } 4128 4129 if (dnAttributes) 4130 { 4131 buffer.append(":dn"); 4132 } 4133 4134 if (matchingRuleID != null) 4135 { 4136 buffer.append(':'); 4137 buffer.append(toLowerCase(matchingRuleID)); 4138 } 4139 4140 buffer.append(":="); 4141 encodeValue(mr.normalize(assertionValue), buffer); 4142 buffer.append(')'); 4143 break; 4144 } 4145 } 4146 4147 4148 4149 /** 4150 * Encodes the provided value into a form suitable for use as the assertion 4151 * value in the string representation of a search filter. Parentheses, 4152 * asterisks, backslashes, null characters, and any non-ASCII characters will 4153 * be escaped using a backslash before the hexadecimal representation of each 4154 * byte in the character to escape. 4155 * 4156 * @param value The value to be encoded. It must not be {@code null}. 4157 * 4158 * @return The encoded representation of the provided string. 4159 */ 4160 public static String encodeValue(final String value) 4161 { 4162 ensureNotNull(value); 4163 4164 final StringBuilder buffer = new StringBuilder(); 4165 encodeValue(new ASN1OctetString(value), buffer); 4166 return buffer.toString(); 4167 } 4168 4169 4170 4171 /** 4172 * Encodes the provided value into a form suitable for use as the assertion 4173 * value in the string representation of a search filter. Parentheses, 4174 * asterisks, backslashes, null characters, and any non-ASCII characters will 4175 * be escaped using a backslash before the hexadecimal representation of each 4176 * byte in the character to escape. 4177 * 4178 * @param value The value to be encoded. It must not be {@code null}. 4179 * 4180 * @return The encoded representation of the provided string. 4181 */ 4182 public static String encodeValue(final byte[]value) 4183 { 4184 ensureNotNull(value); 4185 4186 final StringBuilder buffer = new StringBuilder(); 4187 encodeValue(new ASN1OctetString(value), buffer); 4188 return buffer.toString(); 4189 } 4190 4191 4192 4193 /** 4194 * Appends the assertion value for this filter to the provided buffer, 4195 * encoding any special characters as necessary. 4196 * 4197 * @param value The value to be encoded. 4198 * @param buffer The buffer to which the assertion value should be appended. 4199 */ 4200 private static void encodeValue(final ASN1OctetString value, 4201 final StringBuilder buffer) 4202 { 4203 final byte[] valueBytes = value.getValue(); 4204 for (int i=0; i < valueBytes.length; i++) 4205 { 4206 switch (numBytesInUTF8CharacterWithFirstByte(valueBytes[i])) 4207 { 4208 case 1: 4209 // This character is ASCII, but might still need to be escaped. We'll 4210 // escape anything 4211 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters. 4212 (valueBytes[i] == 0x28) || // Open parenthesis 4213 (valueBytes[i] == 0x29) || // Close parenthesis 4214 (valueBytes[i] == 0x2A) || // Asterisk 4215 (valueBytes[i] == 0x5C) || // Backslash 4216 (valueBytes[i] == 0x7F)) // DEL 4217 { 4218 buffer.append('\\'); 4219 toHex(valueBytes[i], buffer); 4220 } 4221 else 4222 { 4223 buffer.append((char) valueBytes[i]); 4224 } 4225 break; 4226 4227 case 2: 4228 // If there are at least two bytes left, then we'll hex-encode the 4229 // next two bytes. Otherwise we'll hex-encode whatever is left. 4230 buffer.append('\\'); 4231 toHex(valueBytes[i++], buffer); 4232 if (i < valueBytes.length) 4233 { 4234 buffer.append('\\'); 4235 toHex(valueBytes[i], buffer); 4236 } 4237 break; 4238 4239 case 3: 4240 // If there are at least three bytes left, then we'll hex-encode the 4241 // next three bytes. Otherwise we'll hex-encode whatever is left. 4242 buffer.append('\\'); 4243 toHex(valueBytes[i++], buffer); 4244 if (i < valueBytes.length) 4245 { 4246 buffer.append('\\'); 4247 toHex(valueBytes[i++], buffer); 4248 } 4249 if (i < valueBytes.length) 4250 { 4251 buffer.append('\\'); 4252 toHex(valueBytes[i], buffer); 4253 } 4254 break; 4255 4256 case 4: 4257 // If there are at least four bytes left, then we'll hex-encode the 4258 // next four bytes. Otherwise we'll hex-encode whatever is left. 4259 buffer.append('\\'); 4260 toHex(valueBytes[i++], buffer); 4261 if (i < valueBytes.length) 4262 { 4263 buffer.append('\\'); 4264 toHex(valueBytes[i++], buffer); 4265 } 4266 if (i < valueBytes.length) 4267 { 4268 buffer.append('\\'); 4269 toHex(valueBytes[i++], buffer); 4270 } 4271 if (i < valueBytes.length) 4272 { 4273 buffer.append('\\'); 4274 toHex(valueBytes[i], buffer); 4275 } 4276 break; 4277 4278 default: 4279 // We'll hex-encode whatever is left in the buffer. 4280 while (i < valueBytes.length) 4281 { 4282 buffer.append('\\'); 4283 toHex(valueBytes[i++], buffer); 4284 } 4285 break; 4286 } 4287 } 4288 } 4289 4290 4291 4292 /** 4293 * Appends a number of lines comprising the Java source code that can be used 4294 * to recreate this filter to the given list. Note that unless a first line 4295 * prefix and/or last line suffix are provided, this will just include the 4296 * code for the static method used to create the filter, starting with 4297 * "Filter.createXFilter(" and ending with the closing parenthesis for that 4298 * method call. 4299 * 4300 * @param lineList The list to which the source code lines should be 4301 * added. 4302 * @param indentSpaces The number of spaces that should be used to indent 4303 * the generated code. It must not be negative. 4304 * @param firstLinePrefix An optional string that should precede the static 4305 * method call (e.g., it could be used for an 4306 * attribute assignment, like "Filter f = "). It may 4307 * be {@code null} or empty if there should be no 4308 * first line prefix. 4309 * @param lastLineSuffix An optional suffix that should follow the closing 4310 * parenthesis of the static method call (e.g., it 4311 * could be a semicolon to represent the end of a 4312 * Java statement). It may be {@code null} or empty 4313 * if there should be no last line suffix. 4314 */ 4315 public void toCode(final List<String> lineList, final int indentSpaces, 4316 final String firstLinePrefix, final String lastLineSuffix) 4317 { 4318 // Generate a string with the appropriate indent. 4319 final StringBuilder buffer = new StringBuilder(); 4320 for (int i = 0; i < indentSpaces; i++) 4321 { 4322 buffer.append(' '); 4323 } 4324 final String indent = buffer.toString(); 4325 4326 4327 // Start the first line, including any appropriate prefix. 4328 buffer.setLength(0); 4329 buffer.append(indent); 4330 if (firstLinePrefix != null) 4331 { 4332 buffer.append(firstLinePrefix); 4333 } 4334 4335 4336 // Figure out what type of filter it is and create the appropriate code for 4337 // that type of filter. 4338 switch (filterType) 4339 { 4340 case FILTER_TYPE_AND: 4341 case FILTER_TYPE_OR: 4342 if (filterType == FILTER_TYPE_AND) 4343 { 4344 buffer.append("Filter.createANDFilter("); 4345 } 4346 else 4347 { 4348 buffer.append("Filter.createORFilter("); 4349 } 4350 if (filterComps.length == 0) 4351 { 4352 buffer.append(')'); 4353 if (lastLineSuffix != null) 4354 { 4355 buffer.append(lastLineSuffix); 4356 } 4357 lineList.add(buffer.toString()); 4358 return; 4359 } 4360 4361 for (int i = 0; i < filterComps.length; i++) 4362 { 4363 String suffix; 4364 if (i == (filterComps.length - 1)) 4365 { 4366 suffix = ")"; 4367 if (lastLineSuffix != null) 4368 { 4369 suffix += lastLineSuffix; 4370 } 4371 } 4372 else 4373 { 4374 suffix = ","; 4375 } 4376 4377 filterComps[i].toCode(lineList, indentSpaces + 5, null, suffix); 4378 } 4379 return; 4380 4381 4382 case FILTER_TYPE_NOT: 4383 buffer.append("Filter.createNOTFilter("); 4384 lineList.add(buffer.toString()); 4385 4386 final String suffix; 4387 if (lastLineSuffix == null) 4388 { 4389 suffix = ")"; 4390 } 4391 else 4392 { 4393 suffix = ')' + lastLineSuffix; 4394 } 4395 notComp.toCode(lineList, indentSpaces + 5, null, suffix); 4396 return; 4397 4398 case FILTER_TYPE_PRESENCE: 4399 buffer.append("Filter.createPresenceFilter("); 4400 lineList.add(buffer.toString()); 4401 4402 buffer.setLength(0); 4403 buffer.append(indent); 4404 buffer.append(" \""); 4405 buffer.append(attrName); 4406 buffer.append("\")"); 4407 4408 if (lastLineSuffix != null) 4409 { 4410 buffer.append(lastLineSuffix); 4411 } 4412 4413 lineList.add(buffer.toString()); 4414 return; 4415 4416 4417 case FILTER_TYPE_EQUALITY: 4418 case FILTER_TYPE_GREATER_OR_EQUAL: 4419 case FILTER_TYPE_LESS_OR_EQUAL: 4420 case FILTER_TYPE_APPROXIMATE_MATCH: 4421 if (filterType == FILTER_TYPE_EQUALITY) 4422 { 4423 buffer.append("Filter.createEqualityFilter("); 4424 } 4425 else if (filterType == FILTER_TYPE_GREATER_OR_EQUAL) 4426 { 4427 buffer.append("Filter.createGreaterOrEqualFilter("); 4428 } 4429 else if (filterType == FILTER_TYPE_LESS_OR_EQUAL) 4430 { 4431 buffer.append("Filter.createLessOrEqualFilter("); 4432 } 4433 else 4434 { 4435 buffer.append("Filter.createApproximateMatchFilter("); 4436 } 4437 lineList.add(buffer.toString()); 4438 4439 buffer.setLength(0); 4440 buffer.append(indent); 4441 buffer.append(" \""); 4442 buffer.append(attrName); 4443 buffer.append("\","); 4444 lineList.add(buffer.toString()); 4445 4446 buffer.setLength(0); 4447 buffer.append(indent); 4448 buffer.append(" "); 4449 if (isSensitiveToCodeAttribute(attrName)) 4450 { 4451 buffer.append("\"---redacted-value---\""); 4452 } 4453 else if (isPrintableString(assertionValue.getValue())) 4454 { 4455 buffer.append('"'); 4456 buffer.append(assertionValue.stringValue()); 4457 buffer.append('"'); 4458 } 4459 else 4460 { 4461 byteArrayToCode(assertionValue.getValue(), buffer); 4462 } 4463 4464 buffer.append(')'); 4465 4466 if (lastLineSuffix != null) 4467 { 4468 buffer.append(lastLineSuffix); 4469 } 4470 4471 lineList.add(buffer.toString()); 4472 return; 4473 4474 4475 case FILTER_TYPE_SUBSTRING: 4476 buffer.append("Filter.createSubstringFilter("); 4477 lineList.add(buffer.toString()); 4478 4479 buffer.setLength(0); 4480 buffer.append(indent); 4481 buffer.append(" \""); 4482 buffer.append(attrName); 4483 buffer.append("\","); 4484 lineList.add(buffer.toString()); 4485 4486 final boolean isRedacted = isSensitiveToCodeAttribute(attrName); 4487 boolean isPrintable = true; 4488 if (subInitial != null) 4489 { 4490 isPrintable = isPrintableString(subInitial.getValue()); 4491 } 4492 4493 if (isPrintable && (subAny != null)) 4494 { 4495 for (final ASN1OctetString s : subAny) 4496 { 4497 if (! isPrintableString(s.getValue())) 4498 { 4499 isPrintable = false; 4500 break; 4501 } 4502 } 4503 } 4504 4505 if (isPrintable && (subFinal != null)) 4506 { 4507 isPrintable = isPrintableString(subFinal.getValue()); 4508 } 4509 4510 buffer.setLength(0); 4511 buffer.append(indent); 4512 buffer.append(" "); 4513 if (subInitial == null) 4514 { 4515 buffer.append("null"); 4516 } 4517 else if (isRedacted) 4518 { 4519 buffer.append("\"---redacted-subInitial---\""); 4520 } 4521 else if (isPrintable) 4522 { 4523 buffer.append('"'); 4524 buffer.append(subInitial.stringValue()); 4525 buffer.append('"'); 4526 } 4527 else 4528 { 4529 byteArrayToCode(subInitial.getValue(), buffer); 4530 } 4531 buffer.append(','); 4532 lineList.add(buffer.toString()); 4533 4534 buffer.setLength(0); 4535 buffer.append(indent); 4536 buffer.append(" "); 4537 if ((subAny == null) || (subAny.length == 0)) 4538 { 4539 buffer.append("null,"); 4540 lineList.add(buffer.toString()); 4541 } 4542 else if (isRedacted) 4543 { 4544 buffer.append("new String[]"); 4545 lineList.add(buffer.toString()); 4546 4547 lineList.add(indent + " {"); 4548 4549 for (int i=0; i < subAny.length; i++) 4550 { 4551 buffer.setLength(0); 4552 buffer.append(indent); 4553 buffer.append(" \"---redacted-subAny-"); 4554 buffer.append(i+1); 4555 buffer.append("---\""); 4556 if (i < (subAny.length-1)) 4557 { 4558 buffer.append(','); 4559 } 4560 lineList.add(buffer.toString()); 4561 } 4562 4563 lineList.add(indent + " },"); 4564 } 4565 else if (isPrintable) 4566 { 4567 buffer.append("new String[]"); 4568 lineList.add(buffer.toString()); 4569 4570 lineList.add(indent + " {"); 4571 4572 for (int i=0; i < subAny.length; i++) 4573 { 4574 buffer.setLength(0); 4575 buffer.append(indent); 4576 buffer.append(" \""); 4577 buffer.append(subAny[i].stringValue()); 4578 buffer.append('"'); 4579 if (i < (subAny.length-1)) 4580 { 4581 buffer.append(','); 4582 } 4583 lineList.add(buffer.toString()); 4584 } 4585 4586 lineList.add(indent + " },"); 4587 } 4588 else 4589 { 4590 buffer.append("new String[]"); 4591 lineList.add(buffer.toString()); 4592 4593 lineList.add(indent + " {"); 4594 4595 for (int i=0; i < subAny.length; i++) 4596 { 4597 buffer.setLength(0); 4598 buffer.append(indent); 4599 buffer.append(" "); 4600 byteArrayToCode(subAny[i].getValue(), buffer); 4601 if (i < (subAny.length-1)) 4602 { 4603 buffer.append(','); 4604 } 4605 lineList.add(buffer.toString()); 4606 } 4607 4608 lineList.add(indent + " },"); 4609 } 4610 4611 buffer.setLength(0); 4612 buffer.append(indent); 4613 buffer.append(" "); 4614 if (subFinal == null) 4615 { 4616 buffer.append("null)"); 4617 } 4618 else if (isRedacted) 4619 { 4620 buffer.append("\"---redacted-subFinal---\")"); 4621 } 4622 else if (isPrintable) 4623 { 4624 buffer.append('"'); 4625 buffer.append(subFinal.stringValue()); 4626 buffer.append("\")"); 4627 } 4628 else 4629 { 4630 byteArrayToCode(subFinal.getValue(), buffer); 4631 buffer.append(')'); 4632 } 4633 if (lastLineSuffix != null) 4634 { 4635 buffer.append(lastLineSuffix); 4636 } 4637 lineList.add(buffer.toString()); 4638 return; 4639 4640 4641 case FILTER_TYPE_EXTENSIBLE_MATCH: 4642 buffer.append("Filter.createExtensibleMatchFilter("); 4643 lineList.add(buffer.toString()); 4644 4645 buffer.setLength(0); 4646 buffer.append(indent); 4647 buffer.append(" "); 4648 if (attrName == null) 4649 { 4650 buffer.append("null, // Attribute Description"); 4651 } 4652 else 4653 { 4654 buffer.append('"'); 4655 buffer.append(attrName); 4656 buffer.append("\","); 4657 } 4658 lineList.add(buffer.toString()); 4659 4660 buffer.setLength(0); 4661 buffer.append(indent); 4662 buffer.append(" "); 4663 if (matchingRuleID == null) 4664 { 4665 buffer.append("null, // Matching Rule ID"); 4666 } 4667 else 4668 { 4669 buffer.append('"'); 4670 buffer.append(matchingRuleID); 4671 buffer.append("\","); 4672 } 4673 lineList.add(buffer.toString()); 4674 4675 buffer.setLength(0); 4676 buffer.append(indent); 4677 buffer.append(" "); 4678 buffer.append(dnAttributes); 4679 buffer.append(", // DN Attributes"); 4680 lineList.add(buffer.toString()); 4681 4682 buffer.setLength(0); 4683 buffer.append(indent); 4684 buffer.append(" "); 4685 if ((attrName != null) && isSensitiveToCodeAttribute(attrName)) 4686 { 4687 buffer.append("\"---redacted-value---\")"); 4688 } 4689 else 4690 { 4691 if (isPrintableString(assertionValue.getValue())) 4692 { 4693 buffer.append('"'); 4694 buffer.append(assertionValue.stringValue()); 4695 buffer.append("\")"); 4696 } 4697 else 4698 { 4699 byteArrayToCode(assertionValue.getValue(), buffer); 4700 buffer.append(')'); 4701 } 4702 } 4703 4704 if (lastLineSuffix != null) 4705 { 4706 buffer.append(lastLineSuffix); 4707 } 4708 lineList.add(buffer.toString()); 4709 return; 4710 } 4711 } 4712 }