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