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 }