001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2024 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-2024 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 com.unboundid.asn1.ASN1OctetString;
041import com.unboundid.ldap.sdk.LDAPException;
042import com.unboundid.ldap.sdk.ResultCode;
043import com.unboundid.util.NotNull;
044import com.unboundid.util.Nullable;
045import com.unboundid.util.ObjectPair;
046import com.unboundid.util.PropertyManager;
047import com.unboundid.util.StaticUtils;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.Validator;
051
052import static com.unboundid.ldap.matchingrules.MatchingRuleMessages.*;
053
054
055
056/**
057 * This class provides an implementation of a matching rule that may be used for
058 * telephone numbers.  It will accept values with any ASCII printable character.
059 * When making comparisons, spaces and dashes will be ignored.
060 */
061@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
062public final class TelephoneNumberMatchingRule
063       extends SimpleMatchingRule
064{
065  /**
066   * The name of the system property that may be used to specify the default
067   * comparison policy.  If this is not specified, and if the default value is
068   * not overridden by the {@link #setDefaultComparisonPolicy} method, then a
069   * default policy of
070   * {@link TelephoneNumberComparisonPolicy#IGNORE_ALL_NON_NUMERIC_CHARACTERS}
071   * will be used.
072   */
073  @NotNull public static final String DEFAULT_COMPARISON_POLICY_PROPERTY =
074       TelephoneNumberMatchingRule.class.getName()  +
075            ".defaultComparisonPolicy";
076
077
078
079  /**
080   * The name of the system property that may be used to specify the default
081   * validation policy.  If this is not specified, and if the default value is
082   * not overridden by the {@link #setDefaultValidationPolicy} method, then a
083   * default policy of
084   * {@link TelephoneNumberValidationPolicy#ALLOW_NON_EMPTY_PRINTABLE_STRING}
085   * will be used.
086   */
087  @NotNull public static final String DEFAULT_VALIDATION_POLICY_PROPERTY =
088       TelephoneNumberMatchingRule.class.getName()  +
089            ".defaultValidationPolicy";
090
091
092
093  /**
094   * The default comparison policy that will be used if none is specified.
095   */
096  @NotNull private static TelephoneNumberComparisonPolicy
097       DEFAULT_COMPARISON_POLICY;
098
099
100
101  /**
102   * The default validation policy that will be used if none is specified.
103   */
104  @NotNull private static TelephoneNumberValidationPolicy
105       DEFAULT_VALIDATION_POLICY;
106
107
108
109  /**
110   * The instance that will be returned from the {@code getInstance} method.
111   */
112  @NotNull private static TelephoneNumberMatchingRule INSTANCE;
113
114
115
116  static
117  {
118    final ObjectPair<TelephoneNumberValidationPolicy,
119                     TelephoneNumberComparisonPolicy> defaultPolicyPair =
120         computeDefaultPolicies();
121
122    DEFAULT_VALIDATION_POLICY = defaultPolicyPair.getFirst();
123    DEFAULT_COMPARISON_POLICY = defaultPolicyPair.getSecond();
124    INSTANCE = new TelephoneNumberMatchingRule(DEFAULT_VALIDATION_POLICY,
125         DEFAULT_COMPARISON_POLICY);
126  }
127
128
129
130  /**
131   * The name for the telephoneNumberMatch equality matching rule.
132   */
133  @NotNull public static final String EQUALITY_RULE_NAME =
134       "telephoneNumberMatch";
135
136
137
138  /**
139   * The name for the telephoneNumberMatch equality matching rule, formatted in
140   * all lowercase characters.
141   */
142  @NotNull static final String LOWER_EQUALITY_RULE_NAME =
143       StaticUtils.toLowerCase(EQUALITY_RULE_NAME);
144
145
146
147  /**
148   * The OID for the telephoneNumberMatch equality matching rule.
149   */
150  @NotNull public static final String EQUALITY_RULE_OID = "2.5.13.20";
151
152
153
154  /**
155   * The name for the telephoneNumberSubstringsMatch substring matching rule.
156   */
157  @NotNull public static final String SUBSTRING_RULE_NAME =
158       "telephoneNumberSubstringsMatch";
159
160
161
162  /**
163   * The name for the telephoneNumberSubstringsMatch substring matching rule,
164   * formatted in all lowercase characters.
165   */
166  @NotNull static final String LOWER_SUBSTRING_RULE_NAME =
167       StaticUtils.toLowerCase(SUBSTRING_RULE_NAME);
168
169
170
171  /**
172   * The OID for the telephoneNumberSubstringsMatch substring matching rule.
173   */
174  @NotNull public static final String SUBSTRING_RULE_OID = "2.5.13.21";
175
176
177
178  /**
179   * The serial version UID for this serializable class.
180   */
181  private static final long serialVersionUID = -5463096544849211252L;
182
183
184
185  // The policy to use when comparing telephone number values.
186  @NotNull private final TelephoneNumberComparisonPolicy comparisonPolicy;
187
188  // The policy to use when validating telephone number values.
189  @NotNull private final TelephoneNumberValidationPolicy validationPolicy;
190
191
192
193  /**
194   * Creates a new instance of this telephone number matching rule with the
195   * default validation and comparison policies.
196   */
197  public TelephoneNumberMatchingRule()
198  {
199    this(DEFAULT_VALIDATION_POLICY, DEFAULT_COMPARISON_POLICY);
200  }
201
202
203
204  /**
205   * Creates a new instance of this telephone number matching rule with the
206   * specified validation and comparison policies.
207   *
208   * @param  validationPolicy  The policy to use when validating telephone
209   *                           number values.  It must not be
210   *                           {@code null}.
211   * @param  comparisonPolicy  The policy to use when comparing telephone number
212   *                           values.  It must not be {@code null}.
213   */
214  public TelephoneNumberMatchingRule(
215              @NotNull final TelephoneNumberValidationPolicy validationPolicy,
216              @NotNull final TelephoneNumberComparisonPolicy comparisonPolicy)
217  {
218    Validator.ensureNotNullWithMessage(validationPolicy,
219         "TelephoneNumberMatchingRule.validationPolicy must not be null.");
220    Validator.ensureNotNullWithMessage(comparisonPolicy,
221         "TelephoneNumberMatchingRule.comparisonPolicy must not be null.");
222
223    this.validationPolicy = validationPolicy;
224    this.comparisonPolicy = comparisonPolicy;
225  }
226
227
228
229  /**
230   * Retrieves a singleton instance of this matching rule.
231   *
232   * @return  A singleton instance of this matching rule.
233   */
234  @NotNull()
235  public static TelephoneNumberMatchingRule getInstance()
236  {
237    return INSTANCE;
238  }
239
240
241
242  /**
243   * Retrieves the policy that will be used for validating telephone number
244   * values.
245   *
246   * @return  The policy that will be used for validating telephone number
247   *          values.
248   */
249  @NotNull()
250  public TelephoneNumberValidationPolicy getValidationPolicy()
251  {
252    return validationPolicy;
253  }
254
255
256
257  /**
258   * Retrieves the policy that will be used for validating telephone number
259   * values when creating an instance of this matching rule using the default
260   * constructor.
261   *
262   * @return  The policy that will be used for validating telephone number
263   *          values when creating an instance of this matching rule using the
264   *          default constructor.
265   */
266  @NotNull()
267  public static TelephoneNumberValidationPolicy getDefaultValidationPolicy()
268  {
269    return DEFAULT_VALIDATION_POLICY;
270  }
271
272
273
274  /**
275   * Specifies the policy that will be used for validating telephone number
276   * values when creating an instance of this matching rule using the default
277   * constructor.
278   *
279   * @param  defaultValidationPolicy  The policy that will be used for
280   *                                  validating telephone number values when
281   *                                  creating an instance of this matching rule
282   *                                  using the default constructor.
283   */
284  public static synchronized void setDefaultValidationPolicy(
285       @NotNull final TelephoneNumberValidationPolicy defaultValidationPolicy)
286  {
287    Validator.ensureNotNullWithMessage(defaultValidationPolicy,
288         "TelephoneNumberMatchingRule.defaultValidationPolicy must not be " +
289              "null.");
290
291    DEFAULT_VALIDATION_POLICY = defaultValidationPolicy;
292    INSTANCE = new TelephoneNumberMatchingRule(DEFAULT_VALIDATION_POLICY,
293         DEFAULT_COMPARISON_POLICY);
294  }
295
296
297
298  /**
299   * Retrieves the policy that will be used for comparing telephone number
300   * values.
301   *
302   * @return  The policy that will be used for comparing telephone number
303   *          values.
304   */
305  @NotNull()
306  public TelephoneNumberComparisonPolicy getComparisonPolicy()
307  {
308    return comparisonPolicy;
309  }
310
311
312
313  /**
314   * Retrieves the policy that will be used for comparing telephone number
315   * values when creating an instance of this matching rule using the default
316   * constructor.
317   *
318   * @return  The policy that will be used for comparing telephone number
319   *          values when creating an instance of this matching rule using the
320   *          default constructor.
321   */
322  @NotNull()
323  public static TelephoneNumberComparisonPolicy getDefaultComparisonPolicy()
324  {
325    return DEFAULT_COMPARISON_POLICY;
326  }
327
328
329
330  /**
331   * Specifies the policy that will be used for comparing telephone number
332   * values when creating an instance of this matching rule using the default
333   * constructor.
334   *
335   * @param  defaultComparisonPolicy  The policy that will be used for
336   *                                  comparing telephone number values when
337   *                                  creating an instance of this matching rule
338   *                                  using the default constructor.
339   */
340  public static synchronized void setDefaultComparisonPolicy(
341       @NotNull final TelephoneNumberComparisonPolicy defaultComparisonPolicy)
342  {
343    Validator.ensureNotNullWithMessage(defaultComparisonPolicy,
344         "TelephoneNumberMatchingRule.defaultComparisonPolicy must not be " +
345              "null.");
346
347    DEFAULT_COMPARISON_POLICY = defaultComparisonPolicy;
348    INSTANCE = new TelephoneNumberMatchingRule(DEFAULT_VALIDATION_POLICY,
349         DEFAULT_COMPARISON_POLICY);
350  }
351
352
353
354  /**
355   * {@inheritDoc}
356   */
357  @Override()
358  @NotNull()
359  public String getEqualityMatchingRuleName()
360  {
361    return EQUALITY_RULE_NAME;
362  }
363
364
365
366  /**
367   * {@inheritDoc}
368   */
369  @Override()
370  @NotNull()
371  public String getEqualityMatchingRuleOID()
372  {
373    return EQUALITY_RULE_OID;
374  }
375
376
377
378  /**
379   * {@inheritDoc}
380   */
381  @Override()
382  @Nullable()
383  public String getOrderingMatchingRuleName()
384  {
385    return null;
386  }
387
388
389
390  /**
391   * {@inheritDoc}
392   */
393  @Override()
394  @Nullable()
395  public String getOrderingMatchingRuleOID()
396  {
397    return null;
398  }
399
400
401
402  /**
403   * {@inheritDoc}
404   */
405  @Override()
406  @NotNull()
407  public String getSubstringMatchingRuleName()
408  {
409    return SUBSTRING_RULE_NAME;
410  }
411
412
413
414  /**
415   * {@inheritDoc}
416   */
417  @Override()
418  @NotNull()
419  public String getSubstringMatchingRuleOID()
420  {
421    return SUBSTRING_RULE_OID;
422  }
423
424
425
426  /**
427   * {@inheritDoc}
428   */
429  @Override()
430  public int compareValues(@NotNull final ASN1OctetString value1,
431                           @NotNull final ASN1OctetString value2)
432         throws LDAPException
433  {
434    throw new LDAPException(ResultCode.INAPPROPRIATE_MATCHING,
435         ERR_TELEPHONE_NUMBER_ORDERING_MATCHING_NOT_SUPPORTED.get());
436  }
437
438
439
440  /**
441   * {@inheritDoc}
442   */
443  @Override()
444  @NotNull()
445  public ASN1OctetString normalize(@NotNull final ASN1OctetString value)
446         throws LDAPException
447  {
448    validationPolicy.validateValue(value, false);
449    return comparisonPolicy.normalizeValue(value);
450  }
451
452
453
454  /**
455   * {@inheritDoc}
456   */
457  @Override()
458  @NotNull()
459  public ASN1OctetString normalizeSubstring(
460                              @NotNull final ASN1OctetString value,
461                              final byte substringType)
462         throws LDAPException
463  {
464    validationPolicy.validateValue(value, true);
465    return comparisonPolicy.normalizeValue(value);
466  }
467
468
469
470  /**
471   * Computes the default validation and comparison policies that should be used
472   * for this class.
473   *
474   * @return  An object pair in which the first element is the selected default
475   *          validation policy and the second element is the selected default
476   *          comparison policy.
477   */
478  @NotNull()
479  static ObjectPair<TelephoneNumberValidationPolicy,
480                   TelephoneNumberComparisonPolicy> computeDefaultPolicies()
481  {
482    // Determine the appropriate default validation policy.
483    TelephoneNumberValidationPolicy validationPolicy = null;
484    final String validationPropertyValue =
485         PropertyManager.get(DEFAULT_VALIDATION_POLICY_PROPERTY);
486    if (validationPropertyValue != null)
487    {
488      final String normalizedPropertyValue =
489           validationPropertyValue.toUpperCase().replace('-', '_');
490      for (final TelephoneNumberValidationPolicy v :
491           TelephoneNumberValidationPolicy.values())
492      {
493        if (v.name().equals(normalizedPropertyValue))
494        {
495          validationPolicy = v;
496          break;
497        }
498      }
499    }
500
501    if (validationPolicy == null)
502    {
503      validationPolicy =
504           TelephoneNumberValidationPolicy.ALLOW_NON_EMPTY_PRINTABLE_STRING;
505    }
506
507
508    // Determine the appropriate default comparison policy.
509    TelephoneNumberComparisonPolicy comparisonPolicy = null;
510    final String comparisonPropertyValue =
511         PropertyManager.get(DEFAULT_COMPARISON_POLICY_PROPERTY);
512    if (comparisonPropertyValue != null)
513    {
514      final String normalizedPropertyValue =
515           comparisonPropertyValue.toUpperCase().replace('-', '_');
516      for (final TelephoneNumberComparisonPolicy v :
517           TelephoneNumberComparisonPolicy.values())
518      {
519        if (v.name().equals(normalizedPropertyValue))
520        {
521          comparisonPolicy = v;
522          break;
523        }
524      }
525    }
526
527    if (comparisonPolicy == null)
528    {
529      comparisonPolicy =
530           TelephoneNumberComparisonPolicy.IGNORE_ALL_NON_NUMERIC_CHARACTERS;
531    }
532
533
534    return new ObjectPair<>(validationPolicy, comparisonPolicy);
535  }
536}