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.pagedSearch;
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.grammar.IStates;
029    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
030    import org.apache.directory.shared.asn1.ber.tlv.Value;
031    import org.apache.directory.shared.asn1.codec.DecoderException;
032    import org.apache.directory.shared.asn1.util.IntegerDecoder;
033    import org.apache.directory.shared.asn1.util.IntegerDecoderException;
034    import org.apache.directory.shared.ldap.util.StringTools;
035    import org.slf4j.Logger;
036    import org.slf4j.LoggerFactory;
037    
038    
039    /**
040     * This class implements the PagedSearchControl. All the actions are declared in
041     * this class. As it is a singleton, these declaration are only done once.
042     * 
043     * The decoded grammar is the following :
044     * 
045     * realSearchControlValue ::= SEQUENCE {
046     *     size   INTEGER,
047     *     cookie OCTET STRING,
048     * }
049     * 
050     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051     * @version $Rev: 664290 $, $Date: 2008-06-07 08:28:06 +0200 (Sat, 07 Jun 2008) $, 
052     */
053    public class PagedSearchControlGrammar extends AbstractGrammar
054    {
055        /** The logger */
056        static final Logger log = LoggerFactory.getLogger( PagedSearchControlGrammar.class );
057    
058        /** Speedup for logs */
059        static final boolean IS_DEBUG = log.isDebugEnabled();
060    
061        /** The instance of grammar. PagedSearchControlGrammar is a singleton */
062        private static IGrammar instance = new PagedSearchControlGrammar();
063    
064    
065        /**
066         * Creates a new PagedSearchControlGrammar object.
067         */
068        private PagedSearchControlGrammar()
069        {
070            name = PagedSearchControlGrammar.class.getName();
071            statesEnum = PagedSearchControlStatesEnum.getInstance();
072    
073            // Create the transitions table
074            super.transitions = new GrammarTransition[PagedSearchControlStatesEnum.LAST_PAGED_SEARCH_STATE][256];
075    
076            /** 
077             * Transition from initial state to PagedSearch sequence
078             * realSearchControlValue ::= SEQUENCE OF {
079             *     ...
080             *     
081             * Initialize the persistence search object
082             */
083            super.transitions[IStates.INIT_GRAMMAR_STATE][UniversalTag.SEQUENCE_TAG] = 
084                new GrammarTransition( IStates.INIT_GRAMMAR_STATE, 
085                                        PagedSearchControlStatesEnum.PAGED_SEARCH_SEQUENCE_STATE, 
086                                        UniversalTag.SEQUENCE_TAG, 
087                    new GrammarAction( "Init PagedSearchControl" )
088                {
089                    public void action( IAsn1Container container )
090                    {
091                        PagedSearchControlContainer pagedSearchContainer = ( PagedSearchControlContainer ) container;
092                        PagedSearchControlCodec control = new PagedSearchControlCodec();
093                        pagedSearchContainer.setPagedSearchControl( control );
094                    }
095                } );
096    
097    
098            /** 
099             * Transition from PagedSearch sequence to size
100             * 
101             * realSearchControlValue ::= SEQUENCE OF {
102             *     size  INTEGER,  -- INTEGER (0..maxInt),
103             *     ...
104             *     
105             * Stores the size value
106             */
107            super.transitions[PagedSearchControlStatesEnum.PAGED_SEARCH_SEQUENCE_STATE][UniversalTag.INTEGER_TAG] = 
108                new GrammarTransition( PagedSearchControlStatesEnum.PAGED_SEARCH_SEQUENCE_STATE, 
109                    PagedSearchControlStatesEnum.SIZE_STATE, 
110                    UniversalTag.INTEGER_TAG,
111                    new GrammarAction( "Set PagedSearchControl size" )
112                {
113                    public void action( IAsn1Container container ) throws DecoderException
114                    {
115                        PagedSearchControlContainer pagedSearchContainer = ( PagedSearchControlContainer ) container;
116                        Value value = pagedSearchContainer.getCurrentTLV().getValue();
117    
118                        try
119                        {
120                            // Check that the value is into the allowed interval
121                            int size = IntegerDecoder.parse( value, Integer.MIN_VALUE, Integer.MAX_VALUE );
122                            
123                            // We allow negative value to absorb a bug in some M$ client.
124                            // Those negative values will be transformed to Integer.MAX_VALUE.
125                            if ( size < 0 )
126                            {
127                                size = Integer.MAX_VALUE;
128                            }
129                            
130                            if ( IS_DEBUG )
131                            {
132                                log.debug( "size = " + size );
133                            }
134    
135                            pagedSearchContainer.getPagedSearchControl().setSize( size );
136                        }
137                        catch ( IntegerDecoderException e )
138                        {
139                            String msg = "failed to decode the size for PagedSearchControl";
140                            log.error( msg, e );
141                            throw new DecoderException( msg );
142                        }
143                    }
144                } );
145    
146            /** 
147             * Transition from size to cookie
148             * realSearchControlValue ::= SEQUENCE OF {
149             *     ...
150             *     cookie   OCTET STRING
151             * }
152             *     
153             * Stores the cookie flag
154             */
155            super.transitions[PagedSearchControlStatesEnum.SIZE_STATE][UniversalTag.OCTET_STRING_TAG] = 
156                new GrammarTransition( PagedSearchControlStatesEnum.SIZE_STATE,
157                                        PagedSearchControlStatesEnum.COOKIE_STATE, UniversalTag.OCTET_STRING_TAG,
158                    new GrammarAction( "Set PagedSearchControl cookie" )
159                {
160                    public void action( IAsn1Container container ) throws DecoderException
161                    {
162                        PagedSearchControlContainer pagedSearchContainer = ( PagedSearchControlContainer ) container;
163                        Value value = pagedSearchContainer.getCurrentTLV().getValue();
164    
165                        if ( pagedSearchContainer.getCurrentTLV().getLength() == 0 )
166                        {
167                            pagedSearchContainer.getPagedSearchControl().setCookie( StringTools.EMPTY_BYTES );
168                        }
169                        else
170                        {
171                            pagedSearchContainer.getPagedSearchControl().setCookie( value.getData() );
172                        }
173    
174                        // We can have an END transition
175                        pagedSearchContainer.grammarEndAllowed( true );
176                    }
177                } );
178        }
179    
180    
181        /**
182         * This class is a singleton.
183         * 
184         * @return An instance on this grammar
185         */
186        public static IGrammar getInstance()
187        {
188            return instance;
189        }
190    }