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   *    http://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 default pattern */
63      private final String defaultRegexp;
64  
65      /** The compiled default pattern */
66      private Pattern defaultPattern;
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          * @return A new instance of FacsimileTelephoneNumberSyntaxChecker
122          */
123         @Override
124         public FacsimileTelephoneNumberSyntaxChecker build()
125         {
126             return new FacsimileTelephoneNumberSyntaxChecker( oid, defaultRegexp, defaultPattern );
127         }
128 
129 
130         /**
131          * Set the default regular expression for the Telephone number
132          * 
133          * @param regexp the default regular expression.
134          */
135         public Builder setDefaultRegexp( String regexp )
136         {
137             defaultRegexp = regexp;
138             
139             try
140             {
141                 defaultPattern = Pattern.compile( regexp );
142             }
143             catch ( PatternSyntaxException pse )
144             {
145                 // Roll back to the default pattern
146                 defaultPattern = Pattern.compile( DEFAULT_REGEXP );
147             }
148 
149             return this;
150         }
151     }
152 
153 
154     /**
155      * Creates a new instance of TelephoneNumberSyntaxChecker.
156      */
157     private FacsimileTelephoneNumberSyntaxChecker( String oid )
158     {
159         this( oid, DEFAULT_REGEXP, Pattern.compile( DEFAULT_REGEXP ) );
160     }
161 
162 
163     /**
164      * Creates a new instance of TelephoneNumberSyntaxChecker.
165      */
166     private FacsimileTelephoneNumberSyntaxChecker( String oid, String defaultRegexp, Pattern defaultPattern )
167     {
168         super( oid );
169 
170         this.defaultPattern = defaultPattern;
171         this.defaultRegexp = defaultRegexp;
172     }
173 
174 
175     /**
176      * @return An instance of the Builder for this class
177      */
178     public static Builder builder()
179     {
180         return new Builder();
181     }
182 
183 
184     /**
185      * Get the default regexp (either the original one, or the one that has been set)
186      * 
187      * @return The default regexp
188      */
189     public String getRegexp()
190     {
191         if ( defaultRegexp == null )
192         {
193             return DEFAULT_REGEXP;
194         }
195         else
196         {
197             return defaultRegexp;
198         }
199     }
200 
201 
202     /**
203      * {@inheritDoc}
204      */
205     @Override
206     public boolean isValidSyntax( Object value )
207     {
208         String strValue;
209 
210         if ( value == null )
211         {
212             if ( LOG.isDebugEnabled() )
213             {
214                 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, "null" ) );
215             }
216             
217             return false;
218         }
219 
220         if ( value instanceof String )
221         {
222             strValue = ( String ) value;
223         }
224         else if ( value instanceof byte[] )
225         {
226             strValue = Strings.utf8ToString( ( byte[] ) value );
227         }
228         else
229         {
230             strValue = value.toString();
231         }
232 
233         if ( strValue.length() == 0 )
234         {
235             if ( LOG.isDebugEnabled() )
236             {
237                 LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
238             }
239             
240             return false;
241         }
242 
243         // The facsimile telephone number might be composed
244         // of two parts separated by a '$'.
245         int dollarPos = strValue.indexOf( '$' );
246 
247         if ( dollarPos == -1 )
248         {
249             // We have no fax-parameter : check the Telephone number
250             boolean result = defaultPattern.matcher( strValue ).matches();
251 
252             if ( LOG.isDebugEnabled() )
253             {
254                 if ( result )
255                 {
256                     LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
257                 }
258                 else
259                 {
260                     LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
261                 }
262             }
263 
264             return result;
265         }
266 
267         // First check the telephone number if the '$' is not at the first position
268         if ( dollarPos > 0 )
269         {
270             boolean result = defaultPattern.matcher( strValue.substring( 0, dollarPos - 1 ) ).matches();
271 
272             if ( LOG.isDebugEnabled() )
273             {
274                 if ( result )
275                 {
276                     LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
277                 }
278                 else
279                 {
280                     LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
281                     
282                     return false;
283                 }
284             }
285 
286             // Now, try to validate the fax-parameters : we may
287             // have more than one, so we will store the seen params
288             // in a set to check that we don't have the same param twice
289             Set<String> paramsSeen = new HashSet<>();
290 
291             while ( dollarPos > 0 )
292             {
293                 String faxParam;
294                 int newDollar = strValue.indexOf( '$', dollarPos + 1 );
295 
296                 if ( newDollar == -1 )
297                 {
298                     faxParam = strValue.substring( dollarPos + 1 );
299                 }
300                 else
301                 {
302                     faxParam = strValue.substring( dollarPos + 1, newDollar );
303                 }
304 
305                 if ( faxParam.length() == 0 )
306                 {
307                     // Not allowed
308                     if ( LOG.isDebugEnabled() )
309                     {
310                         LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
311                     }
312                     
313                     return false;
314                 }
315 
316                 // Relax a little bit the syntax by lowercasing the param
317                 faxParam = Strings.toLowerCaseAscii( faxParam );
318 
319                 if ( !faxParameters.contains( faxParam ) || paramsSeen.contains( faxParam ) )
320                 {
321                     // This parameter is not in the possible set
322                     if ( LOG.isDebugEnabled() )
323                     {
324                         LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
325                     }
326                     
327                     return false;
328                 }
329                 else
330                 {
331                     // It's a correct param, let's add it to the seen 
332                     // params.
333                     paramsSeen.add( faxParam );
334                 }
335 
336                 dollarPos = newDollar;
337             }
338 
339             if ( LOG.isDebugEnabled() )
340             {
341                 LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
342             }
343             
344             return true;
345         }
346 
347         // We must have a valid telephone number !
348         if ( LOG.isDebugEnabled() )
349         {
350             LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
351         }
352         
353         return false;
354     }
355 }