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