001/*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.matchingrules;
022
023
024
025import java.io.Serializable;
026import java.lang.reflect.Method;
027
028import com.unboundid.asn1.ASN1OctetString;
029import com.unboundid.ldap.sdk.LDAPException;
030import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
031import com.unboundid.ldap.sdk.schema.Schema;
032import com.unboundid.util.Debug;
033import com.unboundid.util.Extensible;
034import com.unboundid.util.ThreadSafety;
035import com.unboundid.util.ThreadSafetyLevel;
036
037import static com.unboundid.util.StaticUtils.*;
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(final ASN1OctetString value1,
247                                      final ASN1OctetString value2)
248         throws LDAPException;
249
250
251
252  /**
253   * Indicates whether the provided value matches the given substring assertion,
254   * according to the constraints of this matching rule.
255   *
256   * @param  value       The value for which to make the determination.
257   * @param  subInitial  The subInitial portion of the substring assertion, or
258   *                     {@code null} if there is no subInitial element.
259   * @param  subAny      The subAny elements of the substring assertion, or
260   *                     {@code null} if there are no subAny elements.
261   * @param  subFinal    The subFinal portion of the substring assertion, or
262   *                     {@code null} if there is no subFinal element.
263   *
264   * @return  {@code true} if the provided value matches the substring
265   *          assertion, or {@code false} if not.
266   *
267   * @throws  LDAPException  If a problem occurs while making the determination,
268   *                         or if this matching rule does not support substring
269   *                         matching.
270   */
271  public abstract boolean matchesSubstring(final ASN1OctetString value,
272                                           final ASN1OctetString subInitial,
273                                           final ASN1OctetString[] subAny,
274                                           final ASN1OctetString subFinal)
275         throws LDAPException;
276
277
278
279  /**
280   * Compares the provided values to determine their relative order in a sorted
281   * list.
282   *
283   * @param  value1  The first value to compare.
284   * @param  value2  The second value to compare.
285   *
286   * @return  A negative value if {@code value1} should come before
287   *          {@code value2} in a sorted list, a positive value if
288   *          {@code value1} should come after {@code value2} in a sorted list,
289   *          or zero if the values are equal or there is no distinction between
290   *          their orders in a sorted list.
291   *
292   * @throws  LDAPException  If a problem occurs while making the determination,
293   *                         or if this matching rule does not support ordering
294   *                         matching.
295   */
296  public abstract int compareValues(final ASN1OctetString value1,
297                                    final ASN1OctetString value2)
298         throws LDAPException;
299
300
301
302  /**
303   * Normalizes the provided value for easier matching.
304   *
305   * @param  value  The value to be normalized.
306   *
307   * @return  The normalized form of the provided value.
308   *
309   * @throws  LDAPException  If a problem occurs while normalizing the provided
310   *                         value.
311   */
312  public abstract ASN1OctetString normalize(final ASN1OctetString value)
313         throws LDAPException;
314
315
316
317  /**
318   * Normalizes the provided value for use as part of a substring assertion.
319   *
320   * @param  value          The value to be normalized for use as part of a
321   *                        substring assertion.
322   * @param  substringType  The substring assertion component type for the
323   *                        provided value.  It should be one of
324   *                        {@code SUBSTRING_TYPE_SUBINITIAL},
325   *                        {@code SUBSTRING_TYPE_SUBANY}, or
326   *                        {@code SUBSTRING_TYPE_SUBFINAL}.
327   *
328   * @return  The normalized form of the provided value.
329   *
330   * @throws  LDAPException  If a problem occurs while normalizing the provided
331   *                         value.
332   */
333  public abstract ASN1OctetString normalizeSubstring(
334                                       final ASN1OctetString value,
335                                       final byte substringType)
336         throws LDAPException;
337
338
339
340  /**
341   * Attempts to select the appropriate matching rule to use for equality
342   * matching against the specified attribute.  If an appropriate matching rule
343   * cannot be determined, then the default equality matching rule will be
344   * selected.
345   *
346   * @param  attrName  The name of the attribute to examine in the provided
347   *                   schema.
348   * @param  schema    The schema to examine to make the appropriate
349   *                   determination.  If this is {@code null}, then the default
350   *                   equality matching rule will be selected.
351   *
352   * @return  The selected matching rule.
353   */
354  public static MatchingRule selectEqualityMatchingRule(final String attrName,
355                                                        final Schema schema)
356  {
357    return selectEqualityMatchingRule(attrName, null, schema);
358  }
359
360
361
362  /**
363   * Attempts to select the appropriate matching rule to use for equality
364   * matching against the specified attribute.  If an appropriate matching rule
365   * cannot be determined, then the default equality matching rule will be
366   * selected.
367   *
368   * @param  attrName  The name of the attribute to examine in the provided
369   *                   schema.  It may be {@code null} if the matching rule
370   *                   should be selected using the matching rule ID.
371   * @param  ruleID    The OID of the desired matching rule.  It may be
372   *                   {@code null} if the matching rule should be selected only
373   *                   using the attribute name.  If a rule ID is provided, then
374   *                   it will be the only criteria used to select the matching
375   *                   rule.
376   * @param  schema    The schema to examine to make the appropriate
377   *                   determination.  If this is {@code null} and no rule ID
378   *                   was provided, then the default equality matching rule
379   *                   will be selected.
380   *
381   * @return  The selected matching rule.
382   */
383  public static MatchingRule selectEqualityMatchingRule(final String attrName,
384                                  final String ruleID, final Schema schema)
385  {
386    if (ruleID != null)
387    {
388      return selectEqualityMatchingRule(ruleID);
389    }
390
391    if ((attrName == null) || (schema == null))
392    {
393      return getDefaultEqualityMatchingRule();
394    }
395
396    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
397    if (attrType == null)
398    {
399      return getDefaultEqualityMatchingRule();
400    }
401
402    final String mrName = attrType.getEqualityMatchingRule(schema);
403    if (mrName != null)
404    {
405      return selectEqualityMatchingRule(mrName);
406    }
407
408    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
409    if (syntaxOID != null)
410    {
411      return selectMatchingRuleForSyntax(syntaxOID);
412    }
413
414    return getDefaultEqualityMatchingRule();
415  }
416
417
418
419  /**
420   * Attempts to select the appropriate matching rule to use for equality
421   * matching using the specified matching rule.  If an appropriate matching
422   * rule cannot be determined, then the default equality matching rule will be
423   * selected.
424   *
425   * @param  ruleID  The name or OID of the desired matching rule.
426   *
427   * @return  The selected matching rule.
428   */
429  public static MatchingRule selectEqualityMatchingRule(final String ruleID)
430  {
431    if ((ruleID == null) || (ruleID.length() == 0))
432    {
433      return getDefaultEqualityMatchingRule();
434    }
435
436    final String lowerName = toLowerCase(ruleID);
437    if (lowerName.equals(BooleanMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
438        lowerName.equals(BooleanMatchingRule.EQUALITY_RULE_OID))
439    {
440      return BooleanMatchingRule.getInstance();
441    }
442    else if (lowerName.equals(
443                  CaseExactStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
444             lowerName.equals(CaseExactStringMatchingRule.EQUALITY_RULE_OID) ||
445             lowerName.equals("caseexactia5match") ||
446             lowerName.equals("1.3.6.1.4.1.1466.109.114.1"))
447    {
448      return CaseExactStringMatchingRule.getInstance();
449    }
450    else if (lowerName.equals(
451                  CaseIgnoreListMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
452             lowerName.equals(CaseIgnoreListMatchingRule.EQUALITY_RULE_OID))
453    {
454      return CaseIgnoreListMatchingRule.getInstance();
455    }
456    else if (lowerName.equals(
457                  CaseIgnoreStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
458             lowerName.equals(CaseIgnoreStringMatchingRule.EQUALITY_RULE_OID) ||
459             lowerName.equals("caseignoreia5match") ||
460             lowerName.equals("1.3.6.1.4.1.1466.109.114.2"))
461    {
462      return CaseIgnoreStringMatchingRule.getInstance();
463    }
464    else if (lowerName.equals(
465                  DistinguishedNameMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
466             lowerName.equals(
467                  DistinguishedNameMatchingRule.EQUALITY_RULE_OID) ||
468             lowerName.equals("uniquemembermatch") ||
469             lowerName.equals("2.5.13.23"))
470    {
471      // NOTE -- Technically uniqueMember should use a name and optional UID
472      // matching rule, but the SDK doesn't currently provide one and the
473      // distinguished name matching rule should be sufficient the vast
474      // majority of the time.
475      return DistinguishedNameMatchingRule.getInstance();
476    }
477    else if (lowerName.equals(
478                  GeneralizedTimeMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
479             lowerName.equals(GeneralizedTimeMatchingRule.EQUALITY_RULE_OID))
480    {
481      return GeneralizedTimeMatchingRule.getInstance();
482    }
483    else if (lowerName.equals(IntegerMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
484             lowerName.equals(IntegerMatchingRule.EQUALITY_RULE_OID))
485    {
486      return IntegerMatchingRule.getInstance();
487    }
488    else if (lowerName.equals(
489                  NumericStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
490             lowerName.equals(NumericStringMatchingRule.EQUALITY_RULE_OID))
491    {
492      return NumericStringMatchingRule.getInstance();
493    }
494    else if (lowerName.equals(
495                  OctetStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
496             lowerName.equals(OctetStringMatchingRule.EQUALITY_RULE_OID))
497    {
498      return OctetStringMatchingRule.getInstance();
499    }
500    else if (lowerName.equals(
501                  TelephoneNumberMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
502             lowerName.equals(TelephoneNumberMatchingRule.EQUALITY_RULE_OID))
503    {
504      return TelephoneNumberMatchingRule.getInstance();
505    }
506    else if (lowerName.equals("jsonobjectexactmatch") ||
507             lowerName.equals("1.3.6.1.4.1.30221.2.4.12"))
508    {
509      // This is the jsonObjectExactMatch matching rule.  That rule is only
510      // supported in the Commercial Edition of the LDAP SDK.  Use reflection to
511      // get it if it's available.
512      try
513      {
514        final Class<?> c = Class.forName("com.unboundid.ldap.sdk.unboundidds." +
515             "jsonfilter.JSONObjectExactMatchingRule");
516        final Method m = c.getMethod("getInstance");
517        return (MatchingRule) m.invoke(null);
518      }
519      catch (final Exception e)
520      {
521        Debug.debugException(e);
522        return CaseIgnoreStringMatchingRule.getInstance();
523      }
524    }
525    else
526    {
527      return getDefaultEqualityMatchingRule();
528    }
529  }
530
531
532
533  /**
534   * Retrieves the default matching rule that will be used for equality matching
535   * if no other matching rule is specified or available.  The rule returned
536   * will perform case-ignore string matching.
537   *
538   * @return  The default matching rule that will be used for equality matching
539   *          if no other matching rule is specified or available.
540   */
541  public static MatchingRule getDefaultEqualityMatchingRule()
542  {
543    return CaseIgnoreStringMatchingRule.getInstance();
544  }
545
546
547
548  /**
549   * Attempts to select the appropriate matching rule to use for ordering
550   * matching against the specified attribute.  If an appropriate matching rule
551   * cannot be determined, then the default ordering matching rule will be
552   * selected.
553   *
554   * @param  attrName  The name of the attribute to examine in the provided
555   *                   schema.
556   * @param  schema    The schema to examine to make the appropriate
557   *                   determination.  If this is {@code null}, then the default
558   *                   ordering matching rule will be selected.
559   *
560   * @return  The selected matching rule.
561   */
562  public static MatchingRule selectOrderingMatchingRule(final String attrName,
563                                                        final Schema schema)
564  {
565    return selectOrderingMatchingRule(attrName, null, schema);
566  }
567
568
569
570  /**
571   * Attempts to select the appropriate matching rule to use for ordering
572   * matching against the specified attribute.  If an appropriate matching rule
573   * cannot be determined, then the default ordering matching rule will be
574   * selected.
575   *
576   * @param  attrName  The name of the attribute to examine in the provided
577   *                   schema.  It may be {@code null} if the matching rule
578   *                   should be selected using the matching rule ID.
579   * @param  ruleID    The OID of the desired matching rule.  It may be
580   *                   {@code null} if the matching rule should be selected only
581   *                   using the attribute name.  If a rule ID is provided, then
582   *                   it will be the only criteria used to select the matching
583   *                   rule.
584   * @param  schema    The schema to examine to make the appropriate
585   *                   determination.  If this is {@code null} and no rule ID
586   *                   was provided, then the default ordering matching rule
587   *                   will be selected.
588   *
589   * @return  The selected matching rule.
590   */
591  public static MatchingRule selectOrderingMatchingRule(final String attrName,
592                                                        final String ruleID,
593                                                        final Schema schema)
594  {
595    if (ruleID != null)
596    {
597      return selectOrderingMatchingRule(ruleID);
598    }
599
600    if ((attrName == null) || (schema == null))
601    {
602      return getDefaultOrderingMatchingRule();
603    }
604
605    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
606    if (attrType == null)
607    {
608      return getDefaultOrderingMatchingRule();
609    }
610
611    final String mrName = attrType.getOrderingMatchingRule(schema);
612    if (mrName != null)
613    {
614      return selectOrderingMatchingRule(mrName);
615    }
616
617    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
618    if (syntaxOID != null)
619    {
620      return selectMatchingRuleForSyntax(syntaxOID);
621    }
622
623    return getDefaultOrderingMatchingRule();
624  }
625
626
627
628  /**
629   * Attempts to select the appropriate matching rule to use for ordering
630   * matching using the specified matching rule.  If an appropriate matching
631   * rule cannot be determined, then the default ordering matching rule will be
632   * selected.
633   *
634   * @param  ruleID  The name or OID of the desired matching rule.
635   *
636   * @return  The selected matching rule.
637   */
638  public static MatchingRule selectOrderingMatchingRule(final String ruleID)
639  {
640    if ((ruleID == null) || (ruleID.length() == 0))
641    {
642      return getDefaultOrderingMatchingRule();
643    }
644
645    final String lowerName = toLowerCase(ruleID);
646    if (lowerName.equals(
647             CaseExactStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
648        lowerName.equals(CaseExactStringMatchingRule.ORDERING_RULE_OID))
649    {
650      return CaseExactStringMatchingRule.getInstance();
651    }
652    else if (lowerName.equals(
653                  CaseIgnoreStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
654             lowerName.equals(CaseIgnoreStringMatchingRule.ORDERING_RULE_OID))
655    {
656      return CaseIgnoreStringMatchingRule.getInstance();
657    }
658    else if (lowerName.equals(
659                  GeneralizedTimeMatchingRule.LOWER_ORDERING_RULE_NAME) ||
660             lowerName.equals(GeneralizedTimeMatchingRule.ORDERING_RULE_OID))
661    {
662      return GeneralizedTimeMatchingRule.getInstance();
663    }
664    else if (lowerName.equals(IntegerMatchingRule.LOWER_ORDERING_RULE_NAME) ||
665             lowerName.equals(IntegerMatchingRule.ORDERING_RULE_OID))
666    {
667      return IntegerMatchingRule.getInstance();
668    }
669    else if (lowerName.equals(
670                  NumericStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
671             lowerName.equals(NumericStringMatchingRule.ORDERING_RULE_OID))
672    {
673      return NumericStringMatchingRule.getInstance();
674    }
675    else if (lowerName.equals(
676                  OctetStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
677             lowerName.equals(OctetStringMatchingRule.ORDERING_RULE_OID))
678    {
679      return OctetStringMatchingRule.getInstance();
680    }
681    else
682    {
683      return getDefaultOrderingMatchingRule();
684    }
685  }
686
687
688
689  /**
690   * Retrieves the default matching rule that will be used for ordering matching
691   * if no other matching rule is specified or available.  The rule returned
692   * will perform case-ignore string matching.
693   *
694   * @return  The default matching rule that will be used for ordering matching
695   *          if no other matching rule is specified or available.
696   */
697  public static MatchingRule getDefaultOrderingMatchingRule()
698  {
699    return CaseIgnoreStringMatchingRule.getInstance();
700  }
701
702
703
704  /**
705   * Attempts to select the appropriate matching rule to use for substring
706   * matching against the specified attribute.  If an appropriate matching rule
707   * cannot be determined, then the default substring matching rule will be
708   * selected.
709   *
710   * @param  attrName  The name of the attribute to examine in the provided
711   *                   schema.
712   * @param  schema    The schema to examine to make the appropriate
713   *                   determination.  If this is {@code null}, then the default
714   *                   substring matching rule will be selected.
715   *
716   * @return  The selected matching rule.
717   */
718  public static MatchingRule selectSubstringMatchingRule(final String attrName,
719                                                         final Schema schema)
720  {
721    return selectSubstringMatchingRule(attrName, null, schema);
722  }
723
724
725
726  /**
727   * Attempts to select the appropriate matching rule to use for substring
728   * matching against the specified attribute.  If an appropriate matching rule
729   * cannot be determined, then the default substring matching rule will be
730   * selected.
731   *
732   * @param  attrName  The name of the attribute to examine in the provided
733   *                   schema.  It may be {@code null} if the matching rule
734   *                   should be selected using the matching rule ID.
735   * @param  ruleID    The OID of the desired matching rule.  It may be
736   *                   {@code null} if the matching rule should be selected only
737   *                   using the attribute name.  If a rule ID is provided, then
738   *                   it will be the only criteria used to select the matching
739   *                   rule.
740   * @param  schema    The schema to examine to make the appropriate
741   *                   determination.  If this is {@code null} and no rule ID
742   *                   was provided, then the default substring matching rule
743   *                   will be selected.
744   *
745   * @return  The selected matching rule.
746   */
747  public static MatchingRule selectSubstringMatchingRule(final String attrName,
748                                                         final String ruleID,
749                                                         final Schema schema)
750  {
751    if (ruleID != null)
752    {
753      return selectSubstringMatchingRule(ruleID);
754    }
755
756    if ((attrName == null) || (schema == null))
757    {
758      return getDefaultSubstringMatchingRule();
759    }
760
761    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
762    if (attrType == null)
763    {
764      return getDefaultSubstringMatchingRule();
765    }
766
767    final String mrName = attrType.getSubstringMatchingRule(schema);
768    if (mrName != null)
769    {
770      return selectSubstringMatchingRule(mrName);
771    }
772
773    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
774    if (syntaxOID != null)
775    {
776      return selectMatchingRuleForSyntax(syntaxOID);
777    }
778
779    return getDefaultSubstringMatchingRule();
780  }
781
782
783
784  /**
785   * Attempts to select the appropriate matching rule to use for substring
786   * matching using the specified matching rule.  If an appropriate matching
787   * rule cannot be determined, then the default substring matching rule will be
788   * selected.
789   *
790   * @param  ruleID  The name or OID of the desired matching rule.
791   *
792   * @return  The selected matching rule.
793   */
794  public static MatchingRule selectSubstringMatchingRule(final String ruleID)
795  {
796    if ((ruleID == null) || (ruleID.length() == 0))
797    {
798      return getDefaultSubstringMatchingRule();
799    }
800
801    final String lowerName = toLowerCase(ruleID);
802    if (lowerName.equals(
803             CaseExactStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
804        lowerName.equals(CaseExactStringMatchingRule.SUBSTRING_RULE_OID) ||
805        lowerName.equals("caseexactia5substringsmatch"))
806    {
807      return CaseExactStringMatchingRule.getInstance();
808    }
809    else if (lowerName.equals(
810                  CaseIgnoreListMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
811             lowerName.equals(CaseIgnoreListMatchingRule.SUBSTRING_RULE_OID))
812    {
813      return CaseIgnoreListMatchingRule.getInstance();
814    }
815    else if (lowerName.equals(
816                  CaseIgnoreStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
817             lowerName.equals(
818                  CaseIgnoreStringMatchingRule.SUBSTRING_RULE_OID) ||
819             lowerName.equals("caseignoreia5substringsmatch") ||
820             lowerName.equals("1.3.6.1.4.1.1466.109.114.3"))
821    {
822      return CaseIgnoreStringMatchingRule.getInstance();
823    }
824    else if (lowerName.equals(
825                  NumericStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
826             lowerName.equals(NumericStringMatchingRule.SUBSTRING_RULE_OID))
827    {
828      return NumericStringMatchingRule.getInstance();
829    }
830    else if (lowerName.equals(
831                  OctetStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
832             lowerName.equals(OctetStringMatchingRule.SUBSTRING_RULE_OID))
833    {
834      return OctetStringMatchingRule.getInstance();
835    }
836    else if (lowerName.equals(
837                  TelephoneNumberMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
838             lowerName.equals(TelephoneNumberMatchingRule.SUBSTRING_RULE_OID))
839    {
840      return TelephoneNumberMatchingRule.getInstance();
841    }
842    else
843    {
844      return getDefaultSubstringMatchingRule();
845    }
846  }
847
848
849
850  /**
851   * Retrieves the default matching rule that will be used for substring
852   * matching if no other matching rule is specified or available.  The rule
853   * returned will perform case-ignore string matching.
854   *
855   * @return  The default matching rule that will be used for substring matching
856   *          if no other matching rule is specified or available.
857   */
858  public static MatchingRule getDefaultSubstringMatchingRule()
859  {
860    return CaseIgnoreStringMatchingRule.getInstance();
861  }
862
863
864
865  /**
866   * Attempts to select the appropriate matching rule for use with the syntax
867   * with the specified OID.  If an appropriate matching rule cannot be
868   * determined, then the case-ignore string matching rule will be selected.
869   *
870   * @param  syntaxOID  The OID of the attribute syntax for which to make the
871   *                    determination.
872   *
873   * @return  The selected matching rule.
874   */
875  public static MatchingRule selectMatchingRuleForSyntax(final String syntaxOID)
876  {
877    if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.7"))
878    {
879      return BooleanMatchingRule.getInstance();
880    }
881    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.41")) // Postal addr.
882    {
883      return CaseIgnoreListMatchingRule.getInstance();
884    }
885    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.12") ||
886         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.34")) // name&optional UID
887    {
888      return DistinguishedNameMatchingRule.getInstance();
889    }
890    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.24") ||
891         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.53")) // UTC time
892    {
893      return GeneralizedTimeMatchingRule.getInstance();
894    }
895    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.27"))
896    {
897      return IntegerMatchingRule.getInstance();
898    }
899    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.36"))
900    {
901      return NumericStringMatchingRule.getInstance();
902    }
903    else if (syntaxOID.equals("1.3.6.1.4.1.4203.1.1.2") || // auth password
904         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.5") || // binary
905         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.8") || // certificate
906         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.9") || // cert list
907         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.10") || // cert pair
908         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.28") || // JPEG
909         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.40")) // octet string
910    {
911      return OctetStringMatchingRule.getInstance();
912    }
913    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.50"))
914    {
915      return TelephoneNumberMatchingRule.getInstance();
916    }
917    else if (syntaxOID.equals("1.3.6.1.4.1.30221.2.3.4")) // JSON object
918    {
919      // This is only supported in the Commercial Edition of the LDAP SDK.  Use
920      // reflection to get the appropriate matching rule if it's available.
921      try
922      {
923        final Class<?> c = Class.forName("com.unboundid.ldap.sdk.unboundidds." +
924             "jsonfilter.JSONObjectExactMatchingRule");
925        final Method m = c.getMethod("getInstance");
926        return (MatchingRule) m.invoke(null);
927      }
928      catch (final Exception e)
929      {
930        Debug.debugException(e);
931        return CaseIgnoreStringMatchingRule.getInstance();
932      }
933    }
934    else
935    {
936      return CaseIgnoreStringMatchingRule.getInstance();
937    }
938  }
939}