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     */
020    package org.apache.directory.shared.ldap.codec.search.controls.entryChange;
021    
022    
023    import org.apache.directory.shared.asn1.ber.IAsn1Container;
024    import org.apache.directory.shared.asn1.ber.grammar.AbstractGrammar;
025    import org.apache.directory.shared.asn1.ber.grammar.GrammarAction;
026    import org.apache.directory.shared.asn1.ber.grammar.GrammarTransition;
027    import org.apache.directory.shared.asn1.ber.grammar.IGrammar;
028    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
029    import org.apache.directory.shared.asn1.ber.tlv.Value;
030    import org.apache.directory.shared.asn1.codec.DecoderException;
031    import org.apache.directory.shared.asn1.util.IntegerDecoder;
032    import org.apache.directory.shared.asn1.util.IntegerDecoderException;
033    import org.apache.directory.shared.asn1.util.LongDecoder;
034    import org.apache.directory.shared.asn1.util.LongDecoderException;
035    import org.apache.directory.shared.i18n.I18n;
036    import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
037    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
038    import org.apache.directory.shared.ldap.name.DN;
039    import org.apache.directory.shared.ldap.util.StringTools;
040    import org.slf4j.Logger;
041    import org.slf4j.LoggerFactory;
042    
043    
044    /**
045     * This class implements the EntryChangeControl. All the actions are declared in
046     * this class. As it is a singleton, these declaration are only done once.
047     * 
048     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
049     * @version $Rev: 923473 $, $Date: 2010-03-15 23:25:03 +0100 (Mon, 15 Mar 2010) $, 
050     */
051    public class EntryChangeControlGrammar extends AbstractGrammar
052    {
053        /** The logger */
054        static final Logger log = LoggerFactory.getLogger( EntryChangeControlGrammar.class );
055    
056        /** Speedup for logs */
057        static final boolean IS_DEBUG = log.isDebugEnabled();
058    
059        /** The instance of grammar. EntryChangeControlGrammar is a singleton */
060        private static IGrammar instance = new EntryChangeControlGrammar();
061    
062    
063        /**
064         * Creates a new EntryChangeControlGrammar object.
065         */
066        private EntryChangeControlGrammar()
067        {
068            name = EntryChangeControlGrammar.class.getName();
069            statesEnum = EntryChangeControlStatesEnum.getInstance();
070    
071            // Create the transitions table
072            super.transitions = new GrammarTransition[EntryChangeControlStatesEnum.LAST_EC_STATE][256];
073    
074            // ============================================================================================
075            // Transition from start state to Entry Change sequence
076            // ============================================================================================
077            // EntryChangeNotification ::= SEQUENCE {
078            //     ...
079            //
080            // Initialization of the structure
081            super.transitions[EntryChangeControlStatesEnum.START_STATE][UniversalTag.SEQUENCE_TAG] = 
082                new GrammarTransition( EntryChangeControlStatesEnum.START_STATE, 
083                                        EntryChangeControlStatesEnum.EC_SEQUENCE_STATE, 
084                                        UniversalTag.SEQUENCE_TAG, null );
085    
086            // ============================================================================================
087            // transition from Entry Change sequence to Change Type
088            // ============================================================================================
089            // EntryChangeNotification ::= SEQUENCE {
090            //     changeType ENUMERATED {
091            //     ...
092            //
093            // Evaluates the changeType
094            super.transitions[EntryChangeControlStatesEnum.EC_SEQUENCE_STATE][UniversalTag.ENUMERATED_TAG] = 
095                new GrammarTransition( EntryChangeControlStatesEnum.EC_SEQUENCE_STATE,
096                                        EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
097                                        UniversalTag.ENUMERATED_TAG,
098                new GrammarAction( "Set EntryChangeControl changeType" )
099            {
100                public void action( IAsn1Container container ) throws DecoderException
101                {
102                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
103                    Value value = entryChangeContainer.getCurrentTLV().getValue();
104    
105                    try
106                    {
107                        int change = IntegerDecoder.parse( value, 1, 8 );
108    
109                        switch ( change )
110                        {
111                            case ChangeType.ADD_VALUE:
112                            case ChangeType.DELETE_VALUE:
113                            case ChangeType.MODDN_VALUE:
114                            case ChangeType.MODIFY_VALUE:
115                                ChangeType changeType = ChangeType.getChangeType( change );
116    
117                                if ( IS_DEBUG )
118                                {
119                                    log.debug( "changeType = " + changeType );
120                                }
121    
122                                entryChangeContainer.getEntryChangeControl().setChangeType( changeType );
123                                break;
124    
125                            default:
126                                String msg = I18n.err( I18n.ERR_04044 );
127                                log.error( msg );
128                                throw new DecoderException( msg );
129                        }
130    
131                        // We can have an END transition
132                        entryChangeContainer.grammarEndAllowed( true );
133                    }
134                    catch ( IntegerDecoderException e )
135                    {
136                        String msg = I18n.err( I18n.ERR_04044 );
137                        log.error( msg, e );
138                        throw new DecoderException( msg );
139                    }
140                }
141            } );
142    
143            // ============================================================================================
144            // Transition from Change Type to Previous DN
145            // ============================================================================================
146            // EntryChangeNotification ::= SEQUENCE {
147            //     ...
148            //     previousDN LDAPDN OPTIONAL,
149            //     ...
150            //
151            // Set the previousDN into the structure. We first check that it's a
152            // valid DN
153            super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_STATE][UniversalTag.OCTET_STRING_TAG] = 
154                new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
155                                        EntryChangeControlStatesEnum.PREVIOUS_DN_STATE,
156                                        UniversalTag.OCTET_STRING_TAG,
157                new GrammarAction( "Set EntryChangeControl previousDN" )
158            {
159                public void action( IAsn1Container container ) throws DecoderException
160                {
161                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
162    
163                    ChangeType changeType = entryChangeContainer.getEntryChangeControl().getChangeType();
164    
165                    if ( changeType != ChangeType.MODDN )
166                    {
167                        log.error( I18n.err( I18n.ERR_04045 ) );
168                        throw new DecoderException( I18n.err( I18n.ERR_04046 ));
169                    }
170                    else
171                    {
172                        Value value = entryChangeContainer.getCurrentTLV().getValue();
173                        DN previousDn = null;
174    
175                        try
176                        {
177                            previousDn = new DN( StringTools.utf8ToString( value.getData() ) );
178                        }
179                        catch ( LdapInvalidDnException ine )
180                        {
181                            log.error( I18n.err( I18n.ERR_04047, StringTools.dumpBytes( value.getData() ) ) );
182                            throw new DecoderException( I18n.err( I18n.ERR_04048 ) );
183                        }
184    
185                        if ( IS_DEBUG )
186                        {
187                            log.debug( "previousDN = " + previousDn );
188                        }
189    
190                        entryChangeContainer.getEntryChangeControl().setPreviousDn( previousDn );
191    
192                        // We can have an END transition
193                        entryChangeContainer.grammarEndAllowed( true );
194                    }
195                }
196            } );
197    
198            // Change Number action
199            GrammarAction setChangeNumberAction = new GrammarAction( "Set EntryChangeControl changeNumber" )
200            {
201                public void action( IAsn1Container container ) throws DecoderException
202                {
203                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
204                    Value value = entryChangeContainer.getCurrentTLV().getValue();
205    
206                    try
207                    {
208                        long changeNumber = LongDecoder.parse( value );
209    
210                        if ( IS_DEBUG )
211                        {
212                            log.debug( "changeNumber = " + changeNumber );
213                        }
214    
215                        entryChangeContainer.getEntryChangeControl().setChangeNumber( changeNumber );
216    
217                        // We can have an END transition
218                        entryChangeContainer.grammarEndAllowed( true );
219                    }
220                    catch ( LongDecoderException e )
221                    {
222                        String msg = I18n.err( I18n.ERR_04049 );
223                        log.error( msg, e );
224                        throw new DecoderException( msg );
225                    }
226                }
227            };
228    
229            // ============================================================================================
230            // Transition from Previous DN to Change Number
231            // ============================================================================================
232            // EntryChangeNotification ::= SEQUENCE {
233            //     ...
234            //     changeNumber INTEGER OPTIONAL
235            // }
236            //
237            // Set the changeNumber into the structure
238            super.transitions[EntryChangeControlStatesEnum.PREVIOUS_DN_STATE][UniversalTag.INTEGER_TAG] = 
239                new GrammarTransition( EntryChangeControlStatesEnum.PREVIOUS_DN_STATE, 
240                                        EntryChangeControlStatesEnum.CHANGE_NUMBER_STATE, 
241                                        UniversalTag.INTEGER_TAG,
242                    setChangeNumberAction );
243    
244            // ============================================================================================
245            // Transition from Previous DN to Change Number
246            // ============================================================================================
247            // EntryChangeNotification ::= SEQUENCE {
248            //     ...
249            //     changeNumber INTEGER OPTIONAL
250            // }
251            //
252            // Set the changeNumber into the structure
253            super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_STATE][UniversalTag.INTEGER_TAG] = 
254                new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
255                                        EntryChangeControlStatesEnum.CHANGE_NUMBER_STATE, 
256                                        UniversalTag.INTEGER_TAG,
257                    setChangeNumberAction );
258        }
259    
260    
261        /**
262         * This class is a singleton.
263         * 
264         * @return An instance on this grammar
265         */
266        public static IGrammar getInstance()
267        {
268            return instance;
269        }
270    }