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