1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 }