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