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