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