View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    https://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.schema.syntaxCheckers;
21  
22  
23  import java.util.HashSet;
24  import java.util.Set;
25  import java.util.regex.Pattern;
26  import java.util.regex.PatternSyntaxException;
27  
28  import org.apache.directory.api.i18n.I18n;
29  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
30  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
31  import org.apache.directory.api.util.Strings;
32  
33  
34  /**
35   * A SyntaxChecker which verifies that a value is a facsimile TelephoneNumber according 
36   * to ITU recommendation E.123 for the Telephone number part, and from RFC 4517, par. 
37   * 3.3.11 :
38   * 
39   * <pre>
40   * fax-number       = telephone-number *( DOLLAR fax-parameter )
41   * telephone-number = PrintableString
42   * fax-parameter    = "twoDimensional" |
43   *                    "fineResolution" |
44   *                    "unlimitedLength" |
45   *                    "b4Length" |
46   *                    "a3Width" |
47   *                    "b4Width" |
48   *                    "uncompressed"
49   * </pre>
50   * 
51   * If needed, and to allow more syntaxes, a list of regexps has been added
52   * which can be initialized to other values
53   * 
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   */
56  @SuppressWarnings("serial")
57  public final class FacsimileTelephoneNumberSyntaxChecker extends SyntaxChecker
58  {
59      /** The default pattern used to check a TelephoneNumber */
60      private static final String DEFAULT_REGEXP = "^ *[+]? *((\\([0-9- ,;/#*]+\\))|[0-9- ,;/#*]+)+$";
61      
62      /** The compiled default pattern */
63      private String defaultRegexp;
64  
65      /** The compiled default pattern */
66      private Pattern defaultPattern = Pattern.compile( DEFAULT_REGEXP );
67  
68      /** Fax parameters possible values */
69      private static final String TWO_DIMENSIONAL = "twoDimensional";
70      private static final String FINE_RESOLUTION = "fineResolution";
71      private static final String UNLIMITED_LENGTH = "unlimitedLength";
72      private static final String B4_LENGTH = "b4Length";
73      private static final String A3_LENGTH = "a3Width";
74      private static final String B4_WIDTH = "b4Width";
75      private static final String UNCOMPRESSED = "uncompressed";
76  
77      /** A set which contains all the possible fax parameters values */
78      private static Set<String> faxParameters = new HashSet<>();
79  
80      /** Initialization of the fax parameters set of values */
81      static
82      {
83          faxParameters.add( Strings.toLowerCaseAscii( TWO_DIMENSIONAL ) );
84          faxParameters.add( Strings.toLowerCaseAscii( FINE_RESOLUTION ) );
85          faxParameters.add( Strings.toLowerCaseAscii( UNLIMITED_LENGTH ) );
86          faxParameters.add( Strings.toLowerCaseAscii( B4_LENGTH ) );
87          faxParameters.add( Strings.toLowerCaseAscii( A3_LENGTH ) );
88          faxParameters.add( Strings.toLowerCaseAscii( B4_WIDTH ) );
89          faxParameters.add( Strings.toLowerCaseAscii( UNCOMPRESSED ) );
90      }
91      
92      /**
93       * A static instance of FacsimileTelephoneNumberSyntaxChecker
94       */
95      public static final FacsimileTelephoneNumberSyntaxChecker INSTANCE = 
96          new FacsimileTelephoneNumberSyntaxChecker( SchemaConstants.FACSIMILE_TELEPHONE_NUMBER_SYNTAX );
97      
98      /**
99       * A static Builder for this class
100      */
101     public static final class Builder extends SCBuilder<FacsimileTelephoneNumberSyntaxChecker>
102     {
103         /** The compiled default pattern */
104         private String defaultRegexp;
105 
106         /** The compiled default pattern */
107         private Pattern defaultPattern;
108 
109         /**
110          * The Builder constructor
111          */
112         private Builder()
113         {
114             super( SchemaConstants.FACSIMILE_TELEPHONE_NUMBER_SYNTAX );
115             setDefaultRegexp( DEFAULT_REGEXP );
116         }
117         
118         
119         /**
120          * Create a new instance of FacsimileTelephoneNumberSyntaxChecker
121          * 
122          * @return A new instance of FacsimileTelephoneNumberSyntaxChecker
123          */
124         @Override
125         public FacsimileTelephoneNumberSyntaxChecker build()
126         {
127             return new FacsimileTelephoneNumberSyntaxChecker( oid, defaultRegexp, defaultPattern );
128         }
129 
130 
131         /**
132          * Set the default regular expression for the Telephone number
133          * 
134          * @param regexp the default regular expression.
135          * @return The FacsimileTelephneNumber Builder instance
136          */
137         public Builder setDefaultRegexp( String regexp )
138         {
139             defaultRegexp = regexp;
140             
141             try
142             {
143                 defaultPattern = Pattern.compile( regexp );
144             }
145             catch ( PatternSyntaxException pse )
146             {
147                 // Roll back to the default pattern
148                 defaultPattern = Pattern.compile( DEFAULT_REGEXP );
149             }
150 
151             return this;
152         }
153     }
154 
155     
156     /**
157      * Creates a new instance of TelephoneNumberSyntaxChecker.
158      * 
159      * @param oid the OID
160      */
161     private FacsimileTelephoneNumberSyntaxChecker( String oid )
162     {
163         super( oid );
164     }
165 
166     
167     /**
168      * Creates a new instance of TelephoneNumberSyntaxChecker.
169      * 
170      * @param oid The OID
171      * @param defaultRegexp the default regexp to use
172      * @param defaultPattern The default pattern to use
173      */
174     private FacsimileTelephoneNumberSyntaxChecker( String oid, String defaultRegexp, Pattern defaultPattern )
175     {
176         super( oid );
177         
178         this.defaultPattern = defaultPattern;
179         this.defaultRegexp = defaultRegexp;
180     }
181 
182     
183     /**
184      * @return An instance of the Builder for this class
185      */
186     public static Builder builder()
187     {
188         return new Builder();
189     }
190 
191 
192     /**
193      * Get the default regexp (either the original one, or the one that has been set)
194      * 
195      * @return The default regexp
196      */
197     public String getRegexp()
198     {
199         if ( defaultRegexp == null )
200         {
201             return DEFAULT_REGEXP;
202         }
203         else
204         {
205             return defaultRegexp;
206         }
207     }
208 
209 
210     /**
211      * {@inheritDoc}
212      */
213     @Override
214     public boolean isValidSyntax( Object value )
215     {
216         String strValue;
217 
218         if ( value == null )
219         {
220             if ( LOG.isDebugEnabled() )
221             {
222                 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) );
223             }
224             
225             return false;
226         }
227 
228         if ( value instanceof String )
229         {
230             strValue = ( String ) value;
231         }
232         else if ( value instanceof byte[] )
233         {
234             strValue = Strings.utf8ToString( ( byte[] ) value );
235         }
236         else
237         {
238             strValue = value.toString();
239         }
240 
241         if ( strValue.length() == 0 )
242         {
243             if ( LOG.isDebugEnabled() )
244             {
245                 LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
246             }
247             
248             return false;
249         }
250 
251         // The facsimile telephone number might be composed
252         // of two parts separated by a '$'.
253         int dollarPos = strValue.indexOf( '$' );
254 
255         if ( dollarPos == -1 )
256         {
257             // We have no fax-parameter : check the Telephone number
258             boolean result = defaultPattern.matcher( strValue ).matches();
259 
260             if ( LOG.isDebugEnabled() )
261             {
262                 if ( result )
263                 {
264                     LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) );
265                 }
266                 else
267                 {
268                     LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
269                 }
270             }
271 
272             return result;
273         }
274 
275         // First check the telephone number if the '$' is not at the first position
276         if ( dollarPos > 0 )
277         {
278             boolean result = defaultPattern.matcher( strValue.substring( 0, dollarPos - 1 ) ).matches();
279 
280             if ( LOG.isDebugEnabled() )
281             {
282                 if ( result )
283                 {
284                     LOG.debug( I18n.err( I18n.MSG_13701_SYNTAX_VALID, value ) );
285                 }
286                 else
287                 { 
288                     LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
289                     
290                     return false;
291                 }
292             }
293 
294             // Now, try to validate the fax-parameters : we may
295             // have more than one, so we will store the seen params
296             // in a set to check that we don't have the same param twice
297             Set<String> paramsSeen = new HashSet<>();
298 
299             while ( dollarPos > 0 )
300             {
301                 String faxParam;
302                 int newDollar = strValue.indexOf( '$', dollarPos + 1 );
303 
304                 if ( newDollar == -1 )
305                 {
306                     faxParam = strValue.substring( dollarPos + 1 );
307                 }
308                 else
309                 {
310                     faxParam = strValue.substring( dollarPos + 1, newDollar );
311                 }
312 
313                 if ( faxParam.length() == 0 )
314                 {
315                     // Not allowed
316                     if ( LOG.isDebugEnabled() )
317                     {
318                         LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
319                     }
320                     
321                     return false;
322                 }
323 
324                 // Relax a little bit the syntax by lowercasing the param
325                 faxParam = Strings.toLowerCaseAscii( faxParam );
326 
327                 if ( !faxParameters.contains( faxParam ) || paramsSeen.contains( faxParam ) )
328                 {
329                     // This parameter is not in the possible set
330                     if ( LOG.isDebugEnabled() )
331                     {
332                         LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
333                     }
334                     
335                     return false;
336                 }
337                 else
338                 {
339                     // It's a correct param, let's add it to the seen 
340                     // params.
341                     paramsSeen.add( faxParam );
342                 }
343 
344                 dollarPos = newDollar;
345             }
346 
347             if ( LOG.isDebugEnabled() )
348             {
349                 LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) );
350             }
351             
352             return true;
353         }
354 
355         // We must have a valid telephone number !
356         if ( LOG.isDebugEnabled() )
357         {
358             LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
359         }
360         
361         return false;
362     }
363 }