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 */
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 * 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 }