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