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