001/*
002 * Copyright 2007-2017 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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.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(ASN1OctetString value1,
247                                      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(ASN1OctetString value,
272                                           ASN1OctetString subInitial,
273                                           ASN1OctetString[] subAny,
274                                           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(ASN1OctetString value1,
297                                    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(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(ASN1OctetString value,
334                                                     byte substringType)
335         throws LDAPException;
336
337
338
339  /**
340   * Attempts to select the appropriate matching rule to use for equality
341   * matching against the specified attribute.  If an appropriate matching rule
342   * cannot be determined, then the default equality matching rule will be
343   * selected.
344   *
345   * @param  attrName  The name of the attribute to examine in the provided
346   *                   schema.
347   * @param  schema    The schema to examine to make the appropriate
348   *                   determination.  If this is {@code null}, then the default
349   *                   equality matching rule will be selected.
350   *
351   * @return  The selected matching rule.
352   */
353  public static MatchingRule selectEqualityMatchingRule(final String attrName,
354                                                        final Schema schema)
355  {
356    return selectEqualityMatchingRule(attrName, null, schema);
357  }
358
359
360
361  /**
362   * Attempts to select the appropriate matching rule to use for equality
363   * matching against the specified attribute.  If an appropriate matching rule
364   * cannot be determined, then the default equality matching rule will be
365   * selected.
366   *
367   * @param  attrName  The name of the attribute to examine in the provided
368   *                   schema.  It may be {@code null} if the matching rule
369   *                   should be selected using the matching rule ID.
370   * @param  ruleID    The OID of the desired matching rule.  It may be
371   *                   {@code null} if the matching rule should be selected only
372   *                   using the attribute name.  If a rule ID is provided, then
373   *                   it will be the only criteria used to select the matching
374   *                   rule.
375   * @param  schema    The schema to examine to make the appropriate
376   *                   determination.  If this is {@code null} and no rule ID
377   *                   was provided, then the default equality matching rule
378   *                   will be selected.
379   *
380   * @return  The selected matching rule.
381   */
382  public static MatchingRule selectEqualityMatchingRule(final String attrName,
383                                  final String ruleID, final Schema schema)
384  {
385    if (ruleID != null)
386    {
387      return selectEqualityMatchingRule(ruleID);
388    }
389
390    if ((attrName == null) || (schema == null))
391    {
392      return getDefaultEqualityMatchingRule();
393    }
394
395    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
396    if (attrType == null)
397    {
398      return getDefaultEqualityMatchingRule();
399    }
400
401    final String mrName = attrType.getEqualityMatchingRule(schema);
402    if (mrName != null)
403    {
404      return selectEqualityMatchingRule(mrName);
405    }
406
407    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
408    if (syntaxOID != null)
409    {
410      return selectMatchingRuleForSyntax(syntaxOID);
411    }
412
413    return getDefaultEqualityMatchingRule();
414  }
415
416
417
418  /**
419   * Attempts to select the appropriate matching rule to use for equality
420   * matching using the specified matching rule.  If an appropriate matching
421   * rule cannot be determined, then the default equality matching rule will be
422   * selected.
423   *
424   * @param  ruleID  The name or OID of the desired matching rule.
425   *
426   * @return  The selected matching rule.
427   */
428  public static MatchingRule selectEqualityMatchingRule(final String ruleID)
429  {
430    if ((ruleID == null) || (ruleID.length() == 0))
431    {
432      return getDefaultEqualityMatchingRule();
433    }
434
435    final String lowerName = toLowerCase(ruleID);
436    if (lowerName.equals(BooleanMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
437        lowerName.equals(BooleanMatchingRule.EQUALITY_RULE_OID))
438    {
439      return BooleanMatchingRule.getInstance();
440    }
441    else if (lowerName.equals(
442                  CaseExactStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
443             lowerName.equals(CaseExactStringMatchingRule.EQUALITY_RULE_OID) ||
444             lowerName.equals("caseexactia5match") ||
445             lowerName.equals("1.3.6.1.4.1.1466.109.114.1"))
446    {
447      return CaseExactStringMatchingRule.getInstance();
448    }
449    else if (lowerName.equals(
450                  CaseIgnoreListMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
451             lowerName.equals(CaseIgnoreListMatchingRule.EQUALITY_RULE_OID))
452    {
453      return CaseIgnoreListMatchingRule.getInstance();
454    }
455    else if (lowerName.equals(
456                  CaseIgnoreStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
457             lowerName.equals(CaseIgnoreStringMatchingRule.EQUALITY_RULE_OID) ||
458             lowerName.equals("caseignoreia5match") ||
459             lowerName.equals("1.3.6.1.4.1.1466.109.114.2"))
460    {
461      return CaseIgnoreStringMatchingRule.getInstance();
462    }
463    else if (lowerName.equals(
464                  DistinguishedNameMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
465             lowerName.equals(
466                  DistinguishedNameMatchingRule.EQUALITY_RULE_OID) ||
467             lowerName.equals("uniquemembermatch") ||
468             lowerName.equals("2.5.13.23"))
469    {
470      // NOTE -- Technically uniqueMember should use a name and optional UID
471      // matching rule, but the SDK doesn't currently provide one and the
472      // distinguished name matching rule should be sufficient the vast
473      // majority of the time.
474      return DistinguishedNameMatchingRule.getInstance();
475    }
476    else if (lowerName.equals(
477                  GeneralizedTimeMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
478             lowerName.equals(GeneralizedTimeMatchingRule.EQUALITY_RULE_OID))
479    {
480      return GeneralizedTimeMatchingRule.getInstance();
481    }
482    else if (lowerName.equals(IntegerMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
483             lowerName.equals(IntegerMatchingRule.EQUALITY_RULE_OID))
484    {
485      return IntegerMatchingRule.getInstance();
486    }
487    else if (lowerName.equals(
488                  NumericStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
489             lowerName.equals(NumericStringMatchingRule.EQUALITY_RULE_OID))
490    {
491      return NumericStringMatchingRule.getInstance();
492    }
493    else if (lowerName.equals(
494                  OctetStringMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
495             lowerName.equals(OctetStringMatchingRule.EQUALITY_RULE_OID))
496    {
497      return OctetStringMatchingRule.getInstance();
498    }
499    else if (lowerName.equals(
500                  TelephoneNumberMatchingRule.LOWER_EQUALITY_RULE_NAME) ||
501             lowerName.equals(TelephoneNumberMatchingRule.EQUALITY_RULE_OID))
502    {
503      return TelephoneNumberMatchingRule.getInstance();
504    }
505    else if (lowerName.equals("jsonobjectexactmatch") ||
506             lowerName.equals("1.3.6.1.4.1.30221.2.4.12"))
507    {
508      return JSONObjectExactMatchingRule.getInstance();
509    }
510    else
511    {
512      return getDefaultEqualityMatchingRule();
513    }
514  }
515
516
517
518  /**
519   * Retrieves the default matching rule that will be used for equality matching
520   * if no other matching rule is specified or available.  The rule returned
521   * will perform case-ignore string matching.
522   *
523   * @return  The default matching rule that will be used for equality matching
524   *          if no other matching rule is specified or available.
525   */
526  public static MatchingRule getDefaultEqualityMatchingRule()
527  {
528    return CaseIgnoreStringMatchingRule.getInstance();
529  }
530
531
532
533  /**
534   * Attempts to select the appropriate matching rule to use for ordering
535   * matching against the specified attribute.  If an appropriate matching rule
536   * cannot be determined, then the default ordering matching rule will be
537   * selected.
538   *
539   * @param  attrName  The name of the attribute to examine in the provided
540   *                   schema.
541   * @param  schema    The schema to examine to make the appropriate
542   *                   determination.  If this is {@code null}, then the default
543   *                   ordering matching rule will be selected.
544   *
545   * @return  The selected matching rule.
546   */
547  public static MatchingRule selectOrderingMatchingRule(final String attrName,
548                                                        final Schema schema)
549  {
550    return selectOrderingMatchingRule(attrName, null, schema);
551  }
552
553
554
555  /**
556   * Attempts to select the appropriate matching rule to use for ordering
557   * matching against the specified attribute.  If an appropriate matching rule
558   * cannot be determined, then the default ordering matching rule will be
559   * selected.
560   *
561   * @param  attrName  The name of the attribute to examine in the provided
562   *                   schema.  It may be {@code null} if the matching rule
563   *                   should be selected using the matching rule ID.
564   * @param  ruleID    The OID of the desired matching rule.  It may be
565   *                   {@code null} if the matching rule should be selected only
566   *                   using the attribute name.  If a rule ID is provided, then
567   *                   it will be the only criteria used to select the matching
568   *                   rule.
569   * @param  schema    The schema to examine to make the appropriate
570   *                   determination.  If this is {@code null} and no rule ID
571   *                   was provided, then the default ordering matching rule
572   *                   will be selected.
573   *
574   * @return  The selected matching rule.
575   */
576  public static MatchingRule selectOrderingMatchingRule(final String attrName,
577                                                        final String ruleID,
578                                                        final Schema schema)
579  {
580    if (ruleID != null)
581    {
582      return selectOrderingMatchingRule(ruleID);
583    }
584
585    if ((attrName == null) || (schema == null))
586    {
587      return getDefaultOrderingMatchingRule();
588    }
589
590    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
591    if (attrType == null)
592    {
593      return getDefaultOrderingMatchingRule();
594    }
595
596    final String mrName = attrType.getOrderingMatchingRule(schema);
597    if (mrName != null)
598    {
599      return selectOrderingMatchingRule(mrName);
600    }
601
602    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
603    if (syntaxOID != null)
604    {
605      return selectMatchingRuleForSyntax(syntaxOID);
606    }
607
608    return getDefaultOrderingMatchingRule();
609  }
610
611
612
613  /**
614   * Attempts to select the appropriate matching rule to use for ordering
615   * matching using the specified matching rule.  If an appropriate matching
616   * rule cannot be determined, then the default ordering matching rule will be
617   * selected.
618   *
619   * @param  ruleID  The name or OID of the desired matching rule.
620   *
621   * @return  The selected matching rule.
622   */
623  public static MatchingRule selectOrderingMatchingRule(final String ruleID)
624  {
625    if ((ruleID == null) || (ruleID.length() == 0))
626    {
627      return getDefaultOrderingMatchingRule();
628    }
629
630    final String lowerName = toLowerCase(ruleID);
631    if (lowerName.equals(
632             CaseExactStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
633        lowerName.equals(CaseExactStringMatchingRule.ORDERING_RULE_OID))
634    {
635      return CaseExactStringMatchingRule.getInstance();
636    }
637    else if (lowerName.equals(
638                  CaseIgnoreStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
639             lowerName.equals(CaseIgnoreStringMatchingRule.ORDERING_RULE_OID))
640    {
641      return CaseIgnoreStringMatchingRule.getInstance();
642    }
643    else if (lowerName.equals(
644                  GeneralizedTimeMatchingRule.LOWER_ORDERING_RULE_NAME) ||
645             lowerName.equals(GeneralizedTimeMatchingRule.ORDERING_RULE_OID))
646    {
647      return GeneralizedTimeMatchingRule.getInstance();
648    }
649    else if (lowerName.equals(IntegerMatchingRule.LOWER_ORDERING_RULE_NAME) ||
650             lowerName.equals(IntegerMatchingRule.ORDERING_RULE_OID))
651    {
652      return IntegerMatchingRule.getInstance();
653    }
654    else if (lowerName.equals(
655                  NumericStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
656             lowerName.equals(NumericStringMatchingRule.ORDERING_RULE_OID))
657    {
658      return NumericStringMatchingRule.getInstance();
659    }
660    else if (lowerName.equals(
661                  OctetStringMatchingRule.LOWER_ORDERING_RULE_NAME) ||
662             lowerName.equals(OctetStringMatchingRule.ORDERING_RULE_OID))
663    {
664      return OctetStringMatchingRule.getInstance();
665    }
666    else
667    {
668      return getDefaultOrderingMatchingRule();
669    }
670  }
671
672
673
674  /**
675   * Retrieves the default matching rule that will be used for ordering matching
676   * if no other matching rule is specified or available.  The rule returned
677   * will perform case-ignore string matching.
678   *
679   * @return  The default matching rule that will be used for ordering matching
680   *          if no other matching rule is specified or available.
681   */
682  public static MatchingRule getDefaultOrderingMatchingRule()
683  {
684    return CaseIgnoreStringMatchingRule.getInstance();
685  }
686
687
688
689  /**
690   * Attempts to select the appropriate matching rule to use for substring
691   * matching against the specified attribute.  If an appropriate matching rule
692   * cannot be determined, then the default substring matching rule will be
693   * selected.
694   *
695   * @param  attrName  The name of the attribute to examine in the provided
696   *                   schema.
697   * @param  schema    The schema to examine to make the appropriate
698   *                   determination.  If this is {@code null}, then the default
699   *                   substring matching rule will be selected.
700   *
701   * @return  The selected matching rule.
702   */
703  public static MatchingRule selectSubstringMatchingRule(final String attrName,
704                                                         final Schema schema)
705  {
706    return selectSubstringMatchingRule(attrName, null, schema);
707  }
708
709
710
711  /**
712   * Attempts to select the appropriate matching rule to use for substring
713   * matching against the specified attribute.  If an appropriate matching rule
714   * cannot be determined, then the default substring matching rule will be
715   * selected.
716   *
717   * @param  attrName  The name of the attribute to examine in the provided
718   *                   schema.  It may be {@code null} if the matching rule
719   *                   should be selected using the matching rule ID.
720   * @param  ruleID    The OID of the desired matching rule.  It may be
721   *                   {@code null} if the matching rule should be selected only
722   *                   using the attribute name.  If a rule ID is provided, then
723   *                   it will be the only criteria used to select the matching
724   *                   rule.
725   * @param  schema    The schema to examine to make the appropriate
726   *                   determination.  If this is {@code null} and no rule ID
727   *                   was provided, then the default substring matching rule
728   *                   will be selected.
729   *
730   * @return  The selected matching rule.
731   */
732  public static MatchingRule selectSubstringMatchingRule(final String attrName,
733                                                         final String ruleID,
734                                                         final Schema schema)
735  {
736    if (ruleID != null)
737    {
738      return selectSubstringMatchingRule(ruleID);
739    }
740
741    if ((attrName == null) || (schema == null))
742    {
743      return getDefaultSubstringMatchingRule();
744    }
745
746    final AttributeTypeDefinition attrType = schema.getAttributeType(attrName);
747    if (attrType == null)
748    {
749      return getDefaultSubstringMatchingRule();
750    }
751
752    final String mrName = attrType.getSubstringMatchingRule(schema);
753    if (mrName != null)
754    {
755      return selectSubstringMatchingRule(mrName);
756    }
757
758    final String syntaxOID = attrType.getBaseSyntaxOID(schema);
759    if (syntaxOID != null)
760    {
761      return selectMatchingRuleForSyntax(syntaxOID);
762    }
763
764    return getDefaultSubstringMatchingRule();
765  }
766
767
768
769  /**
770   * Attempts to select the appropriate matching rule to use for substring
771   * matching using the specified matching rule.  If an appropriate matching
772   * rule cannot be determined, then the default substring matching rule will be
773   * selected.
774   *
775   * @param  ruleID  The name or OID of the desired matching rule.
776   *
777   * @return  The selected matching rule.
778   */
779  public static MatchingRule selectSubstringMatchingRule(final String ruleID)
780  {
781    if ((ruleID == null) || (ruleID.length() == 0))
782    {
783      return getDefaultSubstringMatchingRule();
784    }
785
786    final String lowerName = toLowerCase(ruleID);
787    if (lowerName.equals(
788             CaseExactStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
789        lowerName.equals(CaseExactStringMatchingRule.SUBSTRING_RULE_OID) ||
790        lowerName.equals("caseexactia5substringsmatch"))
791    {
792      return CaseExactStringMatchingRule.getInstance();
793    }
794    else if (lowerName.equals(
795                  CaseIgnoreListMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
796             lowerName.equals(CaseIgnoreListMatchingRule.SUBSTRING_RULE_OID))
797    {
798      return CaseIgnoreListMatchingRule.getInstance();
799    }
800    else if (lowerName.equals(
801                  CaseIgnoreStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
802             lowerName.equals(
803                  CaseIgnoreStringMatchingRule.SUBSTRING_RULE_OID) ||
804             lowerName.equals("caseignoreia5substringsmatch") ||
805             lowerName.equals("1.3.6.1.4.1.1466.109.114.3"))
806    {
807      return CaseIgnoreStringMatchingRule.getInstance();
808    }
809    else if (lowerName.equals(
810                  NumericStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
811             lowerName.equals(NumericStringMatchingRule.SUBSTRING_RULE_OID))
812    {
813      return NumericStringMatchingRule.getInstance();
814    }
815    else if (lowerName.equals(
816                  OctetStringMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
817             lowerName.equals(OctetStringMatchingRule.SUBSTRING_RULE_OID))
818    {
819      return OctetStringMatchingRule.getInstance();
820    }
821    else if (lowerName.equals(
822                  TelephoneNumberMatchingRule.LOWER_SUBSTRING_RULE_NAME) ||
823             lowerName.equals(TelephoneNumberMatchingRule.SUBSTRING_RULE_OID))
824    {
825      return TelephoneNumberMatchingRule.getInstance();
826    }
827    else
828    {
829      return getDefaultSubstringMatchingRule();
830    }
831  }
832
833
834
835  /**
836   * Retrieves the default matching rule that will be used for substring
837   * matching if no other matching rule is specified or available.  The rule
838   * returned will perform case-ignore string matching.
839   *
840   * @return  The default matching rule that will be used for substring matching
841   *          if no other matching rule is specified or available.
842   */
843  public static MatchingRule getDefaultSubstringMatchingRule()
844  {
845    return CaseIgnoreStringMatchingRule.getInstance();
846  }
847
848
849
850  /**
851   * Attempts to select the appropriate matching rule for use with the syntax
852   * with the specified OID.  If an appropriate matching rule cannot be
853   * determined, then the case-ignore string matching rule will be selected.
854   *
855   * @param  syntaxOID  The OID of the attribute syntax for which to make the
856   *                    determination.
857   *
858   * @return  The selected matching rule.
859   */
860  public static MatchingRule selectMatchingRuleForSyntax(final String syntaxOID)
861  {
862    if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.7"))
863    {
864      return BooleanMatchingRule.getInstance();
865    }
866    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.41")) // Postal addr.
867    {
868      return CaseIgnoreListMatchingRule.getInstance();
869    }
870    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.12") ||
871         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.34")) // name&optional UID
872    {
873      return DistinguishedNameMatchingRule.getInstance();
874    }
875    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.24") ||
876         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.53")) // UTC time
877    {
878      return GeneralizedTimeMatchingRule.getInstance();
879    }
880    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.27"))
881    {
882      return IntegerMatchingRule.getInstance();
883    }
884    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.36"))
885    {
886      return NumericStringMatchingRule.getInstance();
887    }
888    else if (syntaxOID.equals("1.3.6.1.4.1.4203.1.1.2") || // auth password
889         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.5") || // binary
890         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.8") || // certificate
891         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.9") || // cert list
892         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.10") || // cert pair
893         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.28") || // JPEG
894         syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.40")) // octet string
895    {
896      return OctetStringMatchingRule.getInstance();
897    }
898    else if (syntaxOID.equals("1.3.6.1.4.1.1466.115.121.1.50"))
899    {
900      return TelephoneNumberMatchingRule.getInstance();
901    }
902    else if (syntaxOID.equals("1.3.6.1.4.1.30221.2.3.4")) // JSON object exact
903    {
904      return JSONObjectExactMatchingRule.getInstance();
905    }
906    else
907    {
908      return CaseIgnoreStringMatchingRule.getInstance();
909    }
910  }
911}