001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 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-2024 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.controls; 037 038 039 040import java.io.Serializable; 041import java.util.ArrayList; 042import java.util.Arrays; 043 044import com.unboundid.asn1.ASN1Element; 045import com.unboundid.asn1.ASN1OctetString; 046import com.unboundid.asn1.ASN1Sequence; 047import com.unboundid.ldap.sdk.Filter; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.ResultCode; 050import com.unboundid.util.Debug; 051import com.unboundid.util.NotMutable; 052import com.unboundid.util.NotNull; 053import com.unboundid.util.Nullable; 054import com.unboundid.util.StaticUtils; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057import com.unboundid.util.Validator; 058 059import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 060 061 062 063/** 064 * This class provides an implementation of the simple filter item for use with 065 * the {@link MatchedValuesRequestControl} as defined in 066 * <A HREF="http://www.ietf.org/rfc/rfc3876.txt">RFC 3876</A>. It is similar to 067 * a search filter (see the {@link Filter} class), but may only contain a single 068 * element (i.e., no AND, OR, or NOT components are allowed), and extensible 069 * matching does not allow the use of the dnAttributes field. 070 */ 071@NotMutable() 072@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 073public final class MatchedValuesFilter 074 implements Serializable 075{ 076 /** 077 * The match type that will be used for equality match filters. 078 */ 079 public static final byte MATCH_TYPE_EQUALITY = (byte) 0xA3; 080 081 082 083 /** 084 * The match type that will be used for substring match filters. 085 */ 086 public static final byte MATCH_TYPE_SUBSTRINGS = (byte) 0xA4; 087 088 089 090 /** 091 * The match type that will be used for greater-or-equal match filters. 092 */ 093 public static final byte MATCH_TYPE_GREATER_OR_EQUAL = (byte) 0xA5; 094 095 096 097 /** 098 * The match type that will be used for less-or-equal match filters. 099 */ 100 public static final byte MATCH_TYPE_LESS_OR_EQUAL = (byte) 0xA6; 101 102 103 104 /** 105 * The match type that will be used for presence match filters. 106 */ 107 public static final byte MATCH_TYPE_PRESENT = (byte) 0x87; 108 109 110 111 /** 112 * The match type that will be used for approximate match filters. 113 */ 114 public static final byte MATCH_TYPE_APPROXIMATE = (byte) 0xA8; 115 116 117 118 /** 119 * The match type that will be used for extensible match filters. 120 */ 121 public static final byte MATCH_TYPE_EXTENSIBLE = (byte) 0xA9; 122 123 124 125 /** 126 * The BER type for the subInitial substring filter element. 127 */ 128 private static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80; 129 130 131 132 /** 133 * The BER type for the subAny substring filter element. 134 */ 135 private static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81; 136 137 138 139 /** 140 * The BER type for the subFinal substring filter element. 141 */ 142 private static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82; 143 144 145 146 /** 147 * The BER type for the matching rule ID extensible match filter element. 148 */ 149 private static final byte EXTENSIBLE_TYPE_MATCHING_RULE_ID = (byte) 0x81; 150 151 152 153 /** 154 * The BER type for the attribute name extensible match filter element. 155 */ 156 private static final byte EXTENSIBLE_TYPE_ATTRIBUTE_NAME = (byte) 0x82; 157 158 159 160 /** 161 * The BER type for the match value extensible match filter element. 162 */ 163 private static final byte EXTENSIBLE_TYPE_MATCH_VALUE = (byte) 0x83; 164 165 166 167 /** 168 * An empty array that will be used if there are no subAny elements. 169 */ 170 @NotNull private static final ASN1OctetString[] NO_SUB_ANY = 171 new ASN1OctetString[0]; 172 173 174 175 /** 176 * An empty array that will be used if there are no subAny elements. 177 */ 178 @NotNull private static final String[] NO_SUB_ANY_STRINGS = 179 StaticUtils.NO_STRINGS; 180 181 182 183 /** 184 * An empty array that will be used if there are no subAny elements. 185 */ 186 @NotNull private static final byte[][] NO_SUB_ANY_BYTES = new byte[0][]; 187 188 189 190 /** 191 * The serial version UID for this serializable class. 192 */ 193 private static final long serialVersionUID = 8144732301100674661L; 194 195 196 197 // The assertion value to include in this filter, if appropriate. 198 @Nullable private final ASN1OctetString assertionValue; 199 200 // The subFinal value for this filter, if appropriate. 201 @Nullable private final ASN1OctetString subFinalValue; 202 203 // The subInitial value for this filter, if appropriate. 204 @Nullable private final ASN1OctetString subInitialValue; 205 206 // The subAny values for this filter, if appropriate. 207 @NotNull private final ASN1OctetString[] subAnyValues; 208 209 // The filter type for this filter. 210 private final byte matchType; 211 212 // The name of the attribute type to include in this filter, if appropriate. 213 @Nullable private final String attributeType; 214 215 // The matching rule ID for this filter, if appropriate. 216 @Nullable private final String matchingRuleID; 217 218 219 220 /** 221 * Creates a new matched values filter with the provided information. 222 * 223 * @param matchType The filter type for this filter. 224 * @param attributeType The name of the attribute type. It may be 225 * {@code null} only for extensible match filters and 226 * only if a non-{@code null} matching rule ID is 227 * provided. 228 * @param assertionValue The assertion value for this filter. It may only 229 * be {@code null} for substring and presence 230 * filters. 231 * @param subInitialValue The subInitial value for this filter. It may only 232 * be provided for substring filters. 233 * @param subAnyValues The set of subAny values for this filter. It may 234 * only be provided for substring filters. 235 * @param subFinalValue The subFinal value for this filter. It may only 236 * be provided for substring filters. 237 * @param matchingRuleID The matching rule ID for this filter. It may only 238 * be provided for extensible match filters. 239 */ 240 private MatchedValuesFilter(final byte matchType, 241 @Nullable final String attributeType, 242 @Nullable final ASN1OctetString assertionValue, 243 @Nullable final ASN1OctetString subInitialValue, 244 @NotNull final ASN1OctetString[] subAnyValues, 245 @Nullable final ASN1OctetString subFinalValue, 246 @Nullable final String matchingRuleID) 247 { 248 this.matchType = matchType; 249 this.attributeType = attributeType; 250 this.assertionValue = assertionValue; 251 this.subInitialValue = subInitialValue; 252 this.subAnyValues = subAnyValues; 253 this.subFinalValue = subFinalValue; 254 this.matchingRuleID = matchingRuleID; 255 } 256 257 258 259 /** 260 * Creates a new matched values filter for equality matching with the provided 261 * information. 262 * 263 * @param attributeType The attribute type for the filter. It must not be 264 * {@code null}. 265 * @param assertionValue The assertion value for the filter. It must not be 266 * {@code null}. 267 * 268 * @return The created equality match filter. 269 */ 270 @NotNull() 271 public static MatchedValuesFilter createEqualityFilter( 272 @NotNull final String attributeType, 273 @NotNull final String assertionValue) 274 { 275 Validator.ensureNotNull(attributeType, assertionValue); 276 277 return new MatchedValuesFilter(MATCH_TYPE_EQUALITY, attributeType, 278 new ASN1OctetString(assertionValue), null, NO_SUB_ANY, null, null); 279 } 280 281 282 283 /** 284 * Creates a new matched values filter for equality matching with the provided 285 * information. 286 * 287 * @param attributeType The attribute type for the filter. It must not be 288 * {@code null}. 289 * @param assertionValue The assertion value for the filter. It must not be 290 * {@code null}. 291 * 292 * @return The created equality match filter. 293 */ 294 @NotNull() 295 public static MatchedValuesFilter createEqualityFilter( 296 @NotNull final String attributeType, 297 @NotNull final byte[] assertionValue) 298 { 299 Validator.ensureNotNull(attributeType, assertionValue); 300 301 return new MatchedValuesFilter(MATCH_TYPE_EQUALITY, attributeType, 302 new ASN1OctetString(assertionValue), null, NO_SUB_ANY, null, null); 303 } 304 305 306 307 /** 308 * Creates a new matched values filter for substring matching with the 309 * provided information. At least one substring filter element must be 310 * provided. 311 * 312 * @param attributeType The attribute type for the filter. It must not be 313 * {@code null}. 314 * @param subInitialValue The subInitial value for the filter, or 315 * {@code null} if there is no subInitial element. 316 * @param subAnyValues The set of subAny values for the filter, or 317 * {@code null} if there are no subAny elements. 318 * @param subFinalValue The subFinal value for the filter, or {@code null} 319 * if there is no subFinal element. 320 * 321 * @return The created equality match filter. 322 */ 323 @NotNull() 324 public static MatchedValuesFilter createSubstringFilter( 325 @NotNull final String attributeType, 326 @Nullable final String subInitialValue, 327 @Nullable final String[] subAnyValues, 328 @Nullable final String subFinalValue) 329 { 330 Validator.ensureNotNull(attributeType); 331 Validator.ensureTrue((subInitialValue != null) || 332 ((subAnyValues != null) && (subAnyValues.length > 0)) || 333 (subFinalValue != null)); 334 335 final ASN1OctetString subInitialOS; 336 if (subInitialValue == null) 337 { 338 subInitialOS = null; 339 } 340 else 341 { 342 subInitialOS = new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, 343 subInitialValue); 344 } 345 346 final ASN1OctetString[] subAnyOS; 347 if ((subAnyValues == null) || (subAnyValues.length == 0)) 348 { 349 subAnyOS = NO_SUB_ANY; 350 } 351 else 352 { 353 subAnyOS = new ASN1OctetString[subAnyValues.length]; 354 for (int i=0; i < subAnyValues.length; i++) 355 { 356 subAnyOS[i] = new ASN1OctetString(SUBSTRING_TYPE_SUBANY, 357 subAnyValues[i]); 358 } 359 } 360 361 final ASN1OctetString subFinalOS; 362 if (subFinalValue == null) 363 { 364 subFinalOS = null; 365 } 366 else 367 { 368 subFinalOS = new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, subFinalValue); 369 } 370 371 return new MatchedValuesFilter(MATCH_TYPE_SUBSTRINGS, attributeType, null, 372 subInitialOS, subAnyOS, subFinalOS, null); 373 } 374 375 376 377 /** 378 * Creates a new matched values filter for substring matching with the 379 * provided information. At least one substring filter element must be 380 * provided. 381 * 382 * @param attributeType The attribute type for the filter. It must not be 383 * {@code null}. 384 * @param subInitialValue The subInitial value for the filter, or 385 * {@code null} if there is no subInitial element. 386 * @param subAnyValues The set of subAny values for the filter, or 387 * {@code null} if there are no subAny elements. 388 * @param subFinalValue The subFinal value for the filter, or {@code null} 389 * if there is no subFinal element. 390 * 391 * @return The created equality match filter. 392 */ 393 @NotNull() 394 public static MatchedValuesFilter createSubstringFilter( 395 @NotNull final String attributeType, 396 @Nullable final byte[] subInitialValue, 397 @Nullable final byte[][] subAnyValues, 398 @Nullable final byte[] subFinalValue) 399 { 400 Validator.ensureNotNull(attributeType); 401 Validator.ensureTrue((subInitialValue != null) || 402 ((subAnyValues != null) && (subAnyValues.length > 0)) || 403 (subFinalValue != null)); 404 405 final ASN1OctetString subInitialOS; 406 if (subInitialValue == null) 407 { 408 subInitialOS = null; 409 } 410 else 411 { 412 subInitialOS = new ASN1OctetString(SUBSTRING_TYPE_SUBINITIAL, 413 subInitialValue); 414 } 415 416 final ASN1OctetString[] subAnyOS; 417 if ((subAnyValues == null) || (subAnyValues.length == 0)) 418 { 419 subAnyOS = NO_SUB_ANY; 420 } 421 else 422 { 423 subAnyOS = new ASN1OctetString[subAnyValues.length]; 424 for (int i=0; i < subAnyValues.length; i++) 425 { 426 subAnyOS[i] = new ASN1OctetString(SUBSTRING_TYPE_SUBANY, 427 subAnyValues[i]); 428 } 429 } 430 431 final ASN1OctetString subFinalOS; 432 if (subFinalValue == null) 433 { 434 subFinalOS = null; 435 } 436 else 437 { 438 subFinalOS = new ASN1OctetString(SUBSTRING_TYPE_SUBFINAL, subFinalValue); 439 } 440 441 return new MatchedValuesFilter(MATCH_TYPE_SUBSTRINGS, attributeType, null, 442 subInitialOS, subAnyOS, subFinalOS, null); 443 } 444 445 446 447 /** 448 * Creates a new matched values filter for greater-or-equal matching with the 449 * provided information. 450 * 451 * @param attributeType The attribute type for the filter. It must not be 452 * {@code null}. 453 * @param assertionValue The assertion value for the filter. It must not be 454 * {@code null}. 455 * 456 * @return The created greater-or-equal match filter. 457 */ 458 @NotNull() 459 public static MatchedValuesFilter createGreaterOrEqualFilter( 460 @NotNull final String attributeType, 461 @NotNull final String assertionValue) 462 { 463 Validator.ensureNotNull(attributeType, assertionValue); 464 465 return new MatchedValuesFilter(MATCH_TYPE_GREATER_OR_EQUAL, attributeType, 466 new ASN1OctetString(assertionValue), null, 467 NO_SUB_ANY, null, null); 468 } 469 470 471 472 /** 473 * Creates a new matched values filter for greater-or-equal matching with the 474 * provided information. 475 * 476 * @param attributeType The attribute type for the filter. It must not be 477 * {@code null}. 478 * @param assertionValue The assertion value for the filter. It must not be 479 * {@code null}. 480 * 481 * @return The created greater-or-equal match filter. 482 */ 483 @NotNull() 484 public static MatchedValuesFilter createGreaterOrEqualFilter( 485 @NotNull final String attributeType, 486 @NotNull final byte[] assertionValue) 487 { 488 Validator.ensureNotNull(attributeType, assertionValue); 489 490 return new MatchedValuesFilter(MATCH_TYPE_GREATER_OR_EQUAL, attributeType, 491 new ASN1OctetString(assertionValue), null, 492 NO_SUB_ANY, null, null); 493 } 494 495 496 497 /** 498 * Creates a new matched values filter for less-or-equal matching with the 499 * provided information. 500 * 501 * @param attributeType The attribute type for the filter. It must not be 502 * {@code null}. 503 * @param assertionValue The assertion value for the filter. It must not be 504 * {@code null}. 505 * 506 * @return The created less-or-equal match filter. 507 */ 508 @NotNull() 509 public static MatchedValuesFilter createLessOrEqualFilter( 510 @NotNull final String attributeType, 511 @NotNull final String assertionValue) 512 { 513 Validator.ensureNotNull(attributeType, assertionValue); 514 515 return new MatchedValuesFilter(MATCH_TYPE_LESS_OR_EQUAL, attributeType, 516 new ASN1OctetString(assertionValue), null, 517 NO_SUB_ANY, null, null); 518 } 519 520 521 522 /** 523 * Creates a new matched values filter for less-or-equal matching with the 524 * provided information. 525 * 526 * @param attributeType The attribute type for the filter. It must not be 527 * {@code null}. 528 * @param assertionValue The assertion value for the filter. It must not be 529 * {@code null}. 530 * 531 * @return The created less-or-equal match filter. 532 */ 533 @NotNull() 534 public static MatchedValuesFilter createLessOrEqualFilter( 535 @NotNull final String attributeType, 536 @NotNull final byte[] assertionValue) 537 { 538 Validator.ensureNotNull(attributeType, assertionValue); 539 540 return new MatchedValuesFilter(MATCH_TYPE_LESS_OR_EQUAL, attributeType, 541 new ASN1OctetString(assertionValue), null, 542 NO_SUB_ANY, null, null); 543 } 544 545 546 547 /** 548 * Creates a new matched values filter for presence matching with the provided 549 * information. 550 * 551 * @param attributeType The attribute type for the filter. It must not be 552 * {@code null}. 553 * 554 * @return The created present match filter. 555 */ 556 @NotNull() 557 public static MatchedValuesFilter createPresentFilter( 558 @NotNull final String attributeType) 559 { 560 Validator.ensureNotNull(attributeType); 561 562 return new MatchedValuesFilter(MATCH_TYPE_PRESENT, attributeType, null, 563 null, NO_SUB_ANY, null, null); 564 } 565 566 567 568 /** 569 * Creates a new matched values filter for approximate matching with the 570 * provided information. 571 * 572 * @param attributeType The attribute type for the filter. It must not be 573 * {@code null}. 574 * @param assertionValue The assertion value for the filter. It must not be 575 * {@code null}. 576 * 577 * @return The created approximate match filter. 578 */ 579 @NotNull() 580 public static MatchedValuesFilter createApproximateFilter( 581 @NotNull final String attributeType, 582 @NotNull final String assertionValue) 583 { 584 Validator.ensureNotNull(attributeType, assertionValue); 585 586 return new MatchedValuesFilter(MATCH_TYPE_APPROXIMATE, attributeType, 587 new ASN1OctetString(assertionValue), null, 588 NO_SUB_ANY, null, null); 589 } 590 591 592 593 /** 594 * Creates a new matched values filter for approximate matching with the 595 * provided information. 596 * 597 * @param attributeType The attribute type for the filter. It must not be 598 * {@code null}. 599 * @param assertionValue The assertion value for the filter. It must not be 600 * {@code null}. 601 * 602 * @return The created greater-or-equal match filter. 603 */ 604 @NotNull() 605 public static MatchedValuesFilter createApproximateFilter( 606 @NotNull final String attributeType, 607 @NotNull final byte[] assertionValue) 608 { 609 Validator.ensureNotNull(attributeType, assertionValue); 610 611 return new MatchedValuesFilter(MATCH_TYPE_APPROXIMATE, attributeType, 612 new ASN1OctetString(assertionValue), null, 613 NO_SUB_ANY, null, null); 614 } 615 616 617 618 /** 619 * Creates a new matched values filter for extensible matching with the 620 * provided information. At least one of the attribute type and matching rule 621 * ID must be provided. 622 * 623 * @param attributeType The attribute type for the filter, or {@code null} 624 * if there is no attribute type. 625 * @param matchingRuleID The matching rule ID for the filter, or 626 * {@code null} if there is no matching rule ID. 627 * @param assertionValue The assertion value for the filter. It must not be 628 * {@code null}. 629 * 630 * @return The created extensible match filter. 631 */ 632 @NotNull() 633 public static MatchedValuesFilter createExtensibleMatchFilter( 634 @Nullable final String attributeType, 635 @Nullable final String matchingRuleID, 636 @NotNull final String assertionValue) 637 { 638 Validator.ensureNotNull(assertionValue); 639 Validator.ensureTrue((attributeType != null) || (matchingRuleID != null)); 640 641 final ASN1OctetString matchValue = 642 new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, assertionValue); 643 644 return new MatchedValuesFilter(MATCH_TYPE_EXTENSIBLE, attributeType, 645 matchValue, null, NO_SUB_ANY, null, 646 matchingRuleID); 647 } 648 649 650 651 /** 652 * Creates a new matched values filter for extensible matching with the 653 * provided information. At least one of the attribute type and matching rule 654 * ID must be provided. 655 * 656 * @param attributeType The attribute type for the filter, or {@code null} 657 * if there is no attribute type. 658 * @param matchingRuleID The matching rule ID for the filter, or 659 * {@code null} if there is no matching rule ID. 660 * @param assertionValue The assertion value for the filter. It must not be 661 * {@code null}. 662 * 663 * @return The created extensible match filter. 664 */ 665 @NotNull() 666 public static MatchedValuesFilter createExtensibleMatchFilter( 667 @Nullable final String attributeType, 668 @Nullable final String matchingRuleID, 669 @NotNull final byte[] assertionValue) 670 { 671 Validator.ensureNotNull(assertionValue); 672 Validator.ensureTrue((attributeType != null) || (matchingRuleID != null)); 673 674 final ASN1OctetString matchValue = 675 new ASN1OctetString(EXTENSIBLE_TYPE_MATCH_VALUE, assertionValue); 676 677 return new MatchedValuesFilter(MATCH_TYPE_EXTENSIBLE, attributeType, 678 matchValue, null, NO_SUB_ANY, null, 679 matchingRuleID); 680 } 681 682 683 684 /** 685 * Creates a new matched values filter from the provided search filter, if 686 * possible. 687 * 688 * @param filter The search filter to use to create this matched values 689 * filter. 690 * 691 * @return The search filter that corresponds to this matched values filter. 692 * 693 * @throws LDAPException If the provided search filter cannot be represented 694 * as a matched values filter. 695 */ 696 @NotNull() 697 public static MatchedValuesFilter create(@NotNull final Filter filter) 698 throws LDAPException 699 { 700 switch (filter.getFilterType()) 701 { 702 case Filter.FILTER_TYPE_AND: 703 throw new LDAPException(ResultCode.DECODING_ERROR, 704 ERR_MV_FILTER_AND_NOT_SUPPORTED.get()); 705 706 case Filter.FILTER_TYPE_OR: 707 throw new LDAPException(ResultCode.DECODING_ERROR, 708 ERR_MV_FILTER_OR_NOT_SUPPORTED.get()); 709 710 case Filter.FILTER_TYPE_NOT: 711 throw new LDAPException(ResultCode.DECODING_ERROR, 712 ERR_MV_FILTER_NOT_NOT_SUPPORTED.get()); 713 714 case Filter.FILTER_TYPE_EQUALITY: 715 return createEqualityFilter(filter.getAttributeName(), 716 filter.getAssertionValueBytes()); 717 718 case Filter.FILTER_TYPE_SUBSTRING: 719 return createSubstringFilter(filter.getAttributeName(), 720 filter.getSubInitialBytes(), filter.getSubAnyBytes(), 721 filter.getSubFinalBytes()); 722 723 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 724 return createGreaterOrEqualFilter(filter.getAttributeName(), 725 filter.getAssertionValueBytes()); 726 727 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 728 return createLessOrEqualFilter(filter.getAttributeName(), 729 filter.getAssertionValueBytes()); 730 731 case Filter.FILTER_TYPE_PRESENCE: 732 return createPresentFilter(filter.getAttributeName()); 733 734 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 735 return createApproximateFilter(filter.getAttributeName(), 736 filter.getAssertionValueBytes()); 737 738 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 739 if (filter.getDNAttributes()) 740 { 741 throw new LDAPException(ResultCode.DECODING_ERROR, 742 ERR_MV_FILTER_DNATTRS_NOT_SUPPORTED.get()); 743 } 744 745 return createExtensibleMatchFilter(filter.getAttributeName(), 746 filter.getMatchingRuleID(), 747 filter.getAssertionValueBytes()); 748 749 default: 750 // This should never happen. 751 throw new LDAPException(ResultCode.DECODING_ERROR, 752 ERR_MV_FILTER_INVALID_FILTER_TYPE.get( 753 StaticUtils.toHex(filter.getFilterType()))); 754 } 755 } 756 757 758 759 /** 760 * Retrieves the match type for this matched values filter. 761 * 762 * @return The match type for this matched values filter. 763 */ 764 public byte getMatchType() 765 { 766 return matchType; 767 } 768 769 770 771 /** 772 * Retrieves the name of the attribute type for this matched values filter, 773 * if available. 774 * 775 * @return The name of the attribute type for this matched values filter, or 776 * {@code null} if there is none. 777 */ 778 @Nullable() 779 public String getAttributeType() 780 { 781 return attributeType; 782 } 783 784 785 786 /** 787 * Retrieves the string representation of the assertion value for this matched 788 * values filter, if available. 789 * 790 * @return The string representation of the assertion value for this matched 791 * values filter, or {@code null} if there is none. 792 */ 793 @Nullable() 794 public String getAssertionValue() 795 { 796 if (assertionValue == null) 797 { 798 return null; 799 } 800 else 801 { 802 return assertionValue.stringValue(); 803 } 804 } 805 806 807 808 /** 809 * Retrieves the binary representation of the assertion value for this matched 810 * values filter, if available. 811 * 812 * @return The binary representation of the assertion value for this matched 813 * values filter, or {@code null} if there is none. 814 */ 815 @Nullable() 816 public byte[] getAssertionValueBytes() 817 { 818 if (assertionValue == null) 819 { 820 return null; 821 } 822 else 823 { 824 return assertionValue.getValue(); 825 } 826 } 827 828 829 830 /** 831 * Retrieves raw assertion value for this matched values filter, if available. 832 * 833 * @return The raw assertion value for this matched values filter, or 834 * {@code null} if there is none. 835 */ 836 @Nullable() 837 public ASN1OctetString getRawAssertionValue() 838 { 839 return assertionValue; 840 } 841 842 843 844 /** 845 * Retrieves the string representation of the subInitial element for this 846 * matched values filter, if available. 847 * 848 * @return The string representation of the subInitial element for this 849 * matched values filter, or {@code null} if there is none. 850 */ 851 @Nullable() 852 public String getSubInitialValue() 853 { 854 if (subInitialValue == null) 855 { 856 return null; 857 } 858 else 859 { 860 return subInitialValue.stringValue(); 861 } 862 } 863 864 865 866 /** 867 * Retrieves the binary representation of the subInitial element for this 868 * matched values filter, if available. 869 * 870 * @return The binary representation of the subInitial element for this 871 * matched values filter, or {@code null} if there is none. 872 */ 873 @Nullable() 874 public byte[] getSubInitialValueBytes() 875 { 876 if (subInitialValue == null) 877 { 878 return null; 879 } 880 else 881 { 882 return subInitialValue.getValue(); 883 } 884 } 885 886 887 888 /** 889 * Retrieves the raw subInitial element for this matched values filter, if 890 * available. 891 * 892 * @return The raw subInitial element for this matched values filter, or 893 * {@code null} if there is none. 894 */ 895 @Nullable() 896 public ASN1OctetString getRawSubInitialValue() 897 { 898 return subInitialValue; 899 } 900 901 902 903 /** 904 * Retrieves the string representations of the subAny elements for this 905 * matched values filter, if available. 906 * 907 * @return The string representations of the subAny element for this matched 908 * values filter, or an empty array if there are none. 909 */ 910 @NotNull() 911 public String[] getSubAnyValues() 912 { 913 if (subAnyValues.length == 0) 914 { 915 return NO_SUB_ANY_STRINGS; 916 } 917 else 918 { 919 final String[] subAnyStrings = new String[subAnyValues.length]; 920 for (int i=0; i < subAnyValues.length; i++) 921 { 922 subAnyStrings[i] = subAnyValues[i].stringValue(); 923 } 924 925 return subAnyStrings; 926 } 927 } 928 929 930 931 /** 932 * Retrieves the binary representations of the subAny elements for this 933 * matched values filter, if available. 934 * 935 * @return The binary representations of the subAny element for this matched 936 * values filter, or an empty array if there are none. 937 */ 938 @NotNull() 939 public byte[][] getSubAnyValueBytes() 940 { 941 if (subAnyValues.length == 0) 942 { 943 return NO_SUB_ANY_BYTES; 944 } 945 else 946 { 947 final byte[][] subAnyBytes = new byte[subAnyValues.length][]; 948 for (int i=0; i < subAnyValues.length; i++) 949 { 950 subAnyBytes[i] = subAnyValues[i].getValue(); 951 } 952 953 return subAnyBytes; 954 } 955 } 956 957 958 959 /** 960 * Retrieves the raw subAny elements for this matched values filter, if 961 * available. 962 * 963 * @return The raw subAny element for this matched values filter, or an empty 964 * array if there are none. 965 */ 966 @NotNull() 967 public ASN1OctetString[] getRawSubAnyValues() 968 { 969 return subAnyValues; 970 } 971 972 973 974 /** 975 * Retrieves the string representation of the subFinal element for this 976 * matched values filter, if available. 977 * 978 * @return The string representation of the subFinal element for this 979 * matched values filter, or {@code null} if there is none. 980 */ 981 @Nullable() 982 public String getSubFinalValue() 983 { 984 if (subFinalValue == null) 985 { 986 return null; 987 } 988 else 989 { 990 return subFinalValue.stringValue(); 991 } 992 } 993 994 995 996 /** 997 * Retrieves the binary representation of the subFinal element for this 998 * matched values filter, if available. 999 * 1000 * @return The binary representation of the subFinal element for this matched 1001 * values filter, or {@code null} if there is none. 1002 */ 1003 @Nullable() 1004 public byte[] getSubFinalValueBytes() 1005 { 1006 if (subFinalValue == null) 1007 { 1008 return null; 1009 } 1010 else 1011 { 1012 return subFinalValue.getValue(); 1013 } 1014 } 1015 1016 1017 1018 /** 1019 * Retrieves the raw subFinal element for this matched values filter, if 1020 * available. 1021 * 1022 * @return The raw subFinal element for this matched values filter, or 1023 * {@code null} if there is none. 1024 */ 1025 @Nullable() 1026 public ASN1OctetString getRawSubFinalValue() 1027 { 1028 return subFinalValue; 1029 } 1030 1031 1032 1033 /** 1034 * Retrieves the matching rule ID for this matched values filter, if 1035 * available. 1036 * 1037 * @return The matching rule ID for this matched values filter, or 1038 * {@code null} if there is none. 1039 */ 1040 @Nullable() 1041 public String getMatchingRuleID() 1042 { 1043 return matchingRuleID; 1044 } 1045 1046 1047 1048 /** 1049 * Encodes this matched values filter for use in the matched values control. 1050 * 1051 * @return The ASN.1 element containing the encoded representation of this 1052 * matched values filter. 1053 */ 1054 @NotNull() 1055 public ASN1Element encode() 1056 { 1057 switch (matchType) 1058 { 1059 case MATCH_TYPE_EQUALITY: 1060 case MATCH_TYPE_GREATER_OR_EQUAL: 1061 case MATCH_TYPE_LESS_OR_EQUAL: 1062 case MATCH_TYPE_APPROXIMATE: 1063 ASN1Element[] elements = 1064 { 1065 new ASN1OctetString(attributeType), 1066 assertionValue 1067 }; 1068 return new ASN1Sequence(matchType, elements); 1069 1070 case MATCH_TYPE_SUBSTRINGS: 1071 final ArrayList<ASN1Element> subElements = new ArrayList<>(3); 1072 if (subInitialValue != null) 1073 { 1074 subElements.add(subInitialValue); 1075 } 1076 1077 if (subAnyValues.length > 0) 1078 { 1079 subElements.addAll(Arrays.asList(subAnyValues)); 1080 } 1081 1082 if (subFinalValue != null) 1083 { 1084 subElements.add(subFinalValue); 1085 } 1086 1087 elements = new ASN1Element[] 1088 { 1089 new ASN1OctetString(attributeType), 1090 new ASN1Sequence(subElements) 1091 }; 1092 return new ASN1Sequence(matchType, elements); 1093 1094 case MATCH_TYPE_PRESENT: 1095 return new ASN1OctetString(matchType, attributeType); 1096 1097 case MATCH_TYPE_EXTENSIBLE: 1098 final ArrayList<ASN1Element> extElements = new ArrayList<>(3); 1099 if (attributeType != null) 1100 { 1101 extElements.add(new ASN1OctetString(EXTENSIBLE_TYPE_ATTRIBUTE_NAME, 1102 attributeType)); 1103 } 1104 1105 if (matchingRuleID != null) 1106 { 1107 extElements.add(new ASN1OctetString(EXTENSIBLE_TYPE_MATCHING_RULE_ID, 1108 matchingRuleID)); 1109 } 1110 1111 extElements.add(assertionValue); 1112 return new ASN1Sequence(matchType, extElements); 1113 1114 default: 1115 // This should never happen. 1116 return null; 1117 } 1118 } 1119 1120 1121 1122 /** 1123 * Decodes the provided ASN.1 element as a matched values filter. 1124 * 1125 * @param element The ASN.1 element to decode as a matched values filter. 1126 * 1127 * @return The decoded matched values filter. 1128 * 1129 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 1130 * a matched values filter. 1131 */ 1132 @NotNull() 1133 public static MatchedValuesFilter decode(@NotNull final ASN1Element element) 1134 throws LDAPException 1135 { 1136 ASN1OctetString assertionValue = null; 1137 ASN1OctetString subInitialValue = null; 1138 ASN1OctetString subFinalValue = null; 1139 ASN1OctetString[] subAnyValues = NO_SUB_ANY; 1140 final byte matchType = element.getType(); 1141 String attributeType = null; 1142 String matchingRuleID = null; 1143 1144 switch (matchType) 1145 { 1146 case MATCH_TYPE_EQUALITY: 1147 case MATCH_TYPE_GREATER_OR_EQUAL: 1148 case MATCH_TYPE_LESS_OR_EQUAL: 1149 case MATCH_TYPE_APPROXIMATE: 1150 try 1151 { 1152 final ASN1Element[] elements = 1153 ASN1Sequence.decodeAsSequence(element).elements(); 1154 attributeType = 1155 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 1156 assertionValue = 1157 ASN1OctetString.decodeAsOctetString(elements[1]); 1158 } 1159 catch (final Exception e) 1160 { 1161 Debug.debugException(e); 1162 throw new LDAPException(ResultCode.DECODING_ERROR, 1163 ERR_MV_FILTER_NOT_AVA.get(e), e); 1164 } 1165 break; 1166 1167 case MATCH_TYPE_SUBSTRINGS: 1168 try 1169 { 1170 final ASN1Element[] elements = 1171 ASN1Sequence.decodeAsSequence(element).elements(); 1172 attributeType = 1173 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 1174 1175 ArrayList<ASN1OctetString> subAnyList = null; 1176 final ASN1Element[] subElements = 1177 ASN1Sequence.decodeAsSequence(elements[1]).elements(); 1178 for (final ASN1Element e : subElements) 1179 { 1180 switch (e.getType()) 1181 { 1182 case SUBSTRING_TYPE_SUBINITIAL: 1183 if (subInitialValue == null) 1184 { 1185 subInitialValue = ASN1OctetString.decodeAsOctetString(e); 1186 } 1187 else 1188 { 1189 throw new LDAPException(ResultCode.DECODING_ERROR, 1190 ERR_MV_FILTER_MULTIPLE_SUBINITIAL.get()); 1191 } 1192 break; 1193 1194 case SUBSTRING_TYPE_SUBANY: 1195 if (subAnyList == null) 1196 { 1197 subAnyList = new ArrayList<>(subElements.length); 1198 } 1199 subAnyList.add(ASN1OctetString.decodeAsOctetString(e)); 1200 break; 1201 1202 case SUBSTRING_TYPE_SUBFINAL: 1203 if (subFinalValue == null) 1204 { 1205 subFinalValue = ASN1OctetString.decodeAsOctetString(e); 1206 } 1207 else 1208 { 1209 throw new LDAPException(ResultCode.DECODING_ERROR, 1210 ERR_MV_FILTER_MULTIPLE_SUBFINAL.get()); 1211 } 1212 break; 1213 1214 default: 1215 throw new LDAPException(ResultCode.DECODING_ERROR, 1216 ERR_MV_FILTER_INVALID_SUB_TYPE.get( 1217 StaticUtils.toHex(e.getType()))); 1218 } 1219 } 1220 1221 if (subAnyList != null) 1222 { 1223 subAnyValues = 1224 subAnyList.toArray(new ASN1OctetString[subAnyList.size()]); 1225 } 1226 } 1227 catch (final LDAPException le) 1228 { 1229 Debug.debugException(le); 1230 throw le; 1231 } 1232 catch (final Exception e) 1233 { 1234 Debug.debugException(e); 1235 throw new LDAPException(ResultCode.DECODING_ERROR, 1236 ERR_MV_FILTER_CANNOT_DECODE_SUBSTRING.get(e), e); 1237 } 1238 1239 if ((subInitialValue == null) && (subAnyValues.length == 0) && 1240 (subFinalValue == null)) 1241 { 1242 throw new LDAPException(ResultCode.DECODING_ERROR, 1243 ERR_MV_FILTER_NO_SUBSTRING_ELEMENTS.get()); 1244 } 1245 break; 1246 1247 case MATCH_TYPE_PRESENT: 1248 attributeType = 1249 ASN1OctetString.decodeAsOctetString(element).stringValue(); 1250 break; 1251 1252 case MATCH_TYPE_EXTENSIBLE: 1253 try 1254 { 1255 final ASN1Element[] elements = 1256 ASN1Sequence.decodeAsSequence(element).elements(); 1257 for (final ASN1Element e : elements) 1258 { 1259 switch (e.getType()) 1260 { 1261 case EXTENSIBLE_TYPE_ATTRIBUTE_NAME: 1262 if (attributeType == null) 1263 { 1264 attributeType = 1265 ASN1OctetString.decodeAsOctetString(e).stringValue(); 1266 } 1267 else 1268 { 1269 throw new LDAPException(ResultCode.DECODING_ERROR, 1270 ERR_MV_FILTER_EXT_MULTIPLE_AT.get()); 1271 } 1272 break; 1273 1274 case EXTENSIBLE_TYPE_MATCHING_RULE_ID: 1275 if (matchingRuleID == null) 1276 { 1277 matchingRuleID = 1278 ASN1OctetString.decodeAsOctetString(e).stringValue(); 1279 } 1280 else 1281 { 1282 throw new LDAPException(ResultCode.DECODING_ERROR, 1283 ERR_MV_FILTER_MULTIPLE_MRID.get()); 1284 } 1285 break; 1286 1287 case EXTENSIBLE_TYPE_MATCH_VALUE: 1288 if (assertionValue == null) 1289 { 1290 assertionValue = 1291 ASN1OctetString.decodeAsOctetString(e); 1292 } 1293 else 1294 { 1295 throw new LDAPException(ResultCode.DECODING_ERROR, 1296 ERR_MV_FILTER_EXT_MULTIPLE_VALUE.get()); 1297 } 1298 break; 1299 1300 default: 1301 throw new LDAPException(ResultCode.DECODING_ERROR, 1302 ERR_MV_FILTER_EXT_INVALID_TYPE.get( 1303 StaticUtils.toHex(e.getType()))); 1304 } 1305 } 1306 } 1307 catch (final LDAPException le) 1308 { 1309 Debug.debugException(le); 1310 throw le; 1311 } 1312 catch (final Exception e) 1313 { 1314 Debug.debugException(e); 1315 throw new LDAPException(ResultCode.DECODING_ERROR, 1316 ERR_MV_FILTER_EXT_NOT_SEQUENCE.get(e), e); 1317 } 1318 1319 if ((attributeType == null) && (matchingRuleID == null)) 1320 { 1321 throw new LDAPException(ResultCode.DECODING_ERROR, 1322 ERR_MV_FILTER_NO_ATTR_OR_MRID.get()); 1323 } 1324 1325 if (assertionValue == null) 1326 { 1327 throw new LDAPException(ResultCode.DECODING_ERROR, 1328 ERR_MV_FILTER_EXT_NO_VALUE.get()); 1329 } 1330 break; 1331 1332 default: 1333 throw new LDAPException(ResultCode.DECODING_ERROR, 1334 ERR_MV_FILTER_INVALID_TYPE.get( 1335 StaticUtils.toHex(matchType))); 1336 } 1337 1338 return new MatchedValuesFilter(matchType, attributeType, assertionValue, 1339 subInitialValue, subAnyValues, subFinalValue, 1340 matchingRuleID); 1341 } 1342 1343 1344 1345 /** 1346 * Creates a search filter that is the equivalent of this matched values 1347 * filter. 1348 * 1349 * @return A search filter that is the equivalent of this matched values 1350 * filter. 1351 */ 1352 @NotNull() 1353 public Filter toFilter() 1354 { 1355 switch (matchType) 1356 { 1357 case MATCH_TYPE_EQUALITY: 1358 return Filter.createEqualityFilter(attributeType, 1359 assertionValue.getValue()); 1360 1361 case MATCH_TYPE_SUBSTRINGS: 1362 return Filter.createSubstringFilter(attributeType, 1363 getSubInitialValueBytes(), getSubAnyValueBytes(), 1364 getSubFinalValueBytes()); 1365 1366 case MATCH_TYPE_GREATER_OR_EQUAL: 1367 return Filter.createGreaterOrEqualFilter(attributeType, 1368 assertionValue.getValue()); 1369 1370 case MATCH_TYPE_LESS_OR_EQUAL: 1371 return Filter.createLessOrEqualFilter(attributeType, 1372 assertionValue.getValue()); 1373 1374 case MATCH_TYPE_PRESENT: 1375 return Filter.createPresenceFilter(attributeType); 1376 1377 case MATCH_TYPE_APPROXIMATE: 1378 return Filter.createApproximateMatchFilter(attributeType, 1379 assertionValue.getValue()); 1380 1381 case MATCH_TYPE_EXTENSIBLE: 1382 return Filter.createExtensibleMatchFilter(attributeType, matchingRuleID, 1383 false, assertionValue.getValue()); 1384 1385 default: 1386 // This should never happen. 1387 return null; 1388 } 1389 } 1390 1391 1392 1393 /** 1394 * Retrieves a string representation of this matched values filter. 1395 * 1396 * @return A string representation of this matched values filter. 1397 */ 1398 @Override() 1399 @NotNull() 1400 public String toString() 1401 { 1402 final StringBuilder buffer = new StringBuilder(); 1403 toString(buffer); 1404 return buffer.toString(); 1405 } 1406 1407 1408 1409 /** 1410 * Appends a string representation of this matched values filter to the 1411 * provided buffer. 1412 * 1413 * @param buffer The buffer to which to append the string representation of 1414 * this matched values filter. 1415 */ 1416 public void toString(@NotNull final StringBuilder buffer) 1417 { 1418 buffer.append('('); 1419 1420 switch (matchType) 1421 { 1422 case MATCH_TYPE_EQUALITY: 1423 buffer.append(attributeType); 1424 buffer.append('='); 1425 buffer.append(assertionValue.stringValue()); 1426 break; 1427 1428 case MATCH_TYPE_SUBSTRINGS: 1429 buffer.append(attributeType); 1430 buffer.append('='); 1431 1432 if (subInitialValue != null) 1433 { 1434 buffer.append(subInitialValue.stringValue()); 1435 } 1436 1437 for (final ASN1OctetString s : subAnyValues) 1438 { 1439 buffer.append('*'); 1440 buffer.append(s.stringValue()); 1441 } 1442 1443 buffer.append('*'); 1444 if (subFinalValue != null) 1445 { 1446 buffer.append(subFinalValue.stringValue()); 1447 } 1448 break; 1449 1450 case MATCH_TYPE_GREATER_OR_EQUAL: 1451 buffer.append(attributeType); 1452 buffer.append(">="); 1453 buffer.append(assertionValue.stringValue()); 1454 break; 1455 1456 case MATCH_TYPE_LESS_OR_EQUAL: 1457 buffer.append(attributeType); 1458 buffer.append("<="); 1459 buffer.append(assertionValue.stringValue()); 1460 break; 1461 1462 case MATCH_TYPE_PRESENT: 1463 buffer.append(attributeType); 1464 buffer.append("=*"); 1465 break; 1466 1467 case MATCH_TYPE_APPROXIMATE: 1468 buffer.append(attributeType); 1469 buffer.append("~="); 1470 buffer.append(assertionValue.stringValue()); 1471 break; 1472 1473 case MATCH_TYPE_EXTENSIBLE: 1474 if (attributeType != null) 1475 { 1476 buffer.append(attributeType); 1477 } 1478 1479 if (matchingRuleID != null) 1480 { 1481 buffer.append(':'); 1482 buffer.append(matchingRuleID); 1483 } 1484 1485 buffer.append(":="); 1486 buffer.append(assertionValue.stringValue()); 1487 break; 1488 } 1489 1490 buffer.append(')'); 1491 } 1492}