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 NisNetGroupTriple according to 
031 * RFC 2307 :
032 * <pre>
033 * nisnetgrouptriple = "(" hostname "," username "," domainname ")"
034 *      hostname          = "" / "-" / keystring
035 *      username          = "" / "-" / keystring
036 *      domainname        = "" / "-" / keystring
037 * </pre>
038 * 
039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040 */
041@SuppressWarnings("serial")
042public final class NisNetgroupTripleSyntaxChecker extends SyntaxChecker
043{
044    /**
045     * A static instance of NisNetGroupTripleChecker
046     */
047    public static final NisNetgroupTripleSyntaxChecker INSTANCE = 
048        new NisNetgroupTripleSyntaxChecker( SchemaConstants.NIS_NETGROUP_TRIPLE_SYNTAX );
049    
050    /**
051     * A static Builder for this class
052     */
053    public static final class Builder extends SCBuilder<NisNetgroupTripleSyntaxChecker>
054    {
055        /**
056         * The Builder constructor
057         */
058        private Builder()
059        {
060            super( SchemaConstants.NIS_NETGROUP_TRIPLE_SYNTAX );
061        }
062        
063        
064        /**
065         * Create a new instance of NisNetgroupTripleSyntaxChecker
066         * @return A new instance of NisNetgroupTripleSyntaxChecker
067         */
068        @Override
069        public NisNetgroupTripleSyntaxChecker build()
070        {
071            return new NisNetgroupTripleSyntaxChecker( oid );
072        }
073    }
074
075    
076    /**
077     * Creates a new instance of NisNetgroupTripleSyntaxChecker.
078     * 
079     * @param oid The OID to use for this SyntaxChecker
080     */
081    private NisNetgroupTripleSyntaxChecker( 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 )
097    {
098        char c = strValue.charAt( pos );
099        
100        // The end of the keyString
101        if ( ( c == ',' ) || ( c == ')' ) )
102        {
103            return pos;
104        }
105        
106        // We must have a first alphabetic char
107        if ( Character.isUpperCase( c ) || Character.isLowerCase( c ) )
108        {
109            pos++;
110        }
111        else
112        {
113            return -1;
114        }
115        
116        try 
117        { 
118            c = strValue.charAt( pos );
119            
120            while ( ( c != ',' ) && ( c != ')' ) )
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    /**
145     * {@inheritDoc}
146     */
147    @Override
148    public boolean isValidSyntax( Object value )
149    {
150        String strValue;
151
152        if ( value == null )
153        {
154            if ( LOG.isDebugEnabled() )
155            {
156                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, "null" ) );
157            }
158            
159            return false;
160        }
161
162        if ( value instanceof String )
163        {
164            strValue = ( String ) value;
165        }
166        else if ( value instanceof byte[] )
167        {
168            strValue = Strings.utf8ToString( ( byte[] ) value );
169        }
170        else
171        {
172            strValue = value.toString();
173        }
174
175        // The  nisNetGroup must at least contain a '(', 2 ',' and a ')'
176        if ( strValue.length() < 4 )
177        {
178            if ( LOG.isDebugEnabled() )
179            {
180                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
181            }
182            
183            return false;
184        }
185
186        // The hostname
187        int pos = parseKeyString( strValue, 1 );
188        
189        if ( pos == -1 )
190        {
191            if ( LOG.isDebugEnabled() )
192            {
193                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
194            }
195            
196            return false;
197        }
198        
199        if ( strValue.charAt( pos ) != ',' )
200        {
201            if ( LOG.isDebugEnabled() )
202            {
203                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
204            }
205            
206            return false;
207        }
208        else
209        {
210            pos++;
211        }
212
213        // The username
214        pos = parseKeyString( strValue, pos );
215        
216        if ( pos == -1 )
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        if ( strValue.charAt( pos ) != ',' )
227        {
228            if ( LOG.isDebugEnabled() )
229            {
230                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
231            }
232            
233            return false;
234        }
235        else
236        {
237            pos++;
238        }
239
240        // The domainname
241        pos = parseKeyString( strValue, pos );
242        
243        if ( pos == -1 )
244        {
245            if ( LOG.isDebugEnabled() )
246            {
247                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
248            }
249            
250            return false;
251        }
252        
253        if ( strValue.charAt( pos ) != ')' )
254        {
255            if ( LOG.isDebugEnabled() )
256            {
257                LOG.debug( I18n.err( I18n.ERR_13210_SYNTAX_INVALID, value ) );
258            }
259            
260            return false;
261        }
262
263        return true;
264    }
265}