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 javax.naming.InvalidNameException;
024    
025    import org.apache.directory.shared.asn1.ber.IAsn1Container;
026    import org.apache.directory.shared.asn1.ber.grammar.AbstractGrammar;
027    import org.apache.directory.shared.asn1.ber.grammar.GrammarAction;
028    import org.apache.directory.shared.asn1.ber.grammar.GrammarTransition;
029    import org.apache.directory.shared.asn1.ber.grammar.IGrammar;
030    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
031    import org.apache.directory.shared.asn1.ber.tlv.Value;
032    import org.apache.directory.shared.asn1.codec.DecoderException;
033    import org.apache.directory.shared.asn1.util.IntegerDecoder;
034    import org.apache.directory.shared.asn1.util.IntegerDecoderException;
035    import org.apache.directory.shared.asn1.util.LongDecoder;
036    import org.apache.directory.shared.asn1.util.LongDecoderException;
037    import org.apache.directory.shared.ldap.codec.search.controls.ChangeType;
038    import org.apache.directory.shared.ldap.name.LdapDN;
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: 764131 $, $Date: 2009-04-11 03:03:00 +0200 (Sam, 11 avr 2009) $, 
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,
085                    new GrammarAction( "Init EntryChangeControl" )
086                {
087                    public void action( IAsn1Container container )
088                    {
089                        EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
090                        EntryChangeControlCodec control = new EntryChangeControlCodec();
091                        entryChangeContainer.setEntryChangeControl( control );
092                    }
093                } );
094    
095            // ============================================================================================
096            // transition from Entry Change sequence to Change Type
097            // ============================================================================================
098            // EntryChangeNotification ::= SEQUENCE {
099            //     changeType ENUMERATED {
100            //     ...
101            //
102            // Evaluates the changeType
103            super.transitions[EntryChangeControlStatesEnum.EC_SEQUENCE_STATE][UniversalTag.ENUMERATED_TAG] = 
104                new GrammarTransition( EntryChangeControlStatesEnum.EC_SEQUENCE_STATE,
105                                        EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
106                                        UniversalTag.ENUMERATED_TAG,
107                new GrammarAction( "Set EntryChangeControl changeType" )
108            {
109                public void action( IAsn1Container container ) throws DecoderException
110                {
111                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
112                    Value value = entryChangeContainer.getCurrentTLV().getValue();
113    
114                    try
115                    {
116                        int change = IntegerDecoder.parse( value, 1, 8 );
117    
118                        switch ( change )
119                        {
120                            case ChangeType.ADD_VALUE:
121                            case ChangeType.DELETE_VALUE:
122                            case ChangeType.MODDN_VALUE:
123                            case ChangeType.MODIFY_VALUE:
124                                ChangeType changeType = ChangeType.getChangeType( change );
125    
126                                if ( IS_DEBUG )
127                                {
128                                    log.debug( "changeType = " + changeType );
129                                }
130    
131                                entryChangeContainer.getEntryChangeControl().setChangeType( changeType );
132                                break;
133    
134                            default:
135                                String msg = "failed to decode the changeType for EntryChangeControl";
136                                log.error( msg );
137                                throw new DecoderException( msg );
138                        }
139    
140                        // We can have an END transition
141                        entryChangeContainer.grammarEndAllowed( true );
142                    }
143                    catch ( IntegerDecoderException e )
144                    {
145                        String msg = "failed to decode the changeType for EntryChangeControl";
146                        log.error( msg, e );
147                        throw new DecoderException( msg );
148                    }
149                }
150            } );
151    
152            // ============================================================================================
153            // Transition from Change Type to Previous DN
154            // ============================================================================================
155            // EntryChangeNotification ::= SEQUENCE {
156            //     ...
157            //     previousDN LDAPDN OPTIONAL,
158            //     ...
159            //
160            // Set the previousDN into the structure. We first check that it's a
161            // valid DN
162            super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_STATE][UniversalTag.OCTET_STRING_TAG] = 
163                new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
164                                        EntryChangeControlStatesEnum.PREVIOUS_DN_STATE,
165                                        UniversalTag.OCTET_STRING_TAG,
166                new GrammarAction( "Set EntryChangeControl previousDN" )
167            {
168                public void action( IAsn1Container container ) throws DecoderException
169                {
170                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
171    
172                    ChangeType changeType = entryChangeContainer.getEntryChangeControl().getChangeType();
173    
174                    if ( changeType != ChangeType.MODDN )
175                    {
176                        log.error( "The previousDN field should not contain anything if the changeType is not MODDN" );
177                        throw new DecoderException( "Previous DN is not allowed for this change type" );
178                    }
179                    else
180                    {
181                        Value value = entryChangeContainer.getCurrentTLV().getValue();
182                        LdapDN previousDn = null;
183    
184                        try
185                        {
186                            previousDn = new LdapDN( StringTools.utf8ToString( value.getData() ) );
187                        }
188                        catch ( InvalidNameException ine )
189                        {
190                            log.error( "Bad Previous DN : '" + StringTools.dumpBytes( value.getData() ) );
191                            throw new DecoderException( "failed to decode the previous DN" );
192                        }
193    
194                        if ( IS_DEBUG )
195                        {
196                            log.debug( "previousDN = " + previousDn );
197                        }
198    
199                        entryChangeContainer.getEntryChangeControl().setPreviousDn( previousDn );
200    
201                        // We can have an END transition
202                        entryChangeContainer.grammarEndAllowed( true );
203                    }
204                }
205            } );
206    
207            // Change Number action
208            GrammarAction setChangeNumberAction = new GrammarAction( "Set EntryChangeControl changeNumber" )
209            {
210                public void action( IAsn1Container container ) throws DecoderException
211                {
212                    EntryChangeControlContainer entryChangeContainer = ( EntryChangeControlContainer ) container;
213                    Value value = entryChangeContainer.getCurrentTLV().getValue();
214    
215                    try
216                    {
217                        long changeNumber = LongDecoder.parse( value );
218    
219                        if ( IS_DEBUG )
220                        {
221                            log.debug( "changeNumber = " + changeNumber );
222                        }
223    
224                        entryChangeContainer.getEntryChangeControl().setChangeNumber( changeNumber );
225    
226                        // We can have an END transition
227                        entryChangeContainer.grammarEndAllowed( true );
228                    }
229                    catch ( LongDecoderException e )
230                    {
231                        String msg = "failed to decode the changeNumber for EntryChangeControl";
232                        log.error( msg, e );
233                        throw new DecoderException( msg );
234                    }
235                }
236            };
237    
238            // ============================================================================================
239            // Transition from Previous DN to Change Number
240            // ============================================================================================
241            // EntryChangeNotification ::= SEQUENCE {
242            //     ...
243            //     changeNumber INTEGER OPTIONAL
244            // }
245            //
246            // Set the changeNumber into the structure
247            super.transitions[EntryChangeControlStatesEnum.PREVIOUS_DN_STATE][UniversalTag.INTEGER_TAG] = 
248                new GrammarTransition( EntryChangeControlStatesEnum.PREVIOUS_DN_STATE, 
249                                        EntryChangeControlStatesEnum.CHANGE_NUMBER_STATE, 
250                                        UniversalTag.INTEGER_TAG,
251                    setChangeNumberAction );
252    
253            // ============================================================================================
254            // Transition from Previous DN to Change Number
255            // ============================================================================================
256            // EntryChangeNotification ::= SEQUENCE {
257            //     ...
258            //     changeNumber INTEGER OPTIONAL
259            // }
260            //
261            // Set the changeNumber into the structure
262            super.transitions[EntryChangeControlStatesEnum.CHANGE_TYPE_STATE][UniversalTag.INTEGER_TAG] = 
263                new GrammarTransition( EntryChangeControlStatesEnum.CHANGE_TYPE_STATE, 
264                                        EntryChangeControlStatesEnum.CHANGE_NUMBER_STATE, 
265                                        UniversalTag.INTEGER_TAG,
266                    setChangeNumberAction );
267        }
268    
269    
270        /**
271         * This class is a singleton.
272         * 
273         * @return An instance on this grammar
274         */
275        public static IGrammar getInstance()
276        {
277            return instance;
278        }
279    }