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