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 org.apache.directory.api.i18n.I18n;
024import org.apache.directory.api.ldap.model.constants.SchemaConstants;
025import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
026import org.apache.directory.api.util.Chars;
027import org.apache.directory.api.util.Strings;
028
029
030/**
031 * A SyntaxChecker which verifies that a value is a valid Java primitive int or
032 * the Integer wrapper.  Essentially this constrains the min and max values of
033 * the Integer.
034 * <p>
035 * From RFC 4517 :
036 * <pre>
037 * Integer = ( HYPHEN LDIGIT *DIGIT ) | number
038 *
039 * From RFC 4512 :
040 * number  = DIGIT | ( LDIGIT 1*DIGIT )
041 * DIGIT   = %x30 | LDIGIT       ; "0"-"9"
042 * LDIGIT  = %x31-39             ; "1"-"9"
043 * HYPHEN  = %x2D                ; hyphen ("-")
044 * </pre>
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 */
048@SuppressWarnings("serial")
049public final class JavaIntegerSyntaxChecker extends SyntaxChecker
050{
051    /**
052     * A static instance of JavaIntegerSyntaxChecker
053     */
054    public static final JavaIntegerSyntaxChecker INSTANCE = 
055        new JavaIntegerSyntaxChecker( SchemaConstants.JAVA_INT_SYNTAX );
056    
057    /**
058     * A static Builder for this class
059     */
060    public static final class Builder extends SCBuilder<JavaIntegerSyntaxChecker>
061    {
062        /**
063         * The Builder constructor
064         */
065        private Builder()
066        {
067            super( SchemaConstants.JAVA_INT_SYNTAX );
068        }
069        
070        
071        /**
072         * Create a new instance of JavaIntegerSyntaxChecker
073         * @return A new instance of JavaIntegerSyntaxChecker
074         */
075        @Override
076        public JavaIntegerSyntaxChecker build()
077        {
078            return new JavaIntegerSyntaxChecker( oid );
079        }
080    }
081
082    
083    /**
084     * Creates a new instance of JavaIntegerSyntaxChecker.
085     * 
086     * @param oid The OID to use for this SyntaxChecker
087     */
088    private JavaIntegerSyntaxChecker( String oid )
089    {
090        super( oid );
091    }
092
093    
094    /**
095     * @return An instance of the Builder for this class
096     */
097    public static Builder builder()
098    {
099        return new Builder();
100    }
101
102
103    /**
104     * {@inheritDoc}
105     */
106    @Override
107    public boolean isValidSyntax( Object value )
108    {
109        String strValue;
110
111        if ( value == null )
112        {
113            if ( LOG.isDebugEnabled() )
114            {
115                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, "null" ) );
116            }
117            
118            return false;
119        }
120
121        if ( value instanceof String )
122        {
123            strValue = ( String ) value;
124        }
125        else if ( value instanceof byte[] )
126        {
127            strValue = Strings.utf8ToString( ( byte[] ) value );
128        }
129        else
130        {
131            strValue = value.toString();
132        }
133
134        if ( strValue.length() == 0 )
135        {
136            if ( LOG.isDebugEnabled() )
137            {
138                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
139            }
140            
141            return false;
142        }
143
144        // The first char must be either a '-' or in [0..9].
145        // If it's a '0', then there should be any other char after
146        int pos = 0;
147        char c = strValue.charAt( pos );
148
149        if ( c == '-' )
150        {
151            pos = 1;
152        }
153        else if ( !Chars.isDigit( c ) )
154        {
155            if ( LOG.isDebugEnabled() )
156            {
157                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
158            }
159            
160            return false;
161        }
162        else if ( c == '0' )
163        {
164            boolean result = strValue.length() <= 1;
165
166            if ( LOG.isDebugEnabled() )
167            {
168                if ( result )
169                {
170                    LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
171                }
172                else
173                {
174                    LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
175                }
176            }
177
178            return result;
179        }
180
181        // We must have at least a digit which is not '0'
182        if ( !Chars.isDigit( strValue, pos ) || Strings.isCharASCII( strValue, pos, '0' ) )
183        {
184            if ( LOG.isDebugEnabled() )
185            {
186                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
187            }
188            
189            return false;
190        }
191        else
192        {
193            pos++;
194        }
195
196        while ( Chars.isDigit( strValue, pos ) )
197        {
198            pos++;
199        }
200
201        if ( pos != strValue.length() )
202        {
203            if ( LOG.isDebugEnabled() )
204            {
205                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
206            }
207            
208            return false;
209        }
210
211        try
212        {
213            Integer.valueOf( strValue );
214
215            if ( LOG.isDebugEnabled() )
216            {
217                LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
218            }
219            
220            return true;
221        }
222        catch ( NumberFormatException e )
223        {
224            if ( LOG.isDebugEnabled() )
225            {
226                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
227            }
228            
229            return false;
230        }
231    }
232}