001/* 002 * Copyright 2007-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.matchingrules; 022 023 024 025import java.io.Serializable; 026 027import com.unboundid.asn1.ASN1OctetString; 028import com.unboundid.ldap.sdk.LDAPException; 029import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 030import com.unboundid.ldap.sdk.schema.Schema; 031import com.unboundid.ldap.sdk.unboundidds.jsonfilter. 032 JSONObjectExactMatchingRule; 033import com.unboundid.util.Debug; 034import com.unboundid.util.Extensible; 035import com.unboundid.util.StaticUtils; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039 040 041/** 042 * This class defines the API for an LDAP matching rule, which may be used to 043 * determine whether two values are equal to each other, and to normalize values 044 * so that they may be more easily compared. 045 */ 046@Extensible() 047@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 048public abstract class MatchingRule 049 implements Serializable 050{ 051 /** 052 * The substring element type used for subInitial substring assertion 053 * components. 054 */ 055 public static final byte SUBSTRING_TYPE_SUBINITIAL = (byte) 0x80; 056 057 058 059 /** 060 * The substring element type used for subAny substring assertion components. 061 */ 062 public static final byte SUBSTRING_TYPE_SUBANY = (byte) 0x81; 063 064 065 066 /** 067 * The substring element type used for subFinal substring assertion 068 * components. 069 */ 070 public static final byte SUBSTRING_TYPE_SUBFINAL = (byte) 0x82; 071 072 073 074 /** 075 * The serial version UID for this serializable class. 076 */ 077 private static final long serialVersionUID = 6050276733546358513L; 078 079 080 081 /** 082 * Creates a new instance of this matching rule. 083 */ 084 protected MatchingRule() 085 { 086 // No implementation is required. 087 } 088 089 090 091 /** 092 * Retrieves the name for this matching rule when used to perform equality 093 * matching, if appropriate. 094 * 095 * @return The name for this matching rule when used to perform equality 096 * matching, or {@code null} if this matching rule is not intended 097 * to be used for equality matching. 098 */ 099 public abstract String getEqualityMatchingRuleName(); 100 101 102 103 /** 104 * Retrieves the OID for this matching rule when used to perform equality 105 * matching, if appropriate. 106 * 107 * @return The OID for this matching rule when used to perform equality 108 * matching, or {@code null} if this matching rule is not intended 109 * to be used for equality matching. 110 */ 111 public abstract String getEqualityMatchingRuleOID(); 112 113 114 115 /** 116 * Retrieves the name for this matching rule when used to perform equality 117 * matching if defined, or the OID if no name is available. 118 * 119 * @return The name or OID for this matching rule when used to perform 120 * equality matching, or {@code null} if this matching rule cannot 121 * be used to perform equality matching. 122 */ 123 public String getEqualityMatchingRuleNameOrOID() 124 { 125 final String name = getEqualityMatchingRuleName(); 126 if (name == null) 127 { 128 return getEqualityMatchingRuleOID(); 129 } 130 else 131 { 132 return name; 133 } 134 } 135 136 137 138 /** 139 * Retrieves the name for this matching rule when used to perform ordering 140 * matching, if appropriate. 141 * 142 * @return The name for this matching rule when used to perform ordering 143 * matching, or {@code null} if this matching rule is not intended 144 * to be used for ordering matching. 145 */ 146 public abstract String getOrderingMatchingRuleName(); 147 148 149 150 /** 151 * Retrieves the OID for this matching rule when used to perform ordering 152 * matching, if appropriate. 153 * 154 * @return The OID for this matching rule when used to perform ordering 155 * matching, or {@code null} if this matching rule is not intended 156 * to be used for ordering matching. 157 */ 158 public abstract String getOrderingMatchingRuleOID(); 159 160 161 162 /** 163 * Retrieves the name for this matching rule when used to perform ordering 164 * matching if defined, or the OID if no name is available. 165 * 166 * @return The name or OID for this matching rule when used to perform 167 * ordering matching, or {@code null} if this matching rule cannot 168 * be used to perform equality matching. 169 */ 170 public String getOrderingMatchingRuleNameOrOID() 171 { 172 final String name = getOrderingMatchingRuleName(); 173 if (name == null) 174 { 175 return getOrderingMatchingRuleOID(); 176 } 177 else 178 { 179 return name; 180 } 181 } 182 183 184 185 /** 186 * Retrieves the name for this matching rule when used to perform substring 187 * matching, if appropriate. 188 * 189 * @return The name for this matching rule when used to perform substring 190 * matching, or {@code null} if this matching rule is not intended 191 * to be used for substring matching. 192 */ 193 public abstract String getSubstringMatchingRuleName(); 194 195 196 197 /** 198 * Retrieves the OID for this matching rule when used to perform substring 199 * matching, if appropriate. 200 * 201 * @return The OID for this matching rule when used to perform substring 202 * matching, or {@code null} if this matching rule is not intended 203 * to be used for substring matching. 204 */ 205 public abstract String getSubstringMatchingRuleOID(); 206 207 208 209 /** 210 * Retrieves the name for this matching rule when used to perform substring 211 * matching if defined, or the OID if no name is available. 212 * 213 * @return The name or OID for this matching rule when used to perform 214 * substring matching, or {@code null} if this matching rule cannot 215 * be used to perform equality matching. 216 */ 217 public String getSubstringMatchingRuleNameOrOID() 218 { 219 final String name = getSubstringMatchingRuleName(); 220 if (name == null) 221 { 222 return getSubstringMatchingRuleOID(); 223 } 224 else 225 { 226 return name; 227 } 228 } 229 230 231 232 /** 233 * Indicates whether the provided values are equal to each other, according to 234 * the constraints of this matching rule. 235 * 236 * @param value1 The first value for which to make the determination. 237 * @param value2 The second value for which to make the determination. 238 * 239 * @return {@code true} if the provided values are considered equal, or 240 * {@code false} if not. 241 * 242 * @throws LDAPException If a problem occurs while making the determination, 243 * or if this matching rule does not support equality 244 * matching. 245 */ 246 public abstract boolean valuesMatch(ASN1OctetString value1, 247 ASN1OctetString value2) 248 throws LDAPException; 249 250 251 252 /** 253 * Indicates whether the provided assertion value matches any of the provided 254 * attribute values. 255 * 256 * @param assertionValue The assertion value for which to make the 257 * determination. 258 * @param attributeValues The set of attribute values to compare against the 259 * provided assertion value. 260 * 261 * @return {@code true} if the provided assertion value matches any of the 262 * given attribute values, or {@code false} if not. 263 * 264 * @throws LDAPException If a problem occurs while making the determination, 265 * or if this matching rule does not support equality 266 * matching. 267 */ 268 public boolean matchesAnyValue(final ASN1OctetString assertionValue, 269 final ASN1OctetString[] attributeValues) 270 throws LDAPException 271 { 272 if ((assertionValue == null) || (attributeValues == null) || 273 (attributeValues.length == 0)) 274 { 275 return false; 276 } 277 278 boolean exceptionOnEveryAttempt = true; 279 LDAPException firstException = null; 280 for (final ASN1OctetString attributeValue : attributeValues) 281 { 282 try 283 { 284 if (valuesMatch(assertionValue, attributeValue)) 285 { 286 return true; 287 } 288 289 exceptionOnEveryAttempt = false; 290 } 291 catch (final LDAPException le) 292 { 293 Debug.debugException(le); 294 if (firstException == null) 295 { 296 firstException = le; 297 } 298 } 299 } 300 301 if (exceptionOnEveryAttempt) 302 { 303 throw firstException; 304 } 305 306 return false; 307 } 308 309 310 311 /** 312 * Indicates whether the provided value matches the given substring assertion, 313 * according to the constraints of this matching rule. 314 * 315 * @param value The value for which to make the determination. 316 * @param subInitial The subInitial portion of the substring assertion, or 317 * {@code null} if there is no subInitial element. 318 * @param subAny The subAny elements of the substring assertion, or 319 * {@code null} if there are no subAny elements. 320 * @param subFinal The subFinal portion of the substring assertion, or 321 * {@code null} if there is no subFinal element. 322 * 323 * @return {@code true} if the provided value matches the substring 324 * assertion, or {@code false} if not. 325 * 326 * @throws LDAPException If a problem occurs while making the determination, 327 * or if this matching rule does not support substring 328 * matching. 329 */ 330 public abstract boolean matchesSubstring(ASN1OctetString value, 331 ASN1OctetString subInitial, 332 ASN1OctetString[] subAny, 333 ASN1OctetString subFinal) 334 throws LDAPException; 335 336 337 338 /** 339 * Compares the provided values to determine their relative order in a sorted 340 * list. 341 * 342 * @param value1 The first value to compare. 343 * @param value2 The second value to compare. 344 * 345 * @return A negative value if {@code value1} should come before 346 * {@code value2} in a sorted list, a positive value if 347 * {@code value1} should come after {@code value2} in a sorted list, 348 * or zero if the values are equal or there is no distinction between 349 * their orders in a sorted list. 350 * 351 * @throws LDAPException If a problem occurs while making the determination, 352 * or if this matching rule does not support ordering 353 * matching. 354 */ 355 public abstract int compareValues(ASN1OctetString value1, 356 ASN1OctetString value2) 357 throws LDAPException; 358 359 360 361 /** 362 * Normalizes the provided value for easier matching. 363 * 364 * @param value The value to be normalized. 365 * 366 * @return The normalized form of the provided value. 367 * 368 * @throws LDAPException If a problem occurs while normalizing the provided 369 * value. 370 */ 371 public abstract ASN1OctetString normalize(ASN1OctetString value) 372 throws LDAPException; 373 374 375 376 /** 377 * Normalizes the provided value for use as part of a substring assertion. 378 * 379 * @param value The value to be normalized for use as part of a 380 * substring assertion. 381 * @param substringType The substring assertion component type for the 382 * provided value. It should be one of 383 * {@code SUBSTRING_TYPE_SUBINITIAL}, 384 * {@code SUBSTRING_TYPE_SUBANY}, or 385 * {@code SUBSTRING_TYPE_SUBFINAL}. 386 * 387 * @return The normalized form of the provided value. 388 * 389 * @throws LDAPException If a problem occurs while normalizing the provided 390 * value. 391 */ 392 public abstract ASN1OctetString normalizeSubstring(ASN1OctetString value, 393 byte substringType) 394 throws LDAPException; 395 396 397 398 /** 399 * Attempts to select the appropriate matching rule to use for equality 400 * matching against the specified attribute. If an appropriate matching rule 401 * cannot be determined, then the default equality matching rule will be 402 * selected. 403 * 404 * @param attrName The name of the attribute to examine in the provided 405 * schema. 406 * @param schema The schema to examine to make the appropriate 407 * determination. If this is {@code null}, then the default 408 * equality matching rule will be selected. 409 * 410 * @return The selected matching rule. 411 */ 412 public static MatchingRule selectEqualityMatchingRule(final String attrName, 413 final Schema schema) 414 { 415 return selectEqualityMatchingRule(attrName, null, schema); 416 } 417 418 419 420 /** 421 * Attempts to select the appropriate matching rule to use for equality 422 * matching against the specified attribute. If an appropriate matching rule 423 * cannot be determined, then the default equality matching rule will be 424 * selected. 425 * 426 * @param attrName The name of the attribute to examine in the provided 427 * schema. It may be {@code null} if the matching rule 428 * should be selected using the matching rule ID. 429 * @param ruleID The OID of the desired matching rule. It may be 430 * {@code null} if the matching rule should be selected only 431 * using the attribute name. If a rule ID is provided, then 432 * it will be the only criteria used to select the matching 433 * rule. 434 * @param schema The schema to examine to make the appropriate 435 * determination. If this is {@code null} and no rule ID 436 * was provided, then the default equality matching rule 437 * will be selected. 438 * 439 * @return The selected matching rule. 440 */ 441 public static MatchingRule selectEqualityMatchingRule(final String attrName, 442 final String ruleID, final Schema schema) 443 { 444 if (ruleID != null) 445 { 446 return selectEqualityMatchingRule(ruleID); 447 } 448 449 if ((attrName == null) || (schema == null)) 450 { 451 return getDefaultEqualityMatchingRule(); 452 } 453 454 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 455 if (attrType == null) 456 { 457 return getDefaultEqualityMatchingRule(); 458 } 459 460 final String mrName = attrType.getEqualityMatchingRule(schema); 461 if (mrName != null) 462 { 463 return selectEqualityMatchingRule(mrName); 464 } 465 466 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 467 if (syntaxOID != null) 468 { 469 return selectMatchingRuleForSyntax(syntaxOID); 470 } 471 472 return getDefaultEqualityMatchingRule(); 473 } 474 475 476 477 /** 478 * Attempts to select the appropriate matching rule to use for equality 479 * matching using the specified matching rule. If an appropriate matching 480 * rule cannot be determined, then the default equality matching rule will be 481 * selected. 482 * 483 * @param ruleID The name or OID of the desired matching rule. 484 * 485 * @return The selected matching rule. 486 */ 487 public static MatchingRule selectEqualityMatchingRule(final String ruleID) 488 { 489 if ((ruleID == null) || ruleID.isEmpty()) 490 { 491 return getDefaultEqualityMatchingRule(); 492 } 493 494 final String lowerName = StaticUtils.toLowerCase(ruleID); 495 if (lowerName.equals(BooleanMatchingRule.LOWER_EQUALITY_RULE_NAME) || 496 lowerName.equals(BooleanMatchingRule.EQUALITY_RULE_OID)) 497 { 498 return BooleanMatchingRule.getInstance(); 499 } 500 else if (lowerName.equals( 501 CaseExactStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 502 lowerName.equals(CaseExactStringMatchingRule.EQUALITY_RULE_OID) || 503 lowerName.equals("caseexactia5match") || 504 lowerName.equals("1.3.6.1.4.1.1466.109.114.1")) 505 { 506 return CaseExactStringMatchingRule.getInstance(); 507 } 508 else if (lowerName.equals( 509 CaseIgnoreListMatchingRule.LOWER_EQUALITY_RULE_NAME) || 510 lowerName.equals(CaseIgnoreListMatchingRule.EQUALITY_RULE_OID)) 511 { 512 return CaseIgnoreListMatchingRule.getInstance(); 513 } 514 else if (lowerName.equals( 515 CaseIgnoreStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 516 lowerName.equals(CaseIgnoreStringMatchingRule.EQUALITY_RULE_OID) || 517 lowerName.equals("caseignoreia5match") || 518 lowerName.equals("1.3.6.1.4.1.1466.109.114.2")) 519 { 520 return CaseIgnoreStringMatchingRule.getInstance(); 521 } 522 else if (lowerName.equals( 523 DistinguishedNameMatchingRule.LOWER_EQUALITY_RULE_NAME) || 524 lowerName.equals( 525 DistinguishedNameMatchingRule.EQUALITY_RULE_OID) || 526 lowerName.equals("uniquemembermatch") || 527 lowerName.equals("2.5.13.23")) 528 { 529 // NOTE -- Technically uniqueMember should use a name and optional UID 530 // matching rule, but the SDK doesn't currently provide one and the 531 // distinguished name matching rule should be sufficient the vast 532 // majority of the time. 533 return DistinguishedNameMatchingRule.getInstance(); 534 } 535 else if (lowerName.equals( 536 GeneralizedTimeMatchingRule.LOWER_EQUALITY_RULE_NAME) || 537 lowerName.equals(GeneralizedTimeMatchingRule.EQUALITY_RULE_OID)) 538 { 539 return GeneralizedTimeMatchingRule.getInstance(); 540 } 541 else if (lowerName.equals(IntegerMatchingRule.LOWER_EQUALITY_RULE_NAME) || 542 lowerName.equals(IntegerMatchingRule.EQUALITY_RULE_OID)) 543 { 544 return IntegerMatchingRule.getInstance(); 545 } 546 else if (lowerName.equals( 547 NumericStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 548 lowerName.equals(NumericStringMatchingRule.EQUALITY_RULE_OID)) 549 { 550 return NumericStringMatchingRule.getInstance(); 551 } 552 else if (lowerName.equals( 553 OctetStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 554 lowerName.equals(OctetStringMatchingRule.EQUALITY_RULE_OID)) 555 { 556 return OctetStringMatchingRule.getInstance(); 557 } 558 else if (lowerName.equals( 559 TelephoneNumberMatchingRule.LOWER_EQUALITY_RULE_NAME) || 560 lowerName.equals(TelephoneNumberMatchingRule.EQUALITY_RULE_OID)) 561 { 562 return TelephoneNumberMatchingRule.getInstance(); 563 } 564 else if (lowerName.equals("jsonobjectexactmatch") || 565 lowerName.equals("1.3.6.1.4.1.30221.2.4.12")) 566 { 567 return JSONObjectExactMatchingRule.getInstance(); 568 } 569 else 570 { 571 return getDefaultEqualityMatchingRule(); 572 } 573 } 574 575 576 577 /** 578 * Retrieves the default matching rule that will be used for equality matching 579 * if no other matching rule is specified or available. The rule returned 580 * will perform case-ignore string matching. 581 * 582 * @return The default matching rule that will be used for equality matching 583 * if no other matching rule is specified or available. 584 */ 585 public static MatchingRule getDefaultEqualityMatchingRule() 586 { 587 return CaseIgnoreStringMatchingRule.getInstance(); 588 } 589 590 591 592 /** 593 * Attempts to select the appropriate matching rule to use for ordering 594 * matching against the specified attribute. If an appropriate matching rule 595 * cannot be determined, then the default ordering matching rule will be 596 * selected. 597 * 598 * @param attrName The name of the attribute to examine in the provided 599 * schema. 600 * @param schema The schema to examine to make the appropriate 601 * determination. If this is {@code null}, then the default 602 * ordering matching rule will be selected. 603 * 604 * @return The selected matching rule. 605 */ 606 public static MatchingRule selectOrderingMatchingRule(final String attrName, 607 final Schema schema) 608 { 609 return selectOrderingMatchingRule(attrName, null, schema); 610 } 611 612 613 614 /** 615 * Attempts to select the appropriate matching rule to use for ordering 616 * matching against the specified attribute. If an appropriate matching rule 617 * cannot be determined, then the default ordering matching rule will be 618 * selected. 619 * 620 * @param attrName The name of the attribute to examine in the provided 621 * schema. It may be {@code null} if the matching rule 622 * should be selected using the matching rule ID. 623 * @param ruleID The OID of the desired matching rule. It may be 624 * {@code null} if the matching rule should be selected only 625 * using the attribute name. If a rule ID is provided, then 626 * it will be the only criteria used to select the matching 627 * rule. 628 * @param schema The schema to examine to make the appropriate 629 * determination. If this is {@code null} and no rule ID 630 * was provided, then the default ordering matching rule 631 * will be selected. 632 * 633 * @return The selected matching rule. 634 */ 635 public static MatchingRule selectOrderingMatchingRule(final String attrName, 636 final String ruleID, 637 final Schema schema) 638 { 639 if (ruleID != null) 640 { 641 return selectOrderingMatchingRule(ruleID); 642 } 643 644 if ((attrName == null) || (schema == null)) 645 { 646 return getDefaultOrderingMatchingRule(); 647 } 648 649 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 650 if (attrType == null) 651 { 652 return getDefaultOrderingMatchingRule(); 653 } 654 655 final String mrName = attrType.getOrderingMatchingRule(schema); 656 if (mrName != null) 657 { 658 return selectOrderingMatchingRule(mrName); 659 } 660 661 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 662 if (syntaxOID != null) 663 { 664 return selectMatchingRuleForSyntax(syntaxOID); 665 } 666 667 return getDefaultOrderingMatchingRule(); 668 } 669 670 671 672 /** 673 * Attempts to select the appropriate matching rule to use for ordering 674 * matching using the specified matching rule. If an appropriate matching 675 * rule cannot be determined, then the default ordering matching rule will be 676 * selected. 677 * 678 * @param ruleID The name or OID of the desired matching rule. 679 * 680 * @return The selected matching rule. 681 */ 682 public static MatchingRule selectOrderingMatchingRule(final String ruleID) 683 { 684 if ((ruleID == null) || ruleID.isEmpty()) 685 { 686 return getDefaultOrderingMatchingRule(); 687 } 688 689 final String lowerName = StaticUtils.toLowerCase(ruleID); 690 if (lowerName.equals( 691 CaseExactStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 692 lowerName.equals(CaseExactStringMatchingRule.ORDERING_RULE_OID)) 693 { 694 return CaseExactStringMatchingRule.getInstance(); 695 } 696 else if (lowerName.equals( 697 CaseIgnoreStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 698 lowerName.equals(CaseIgnoreStringMatchingRule.ORDERING_RULE_OID)) 699 { 700 return CaseIgnoreStringMatchingRule.getInstance(); 701 } 702 else if (lowerName.equals( 703 GeneralizedTimeMatchingRule.LOWER_ORDERING_RULE_NAME) || 704 lowerName.equals(GeneralizedTimeMatchingRule.ORDERING_RULE_OID)) 705 { 706 return GeneralizedTimeMatchingRule.getInstance(); 707 } 708 else if (lowerName.equals(IntegerMatchingRule.LOWER_ORDERING_RULE_NAME) || 709 lowerName.equals(IntegerMatchingRule.ORDERING_RULE_OID)) 710 { 711 return IntegerMatchingRule.getInstance(); 712 } 713 else if (lowerName.equals( 714 NumericStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 715 lowerName.equals(NumericStringMatchingRule.ORDERING_RULE_OID)) 716 { 717 return NumericStringMatchingRule.getInstance(); 718 } 719 else if (lowerName.equals( 720 OctetStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 721 lowerName.equals(OctetStringMatchingRule.ORDERING_RULE_OID)) 722 { 723 return OctetStringMatchingRule.getInstance(); 724 } 725 else 726 { 727 return getDefaultOrderingMatchingRule(); 728 } 729 } 730 731 732 733 /** 734 * Retrieves the default matching rule that will be used for ordering matching 735 * if no other matching rule is specified or available. The rule returned 736 * will perform case-ignore string matching. 737 * 738 * @return The default matching rule that will be used for ordering matching 739 * if no other matching rule is specified or available. 740 */ 741 public static MatchingRule getDefaultOrderingMatchingRule() 742 { 743 return CaseIgnoreStringMatchingRule.getInstance(); 744 } 745 746 747 748 /** 749 * Attempts to select the appropriate matching rule to use for substring 750 * matching against the specified attribute. If an appropriate matching rule 751 * cannot be determined, then the default substring matching rule will be 752 * selected. 753 * 754 * @param attrName The name of the attribute to examine in the provided 755 * schema. 756 * @param schema The schema to examine to make the appropriate 757 * determination. If this is {@code null}, then the default 758 * substring matching rule will be selected. 759 * 760 * @return The selected matching rule. 761 */ 762 public static MatchingRule selectSubstringMatchingRule(final String attrName, 763 final Schema schema) 764 { 765 return selectSubstringMatchingRule(attrName, null, schema); 766 } 767 768 769 770 /** 771 * Attempts to select the appropriate matching rule to use for substring 772 * matching against the specified attribute. If an appropriate matching rule 773 * cannot be determined, then the default substring matching rule will be 774 * selected. 775 * 776 * @param attrName The name of the attribute to examine in the provided 777 * schema. It may be {@code null} if the matching rule 778 * should be selected using the matching rule ID. 779 * @param ruleID The OID of the desired matching rule. It may be 780 * {@code null} if the matching rule should be selected only 781 * using the attribute name. If a rule ID is provided, then 782 * it will be the only criteria used to select the matching 783 * rule. 784 * @param schema The schema to examine to make the appropriate 785 * determination. If this is {@code null} and no rule ID 786 * was provided, then the default substring matching rule 787 * will be selected. 788 * 789 * @return The selected matching rule. 790 */ 791 public static MatchingRule selectSubstringMatchingRule(final String attrName, 792 final String ruleID, 793 final Schema schema) 794 { 795 if (ruleID != null) 796 { 797 return selectSubstringMatchingRule(ruleID); 798 } 799 800 if ((attrName == null) || (schema == null)) 801 { 802 return getDefaultSubstringMatchingRule(); 803 } 804 805 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 806 if (attrType == null) 807 { 808 return getDefaultSubstringMatchingRule(); 809 } 810 811 final String mrName = attrType.getSubstringMatchingRule(schema); 812 if (mrName != null) 813 { 814 return selectSubstringMatchingRule(mrName); 815 } 816 817 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 818 if (syntaxOID != null) 819 { 820 return selectMatchingRuleForSyntax(syntaxOID); 821 } 822 823 return getDefaultSubstringMatchingRule(); 824 } 825 826 827 828 /** 829 * Attempts to select the appropriate matching rule to use for substring 830 * matching using the specified matching rule. If an appropriate matching 831 * rule cannot be determined, then the default substring matching rule will be 832 * selected. 833 * 834 * @param ruleID The name or OID of the desired matching rule. 835 * 836 * @return The selected matching rule. 837 */ 838 public static MatchingRule selectSubstringMatchingRule(final String ruleID) 839 { 840 if ((ruleID == null) || ruleID.isEmpty()) 841 { 842 return getDefaultSubstringMatchingRule(); 843 } 844 845 final String lowerName = StaticUtils.toLowerCase(ruleID); 846 if (lowerName.equals( 847 CaseExactStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 848 lowerName.equals(CaseExactStringMatchingRule.SUBSTRING_RULE_OID) || 849 lowerName.equals("caseexactia5substringsmatch")) 850 { 851 return CaseExactStringMatchingRule.getInstance(); 852 } 853 else if (lowerName.equals( 854 CaseIgnoreListMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 855 lowerName.equals(CaseIgnoreListMatchingRule.SUBSTRING_RULE_OID)) 856 { 857 return CaseIgnoreListMatchingRule.getInstance(); 858 } 859 else if (lowerName.equals( 860 CaseIgnoreStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 861 lowerName.equals( 862 CaseIgnoreStringMatchingRule.SUBSTRING_RULE_OID) || 863 lowerName.equals("caseignoreia5substringsmatch") || 864 lowerName.equals("1.3.6.1.4.1.1466.109.114.3")) 865 { 866 return CaseIgnoreStringMatchingRule.getInstance(); 867 } 868 else if (lowerName.equals( 869 NumericStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 870 lowerName.equals(NumericStringMatchingRule.SUBSTRING_RULE_OID)) 871 { 872 return NumericStringMatchingRule.getInstance(); 873 } 874 else if (lowerName.equals( 875 OctetStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 876 lowerName.equals(OctetStringMatchingRule.SUBSTRING_RULE_OID)) 877 { 878 return OctetStringMatchingRule.getInstance(); 879 } 880 else if (lowerName.equals( 881 TelephoneNumberMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 882 lowerName.equals(TelephoneNumberMatchingRule.SUBSTRING_RULE_OID)) 883 { 884 return TelephoneNumberMatchingRule.getInstance(); 885 } 886 else 887 { 888 return getDefaultSubstringMatchingRule(); 889 } 890 } 891 892 893 894 /** 895 * Retrieves the default matching rule that will be used for substring 896 * matching if no other matching rule is specified or available. The rule 897 * returned will perform case-ignore string matching. 898 * 899 * @return The default matching rule that will be used for substring matching 900 * if no other matching rule is specified or available. 901 */ 902 public static MatchingRule getDefaultSubstringMatchingRule() 903 { 904 return CaseIgnoreStringMatchingRule.getInstance(); 905 } 906 907 908 909 /** 910 * Attempts to select the appropriate matching rule for use with the syntax 911 * with the specified OID. If an appropriate matching rule cannot be 912 * determined, then the case-ignore string matching rule will be selected. 913 * 914 * @param syntaxOID The OID of the attribute syntax for which to make the 915 * determination. 916 * 917 * @return The selected matching rule. 918 */ 919 public static MatchingRule selectMatchingRuleForSyntax(final String syntaxOID) 920 { 921 if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.7")) 922 { 923 return BooleanMatchingRule.getInstance(); 924 } 925 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.41")) // Postal addr. 926 { 927 return CaseIgnoreListMatchingRule.getInstance(); 928 } 929 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.12") || 930 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.34")) // name&optional UID 931 { 932 return DistinguishedNameMatchingRule.getInstance(); 933 } 934 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.24") || 935 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.53")) // UTC time 936 { 937 return GeneralizedTimeMatchingRule.getInstance(); 938 } 939 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.27")) 940 { 941 return IntegerMatchingRule.getInstance(); 942 } 943 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.36")) 944 { 945 return NumericStringMatchingRule.getInstance(); 946 } 947 else if (syntaxOID.equals("1.3.6.1.4.1.4203.1.1.2") || // auth password 948 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.5") || // binary 949 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.8") || // certificate 950 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.9") || // cert list 951 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.10") || // cert pair 952 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.28") || // JPEG 953 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.40")) // octet string 954 { 955 return OctetStringMatchingRule.getInstance(); 956 } 957 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.50")) 958 { 959 return TelephoneNumberMatchingRule.getInstance(); 960 } 961 else if (syntaxOID.equals("1.3.6.1.4.1.30221.2.3.4")) // JSON object exact 962 { 963 return JSONObjectExactMatchingRule.getInstance(); 964 } 965 else 966 { 967 return CaseIgnoreStringMatchingRule.getInstance(); 968 } 969 } 970}