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