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