View Javadoc

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 }