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 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.Strings;
027
028
029/**
030 * A SyntaxChecker which verifies that a value is a BootParameter according to 
031 * RFC 2307 :
032 * <pre>
033 * bootparameter     = key "=" server ":" path
034 *      key               = keystring
035 *      server            = keystring
036 *      path              = keystring
037 * </pre>
038 * 
039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040 */
041@SuppressWarnings("serial")
042public final class BootParameterSyntaxChecker extends SyntaxChecker
043{
044    /**
045     * A static instance of BootParameterSyntaxChecker
046     */
047    public static final BootParameterSyntaxChecker INSTANCE = 
048        new BootParameterSyntaxChecker( SchemaConstants.BOOT_PARAMETER_SYNTAX );
049    
050    /**
051     * A static Builder for this class
052     */
053    public static final class Builder extends SCBuilder<BootParameterSyntaxChecker>
054    {
055        /**
056         * The Builder constructor
057         */
058        private Builder()
059        {
060            super( SchemaConstants.BOOT_PARAMETER_SYNTAX );
061        }
062        
063        
064        /**
065         * Create a new instance of BootParameterSyntaxChecker
066         * @return A new instance of BootParameterSyntaxChecker
067         */
068        @Override
069        public BootParameterSyntaxChecker build()
070        {
071            return new BootParameterSyntaxChecker( oid );
072        }
073    }
074
075    
076    /**
077     * Creates a new instance of BootParameterSyntaxChecker.
078     * 
079     * @param oid The OID to use for this SyntaxChecker
080     */
081    private BootParameterSyntaxChecker( String oid )
082    {
083        super( oid );
084    }
085
086    
087    /**
088     * @return An instance of the Builder for this class
089     */
090    public static Builder builder()
091    {
092        return new Builder();
093    }
094    
095    
096    private int parseKeyString( String strValue, int pos, char limit )
097    {
098        try 
099        { 
100            char c = strValue.charAt( pos );
101            
102            // The end of the keyString
103            if ( c == limit )
104            {
105                return pos;
106            }
107            
108            // We must have a first alphabetic char
109            if ( Character.isUpperCase( c ) || Character.isLowerCase( c ) )
110            {
111                pos++;
112            }
113            else
114            {
115                return -1;
116            }
117            
118            c = strValue.charAt( pos );
119            
120            while ( c != limit )
121            {
122                if ( Character.isUpperCase( c ) || Character.isLowerCase( c ) || Character.isDigit( c )
123                    || ( c == '-' ) || ( c == ';' ) || ( c == '_' ) )
124                {
125                    pos++;
126                }
127                else
128                {
129                    return -1;
130                }
131
132                c = strValue.charAt( pos );
133            }
134            
135            return pos;
136        }
137        catch ( IndexOutOfBoundsException ioobe )
138        {
139            return -1;
140        }
141    }
142    
143    
144    private int parseKeyString( String strValue, int pos )
145    {
146        try 
147        { 
148            char c = strValue.charAt( pos );
149            
150            // We must have a first alphabetic char
151            if ( Character.isUpperCase( c ) || Character.isLowerCase( c ) )
152            {
153                pos++;
154            }
155            else
156            {
157                return -1;
158            }
159            
160            while ( pos < strValue.length() )
161            {
162                c = strValue.charAt( pos );
163
164                if ( Character.isUpperCase( c ) || Character.isLowerCase( c ) || Character.isDigit( c )
165                    || ( c == '-' ) || ( c == ';' ) || ( c == '_' ) )
166                {
167                    pos++;
168                }
169                else
170                {
171                    return -1;
172                }
173            }
174            
175            return pos;
176        }
177        catch ( IndexOutOfBoundsException ioobe )
178        {
179            return -1;
180        }
181    }
182    
183
184    /**
185     * {@inheritDoc}
186     */
187    @Override
188    public boolean isValidSyntax( Object value )
189    {
190        String strValue;
191
192        if ( value == null )
193        {
194            if ( LOG.isDebugEnabled() )
195            {
196                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) );
197            }
198            
199            return false;
200        }
201
202        if ( value instanceof String )
203        {
204            strValue = ( String ) value;
205        }
206        else if ( value instanceof byte[] )
207        {
208            strValue = Strings.utf8ToString( ( byte[] ) value );
209        }
210        else
211        {
212            strValue = value.toString();
213        }
214
215        // The  BootParameter must at least contain a '=' and a ':', plus 3 keyStrings
216        if ( strValue.length() < 5 )
217        {
218            if ( LOG.isDebugEnabled() )
219            {
220                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
221            }
222            
223            return false;
224        }
225
226        // The key
227        int pos = parseKeyString( strValue, 0, '=' );
228        
229        if ( pos == -1 )
230        {
231            if ( LOG.isDebugEnabled() )
232            {
233                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
234            }
235            
236            return false;
237        }
238        
239        if ( strValue.charAt( pos ) != '=' )
240        {
241            if ( LOG.isDebugEnabled() )
242            {
243                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
244            }
245            
246            return false;
247        }
248        else
249        {
250            pos++;
251        }
252
253        // The server
254        pos = parseKeyString( strValue, pos, ':' );
255        
256        if ( pos == -1 )
257        {
258            if ( LOG.isDebugEnabled() )
259            {
260                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
261            }
262            
263            return false;
264        }
265        
266        if ( strValue.charAt( pos ) != ':' )
267        {
268            if ( LOG.isDebugEnabled() )
269            {
270                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
271            }
272            
273            return false;
274        }
275        else
276        {
277            pos++;
278        }
279
280        // The path
281        pos = parseKeyString( strValue, pos );
282        
283        if ( pos == -1 )
284        {
285            if ( LOG.isDebugEnabled() )
286            {
287                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
288            }
289            
290            return false;
291        }
292        
293        return true;
294    }
295}