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.asn1.util.Oid;
024import org.apache.directory.api.i18n.I18n;
025import org.apache.directory.api.ldap.model.constants.SchemaConstants;
026import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
027import org.apache.directory.api.util.Strings;
028
029
030/**
031 * A SyntaxChecker which verifies that a value is a numeric oid and a length
032 * constraint according to RFC 4512.
033 * <p>
034 * From RFC 4512 :
035 * <pre>
036 * noidlen    = numericoid [ LCURLY len RCURLY ]
037 * numericoid = number 1*( DOT number )
038 * len        = number
039 * number     = DIGIT | ( LDIGIT 1*DIGIT )
040 * DIGIT      = %x30 | LDIGIT                  ; "0"-"9"
041 * LDIGIT     = %x31-39                        ; "1"-"9"
042 * DOT        = %x2E                           ; period (".")
043 * LCURLY  = %x7B                              ; left curly brace "{"
044 * RCURLY  = %x7D                              ; right curly brace "}"
045 * </pre>
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049@SuppressWarnings("serial")
050public final class OidLenSyntaxChecker extends SyntaxChecker
051{
052    /**
053     * A static instance of OidLenSyntaxChecker
054     */
055    public static final OidLenSyntaxChecker INSTANCE = 
056        new OidLenSyntaxChecker( SchemaConstants.OID_LEN_SYNTAX );
057    
058    /**
059     * A static Builder for this class
060     */
061    public static final class Builder extends SCBuilder<OidLenSyntaxChecker>
062    {
063        /**
064         * The Builder constructor
065         */
066        private Builder()
067        {
068            super( SchemaConstants.OID_LEN_SYNTAX );
069        }
070        
071        
072        /**
073         * Create a new instance of OidLenSyntaxChecker
074         * @return A new instance of OidLenSyntaxChecker
075         */
076        @Override
077        public OidLenSyntaxChecker build()
078        {
079            return new OidLenSyntaxChecker( oid );
080        }
081    }
082
083    
084    /**
085     * 
086     * Creates a new instance of OidLenSyntaxChecker.
087     * 
088     * @param oid The OID
089     */
090    private OidLenSyntaxChecker( String oid )
091    {
092        super( oid );
093    }
094
095    
096    /**
097     * @return An instance of the Builder for this class
098     */
099    public static Builder builder()
100    {
101        return new Builder();
102    }
103
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public boolean isValidSyntax( Object value )
110    {
111        String strValue;
112
113        if ( value == null )
114        {
115            if ( LOG.isDebugEnabled() )
116            {
117                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) );
118            }
119            
120            return false;
121        }
122
123        if ( value instanceof String )
124        {
125            strValue = ( String ) value;
126        }
127        else if ( value instanceof byte[] )
128        {
129            strValue = Strings.utf8ToString( ( byte[] ) value );
130        }
131        else
132        {
133            strValue = value.toString();
134        }
135
136        if ( strValue.length() == 0 )
137        {
138            if ( LOG.isDebugEnabled() )
139            {
140                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
141            }
142            
143            return false;
144        }
145
146        // We are looking at the first position of the len part
147        int pos = strValue.indexOf( '{' );
148
149        if ( pos < 0 )
150        {
151            // Not found ... but it may still be a valid OID
152            boolean result = Oid.isOid( strValue );
153
154            if ( LOG.isDebugEnabled() )
155            {
156                if ( result )
157                {
158                    LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) );
159                }
160                else
161                {
162                    LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
163                }
164            }
165                
166            return result;
167        }
168        else
169        {
170            // we should have a len value. First check that the OID is valid
171            String oid = strValue.substring( 0, pos );
172
173            if ( !Oid.isOid( oid ) )
174            {
175                if ( LOG.isDebugEnabled() )
176                {
177                    LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
178                }
179                
180                return false;
181            }
182
183            String len = strValue.substring( pos );
184
185            // We must have a number and a '}' at the end
186            if ( len.charAt( len.length() - 1 ) != '}' )
187            {
188                // No final '}'
189                if ( LOG.isDebugEnabled() )
190                {
191                    LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
192                }
193                
194                return false;
195            }
196
197            for ( int i = 1; i < len.length() - 1; i++ )
198            {
199                switch ( len.charAt( i ) )
200                {
201                    case '0':
202                    case '1':
203                    case '2':
204                    case '3':
205                    case '4':
206                    case '5':
207                    case '6':
208                    case '7':
209                    case '8':
210                    case '9':
211                        break;
212
213                    default:
214                        if ( LOG.isDebugEnabled() )
215                        {
216                            LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
217                        }
218                        
219                        return false;
220                }
221            }
222
223            if ( ( len.charAt( 1 ) == '0' ) && len.length() > 3 )
224            {
225                // A number can't start with a '0' unless it's the only
226                // number
227                if ( LOG.isDebugEnabled() )
228                {
229                    LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
230                }
231                
232                return false;
233            }
234
235            if ( LOG.isDebugEnabled() )
236            {
237                LOG.debug( I18n.msg( I18n.MSG_13701_SYNTAX_VALID, value ) );
238            }
239            
240            return true;
241        }
242    }
243}