001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.HashSet; 030import java.util.LinkedHashSet; 031import java.util.List; 032import java.util.TreeMap; 033 034import com.unboundid.asn1.ASN1Boolean; 035import com.unboundid.asn1.ASN1Buffer; 036import com.unboundid.asn1.ASN1BufferSequence; 037import com.unboundid.asn1.ASN1BufferSet; 038import com.unboundid.asn1.ASN1Element; 039import com.unboundid.asn1.ASN1Exception; 040import com.unboundid.asn1.ASN1OctetString; 041import com.unboundid.asn1.ASN1Sequence; 042import com.unboundid.asn1.ASN1Set; 043import com.unboundid.asn1.ASN1StreamReader; 044import com.unboundid.asn1.ASN1StreamReaderSequence; 045import com.unboundid.asn1.ASN1StreamReaderSet; 046import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule; 047import com.unboundid.ldap.matchingrules.MatchingRule; 048import com.unboundid.ldap.sdk.schema.Schema; 049import com.unboundid.util.ByteStringBuffer; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.ThreadSafety; 052import com.unboundid.util.ThreadSafetyLevel; 053 054import static com.unboundid.ldap.sdk.LDAPMessages.*; 055import static com.unboundid.util.Debug.*; 056import static com.unboundid.util.StaticUtils.*; 057import static com.unboundid.util.Validator.*; 058 059 060 061/** 062 * This class provides a data structure that represents an LDAP search filter. 063 * It provides methods for creating various types of filters, as well as parsing 064 * a filter from a string. See 065 * <A HREF="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</A> for more 066 * information about representing search filters as strings. 067 * <BR><BR> 068 * The following filter types are defined: 069 * <UL> 070 * <LI><B>AND</B> -- This is used to indicate that a filter should match an 071 * entry only if all of the embedded filter components match that entry. 072 * An AND filter with zero embedded filter components is considered an 073 * LDAP TRUE filter as defined in 074 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will 075 * match any entry. AND filters contain only a set of embedded filter 076 * components, and each of those embedded components can itself be any 077 * type of filter, including an AND, OR, or NOT filter with additional 078 * embedded components.</LI> 079 * <LI><B>OR</B> -- This is used to indicate that a filter should match an 080 * entry only if at least one of the embedded filter components matches 081 * that entry. An OR filter with zero embedded filter components is 082 * considered an LDAP FALSE filter as defined in 083 * <A HREF="http://www.ietf.org/rfc/rfc4526.txt">RFC 4526</A> and will 084 * never match any entry. OR filters contain only a set of embedded 085 * filter components, and each of those embedded components can itself be 086 * any type of filter, including an AND, OR, or NOT filter with additional 087 * embedded components.</LI> 088 * <LI><B>NOT</B> -- This is used to indicate that a filter should match an 089 * entry only if the embedded NOT component does not match the entry. A 090 * NOT filter contains only a single embedded NOT filter component, but 091 * that embedded component can itself be any type of filter, including an 092 * AND, OR, or NOT filter with additional embedded components.</LI> 093 * <LI><B>EQUALITY</B> -- This is used to indicate that a filter should match 094 * an entry only if the entry contains a value for the specified attribute 095 * that is equal to the provided assertion value. An equality filter 096 * contains only an attribute name and an assertion value.</LI> 097 * <LI><B>SUBSTRING</B> -- This is used to indicate that a filter should match 098 * an entry only if the entry contains at least one value for the 099 * specified attribute that matches the provided substring assertion. The 100 * substring assertion must contain at least one element of the following 101 * types: 102 * <UL> 103 * <LI>subInitial -- This indicates that the specified string must 104 * appear at the beginning of the attribute value. There can be at 105 * most one subInitial element in a substring assertion.</LI> 106 * <LI>subAny -- This indicates that the specified string may appear 107 * anywhere in the attribute value. There can be any number of 108 * substring subAny elements in a substring assertion. If there are 109 * multiple subAny elements, then they must match in the order that 110 * they are provided.</LI> 111 * <LI>subFinal -- This indicates that the specified string must appear 112 * at the end of the attribute value. There can be at most one 113 * subFinal element in a substring assertion.</LI> 114 * </UL> 115 * A substring filter contains only an attribute name and subInitial, 116 * subAny, and subFinal elements.</LI> 117 * <LI><B>GREATER-OR-EQUAL</B> -- This is used to indicate that a filter 118 * should match an entry only if that entry contains at least one value 119 * for the specified attribute that is greater than or equal to the 120 * provided assertion value. A greater-or-equal filter contains only an 121 * attribute name and an assertion value.</LI> 122 * <LI><B>LESS-OR-EQUAL</B> -- This is used to indicate that a filter should 123 * match an entry only if that entry contains at least one value for the 124 * specified attribute that is less than or equal to the provided 125 * assertion value. A less-or-equal filter contains only an attribute 126 * name and an assertion value.</LI> 127 * <LI><B>PRESENCE</B> -- This is used to indicate that a filter should match 128 * an entry only if the entry contains at least one value for the 129 * specified attribute. A presence filter contains only an attribute 130 * name.</LI> 131 * <LI><B>APPROXIMATE-MATCH</B> -- This is used to indicate that a filter 132 * should match an entry only if the entry contains at least one value for 133 * the specified attribute that is approximately equal to the provided 134 * assertion value. The definition of "approximately equal to" may vary 135 * from one server to another, and from one attribute to another, but it 136 * is often implemented as a "sounds like" match using a variant of the 137 * metaphone or double-metaphone algorithm. An approximate-match filter 138 * contains only an attribute name and an assertion value.</LI> 139 * <LI><B>EXTENSIBLE-MATCH</B> -- This is used to perform advanced types of 140 * matching against entries, according to the following criteria: 141 * <UL> 142 * <LI>If an attribute name is provided, then the assertion value must 143 * match one of the values for that attribute (potentially including 144 * values contained in the entry's DN). If a matching rule ID is 145 * also provided, then the associated matching rule will be used to 146 * determine whether there is a match; otherwise the default 147 * equality matching rule for that attribute will be used.</LI> 148 * <LI>If no attribute name is provided, then a matching rule ID must be 149 * given, and the corresponding matching rule will be used to 150 * determine whether any attribute in the target entry (potentially 151 * including attributes contained in the entry's DN) has at least 152 * one value that matches the provided assertion value.</LI> 153 * <LI>If the dnAttributes flag is set, then attributes contained in the 154 * entry's DN will also be evaluated to determine if they match the 155 * filter criteria. If it is not set, then attributes contained in 156 * the entry's DN (other than those contained in its RDN which are 157 * also present as separate attributes in the entry) will not be 158* examined.</LI> 159 * </UL> 160 * An extensible match filter contains only an attribute name, matching 161 * rule ID, dnAttributes flag, and an assertion value.</LI> 162 * </UL> 163 * <BR><BR> 164 * There are two primary ways to create a search filter. The first is to create 165 * a filter from its string representation with the 166 * {@link Filter#create(String)} method, using the syntax described in RFC 4515. 167 * For example: 168 * <PRE> 169 * Filter f1 = Filter.create("(objectClass=*)"); 170 * Filter f2 = Filter.create("(uid=john.doe)"); 171 * Filter f3 = Filter.create("(|(givenName=John)(givenName=Johnathan))"); 172 * </PRE> 173 * <BR><BR> 174 * Creating a filter from its string representation is a common approach and 175 * seems to be relatively straightforward, but it does have some hidden dangers. 176 * This primarily comes from the potential for special characters in the filter 177 * string which need to be properly escaped. If this isn't done, then the 178 * search may fail or behave unexpectedly, or worse it could lead to a 179 * vulnerability in the application in which a malicious user could trick the 180 * application into retrieving more information than it should have. To avoid 181 * these problems, it may be better to construct filters from their individual 182 * components rather than their string representations, like: 183 * <PRE> 184 * Filter f1 = Filter.createPresenceFilter("objectClass"); 185 * Filter f2 = Filter.createEqualityFilter("uid", "john.doe"); 186 * Filter f3 = Filter.createORFilter( 187 * Filter.createEqualityFilter("givenName", "John"), 188 * Filter.createEqualityFilter("givenName", "Johnathan")); 189 * </PRE> 190 * In general, it is recommended to avoid creating filters from their string 191 * representations if any of that string representation may include 192 * user-provided data or special characters including non-ASCII characters, 193 * parentheses, asterisks, or backslashes. 194 */ 195@NotMutable() 196@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 197public final class Filter 198 implements Serializable 199{ 200 /** 201 * The BER type for AND search filters. 202 */ 203 public static final byte FILTER_TYPE_AND = (byte) 0xA0; 204 205 206 207 /** 208 * The BER type for OR search filters. 209 */ 210 public static final byte FILTER_TYPE_OR = (byte) 0xA1; 211 212 213 214 /** 215 * The BER type for NOT search filters. 216 */ 217 public static final byte FILTER_TYPE_NOT = (byte) 0xA2; 218 219 220 221 /** 222 * The BER type for equality search filters. 223 */ 224 public static final byte FILTER_TYPE_EQUALITY = (byte) 0xA3; 225 226 227 228 /** 229 * The BER type for substring search filters. 230 */ 231 public static final byte FILTER_TYPE_SUBSTRING = (byte) 0xA4; 232 233 234 235 /** 236 * The BER type for greaterOrEqual search filters. 237 */ 238 public static final byte FILTER_TYPE_GREATER_OR_EQUAL = (byte) 0xA5; 239 240 241 242 /** 243 * The BER type for lessOrEqual search filters. 244 */ 245 public static final byte FILTER_TYPE_LESS_OR_EQUAL = (byte) 0xA6; 246 247 248 249 /** 250 * The BER type for presence search filters. 251 */ 252 public static final byte FILTER_TYPE_PRESENCE = (byte) 0x87; 253 254 255 256 /** 257 * The BER type for approximate match search filters. 258 */ 259 public static final byte FILTER_TYPE_APPROXIMATE_MATCH = (byte) 0xA8; 260 261 262 263 /** 264 * The BER type for extensible match search filters. 265 */ 266 public static final byte FILTER_TYPE_EXTENSIBLE_MATCH = (byte) 0xA9; 267 268 269 270 /** 271 * The BER type for the subInitial substring filter element. 272 */ 273 private static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80; 274 275 276 277 /** 278 * The BER type for the subAny substring filter element. 279 */ 280 private static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81; 281 282 283 284 /** 285 * The BER type for the subFinal substring filter element. 286 */ 287 private static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82; 288 289 290 291 /** 292 * The BER type for the matching rule ID extensible match filter element. 293 */ 294 private static final byte EXTENSIBLE_TYPE_MATCHING_RULE_ID = (byte) 0x81; 295 296 297 298 /** 299 * The BER type for the attribute name extensible match filter element. 300 */ 301 private static final byte EXTENSIBLE_TYPE_ATTRIBUTE_NAME = (byte) 0x82; 302 303 304 305 /** 306 * The BER type for the match value extensible match filter element. 307 */ 308 private static final byte EXTENSIBLE_TYPE_MATCH_VALUE = (byte) 0x83; 309 310 311 312 /** 313 * The BER type for the DN attributes extensible match filter element. 314 */ 315 private static final byte EXTENSIBLE_TYPE_DN_ATTRIBUTES = (byte) 0x84; 316 317 318 319 /** 320 * The set of filters that will be used if there are no subordinate filters. 321 */ 322 private static final Filter[] NO_FILTERS = new Filter[0]; 323 324 325 326 /** 327 * The set of subAny components that will be used if there are no subAny 328 * components. 329 */ 330 private static final ASN1OctetString[] NO_SUB_ANY = new ASN1OctetString[0]; 331 332 333 334 /** 335 * The serial version UID for this serializable class. 336 */ 337 private static final long serialVersionUID = -2734184402804691970L; 338 339 340 341 // The assertion value for this filter. 342 private final ASN1OctetString assertionValue; 343 344 // The subFinal component for this filter. 345 private final ASN1OctetString subFinal; 346 347 // The subInitial component for this filter. 348 private final ASN1OctetString subInitial; 349 350 // The subAny components for this filter. 351 private final ASN1OctetString[] subAny; 352 353 // The dnAttrs element for this filter. 354 private final boolean dnAttributes; 355 356 // The filter component to include in a NOT filter. 357 private final Filter notComp; 358 359 // The set of filter components to include in an AND or OR filter. 360 private final Filter[] filterComps; 361 362 // The filter type for this search filter. 363 private final byte filterType; 364 365 // The attribute name for this filter. 366 private final String attrName; 367 368 // The string representation of this search filter. 369 private volatile String filterString; 370 371 // The matching rule ID for this filter. 372 private final String matchingRuleID; 373 374 // The normalized string representation of this search filter. 375 private volatile String normalizedString; 376 377 378 379 /** 380 * Creates a new filter with the appropriate subset of the provided 381 * information. 382 * 383 * @param filterString The string representation of this search filter. 384 * It may be {@code null} if it is not yet known. 385 * @param filterType The filter type for this filter. 386 * @param filterComps The set of filter components for this filter. 387 * @param notComp The filter component for this NOT filter. 388 * @param attrName The name of the target attribute for this filter. 389 * @param assertionValue Then assertion value for this filter. 390 * @param subInitial The subInitial component for this filter. 391 * @param subAny The set of subAny components for this filter. 392 * @param subFinal The subFinal component for this filter. 393 * @param matchingRuleID The matching rule ID for this filter. 394 * @param dnAttributes The dnAttributes flag. 395 */ 396 private Filter(final String filterString, final byte filterType, 397 final Filter[] filterComps, final Filter notComp, 398 final String attrName, final ASN1OctetString assertionValue, 399 final ASN1OctetString subInitial, 400 final ASN1OctetString[] subAny, final ASN1OctetString subFinal, 401 final String matchingRuleID, final boolean dnAttributes) 402 { 403 this.filterString = filterString; 404 this.filterType = filterType; 405 this.filterComps = filterComps; 406 this.notComp = notComp; 407 this.attrName = attrName; 408 this.assertionValue = assertionValue; 409 this.subInitial = subInitial; 410 this.subAny = subAny; 411 this.subFinal = subFinal; 412 this.matchingRuleID = matchingRuleID; 413 this.dnAttributes = dnAttributes; 414 } 415 416 417 418 /** 419 * Creates a new AND search filter with the provided components. 420 * 421 * @param andComponents The set of filter components to include in the AND 422 * filter. It must not be {@code null}. 423 * 424 * @return The created AND search filter. 425 */ 426 public static Filter createANDFilter(final Filter... andComponents) 427 { 428 ensureNotNull(andComponents); 429 430 return new Filter(null, FILTER_TYPE_AND, andComponents, null, null, null, 431 null, NO_SUB_ANY, null, null, false); 432 } 433 434 435 436 /** 437 * Creates a new AND search filter with the provided components. 438 * 439 * @param andComponents The set of filter components to include in the AND 440 * filter. It must not be {@code null}. 441 * 442 * @return The created AND search filter. 443 */ 444 public static Filter createANDFilter(final List<Filter> andComponents) 445 { 446 ensureNotNull(andComponents); 447 448 return new Filter(null, FILTER_TYPE_AND, 449 andComponents.toArray(new Filter[andComponents.size()]), 450 null, null, null, null, NO_SUB_ANY, null, null, false); 451 } 452 453 454 455 /** 456 * Creates a new AND search filter with the provided components. 457 * 458 * @param andComponents The set of filter components to include in the AND 459 * filter. It must not be {@code null}. 460 * 461 * @return The created AND search filter. 462 */ 463 public static Filter createANDFilter(final Collection<Filter> andComponents) 464 { 465 ensureNotNull(andComponents); 466 467 return new Filter(null, FILTER_TYPE_AND, 468 andComponents.toArray(new Filter[andComponents.size()]), 469 null, null, null, null, NO_SUB_ANY, null, null, false); 470 } 471 472 473 474 /** 475 * Creates a new OR search filter with the provided components. 476 * 477 * @param orComponents The set of filter components to include in the OR 478 * filter. It must not be {@code null}. 479 * 480 * @return The created OR search filter. 481 */ 482 public static Filter createORFilter(final Filter... orComponents) 483 { 484 ensureNotNull(orComponents); 485 486 return new Filter(null, FILTER_TYPE_OR, orComponents, null, null, null, 487 null, NO_SUB_ANY, null, null, false); 488 } 489 490 491 492 /** 493 * Creates a new OR search filter with the provided components. 494 * 495 * @param orComponents The set of filter components to include in the OR 496 * filter. It must not be {@code null}. 497 * 498 * @return The created OR search filter. 499 */ 500 public static Filter createORFilter(final List<Filter> orComponents) 501 { 502 ensureNotNull(orComponents); 503 504 return new Filter(null, FILTER_TYPE_OR, 505 orComponents.toArray(new Filter[orComponents.size()]), 506 null, null, null, null, NO_SUB_ANY, null, null, false); 507 } 508 509 510 511 /** 512 * Creates a new OR search filter with the provided components. 513 * 514 * @param orComponents The set of filter components to include in the OR 515 * filter. It must not be {@code null}. 516 * 517 * @return The created OR search filter. 518 */ 519 public static Filter createORFilter(final Collection<Filter> orComponents) 520 { 521 ensureNotNull(orComponents); 522 523 return new Filter(null, FILTER_TYPE_OR, 524 orComponents.toArray(new Filter[orComponents.size()]), 525 null, null, null, null, NO_SUB_ANY, null, null, false); 526 } 527 528 529 530 /** 531 * Creates a new NOT search filter with the provided component. 532 * 533 * @param notComponent The filter component to include in this NOT filter. 534 * It must not be {@code null}. 535 * 536 * @return The created NOT search filter. 537 */ 538 public static Filter createNOTFilter(final Filter notComponent) 539 { 540 ensureNotNull(notComponent); 541 542 return new Filter(null, FILTER_TYPE_NOT, NO_FILTERS, notComponent, null, 543 null, null, NO_SUB_ANY, null, null, false); 544 } 545 546 547 548 /** 549 * Creates a new equality search filter with the provided information. 550 * 551 * @param attributeName The attribute name for this equality filter. It 552 * must not be {@code null}. 553 * @param assertionValue The assertion value for this equality filter. It 554 * must not be {@code null}. 555 * 556 * @return The created equality search filter. 557 */ 558 public static Filter createEqualityFilter(final String attributeName, 559 final String assertionValue) 560 { 561 ensureNotNull(attributeName, assertionValue); 562 563 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 564 attributeName, new ASN1OctetString(assertionValue), null, 565 NO_SUB_ANY, null, null, false); 566 } 567 568 569 570 /** 571 * Creates a new equality search filter with the provided information. 572 * 573 * @param attributeName The attribute name for this equality filter. It 574 * must not be {@code null}. 575 * @param assertionValue The assertion value for this equality filter. It 576 * must not be {@code null}. 577 * 578 * @return The created equality search filter. 579 */ 580 public static Filter createEqualityFilter(final String attributeName, 581 final byte[] assertionValue) 582 { 583 ensureNotNull(attributeName, assertionValue); 584 585 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 586 attributeName, new ASN1OctetString(assertionValue), null, 587 NO_SUB_ANY, null, null, false); 588 } 589 590 591 592 /** 593 * Creates a new equality search filter with the provided information. 594 * 595 * @param attributeName The attribute name for this equality filter. It 596 * must not be {@code null}. 597 * @param assertionValue The assertion value for this equality filter. It 598 * must not be {@code null}. 599 * 600 * @return The created equality search filter. 601 */ 602 static Filter createEqualityFilter(final String attributeName, 603 final ASN1OctetString assertionValue) 604 { 605 ensureNotNull(attributeName, assertionValue); 606 607 return new Filter(null, FILTER_TYPE_EQUALITY, NO_FILTERS, null, 608 attributeName, assertionValue, null, NO_SUB_ANY, null, 609 null, false); 610 } 611 612 613 614 /** 615 * Creates a new substring search filter with the provided information. At 616 * least one of the subInitial, subAny, and subFinal components must not be 617 * {@code null}. 618 * 619 * @param attributeName The attribute name for this substring filter. It 620 * must not be {@code null}. 621 * @param subInitial The subInitial component for this substring filter. 622 * @param subAny The set of subAny components for this substring 623 * filter. 624 * @param subFinal The subFinal component for this substring filter. 625 * 626 * @return The created substring search filter. 627 */ 628 public static Filter createSubstringFilter(final String attributeName, 629 final String subInitial, 630 final String[] subAny, 631 final String subFinal) 632 { 633 ensureNotNull(attributeName); 634 ensureTrue((subInitial != null) || 635 ((subAny != null) && (subAny.length > 0)) || 636 (subFinal != null)); 637 638 final ASN1OctetString subInitialOS; 639 if (subInitial == null) 640 { 641 subInitialOS = null; 642 } 643 else 644 { 645 subInitialOS = new ASN1OctetString(subInitial); 646 } 647 648 final ASN1OctetString[] subAnyArray; 649 if (subAny == null) 650 { 651 subAnyArray = NO_SUB_ANY; 652 } 653 else 654 { 655 subAnyArray = new ASN1OctetString[subAny.length]; 656 for (int i=0; i < subAny.length; i++) 657 { 658 subAnyArray[i] = new ASN1OctetString(subAny[i]); 659 } 660 } 661 662 final ASN1OctetString subFinalOS; 663 if (subFinal == null) 664 { 665 subFinalOS = null; 666 } 667 else 668 { 669 subFinalOS = new ASN1OctetString(subFinal); 670 } 671 672 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 673 attributeName, null, subInitialOS, subAnyArray, 674 subFinalOS, null, false); 675 } 676 677 678 679 /** 680 * Creates a new substring search filter with the provided information. At 681 * least one of the subInitial, subAny, and subFinal components must not be 682 * {@code null}. 683 * 684 * @param attributeName The attribute name for this substring filter. It 685 * must not be {@code null}. 686 * @param subInitial The subInitial component for this substring filter. 687 * @param subAny The set of subAny components for this substring 688 * filter. 689 * @param subFinal The subFinal component for this substring filter. 690 * 691 * @return The created substring search filter. 692 */ 693 public static Filter createSubstringFilter(final String attributeName, 694 final byte[] subInitial, 695 final byte[][] subAny, 696 final byte[] subFinal) 697 { 698 ensureNotNull(attributeName); 699 ensureTrue((subInitial != null) || 700 ((subAny != null) && (subAny.length > 0)) || 701 (subFinal != null)); 702 703 final ASN1OctetString subInitialOS; 704 if (subInitial == null) 705 { 706 subInitialOS = null; 707 } 708 else 709 { 710 subInitialOS = new ASN1OctetString(subInitial); 711 } 712 713 final ASN1OctetString[] subAnyArray; 714 if (subAny == null) 715 { 716 subAnyArray = NO_SUB_ANY; 717 } 718 else 719 { 720 subAnyArray = new ASN1OctetString[subAny.length]; 721 for (int i=0; i < subAny.length; i++) 722 { 723 subAnyArray[i] = new ASN1OctetString(subAny[i]); 724 } 725 } 726 727 final ASN1OctetString subFinalOS; 728 if (subFinal == null) 729 { 730 subFinalOS = null; 731 } 732 else 733 { 734 subFinalOS = new ASN1OctetString(subFinal); 735 } 736 737 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 738 attributeName, null, subInitialOS, subAnyArray, 739 subFinalOS, null, false); 740 } 741 742 743 744 /** 745 * Creates a new substring search filter with the provided information. At 746 * least one of the subInitial, subAny, and subFinal components must not be 747 * {@code null}. 748 * 749 * @param attributeName The attribute name for this substring filter. It 750 * must not be {@code null}. 751 * @param subInitial The subInitial component for this substring filter. 752 * @param subAny The set of subAny components for this substring 753 * filter. 754 * @param subFinal The subFinal component for this substring filter. 755 * 756 * @return The created substring search filter. 757 */ 758 static Filter createSubstringFilter(final String attributeName, 759 final ASN1OctetString subInitial, 760 final ASN1OctetString[] subAny, 761 final ASN1OctetString subFinal) 762 { 763 ensureNotNull(attributeName); 764 ensureTrue((subInitial != null) || 765 ((subAny != null) && (subAny.length > 0)) || 766 (subFinal != null)); 767 768 if (subAny == null) 769 { 770 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 771 attributeName, null, subInitial, NO_SUB_ANY, subFinal, 772 null, false); 773 } 774 else 775 { 776 return new Filter(null, FILTER_TYPE_SUBSTRING, NO_FILTERS, null, 777 attributeName, null, subInitial, subAny, subFinal, null, 778 false); 779 } 780 } 781 782 783 784 /** 785 * Creates a new greater-or-equal search filter with the provided information. 786 * 787 * @param attributeName The attribute name for this greater-or-equal 788 * filter. It must not be {@code null}. 789 * @param assertionValue The assertion value for this greater-or-equal 790 * filter. It must not be {@code null}. 791 * 792 * @return The created greater-or-equal search filter. 793 */ 794 public static Filter createGreaterOrEqualFilter(final String attributeName, 795 final String assertionValue) 796 { 797 ensureNotNull(attributeName, assertionValue); 798 799 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 800 attributeName, new ASN1OctetString(assertionValue), null, 801 NO_SUB_ANY, null, null, false); 802 } 803 804 805 806 /** 807 * Creates a new greater-or-equal search filter with the provided information. 808 * 809 * @param attributeName The attribute name for this greater-or-equal 810 * filter. It must not be {@code null}. 811 * @param assertionValue The assertion value for this greater-or-equal 812 * filter. It must not be {@code null}. 813 * 814 * @return The created greater-or-equal search filter. 815 */ 816 public static Filter createGreaterOrEqualFilter(final String attributeName, 817 final byte[] assertionValue) 818 { 819 ensureNotNull(attributeName, assertionValue); 820 821 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 822 attributeName, new ASN1OctetString(assertionValue), null, 823 NO_SUB_ANY, null, null, false); 824 } 825 826 827 828 /** 829 * Creates a new greater-or-equal search filter with the provided information. 830 * 831 * @param attributeName The attribute name for this greater-or-equal 832 * filter. It must not be {@code null}. 833 * @param assertionValue The assertion value for this greater-or-equal 834 * filter. It must not be {@code null}. 835 * 836 * @return The created greater-or-equal search filter. 837 */ 838 static Filter createGreaterOrEqualFilter(final String attributeName, 839 final ASN1OctetString assertionValue) 840 { 841 ensureNotNull(attributeName, assertionValue); 842 843 return new Filter(null, FILTER_TYPE_GREATER_OR_EQUAL, NO_FILTERS, null, 844 attributeName, assertionValue, null, NO_SUB_ANY, null, 845 null, false); 846 } 847 848 849 850 /** 851 * Creates a new less-or-equal search filter with the provided information. 852 * 853 * @param attributeName The attribute name for this less-or-equal 854 * filter. It must not be {@code null}. 855 * @param assertionValue The assertion value for this less-or-equal 856 * filter. It must not be {@code null}. 857 * 858 * @return The created less-or-equal search filter. 859 */ 860 public static Filter createLessOrEqualFilter(final String attributeName, 861 final String assertionValue) 862 { 863 ensureNotNull(attributeName, assertionValue); 864 865 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 866 attributeName, new ASN1OctetString(assertionValue), null, 867 NO_SUB_ANY, null, null, false); 868 } 869 870 871 872 /** 873 * Creates a new less-or-equal search filter with the provided information. 874 * 875 * @param attributeName The attribute name for this less-or-equal 876 * filter. It must not be {@code null}. 877 * @param assertionValue The assertion value for this less-or-equal 878 * filter. It must not be {@code null}. 879 * 880 * @return The created less-or-equal search filter. 881 */ 882 public static Filter createLessOrEqualFilter(final String attributeName, 883 final byte[] assertionValue) 884 { 885 ensureNotNull(attributeName, assertionValue); 886 887 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 888 attributeName, new ASN1OctetString(assertionValue), null, 889 NO_SUB_ANY, null, null, false); 890 } 891 892 893 894 /** 895 * Creates a new less-or-equal search filter with the provided information. 896 * 897 * @param attributeName The attribute name for this less-or-equal 898 * filter. It must not be {@code null}. 899 * @param assertionValue The assertion value for this less-or-equal 900 * filter. It must not be {@code null}. 901 * 902 * @return The created less-or-equal search filter. 903 */ 904 static Filter createLessOrEqualFilter(final String attributeName, 905 final ASN1OctetString assertionValue) 906 { 907 ensureNotNull(attributeName, assertionValue); 908 909 return new Filter(null, FILTER_TYPE_LESS_OR_EQUAL, NO_FILTERS, null, 910 attributeName, assertionValue, null, NO_SUB_ANY, null, 911 null, false); 912 } 913 914 915 916 /** 917 * Creates a new presence search filter with the provided information. 918 * 919 * @param attributeName The attribute name for this presence filter. It 920 * must not be {@code null}. 921 * 922 * @return The created presence search filter. 923 */ 924 public static Filter createPresenceFilter(final String attributeName) 925 { 926 ensureNotNull(attributeName); 927 928 return new Filter(null, FILTER_TYPE_PRESENCE, NO_FILTERS, null, 929 attributeName, null, null, NO_SUB_ANY, null, null, false); 930 } 931 932 933 934 /** 935 * Creates a new approximate match search filter with the provided 936 * information. 937 * 938 * @param attributeName The attribute name for this approximate match 939 * filter. It must not be {@code null}. 940 * @param assertionValue The assertion value for this approximate match 941 * filter. It must not be {@code null}. 942 * 943 * @return The created approximate match search filter. 944 */ 945 public static Filter createApproximateMatchFilter(final String attributeName, 946 final String assertionValue) 947 { 948 ensureNotNull(attributeName, assertionValue); 949 950 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 951 attributeName, new ASN1OctetString(assertionValue), null, 952 NO_SUB_ANY, null, null, false); 953 } 954 955 956 957 /** 958 * Creates a new approximate match search filter with the provided 959 * information. 960 * 961 * @param attributeName The attribute name for this approximate match 962 * filter. It must not be {@code null}. 963 * @param assertionValue The assertion value for this approximate match 964 * filter. It must not be {@code null}. 965 * 966 * @return The created approximate match search filter. 967 */ 968 public static Filter createApproximateMatchFilter(final String attributeName, 969 final byte[] assertionValue) 970 { 971 ensureNotNull(attributeName, assertionValue); 972 973 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 974 attributeName, new ASN1OctetString(assertionValue), null, 975 NO_SUB_ANY, null, null, false); 976 } 977 978 979 980 /** 981 * Creates a new approximate match search filter with the provided 982 * information. 983 * 984 * @param attributeName The attribute name for this approximate match 985 * filter. It must not be {@code null}. 986 * @param assertionValue The assertion value for this approximate match 987 * filter. It must not be {@code null}. 988 * 989 * @return The created approximate match search filter. 990 */ 991 static Filter createApproximateMatchFilter(final String attributeName, 992 final ASN1OctetString assertionValue) 993 { 994 ensureNotNull(attributeName, assertionValue); 995 996 return new Filter(null, FILTER_TYPE_APPROXIMATE_MATCH, NO_FILTERS, null, 997 attributeName, assertionValue, null, NO_SUB_ANY, null, 998 null, false); 999 } 1000 1001 1002 1003 /** 1004 * Creates a new extensible match search filter with the provided 1005 * information. At least one of the attribute name and matching rule ID must 1006 * be specified, and the assertion value must always be present. 1007 * 1008 * @param attributeName The attribute name for this extensible match 1009 * filter. 1010 * @param matchingRuleID The matching rule ID for this extensible match 1011 * filter. 1012 * @param dnAttributes Indicates whether the match should be performed 1013 * against attributes in the target entry's DN. 1014 * @param assertionValue The assertion value for this extensible match 1015 * filter. It must not be {@code null}. 1016 * 1017 * @return The created extensible match search filter. 1018 */ 1019 public static Filter createExtensibleMatchFilter(final String attributeName, 1020 final String matchingRuleID, 1021 final boolean dnAttributes, 1022 final String assertionValue) 1023 { 1024 ensureNotNull(assertionValue); 1025 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1026 1027 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1028 attributeName, new ASN1OctetString(assertionValue), null, 1029 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1030 } 1031 1032 1033 1034 /** 1035 * Creates a new extensible match search filter with the provided 1036 * information. At least one of the attribute name and matching rule ID must 1037 * be specified, and the assertion value must always be present. 1038 * 1039 * @param attributeName The attribute name for this extensible match 1040 * filter. 1041 * @param matchingRuleID The matching rule ID for this extensible match 1042 * filter. 1043 * @param dnAttributes Indicates whether the match should be performed 1044 * against attributes in the target entry's DN. 1045 * @param assertionValue The assertion value for this extensible match 1046 * filter. It must not be {@code null}. 1047 * 1048 * @return The created extensible match search filter. 1049 */ 1050 public static Filter createExtensibleMatchFilter(final String attributeName, 1051 final String matchingRuleID, 1052 final boolean dnAttributes, 1053 final byte[] assertionValue) 1054 { 1055 ensureNotNull(assertionValue); 1056 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1057 1058 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1059 attributeName, new ASN1OctetString(assertionValue), null, 1060 NO_SUB_ANY, null, matchingRuleID, dnAttributes); 1061 } 1062 1063 1064 1065 /** 1066 * Creates a new extensible match search filter with the provided 1067 * information. At least one of the attribute name and matching rule ID must 1068 * be specified, and the assertion value must always be present. 1069 * 1070 * @param attributeName The attribute name for this extensible match 1071 * filter. 1072 * @param matchingRuleID The matching rule ID for this extensible match 1073 * filter. 1074 * @param dnAttributes Indicates whether the match should be performed 1075 * against attributes in the target entry's DN. 1076 * @param assertionValue The assertion value for this extensible match 1077 * filter. It must not be {@code null}. 1078 * 1079 * @return The created approximate match search filter. 1080 */ 1081 static Filter createExtensibleMatchFilter(final String attributeName, 1082 final String matchingRuleID, final boolean dnAttributes, 1083 final ASN1OctetString assertionValue) 1084 { 1085 ensureNotNull(assertionValue); 1086 ensureFalse((attributeName == null) && (matchingRuleID == null)); 1087 1088 return new Filter(null, FILTER_TYPE_EXTENSIBLE_MATCH, NO_FILTERS, null, 1089 attributeName, assertionValue, null, NO_SUB_ANY, null, 1090 matchingRuleID, dnAttributes); 1091 } 1092 1093 1094 1095 /** 1096 * Creates a new search filter from the provided string representation. 1097 * 1098 * @param filterString The string representation of the filter to create. 1099 * It must not be {@code null}. 1100 * 1101 * @return The search filter decoded from the provided filter string. 1102 * 1103 * @throws LDAPException If the provided string cannot be decoded as a valid 1104 * LDAP search filter. 1105 */ 1106 public static Filter create(final String filterString) 1107 throws LDAPException 1108 { 1109 ensureNotNull(filterString); 1110 1111 return create(filterString, 0, (filterString.length() - 1), 0); 1112 } 1113 1114 1115 1116 /** 1117 * Creates a new search filter from the specified portion of the provided 1118 * string representation. 1119 * 1120 * @param filterString The string representation of the filter to create. 1121 * @param startPos The position of the first character to consider as 1122 * part of the filter. 1123 * @param endPos The position of the last character to consider as 1124 * part of the filter. 1125 * @param depth The current nesting depth for this filter. It should 1126 * be increased by one for each AND, OR, or NOT filter 1127 * encountered, in order to prevent stack overflow 1128 * errors from excessive recursion. 1129 * 1130 * @return The decoded search filter. 1131 * 1132 * @throws LDAPException If the provided string cannot be decoded as a valid 1133 * LDAP search filter. 1134 */ 1135 private static Filter create(final String filterString, final int startPos, 1136 final int endPos, final int depth) 1137 throws LDAPException 1138 { 1139 if (depth > 100) 1140 { 1141 throw new LDAPException(ResultCode.FILTER_ERROR, 1142 ERR_FILTER_TOO_DEEP.get(filterString)); 1143 } 1144 1145 final byte filterType; 1146 final Filter[] filterComps; 1147 final Filter notComp; 1148 final String attrName; 1149 final ASN1OctetString assertionValue; 1150 final ASN1OctetString subInitial; 1151 final ASN1OctetString[] subAny; 1152 final ASN1OctetString subFinal; 1153 final String matchingRuleID; 1154 final boolean dnAttributes; 1155 1156 if (startPos >= endPos) 1157 { 1158 throw new LDAPException(ResultCode.FILTER_ERROR, 1159 ERR_FILTER_TOO_SHORT.get(filterString)); 1160 } 1161 1162 int l = startPos; 1163 int r = endPos; 1164 1165 // First, see if the provided filter string is enclosed in parentheses, like 1166 // it should be. If so, then strip off the outer parentheses. 1167 if (filterString.charAt(l) == '(') 1168 { 1169 if (filterString.charAt(r) == ')') 1170 { 1171 l++; 1172 r--; 1173 } 1174 else 1175 { 1176 throw new LDAPException(ResultCode.FILTER_ERROR, 1177 ERR_FILTER_OPEN_WITHOUT_CLOSE.get(filterString, l, r)); 1178 } 1179 } 1180 else 1181 { 1182 // This is technically an error, and it's a bad practice. If we're 1183 // working on the complete filter string then we'll let it slide, but 1184 // otherwise we'll raise an error. 1185 if (l != 0) 1186 { 1187 throw new LDAPException(ResultCode.FILTER_ERROR, 1188 ERR_FILTER_MISSING_PARENTHESES.get(filterString, 1189 filterString.substring(l, r+1))); 1190 } 1191 } 1192 1193 1194 // Look at the first character of the filter to see if it's an '&', '|', or 1195 // '!'. If we find a parenthesis, then that's an error. 1196 switch (filterString.charAt(l)) 1197 { 1198 case '&': 1199 filterType = FILTER_TYPE_AND; 1200 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1201 notComp = null; 1202 attrName = null; 1203 assertionValue = null; 1204 subInitial = null; 1205 subAny = NO_SUB_ANY; 1206 subFinal = null; 1207 matchingRuleID = null; 1208 dnAttributes = false; 1209 break; 1210 1211 case '|': 1212 filterType = FILTER_TYPE_OR; 1213 filterComps = parseFilterComps(filterString, l+1, r, depth+1); 1214 notComp = null; 1215 attrName = null; 1216 assertionValue = null; 1217 subInitial = null; 1218 subAny = NO_SUB_ANY; 1219 subFinal = null; 1220 matchingRuleID = null; 1221 dnAttributes = false; 1222 break; 1223 1224 case '!': 1225 filterType = FILTER_TYPE_NOT; 1226 filterComps = NO_FILTERS; 1227 notComp = create(filterString, l+1, r, depth+1); 1228 attrName = null; 1229 assertionValue = null; 1230 subInitial = null; 1231 subAny = NO_SUB_ANY; 1232 subFinal = null; 1233 matchingRuleID = null; 1234 dnAttributes = false; 1235 break; 1236 1237 case '(': 1238 throw new LDAPException(ResultCode.FILTER_ERROR, 1239 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1240 1241 case ':': 1242 // This must be an extensible matching filter that starts with a 1243 // dnAttributes flag and/or matching rule ID, and we should parse it 1244 // accordingly. 1245 filterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1246 filterComps = NO_FILTERS; 1247 notComp = null; 1248 attrName = null; 1249 subInitial = null; 1250 subAny = NO_SUB_ANY; 1251 subFinal = null; 1252 1253 // The next element must be either the "dn:{matchingruleid}" or just 1254 // "{matchingruleid}", and it must be followed by a colon. 1255 final int dnMRIDStart = ++l; 1256 while ((l <= r) && (filterString.charAt(l) != ':')) 1257 { 1258 l++; 1259 } 1260 1261 if (l > r) 1262 { 1263 throw new LDAPException(ResultCode.FILTER_ERROR, 1264 ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos)); 1265 } 1266 else if (l == dnMRIDStart) 1267 { 1268 throw new LDAPException(ResultCode.FILTER_ERROR, 1269 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1270 } 1271 final String s = filterString.substring(dnMRIDStart, l++); 1272 if (s.equalsIgnoreCase("dn")) 1273 { 1274 dnAttributes = true; 1275 1276 // The colon must be followed by the matching rule ID and another 1277 // colon. 1278 final int mrIDStart = l; 1279 while ((l < r) && (filterString.charAt(l) != ':')) 1280 { 1281 l++; 1282 } 1283 1284 if (l >= r) 1285 { 1286 throw new LDAPException(ResultCode.FILTER_ERROR, 1287 ERR_FILTER_NO_COLON_AFTER_MRID.get(filterString, startPos)); 1288 } 1289 1290 matchingRuleID = filterString.substring(mrIDStart, l); 1291 if (matchingRuleID.length() == 0) 1292 { 1293 throw new LDAPException(ResultCode.FILTER_ERROR, 1294 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1295 } 1296 1297 if ((++l > r) || (filterString.charAt(l) != '=')) 1298 { 1299 throw new LDAPException(ResultCode.FILTER_ERROR, 1300 ERR_FILTER_UNEXPECTED_CHAR_AFTER_MRID.get(filterString, 1301 startPos, filterString.charAt(l))); 1302 } 1303 } 1304 else 1305 { 1306 matchingRuleID = s; 1307 dnAttributes = false; 1308 1309 // The colon must be followed by an equal sign. 1310 if ((l > r) || (filterString.charAt(l) != '=')) 1311 { 1312 throw new LDAPException(ResultCode.FILTER_ERROR, 1313 ERR_FILTER_NO_EQUAL_AFTER_MRID.get(filterString, startPos)); 1314 } 1315 } 1316 1317 // Now we should be able to read the value, handling any escape 1318 // characters as we go. 1319 l++; 1320 final ByteStringBuffer valueBuffer = new ByteStringBuffer(r - l + 1); 1321 while (l <= r) 1322 { 1323 final char c = filterString.charAt(l); 1324 if (c == '\\') 1325 { 1326 l = readEscapedHexString(filterString, ++l, valueBuffer); 1327 } 1328 else if (c == '(') 1329 { 1330 throw new LDAPException(ResultCode.FILTER_ERROR, 1331 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1332 } 1333 else if (c == ')') 1334 { 1335 throw new LDAPException(ResultCode.FILTER_ERROR, 1336 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l)); 1337 } 1338 else 1339 { 1340 valueBuffer.append(c); 1341 l++; 1342 } 1343 } 1344 assertionValue = new ASN1OctetString(valueBuffer.toByteArray()); 1345 break; 1346 1347 1348 default: 1349 // We know that it's not an AND, OR, or NOT filter, so we can eliminate 1350 // the variables used only for them. 1351 filterComps = NO_FILTERS; 1352 notComp = null; 1353 1354 1355 // We should now be able to read a non-empty attribute name. 1356 final int attrStartPos = l; 1357 int attrEndPos = -1; 1358 byte tempFilterType = 0x00; 1359 boolean filterTypeKnown = false; 1360 boolean equalFound = false; 1361attrNameLoop: 1362 while (l <= r) 1363 { 1364 final char c = filterString.charAt(l++); 1365 switch (c) 1366 { 1367 case ':': 1368 tempFilterType = FILTER_TYPE_EXTENSIBLE_MATCH; 1369 filterTypeKnown = true; 1370 attrEndPos = l - 1; 1371 break attrNameLoop; 1372 1373 case '>': 1374 tempFilterType = FILTER_TYPE_GREATER_OR_EQUAL; 1375 filterTypeKnown = true; 1376 attrEndPos = l - 1; 1377 1378 if (l <= r) 1379 { 1380 if (filterString.charAt(l++) != '=') 1381 { 1382 throw new LDAPException(ResultCode.FILTER_ERROR, 1383 ERR_FILTER_UNEXPECTED_CHAR_AFTER_GT.get(filterString, 1384 startPos, filterString.charAt(l-1))); 1385 } 1386 } 1387 else 1388 { 1389 throw new LDAPException(ResultCode.FILTER_ERROR, 1390 ERR_FILTER_END_AFTER_GT.get(filterString, startPos)); 1391 } 1392 break attrNameLoop; 1393 1394 case '<': 1395 tempFilterType = FILTER_TYPE_LESS_OR_EQUAL; 1396 filterTypeKnown = true; 1397 attrEndPos = l - 1; 1398 1399 if (l <= r) 1400 { 1401 if (filterString.charAt(l++) != '=') 1402 { 1403 throw new LDAPException(ResultCode.FILTER_ERROR, 1404 ERR_FILTER_UNEXPECTED_CHAR_AFTER_LT.get(filterString, 1405 startPos, filterString.charAt(l-1))); 1406 } 1407 } 1408 else 1409 { 1410 throw new LDAPException(ResultCode.FILTER_ERROR, 1411 ERR_FILTER_END_AFTER_LT.get(filterString, startPos)); 1412 } 1413 break attrNameLoop; 1414 1415 case '~': 1416 tempFilterType = FILTER_TYPE_APPROXIMATE_MATCH; 1417 filterTypeKnown = true; 1418 attrEndPos = l - 1; 1419 1420 if (l <= r) 1421 { 1422 if (filterString.charAt(l++) != '=') 1423 { 1424 throw new LDAPException(ResultCode.FILTER_ERROR, 1425 ERR_FILTER_UNEXPECTED_CHAR_AFTER_TILDE.get(filterString, 1426 startPos, filterString.charAt(l-1))); 1427 } 1428 } 1429 else 1430 { 1431 throw new LDAPException(ResultCode.FILTER_ERROR, 1432 ERR_FILTER_END_AFTER_TILDE.get(filterString, startPos)); 1433 } 1434 break attrNameLoop; 1435 1436 case '=': 1437 // It could be either an equality, presence, or substring filter. 1438 // We'll need to look at the value to determine that. 1439 attrEndPos = l - 1; 1440 equalFound = true; 1441 break attrNameLoop; 1442 } 1443 } 1444 1445 if (attrEndPos <= attrStartPos) 1446 { 1447 if (equalFound) 1448 { 1449 throw new LDAPException(ResultCode.FILTER_ERROR, 1450 ERR_FILTER_EMPTY_ATTR_NAME.get(filterString, startPos)); 1451 } 1452 else 1453 { 1454 throw new LDAPException(ResultCode.FILTER_ERROR, 1455 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1456 } 1457 } 1458 attrName = filterString.substring(attrStartPos, attrEndPos); 1459 1460 1461 // See if we're dealing with an extensible match filter. If so, then 1462 // we may still need to do additional parsing to get the matching rule 1463 // ID and/or the dnAttributes flag. Otherwise, we can rule out any 1464 // variables that are specific to extensible matching filters. 1465 if (filterTypeKnown && (tempFilterType == FILTER_TYPE_EXTENSIBLE_MATCH)) 1466 { 1467 if (l > r) 1468 { 1469 throw new LDAPException(ResultCode.FILTER_ERROR, 1470 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1471 } 1472 1473 final char c = filterString.charAt(l++); 1474 if (c == '=') 1475 { 1476 matchingRuleID = null; 1477 dnAttributes = false; 1478 } 1479 else 1480 { 1481 // We have either a matching rule ID or a dnAttributes flag, or 1482 // both. Iterate through the filter until we find the equal sign, 1483 // and then figure out what we have from that. 1484 equalFound = false; 1485 final int substrStartPos = l - 1; 1486 while (l <= r) 1487 { 1488 if (filterString.charAt(l++) == '=') 1489 { 1490 equalFound = true; 1491 break; 1492 } 1493 } 1494 1495 if (! equalFound) 1496 { 1497 throw new LDAPException(ResultCode.FILTER_ERROR, 1498 ERR_FILTER_NO_EQUAL_SIGN.get(filterString, startPos)); 1499 } 1500 1501 final String substr = filterString.substring(substrStartPos, l-1); 1502 final String lowerSubstr = toLowerCase(substr); 1503 if (! substr.endsWith(":")) 1504 { 1505 throw new LDAPException(ResultCode.FILTER_ERROR, 1506 ERR_FILTER_CANNOT_PARSE_MRID.get(filterString, startPos)); 1507 } 1508 1509 if (lowerSubstr.equals("dn:")) 1510 { 1511 matchingRuleID = null; 1512 dnAttributes = true; 1513 } 1514 else if (lowerSubstr.startsWith("dn:")) 1515 { 1516 matchingRuleID = substr.substring(3, substr.length() - 1); 1517 if (matchingRuleID.length() == 0) 1518 { 1519 throw new LDAPException(ResultCode.FILTER_ERROR, 1520 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1521 } 1522 1523 dnAttributes = true; 1524 } 1525 else 1526 { 1527 matchingRuleID = substr.substring(0, substr.length() - 1); 1528 dnAttributes = false; 1529 1530 if (matchingRuleID.length() == 0) 1531 { 1532 throw new LDAPException(ResultCode.FILTER_ERROR, 1533 ERR_FILTER_EMPTY_MRID.get(filterString, startPos)); 1534 } 1535 } 1536 } 1537 } 1538 else 1539 { 1540 matchingRuleID = null; 1541 dnAttributes = false; 1542 } 1543 1544 1545 // At this point, we're ready to read the value. If we still don't 1546 // know what type of filter we're dealing with, then we can tell that 1547 // based on asterisks in the value. 1548 if (l > r) 1549 { 1550 assertionValue = new ASN1OctetString(); 1551 if (! filterTypeKnown) 1552 { 1553 tempFilterType = FILTER_TYPE_EQUALITY; 1554 } 1555 1556 subInitial = null; 1557 subAny = NO_SUB_ANY; 1558 subFinal = null; 1559 } 1560 else if (l == r) 1561 { 1562 if (filterTypeKnown) 1563 { 1564 switch (filterString.charAt(l)) 1565 { 1566 case '*': 1567 case '(': 1568 case ')': 1569 case '\\': 1570 throw new LDAPException(ResultCode.FILTER_ERROR, 1571 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, 1572 startPos, filterString.charAt(l))); 1573 } 1574 1575 assertionValue = 1576 new ASN1OctetString(filterString.substring(l, l+1)); 1577 } 1578 else 1579 { 1580 final char c = filterString.charAt(l); 1581 switch (c) 1582 { 1583 case '*': 1584 tempFilterType = FILTER_TYPE_PRESENCE; 1585 assertionValue = null; 1586 break; 1587 1588 case '\\': 1589 case '(': 1590 case ')': 1591 throw new LDAPException(ResultCode.FILTER_ERROR, 1592 ERR_FILTER_UNEXPECTED_CHAR_IN_AV.get(filterString, 1593 startPos, filterString.charAt(l))); 1594 1595 default: 1596 tempFilterType = FILTER_TYPE_EQUALITY; 1597 assertionValue = 1598 new ASN1OctetString(filterString.substring(l, l+1)); 1599 break; 1600 } 1601 } 1602 1603 subInitial = null; 1604 subAny = NO_SUB_ANY; 1605 subFinal = null; 1606 } 1607 else 1608 { 1609 if (! filterTypeKnown) 1610 { 1611 tempFilterType = FILTER_TYPE_EQUALITY; 1612 } 1613 1614 final int valueStartPos = l; 1615 ASN1OctetString tempSubInitial = null; 1616 ASN1OctetString tempSubFinal = null; 1617 final ArrayList<ASN1OctetString> subAnyList = 1618 new ArrayList<ASN1OctetString>(1); 1619 ByteStringBuffer buffer = new ByteStringBuffer(r - l + 1); 1620 while (l <= r) 1621 { 1622 final char c = filterString.charAt(l++); 1623 switch (c) 1624 { 1625 case '*': 1626 if (filterTypeKnown) 1627 { 1628 throw new LDAPException(ResultCode.FILTER_ERROR, 1629 ERR_FILTER_UNEXPECTED_ASTERISK.get(filterString, 1630 startPos)); 1631 } 1632 else 1633 { 1634 if ((l-1) == valueStartPos) 1635 { 1636 // The first character is an asterisk, so there is no 1637 // subInitial. 1638 } 1639 else 1640 { 1641 if (tempFilterType == FILTER_TYPE_SUBSTRING) 1642 { 1643 // We already know that it's a substring filter, so this 1644 // must be a subAny portion. However, if the buffer is 1645 // empty, then that means that there were two asterisks 1646 // right next to each other, which is invalid. 1647 if (buffer.length() == 0) 1648 { 1649 throw new LDAPException(ResultCode.FILTER_ERROR, 1650 ERR_FILTER_UNEXPECTED_DOUBLE_ASTERISK.get( 1651 filterString, startPos)); 1652 } 1653 else 1654 { 1655 subAnyList.add( 1656 new ASN1OctetString(buffer.toByteArray())); 1657 buffer = new ByteStringBuffer(r - l + 1); 1658 } 1659 } 1660 else 1661 { 1662 // We haven't yet set the filter type, so the buffer must 1663 // contain the subInitial portion. We also know it's not 1664 // empty because of an earlier check. 1665 tempSubInitial = 1666 new ASN1OctetString(buffer.toByteArray()); 1667 buffer = new ByteStringBuffer(r - l + 1); 1668 } 1669 } 1670 1671 tempFilterType = FILTER_TYPE_SUBSTRING; 1672 } 1673 break; 1674 1675 case '\\': 1676 l = readEscapedHexString(filterString, l, buffer); 1677 break; 1678 1679 case '(': 1680 throw new LDAPException(ResultCode.FILTER_ERROR, 1681 ERR_FILTER_UNEXPECTED_OPEN_PAREN.get(filterString, l)); 1682 1683 case ')': 1684 throw new LDAPException(ResultCode.FILTER_ERROR, 1685 ERR_FILTER_UNEXPECTED_CLOSE_PAREN.get(filterString, l)); 1686 1687 default: 1688 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 return matchingRule.matchesAnyValue(assertionValue, a.getRawValues()); 3208 3209 case FILTER_TYPE_SUBSTRING: 3210 a = entry.getAttribute(attrName, schema); 3211 if (a == null) 3212 { 3213 return false; 3214 } 3215 3216 matchingRule = 3217 MatchingRule.selectSubstringMatchingRule(attrName, schema); 3218 for (final ASN1OctetString v : a.getRawValues()) 3219 { 3220 if (matchingRule.matchesSubstring(v, subInitial, subAny, subFinal)) 3221 { 3222 return true; 3223 } 3224 } 3225 return false; 3226 3227 case FILTER_TYPE_GREATER_OR_EQUAL: 3228 a = entry.getAttribute(attrName, schema); 3229 if (a == null) 3230 { 3231 return false; 3232 } 3233 3234 matchingRule = 3235 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3236 for (final ASN1OctetString v : a.getRawValues()) 3237 { 3238 if (matchingRule.compareValues(v, assertionValue) >= 0) 3239 { 3240 return true; 3241 } 3242 } 3243 return false; 3244 3245 case FILTER_TYPE_LESS_OR_EQUAL: 3246 a = entry.getAttribute(attrName, schema); 3247 if (a == null) 3248 { 3249 return false; 3250 } 3251 3252 matchingRule = 3253 MatchingRule.selectOrderingMatchingRule(attrName, schema); 3254 for (final ASN1OctetString v : a.getRawValues()) 3255 { 3256 if (matchingRule.compareValues(v, assertionValue) <= 0) 3257 { 3258 return true; 3259 } 3260 } 3261 return false; 3262 3263 case FILTER_TYPE_PRESENCE: 3264 return (entry.hasAttribute(attrName)); 3265 3266 case FILTER_TYPE_APPROXIMATE_MATCH: 3267 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3268 ERR_FILTER_APPROXIMATE_MATCHING_NOT_SUPPORTED.get()); 3269 3270 case FILTER_TYPE_EXTENSIBLE_MATCH: 3271 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3272 ERR_FILTER_EXTENSIBLE_MATCHING_NOT_SUPPORTED.get()); 3273 3274 default: 3275 throw new LDAPException(ResultCode.PARAM_ERROR, 3276 ERR_FILTER_INVALID_TYPE.get()); 3277 } 3278 } 3279 3280 3281 3282 /** 3283 * Attempts to simplify the provided filter to allow it to be more efficiently 3284 * processed by the server. The simplifications it will make include: 3285 * <UL> 3286 * <LI>Any AND or OR filter that contains only a single filter component 3287 * will be converted to just that embedded filter component to eliminate 3288 * the unnecessary AND or OR wrapper. For example, the filter 3289 * "(&(uid=john.doe))" will be converted to just 3290 * "(uid=john.doe)".</LI> 3291 * <LI>Any AND components inside of an AND filter will be merged into the 3292 * outer AND filter. Any OR components inside of an OR filter will be 3293 * merged into the outer OR filter. For example, the filter 3294 * "(&(objectClass=person)(&(givenName=John)(sn=Doe)))" will be 3295 * converted to 3296 * "(&(objectClass=person)(givenName=John)(sn=Doe))".</LI> 3297 * <LI>If {@code reOrderElements} is true, then this method will attempt to 3298 * re-order the elements inside AND and OR filters in an attempt to 3299 * ensure that the components which are likely to be the most efficient 3300 * come earlier than those which are likely to be the least efficient. 3301 * This can speed up processing in servers that process filter 3302 * components in a left-to-right order.</LI> 3303 * </UL> 3304 * <BR><BR> 3305 * The simplification will happen recursively, in an attempt to generate a 3306 * filter that is as simple and efficient as possible. 3307 * 3308 * @param filter The filter to attempt to simplify. 3309 * @param reOrderElements Indicates whether this method may re-order the 3310 * elements in the filter so that, in a server that 3311 * evaluates the components in a left-to-right order, 3312 * the components which are likely to be more 3313 * efficient to process will be listed before those 3314 * which are likely to be less efficient. 3315 * 3316 * @return The simplified filter, or the original filter if the provided 3317 * filter is not one that can be simplified any further. 3318 */ 3319 public static Filter simplifyFilter(final Filter filter, 3320 final boolean reOrderElements) 3321 { 3322 final byte filterType = filter.filterType; 3323 switch (filterType) 3324 { 3325 case FILTER_TYPE_AND: 3326 case FILTER_TYPE_OR: 3327 // These will be handled below. 3328 break; 3329 3330 case FILTER_TYPE_NOT: 3331 // We may be able to simplify the filter component contained inside the 3332 // NOT. 3333 return createNOTFilter(simplifyFilter(filter.notComp, reOrderElements)); 3334 3335 default: 3336 // We can't simplify this filter, so just return what was provided. 3337 return filter; 3338 } 3339 3340 3341 // An AND filter with zero components is an LDAP true filter, and we can't 3342 // simplify that. An OR filter with zero components is an LDAP false 3343 // filter, and we can't simplify that either. The set of components 3344 // should never be null for an AND or OR filter, but if that happens to be 3345 // the case, then we'll return the original filter. 3346 final Filter[] components = filter.filterComps; 3347 if ((components == null) || (components.length == 0)) 3348 { 3349 return filter; 3350 } 3351 3352 3353 // For either an AND or an OR filter with just a single component, then just 3354 // return that embedded component. But simplify it first. 3355 if (components.length == 1) 3356 { 3357 return simplifyFilter(components[0], reOrderElements); 3358 } 3359 3360 3361 // If we've gotten here, then we have a filter with multiple components. 3362 // Simplify each of them to the extent possible, un-embed any ANDs 3363 // contained inside an AND or ORs contained inside an OR, and eliminate any 3364 // duplicate components in the resulting top-level filter. 3365 final LinkedHashSet<Filter> componentSet = new LinkedHashSet<Filter>(10); 3366 for (final Filter f : components) 3367 { 3368 final Filter simplifiedFilter = simplifyFilter(f, reOrderElements); 3369 if (simplifiedFilter.filterType == FILTER_TYPE_AND) 3370 { 3371 if (filterType == FILTER_TYPE_AND) 3372 { 3373 // This is an AND nested inside an AND. In that case, we'll just put 3374 // all the nested components inside the outer AND. 3375 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3376 } 3377 else 3378 { 3379 componentSet.add(simplifiedFilter); 3380 } 3381 } 3382 else if (simplifiedFilter.filterType == FILTER_TYPE_OR) 3383 { 3384 if (filterType == FILTER_TYPE_OR) 3385 { 3386 // This is an OR nested inside an OR. In that case, we'll just put 3387 // all the nested components inside the outer OR. 3388 componentSet.addAll(Arrays.asList(simplifiedFilter.filterComps)); 3389 } 3390 else 3391 { 3392 componentSet.add(simplifiedFilter); 3393 } 3394 } 3395 else 3396 { 3397 componentSet.add(simplifiedFilter); 3398 } 3399 } 3400 3401 3402 // It's possible at this point that we are down to just a single component. 3403 // That can happen if the filter was an AND or an OR with a duplicate 3404 // element, like "(&(a=b)(a=b))". In that case, just return that one 3405 // component. 3406 if (componentSet.size() == 1) 3407 { 3408 return componentSet.iterator().next(); 3409 } 3410 3411 3412 // If we should re-order the components, then use the following priority 3413 // list: 3414 // 3415 // 1. Equality components that target an attribute other than objectClass. 3416 // These are most likely to require only a single database lookup to get 3417 // the candidate list, and that candidate list will frequently be small. 3418 // 2. Equality components that target the objectClass attribute. These are 3419 // likely to require only a single database lookup to get the candidate 3420 // list, but the candidate list is more likely to be larger. 3421 // 3. Approximate match components. These are also likely to require only 3422 // a single database lookup to get the candidate list, but that 3423 // candidate list is likely to have a larger number of candidates. 3424 // 4. Presence components that target an attribute other than objectClass. 3425 // These are also likely to require only a single database lookup to get 3426 // the candidate list, but are likely to have a large number of 3427 // candidates. 3428 // 5. Substring components that have a subInitial element. These are 3429 // generally the most efficient substring filters to process, requiring 3430 // access to fewer database keys than substring filters with only subAny 3431 // and/or subFinal components. 3432 // 6. Substring components that only have subAny and/or subFinal elements. 3433 // These will probably require a number of database lookups and will 3434 // probably result in large candidate lists. 3435 // 7. Greater-or-equal components and less-or-equal components. These 3436 // will probably require a number of database lookups and will probably 3437 // result in large candidate lists. 3438 // 8. Extensible match components. Even if these are indexed, there isn't 3439 // any good way to know how expensive they might be to process or how 3440 // big the candidate list might be. 3441 // 9. Presence components that target the objectClass attribute. This is 3442 // likely to require only a single database lookup to get the candidate 3443 // list, but the candidate list will also be extremely large (if it's 3444 // indexed at all) since it will match every entry. 3445 // 10. NOT components. These are generally not possible to index and 3446 // therefore cannot be used to create a candidate list. 3447 // 3448 // AND and OR components will be ordered according to the first of their 3449 // embedded components Since the filter has already been simplified, then 3450 // the first element in the list will be the one we think will be the most 3451 // efficient to process. 3452 if (reOrderElements) 3453 { 3454 final TreeMap<Integer,LinkedHashSet<Filter>> m = 3455 new TreeMap<Integer,LinkedHashSet<Filter>>(); 3456 for (final Filter f : componentSet) 3457 { 3458 final Filter prioritizeComp; 3459 if ((f.filterType == FILTER_TYPE_AND) || 3460 (f.filterType == FILTER_TYPE_OR)) 3461 { 3462 if (f.filterComps.length > 0) 3463 { 3464 prioritizeComp = f.filterComps[0]; 3465 } 3466 else 3467 { 3468 prioritizeComp = f; 3469 } 3470 } 3471 else 3472 { 3473 prioritizeComp = f; 3474 } 3475 3476 final Integer slot; 3477 switch (prioritizeComp.filterType) 3478 { 3479 case FILTER_TYPE_EQUALITY: 3480 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3481 { 3482 slot = 2; 3483 } 3484 else 3485 { 3486 slot = 1; 3487 } 3488 break; 3489 3490 case FILTER_TYPE_APPROXIMATE_MATCH: 3491 slot = 3; 3492 break; 3493 3494 case FILTER_TYPE_PRESENCE: 3495 if (prioritizeComp.attrName.equalsIgnoreCase("objectClass")) 3496 { 3497 slot = 9; 3498 } 3499 else 3500 { 3501 slot = 4; 3502 } 3503 break; 3504 3505 case FILTER_TYPE_SUBSTRING: 3506 if (prioritizeComp.subInitial == null) 3507 { 3508 slot = 6; 3509 } 3510 else 3511 { 3512 slot = 5; 3513 } 3514 break; 3515 3516 case FILTER_TYPE_GREATER_OR_EQUAL: 3517 case FILTER_TYPE_LESS_OR_EQUAL: 3518 slot = 7; 3519 break; 3520 3521 case FILTER_TYPE_EXTENSIBLE_MATCH: 3522 slot = 8; 3523 break; 3524 3525 case FILTER_TYPE_NOT: 3526 default: 3527 slot = 10; 3528 break; 3529 } 3530 3531 LinkedHashSet<Filter> filterSet = m.get(slot-1); 3532 if (filterSet == null) 3533 { 3534 filterSet = new LinkedHashSet<Filter>(10); 3535 m.put(slot-1, filterSet); 3536 } 3537 filterSet.add(f); 3538 } 3539 3540 componentSet.clear(); 3541 for (final LinkedHashSet<Filter> filterSet : m.values()) 3542 { 3543 componentSet.addAll(filterSet); 3544 } 3545 } 3546 3547 3548 // Return the new, possibly simplified filter. 3549 if (filterType == FILTER_TYPE_AND) 3550 { 3551 return createANDFilter(componentSet); 3552 } 3553 else 3554 { 3555 return createORFilter(componentSet); 3556 } 3557 } 3558 3559 3560 3561 /** 3562 * Generates a hash code for this search filter. 3563 * 3564 * @return The generated hash code for this search filter. 3565 */ 3566 @Override() 3567 public int hashCode() 3568 { 3569 final CaseIgnoreStringMatchingRule matchingRule = 3570 CaseIgnoreStringMatchingRule.getInstance(); 3571 int hashCode = filterType; 3572 3573 switch (filterType) 3574 { 3575 case FILTER_TYPE_AND: 3576 case FILTER_TYPE_OR: 3577 for (final Filter f : filterComps) 3578 { 3579 hashCode += f.hashCode(); 3580 } 3581 break; 3582 3583 case FILTER_TYPE_NOT: 3584 hashCode += notComp.hashCode(); 3585 break; 3586 3587 case FILTER_TYPE_EQUALITY: 3588 case FILTER_TYPE_GREATER_OR_EQUAL: 3589 case FILTER_TYPE_LESS_OR_EQUAL: 3590 case FILTER_TYPE_APPROXIMATE_MATCH: 3591 hashCode += toLowerCase(attrName).hashCode(); 3592 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3593 break; 3594 3595 case FILTER_TYPE_SUBSTRING: 3596 hashCode += toLowerCase(attrName).hashCode(); 3597 if (subInitial != null) 3598 { 3599 hashCode += matchingRule.normalizeSubstring(subInitial, 3600 MatchingRule.SUBSTRING_TYPE_SUBINITIAL).hashCode(); 3601 } 3602 for (final ASN1OctetString s : subAny) 3603 { 3604 hashCode += matchingRule.normalizeSubstring(s, 3605 MatchingRule.SUBSTRING_TYPE_SUBANY).hashCode(); 3606 } 3607 if (subFinal != null) 3608 { 3609 hashCode += matchingRule.normalizeSubstring(subFinal, 3610 MatchingRule.SUBSTRING_TYPE_SUBFINAL).hashCode(); 3611 } 3612 break; 3613 3614 case FILTER_TYPE_PRESENCE: 3615 hashCode += toLowerCase(attrName).hashCode(); 3616 break; 3617 3618 case FILTER_TYPE_EXTENSIBLE_MATCH: 3619 if (attrName != null) 3620 { 3621 hashCode += toLowerCase(attrName).hashCode(); 3622 } 3623 3624 if (matchingRuleID != null) 3625 { 3626 hashCode += toLowerCase(matchingRuleID).hashCode(); 3627 } 3628 3629 if (dnAttributes) 3630 { 3631 hashCode++; 3632 } 3633 3634 hashCode += matchingRule.normalize(assertionValue).hashCode(); 3635 break; 3636 } 3637 3638 return hashCode; 3639 } 3640 3641 3642 3643 /** 3644 * Indicates whether the provided object is equal to this search filter. 3645 * 3646 * @param o The object for which to make the determination. 3647 * 3648 * @return {@code true} if the provided object can be considered equal to 3649 * this search filter, or {@code false} if not. 3650 */ 3651 @Override() 3652 public boolean equals(final Object o) 3653 { 3654 if (o == null) 3655 { 3656 return false; 3657 } 3658 3659 if (o == this) 3660 { 3661 return true; 3662 } 3663 3664 if (! (o instanceof Filter)) 3665 { 3666 return false; 3667 } 3668 3669 final Filter f = (Filter) o; 3670 if (filterType != f.filterType) 3671 { 3672 return false; 3673 } 3674 3675 final CaseIgnoreStringMatchingRule matchingRule = 3676 CaseIgnoreStringMatchingRule.getInstance(); 3677 3678 switch (filterType) 3679 { 3680 case FILTER_TYPE_AND: 3681 case FILTER_TYPE_OR: 3682 if (filterComps.length != f.filterComps.length) 3683 { 3684 return false; 3685 } 3686 3687 final HashSet<Filter> compSet = new HashSet<Filter>(); 3688 compSet.addAll(Arrays.asList(filterComps)); 3689 3690 for (final Filter filterComp : f.filterComps) 3691 { 3692 if (! compSet.remove(filterComp)) 3693 { 3694 return false; 3695 } 3696 } 3697 3698 return true; 3699 3700 3701 case FILTER_TYPE_NOT: 3702 return notComp.equals(f.notComp); 3703 3704 3705 case FILTER_TYPE_EQUALITY: 3706 case FILTER_TYPE_GREATER_OR_EQUAL: 3707 case FILTER_TYPE_LESS_OR_EQUAL: 3708 case FILTER_TYPE_APPROXIMATE_MATCH: 3709 return (attrName.equalsIgnoreCase(f.attrName) && 3710 matchingRule.valuesMatch(assertionValue, f.assertionValue)); 3711 3712 3713 case FILTER_TYPE_SUBSTRING: 3714 if (! attrName.equalsIgnoreCase(f.attrName)) 3715 { 3716 return false; 3717 } 3718 3719 if (subAny.length != f.subAny.length) 3720 { 3721 return false; 3722 } 3723 3724 if (subInitial == null) 3725 { 3726 if (f.subInitial != null) 3727 { 3728 return false; 3729 } 3730 } 3731 else 3732 { 3733 if (f.subInitial == null) 3734 { 3735 return false; 3736 } 3737 3738 final ASN1OctetString si1 = matchingRule.normalizeSubstring( 3739 subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3740 final ASN1OctetString si2 = matchingRule.normalizeSubstring( 3741 f.subInitial, MatchingRule.SUBSTRING_TYPE_SUBINITIAL); 3742 if (! si1.equals(si2)) 3743 { 3744 return false; 3745 } 3746 } 3747 3748 for (int i=0; i < subAny.length; i++) 3749 { 3750 final ASN1OctetString sa1 = matchingRule.normalizeSubstring(subAny[i], 3751 MatchingRule.SUBSTRING_TYPE_SUBANY); 3752 final ASN1OctetString sa2 = matchingRule.normalizeSubstring( 3753 f.subAny[i], MatchingRule.SUBSTRING_TYPE_SUBANY); 3754 if (! sa1.equals(sa2)) 3755 { 3756 return false; 3757 } 3758 } 3759 3760 if (subFinal == null) 3761 { 3762 if (f.subFinal != null) 3763 { 3764 return false; 3765 } 3766 } 3767 else 3768 { 3769 if (f.subFinal == null) 3770 { 3771 return false; 3772 } 3773 3774 final ASN1OctetString sf1 = matchingRule.normalizeSubstring(subFinal, 3775 MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3776 final ASN1OctetString sf2 = matchingRule.normalizeSubstring( 3777 f.subFinal, MatchingRule.SUBSTRING_TYPE_SUBFINAL); 3778 if (! sf1.equals(sf2)) 3779 { 3780 return false; 3781 } 3782 } 3783 3784 return true; 3785 3786 3787 case FILTER_TYPE_PRESENCE: 3788 return (attrName.equalsIgnoreCase(f.attrName)); 3789 3790 3791 case FILTER_TYPE_EXTENSIBLE_MATCH: 3792 if (attrName == null) 3793 { 3794 if (f.attrName != null) 3795 { 3796 return false; 3797 } 3798 } 3799 else 3800 { 3801 if (f.attrName == null) 3802 { 3803 return false; 3804 } 3805 else 3806 { 3807 if (! attrName.equalsIgnoreCase(f.attrName)) 3808 { 3809 return false; 3810 } 3811 } 3812 } 3813 3814 if (matchingRuleID == null) 3815 { 3816 if (f.matchingRuleID != null) 3817 { 3818 return false; 3819 } 3820 } 3821 else 3822 { 3823 if (f.matchingRuleID == null) 3824 { 3825 return false; 3826 } 3827 else 3828 { 3829 if (! matchingRuleID.equalsIgnoreCase(f.matchingRuleID)) 3830 { 3831 return false; 3832 } 3833 } 3834 } 3835 3836 if (dnAttributes != f.dnAttributes) 3837 { 3838 return false; 3839 } 3840 3841 return matchingRule.valuesMatch(assertionValue, f.assertionValue); 3842 3843 3844 default: 3845 return false; 3846 } 3847 } 3848 3849 3850 3851 /** 3852 * Retrieves a string representation of this search filter. 3853 * 3854 * @return A string representation of this search filter. 3855 */ 3856 @Override() 3857 public String toString() 3858 { 3859 if (filterString == null) 3860 { 3861 final StringBuilder buffer = new StringBuilder(); 3862 toString(buffer); 3863 filterString = buffer.toString(); 3864 } 3865 3866 return filterString; 3867 } 3868 3869 3870 3871 /** 3872 * Appends a string representation of this search filter to the provided 3873 * buffer. 3874 * 3875 * @param buffer The buffer to which to append a string representation of 3876 * this search filter. 3877 */ 3878 public void toString(final StringBuilder buffer) 3879 { 3880 switch (filterType) 3881 { 3882 case FILTER_TYPE_AND: 3883 buffer.append("(&"); 3884 for (final Filter f : filterComps) 3885 { 3886 f.toString(buffer); 3887 } 3888 buffer.append(')'); 3889 break; 3890 3891 case FILTER_TYPE_OR: 3892 buffer.append("(|"); 3893 for (final Filter f : filterComps) 3894 { 3895 f.toString(buffer); 3896 } 3897 buffer.append(')'); 3898 break; 3899 3900 case FILTER_TYPE_NOT: 3901 buffer.append("(!"); 3902 notComp.toString(buffer); 3903 buffer.append(')'); 3904 break; 3905 3906 case FILTER_TYPE_EQUALITY: 3907 buffer.append('('); 3908 buffer.append(attrName); 3909 buffer.append('='); 3910 encodeValue(assertionValue, buffer); 3911 buffer.append(')'); 3912 break; 3913 3914 case FILTER_TYPE_SUBSTRING: 3915 buffer.append('('); 3916 buffer.append(attrName); 3917 buffer.append('='); 3918 if (subInitial != null) 3919 { 3920 encodeValue(subInitial, buffer); 3921 } 3922 buffer.append('*'); 3923 for (final ASN1OctetString s : subAny) 3924 { 3925 encodeValue(s, buffer); 3926 buffer.append('*'); 3927 } 3928 if (subFinal != null) 3929 { 3930 encodeValue(subFinal, buffer); 3931 } 3932 buffer.append(')'); 3933 break; 3934 3935 case FILTER_TYPE_GREATER_OR_EQUAL: 3936 buffer.append('('); 3937 buffer.append(attrName); 3938 buffer.append(">="); 3939 encodeValue(assertionValue, buffer); 3940 buffer.append(')'); 3941 break; 3942 3943 case FILTER_TYPE_LESS_OR_EQUAL: 3944 buffer.append('('); 3945 buffer.append(attrName); 3946 buffer.append("<="); 3947 encodeValue(assertionValue, buffer); 3948 buffer.append(')'); 3949 break; 3950 3951 case FILTER_TYPE_PRESENCE: 3952 buffer.append('('); 3953 buffer.append(attrName); 3954 buffer.append("=*)"); 3955 break; 3956 3957 case FILTER_TYPE_APPROXIMATE_MATCH: 3958 buffer.append('('); 3959 buffer.append(attrName); 3960 buffer.append("~="); 3961 encodeValue(assertionValue, buffer); 3962 buffer.append(')'); 3963 break; 3964 3965 case FILTER_TYPE_EXTENSIBLE_MATCH: 3966 buffer.append('('); 3967 if (attrName != null) 3968 { 3969 buffer.append(attrName); 3970 } 3971 3972 if (dnAttributes) 3973 { 3974 buffer.append(":dn"); 3975 } 3976 3977 if (matchingRuleID != null) 3978 { 3979 buffer.append(':'); 3980 buffer.append(matchingRuleID); 3981 } 3982 3983 buffer.append(":="); 3984 encodeValue(assertionValue, buffer); 3985 buffer.append(')'); 3986 break; 3987 } 3988 } 3989 3990 3991 3992 /** 3993 * Retrieves a normalized string representation of this search filter. 3994 * 3995 * @return A normalized string representation of this search filter. 3996 */ 3997 public String toNormalizedString() 3998 { 3999 if (normalizedString == null) 4000 { 4001 final StringBuilder buffer = new StringBuilder(); 4002 toNormalizedString(buffer); 4003 normalizedString = buffer.toString(); 4004 } 4005 4006 return normalizedString; 4007 } 4008 4009 4010 4011 /** 4012 * Appends a normalized string representation of this search filter to the 4013 * provided buffer. 4014 * 4015 * @param buffer The buffer to which to append a normalized string 4016 * representation of this search filter. 4017 */ 4018 public void toNormalizedString(final StringBuilder buffer) 4019 { 4020 final CaseIgnoreStringMatchingRule mr = 4021 CaseIgnoreStringMatchingRule.getInstance(); 4022 4023 switch (filterType) 4024 { 4025 case FILTER_TYPE_AND: 4026 buffer.append("(&"); 4027 for (final Filter f : filterComps) 4028 { 4029 f.toNormalizedString(buffer); 4030 } 4031 buffer.append(')'); 4032 break; 4033 4034 case FILTER_TYPE_OR: 4035 buffer.append("(|"); 4036 for (final Filter f : filterComps) 4037 { 4038 f.toNormalizedString(buffer); 4039 } 4040 buffer.append(')'); 4041 break; 4042 4043 case FILTER_TYPE_NOT: 4044 buffer.append("(!"); 4045 notComp.toNormalizedString(buffer); 4046 buffer.append(')'); 4047 break; 4048 4049 case FILTER_TYPE_EQUALITY: 4050 buffer.append('('); 4051 buffer.append(toLowerCase(attrName)); 4052 buffer.append('='); 4053 encodeValue(mr.normalize(assertionValue), buffer); 4054 buffer.append(')'); 4055 break; 4056 4057 case FILTER_TYPE_SUBSTRING: 4058 buffer.append('('); 4059 buffer.append(toLowerCase(attrName)); 4060 buffer.append('='); 4061 if (subInitial != null) 4062 { 4063 encodeValue(mr.normalizeSubstring(subInitial, 4064 MatchingRule.SUBSTRING_TYPE_SUBINITIAL), buffer); 4065 } 4066 buffer.append('*'); 4067 for (final ASN1OctetString s : subAny) 4068 { 4069 encodeValue(mr.normalizeSubstring(s, 4070 MatchingRule.SUBSTRING_TYPE_SUBANY), buffer); 4071 buffer.append('*'); 4072 } 4073 if (subFinal != null) 4074 { 4075 encodeValue(mr.normalizeSubstring(subFinal, 4076 MatchingRule.SUBSTRING_TYPE_SUBFINAL), buffer); 4077 } 4078 buffer.append(')'); 4079 break; 4080 4081 case FILTER_TYPE_GREATER_OR_EQUAL: 4082 buffer.append('('); 4083 buffer.append(toLowerCase(attrName)); 4084 buffer.append(">="); 4085 encodeValue(mr.normalize(assertionValue), buffer); 4086 buffer.append(')'); 4087 break; 4088 4089 case FILTER_TYPE_LESS_OR_EQUAL: 4090 buffer.append('('); 4091 buffer.append(toLowerCase(attrName)); 4092 buffer.append("<="); 4093 encodeValue(mr.normalize(assertionValue), buffer); 4094 buffer.append(')'); 4095 break; 4096 4097 case FILTER_TYPE_PRESENCE: 4098 buffer.append('('); 4099 buffer.append(toLowerCase(attrName)); 4100 buffer.append("=*)"); 4101 break; 4102 4103 case FILTER_TYPE_APPROXIMATE_MATCH: 4104 buffer.append('('); 4105 buffer.append(toLowerCase(attrName)); 4106 buffer.append("~="); 4107 encodeValue(mr.normalize(assertionValue), buffer); 4108 buffer.append(')'); 4109 break; 4110 4111 case FILTER_TYPE_EXTENSIBLE_MATCH: 4112 buffer.append('('); 4113 if (attrName != null) 4114 { 4115 buffer.append(toLowerCase(attrName)); 4116 } 4117 4118 if (dnAttributes) 4119 { 4120 buffer.append(":dn"); 4121 } 4122 4123 if (matchingRuleID != null) 4124 { 4125 buffer.append(':'); 4126 buffer.append(toLowerCase(matchingRuleID)); 4127 } 4128 4129 buffer.append(":="); 4130 encodeValue(mr.normalize(assertionValue), buffer); 4131 buffer.append(')'); 4132 break; 4133 } 4134 } 4135 4136 4137 4138 /** 4139 * Encodes the provided value into a form suitable for use as the assertion 4140 * value in the string representation of a search filter. Parentheses, 4141 * asterisks, backslashes, null characters, and any non-ASCII characters will 4142 * be escaped using a backslash before the hexadecimal representation of each 4143 * byte in the character to escape. 4144 * 4145 * @param value The value to be encoded. It must not be {@code null}. 4146 * 4147 * @return The encoded representation of the provided string. 4148 */ 4149 public static String encodeValue(final String value) 4150 { 4151 ensureNotNull(value); 4152 4153 final StringBuilder buffer = new StringBuilder(); 4154 encodeValue(new ASN1OctetString(value), buffer); 4155 return buffer.toString(); 4156 } 4157 4158 4159 4160 /** 4161 * Encodes the provided value into a form suitable for use as the assertion 4162 * value in the string representation of a search filter. Parentheses, 4163 * asterisks, backslashes, null characters, and any non-ASCII characters will 4164 * be escaped using a backslash before the hexadecimal representation of each 4165 * byte in the character to escape. 4166 * 4167 * @param value The value to be encoded. It must not be {@code null}. 4168 * 4169 * @return The encoded representation of the provided string. 4170 */ 4171 public static String encodeValue(final byte[]value) 4172 { 4173 ensureNotNull(value); 4174 4175 final StringBuilder buffer = new StringBuilder(); 4176 encodeValue(new ASN1OctetString(value), buffer); 4177 return buffer.toString(); 4178 } 4179 4180 4181 4182 /** 4183 * Appends the assertion value for this filter to the provided buffer, 4184 * encoding any special characters as necessary. 4185 * 4186 * @param value The value to be encoded. 4187 * @param buffer The buffer to which the assertion value should be appended. 4188 */ 4189 public static void encodeValue(final ASN1OctetString value, 4190 final StringBuilder buffer) 4191 { 4192 final byte[] valueBytes = value.getValue(); 4193 for (int i=0; i < valueBytes.length; i++) 4194 { 4195 switch (numBytesInUTF8CharacterWithFirstByte(valueBytes[i])) 4196 { 4197 case 1: 4198 // This character is ASCII, but might still need to be escaped. We'll 4199 // escape anything 4200 if ((valueBytes[i] <= 0x1F) || // Non-printable ASCII characters. 4201 (valueBytes[i] == 0x28) || // Open parenthesis 4202 (valueBytes[i] == 0x29) || // Close parenthesis 4203 (valueBytes[i] == 0x2A) || // Asterisk 4204 (valueBytes[i] == 0x5C) || // Backslash 4205 (valueBytes[i] == 0x7F)) // DEL 4206 { 4207 buffer.append('\\'); 4208 toHex(valueBytes[i], buffer); 4209 } 4210 else 4211 { 4212 buffer.append((char) valueBytes[i]); 4213 } 4214 break; 4215 4216 case 2: 4217 // If there are at least two bytes left, then we'll hex-encode the 4218 // next two bytes. Otherwise we'll hex-encode whatever is left. 4219 buffer.append('\\'); 4220 toHex(valueBytes[i++], buffer); 4221 if (i < valueBytes.length) 4222 { 4223 buffer.append('\\'); 4224 toHex(valueBytes[i], buffer); 4225 } 4226 break; 4227 4228 case 3: 4229 // If there are at least three bytes left, then we'll hex-encode the 4230 // next three bytes. Otherwise we'll hex-encode whatever is left. 4231 buffer.append('\\'); 4232 toHex(valueBytes[i++], buffer); 4233 if (i < valueBytes.length) 4234 { 4235 buffer.append('\\'); 4236 toHex(valueBytes[i++], buffer); 4237 } 4238 if (i < valueBytes.length) 4239 { 4240 buffer.append('\\'); 4241 toHex(valueBytes[i], buffer); 4242 } 4243 break; 4244 4245 case 4: 4246 // If there are at least four bytes left, then we'll hex-encode the 4247 // next four bytes. Otherwise we'll hex-encode whatever is left. 4248 buffer.append('\\'); 4249 toHex(valueBytes[i++], buffer); 4250 if (i < valueBytes.length) 4251 { 4252 buffer.append('\\'); 4253 toHex(valueBytes[i++], buffer); 4254 } 4255 if (i < valueBytes.length) 4256 { 4257 buffer.append('\\'); 4258 toHex(valueBytes[i++], buffer); 4259 } 4260 if (i < valueBytes.length) 4261 { 4262 buffer.append('\\'); 4263 toHex(valueBytes[i], buffer); 4264 } 4265 break; 4266 4267 default: 4268 // We'll hex-encode whatever is left in the buffer. 4269 while (i < valueBytes.length) 4270 { 4271 buffer.append('\\'); 4272 toHex(valueBytes[i++], buffer); 4273 } 4274 break; 4275 } 4276 } 4277 } 4278 4279 4280 4281 /** 4282 * Appends a number of lines comprising the Java source code that can be used 4283 * to recreate this filter to the given list. Note that unless a first line 4284 * prefix and/or last line suffix are provided, this will just include the 4285 * code for the static method used to create the filter, starting with 4286 * "Filter.createXFilter(" and ending with the closing parenthesis for that 4287 * method call. 4288 * 4289 * @param lineList The list to which the source code lines should be 4290 * added. 4291 * @param indentSpaces The number of spaces that should be used to indent 4292 * the generated code. It must not be negative. 4293 * @param firstLinePrefix An optional string that should precede the static 4294 * method call (e.g., it could be used for an 4295 * attribute assignment, like "Filter f = "). It may 4296 * be {@code null} or empty if there should be no 4297 * first line prefix. 4298 * @param lastLineSuffix An optional suffix that should follow the closing 4299 * parenthesis of the static method call (e.g., it 4300 * could be a semicolon to represent the end of a 4301 * Java statement). It may be {@code null} or empty 4302 * if there should be no last line suffix. 4303 */ 4304 public void toCode(final List<String> lineList, final int indentSpaces, 4305 final String firstLinePrefix, final String lastLineSuffix) 4306 { 4307 // Generate a string with the appropriate indent. 4308 final StringBuilder buffer = new StringBuilder(); 4309 for (int i = 0; i < indentSpaces; i++) 4310 { 4311 buffer.append(' '); 4312 } 4313 final String indent = buffer.toString(); 4314 4315 4316 // Start the first line, including any appropriate prefix. 4317 buffer.setLength(0); 4318 buffer.append(indent); 4319 if (firstLinePrefix != null) 4320 { 4321 buffer.append(firstLinePrefix); 4322 } 4323 4324 4325 // Figure out what type of filter it is and create the appropriate code for 4326 // that type of filter. 4327 switch (filterType) 4328 { 4329 case FILTER_TYPE_AND: 4330 case FILTER_TYPE_OR: 4331 if (filterType == FILTER_TYPE_AND) 4332 { 4333 buffer.append("Filter.createANDFilter("); 4334 } 4335 else 4336 { 4337 buffer.append("Filter.createORFilter("); 4338 } 4339 if (filterComps.length == 0) 4340 { 4341 buffer.append(')'); 4342 if (lastLineSuffix != null) 4343 { 4344 buffer.append(lastLineSuffix); 4345 } 4346 lineList.add(buffer.toString()); 4347 return; 4348 } 4349 4350 for (int i = 0; i < filterComps.length; i++) 4351 { 4352 String suffix; 4353 if (i == (filterComps.length - 1)) 4354 { 4355 suffix = ")"; 4356 if (lastLineSuffix != null) 4357 { 4358 suffix += lastLineSuffix; 4359 } 4360 } 4361 else 4362 { 4363 suffix = ","; 4364 } 4365 4366 filterComps[i].toCode(lineList, indentSpaces + 5, null, suffix); 4367 } 4368 return; 4369 4370 4371 case FILTER_TYPE_NOT: 4372 buffer.append("Filter.createNOTFilter("); 4373 lineList.add(buffer.toString()); 4374 4375 final String suffix; 4376 if (lastLineSuffix == null) 4377 { 4378 suffix = ")"; 4379 } 4380 else 4381 { 4382 suffix = ')' + lastLineSuffix; 4383 } 4384 notComp.toCode(lineList, indentSpaces + 5, null, suffix); 4385 return; 4386 4387 case FILTER_TYPE_PRESENCE: 4388 buffer.append("Filter.createPresenceFilter("); 4389 lineList.add(buffer.toString()); 4390 4391 buffer.setLength(0); 4392 buffer.append(indent); 4393 buffer.append(" \""); 4394 buffer.append(attrName); 4395 buffer.append("\")"); 4396 4397 if (lastLineSuffix != null) 4398 { 4399 buffer.append(lastLineSuffix); 4400 } 4401 4402 lineList.add(buffer.toString()); 4403 return; 4404 4405 4406 case FILTER_TYPE_EQUALITY: 4407 case FILTER_TYPE_GREATER_OR_EQUAL: 4408 case FILTER_TYPE_LESS_OR_EQUAL: 4409 case FILTER_TYPE_APPROXIMATE_MATCH: 4410 if (filterType == FILTER_TYPE_EQUALITY) 4411 { 4412 buffer.append("Filter.createEqualityFilter("); 4413 } 4414 else if (filterType == FILTER_TYPE_GREATER_OR_EQUAL) 4415 { 4416 buffer.append("Filter.createGreaterOrEqualFilter("); 4417 } 4418 else if (filterType == FILTER_TYPE_LESS_OR_EQUAL) 4419 { 4420 buffer.append("Filter.createLessOrEqualFilter("); 4421 } 4422 else 4423 { 4424 buffer.append("Filter.createApproximateMatchFilter("); 4425 } 4426 lineList.add(buffer.toString()); 4427 4428 buffer.setLength(0); 4429 buffer.append(indent); 4430 buffer.append(" \""); 4431 buffer.append(attrName); 4432 buffer.append("\","); 4433 lineList.add(buffer.toString()); 4434 4435 buffer.setLength(0); 4436 buffer.append(indent); 4437 buffer.append(" "); 4438 if (isSensitiveToCodeAttribute(attrName)) 4439 { 4440 buffer.append("\"---redacted-value---\""); 4441 } 4442 else if (isPrintableString(assertionValue.getValue())) 4443 { 4444 buffer.append('"'); 4445 buffer.append(assertionValue.stringValue()); 4446 buffer.append('"'); 4447 } 4448 else 4449 { 4450 byteArrayToCode(assertionValue.getValue(), buffer); 4451 } 4452 4453 buffer.append(')'); 4454 4455 if (lastLineSuffix != null) 4456 { 4457 buffer.append(lastLineSuffix); 4458 } 4459 4460 lineList.add(buffer.toString()); 4461 return; 4462 4463 4464 case FILTER_TYPE_SUBSTRING: 4465 buffer.append("Filter.createSubstringFilter("); 4466 lineList.add(buffer.toString()); 4467 4468 buffer.setLength(0); 4469 buffer.append(indent); 4470 buffer.append(" \""); 4471 buffer.append(attrName); 4472 buffer.append("\","); 4473 lineList.add(buffer.toString()); 4474 4475 final boolean isRedacted = isSensitiveToCodeAttribute(attrName); 4476 boolean isPrintable = true; 4477 if (subInitial != null) 4478 { 4479 isPrintable = isPrintableString(subInitial.getValue()); 4480 } 4481 4482 if (isPrintable && (subAny != null)) 4483 { 4484 for (final ASN1OctetString s : subAny) 4485 { 4486 if (! isPrintableString(s.getValue())) 4487 { 4488 isPrintable = false; 4489 break; 4490 } 4491 } 4492 } 4493 4494 if (isPrintable && (subFinal != null)) 4495 { 4496 isPrintable = isPrintableString(subFinal.getValue()); 4497 } 4498 4499 buffer.setLength(0); 4500 buffer.append(indent); 4501 buffer.append(" "); 4502 if (subInitial == null) 4503 { 4504 buffer.append("null"); 4505 } 4506 else if (isRedacted) 4507 { 4508 buffer.append("\"---redacted-subInitial---\""); 4509 } 4510 else if (isPrintable) 4511 { 4512 buffer.append('"'); 4513 buffer.append(subInitial.stringValue()); 4514 buffer.append('"'); 4515 } 4516 else 4517 { 4518 byteArrayToCode(subInitial.getValue(), buffer); 4519 } 4520 buffer.append(','); 4521 lineList.add(buffer.toString()); 4522 4523 buffer.setLength(0); 4524 buffer.append(indent); 4525 buffer.append(" "); 4526 if ((subAny == null) || (subAny.length == 0)) 4527 { 4528 buffer.append("null,"); 4529 lineList.add(buffer.toString()); 4530 } 4531 else if (isRedacted) 4532 { 4533 buffer.append("new String[]"); 4534 lineList.add(buffer.toString()); 4535 4536 lineList.add(indent + " {"); 4537 4538 for (int i=0; i < subAny.length; i++) 4539 { 4540 buffer.setLength(0); 4541 buffer.append(indent); 4542 buffer.append(" \"---redacted-subAny-"); 4543 buffer.append(i+1); 4544 buffer.append("---\""); 4545 if (i < (subAny.length-1)) 4546 { 4547 buffer.append(','); 4548 } 4549 lineList.add(buffer.toString()); 4550 } 4551 4552 lineList.add(indent + " },"); 4553 } 4554 else if (isPrintable) 4555 { 4556 buffer.append("new String[]"); 4557 lineList.add(buffer.toString()); 4558 4559 lineList.add(indent + " {"); 4560 4561 for (int i=0; i < subAny.length; i++) 4562 { 4563 buffer.setLength(0); 4564 buffer.append(indent); 4565 buffer.append(" \""); 4566 buffer.append(subAny[i].stringValue()); 4567 buffer.append('"'); 4568 if (i < (subAny.length-1)) 4569 { 4570 buffer.append(','); 4571 } 4572 lineList.add(buffer.toString()); 4573 } 4574 4575 lineList.add(indent + " },"); 4576 } 4577 else 4578 { 4579 buffer.append("new String[]"); 4580 lineList.add(buffer.toString()); 4581 4582 lineList.add(indent + " {"); 4583 4584 for (int i=0; i < subAny.length; i++) 4585 { 4586 buffer.setLength(0); 4587 buffer.append(indent); 4588 buffer.append(" "); 4589 byteArrayToCode(subAny[i].getValue(), buffer); 4590 if (i < (subAny.length-1)) 4591 { 4592 buffer.append(','); 4593 } 4594 lineList.add(buffer.toString()); 4595 } 4596 4597 lineList.add(indent + " },"); 4598 } 4599 4600 buffer.setLength(0); 4601 buffer.append(indent); 4602 buffer.append(" "); 4603 if (subFinal == null) 4604 { 4605 buffer.append("null)"); 4606 } 4607 else if (isRedacted) 4608 { 4609 buffer.append("\"---redacted-subFinal---\")"); 4610 } 4611 else if (isPrintable) 4612 { 4613 buffer.append('"'); 4614 buffer.append(subFinal.stringValue()); 4615 buffer.append("\")"); 4616 } 4617 else 4618 { 4619 byteArrayToCode(subFinal.getValue(), buffer); 4620 buffer.append(')'); 4621 } 4622 if (lastLineSuffix != null) 4623 { 4624 buffer.append(lastLineSuffix); 4625 } 4626 lineList.add(buffer.toString()); 4627 return; 4628 4629 4630 case FILTER_TYPE_EXTENSIBLE_MATCH: 4631 buffer.append("Filter.createExtensibleMatchFilter("); 4632 lineList.add(buffer.toString()); 4633 4634 buffer.setLength(0); 4635 buffer.append(indent); 4636 buffer.append(" "); 4637 if (attrName == null) 4638 { 4639 buffer.append("null, // Attribute Description"); 4640 } 4641 else 4642 { 4643 buffer.append('"'); 4644 buffer.append(attrName); 4645 buffer.append("\","); 4646 } 4647 lineList.add(buffer.toString()); 4648 4649 buffer.setLength(0); 4650 buffer.append(indent); 4651 buffer.append(" "); 4652 if (matchingRuleID == null) 4653 { 4654 buffer.append("null, // Matching Rule ID"); 4655 } 4656 else 4657 { 4658 buffer.append('"'); 4659 buffer.append(matchingRuleID); 4660 buffer.append("\","); 4661 } 4662 lineList.add(buffer.toString()); 4663 4664 buffer.setLength(0); 4665 buffer.append(indent); 4666 buffer.append(" "); 4667 buffer.append(dnAttributes); 4668 buffer.append(", // DN Attributes"); 4669 lineList.add(buffer.toString()); 4670 4671 buffer.setLength(0); 4672 buffer.append(indent); 4673 buffer.append(" "); 4674 if ((attrName != null) && isSensitiveToCodeAttribute(attrName)) 4675 { 4676 buffer.append("\"---redacted-value---\")"); 4677 } 4678 else 4679 { 4680 if (isPrintableString(assertionValue.getValue())) 4681 { 4682 buffer.append('"'); 4683 buffer.append(assertionValue.stringValue()); 4684 buffer.append("\")"); 4685 } 4686 else 4687 { 4688 byteArrayToCode(assertionValue.getValue(), buffer); 4689 buffer.append(')'); 4690 } 4691 } 4692 4693 if (lastLineSuffix != null) 4694 { 4695 buffer.append(lastLineSuffix); 4696 } 4697 lineList.add(buffer.toString()); 4698 return; 4699 } 4700 } 4701}