001/* 002 * Copyright 2007-2016 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2016 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 * 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(final ASN1OctetString value1, 247 final ASN1OctetString value2) 248 throws LDAPException; 249 250 251 252 /** 253 * Indicates whether the provided value matches the given substring assertion, 254 * according to the constraints of this matching rule. 255 * 256 * @param value The value for which to make the determination. 257 * @param subInitial The subInitial portion of the substring assertion, or 258 * {@code null} if there is no subInitial element. 259 * @param subAny The subAny elements of the substring assertion, or 260 * {@code null} if there are no subAny elements. 261 * @param subFinal The subFinal portion of the substring assertion, or 262 * {@code null} if there is no subFinal element. 263 * 264 * @return {@code true} if the provided value matches the substring 265 * assertion, or {@code false} if not. 266 * 267 * @throws LDAPException If a problem occurs while making the determination, 268 * or if this matching rule does not support substring 269 * matching. 270 */ 271 public abstract boolean matchesSubstring(final ASN1OctetString value, 272 final ASN1OctetString subInitial, 273 final ASN1OctetString[] subAny, 274 final ASN1OctetString subFinal) 275 throws LDAPException; 276 277 278 279 /** 280 * Compares the provided values to determine their relative order in a sorted 281 * list. 282 * 283 * @param value1 The first value to compare. 284 * @param value2 The second value to compare. 285 * 286 * @return A negative value if {@code value1} should come before 287 * {@code value2} in a sorted list, a positive value if 288 * {@code value1} should come after {@code value2} in a sorted list, 289 * or zero if the values are equal or there is no distinction between 290 * their orders in a sorted list. 291 * 292 * @throws LDAPException If a problem occurs while making the determination, 293 * or if this matching rule does not support ordering 294 * matching. 295 */ 296 public abstract int compareValues(final ASN1OctetString value1, 297 final ASN1OctetString value2) 298 throws LDAPException; 299 300 301 302 /** 303 * Normalizes the provided value for easier matching. 304 * 305 * @param value The value to be normalized. 306 * 307 * @return The normalized form of the provided value. 308 * 309 * @throws LDAPException If a problem occurs while normalizing the provided 310 * value. 311 */ 312 public abstract ASN1OctetString normalize(final ASN1OctetString value) 313 throws LDAPException; 314 315 316 317 /** 318 * Normalizes the provided value for use as part of a substring assertion. 319 * 320 * @param value The value to be normalized for use as part of a 321 * substring assertion. 322 * @param substringType The substring assertion component type for the 323 * provided value. It should be one of 324 * {@code SUBSTRING_TYPE_SUBINITIAL}, 325 * {@code SUBSTRING_TYPE_SUBANY}, or 326 * {@code SUBSTRING_TYPE_SUBFINAL}. 327 * 328 * @return The normalized form of the provided value. 329 * 330 * @throws LDAPException If a problem occurs while normalizing the provided 331 * value. 332 */ 333 public abstract ASN1OctetString normalizeSubstring( 334 final ASN1OctetString value, 335 final byte substringType) 336 throws LDAPException; 337 338 339 340 /** 341 * Attempts to select the appropriate matching rule to use for equality 342 * matching against the specified attribute. If an appropriate matching rule 343 * cannot be determined, then the default equality matching rule will be 344 * selected. 345 * 346 * @param attrName The name of the attribute to examine in the provided 347 * schema. 348 * @param schema The schema to examine to make the appropriate 349 * determination. If this is {@code null}, then the default 350 * equality matching rule will be selected. 351 * 352 * @return The selected matching rule. 353 */ 354 public static MatchingRule selectEqualityMatchingRule(final String attrName, 355 final Schema schema) 356 { 357 return selectEqualityMatchingRule(attrName, null, schema); 358 } 359 360 361 362 /** 363 * Attempts to select the appropriate matching rule to use for equality 364 * matching against the specified attribute. If an appropriate matching rule 365 * cannot be determined, then the default equality matching rule will be 366 * selected. 367 * 368 * @param attrName The name of the attribute to examine in the provided 369 * schema. It may be {@code null} if the matching rule 370 * should be selected using the matching rule ID. 371 * @param ruleID The OID of the desired matching rule. It may be 372 * {@code null} if the matching rule should be selected only 373 * using the attribute name. If a rule ID is provided, then 374 * it will be the only criteria used to select the matching 375 * rule. 376 * @param schema The schema to examine to make the appropriate 377 * determination. If this is {@code null} and no rule ID 378 * was provided, then the default equality matching rule 379 * will be selected. 380 * 381 * @return The selected matching rule. 382 */ 383 public static MatchingRule selectEqualityMatchingRule(final String attrName, 384 final String ruleID, final Schema schema) 385 { 386 if (ruleID != null) 387 { 388 return selectEqualityMatchingRule(ruleID); 389 } 390 391 if ((attrName == null) || (schema == null)) 392 { 393 return getDefaultEqualityMatchingRule(); 394 } 395 396 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 397 if (attrType == null) 398 { 399 return getDefaultEqualityMatchingRule(); 400 } 401 402 final String mrName = attrType.getEqualityMatchingRule(schema); 403 if (mrName != null) 404 { 405 return selectEqualityMatchingRule(mrName); 406 } 407 408 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 409 if (syntaxOID != null) 410 { 411 return selectMatchingRuleForSyntax(syntaxOID); 412 } 413 414 return getDefaultEqualityMatchingRule(); 415 } 416 417 418 419 /** 420 * Attempts to select the appropriate matching rule to use for equality 421 * matching using the specified matching rule. If an appropriate matching 422 * rule cannot be determined, then the default equality matching rule will be 423 * selected. 424 * 425 * @param ruleID The name or OID of the desired matching rule. 426 * 427 * @return The selected matching rule. 428 */ 429 public static MatchingRule selectEqualityMatchingRule(final String ruleID) 430 { 431 if ((ruleID == null) || (ruleID.length() == 0)) 432 { 433 return getDefaultEqualityMatchingRule(); 434 } 435 436 final String lowerName = toLowerCase(ruleID); 437 if (lowerName.equals(BooleanMatchingRule.LOWER_EQUALITY_RULE_NAME) || 438 lowerName.equals(BooleanMatchingRule.EQUALITY_RULE_OID)) 439 { 440 return BooleanMatchingRule.getInstance(); 441 } 442 else if (lowerName.equals( 443 CaseExactStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 444 lowerName.equals(CaseExactStringMatchingRule.EQUALITY_RULE_OID) || 445 lowerName.equals("caseexactia5match") || 446 lowerName.equals("1.3.6.1.4.1.1466.109.114.1")) 447 { 448 return CaseExactStringMatchingRule.getInstance(); 449 } 450 else if (lowerName.equals( 451 CaseIgnoreListMatchingRule.LOWER_EQUALITY_RULE_NAME) || 452 lowerName.equals(CaseIgnoreListMatchingRule.EQUALITY_RULE_OID)) 453 { 454 return CaseIgnoreListMatchingRule.getInstance(); 455 } 456 else if (lowerName.equals( 457 CaseIgnoreStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 458 lowerName.equals(CaseIgnoreStringMatchingRule.EQUALITY_RULE_OID) || 459 lowerName.equals("caseignoreia5match") || 460 lowerName.equals("1.3.6.1.4.1.1466.109.114.2")) 461 { 462 return CaseIgnoreStringMatchingRule.getInstance(); 463 } 464 else if (lowerName.equals( 465 DistinguishedNameMatchingRule.LOWER_EQUALITY_RULE_NAME) || 466 lowerName.equals( 467 DistinguishedNameMatchingRule.EQUALITY_RULE_OID) || 468 lowerName.equals("uniquemembermatch") || 469 lowerName.equals("2.5.13.23")) 470 { 471 // NOTE -- Technically uniqueMember should use a name and optional UID 472 // matching rule, but the SDK doesn't currently provide one and the 473 // distinguished name matching rule should be sufficient the vast 474 // majority of the time. 475 return DistinguishedNameMatchingRule.getInstance(); 476 } 477 else if (lowerName.equals( 478 GeneralizedTimeMatchingRule.LOWER_EQUALITY_RULE_NAME) || 479 lowerName.equals(GeneralizedTimeMatchingRule.EQUALITY_RULE_OID)) 480 { 481 return GeneralizedTimeMatchingRule.getInstance(); 482 } 483 else if (lowerName.equals(IntegerMatchingRule.LOWER_EQUALITY_RULE_NAME) || 484 lowerName.equals(IntegerMatchingRule.EQUALITY_RULE_OID)) 485 { 486 return IntegerMatchingRule.getInstance(); 487 } 488 else if (lowerName.equals( 489 NumericStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 490 lowerName.equals(NumericStringMatchingRule.EQUALITY_RULE_OID)) 491 { 492 return NumericStringMatchingRule.getInstance(); 493 } 494 else if (lowerName.equals( 495 OctetStringMatchingRule.LOWER_EQUALITY_RULE_NAME) || 496 lowerName.equals(OctetStringMatchingRule.EQUALITY_RULE_OID)) 497 { 498 return OctetStringMatchingRule.getInstance(); 499 } 500 else if (lowerName.equals( 501 TelephoneNumberMatchingRule.LOWER_EQUALITY_RULE_NAME) || 502 lowerName.equals(TelephoneNumberMatchingRule.EQUALITY_RULE_OID)) 503 { 504 return TelephoneNumberMatchingRule.getInstance(); 505 } 506 else if (lowerName.equals("jsonobjectexactmatch") || 507 lowerName.equals("1.3.6.1.4.1.30221.2.4.12")) 508 { 509 // This is the jsonObjectExactMatch matching rule. That rule is only 510 // supported in the Commercial Edition of the LDAP SDK. Use reflection to 511 // get it if it's available. 512 try 513 { 514 final Class<?> c = Class.forName("com.unboundid.ldap.sdk.unboundidds." + 515 "jsonfilter.JSONObjectExactMatchingRule"); 516 final Method m = c.getMethod("getInstance"); 517 return (MatchingRule) m.invoke(null); 518 } 519 catch (final Exception e) 520 { 521 Debug.debugException(e); 522 return CaseIgnoreStringMatchingRule.getInstance(); 523 } 524 } 525 else 526 { 527 return getDefaultEqualityMatchingRule(); 528 } 529 } 530 531 532 533 /** 534 * Retrieves the default matching rule that will be used for equality matching 535 * if no other matching rule is specified or available. The rule returned 536 * will perform case-ignore string matching. 537 * 538 * @return The default matching rule that will be used for equality matching 539 * if no other matching rule is specified or available. 540 */ 541 public static MatchingRule getDefaultEqualityMatchingRule() 542 { 543 return CaseIgnoreStringMatchingRule.getInstance(); 544 } 545 546 547 548 /** 549 * Attempts to select the appropriate matching rule to use for ordering 550 * matching against the specified attribute. If an appropriate matching rule 551 * cannot be determined, then the default ordering matching rule will be 552 * selected. 553 * 554 * @param attrName The name of the attribute to examine in the provided 555 * schema. 556 * @param schema The schema to examine to make the appropriate 557 * determination. If this is {@code null}, then the default 558 * ordering matching rule will be selected. 559 * 560 * @return The selected matching rule. 561 */ 562 public static MatchingRule selectOrderingMatchingRule(final String attrName, 563 final Schema schema) 564 { 565 return selectOrderingMatchingRule(attrName, null, schema); 566 } 567 568 569 570 /** 571 * Attempts to select the appropriate matching rule to use for ordering 572 * matching against the specified attribute. If an appropriate matching rule 573 * cannot be determined, then the default ordering matching rule will be 574 * selected. 575 * 576 * @param attrName The name of the attribute to examine in the provided 577 * schema. It may be {@code null} if the matching rule 578 * should be selected using the matching rule ID. 579 * @param ruleID The OID of the desired matching rule. It may be 580 * {@code null} if the matching rule should be selected only 581 * using the attribute name. If a rule ID is provided, then 582 * it will be the only criteria used to select the matching 583 * rule. 584 * @param schema The schema to examine to make the appropriate 585 * determination. If this is {@code null} and no rule ID 586 * was provided, then the default ordering matching rule 587 * will be selected. 588 * 589 * @return The selected matching rule. 590 */ 591 public static MatchingRule selectOrderingMatchingRule(final String attrName, 592 final String ruleID, 593 final Schema schema) 594 { 595 if (ruleID != null) 596 { 597 return selectOrderingMatchingRule(ruleID); 598 } 599 600 if ((attrName == null) || (schema == null)) 601 { 602 return getDefaultOrderingMatchingRule(); 603 } 604 605 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 606 if (attrType == null) 607 { 608 return getDefaultOrderingMatchingRule(); 609 } 610 611 final String mrName = attrType.getOrderingMatchingRule(schema); 612 if (mrName != null) 613 { 614 return selectOrderingMatchingRule(mrName); 615 } 616 617 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 618 if (syntaxOID != null) 619 { 620 return selectMatchingRuleForSyntax(syntaxOID); 621 } 622 623 return getDefaultOrderingMatchingRule(); 624 } 625 626 627 628 /** 629 * Attempts to select the appropriate matching rule to use for ordering 630 * matching using the specified matching rule. If an appropriate matching 631 * rule cannot be determined, then the default ordering matching rule will be 632 * selected. 633 * 634 * @param ruleID The name or OID of the desired matching rule. 635 * 636 * @return The selected matching rule. 637 */ 638 public static MatchingRule selectOrderingMatchingRule(final String ruleID) 639 { 640 if ((ruleID == null) || (ruleID.length() == 0)) 641 { 642 return getDefaultOrderingMatchingRule(); 643 } 644 645 final String lowerName = toLowerCase(ruleID); 646 if (lowerName.equals( 647 CaseExactStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 648 lowerName.equals(CaseExactStringMatchingRule.ORDERING_RULE_OID)) 649 { 650 return CaseExactStringMatchingRule.getInstance(); 651 } 652 else if (lowerName.equals( 653 CaseIgnoreStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 654 lowerName.equals(CaseIgnoreStringMatchingRule.ORDERING_RULE_OID)) 655 { 656 return CaseIgnoreStringMatchingRule.getInstance(); 657 } 658 else if (lowerName.equals( 659 GeneralizedTimeMatchingRule.LOWER_ORDERING_RULE_NAME) || 660 lowerName.equals(GeneralizedTimeMatchingRule.ORDERING_RULE_OID)) 661 { 662 return GeneralizedTimeMatchingRule.getInstance(); 663 } 664 else if (lowerName.equals(IntegerMatchingRule.LOWER_ORDERING_RULE_NAME) || 665 lowerName.equals(IntegerMatchingRule.ORDERING_RULE_OID)) 666 { 667 return IntegerMatchingRule.getInstance(); 668 } 669 else if (lowerName.equals( 670 NumericStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 671 lowerName.equals(NumericStringMatchingRule.ORDERING_RULE_OID)) 672 { 673 return NumericStringMatchingRule.getInstance(); 674 } 675 else if (lowerName.equals( 676 OctetStringMatchingRule.LOWER_ORDERING_RULE_NAME) || 677 lowerName.equals(OctetStringMatchingRule.ORDERING_RULE_OID)) 678 { 679 return OctetStringMatchingRule.getInstance(); 680 } 681 else 682 { 683 return getDefaultOrderingMatchingRule(); 684 } 685 } 686 687 688 689 /** 690 * Retrieves the default matching rule that will be used for ordering matching 691 * if no other matching rule is specified or available. The rule returned 692 * will perform case-ignore string matching. 693 * 694 * @return The default matching rule that will be used for ordering matching 695 * if no other matching rule is specified or available. 696 */ 697 public static MatchingRule getDefaultOrderingMatchingRule() 698 { 699 return CaseIgnoreStringMatchingRule.getInstance(); 700 } 701 702 703 704 /** 705 * Attempts to select the appropriate matching rule to use for substring 706 * matching against the specified attribute. If an appropriate matching rule 707 * cannot be determined, then the default substring matching rule will be 708 * selected. 709 * 710 * @param attrName The name of the attribute to examine in the provided 711 * schema. 712 * @param schema The schema to examine to make the appropriate 713 * determination. If this is {@code null}, then the default 714 * substring matching rule will be selected. 715 * 716 * @return The selected matching rule. 717 */ 718 public static MatchingRule selectSubstringMatchingRule(final String attrName, 719 final Schema schema) 720 { 721 return selectSubstringMatchingRule(attrName, null, schema); 722 } 723 724 725 726 /** 727 * Attempts to select the appropriate matching rule to use for substring 728 * matching against the specified attribute. If an appropriate matching rule 729 * cannot be determined, then the default substring matching rule will be 730 * selected. 731 * 732 * @param attrName The name of the attribute to examine in the provided 733 * schema. It may be {@code null} if the matching rule 734 * should be selected using the matching rule ID. 735 * @param ruleID The OID of the desired matching rule. It may be 736 * {@code null} if the matching rule should be selected only 737 * using the attribute name. If a rule ID is provided, then 738 * it will be the only criteria used to select the matching 739 * rule. 740 * @param schema The schema to examine to make the appropriate 741 * determination. If this is {@code null} and no rule ID 742 * was provided, then the default substring matching rule 743 * will be selected. 744 * 745 * @return The selected matching rule. 746 */ 747 public static MatchingRule selectSubstringMatchingRule(final String attrName, 748 final String ruleID, 749 final Schema schema) 750 { 751 if (ruleID != null) 752 { 753 return selectSubstringMatchingRule(ruleID); 754 } 755 756 if ((attrName == null) || (schema == null)) 757 { 758 return getDefaultSubstringMatchingRule(); 759 } 760 761 final AttributeTypeDefinition attrType = schema.getAttributeType(attrName); 762 if (attrType == null) 763 { 764 return getDefaultSubstringMatchingRule(); 765 } 766 767 final String mrName = attrType.getSubstringMatchingRule(schema); 768 if (mrName != null) 769 { 770 return selectSubstringMatchingRule(mrName); 771 } 772 773 final String syntaxOID = attrType.getBaseSyntaxOID(schema); 774 if (syntaxOID != null) 775 { 776 return selectMatchingRuleForSyntax(syntaxOID); 777 } 778 779 return getDefaultSubstringMatchingRule(); 780 } 781 782 783 784 /** 785 * Attempts to select the appropriate matching rule to use for substring 786 * matching using the specified matching rule. If an appropriate matching 787 * rule cannot be determined, then the default substring matching rule will be 788 * selected. 789 * 790 * @param ruleID The name or OID of the desired matching rule. 791 * 792 * @return The selected matching rule. 793 */ 794 public static MatchingRule selectSubstringMatchingRule(final String ruleID) 795 { 796 if ((ruleID == null) || (ruleID.length() == 0)) 797 { 798 return getDefaultSubstringMatchingRule(); 799 } 800 801 final String lowerName = toLowerCase(ruleID); 802 if (lowerName.equals( 803 CaseExactStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 804 lowerName.equals(CaseExactStringMatchingRule.SUBSTRING_RULE_OID) || 805 lowerName.equals("caseexactia5substringsmatch")) 806 { 807 return CaseExactStringMatchingRule.getInstance(); 808 } 809 else if (lowerName.equals( 810 CaseIgnoreListMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 811 lowerName.equals(CaseIgnoreListMatchingRule.SUBSTRING_RULE_OID)) 812 { 813 return CaseIgnoreListMatchingRule.getInstance(); 814 } 815 else if (lowerName.equals( 816 CaseIgnoreStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 817 lowerName.equals( 818 CaseIgnoreStringMatchingRule.SUBSTRING_RULE_OID) || 819 lowerName.equals("caseignoreia5substringsmatch") || 820 lowerName.equals("1.3.6.1.4.1.1466.109.114.3")) 821 { 822 return CaseIgnoreStringMatchingRule.getInstance(); 823 } 824 else if (lowerName.equals( 825 NumericStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 826 lowerName.equals(NumericStringMatchingRule.SUBSTRING_RULE_OID)) 827 { 828 return NumericStringMatchingRule.getInstance(); 829 } 830 else if (lowerName.equals( 831 OctetStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 832 lowerName.equals(OctetStringMatchingRule.SUBSTRING_RULE_OID)) 833 { 834 return OctetStringMatchingRule.getInstance(); 835 } 836 else if (lowerName.equals( 837 TelephoneNumberMatchingRule.LOWER_SUBSTRING_RULE_NAME) || 838 lowerName.equals(TelephoneNumberMatchingRule.SUBSTRING_RULE_OID)) 839 { 840 return TelephoneNumberMatchingRule.getInstance(); 841 } 842 else 843 { 844 return getDefaultSubstringMatchingRule(); 845 } 846 } 847 848 849 850 /** 851 * Retrieves the default matching rule that will be used for substring 852 * matching if no other matching rule is specified or available. The rule 853 * returned will perform case-ignore string matching. 854 * 855 * @return The default matching rule that will be used for substring matching 856 * if no other matching rule is specified or available. 857 */ 858 public static MatchingRule getDefaultSubstringMatchingRule() 859 { 860 return CaseIgnoreStringMatchingRule.getInstance(); 861 } 862 863 864 865 /** 866 * Attempts to select the appropriate matching rule for use with the syntax 867 * with the specified OID. If an appropriate matching rule cannot be 868 * determined, then the case-ignore string matching rule will be selected. 869 * 870 * @param syntaxOID The OID of the attribute syntax for which to make the 871 * determination. 872 * 873 * @return The selected matching rule. 874 */ 875 public static MatchingRule selectMatchingRuleForSyntax(final String syntaxOID) 876 { 877 if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.7")) 878 { 879 return BooleanMatchingRule.getInstance(); 880 } 881 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.41")) // Postal addr. 882 { 883 return CaseIgnoreListMatchingRule.getInstance(); 884 } 885 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.12") || 886 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.34")) // name&optional UID 887 { 888 return DistinguishedNameMatchingRule.getInstance(); 889 } 890 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.24") || 891 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.53")) // UTC time 892 { 893 return GeneralizedTimeMatchingRule.getInstance(); 894 } 895 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.27")) 896 { 897 return IntegerMatchingRule.getInstance(); 898 } 899 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.36")) 900 { 901 return NumericStringMatchingRule.getInstance(); 902 } 903 else if (syntaxOID.equals("1.3.6.1.4.1.4203.1.1.2") || // auth password 904 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.5") || // binary 905 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.8") || // certificate 906 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.9") || // cert list 907 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.10") || // cert pair 908 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.28") || // JPEG 909 syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.40")) // octet string 910 { 911 return OctetStringMatchingRule.getInstance(); 912 } 913 else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.50")) 914 { 915 return TelephoneNumberMatchingRule.getInstance(); 916 } 917 else if (syntaxOID.equals("1.3.6.1.4.1.30221.2.3.4")) // JSON object 918 { 919 // This is only supported in the Commercial Edition of the LDAP SDK. Use 920 // reflection to get the appropriate matching rule if it's available. 921 try 922 { 923 final Class<?> c = Class.forName("com.unboundid.ldap.sdk.unboundidds." + 924 "jsonfilter.JSONObjectExactMatchingRule"); 925 final Method m = c.getMethod("getInstance"); 926 return (MatchingRule) m.invoke(null); 927 } 928 catch (final Exception e) 929 { 930 Debug.debugException(e); 931 return CaseIgnoreStringMatchingRule.getInstance(); 932 } 933 } 934 else 935 { 936 return CaseIgnoreStringMatchingRule.getInstance(); 937 } 938 } 939}