1 /*
2 * Copyright 2004-2005 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.ber;
18
19
20 import java.nio.ByteBuffer;
21
22 import org.apache.asn1.codec.stateful.AbstractStatefulEncoder;
23
24
25 /***
26 * A BER TLV tuple encoder. This encoder receives events via a
27 * BEREncoderCallback. Hence the callback is used to deliver events to this
28 * encoder or event consumer. The product is announced via an regular encoder
29 * event.
30 *
31 * @note We tried the route of using a BEREncoderCallback here and well it was
32 * ugly the event consumer producer based mechanism was a much better more
33 * specific approach. Plus it got confusing: we started confusing callbacks.
34 * There was a callback for getting event then another callback for producing
35 * them. It got confusing fast.
36 *
37 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory
38 * Project</a> $Rev: 157644 $
39 */
40 public class BEREncoder extends AbstractStatefulEncoder
41 implements TupleEventConsumer
42 {
43 private static final int DEFAULT_BUFSZ = 32;
44 private ByteBuffer buf = null;
45
46
47 /***
48 * Creates a BEREncoder with the default buffer size.
49 */
50 public BEREncoder()
51 {
52 this( DEFAULT_BUFSZ );
53 }
54
55
56 /***
57 * Creates a BEREncoder with a specific buffer size.
58 *
59 * @param bufSz the size of the buffer
60 */
61 public BEREncoder( int bufSz )
62 {
63 buf = ByteBuffer.allocateDirect( bufSz );
64 }
65
66
67 /***
68 * Overriden encode method which does nothing but throw an exception. This
69 * has been done to prevent interference with the stream of TLV events
70 * processed by this encoder. A special BEREncoderCallback implementation
71 * is used by this class to recieve events. This callback is not the same
72 * as the callback used to inform of encode events which emits ByteBuffer
73 * objects.
74 *
75 * @param obj the object to encode
76 * @throws UnsupportedOperationException every time
77 */
78 public void encode( Object obj )
79 {
80 throw new UnsupportedOperationException(
81 "This encoder receives tuples ONLY via callback methods" );
82 }
83
84
85 /*
86 * The idea here is to see if we can encode the tag bytes into the
87 * remaining bytes of the buffer. If we can then great we do so.
88 * If we cannot then we have to flush out the full buffer with an
89 * encodeOccurred Event. This signals the production of encoded data
90 * and free's up the buffer to have the tag bytes writen to it.
91 */
92 public void tag( Tuple tlv )
93 {
94 if ( buf.remaining() >= tlv.getTagLength() )
95 {
96 tlv.setTag( buf, tlv.getTagLength() );
97 }
98 else
99 {
100 buf.flip();
101 encodeOccurred( buf );
102 buf.clear();
103 tlv.setTag( buf, tlv.getTagLength() );
104 }
105 }
106
107
108 /*
109 * Again we have the same dynamic where we no encode the lenght bytes
110 * into the remaining bytes of the buffer. If we can then great we do
111 * so. If we cannot then we have to flush out the full buffer with an
112 * encodeOccurred Event. This signals the production of encoded data
113 * and free's up the buffer to have the length bytes writen to it.
114 */
115 public void length( Tuple tlv )
116 {
117 if ( buf.remaining() >= tlv.getLengthLength() )
118 {
119 tlv.setLength( buf, tlv.getLengthLength() );
120 }
121 else
122 {
123 buf.flip();
124 encodeOccurred( buf );
125 buf.clear();
126 tlv.setLength( buf, tlv.getLengthLength() );
127 }
128 }
129
130
131 /*
132 * Here the situation is a little different. The values are already
133 * chunked so there is no need to copy them into a buffer. We are
134 * best off passing through this buffer to consumers with an encode but
135 * before we do that we need to check if the present buffer contains
136 * any material that must go out the door first. Doing this prevents
137 * us from mangling the order of bytes to send. So if our buf contains
138 * any bytes from previous operations laying down the tag and length
139 * then we must flush it out. Then we can flush out this chunk.
140 */
141 public void chunkedValue( Tuple tlv, ByteBuffer chunk )
142 {
143 if ( buf.position() > 0 )
144 {
145 buf.flip();
146 BEREncoder.this.encodeOccurred( buf );
147 buf.clear();
148 }
149
150 encodeOccurred( tlv.getLastValueChunk() );
151 }
152
153
154 /*
155 * Keep in mind this method signals the end of a Tuple. It is called
156 * upstream from us by a higher level encoder that generates tuple
157 * streams from objects. This method simply returns if the object is
158 * a primitive Tuple because all value processing has already occurred
159 * for that tuple. If on the otherhand the tuple is constructed and of
160 * the indefinite form need to write the termination sequence (two
161 * zeros) down into the stream. We attempt to do this into the buffer.
162 * If the buffer is full we flush is with an encodeOccurred() event.
163 * Then we write the termination sequence into the buffer and flush
164 * the buffer with an encodeOccurred Event.
165 */
166 public void finish( Tuple tlv )
167 {
168 if ( tlv.isPrimitive() )
169 {
170 return;
171 }
172
173 if ( tlv.isIndefinite() )
174 {
175 if ( buf.remaining() < 2 )
176 {
177 buf.flip();
178 encodeOccurred( buf );
179 buf.clear();
180 }
181
182 buf.put( (byte) 0 );
183 buf.put( (byte) 0 );
184 buf.flip();
185 encodeOccurred( buf );
186 buf.clear();
187 }
188 }
189 }