001/* 002 * Copyright 2007-2017 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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 buffer.append(c); 1689 break; 1690 } 1691 } 1692 1693 if ((tempFilterType == FILTER_TYPE_SUBSTRING) && 1694 (buffer.length() > 0)) 1695 { 1696 // The buffer must contain the subFinal portion. 1697 tempSubFinal = new ASN1OctetString(buffer.toByteArray()); 1698 } 1699 1700 subInitial = tempSubInitial; 1701 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 1702 subFinal = tempSubFinal; 1703 1704 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1705 { 1706 assertionValue = null; 1707 } 1708 else 1709 { 1710 assertionValue = new ASN1OctetString(buffer.toByteArray()); 1711 } 1712 } 1713 1714 filterType = tempFilterType; 1715 break; 1716 } 1717 1718 1719 if (startPos == 0) 1720 { 1721 return new Filter(filterString, filterType, filterComps, notComp, 1722 attrName, assertionValue, subInitial, subAny, subFinal, 1723 matchingRuleID, dnAttributes); 1724 } 1725 else 1726 { 1727 return new Filter(filterString.substring(startPos, endPos+1), filterType, 1728 filterComps, notComp, attrName, assertionValue, 1729 subInitial, subAny, subFinal, matchingRuleID, 1730 dnAttributes); 1731 } 1732 } 1733 1734 1735 1736 /** 1737 * Parses the specified portion of the provided filter string to obtain a set 1738 * of filter components for use in an AND or OR filter. 1739 * 1740 * @param filterString The string representation for the set of filters. 1741 * @param startPos The position of the first character to consider as 1742 * part of the first filter. 1743 * @param endPos The position of the last character to consider as 1744 * part of the last filter. 1745 * @param depth The current nesting depth for this filter. It should 1746 * be increased by one for each AND, OR, or NOT filter 1747 * encountered, in order to prevent stack overflow 1748 * errors from excessive recursion. 1749 * 1750 * @return The decoded set of search filters. 1751 * 1752 * @throws LDAPException If the provided string cannot be decoded as a set 1753 * of LDAP search filters. 1754 */ 1755 private static Filter[] parseFilterComps(final String filterString, 1756 final int startPos, final int endPos, 1757 final int depth) 1758 throws LDAPException 1759 { 1760 if (startPos > endPos) 1761 { 1762 // This is acceptable, since it can represent an LDAP TRUE or FALSE filter 1763 // as described in RFC 4526. 1764 return NO_FILTERS; 1765 } 1766 1767 1768 // The set of filters must start with an opening parenthesis, and end with a 1769 // closing parenthesis. 1770 if (filterString.charAt(startPos) != '(') 1771 { 1772 throw new LDAPException(ResultCode.FILTER_ERROR, 1773 ERR_FILTER_EXPECTED_OPEN_PAREN.get(filterString, startPos)); 1774 } 1775 if (filterString.charAt(endPos) != ')') 1776 { 1777 throw new LDAPException(ResultCode.FILTER_ERROR, 1778 ERR_FILTER_EXPECTED_CLOSE_PAREN.get(filterString, startPos)); 1779 } 1780 1781 1782 // Iterate through the specified portion of the filter string and count 1783 // opening and closing parentheses to figure out where one filter ends and 1784 // another begins. 1785 final ArrayList<Filter> filterList = new ArrayList<Filter>(5); 1786 int filterStartPos = startPos; 1787 int pos = startPos; 1788 int numOpen = 0; 1789 while (pos <= endPos) 1790 { 1791 final char c = filterString.charAt(pos++); 1792 if (c == '(') 1793 { 1794 numOpen++; 1795 } 1796 else if (c == ')') 1797 { 1798 numOpen--; 1799 if (numOpen == 0) 1800 { 1801 filterList.add(create(filterString, filterStartPos, pos-1, depth)); 1802 filterStartPos = pos; 1803 } 1804 } 1805 } 1806 1807 if (numOpen != 0) 1808 { 1809 throw new LDAPException(ResultCode.FILTER_ERROR, 1810 ERR_FILTER_MISMATCHED_PARENS.get(filterString, startPos, endPos)); 1811 } 1812 1813 return filterList.toArray(new Filter[filterList.size()]); 1814 } 1815 1816 1817 1818 /** 1819 * Reads one or more hex-encoded bytes from the specified portion of the 1820 * filter string. 1821 * 1822 * @param filterString The string from which the data is to be read. 1823 * @param startPos The position at which to start reading. This should 1824 * be the position of first hex character immediately 1825 * after the initial backslash. 1826 * @param buffer The buffer to which the decoded string portion should 1827 * be appended. 1828 * 1829 * @return The position at which the caller may resume parsing. 1830 * 1831 * @throws LDAPException If a problem occurs while reading hex-encoded 1832 * bytes. 1833 */ 1834 private static int readEscapedHexString(final String filterString, 1835 final int startPos, 1836 final ByteStringBuffer buffer) 1837 throws LDAPException 1838 { 1839 final byte b; 1840 switch (filterString.charAt(startPos)) 1841 { 1842 case '0': 1843 b = 0x00; 1844 break; 1845 case '1': 1846 b = 0x10; 1847 break; 1848 case '2': 1849 b = 0x20; 1850 break; 1851 case '3': 1852 b = 0x30; 1853 break; 1854 case '4': 1855 b = 0x40; 1856 break; 1857 case '5': 1858 b = 0x50; 1859 break; 1860 case '6': 1861 b = 0x60; 1862 break; 1863 case '7': 1864 b = 0x70; 1865 break; 1866 case '8': 1867 b = (byte) 0x80; 1868 break; 1869 case '9': 1870 b = (byte) 0x90; 1871 break; 1872 case 'a': 1873 case 'A': 1874 b = (byte) 0xA0; 1875 break; 1876 case 'b': 1877 case 'B': 1878 b = (byte) 0xB0; 1879 break; 1880 case 'c': 1881 case 'C': 1882 b = (byte) 0xC0; 1883 break; 1884 case 'd': 1885 case 'D': 1886 b = (byte) 0xD0; 1887 break; 1888 case 'e': 1889 case 'E': 1890 b = (byte) 0xE0; 1891 break; 1892 case 'f': 1893 case 'F': 1894 b = (byte) 0xF0; 1895 break; 1896 default: 1897 throw new LDAPException(ResultCode.FILTER_ERROR, 1898 ERR_FILTER_INVALID_HEX_CHAR.get(filterString, 1899 filterString.charAt(startPos), startPos)); 1900 } 1901 1902 switch (filterString.charAt(startPos+1)) 1903 { 1904 case '0': 1905 buffer.append(b); 1906 break; 1907 case '1': 1908 buffer.append((byte) (b | 0x01)); 1909 break; 1910 case '2': 1911 buffer.append((byte) (b | 0x02)); 1912 break; 1913 case '3': 1914 buffer.append((byte) (b | 0x03)); 1915 break; 1916 case '4': 1917 buffer.append((byte) (b | 0x04)); 1918 break; 1919 case '5': 1920 buffer.append((byte) (b | 0x05)); 1921 break; 1922 case '6': 1923 buffer.append((byte) (b | 0x06)); 1924 break; 1925 case '7': 1926 buffer.append((byte) (b | 0x07)); 1927 break; 1928 case '8': 1929 buffer.append((byte) (b | 0x08)); 1930 break; 1931 case '9': 1932 buffer.append((byte) (b | 0x09)); 1933 break; 1934 case 'a': 1935 case 'A': 1936 buffer.append((byte) (b | 0x0A)); 1937 break; 1938 case 'b': 1939 case 'B': 1940 buffer.append((byte) (b | 0x0B)); 1941 break; 1942 case 'c': 1943 case 'C': 1944 buffer.append((byte) (b | 0x0C)); 1945 break; 1946 case 'd': 1947 case 'D': 1948 buffer.append((byte) (b | 0x0D)); 1949 break; 1950 case 'e': 1951 case 'E': 1952 buffer.append((byte) (b | 0x0E)); 1953 break; 1954 case 'f': 1955 case 'F': 1956 buffer.append((byte) (b | 0x0F)); 1957 break; 1958 default: 1959 throw new LDAPException(ResultCode.FILTER_ERROR, 1960 ERR_FILTER_INVALID_HEX_CHAR.get(filterString, 1961 filterString.charAt(startPos+1), (startPos+1))); 1962 } 1963 1964 return startPos+2; 1965 } 1966 1967 1968 1969 /** 1970 * Writes an ASN.1-encoded representation of this filter to the provided ASN.1 1971 * buffer. 1972 * 1973 * @param buffer The ASN.1 buffer to which the encoded representation should 1974 * be written. 1975 */ 1976 public void writeTo(final ASN1Buffer buffer) 1977 { 1978 switch (filterType) 1979 { 1980 case FILTER_TYPE_AND: 1981 case FILTER_TYPE_OR: 1982 final ASN1BufferSet compSet = buffer.beginSet(filterType); 1983 for (final Filter f : filterComps) 1984 { 1985 f.writeTo(buffer); 1986 } 1987 compSet.end(); 1988 break; 1989 1990 case FILTER_TYPE_NOT: 1991 buffer.addElement( 1992 new ASN1Element(filterType, notComp.encode().encode())); 1993 break; 1994 1995 case FILTER_TYPE_EQUALITY: 1996 case FILTER_TYPE_GREATER_OR_EQUAL: 1997 case FILTER_TYPE_LESS_OR_EQUAL: 1998 case FILTER_TYPE_APPROXIMATE_MATCH: 1999 final ASN1BufferSequence avaSequence = buffer.beginSequence(filterType); 2000 buffer.addOctetString(attrName); 2001 buffer.addElement(assertionValue); 2002 avaSequence.end(); 2003 break; 2004 2005 case FILTER_TYPE_SUBSTRING: 2006 final ASN1BufferSequence subFilterSequence = 2007 buffer.beginSequence(filterType); 2008 buffer.addOctetString(attrName); 2009 2010 final ASN1BufferSequence valueSequence = buffer.beginSequence(); 2011 if (subInitial != null) 2012 { 2013 buffer.addOctetString(SUBSTRING_TYPE_SUBINITIAL, 2014 subInitial.getValue()); 2015 } 2016 2017 for (final ASN1OctetString s : subAny) 2018 { 2019 buffer.addOctetString(SUBSTRING_TYPE_SUBANY, s.getValue()); 2020 } 2021 2022 if (subFinal != null) 2023 { 2024 buffer.addOctetString(SUBSTRING_TYPE_SUBFINAL, subFinal.getValue()); 2025 } 2026 valueSequence.end(); 2027 subFilterSequence.end(); 2028 break; 2029 2030 case FILTER_TYPE_PRESENCE: 2031 buffer.addOctetString(filterType, attrName); 2032 break; 2033 2034 case FILTER_TYPE_EXTENSIBLE_MATCH: 2035 final ASN1BufferSequence mrSequence = buffer.beginSequence(filterType); 2036 if (matchingRuleID != null) 2037 { 2038 buffer.addOctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID, 2039 matchingRuleID); 2040 } 2041 2042 if (attrName != null) 2043 { 2044 buffer.addOctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName); 2045 } 2046 2047 buffer.addOctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2048 assertionValue.getValue()); 2049 2050 if (dnAttributes) 2051 { 2052 buffer.addBoolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, true); 2053 } 2054 mrSequence.end(); 2055 break; 2056 } 2057 } 2058 2059 2060 2061 /** 2062 * Encodes this search filter to an ASN.1 element suitable for inclusion in an 2063 * LDAP search request protocol op. 2064 * 2065 * @return An ASN.1 element containing the encoded search filter. 2066 */ 2067 public ASN1Element encode() 2068 { 2069 switch (filterType) 2070 { 2071 case FILTER_TYPE_AND: 2072 case FILTER_TYPE_OR: 2073 final ASN1Element[] filterElements = 2074 new ASN1Element[filterComps.length]; 2075 for (int i=0; i < filterComps.length; i++) 2076 { 2077 filterElements[i] = filterComps[i].encode(); 2078 } 2079 return new ASN1Set(filterType, filterElements); 2080 2081 2082 case FILTER_TYPE_NOT: 2083 return new ASN1Element(filterType, notComp.encode().encode()); 2084 2085 2086 case FILTER_TYPE_EQUALITY: 2087 case FILTER_TYPE_GREATER_OR_EQUAL: 2088 case FILTER_TYPE_LESS_OR_EQUAL: 2089 case FILTER_TYPE_APPROXIMATE_MATCH: 2090 final ASN1OctetString[] attrValueAssertionElements = 2091 { 2092 new ASN1OctetString(attrName), 2093 assertionValue 2094 }; 2095 return new ASN1Sequence(filterType, attrValueAssertionElements); 2096 2097 2098 case FILTER_TYPE_SUBSTRING: 2099 final ArrayList<ASN1OctetString> subList = 2100 new ArrayList<ASN1OctetString>(2 + subAny.length); 2101 if (subInitial != null) 2102 { 2103 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, 2104 subInitial.getValue())); 2105 } 2106 2107 for (final ASN1Element subAnyElement : subAny) 2108 { 2109 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBANY, 2110 subAnyElement.getValue())); 2111 } 2112 2113 2114 if (subFinal != null) 2115 { 2116 subList.add(new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, 2117 subFinal.getValue())); 2118 } 2119 2120 final ASN1Element[] subFilterElements = 2121 { 2122 new ASN1OctetString(attrName), 2123 new ASN1Sequence(subList) 2124 }; 2125 return new ASN1Sequence(filterType, subFilterElements); 2126 2127 2128 case FILTER_TYPE_PRESENCE: 2129 return new ASN1OctetString(filterType, attrName); 2130 2131 2132 case FILTER_TYPE_EXTENSIBLE_MATCH: 2133 final ArrayList<ASN1Element> emElementList = 2134 new ArrayList<ASN1Element>(4); 2135 if (matchingRuleID != null) 2136 { 2137 emElementList.add(new ASN1OctetString( 2138 EXTENSIBLE_TYPE_MATCHING_RULE_ID, matchingRuleID)); 2139 } 2140 2141 if (attrName != null) 2142 { 2143 emElementList.add(new ASN1OctetString( 2144 EXTENSIBLE_TYPE_ATTRIBUTE_NAME, attrName)); 2145 } 2146 2147 emElementList.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, 2148 assertionValue.getValue())); 2149 2150 if (dnAttributes) 2151 { 2152 emElementList.add(new ASN1Boolean(EXTENSIBLE_TYPE_DN_ATTRIBUTES, 2153 true)); 2154 } 2155 2156 return new ASN1Sequence(filterType, emElementList); 2157 2158 2159 default: 2160 throw new AssertionError(ERR_FILTER_INVALID_TYPE.get( 2161 toHex(filterType))); 2162 } 2163 } 2164 2165 2166 2167 /** 2168 * Reads and decodes a search filter from the provided ASN.1 stream reader. 2169 * 2170 * @param reader The ASN.1 stream reader from which to read the filter. 2171 * 2172 * @return The decoded search filter. 2173 * 2174 * @throws LDAPException If an error occurs while reading or parsing the 2175 * search filter. 2176 */ 2177 public static Filter readFrom(final ASN1StreamReader reader) 2178 throws LDAPException 2179 { 2180 try 2181 { 2182 final Filter[] filterComps; 2183 final Filter notComp; 2184 final String attrName; 2185 final ASN1OctetString assertionValue; 2186 final ASN1OctetString subInitial; 2187 final ASN1OctetString[] subAny; 2188 final ASN1OctetString subFinal; 2189 final String matchingRuleID; 2190 final boolean dnAttributes; 2191 2192 final byte filterType = (byte) reader.peek(); 2193 2194 switch (filterType) 2195 { 2196 case FILTER_TYPE_AND: 2197 case FILTER_TYPE_OR: 2198 final ArrayList<Filter> comps = new ArrayList<Filter>(5); 2199 final ASN1StreamReaderSet elementSet = reader.beginSet(); 2200 while (elementSet.hasMoreElements()) 2201 { 2202 comps.add(readFrom(reader)); 2203 } 2204 2205 filterComps = new Filter[comps.size()]; 2206 comps.toArray(filterComps); 2207 2208 notComp = null; 2209 attrName = null; 2210 assertionValue = null; 2211 subInitial = null; 2212 subAny = NO_SUB_ANY; 2213 subFinal = null; 2214 matchingRuleID = null; 2215 dnAttributes = false; 2216 break; 2217 2218 2219 case FILTER_TYPE_NOT: 2220 final ASN1Element notFilterElement; 2221 try 2222 { 2223 final ASN1Element e = reader.readElement(); 2224 notFilterElement = ASN1Element.decode(e.getValue()); 2225 } 2226 catch (final ASN1Exception ae) 2227 { 2228 debugException(ae); 2229 throw new LDAPException(ResultCode.DECODING_ERROR, 2230 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)), 2231 ae); 2232 } 2233 notComp = decode(notFilterElement); 2234 2235 filterComps = NO_FILTERS; 2236 attrName = null; 2237 assertionValue = null; 2238 subInitial = null; 2239 subAny = NO_SUB_ANY; 2240 subFinal = null; 2241 matchingRuleID = null; 2242 dnAttributes = false; 2243 break; 2244 2245 2246 case FILTER_TYPE_EQUALITY: 2247 case FILTER_TYPE_GREATER_OR_EQUAL: 2248 case FILTER_TYPE_LESS_OR_EQUAL: 2249 case FILTER_TYPE_APPROXIMATE_MATCH: 2250 reader.beginSequence(); 2251 attrName = reader.readString(); 2252 assertionValue = new ASN1OctetString(reader.readBytes()); 2253 2254 filterComps = NO_FILTERS; 2255 notComp = null; 2256 subInitial = null; 2257 subAny = NO_SUB_ANY; 2258 subFinal = null; 2259 matchingRuleID = null; 2260 dnAttributes = false; 2261 break; 2262 2263 2264 case FILTER_TYPE_SUBSTRING: 2265 reader.beginSequence(); 2266 attrName = reader.readString(); 2267 2268 ASN1OctetString tempSubInitial = null; 2269 ASN1OctetString tempSubFinal = null; 2270 final ArrayList<ASN1OctetString> subAnyList = 2271 new ArrayList<ASN1OctetString>(1); 2272 final ASN1StreamReaderSequence subSequence = reader.beginSequence(); 2273 while (subSequence.hasMoreElements()) 2274 { 2275 final byte type = (byte) reader.peek(); 2276 final ASN1OctetString s = 2277 new ASN1OctetString(type, reader.readBytes()); 2278 switch (type) 2279 { 2280 case SUBSTRING_TYPE_SUBINITIAL: 2281 tempSubInitial = s; 2282 break; 2283 case SUBSTRING_TYPE_SUBANY: 2284 subAnyList.add(s); 2285 break; 2286 case SUBSTRING_TYPE_SUBFINAL: 2287 tempSubFinal = s; 2288 break; 2289 default: 2290 throw new LDAPException(ResultCode.DECODING_ERROR, 2291 ERR_FILTER_INVALID_SUBSTR_TYPE.get(toHex(type))); 2292 } 2293 } 2294 2295 subInitial = tempSubInitial; 2296 subFinal = tempSubFinal; 2297 2298 subAny = new ASN1OctetString[subAnyList.size()]; 2299 subAnyList.toArray(subAny); 2300 2301 filterComps = NO_FILTERS; 2302 notComp = null; 2303 assertionValue = null; 2304 matchingRuleID = null; 2305 dnAttributes = false; 2306 break; 2307 2308 2309 case FILTER_TYPE_PRESENCE: 2310 attrName = reader.readString(); 2311 2312 filterComps = NO_FILTERS; 2313 notComp = null; 2314 assertionValue = null; 2315 subInitial = null; 2316 subAny = NO_SUB_ANY; 2317 subFinal = null; 2318 matchingRuleID = null; 2319 dnAttributes = false; 2320 break; 2321 2322 2323 case FILTER_TYPE_EXTENSIBLE_MATCH: 2324 String tempAttrName = null; 2325 ASN1OctetString tempAssertionValue = null; 2326 String tempMatchingRuleID = null; 2327 boolean tempDNAttributes = false; 2328 2329 final ASN1StreamReaderSequence emSequence = reader.beginSequence(); 2330 while (emSequence.hasMoreElements()) 2331 { 2332 final byte type = (byte) reader.peek(); 2333 switch (type) 2334 { 2335 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2336 tempAttrName = reader.readString(); 2337 break; 2338 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2339 tempMatchingRuleID = reader.readString(); 2340 break; 2341 case EXTENSIBLE_TYPE_MATCH_VALUE: 2342 tempAssertionValue = 2343 new ASN1OctetString(type, reader.readBytes()); 2344 break; 2345 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2346 tempDNAttributes = reader.readBoolean(); 2347 break; 2348 default: 2349 throw new LDAPException(ResultCode.DECODING_ERROR, 2350 ERR_FILTER_EXTMATCH_INVALID_TYPE.get(toHex(type))); 2351 } 2352 } 2353 2354 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2355 { 2356 throw new LDAPException(ResultCode.DECODING_ERROR, 2357 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2358 } 2359 2360 if (tempAssertionValue == null) 2361 { 2362 throw new LDAPException(ResultCode.DECODING_ERROR, 2363 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2364 } 2365 2366 attrName = tempAttrName; 2367 assertionValue = tempAssertionValue; 2368 matchingRuleID = tempMatchingRuleID; 2369 dnAttributes = tempDNAttributes; 2370 2371 filterComps = NO_FILTERS; 2372 notComp = null; 2373 subInitial = null; 2374 subAny = NO_SUB_ANY; 2375 subFinal = null; 2376 break; 2377 2378 2379 default: 2380 throw new LDAPException(ResultCode.DECODING_ERROR, 2381 ERR_FILTER_ELEMENT_INVALID_TYPE.get(toHex(filterType))); 2382 } 2383 2384 return new Filter(null, filterType, filterComps, notComp, attrName, 2385 assertionValue, subInitial, subAny, subFinal, 2386 matchingRuleID, dnAttributes); 2387 } 2388 catch (final LDAPException le) 2389 { 2390 debugException(le); 2391 throw le; 2392 } 2393 catch (final Exception e) 2394 { 2395 debugException(e); 2396 throw new LDAPException(ResultCode.DECODING_ERROR, 2397 ERR_FILTER_CANNOT_DECODE.get(getExceptionMessage(e)), e); 2398 } 2399 } 2400 2401 2402 2403 /** 2404 * Decodes the provided ASN.1 element as a search filter. 2405 * 2406 * @param filterElement The ASN.1 element containing the encoded search 2407 * filter. 2408 * 2409 * @return The decoded search filter. 2410 * 2411 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 2412 * a search filter. 2413 */ 2414 public static Filter decode(final ASN1Element filterElement) 2415 throws LDAPException 2416 { 2417 final byte filterType = filterElement.getType(); 2418 final Filter[] filterComps; 2419 final Filter notComp; 2420 final String attrName; 2421 final ASN1OctetString assertionValue; 2422 final ASN1OctetString subInitial; 2423 final ASN1OctetString[] subAny; 2424 final ASN1OctetString subFinal; 2425 final String matchingRuleID; 2426 final boolean dnAttributes; 2427 2428 switch (filterType) 2429 { 2430 case FILTER_TYPE_AND: 2431 case FILTER_TYPE_OR: 2432 notComp = null; 2433 attrName = null; 2434 assertionValue = null; 2435 subInitial = null; 2436 subAny = NO_SUB_ANY; 2437 subFinal = null; 2438 matchingRuleID = null; 2439 dnAttributes = false; 2440 2441 final ASN1Set compSet; 2442 try 2443 { 2444 compSet = ASN1Set.decodeAsSet(filterElement); 2445 } 2446 catch (final ASN1Exception ae) 2447 { 2448 debugException(ae); 2449 throw new LDAPException(ResultCode.DECODING_ERROR, 2450 ERR_FILTER_CANNOT_DECODE_COMPS.get(getExceptionMessage(ae)), ae); 2451 } 2452 2453 final ASN1Element[] compElements = compSet.elements(); 2454 filterComps = new Filter[compElements.length]; 2455 for (int i=0; i < compElements.length; i++) 2456 { 2457 filterComps[i] = decode(compElements[i]); 2458 } 2459 break; 2460 2461 2462 case FILTER_TYPE_NOT: 2463 filterComps = NO_FILTERS; 2464 attrName = null; 2465 assertionValue = null; 2466 subInitial = null; 2467 subAny = NO_SUB_ANY; 2468 subFinal = null; 2469 matchingRuleID = null; 2470 dnAttributes = false; 2471 2472 final ASN1Element notFilterElement; 2473 try 2474 { 2475 notFilterElement = ASN1Element.decode(filterElement.getValue()); 2476 } 2477 catch (final ASN1Exception ae) 2478 { 2479 debugException(ae); 2480 throw new LDAPException(ResultCode.DECODING_ERROR, 2481 ERR_FILTER_CANNOT_DECODE_NOT_COMP.get(getExceptionMessage(ae)), 2482 ae); 2483 } 2484 notComp = decode(notFilterElement); 2485 break; 2486 2487 2488 2489 case FILTER_TYPE_EQUALITY: 2490 case FILTER_TYPE_GREATER_OR_EQUAL: 2491 case FILTER_TYPE_LESS_OR_EQUAL: 2492 case FILTER_TYPE_APPROXIMATE_MATCH: 2493 filterComps = NO_FILTERS; 2494 notComp = null; 2495 subInitial = null; 2496 subAny = NO_SUB_ANY; 2497 subFinal = null; 2498 matchingRuleID = null; 2499 dnAttributes = false; 2500 2501 final ASN1Sequence avaSequence; 2502 try 2503 { 2504 avaSequence = ASN1Sequence.decodeAsSequence(filterElement); 2505 } 2506 catch (final ASN1Exception ae) 2507 { 2508 debugException(ae); 2509 throw new LDAPException(ResultCode.DECODING_ERROR, 2510 ERR_FILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(ae)), ae); 2511 } 2512 2513 final ASN1Element[] avaElements = avaSequence.elements(); 2514 if (avaElements.length != 2) 2515 { 2516 throw new LDAPException(ResultCode.DECODING_ERROR, 2517 ERR_FILTER_INVALID_AVA_ELEMENT_COUNT.get( 2518 avaElements.length)); 2519 } 2520 2521 attrName = 2522 ASN1OctetString.decodeAsOctetString(avaElements[0]).stringValue(); 2523 assertionValue = ASN1OctetString.decodeAsOctetString(avaElements[1]); 2524 break; 2525 2526 2527 case FILTER_TYPE_SUBSTRING: 2528 filterComps = NO_FILTERS; 2529 notComp = null; 2530 assertionValue = null; 2531 matchingRuleID = null; 2532 dnAttributes = false; 2533 2534 final ASN1Sequence subFilterSequence; 2535 try 2536 { 2537 subFilterSequence = ASN1Sequence.decodeAsSequence(filterElement); 2538 } 2539 catch (final ASN1Exception ae) 2540 { 2541 debugException(ae); 2542 throw new LDAPException(ResultCode.DECODING_ERROR, 2543 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)), 2544 ae); 2545 } 2546 2547 final ASN1Element[] subFilterElements = subFilterSequence.elements(); 2548 if (subFilterElements.length != 2) 2549 { 2550 throw new LDAPException(ResultCode.DECODING_ERROR, 2551 ERR_FILTER_INVALID_SUBSTR_ASSERTION_COUNT.get( 2552 subFilterElements.length)); 2553 } 2554 2555 attrName = ASN1OctetString.decodeAsOctetString( 2556 subFilterElements[0]).stringValue(); 2557 2558 final ASN1Sequence subSequence; 2559 try 2560 { 2561 subSequence = ASN1Sequence.decodeAsSequence(subFilterElements[1]); 2562 } 2563 catch (final ASN1Exception ae) 2564 { 2565 debugException(ae); 2566 throw new LDAPException(ResultCode.DECODING_ERROR, 2567 ERR_FILTER_CANNOT_DECODE_SUBSTRING.get(getExceptionMessage(ae)), 2568 ae); 2569 } 2570 2571 ASN1OctetString tempSubInitial = null; 2572 ASN1OctetString tempSubFinal = null; 2573 final ArrayList<ASN1OctetString> subAnyList = 2574 new ArrayList<ASN1OctetString>(1); 2575 2576 final ASN1Element[] subElements = subSequence.elements(); 2577 for (final ASN1Element subElement : subElements) 2578 { 2579 switch (subElement.getType()) 2580 { 2581 case SUBSTRING_TYPE_SUBINITIAL: 2582 if (tempSubInitial == null) 2583 { 2584 tempSubInitial = 2585 ASN1OctetString.decodeAsOctetString(subElement); 2586 } 2587 else 2588 { 2589 throw new LDAPException(ResultCode.DECODING_ERROR, 2590 ERR_FILTER_MULTIPLE_SUBINITIAL.get()); 2591 } 2592 break; 2593 2594 case SUBSTRING_TYPE_SUBANY: 2595 subAnyList.add(ASN1OctetString.decodeAsOctetString(subElement)); 2596 break; 2597 2598 case SUBSTRING_TYPE_SUBFINAL: 2599 if (tempSubFinal == null) 2600 { 2601 tempSubFinal = ASN1OctetString.decodeAsOctetString(subElement); 2602 } 2603 else 2604 { 2605 throw new LDAPException(ResultCode.DECODING_ERROR, 2606 ERR_FILTER_MULTIPLE_SUBFINAL.get()); 2607 } 2608 break; 2609 2610 default: 2611 throw new LDAPException(ResultCode.DECODING_ERROR, 2612 ERR_FILTER_INVALID_SUBSTR_TYPE.get( 2613 toHex(subElement.getType()))); 2614 } 2615 } 2616 2617 subInitial = tempSubInitial; 2618 subAny = subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 2619 subFinal = tempSubFinal; 2620 break; 2621 2622 2623 case FILTER_TYPE_PRESENCE: 2624 filterComps = NO_FILTERS; 2625 notComp = null; 2626 assertionValue = null; 2627 subInitial = null; 2628 subAny = NO_SUB_ANY; 2629 subFinal = null; 2630 matchingRuleID = null; 2631 dnAttributes = false; 2632 attrName = 2633 ASN1OctetString.decodeAsOctetString(filterElement).stringValue(); 2634 break; 2635 2636 2637 case FILTER_TYPE_EXTENSIBLE_MATCH: 2638 filterComps = NO_FILTERS; 2639 notComp = null; 2640 subInitial = null; 2641 subAny = NO_SUB_ANY; 2642 subFinal = null; 2643 2644 final ASN1Sequence emSequence; 2645 try 2646 { 2647 emSequence = ASN1Sequence.decodeAsSequence(filterElement); 2648 } 2649 catch (final ASN1Exception ae) 2650 { 2651 debugException(ae); 2652 throw new LDAPException(ResultCode.DECODING_ERROR, 2653 ERR_FILTER_CANNOT_DECODE_EXTMATCH.get(getExceptionMessage(ae)), 2654 ae); 2655 } 2656 2657 String tempAttrName = null; 2658 ASN1OctetString tempAssertionValue = null; 2659 String tempMatchingRuleID = null; 2660 boolean tempDNAttributes = false; 2661 for (final ASN1Element e : emSequence.elements()) 2662 { 2663 switch (e.getType()) 2664 { 2665 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 2666 if (tempAttrName == null) 2667 { 2668 tempAttrName = 2669 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2670 } 2671 else 2672 { 2673 throw new LDAPException(ResultCode.DECODING_ERROR, 2674 ERR_FILTER_EXTMATCH_MULTIPLE_ATTRS.get()); 2675 } 2676 break; 2677 2678 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 2679 if (tempMatchingRuleID == null) 2680 { 2681 tempMatchingRuleID = 2682 ASN1OctetString.decodeAsOctetString(e).stringValue(); 2683 } 2684 else 2685 { 2686 throw new LDAPException(ResultCode.DECODING_ERROR, 2687 ERR_FILTER_EXTMATCH_MULTIPLE_MRIDS.get()); 2688 } 2689 break; 2690 2691 case EXTENSIBLE_TYPE_MATCH_VALUE: 2692 if (tempAssertionValue == null) 2693 { 2694 tempAssertionValue = ASN1OctetString.decodeAsOctetString(e); 2695 } 2696 else 2697 { 2698 throw new LDAPException(ResultCode.DECODING_ERROR, 2699 ERR_FILTER_EXTMATCH_MULTIPLE_VALUES.get()); 2700 } 2701 break; 2702 2703 case EXTENSIBLE_TYPE_DN_ATTRIBUTES: 2704 try 2705 { 2706 if (tempDNAttributes) 2707 { 2708 throw new LDAPException(ResultCode.DECODING_ERROR, 2709 ERR_FILTER_EXTMATCH_MULTIPLE_DNATTRS.get()); 2710 } 2711 else 2712 { 2713 tempDNAttributes = 2714 ASN1Boolean.decodeAsBoolean(e).booleanValue(); 2715 } 2716 } 2717 catch (final ASN1Exception ae) 2718 { 2719 debugException(ae); 2720 throw new LDAPException(ResultCode.DECODING_ERROR, 2721 ERR_FILTER_EXTMATCH_DNATTRS_NOT_BOOLEAN.get( 2722 getExceptionMessage(ae)), 2723 ae); 2724 } 2725 break; 2726 2727 default: 2728 throw new LDAPException(ResultCode.DECODING_ERROR, 2729 ERR_FILTER_EXTMATCH_INVALID_TYPE.get( 2730 toHex(e.getType()))); 2731 } 2732 } 2733 2734 if ((tempAttrName == null) && (tempMatchingRuleID == null)) 2735 { 2736 throw new LDAPException(ResultCode.DECODING_ERROR, 2737 ERR_FILTER_EXTMATCH_NO_ATTR_OR_MRID.get()); 2738 } 2739 2740 if (tempAssertionValue == null) 2741 { 2742 throw new LDAPException(ResultCode.DECODING_ERROR, 2743 ERR_FILTER_EXTMATCH_NO_VALUE.get()); 2744 } 2745 2746 attrName = tempAttrName; 2747 assertionValue = tempAssertionValue; 2748 matchingRuleID = tempMatchingRuleID; 2749 dnAttributes = tempDNAttributes; 2750 break; 2751 2752 2753 default: 2754 throw new LDAPException(ResultCode.DECODING_ERROR, 2755 ERR_FILTER_ELEMENT_INVALID_TYPE.get( 2756 toHex(filterElement.getType()))); 2757 } 2758 2759 2760 return new Filter(null, filterType, filterComps, notComp, attrName, 2761 assertionValue, subInitial, subAny, subFinal, 2762 matchingRuleID, dnAttributes); 2763 } 2764 2765 2766 2767 /** 2768 * Retrieves the filter type for this filter. 2769 * 2770 * @return The filter type for this filter. 2771 */ 2772 public byte getFilterType() 2773 { 2774 return filterType; 2775 } 2776 2777 2778 2779 /** 2780 * Retrieves the set of filter components used in this AND or OR filter. This 2781 * is not applicable for any other filter type. 2782 * 2783 * @return The set of filter components used in this AND or OR filter, or an 2784 * empty array if this is some other type of filter or if there are 2785 * no components (i.e., as in an LDAP TRUE or LDAP FALSE filter). 2786 */ 2787 public Filter[] getComponents() 2788 { 2789 return filterComps; 2790 } 2791 2792 2793 2794 /** 2795 * Retrieves the filter component used in this NOT filter. This is not 2796 * applicable for any other filter type. 2797 * 2798 * @return The filter component used in this NOT filter, or {@code null} if 2799 * this is some other type of filter. 2800 */ 2801 public Filter getNOTComponent() 2802 { 2803 return notComp; 2804 } 2805 2806 2807 2808 /** 2809 * Retrieves the name of the attribute type for this search filter. This is 2810 * applicable for the following types of filters: 2811 * <UL> 2812 * <LI>Equality</LI> 2813 * <LI>Substring</LI> 2814 * <LI>Greater or Equal</LI> 2815 * <LI>Less or Equal</LI> 2816 * <LI>Presence</LI> 2817 * <LI>Approximate Match</LI> 2818 * <LI>Extensible Match</LI> 2819 * </UL> 2820 * 2821 * @return The name of the attribute type for this search filter, or 2822 * {@code null} if it is not applicable for this type of filter. 2823 */ 2824 public String getAttributeName() 2825 { 2826 return attrName; 2827 } 2828 2829 2830 2831 /** 2832 * Retrieves the string representation of the assertion value for this search 2833 * filter. This is applicable for the following types of filters: 2834 * <UL> 2835 * <LI>Equality</LI> 2836 * <LI>Greater or Equal</LI> 2837 * <LI>Less or Equal</LI> 2838 * <LI>Approximate Match</LI> 2839 * <LI>Extensible Match</LI> 2840 * </UL> 2841 * 2842 * @return The string representation of the assertion value for this search 2843 * filter, or {@code null} if it is not applicable for this type of 2844 * filter. 2845 */ 2846 public String getAssertionValue() 2847 { 2848 if (assertionValue == null) 2849 { 2850 return null; 2851 } 2852 else 2853 { 2854 return assertionValue.stringValue(); 2855 } 2856 } 2857 2858 2859 2860 /** 2861 * Retrieves the binary representation of the assertion value for this search 2862 * filter. This is applicable for the following types of filters: 2863 * <UL> 2864 * <LI>Equality</LI> 2865 * <LI>Greater or Equal</LI> 2866 * <LI>Less or Equal</LI> 2867 * <LI>Approximate Match</LI> 2868 * <LI>Extensible Match</LI> 2869 * </UL> 2870 * 2871 * @return The binary representation of the assertion value for this search 2872 * filter, or {@code null} if it is not applicable for this type of 2873 * filter. 2874 */ 2875 public byte[] getAssertionValueBytes() 2876 { 2877 if (assertionValue == null) 2878 { 2879 return null; 2880 } 2881 else 2882 { 2883 return assertionValue.getValue(); 2884 } 2885 } 2886 2887 2888 2889 /** 2890 * Retrieves the raw assertion value for this search filter as an ASN.1 2891 * octet string. This is applicable for the following types of filters: 2892 * <UL> 2893 * <LI>Equality</LI> 2894 * <LI>Greater or Equal</LI> 2895 * <LI>Less or Equal</LI> 2896 * <LI>Approximate Match</LI> 2897 * <LI>Extensible Match</LI> 2898 * </UL> 2899 * 2900 * @return The raw assertion value for this search filter as an ASN.1 octet 2901 * string, or {@code null} if it is not applicable for this type of 2902 * filter. 2903 */ 2904 public ASN1OctetString getRawAssertionValue() 2905 { 2906 return assertionValue; 2907 } 2908 2909 2910 2911 /** 2912 * Retrieves the string representation of the subInitial element for this 2913 * substring filter. This is not applicable for any other filter type. 2914 * 2915 * @return The string representation of the subInitial element for this 2916 * substring filter, or {@code null} if this is some other type of 2917 * filter, or if it is a substring filter with no subInitial element. 2918 */ 2919 public String getSubInitialString() 2920 { 2921 if (subInitial == null) 2922 { 2923 return null; 2924 } 2925 else 2926 { 2927 return subInitial.stringValue(); 2928 } 2929 } 2930 2931 2932 2933 /** 2934 * Retrieves the binary representation of the subInitial element for this 2935 * substring filter. This is not applicable for any other filter type. 2936 * 2937 * @return The binary representation of the subInitial element for this 2938 * substring filter, or {@code null} if this is some other type of 2939 * filter, or if it is a substring filter with no subInitial element. 2940 */ 2941 public byte[] getSubInitialBytes() 2942 { 2943 if (subInitial == null) 2944 { 2945 return null; 2946 } 2947 else 2948 { 2949 return subInitial.getValue(); 2950 } 2951 } 2952 2953 2954 2955 /** 2956 * Retrieves the raw subInitial element for this filter as an ASN.1 octet 2957 * string. This is not applicable for any other filter type. 2958 * 2959 * @return The raw subInitial element for this filter as an ASN.1 octet 2960 * string, or {@code null} if this is not a substring filter, or if 2961 * it is a substring filter with no subInitial element. 2962 */ 2963 public ASN1OctetString getRawSubInitialValue() 2964 { 2965 return subInitial; 2966 } 2967 2968 2969 2970 /** 2971 * Retrieves the string representations of the subAny elements for this 2972 * substring filter. This is not applicable for any other filter type. 2973 * 2974 * @return The string representations of the subAny elements for this 2975 * substring filter, or an empty array if this is some other type of 2976 * filter, or if it is a substring filter with no subFinal element. 2977 */ 2978 public String[] getSubAnyStrings() 2979 { 2980 final String[] subAnyStrings = new String[subAny.length]; 2981 for (int i=0; i < subAny.length; i++) 2982 { 2983 subAnyStrings[i] = subAny[i].stringValue(); 2984 } 2985 2986 return subAnyStrings; 2987 } 2988 2989 2990 2991 /** 2992 * Retrieves the binary representations of the subAny elements for this 2993 * substring filter. This is not applicable for any other filter type. 2994 * 2995 * @return The binary representations of the subAny elements for this 2996 * substring filter, or an empty array if this is some other type of 2997 * filter, or if it is a substring filter with no subFinal element. 2998 */ 2999 public byte[][] getSubAnyBytes() 3000 { 3001 final byte[][] subAnyBytes = new byte[subAny.length][]; 3002 for (int i=0; i < subAny.length; i++) 3003 { 3004 subAnyBytes[i] = subAny[i].getValue(); 3005 } 3006 3007 return subAnyBytes; 3008 } 3009 3010 3011 3012 /** 3013 * Retrieves the raw subAny values for this substring filter. This is not 3014 * applicable for any other filter type. 3015 * 3016 * @return The raw subAny values for this substring filter, or an empty array 3017 * if this is some other type of filter, or if it is a substring 3018 * filter with no subFinal element. 3019 */ 3020 public ASN1OctetString[] getRawSubAnyValues() 3021 { 3022 return subAny; 3023 } 3024 3025 3026 3027 /** 3028 * Retrieves the string representation of the subFinal element for this 3029 * substring filter. This is not applicable for any other filter type. 3030 * 3031 * @return The string representation of the subFinal element for this 3032 * substring filter, or {@code null} if this is some other type of 3033 * filter, or if it is a substring filter with no subFinal element. 3034 */ 3035 public String getSubFinalString() 3036 { 3037 if (subFinal == null) 3038 { 3039 return null; 3040 } 3041 else 3042 { 3043 return subFinal.stringValue(); 3044 } 3045 } 3046 3047 3048 3049 /** 3050 * Retrieves the binary representation of the subFinal element for this 3051 * substring filter. This is not applicable for any other filter type. 3052 * 3053 * @return The binary representation of the subFinal element for this 3054 * substring filter, or {@code null} if this is some other type of 3055 * filter, or if it is a substring filter with no subFinal element. 3056 */ 3057 public byte[] getSubFinalBytes() 3058 { 3059 if (subFinal == null) 3060 { 3061 return null; 3062 } 3063 else 3064 { 3065 return subFinal.getValue(); 3066 } 3067 } 3068 3069 3070 3071 /** 3072 * Retrieves the raw subFinal element for this filter as an ASN.1 octet 3073 * string. This is not applicable for any other filter type. 3074 * 3075 * @return The raw subFinal element for this filter as an ASN.1 octet 3076 * string, or {@code null} if this is not a substring filter, or if 3077 * it is a substring filter with no subFinal element. 3078 */ 3079 public ASN1OctetString getRawSubFinalValue() 3080 { 3081 return subFinal; 3082 } 3083 3084 3085 3086 /** 3087 * Retrieves the matching rule ID for this extensible match filter. This is 3088 * not applicable for any other filter type. 3089 * 3090 * @return The matching rule ID for this extensible match filter, or 3091 * {@code null} if this is some other type of filter, or if this 3092 * extensible match filter does not have a matching rule ID. 3093 */ 3094 public String getMatchingRuleID() 3095 { 3096 return matchingRuleID; 3097 } 3098 3099 3100 3101 /** 3102 * Retrieves the dnAttributes flag for this extensible match filter. This is 3103 * not applicable for any other filter type. 3104 * 3105 * @return The dnAttributes flag for this extensible match filter. 3106 */ 3107 public boolean getDNAttributes() 3108 { 3109 return dnAttributes; 3110 } 3111 3112 3113 3114 /** 3115 * Indicates whether this filter matches the provided entry. Note that this 3116 * is a best-guess effort and may not be completely accurate in all cases. 3117 * All matching will be performed using case-ignore string matching, which may 3118 * yield an unexpected result for values that should not be treated as simple 3119 * strings. For example: 3120 * <UL> 3121 * <LI>Two DN values which are logically equivalent may not be considered 3122 * matches if they have different spacing.</LI> 3123 * <LI>Ordering comparisons against numeric values may yield unexpected 3124 * results (e.g., "2" will be considered greater than "10" because the 3125 * character "2" has a larger ASCII value than the character "1").</LI> 3126 * </UL> 3127 * <BR> 3128 * In addition to the above constraints, it should be noted that neither 3129 * approximate matching nor extensible matching are currently supported. 3130 * 3131 * @param entry The entry for which to make the determination. It must not 3132 * be {@code null}. 3133 * 3134 * @return {@code true} if this filter appears to match the provided entry, 3135 * or {@code false} if not. 3136 * 3137 * @throws LDAPException If a problem occurs while trying to make the 3138 * determination. 3139 */ 3140 public boolean matchesEntry(final Entry entry) 3141 throws LDAPException 3142 { 3143 return matchesEntry(entry, entry.getSchema()); 3144 } 3145 3146 3147 3148 /** 3149 * Indicates whether this filter matches the provided entry. Note that this 3150 * is a best-guess effort and may not be completely accurate in all cases. 3151 * If provided, the given schema will be used in an attempt to determine the 3152 * appropriate matching rule for making the determinations, but some corner 3153 * cases may not be handled accurately. Neither approximate matching nor 3154 * extensible matching are currently supported. 3155 * 3156 * @param entry The entry for which to make the determination. It must not 3157 * be {@code null}. 3158 * @param schema The schema to use when making the determination. If this 3159 * is {@code null}, then all matching will be performed using 3160 * a case-ignore matching rule. 3161 * 3162 * @return {@code true} if this filter appears to match the provided entry, 3163 * or {@code false} if not. 3164 * 3165 * @throws LDAPException If a problem occurs while trying to make the 3166 * determination. 3167 */ 3168 public boolean matchesEntry(final Entry entry, final Schema schema) 3169 throws LDAPException 3170 { 3171 ensureNotNull(entry); 3172 3173 switch (filterType) 3174 { 3175 case FILTER_TYPE_AND: 3176 for (final Filter f : filterComps) 3177 { 3178 if (! f.matchesEntry(entry, schema)) 3179 { 3180 return false; 3181 } 3182 } 3183 return true; 3184 3185 case FILTER_TYPE_OR: 3186 for (final Filter f : filterComps) 3187 { 3188 if (f.matchesEntry(entry, schema)) 3189 { 3190 return true; 3191 } 3192 } 3193 return false; 3194 3195 case FILTER_TYPE_NOT: 3196 return (! notComp.matchesEntry(entry, schema)); 3197 3198 case FILTER_TYPE_EQUALITY: 3199 Attribute a = entry.getAttribute(attrName, schema); 3200 if (a == null) 3201 { 3202 return false; 3203 } 3204 3205 MatchingRule matchingRule = 3206 MatchingRule.selectEqualityMatchingRule(attrName, schema); 3207 for (final ASN1OctetString v : a.getRawValues()) 3208 { 3209 if (matchingRule.valuesMatch(v, assertionValue)) 3210 { 3211 return true; 3212 } 3213 } 3214 return false; 3215 3216 case FILTER_TYPE_SUBSTRING: 3217 a = entry.getAttribute(attrName, schema); 3218 if (a == null) 3219 { 3220 return false; 3221 } 3222 3223 matchingRule = 3224 MatchingRule.selectSubstringMatchingRule(attrName, schema); 3225 for (final ASN1OctetString v : a.getRawValues()) 3226 { 3227 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal)) 3228 { 3229 return true; 3230 } 3231 } 3232 return false; 3233 3234 case FILTER_TYPE_GREATER_OR_EQUAL: 3235 a = entry.getAttribute(attrName, schema); 3236 if (a == null) 3237 { 3238 return false; 3239 } 3240 3241 matchingRule = 3242 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3243 for (final ASN1OctetString v : a.getRawValues()) 3244 { 3245 if (matchingRule.compareValues(v, assertionValue) >= 0) 3246 { 3247 return true; 3248 } 3249 } 3250 return false; 3251 3252 case FILTER_TYPE_LESS_OR_EQUAL: 3253 a = entry.getAttribute(attrName, schema); 3254 if (a == null) 3255 { 3256 return false; 3257 } 3258 3259 matchingRule = 3260 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3261 for (final ASN1OctetString v : a.getRawValues()) 3262 { 3263 if (matchingRule.compareValues(v, assertionValue) <= 0) 3264 { 3265 return true; 3266 } 3267 } 3268 return false; 3269 3270 case FILTER_TYPE_PRESENCE: 3271 return (entry.hasAttribute(attrName)); 3272 3273 case FILTER_TYPE_APPROXIMATE_MATCH: 3274 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3275 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get()); 3276 3277 case FILTER_TYPE_EXTENSIBLE_MATCH: 3278 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3279 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get()); 3280 3281 default: 3282 throw new LDAPException(ResultCode.PARAM_ERROR, 3283 ERR_FILTER_INVALID_TYPE.get()); 3284 } 3285 } 3286 3287 3288 3289 /** 3290 * Attempts to simplify the provided filter to allow it to be more efficiently 3291 * processed by the server. The simplifications it will make include: 3292 * <UL> 3293 * <LI>Any AND or OR filter that contains only a single filter component 3294 * will be converted to just that embedded filter component to eliminate 3295 * the unnecessary AND or OR wrapper. For example, the filter 3296 * "(&(uid=john.doe))" will be converted to just 3297 * "(uid=john.doe)".</LI> 3298 * <LI>Any AND components inside of an AND filter will be merged into the 3299 * outer AND filter. Any OR components inside of an OR filter will be 3300 * merged into the outer OR filter. For example, the filter 3301 * "(&(objectClass=person)(&(givenName=John)(sn=Doe)))" will be 3302 * converted to 3303 * "(&(objectClass=person)(givenName=John)(sn=Doe))".</LI> 3304 * <LI>If {@code reOrderElements} is true, then this method will attempt to 3305 * re-order the elements inside AND and OR filters in an attempt to 3306 * ensure that the components which are likely to be the most efficient 3307 * come earlier than those which are likely to be the least efficient. 3308 * This can speed up processing in servers that process filter 3309 * components in a left-to-right order.</LI> 3310 * </UL> 3311 * <BR><BR> 3312 * The simplification will happen recursively, in an attempt to generate a 3313 * filter that is as simple and efficient as possible. 3314 * 3315 * @param filter The filter to attempt to simplify. 3316 * @param reOrderElements Indicates whether this method may re-order the 3317 * elements in the filter so that, in a server that 3318 * evaluates the components in a left-to-right order, 3319 * the components which are likely to be more 3320 * efficient to process will be listed before those 3321 * which are likely to be less efficient. 3322 * 3323 * @return The simplified filter, or the original filter if the provided 3324 * filter is not one that can be simplified any further. 3325 */ 3326 public static Filter simplifyFilter(final Filter filter, 3327 final boolean reOrderElements) 3328 { 3329 final byte filterType = filter.filterType; 3330 switch (filterType) 3331 { 3332 case FILTER_TYPE_AND: 3333 case FILTER_TYPE_OR: 3334 // These will be handled below. 3335 break; 3336 3337 case FILTER_TYPE_NOT: 3338 // We may be able to simplify the filter component contained inside the 3339 // NOT. 3340 return createNOTFilter(simplifyFilter(filter.notComp, reOrderElements)); 3341 3342 default: 3343 // We can't simplify this filter, so just return what was provided. 3344 return filter; 3345 } 3346 3347 3348 // An AND filter with zero components is an LDAP true filter, and we can't 3349 // simplify that. An OR filter with zero components is an LDAP false 3350 // filter, and we can't simplify that either. The set of components 3351 // should never be null for an AND or OR filter, but if that happens to be 3352 // the case, then we'll return the original filter. 3353 final Filter[] components = filter.filterComps; 3354 if ((components == null) || (components.length == 0)) 3355 { 3356 return filter; 3357 } 3358 3359 3360 // For either an AND or an OR filter with just a single component, then just 3361 // return that embedded component. But simplify it first. 3362 if (components.length == 1) 3363 { 3364 return simplifyFilter(components[0], reOrderElements); 3365 } 3366 3367 3368 // If we've gotten here, then we have a filter with multiple components. 3369 // Simplify each of them to the extent possible, un-embed any ANDs 3370 // contained inside an AND or ORs contained inside an OR, and eliminate any 3371 // duplicate components in the resulting top-level filter. 3372 final LinkedHashSet<Filter> componentSet = new LinkedHashSet<Filter>(10); 3373 for (final Filter f : components) 3374 { 3375 final Filter simplifiedFilter = simplifyFilter(f, reOrderElements); 3376 if (simplifiedFilter.filterType == FILTER_TYPE_AND) 3377 { 3378 if (filterType == FILTER_TYPE_AND) 3379 { 3380 // This is an AND nested inside an AND. In that case, we'll just put 3381 // all the nested components inside the outer AND. 3382 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3383 } 3384 else 3385 { 3386 componentSet.add(simplifiedFilter); 3387 } 3388 } 3389 else if (simplifiedFilter.filterType == FILTER_TYPE_OR) 3390 { 3391 if (filterType == FILTER_TYPE_OR) 3392 { 3393 // This is an OR nested inside an OR. In that case, we'll just put 3394 // all the nested components inside the outer OR. 3395 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3396 } 3397 else 3398 { 3399 componentSet.add(simplifiedFilter); 3400 } 3401 } 3402 else 3403 { 3404 componentSet.add(simplifiedFilter); 3405 } 3406 } 3407 3408 3409 // It's possible at this point that we are down to just a single component. 3410 // That can happen if the filter was an AND or an OR with a duplicate 3411 // element, like "(&(a=b)(a=b))". In that case, just return that one 3412 // component. 3413 if (componentSet.size() == 1) 3414 { 3415 return componentSet.iterator().next(); 3416 } 3417 3418 3419 // If we should re-order the components, then use the following priority 3420 // list: 3421 // 3422 // 1. Equality components that target an attribute other than objectClass. 3423 // These are most likely to require only a single database lookup to get 3424 // the candidate list, and that candidate list will frequently be small. 3425 // 2. Equality components that target the objectClass attribute. These are 3426 // likely to require only a single database lookup to get the candidate 3427 // list, but the candidate list is more likely to be larger. 3428 // 3. Approximate match components. These are also likely to require only 3429 // a single database lookup to get the candidate list, but that 3430 // candidate list is likely to have a larger number of candidates. 3431 // 4. Presence components that target an attribute other than objectClass. 3432 // These are also likely to require only a single database lookup to get 3433 // the candidate list, but are likely to have a large number of 3434 // candidates. 3435 // 5. Substring components that have a subInitial element. These are 3436 // generally the most efficient substring filters to process, requiring 3437 // access to fewer database keys than substring filters with only subAny 3438 // and/or subFinal components. 3439 // 6. Substring components that only have subAny and/or subFinal elements. 3440 // These will probably require a number of database lookups and will 3441 // probably result in large candidate lists. 3442 // 7. Greater-or-equal components and less-or-equal components. These 3443 // will probably require a number of database lookups and will probably 3444 // result in large candidate lists. 3445 // 8. Extensible match components. Even if these are indexed, there isn't 3446 // any good way to know how expensive they might be to process or how 3447 // big the candidate list might be. 3448 // 9. Presence components that target the objectClass attribute. This is 3449 // likely to require only a single database lookup to get the candidate 3450 // list, but the candidate list will also be extremely large (if it's 3451 // indexed at all) since it will match every entry. 3452 // 10. NOT components. These are generally not possible to index and 3453 // therefore cannot be used to create a candidate list. 3454 // 3455 // AND and OR components will be ordered according to the first of their 3456 // embedded components Since the filter has already been simplified, then 3457 // the first element in the list will be the one we think will be the most 3458 // efficient to process. 3459 if (reOrderElements) 3460 { 3461 final TreeMap<Integer,LinkedHashSet<Filter>> m = 3462 new TreeMap<Integer,LinkedHashSet<Filter>>(); 3463 for (final Filter f : componentSet) 3464 { 3465 final Filter prioritizeComp; 3466 if ((f.filterType == FILTER_TYPE_AND) || 3467 (f.filterType == FILTER_TYPE_OR)) 3468 { 3469 if (f.filterComps.length > 0) 3470 { 3471 prioritizeComp = f.filterComps[0]; 3472 } 3473 else 3474 { 3475 prioritizeComp = f; 3476 } 3477 } 3478 else 3479 { 3480 prioritizeComp = f; 3481 } 3482 3483 final Integer slot; 3484 switch (prioritizeComp.filterType) 3485 { 3486 case FILTER_TYPE_EQUALITY: 3487 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3488 { 3489 slot = 2; 3490 } 3491 else 3492 { 3493 slot = 1; 3494 } 3495 break; 3496 3497 case FILTER_TYPE_APPROXIMATE_MATCH: 3498 slot = 3; 3499 break; 3500 3501 case FILTER_TYPE_PRESENCE: 3502 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3503 { 3504 slot = 9; 3505 } 3506 else 3507 { 3508 slot = 4; 3509 } 3510 break; 3511 3512 case FILTER_TYPE_SUBSTRING: 3513 if (prioritizeComp.subInitial == null) 3514 { 3515 slot = 6; 3516 } 3517 else 3518 { 3519 slot = 5; 3520 } 3521 break; 3522 3523 case FILTER_TYPE_GREATER_OR_EQUAL: 3524 case FILTER_TYPE_LESS_OR_EQUAL: 3525 slot = 7; 3526 break; 3527 3528 case FILTER_TYPE_EXTENSIBLE_MATCH: 3529 slot = 8; 3530 break; 3531 3532 case FILTER_TYPE_NOT: 3533 default: 3534 slot = 10; 3535 break; 3536 } 3537 3538 LinkedHashSet<Filter> filterSet = m.get(slot-1); 3539 if (filterSet == null) 3540 { 3541 filterSet = new LinkedHashSet<Filter>(10); 3542 m.put(slot-1, filterSet); 3543 } 3544 filterSet.add(f); 3545 } 3546 3547 componentSet.clear(); 3548 for (final LinkedHashSet<Filter> filterSet : m.values()) 3549 { 3550 componentSet.addAll(filterSet); 3551 } 3552 } 3553 3554 3555 // Return the new, possibly simplified filter. 3556 if (filterType == FILTER_TYPE_AND) 3557 { 3558 return createANDFilter(componentSet); 3559 } 3560 else 3561 { 3562 return createORFilter(componentSet); 3563 } 3564 } 3565 3566 3567 3568 /** 3569 * Generates a hash code for this search filter. 3570 * 3571 * @return The generated hash code for this search filter. 3572 */ 3573 @Override() 3574 public int hashCode() 3575 { 3576 final CaseIgnoreStringMatchingRule matchingRule = 3577 CaseIgnoreStringMatchingRule.getInstance(); 3578 int hashCode = filterType; 3579 3580 switch (filterType) 3581 { 3582 case FILTER_TYPE_AND: 3583 case FILTER_TYPE_OR: 3584 for (final Filter f : filterComps) 3585 { 3586 hashCode += f.hashCode(); 3587 } 3588 break; 3589 3590 case FILTER_TYPE_NOT: 3591 hashCode += notComp.hashCode(); 3592 break; 3593 3594 case FILTER_TYPE_EQUALITY: 3595 case FILTER_TYPE_GREATER_OR_EQUAL: 3596 case FILTER_TYPE_LESS_OR_EQUAL: 3597 case FILTER_TYPE_APPROXIMATE_MATCH: 3598 hashCode += toLowerCase(attrName).hashCode(); 3599 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3600 break; 3601 3602 case FILTER_TYPE_SUBSTRING: 3603 hashCode += toLowerCase(attrName).hashCode(); 3604 if (subInitial != null) 3605 { 3606 hashCode += matchingRule.normalizeSubstring(subInitial, 3607 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode(); 3608 } 3609 for (final ASN1OctetString s : subAny) 3610 { 3611 hashCode += matchingRule.normalizeSubstring(s, 3612 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode(); 3613 } 3614 if (subFinal != null) 3615 { 3616 hashCode += matchingRule.normalizeSubstring(subFinal, 3617 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode(); 3618 } 3619 break; 3620 3621 case FILTER_TYPE_PRESENCE: 3622 hashCode += toLowerCase(attrName).hashCode(); 3623 break; 3624 3625 case FILTER_TYPE_EXTENSIBLE_MATCH: 3626 if (attrName != null) 3627 { 3628 hashCode += toLowerCase(attrName).hashCode(); 3629 } 3630 3631 if (matchingRuleID != null) 3632 { 3633 hashCode += toLowerCase(matchingRuleID).hashCode(); 3634 } 3635 3636 if (dnAttributes) 3637 { 3638 hashCode++; 3639 } 3640 3641 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3642 break; 3643 } 3644 3645 return hashCode; 3646 } 3647 3648 3649 3650 /** 3651 * Indicates whether the provided object is equal to this search filter. 3652 * 3653 * @param o The object for which to make the determination. 3654 * 3655 * @return {@code true} if the provided object can be considered equal to 3656 * this search filter, or {@code false} if not. 3657 */ 3658 @Override() 3659 public boolean equals(final Object o) 3660 { 3661 if (o == null) 3662 { 3663 return false; 3664 } 3665 3666 if (o == this) 3667 { 3668 return true; 3669 } 3670 3671 if (! (o instanceof Filter)) 3672 { 3673 return false; 3674 } 3675 3676 final Filter f = (Filter) o; 3677 if (filterType != f.filterType) 3678 { 3679 return false; 3680 } 3681 3682 final CaseIgnoreStringMatchingRule matchingRule = 3683 CaseIgnoreStringMatchingRule.getInstance(); 3684 3685 switch (filterType) 3686 { 3687 case FILTER_TYPE_AND: 3688 case FILTER_TYPE_OR: 3689 if (filterComps.length != f.filterComps.length) 3690 { 3691 return false; 3692 } 3693 3694 final HashSet<Filter> compSet = new HashSet<Filter>(); 3695 compSet.addAll(Arrays.asList(filterComps)); 3696 3697 for (final Filter filterComp : f.filterComps) 3698 { 3699 if (! compSet.remove(filterComp)) 3700 { 3701 return false; 3702 } 3703 } 3704 3705 return true; 3706 3707 3708 case FILTER_TYPE_NOT: 3709 return notComp.equals(f.notComp); 3710 3711 3712 case FILTER_TYPE_EQUALITY: 3713 case FILTER_TYPE_GREATER_OR_EQUAL: 3714 case FILTER_TYPE_LESS_OR_EQUAL: 3715 case FILTER_TYPE_APPROXIMATE_MATCH: 3716 return (attrName.equalsIgnoreCase(f.attrName) && 3717 matchingRule.valuesMatch(assertionValue, f.assertionValue)); 3718 3719 3720 case FILTER_TYPE_SUBSTRING: 3721 if (! attrName.equalsIgnoreCase(f.attrName)) 3722 { 3723 return false; 3724 } 3725 3726 if (subAny.length != f.subAny.length) 3727 { 3728 return false; 3729 } 3730 3731 if (subInitial == null) 3732 { 3733 if (f.subInitial != null) 3734 { 3735 return false; 3736 } 3737 } 3738 else 3739 { 3740 if (f.subInitial == null) 3741 { 3742 return false; 3743 } 3744 3745 final ASN1OctetString si1 = matchingRule.normalizeSubstring( 3746 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3747 final ASN1OctetString si2 = matchingRule.normalizeSubstring( 3748 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3749 if (! si1.equals(si2)) 3750 { 3751 return false; 3752 } 3753 } 3754 3755 for (int i=0; i < subAny.length; i++) 3756 { 3757 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i], 3758 MatchingRule.SUBSTRING_TYPE_SUBANY); 3759 final ASN1OctetString sa2 = matchingRule.normalizeSubstring( 3760 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY); 3761 if (! sa1.equals(sa2)) 3762 { 3763 return false; 3764 } 3765 } 3766 3767 if (subFinal == null) 3768 { 3769 if (f.subFinal != null) 3770 { 3771 return false; 3772 } 3773 } 3774 else 3775 { 3776 if (f.subFinal == null) 3777 { 3778 return false; 3779 } 3780 3781 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal, 3782 MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3783 final ASN1OctetString sf2 = matchingRule.normalizeSubstring( 3784 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3785 if (! sf1.equals(sf2)) 3786 { 3787 return false; 3788 } 3789 } 3790 3791 return true; 3792 3793 3794 case FILTER_TYPE_PRESENCE: 3795 return (attrName.equalsIgnoreCase(f.attrName)); 3796 3797 3798 case FILTER_TYPE_EXTENSIBLE_MATCH: 3799 if (attrName == null) 3800 { 3801 if (f.attrName != null) 3802 { 3803 return false; 3804 } 3805 } 3806 else 3807 { 3808 if (f.attrName == null) 3809 { 3810 return false; 3811 } 3812 else 3813 { 3814 if (! attrName.equalsIgnoreCase(f.attrName)) 3815 { 3816 return false; 3817 } 3818 } 3819 } 3820 3821 if (matchingRuleID == null) 3822 { 3823 if (f.matchingRuleID != null) 3824 { 3825 return false; 3826 } 3827 } 3828 else 3829 { 3830 if (f.matchingRuleID == null) 3831 { 3832 return false; 3833 } 3834 else 3835 { 3836 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID)) 3837 { 3838 return false; 3839 } 3840 } 3841 } 3842 3843 if (dnAttributes != f.dnAttributes) 3844 { 3845 return false; 3846 } 3847 3848 return matchingRule.valuesMatch(assertionValue, f.assertionValue); 3849 3850 3851 default: 3852 return false; 3853 } 3854 } 3855 3856 3857 3858 /** 3859 * Retrieves a string representation of this search filter. 3860 * 3861 * @return A string representation of this search filter. 3862 */ 3863 @Override() 3864 public String toString() 3865 { 3866 if (filterString == null) 3867 { 3868 final StringBuilder buffer = new StringBuilder(); 3869 toString(buffer); 3870 filterString = buffer.toString(); 3871 } 3872 3873 return filterString; 3874 } 3875 3876 3877 3878 /** 3879 * Appends a string representation of this search filter to the provided 3880 * buffer. 3881 * 3882 * @param buffer The buffer to which to append a string representation of 3883 * this search filter. 3884 */ 3885 public void toString(final StringBuilder buffer) 3886 { 3887 switch (filterType) 3888 { 3889 case FILTER_TYPE_AND: 3890 buffer.append("(&"); 3891 for (final Filter f : filterComps) 3892 { 3893 f.toString(buffer); 3894 } 3895 buffer.append(')'); 3896 break; 3897 3898 case FILTER_TYPE_OR: 3899 buffer.append("(|"); 3900 for (final Filter f : filterComps) 3901 { 3902 f.toString(buffer); 3903 } 3904 buffer.append(')'); 3905 break; 3906 3907 case FILTER_TYPE_NOT: 3908 buffer.append("(!"); 3909 notComp.toString(buffer); 3910 buffer.append(')'); 3911 break; 3912 3913 case FILTER_TYPE_EQUALITY: 3914 buffer.append('('); 3915 buffer.append(attrName); 3916 buffer.append('='); 3917 encodeValue(assertionValue, buffer); 3918 buffer.append(')'); 3919 break; 3920 3921 case FILTER_TYPE_SUBSTRING: 3922 buffer.append('('); 3923 buffer.append(attrName); 3924 buffer.append('='); 3925 if (subInitial != null) 3926 { 3927 encodeValue(subInitial, buffer); 3928 } 3929 buffer.append('*'); 3930 for (final ASN1OctetString s : subAny) 3931 { 3932 encodeValue(s, buffer); 3933 buffer.append('*'); 3934 } 3935 if (subFinal != null) 3936 { 3937 encodeValue(subFinal, buffer); 3938 } 3939 buffer.append(')'); 3940 break; 3941 3942 case FILTER_TYPE_GREATER_OR_EQUAL: 3943 buffer.append('('); 3944 buffer.append(attrName); 3945 buffer.append(">="); 3946 encodeValue(assertionValue, buffer); 3947 buffer.append(')'); 3948 break; 3949 3950 case FILTER_TYPE_LESS_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_PRESENCE: 3959 buffer.append('('); 3960 buffer.append(attrName); 3961 buffer.append("=*)"); 3962 break; 3963 3964 case FILTER_TYPE_APPROXIMATE_MATCH: 3965 buffer.append('('); 3966 buffer.append(attrName); 3967 buffer.append("~="); 3968 encodeValue(assertionValue, buffer); 3969 buffer.append(')'); 3970 break; 3971 3972 case FILTER_TYPE_EXTENSIBLE_MATCH: 3973 buffer.append('('); 3974 if (attrName != null) 3975 { 3976 buffer.append(attrName); 3977 } 3978 3979 if (dnAttributes) 3980 { 3981 buffer.append(":dn"); 3982 } 3983 3984 if (matchingRuleID != null) 3985 { 3986 buffer.append(':'); 3987 buffer.append(matchingRuleID); 3988 } 3989 3990 buffer.append(":="); 3991 encodeValue(assertionValue, buffer); 3992 buffer.append(')'); 3993 break; 3994 } 3995 } 3996 3997 3998 3999 /** 4000 * Retrieves a normalized string representation of this search filter. 4001 * 4002 * @return A normalized string representation of this search filter. 4003 */ 4004 public String toNormalizedString() 4005 { 4006 if (normalizedString == null) 4007 { 4008 final StringBuilder buffer = new StringBuilder(); 4009 toNormalizedString(buffer); 4010 normalizedString = buffer.toString(); 4011 } 4012 4013 return normalizedString; 4014 } 4015 4016 4017 4018 /** 4019 * Appends a normalized string representation of this search filter to the 4020 * provided buffer. 4021 * 4022 * @param buffer The buffer to which to append a normalized string 4023 * representation of this search filter. 4024 */ 4025 public void toNormalizedString(final StringBuilder buffer) 4026 { 4027 final CaseIgnoreStringMatchingRule mr = 4028 CaseIgnoreStringMatchingRule.getInstance(); 4029 4030 switch (filterType) 4031 { 4032 case FILTER_TYPE_AND: 4033 buffer.append("(&"); 4034 for (final Filter f : filterComps) 4035 { 4036 f.toNormalizedString(buffer); 4037 } 4038 buffer.append(')'); 4039 break; 4040 4041 case FILTER_TYPE_OR: 4042 buffer.append("(|"); 4043 for (final Filter f : filterComps) 4044 { 4045 f.toNormalizedString(buffer); 4046 } 4047 buffer.append(')'); 4048 break; 4049 4050 case FILTER_TYPE_NOT: 4051 buffer.append("(!"); 4052 notComp.toNormalizedString(buffer); 4053 buffer.append(')'); 4054 break; 4055 4056 case FILTER_TYPE_EQUALITY: 4057 buffer.append('('); 4058 buffer.append(toLowerCase(attrName)); 4059 buffer.append('='); 4060 encodeValue(mr.normalize(assertionValue), buffer); 4061 buffer.append(')'); 4062 break; 4063 4064 case FILTER_TYPE_SUBSTRING: 4065 buffer.append('('); 4066 buffer.append(toLowerCase(attrName)); 4067 buffer.append('='); 4068 if (subInitial != null) 4069 { 4070 encodeValue(mr.normalizeSubstring(subInitial, 4071 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer); 4072 } 4073 buffer.append('*'); 4074 for (final ASN1OctetString s : subAny) 4075 { 4076 encodeValue(mr.normalizeSubstring(s, 4077 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer); 4078 buffer.append('*'); 4079 } 4080 if (subFinal != null) 4081 { 4082 encodeValue(mr.normalizeSubstring(subFinal, 4083 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer); 4084 } 4085 buffer.append(')'); 4086 break; 4087 4088 case FILTER_TYPE_GREATER_OR_EQUAL: 4089 buffer.append('('); 4090 buffer.append(toLowerCase(attrName)); 4091 buffer.append(">="); 4092 encodeValue(mr.normalize(assertionValue), buffer); 4093 buffer.append(')'); 4094 break; 4095 4096 case FILTER_TYPE_LESS_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_PRESENCE: 4105 buffer.append('('); 4106 buffer.append(toLowerCase(attrName)); 4107 buffer.append("=*)"); 4108 break; 4109 4110 case FILTER_TYPE_APPROXIMATE_MATCH: 4111 buffer.append('('); 4112 buffer.append(toLowerCase(attrName)); 4113 buffer.append("~="); 4114 encodeValue(mr.normalize(assertionValue), buffer); 4115 buffer.append(')'); 4116 break; 4117 4118 case FILTER_TYPE_EXTENSIBLE_MATCH: 4119 buffer.append('('); 4120 if (attrName != null) 4121 { 4122 buffer.append(toLowerCase(attrName)); 4123 } 4124 4125 if (dnAttributes) 4126 { 4127 buffer.append(":dn"); 4128 } 4129 4130 if (matchingRuleID != null) 4131 { 4132 buffer.append(':'); 4133 buffer.append(toLowerCase(matchingRuleID)); 4134 } 4135 4136 buffer.append(":="); 4137 encodeValue(mr.normalize(assertionValue), buffer); 4138 buffer.append(')'); 4139 break; 4140 } 4141 } 4142 4143 4144 4145 /** 4146 * Encodes the provided value into a form suitable for use as the assertion 4147 * value in the string representation of a search filter. Parentheses, 4148 * asterisks, backslashes, null characters, and any non-ASCII characters will 4149 * be escaped using a backslash before the hexadecimal representation of each 4150 * byte in the character to escape. 4151 * 4152 * @param value The value to be encoded. It must not be {@code null}. 4153 * 4154 * @return The encoded representation of the provided string. 4155 */ 4156 public static String encodeValue(final String value) 4157 { 4158 ensureNotNull(value); 4159 4160 final StringBuilder buffer = new StringBuilder(); 4161 encodeValue(new ASN1OctetString(value), buffer); 4162 return buffer.toString(); 4163 } 4164 4165 4166 4167 /** 4168 * Encodes the provided value into a form suitable for use as the assertion 4169 * value in the string representation of a search filter. Parentheses, 4170 * asterisks, backslashes, null characters, and any non-ASCII characters will 4171 * be escaped using a backslash before the hexadecimal representation of each 4172 * byte in the character to escape. 4173 * 4174 * @param value The value to be encoded. It must not be {@code null}. 4175 * 4176 * @return The encoded representation of the provided string. 4177 */ 4178 public static String encodeValue(final byte[]value) 4179 { 4180 ensureNotNull(value); 4181 4182 final StringBuilder buffer = new StringBuilder(); 4183 encodeValue(new ASN1OctetString(value), buffer); 4184 return buffer.toString(); 4185 } 4186 4187 4188 4189 /** 4190 * Appends the assertion value for this filter to the provided buffer, 4191 * encoding any special characters as necessary. 4192 * 4193 * @param value The value to be encoded. 4194 * @param buffer The buffer to which the assertion value should be appended. 4195 */ 4196 public static void encodeValue(final ASN1OctetString value, 4197 final StringBuilder buffer) 4198 { 4199 final byte[] valueBytes = value.getValue(); 4200 for (int i=0; i < valueBytes.length; i++) 4201 { 4202 switch (numBytesInUTF8CharacterWithFirstByte(valueBytes[i])) 4203 { 4204 case 1: 4205 // This character is ASCII, but might still need to be escaped. We'll 4206 // escape anything 4207 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters. 4208 (valueBytes[i] == 0x28) || // Open parenthesis 4209 (valueBytes[i] == 0x29) || // Close parenthesis 4210 (valueBytes[i] == 0x2A) || // Asterisk 4211 (valueBytes[i] == 0x5C) || // Backslash 4212 (valueBytes[i] == 0x7F)) // DEL 4213 { 4214 buffer.append('\\'); 4215 toHex(valueBytes[i], buffer); 4216 } 4217 else 4218 { 4219 buffer.append((char) valueBytes[i]); 4220 } 4221 break; 4222 4223 case 2: 4224 // If there are at least two bytes left, then we'll hex-encode the 4225 // next two bytes. Otherwise we'll hex-encode whatever is left. 4226 buffer.append('\\'); 4227 toHex(valueBytes[i++], buffer); 4228 if (i < valueBytes.length) 4229 { 4230 buffer.append('\\'); 4231 toHex(valueBytes[i], buffer); 4232 } 4233 break; 4234 4235 case 3: 4236 // If there are at least three bytes left, then we'll hex-encode the 4237 // next three bytes. Otherwise we'll hex-encode whatever is left. 4238 buffer.append('\\'); 4239 toHex(valueBytes[i++], buffer); 4240 if (i < valueBytes.length) 4241 { 4242 buffer.append('\\'); 4243 toHex(valueBytes[i++], buffer); 4244 } 4245 if (i < valueBytes.length) 4246 { 4247 buffer.append('\\'); 4248 toHex(valueBytes[i], buffer); 4249 } 4250 break; 4251 4252 case 4: 4253 // If there are at least four bytes left, then we'll hex-encode the 4254 // next four bytes. Otherwise we'll hex-encode whatever is left. 4255 buffer.append('\\'); 4256 toHex(valueBytes[i++], buffer); 4257 if (i < valueBytes.length) 4258 { 4259 buffer.append('\\'); 4260 toHex(valueBytes[i++], buffer); 4261 } 4262 if (i < valueBytes.length) 4263 { 4264 buffer.append('\\'); 4265 toHex(valueBytes[i++], buffer); 4266 } 4267 if (i < valueBytes.length) 4268 { 4269 buffer.append('\\'); 4270 toHex(valueBytes[i], buffer); 4271 } 4272 break; 4273 4274 default: 4275 // We'll hex-encode whatever is left in the buffer. 4276 while (i < valueBytes.length) 4277 { 4278 buffer.append('\\'); 4279 toHex(valueBytes[i++], buffer); 4280 } 4281 break; 4282 } 4283 } 4284 } 4285 4286 4287 4288 /** 4289 * Appends a number of lines comprising the Java source code that can be used 4290 * to recreate this filter to the given list. Note that unless a first line 4291 * prefix and/or last line suffix are provided, this will just include the 4292 * code for the static method used to create the filter, starting with 4293 * "Filter.createXFilter(" and ending with the closing parenthesis for that 4294 * method call. 4295 * 4296 * @param lineList The list to which the source code lines should be 4297 * added. 4298 * @param indentSpaces The number of spaces that should be used to indent 4299 * the generated code. It must not be negative. 4300 * @param firstLinePrefix An optional string that should precede the static 4301 * method call (e.g., it could be used for an 4302 * attribute assignment, like "Filter f = "). It may 4303 * be {@code null} or empty if there should be no 4304 * first line prefix. 4305 * @param lastLineSuffix An optional suffix that should follow the closing 4306 * parenthesis of the static method call (e.g., it 4307 * could be a semicolon to represent the end of a 4308 * Java statement). It may be {@code null} or empty 4309 * if there should be no last line suffix. 4310 */ 4311 public void toCode(final List<String> lineList, final int indentSpaces, 4312 final String firstLinePrefix, final String lastLineSuffix) 4313 { 4314 // Generate a string with the appropriate indent. 4315 final StringBuilder buffer = new StringBuilder(); 4316 for (int i = 0; i < indentSpaces; i++) 4317 { 4318 buffer.append(' '); 4319 } 4320 final String indent = buffer.toString(); 4321 4322 4323 // Start the first line, including any appropriate prefix. 4324 buffer.setLength(0); 4325 buffer.append(indent); 4326 if (firstLinePrefix != null) 4327 { 4328 buffer.append(firstLinePrefix); 4329 } 4330 4331 4332 // Figure out what type of filter it is and create the appropriate code for 4333 // that type of filter. 4334 switch (filterType) 4335 { 4336 case FILTER_TYPE_AND: 4337 case FILTER_TYPE_OR: 4338 if (filterType == FILTER_TYPE_AND) 4339 { 4340 buffer.append("Filter.createANDFilter("); 4341 } 4342 else 4343 { 4344 buffer.append("Filter.createORFilter("); 4345 } 4346 if (filterComps.length == 0) 4347 { 4348 buffer.append(')'); 4349 if (lastLineSuffix != null) 4350 { 4351 buffer.append(lastLineSuffix); 4352 } 4353 lineList.add(buffer.toString()); 4354 return; 4355 } 4356 4357 for (int i = 0; i < filterComps.length; i++) 4358 { 4359 String suffix; 4360 if (i == (filterComps.length - 1)) 4361 { 4362 suffix = ")"; 4363 if (lastLineSuffix != null) 4364 { 4365 suffix += lastLineSuffix; 4366 } 4367 } 4368 else 4369 { 4370 suffix = ","; 4371 } 4372 4373 filterComps[i].toCode(lineList, indentSpaces + 5, null, suffix); 4374 } 4375 return; 4376 4377 4378 case FILTER_TYPE_NOT: 4379 buffer.append("Filter.createNOTFilter("); 4380 lineList.add(buffer.toString()); 4381 4382 final String suffix; 4383 if (lastLineSuffix == null) 4384 { 4385 suffix = ")"; 4386 } 4387 else 4388 { 4389 suffix = ')' + lastLineSuffix; 4390 } 4391 notComp.toCode(lineList, indentSpaces + 5, null, suffix); 4392 return; 4393 4394 case FILTER_TYPE_PRESENCE: 4395 buffer.append("Filter.createPresenceFilter("); 4396 lineList.add(buffer.toString()); 4397 4398 buffer.setLength(0); 4399 buffer.append(indent); 4400 buffer.append(" \""); 4401 buffer.append(attrName); 4402 buffer.append("\")"); 4403 4404 if (lastLineSuffix != null) 4405 { 4406 buffer.append(lastLineSuffix); 4407 } 4408 4409 lineList.add(buffer.toString()); 4410 return; 4411 4412 4413 case FILTER_TYPE_EQUALITY: 4414 case FILTER_TYPE_GREATER_OR_EQUAL: 4415 case FILTER_TYPE_LESS_OR_EQUAL: 4416 case FILTER_TYPE_APPROXIMATE_MATCH: 4417 if (filterType == FILTER_TYPE_EQUALITY) 4418 { 4419 buffer.append("Filter.createEqualityFilter("); 4420 } 4421 else if (filterType == FILTER_TYPE_GREATER_OR_EQUAL) 4422 { 4423 buffer.append("Filter.createGreaterOrEqualFilter("); 4424 } 4425 else if (filterType == FILTER_TYPE_LESS_OR_EQUAL) 4426 { 4427 buffer.append("Filter.createLessOrEqualFilter("); 4428 } 4429 else 4430 { 4431 buffer.append("Filter.createApproximateMatchFilter("); 4432 } 4433 lineList.add(buffer.toString()); 4434 4435 buffer.setLength(0); 4436 buffer.append(indent); 4437 buffer.append(" \""); 4438 buffer.append(attrName); 4439 buffer.append("\","); 4440 lineList.add(buffer.toString()); 4441 4442 buffer.setLength(0); 4443 buffer.append(indent); 4444 buffer.append(" "); 4445 if (isSensitiveToCodeAttribute(attrName)) 4446 { 4447 buffer.append("\"---redacted-value---\""); 4448 } 4449 else if (isPrintableString(assertionValue.getValue())) 4450 { 4451 buffer.append('"'); 4452 buffer.append(assertionValue.stringValue()); 4453 buffer.append('"'); 4454 } 4455 else 4456 { 4457 byteArrayToCode(assertionValue.getValue(), buffer); 4458 } 4459 4460 buffer.append(')'); 4461 4462 if (lastLineSuffix != null) 4463 { 4464 buffer.append(lastLineSuffix); 4465 } 4466 4467 lineList.add(buffer.toString()); 4468 return; 4469 4470 4471 case FILTER_TYPE_SUBSTRING: 4472 buffer.append("Filter.createSubstringFilter("); 4473 lineList.add(buffer.toString()); 4474 4475 buffer.setLength(0); 4476 buffer.append(indent); 4477 buffer.append(" \""); 4478 buffer.append(attrName); 4479 buffer.append("\","); 4480 lineList.add(buffer.toString()); 4481 4482 final boolean isRedacted = isSensitiveToCodeAttribute(attrName); 4483 boolean isPrintable = true; 4484 if (subInitial != null) 4485 { 4486 isPrintable = isPrintableString(subInitial.getValue()); 4487 } 4488 4489 if (isPrintable && (subAny != null)) 4490 { 4491 for (final ASN1OctetString s : subAny) 4492 { 4493 if (! isPrintableString(s.getValue())) 4494 { 4495 isPrintable = false; 4496 break; 4497 } 4498 } 4499 } 4500 4501 if (isPrintable && (subFinal != null)) 4502 { 4503 isPrintable = isPrintableString(subFinal.getValue()); 4504 } 4505 4506 buffer.setLength(0); 4507 buffer.append(indent); 4508 buffer.append(" "); 4509 if (subInitial == null) 4510 { 4511 buffer.append("null"); 4512 } 4513 else if (isRedacted) 4514 { 4515 buffer.append("\"---redacted-subInitial---\""); 4516 } 4517 else if (isPrintable) 4518 { 4519 buffer.append('"'); 4520 buffer.append(subInitial.stringValue()); 4521 buffer.append('"'); 4522 } 4523 else 4524 { 4525 byteArrayToCode(subInitial.getValue(), buffer); 4526 } 4527 buffer.append(','); 4528 lineList.add(buffer.toString()); 4529 4530 buffer.setLength(0); 4531 buffer.append(indent); 4532 buffer.append(" "); 4533 if ((subAny == null) || (subAny.length == 0)) 4534 { 4535 buffer.append("null,"); 4536 lineList.add(buffer.toString()); 4537 } 4538 else if (isRedacted) 4539 { 4540 buffer.append("new String[]"); 4541 lineList.add(buffer.toString()); 4542 4543 lineList.add(indent + " {"); 4544 4545 for (int i=0; i < subAny.length; i++) 4546 { 4547 buffer.setLength(0); 4548 buffer.append(indent); 4549 buffer.append(" \"---redacted-subAny-"); 4550 buffer.append(i+1); 4551 buffer.append("---\""); 4552 if (i < (subAny.length-1)) 4553 { 4554 buffer.append(','); 4555 } 4556 lineList.add(buffer.toString()); 4557 } 4558 4559 lineList.add(indent + " },"); 4560 } 4561 else if (isPrintable) 4562 { 4563 buffer.append("new String[]"); 4564 lineList.add(buffer.toString()); 4565 4566 lineList.add(indent + " {"); 4567 4568 for (int i=0; i < subAny.length; i++) 4569 { 4570 buffer.setLength(0); 4571 buffer.append(indent); 4572 buffer.append(" \""); 4573 buffer.append(subAny[i].stringValue()); 4574 buffer.append('"'); 4575 if (i < (subAny.length-1)) 4576 { 4577 buffer.append(','); 4578 } 4579 lineList.add(buffer.toString()); 4580 } 4581 4582 lineList.add(indent + " },"); 4583 } 4584 else 4585 { 4586 buffer.append("new String[]"); 4587 lineList.add(buffer.toString()); 4588 4589 lineList.add(indent + " {"); 4590 4591 for (int i=0; i < subAny.length; i++) 4592 { 4593 buffer.setLength(0); 4594 buffer.append(indent); 4595 buffer.append(" "); 4596 byteArrayToCode(subAny[i].getValue(), buffer); 4597 if (i < (subAny.length-1)) 4598 { 4599 buffer.append(','); 4600 } 4601 lineList.add(buffer.toString()); 4602 } 4603 4604 lineList.add(indent + " },"); 4605 } 4606 4607 buffer.setLength(0); 4608 buffer.append(indent); 4609 buffer.append(" "); 4610 if (subFinal == null) 4611 { 4612 buffer.append("null)"); 4613 } 4614 else if (isRedacted) 4615 { 4616 buffer.append("\"---redacted-subFinal---\")"); 4617 } 4618 else if (isPrintable) 4619 { 4620 buffer.append('"'); 4621 buffer.append(subFinal.stringValue()); 4622 buffer.append("\")"); 4623 } 4624 else 4625 { 4626 byteArrayToCode(subFinal.getValue(), buffer); 4627 buffer.append(')'); 4628 } 4629 if (lastLineSuffix != null) 4630 { 4631 buffer.append(lastLineSuffix); 4632 } 4633 lineList.add(buffer.toString()); 4634 return; 4635 4636 4637 case FILTER_TYPE_EXTENSIBLE_MATCH: 4638 buffer.append("Filter.createExtensibleMatchFilter("); 4639 lineList.add(buffer.toString()); 4640 4641 buffer.setLength(0); 4642 buffer.append(indent); 4643 buffer.append(" "); 4644 if (attrName == null) 4645 { 4646 buffer.append("null, // Attribute Description"); 4647 } 4648 else 4649 { 4650 buffer.append('"'); 4651 buffer.append(attrName); 4652 buffer.append("\","); 4653 } 4654 lineList.add(buffer.toString()); 4655 4656 buffer.setLength(0); 4657 buffer.append(indent); 4658 buffer.append(" "); 4659 if (matchingRuleID == null) 4660 { 4661 buffer.append("null, // Matching Rule ID"); 4662 } 4663 else 4664 { 4665 buffer.append('"'); 4666 buffer.append(matchingRuleID); 4667 buffer.append("\","); 4668 } 4669 lineList.add(buffer.toString()); 4670 4671 buffer.setLength(0); 4672 buffer.append(indent); 4673 buffer.append(" "); 4674 buffer.append(dnAttributes); 4675 buffer.append(", // DN Attributes"); 4676 lineList.add(buffer.toString()); 4677 4678 buffer.setLength(0); 4679 buffer.append(indent); 4680 buffer.append(" "); 4681 if ((attrName != null) && isSensitiveToCodeAttribute(attrName)) 4682 { 4683 buffer.append("\"---redacted-value---\")"); 4684 } 4685 else 4686 { 4687 if (isPrintableString(assertionValue.getValue())) 4688 { 4689 buffer.append('"'); 4690 buffer.append(assertionValue.stringValue()); 4691 buffer.append("\")"); 4692 } 4693 else 4694 { 4695 byteArrayToCode(assertionValue.getValue(), buffer); 4696 buffer.append(')'); 4697 } 4698 } 4699 4700 if (lastLineSuffix != null) 4701 { 4702 buffer.append(lastLineSuffix); 4703 } 4704 lineList.add(buffer.toString()); 4705 return; 4706 } 4707 } 4708}