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