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.name.Dn;
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 valid Name and Optional UID.
032 * <p>
033 * This element is a composition of two parts, a {@link Dn} and an optional UID :
034 * <pre>
035 * NameAndOptionalUID = distinguishedName [ SHARP BitString ]
036 * </pre>
037 * Both part already have their syntax checkers, so we will just call them
038 * after having split the element in two ( if necessary)
039 * <p>
040 * We just check that the {@link Dn} is valid, we don't need to verify each of the {@link Rdn}
041 * syntax.
042 * 
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 */
045@SuppressWarnings("serial")
046public final class NameAndOptionalUIDSyntaxChecker extends SyntaxChecker
047{
048    /**
049     * A static instance of NameAndOptionalUIDSyntaxChecker
050     */
051    public static final NameAndOptionalUIDSyntaxChecker INSTANCE = 
052        new NameAndOptionalUIDSyntaxChecker( SchemaConstants.NAME_AND_OPTIONAL_UID_SYNTAX );
053    
054    /**
055     * A static Builder for this class
056     */
057    public static final class Builder extends SCBuilder<NameAndOptionalUIDSyntaxChecker>
058    {
059        /**
060         * The Builder constructor
061         */
062        private Builder()
063        {
064            super( SchemaConstants.NAME_AND_OPTIONAL_UID_SYNTAX );
065        }
066        
067        
068        /**
069         * Create a new instance of NameAndOptionalUIDSyntaxChecker
070         * @return A new instance of NameAndOptionalUIDSyntaxChecker
071         */
072        @Override
073        public NameAndOptionalUIDSyntaxChecker build()
074        {
075            return new NameAndOptionalUIDSyntaxChecker( oid );
076        }
077    }
078
079    
080    /**
081     * Creates a new instance of NameAndOptionalUIDSyntaxChecker.
082     * 
083     * @param oid The OID to use for this SyntaxChecker
084     */
085    private NameAndOptionalUIDSyntaxChecker( String oid )
086    {
087        super( oid );
088    }
089
090    
091    /**
092     * @return An instance of the Builder for this class
093     */
094    public static Builder builder()
095    {
096        return new Builder();
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    @Override
104    public boolean isValidSyntax( Object value )
105    {
106        String strValue;
107
108        if ( value == null )
109        {
110            if ( LOG.isDebugEnabled() )
111            {
112                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, "null" ) );
113            }
114            
115            return false;
116        }
117
118        if ( value instanceof String )
119        {
120            strValue = ( String ) value;
121        }
122        else if ( value instanceof byte[] )
123        {
124            strValue = Strings.utf8ToString( ( byte[] ) value );
125        }
126        else
127        {
128            strValue = value.toString();
129        }
130
131        if ( strValue.length() == 0 )
132        {
133            if ( LOG.isDebugEnabled() )
134            {
135                LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
136            }
137            
138            return false;
139        }
140
141        // Let's see if we have an UID part
142        int sharpPos = strValue.lastIndexOf( '#' );
143
144        if ( sharpPos != -1 )
145        {
146            // Now, check that we don't have another '#'
147            if ( strValue.indexOf( '#' ) != sharpPos )
148            {
149                // Yes, we have one : this is not allowed, it should have been
150                // escaped.
151                if ( LOG.isDebugEnabled() )
152                {
153                    LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
154                }
155                
156                return false;
157            }
158
159            // This is an UID if the '#' is immediately
160            // followed by a BitString, except if the '#' is
161            // on the last position
162            if ( BitStringSyntaxChecker.isValid( strValue.substring( sharpPos + 1 ) )
163                && ( sharpPos < strValue.length() ) )
164            {
165                // Ok, we have a BitString, now check the Dn,
166                // except if the '#' is in first position
167                if ( sharpPos > 0 )
168                {
169                    boolean result = Dn.isValid( strValue.substring( 0, sharpPos ) );
170
171                    if ( LOG.isDebugEnabled() )
172                    {
173                        if ( result )
174                        {
175                            LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
176                        }
177                        else
178                        {
179                            LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
180                        }
181                    }
182
183                    return result;
184
185                }
186                else
187                {
188                    // The Dn must not be null ?
189                    if ( LOG.isDebugEnabled() )
190                    {
191                        LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
192                    }
193                    
194                    return false;
195                }
196            }
197            else
198            {
199                // We have found a '#' but no UID part.
200                if ( LOG.isDebugEnabled() )
201                {
202                    LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
203                }
204                
205                return false;
206            }
207        }
208        else
209        {
210            // No UID, the strValue is a Dn
211            // Check that the value is a valid Dn
212            boolean result = Dn.isValid( strValue );
213
214            if ( LOG.isDebugEnabled() )
215            {
216                if ( result )
217                {
218                    LOG.debug( I18n.msg( I18n.MSG_04489_SYNTAX_VALID, value ) );
219                }
220                else
221                {
222                    LOG.debug( I18n.err( I18n.ERR_04488_SYNTAX_INVALID, value ) );
223                }
224            }
225
226            return result;
227        }
228    }
229}