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 */
021 package com.unboundid.ldap.matchingrules;
022
023
024
025 import java.io.Serializable;
026 import java.lang.reflect.Method;
027
028 import com.unboundid.asn1.ASN1OctetString;
029 import com.unboundid.ldap.sdk.LDAPException;
030 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
031 import com.unboundid.ldap.sdk.schema.Schema;
032 import com.unboundid.util.Debug;
033 import com.unboundid.util.Extensible;
034 import com.unboundid.util.ThreadSafety;
035 import com.unboundid.util.ThreadSafetyLevel;
036
037 import 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)
048 public 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 }