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