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