View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.asn1.codec.stateful ;
18  
19  
20  import java.util.Stack;
21  
22  import org.apache.asn1.codec.DecoderException;
23  
24  
25  /***
26   * A stack of decoders used for the additive application of multiple decoders 
27   * forming a linear staged decoder pipeline.
28   *
29   * @author <a href="mailto:dev@directory.apache.org">
30   * Apache Directory Project</a>
31   * @version $Rev: 161723 $
32   */
33  public class DecoderStack extends AbstractStatefulDecoder
34  {
35      /*** 
36       * the top decoder callback which calls this decoders callback
37       * @todo determine if this is even necessary - can't we just use cb   
38       */
39      private final DecoderCallback topcb ;
40      /*** a stack of StatefulDecoders */
41      private Stack decoders = new Stack() ;
42      
43      
44      /***
45       * Creates an empty stack of chained decoders. 
46       */
47      public DecoderStack()
48      {
49          topcb = new DecoderCallback()
50          {
51              public void decodeOccurred( StatefulDecoder decoder,Object decoded )
52              {
53                  DecoderStack.this.decodeOccurred( decoded ) ;
54              }
55          };
56      }
57      
58      
59      /***
60       * Pushs a new terminal decoder onto the top of this DecoderStack.  The 
61       * old top decoder is chained to feed its decoded object to the new top
62       * decoder.  The new pushed decoder will report decode events to this
63       * DecoderStacks callback.
64       * 
65       * @param decoder the terminal decoder to push onto this stack
66       */
67      public synchronized void push( StatefulDecoder decoder )
68      {
69          decoder.setCallback( topcb ) ;
70  
71          if( ! decoders.isEmpty() )
72          {
73              StatefulDecoder top = ( StatefulDecoder ) decoders.peek() ;
74              ChainingCallback chaining = new ChainingCallback( top, decoder ) ;
75              top.setCallback( chaining ) ;
76          }
77          
78          decoders.push( decoder ) ;
79      }
80      
81      
82      /***
83       * Pops the terminal decoder off of this DecoderStack.  The popped decoder
84       * has its callback cleared.  If the stack is empty nothing happens and this
85       * StatefulDecoder, the DecoderStack, is returned to protect against null.
86       * 
87       * @return the top decoder that was popped, or this DecoderStack
88       */
89      public synchronized StatefulDecoder pop()
90      {
91          if ( decoders.isEmpty() )
92          {
93              return this ;
94          }
95          
96          StatefulDecoder popped = ( StatefulDecoder ) decoders.pop() ;
97          popped.setCallback( null ) ;
98          
99          if ( ! decoders.isEmpty() )
100         {
101             StatefulDecoder top = ( StatefulDecoder ) decoders.peek() ;
102             top.setCallback( this.topcb ) ;
103         }
104         
105         return popped ;
106     }
107 
108     
109     /***
110      * Decodes an encoded object by calling decode on the decoder at the bottom
111      * of the stack.  Callbacks are chained to feed the output of one decoder
112      * into the input decode method of another.  If the stack is empty then the
113      * arguement is delivered without change to this StatefulDecoder's callback.
114      * 
115      * @see org.apache.asn1.codec.stateful.StatefulDecoder#
116      * decode(java.lang.Object)
117      */
118     public synchronized void decode( Object encoded ) throws DecoderException
119     {
120         if ( decoders.isEmpty() )
121         {
122             decodeOccurred( encoded ) ;
123             return ;
124         }
125         
126         ( ( StatefulDecoder ) decoders.get( 0 ) ).decode( encoded ) ;
127     }
128     
129     
130     /***
131      * Gets whether or not this stack is empty.
132      * 
133      * @return true if the stack is empty, false otherwise
134      */
135     public boolean isEmpty()
136     {
137         return decoders.isEmpty() ;
138     }
139     
140     
141     /***
142      * Clears the stack popping all decoders setting their callbacks to null.
143      */
144     public synchronized void clear()
145     {
146         while ( ! decoders.isEmpty() )
147         {
148             pop() ;
149         }
150     }
151     
152     
153     /***
154      * A callback used to chain decoders.
155      *
156      * @author <a href="mailto:dev@directory.apache.org">
157      * Apache Directory Project</a>
158      * @version $Rev: 161723 $
159      */
160     class ChainingCallback implements DecoderCallback
161     {
162         /*** the source decoder calling this callback */
163         private StatefulDecoder sink ;
164         /*** the sink decoder recieving the src's decoded object */
165         private StatefulDecoder src ;
166         
167         /***
168          * Creates a callback that chains the output of a src decoder to the 
169          * input of a sink decoder.  No side-effects occur like setting the 
170          * callback of the src so this ChainingCallback must be set explicity
171          * as the src decoders callback.
172          * 
173          * @param src the source decoder calling this callback
174          * @param sink the sink decoder recieving the src's decoded object
175          */
176         ChainingCallback( StatefulDecoder src, StatefulDecoder sink )
177         {
178             this.src = src ;
179             this.sink = sink ;
180         }
181         
182         
183         /***
184          * Calls the {@link #decode(Object)} method of the sink if the decoder 
185          * argument is the source.  Any failures that occur during the sink's 
186          * decode operation are reported to the monitor first then rethrown as
187          * runtime exceptions with the root cause set to the faulting exception.
188          * 
189          * @see org.apache.asn1.codec.stateful.DecoderCallback#decodeOccurred
190          * (org.apache.asn1.codec.stateful.StatefulDecoder, java.lang.Object)
191          */
192         public void decodeOccurred( StatefulDecoder decoder, Object decoded )
193         {
194             if ( decoder != src )
195             {
196                 return ;
197             }
198             
199             try
200             {
201                 sink.decode( decoded ) ;
202             }
203             catch( DecoderException e )
204             {
205                 if ( getDecoderMonitor() != null )
206                 {    
207                     getDecoderMonitor().fatalError( DecoderStack.this, e ) ;
208                 }
209                 
210                 throw new RuntimeException( e ) ;
211             }
212         }
213     }
214 }