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