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.asn1.codec.stateful;
021
022
023 import java.util.Stack;
024
025 import org.apache.directory.shared.asn1.codec.DecoderException;
026
027
028 /**
029 * A stack of decoders used for the additive application of multiple decoders
030 * forming a linear staged decoder pipeline.
031 *
032 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
033 * @version $Rev: 664290 $
034 */
035 public class DecoderStack extends AbstractStatefulDecoder
036 {
037 /**
038 * the top decoder callback which calls this decoders callback
039 *
040 * @todo determine if this is even necessary - can't we just use cb
041 */
042 private final DecoderCallback topcb;
043
044 /** a stack of StatefulDecoders */
045 private Stack<StatefulDecoder> decoders = new Stack<StatefulDecoder>();
046
047
048 /**
049 * Creates an empty stack of chained decoders.
050 */
051 public DecoderStack()
052 {
053 topcb = new DecoderCallback()
054 {
055 public void decodeOccurred( StatefulDecoder decoder, Object decoded )
056 {
057 DecoderStack.this.decodeOccurred( decoded );
058 }
059 };
060 }
061
062
063 /**
064 * Pushs a new terminal decoder onto the top of this DecoderStack. The old
065 * top decoder is chained to feed its decoded object to the new top decoder.
066 * The new pushed decoder will report decode events to this DecoderStacks
067 * callback.
068 *
069 * @param decoder
070 * the terminal decoder to push onto this stack
071 */
072 public synchronized void push( StatefulDecoder decoder )
073 {
074 decoder.setCallback( topcb );
075
076 if ( !decoders.isEmpty() )
077 {
078 StatefulDecoder top = decoders.peek();
079 ChainingCallback chaining = new ChainingCallback( top, decoder );
080 top.setCallback( chaining );
081 }
082
083 decoders.push( decoder );
084 }
085
086
087 /**
088 * Pops the terminal decoder off of this DecoderStack. The popped decoder
089 * has its callback cleared. If the stack is empty nothing happens and this
090 * StatefulDecoder, the DecoderStack, is returned to protect against null.
091 *
092 * @return the top decoder that was popped, or this DecoderStack
093 */
094 public synchronized StatefulDecoder pop()
095 {
096 if ( decoders.isEmpty() )
097 {
098 return this;
099 }
100
101 StatefulDecoder popped = decoders.pop();
102 popped.setCallback( null );
103
104 if ( !decoders.isEmpty() )
105 {
106 StatefulDecoder top = decoders.peek();
107 top.setCallback( this.topcb );
108 }
109
110 return popped;
111 }
112
113
114 /**
115 * Decodes an encoded object by calling decode on the decoder at the bottom
116 * of the stack. Callbacks are chained to feed the output of one decoder
117 * into the input decode method of another. If the stack is empty then the
118 * argument is delivered without change to this StatefulDecoder's callback.
119 *
120 * @param encoded an object representing a piece of encoded data
121 * @throws DecoderException if the encoded element can't be decoded
122 */
123 public synchronized void decode( Object encoded ) throws DecoderException
124 {
125 if ( decoders.isEmpty() )
126 {
127 decodeOccurred( encoded );
128 return;
129 }
130
131 decoders.get( 0 ).decode( encoded );
132 }
133
134
135 /**
136 * Gets whether or not this stack is empty.
137 *
138 * @return true if the stack is empty, false otherwise
139 */
140 public boolean isEmpty()
141 {
142 return decoders.isEmpty();
143 }
144
145
146 /**
147 * Clears the stack popping all decoders setting their callbacks to null.
148 */
149 public synchronized void clear()
150 {
151 while ( !decoders.isEmpty() )
152 {
153 pop();
154 }
155 }
156
157 /**
158 * A callback used to chain decoders.
159 *
160 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory
161 * Project</a>
162 * @version $Rev: 664290 $
163 */
164 class ChainingCallback implements DecoderCallback
165 {
166 /** the source decoder calling this callback */
167 private StatefulDecoder sink;
168
169 /** the sink decoder recieving the src's decoded object */
170 private StatefulDecoder src;
171
172
173 /**
174 * Creates a callback that chains the output of a src decoder to the
175 * input of a sink decoder. No side-effects occur like setting the
176 * callback of the src so this ChainingCallback must be set explicity as
177 * the src decoders callback.
178 *
179 * @param src
180 * the source decoder calling this callback
181 * @param sink
182 * the sink decoder recieving the src's decoded object
183 */
184 ChainingCallback(StatefulDecoder src, StatefulDecoder sink)
185 {
186 this.src = src;
187 this.sink = sink;
188 }
189
190
191 /**
192 * Calls the {@link #decode(Object)} method of the sink if the decoder
193 * argument is the source. Any failures that occur during the sink's
194 * decode operation are reported to the monitor first then rethrown as
195 * runtime exceptions with the root cause set to the faulting exception.
196 *
197 * @see org.apache.directory.shared.asn1.codec.stateful.DecoderCallback#decodeOccurred
198 * (org.apache.directory.shared.asn1.codec.stateful.StatefulDecoder,
199 * java.lang.Object)
200 */
201 public void decodeOccurred( StatefulDecoder decoder, Object decoded )
202 {
203 if ( decoder != src )
204 {
205 return;
206 }
207
208 try
209 {
210 sink.decode( decoded );
211 }
212 catch ( DecoderException e )
213 {
214 if ( getDecoderMonitor() != null )
215 {
216 getDecoderMonitor().fatalError( DecoderStack.this, e );
217 }
218
219 throw new RuntimeException( e );
220 }
221 }
222 }
223 }