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 *    http://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 default pattern */
071        private String defaultRegexp;
072
073        /** The compiled 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         */
102        public Builder setDefaultRegexp( String regexp )
103        {
104            defaultRegexp = regexp;
105            
106            try
107            {
108                defaultPattern = Pattern.compile( regexp );
109            }
110            catch ( PatternSyntaxException pse )
111            {
112                // Roll back to the default pattern
113                defaultPattern = Pattern.compile( DEFAULT_REGEXP );
114            }
115
116            return this;
117        }
118    }
119
120    
121    /**
122     * Creates a new instance of a child of this class, with an OID.
123     * 
124     * @param oid the child's OID
125     */
126    private TelephoneNumberSyntaxChecker( String oid )
127    {
128        this( oid, DEFAULT_REGEXP, Pattern.compile( DEFAULT_REGEXP ) );
129    }
130
131    
132    /**
133     * Creates a new instance of a child of this class, with an OID.
134     * 
135     * @param oid the child's OID
136     * @param defaultRegexp The regexp to use
137     * @param defaultPattern The compiled version of the regexp
138     */
139    private TelephoneNumberSyntaxChecker( String oid, String defaultRegexp, Pattern defaultPattern )
140    {
141        super( oid );
142
143        this.defaultPattern = defaultPattern;
144        this.defaultRegexp = defaultRegexp;
145    }
146
147    
148    /**
149     * @return An instance of the Builder for this class
150     */
151    public static Builder builder()
152    {
153        return new Builder();
154    }
155
156
157    /**
158     * Get the default regexp (either the original one, or the one that has been set)
159     * 
160     * @return The default regexp
161     */
162    public String getRegexp()
163    {
164        return defaultRegexp;
165    }
166
167
168    /**
169     * {@inheritDoc}
170     */
171    @Override
172    public boolean isValidSyntax( Object value )
173    {
174        String strValue;
175
176        if ( value == null )
177        {
178            if ( LOG.isDebugEnabled() )
179            {
180                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, "null" ) );
181            }
182            
183            return false;
184        }
185
186        if ( value instanceof String )
187        {
188            strValue = ( String ) value;
189        }
190        else if ( value instanceof byte[] )
191        {
192            strValue = Strings.utf8ToString( ( byte[] ) value );
193        }
194        else
195        {
196            strValue = value.toString();
197        }
198
199        if ( strValue.length() == 0 )
200        {
201            if ( LOG.isDebugEnabled() )
202            {
203                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
204            }
205            
206            return false;
207        }
208
209        // We will use a regexp to check the TelephoneNumber.
210        boolean result;
211        
212        // Not sure this is 100% necessary...
213        synchronized ( defaultPattern )
214        {
215            result = defaultPattern.matcher( strValue ).matches();
216        }
217
218        if ( LOG.isDebugEnabled() )
219        {
220            if ( result )
221            {
222                LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
223            }
224            else
225            {
226                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
227            }
228        }
229
230        return result;
231    }
232}