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