001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    https://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.ldap.model.schema.syntaxCheckers;
021
022
023import java.util.regex.Pattern;
024import java.util.regex.PatternSyntaxException;
025
026import org.apache.directory.api.i18n.I18n;
027import org.apache.directory.api.ldap.model.constants.SchemaConstants;
028import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
029import org.apache.directory.api.util.Strings;
030
031
032/**
033 * A SyntaxChecker which verifies that a value is a TelephoneNumber according to ITU
034 * recommendation E.123 (which is quite vague ...).
035 * <p>
036 * A valid Telephone number respects more or less this syntax :
037 * 
038 * <pre>
039 * " *[+]? *((\([0-9- ,;/#*]+\))|[0-9- ,;/#*]+)+"
040 * </pre>
041 * 
042 * If needed, and to allow more syntaxes, a list of regexps has been added
043 * which can be initialized to other values
044 * 
045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
046 */
047@SuppressWarnings("serial")
048public final class TelephoneNumberSyntaxChecker extends SyntaxChecker
049{
050    /** The default pattern used to check a TelephoneNumber */
051    private static final String DEFAULT_REGEXP = "^ *[+]? *((\\([0-9- ,;/#*]+\\))|[0-9- ,;/#*]+)+$";
052    
053    /** The default pattern */
054    private final String defaultRegexp;
055
056    /** The compiled default pattern */
057    private final Pattern defaultPattern;
058
059    /**
060     * A static instance of TelephoneNumberSyntaxChecker
061     */
062    public static final TelephoneNumberSyntaxChecker INSTANCE = 
063        new TelephoneNumberSyntaxChecker( SchemaConstants.TELEPHONE_NUMBER_SYNTAX );
064    
065    /**
066     * A static Builder for this class
067     */
068    public static final class Builder extends SCBuilder<TelephoneNumberSyntaxChecker>
069    {
070        /** The compiled default pattern */
071        private String defaultRegexp;
072
073        /** The default pattern */
074        private Pattern defaultPattern;
075
076        /**
077         * The Builder constructor
078         */
079        private Builder()
080        {
081            super( SchemaConstants.TELEPHONE_NUMBER_SYNTAX );
082            setDefaultRegexp( DEFAULT_REGEXP );
083        }
084        
085        
086        /**
087         * Create a new instance of TelephoneNumberSyntaxChecker
088         * @return A new instance of TelephoneNumberSyntaxChecker
089         */
090        @Override
091        public TelephoneNumberSyntaxChecker build()
092        {
093            return new TelephoneNumberSyntaxChecker( oid, defaultRegexp, defaultPattern );
094        }
095
096
097        /**
098         * Set the default regular expression for the Telephone number
099         * 
100         * @param regexp the default regular expression.
101         * @return the TelephonenumberSyntaxChecker Builder instance
102         */
103        public Builder setDefaultRegexp( String regexp )
104        {
105            defaultRegexp = regexp;
106            
107            try
108            {
109                defaultPattern = Pattern.compile( regexp );
110            }
111            catch ( PatternSyntaxException pse )
112            {
113                // Roll back to the default pattern
114                defaultPattern = Pattern.compile( DEFAULT_REGEXP );
115            }
116
117            return this;
118        }
119    }
120
121    
122    /**
123     * Creates a new instance of a child of this class, with an OID.
124     * 
125     * @param oid the child's OID
126     */
127    private TelephoneNumberSyntaxChecker( String oid )
128    {
129        this( oid, DEFAULT_REGEXP, Pattern.compile( DEFAULT_REGEXP ) );
130    }
131
132    
133    /**
134     * Creates a new instance of a child of this class, with an OID.
135     * 
136     * @param oid the child's OID
137     * @param defaultRegexp The regexp to use
138     * @param defaultPattern The compiled version of the regexp
139     */
140    private TelephoneNumberSyntaxChecker( String oid, String defaultRegexp, Pattern defaultPattern )
141    {
142        super( oid );
143        
144        this.defaultPattern = defaultPattern;
145        this.defaultRegexp = defaultRegexp;
146    }
147
148    
149    /**
150     * @return An instance of the Builder for this class
151     */
152    public static Builder builder()
153    {
154        return new Builder();
155    }
156
157
158    /**
159     * Get the default regexp (either the original one, or the one that has been set)
160     * 
161     * @return The default regexp
162     */
163    public String getRegexp()
164    {
165        return defaultRegexp;
166    }
167
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public boolean isValidSyntax( Object value )
174    {
175        String strValue;
176
177        if ( value == null )
178        {
179            if ( LOG.isDebugEnabled() )
180            {
181                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) );
182            }
183            
184            return false;
185        }
186
187        if ( value instanceof String )
188        {
189            strValue = ( String ) value;
190        }
191        else if ( value instanceof byte[] )
192        {
193            strValue = Strings.utf8ToString( ( byte[] ) value );
194        }
195        else
196        {
197            strValue = value.toString();
198        }
199
200        if ( strValue.length() == 0 )
201        {
202            if ( LOG.isDebugEnabled() )
203            {
204                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
205            }
206            
207            return false;
208        }
209
210        // We will use a regexp to check the TelephoneNumber.
211        boolean result;
212        
213        // Not sure this is 100% necessary...
214        synchronized ( defaultPattern )
215        {
216            result = defaultPattern.matcher( strValue ).matches();
217        }
218
219        if ( LOG.isDebugEnabled() )
220        {
221            if ( result )
222            {
223                LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) );
224            }
225            else
226            {
227                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
228            }
229        }
230
231        return result;
232    }
233}