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