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 short or
032 * the Short wrapper.  Essentially this constrains the min and max values of
033 * the Integer.
034 * <p>
035 * From RFC 4517 :
036 *
037 * <pre>
038 * Integer = ( HYPHEN LDIGIT *DIGIT ) | number
039 *
040 * From RFC 4512 :
041 * number  = DIGIT | ( LDIGIT 1*DIGIT )
042 * DIGIT   = %x30 | LDIGIT       ; "0"-"9"
043 * LDIGIT  = %x31-39             ; "1"-"9"
044 * HYPHEN  = %x2D                ; hyphen ("-")
045 * </pre>
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049@SuppressWarnings("serial")
050public final class JavaByteSyntaxChecker extends SyntaxChecker
051{
052    /**
053     * A static instance of JavaByteSyntaxChecker
054     */
055    public static final JavaByteSyntaxChecker INSTANCE = new JavaByteSyntaxChecker( SchemaConstants.JAVA_BYTE_SYNTAX );
056    
057    /**
058     * A static Builder for this class
059     */
060    public static final class Builder extends SCBuilder<JavaByteSyntaxChecker>
061    {
062        /**
063         * The Builder constructor
064         */
065        private Builder()
066        {
067            super( SchemaConstants.JAVA_BYTE_SYNTAX );
068        }
069        
070        
071        /**
072         * Create a new instance of JavaByteSyntaxChecker
073         * @return A new instance of JavaByteSyntaxChecker
074         */
075        @Override
076        public JavaByteSyntaxChecker build()
077        {
078            return new JavaByteSyntaxChecker( oid );
079        }
080    }
081
082
083    /**
084     * Creates a new instance of JavaByteSyntaxChecker.
085     * 
086     * @param oid The OID to use for this SyntaxChecker
087     */
088    private JavaByteSyntaxChecker( 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.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
171                }
172                else
173                {
174                    LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, 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        // Should get a NumberFormatException for Byte values out of range
212        try
213        {
214            Byte.valueOf( strValue );
215
216            if ( LOG.isDebugEnabled() )
217            {
218                LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
219            }
220            
221            return true;
222        }
223        catch ( NumberFormatException e )
224        {
225            if ( LOG.isDebugEnabled() )
226            {
227                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
228            }
229            
230            return false;
231        }
232    }
233}