KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > hessian > io > Hessian2Output


1 /*
2  * Copyright (c) 2001-2006 Caucho Technology, Inc. All rights reserved.
3  *
4  * The Apache Software License, Version 1.1
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution, if
19  * any, must include the following acknowlegement:
20  * "This product includes software developed by the
21  * Caucho Technology (http://www.caucho.com/)."
22  * Alternately, this acknowlegement may appear in the software itself,
23  * if and wherever such third-party acknowlegements normally appear.
24  *
25  * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * info@caucho.com.
29  *
30  * 5. Products derived from this software may not be called "Resin"
31  * nor may "Resin" appear in their names without prior written
32  * permission of Caucho Technology.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37  * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
38  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
39  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
40  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
43  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
44  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45  *
46  * @author Scott Ferguson
47  */

48
49 package com.caucho.hessian.io;
50
51 import com.caucho.hessian.util.IdentityIntMap;
52
53 import java.io.IOException JavaDoc;
54 import java.io.OutputStream JavaDoc;
55 import java.util.HashMap JavaDoc;
56
57 /**
58  * Output stream for Hessian 2 requests.
59  *
60  * <p>Since HessianOutput does not depend on any classes other than
61  * in the JDK, it can be extracted independently into a smaller package.
62  *
63  * <p>HessianOutput is unbuffered, so any client needs to provide
64  * its own buffering.
65  *
66  * <pre>
67  * OutputStream os = ...; // from http connection
68  * Hessian2Output out = new Hessian11Output(os);
69  * String value;
70  *
71  * out.startCall("hello"); // start hello call
72  * out.writeString("arg1"); // write a string argument
73  * out.completeCall(); // complete the call
74  * </pre>
75  */

76 public class Hessian2Output
77   extends AbstractHessianOutput
78   implements Hessian2Constants
79 {
80   // the output stream/
81
protected OutputStream JavaDoc _os;
82   
83   // map of references
84
private IdentityIntMap _refs = new IdentityIntMap();
85
86   private HashMap JavaDoc _serializerMap = new HashMap JavaDoc();
87   
88   // map of classes
89
private HashMap JavaDoc _classRefs;
90   
91   // map of types
92
private HashMap JavaDoc _typeRefs;
93
94   private final static int SIZE = 1024;
95   
96   private final byte []_buffer = new byte[SIZE];
97   private int _offset;
98   
99   /**
100    * Creates a new Hessian output stream, initialized with an
101    * underlying output stream.
102    *
103    * @param os the underlying output stream.
104    */

105   public Hessian2Output(OutputStream JavaDoc os)
106   {
107     _os = os;
108   }
109
110   /**
111    * Writes a complete method call.
112    */

113   public void call(String JavaDoc method, Object JavaDoc []args)
114     throws IOException JavaDoc
115   {
116     startCall(method);
117     
118     if (args != null) {
119       for (int i = 0; i < args.length; i++)
120         writeObject(args[i]);
121     }
122     
123     completeCall();
124   }
125   
126   /**
127    * Starts the method call. Clients would use <code>startCall</code>
128    * instead of <code>call</code> if they wanted finer control over
129    * writing the arguments, or needed to write headers.
130    *
131    * <code><pre>
132    * c major minor
133    * m b16 b8 method-name
134    * </pre></code>
135    *
136    * @param method the method name to call.
137    */

138   public void startCall(String JavaDoc method)
139     throws IOException JavaDoc
140   {
141     int offset = _offset;
142
143     if (SIZE < offset + 32) {
144       flush();
145       offset = 0;
146     }
147
148     byte []buffer = _buffer;
149     
150     buffer[offset++] = (byte) 'c';
151     buffer[offset++] = (byte) 2;
152     buffer[offset++] = (byte) 0;
153
154     buffer[offset++] = (byte) 'm';
155     int len = method.length();
156     buffer[offset++] = (byte) (len >> 8);
157     buffer[offset++] = (byte) len;
158
159     _offset = offset;
160     
161     printString(method, 0, len);
162   }
163
164   /**
165    * Writes the call tag. This would be followed by the
166    * headers and the method tag.
167    *
168    * <code><pre>
169    * c major minor
170    * </pre></code>
171    *
172    * @param method the method name to call.
173    */

174   public void startCall()
175     throws IOException JavaDoc
176   {
177     flushIfFull();
178     
179     int offset = _offset;
180     byte []buffer = _buffer;
181     
182     buffer[offset++] = (byte) 'c';
183     buffer[offset++] = (byte) 2;
184     buffer[offset++] = (byte) 0;
185   }
186
187   /**
188    * Writes the method tag.
189    *
190    * <code><pre>
191    * m b16 b8 method-name
192    * </pre></code>
193    *
194    * @param method the method name to call.
195    */

196   public void writeMethod(String JavaDoc method)
197     throws IOException JavaDoc
198   {
199     flushIfFull();
200
201     byte []buffer = _buffer;
202     int offset = _offset;
203     
204     buffer[offset++] = (byte) 'm';
205     int len = method.length();
206     buffer[offset++] = (byte) (len >> 8);
207     buffer[offset++] = (byte) len;
208
209     _offset = offset;
210     
211     printString(method, 0, len);
212   }
213
214   /**
215    * Completes.
216    *
217    * <code><pre>
218    * z
219    * </pre></code>
220    */

221   public void completeCall()
222     throws IOException JavaDoc
223   {
224     flushIfFull();
225     
226     _buffer[_offset++] = (byte) 'z';
227   }
228
229   /**
230    * Starts the reply
231    *
232    * <p>A successful completion will have a single value:
233    *
234    * <pre>
235    * r
236    * </pre>
237    */

238   public void startReply()
239     throws IOException JavaDoc
240   {
241     flushIfFull();
242     
243     _buffer[_offset++] = (byte) 'r';
244     _buffer[_offset++] = (byte) 2;
245     _buffer[_offset++] = (byte) 0;
246   }
247
248   /**
249    * Completes reading the reply
250    *
251    * <p>A successful completion will have a single value:
252    *
253    * <pre>
254    * z
255    * </pre>
256    */

257   public void completeReply()
258     throws IOException JavaDoc
259   {
260     flushIfFull();
261     
262     _buffer[_offset++] = (byte) 'z';
263   }
264
265   /**
266    * Writes a header name. The header value must immediately follow.
267    *
268    * <code><pre>
269    * H b16 b8 foo <em>value</em>
270    * </pre></code>
271    */

272   public void writeHeader(String JavaDoc name)
273     throws IOException JavaDoc
274   {
275     int len = name.length();
276
277     flushIfFull();
278     
279     _buffer[_offset++] = (byte) 'H';
280     _buffer[_offset++] = (byte) (len >> 8);
281     _buffer[_offset++] = (byte) (len);
282
283     printString(name);
284   }
285
286   /**
287    * Writes a fault. The fault will be written
288    * as a descriptive string followed by an object:
289    *
290    * <code><pre>
291    * f
292    * &lt;string>code
293    * &lt;string>the fault code
294    *
295    * &lt;string>message
296    * &lt;string>the fault mesage
297    *
298    * &lt;string>detail
299    * mt\x00\xnnjavax.ejb.FinderException
300    * ...
301    * z
302    * z
303    * </pre></code>
304    *
305    * @param code the fault code, a three digit
306    */

307   public void writeFault(String JavaDoc code, String JavaDoc message, Object JavaDoc detail)
308     throws IOException JavaDoc
309   {
310     flushIfFull();
311     
312     _buffer[_offset++] = (byte) 'f'
313       ;
314     writeString("code");
315     writeString(code);
316
317     writeString("message");
318     writeString(message);
319
320     if (detail != null) {
321       writeString("detail");
322       writeObject(detail);
323     }
324
325     flushIfFull();
326     _buffer[_offset++] = (byte) ('z');
327   }
328
329   /**
330    * Writes any object to the output stream.
331    */

332   public void writeObject(Object JavaDoc object)
333     throws IOException JavaDoc
334   {
335     if (object == null) {
336       writeNull();
337       return;
338     }
339
340     Serializer serializer;
341
342     serializer = findSerializerFactory().getSerializer(object.getClass());
343
344     serializer.writeObject(object, this);
345   }
346
347   /**
348    * Writes the list header to the stream. List writers will call
349    * <code>writeListBegin</code> followed by the list contents and then
350    * call <code>writeListEnd</code>.
351    *
352    * <code><pre>
353    * V
354    * t b16 b8 type
355    * l b32 b24 b16 b8
356    * </pre></code>
357    */

358   public boolean writeListBegin(int length, String JavaDoc type)
359     throws IOException JavaDoc
360   {
361     flushIfFull();
362
363     if (_typeRefs != null) {
364       Integer JavaDoc refV = (Integer JavaDoc) _typeRefs.get(type);
365
366       if (refV != null) {
367     _buffer[_offset++] = (byte) (LIST_FIXED);
368     writeInt(refV.intValue());
369     writeInt(length);
370
371     return false;
372       }
373     }
374     
375     _buffer[_offset++] = (byte) 'V';
376
377     writeType(type);
378
379     flushIfFull();
380
381     if (length < 0) {
382     }
383     else if (length < 0x100) {
384       _buffer[_offset++] = (byte) (LENGTH_BYTE);
385       _buffer[_offset++] = (byte) (length);
386     }
387     else {
388       _buffer[_offset++] = (byte) ('l');
389       _buffer[_offset++] = (byte) (length >> 24);
390       _buffer[_offset++] = (byte) (length >> 16);
391       _buffer[_offset++] = (byte) (length >> 8);
392       _buffer[_offset++] = (byte) (length);
393     }
394
395     return true;
396   }
397
398   /**
399    * Writes the tail of the list to the stream.
400    */

401   public void writeListEnd()
402     throws IOException JavaDoc
403   {
404     flushIfFull();
405     
406     _buffer[_offset++] = (byte) 'z';
407   }
408
409   /**
410    * Writes the map header to the stream. Map writers will call
411    * <code>writeMapBegin</code> followed by the map contents and then
412    * call <code>writeMapEnd</code>.
413    *
414    * <code><pre>
415    * Mt b16 b8 (<key> <value>)z
416    * </pre></code>
417    */

418   public void writeMapBegin(String JavaDoc type)
419     throws IOException JavaDoc
420   {
421     if (SIZE < _offset + 32)
422       flush();
423     
424     _buffer[_offset++] = 'M';
425
426     writeType(type);
427   }
428
429   /**
430    * Writes the tail of the map to the stream.
431    */

432   public void writeMapEnd()
433     throws IOException JavaDoc
434   {
435     if (SIZE < _offset + 32)
436       flush();
437     
438     _buffer[_offset++] = (byte) 'z';
439   }
440
441   /**
442    * Writes the object header to the stream.
443    *
444    * <code><pre>
445    * Ot b16 b8 <key>* Z <value>* z
446    * </pre></code>
447    */

448   public int writeObjectBegin(String JavaDoc type)
449     throws IOException JavaDoc
450   {
451     if (_classRefs == null)
452       _classRefs = new HashMap JavaDoc();
453
454     Integer JavaDoc refV = (Integer JavaDoc) _classRefs.get(type);
455
456     if (refV != null) {
457       int ref = refV.intValue();
458       
459       if (SIZE < _offset + 32)
460     flush();
461
462       _buffer[_offset++] = (byte) 'o';
463       writeInt(ref);
464
465       return ref;
466     }
467     else {
468       int ref = _classRefs.size() + 1;
469       
470       _classRefs.put(type, new Integer JavaDoc(ref));
471       
472       if (SIZE < _offset + 32)
473     flush();
474
475       _buffer[_offset++] = (byte) 'O';
476
477       int len = type.length();
478       writeInt(len);
479       printString(type, 0, len);
480
481       return 0;
482     }
483   }
484
485   /**
486    * Writes the tail of the class definition to the stream.
487    */

488   public void writeClassFieldLength(int len)
489     throws IOException JavaDoc
490   {
491     writeInt(len);
492   }
493
494   /**
495    * Writes the tail of the object definition to the stream.
496    */

497   public void writeObjectEnd()
498     throws IOException JavaDoc
499   {
500   }
501
502   /**
503    * Writes a remote object reference to the stream. The type is the
504    * type of the remote interface.
505    *
506    * <code><pre>
507    * 'r' 't' b16 b8 type url
508    * </pre></code>
509    */

510   public void writeRemote(String JavaDoc type, String JavaDoc url)
511     throws IOException JavaDoc
512   {
513     if (SIZE < _offset + 32)
514       flush();
515
516     _buffer[_offset++] = (byte) 'r';
517
518     writeType(type);
519
520     if (SIZE < _offset + 32)
521       flush();
522
523     _buffer[_offset++] = (byte) 'S';
524     
525     printLenString(url);
526   }
527
528   private void writeType(String JavaDoc type)
529     throws IOException JavaDoc
530   {
531     if (type == null)
532       return;
533
534     int len = type.length();
535     if (len == 0)
536       return;
537
538     if (_typeRefs == null)
539       _typeRefs = new HashMap JavaDoc();
540
541     Integer JavaDoc typeRefV = (Integer JavaDoc) _typeRefs.get(type);
542     
543     if (typeRefV != null) {
544       int typeRef = typeRefV.intValue();
545       
546       flushIfFull();
547       
548       _buffer[_offset++] = (byte) TYPE_REF;
549       
550       writeInt(typeRef);
551     }
552     else {
553       _typeRefs.put(type, new Integer JavaDoc(_typeRefs.size()));
554
555       if (SIZE < _offset + 32)
556     flush();
557       
558       _buffer[_offset++] = (byte) 't';
559       
560       printLenString(type);
561     }
562   }
563
564   /**
565    * Writes a boolean value to the stream. The boolean will be written
566    * with the following syntax:
567    *
568    * <code><pre>
569    * T
570    * F
571    * </pre></code>
572    *
573    * @param value the boolean value to write.
574    */

575   public void writeBoolean(boolean value)
576     throws IOException JavaDoc
577   {
578     if (SIZE < _offset + 16)
579       flush();
580
581     if (value)
582       _buffer[_offset++] = (byte) 'T';
583     else
584       _buffer[_offset++] = (byte) 'F';
585   }
586
587   /**
588    * Writes an integer value to the stream. The integer will be written
589    * with the following syntax:
590    *
591    * <code><pre>
592    * I b32 b24 b16 b8
593    * </pre></code>
594    *
595    * @param value the integer value to write.
596    */

597   public void writeInt(int value)
598     throws IOException JavaDoc
599   {
600     int offset = _offset;
601     byte []buffer = _buffer;
602
603     if (SIZE <= offset + 16) {
604       flush();
605       offset = 0;
606     }
607     
608     if (INT_DIRECT_MIN <= value && value <= INT_DIRECT_MAX)
609       buffer[offset++] = (byte) (value + INT_ZERO);
610     else if (INT_BYTE_MIN <= value && value <= INT_BYTE_MAX) {
611       buffer[offset++] = (byte) (INT_BYTE_ZERO + (value >> 8));
612       buffer[offset++] = (byte) (value);
613     }
614     else if (INT_SHORT_MIN <= value && value <= INT_SHORT_MAX) {
615       buffer[offset++] = (byte) (INT_SHORT_ZERO + (value >> 16));
616       buffer[offset++] = (byte) (value >> 8);
617       buffer[offset++] = (byte) (value);
618     }
619     else {
620       buffer[offset++] = (byte) ('I');
621       buffer[offset++] = (byte) (value >> 24);
622       buffer[offset++] = (byte) (value >> 16);
623       buffer[offset++] = (byte) (value >> 8);
624       buffer[offset++] = (byte) (value);
625     }
626
627     _offset = offset;
628   }
629
630   /**
631    * Writes a long value to the stream. The long will be written
632    * with the following syntax:
633    *
634    * <code><pre>
635    * L b64 b56 b48 b40 b32 b24 b16 b8
636    * </pre></code>
637    *
638    * @param value the long value to write.
639    */

640   public void writeLong(long value)
641     throws IOException JavaDoc
642   {
643     int offset = _offset;
644     byte []buffer = _buffer;
645
646     if (SIZE <= offset + 16) {
647       flush();
648       offset = 0;
649     }
650
651     if (LONG_DIRECT_MIN <= value && value <= LONG_DIRECT_MAX) {
652       buffer[offset++] = (byte) (value + LONG_ZERO);
653     }
654     else if (LONG_BYTE_MIN <= value && value <= LONG_BYTE_MAX) {
655       buffer[offset++] = (byte) (LONG_BYTE_ZERO + (value >> 8));
656       buffer[offset++] = (byte) (value);
657     }
658     else if (LONG_SHORT_MIN <= value && value <= LONG_SHORT_MAX) {
659       buffer[offset++] = (byte) (LONG_SHORT_ZERO + (value >> 16));
660       buffer[offset++] = (byte) (value >> 8);
661       buffer[offset++] = (byte) (value);
662     }
663     else if (-0x80000000L <= value && value <= 0x7fffffffL) {
664       buffer[offset + 0] = (byte) LONG_INT;
665       buffer[offset + 1] = (byte) (value >> 24);
666       buffer[offset + 2] = (byte) (value >> 16);
667       buffer[offset + 3] = (byte) (value >> 8);
668       buffer[offset + 4] = (byte) (value);
669
670       offset += 5;
671     }
672     else {
673       buffer[offset + 0] = (byte) 'L';
674       buffer[offset + 1] = (byte) (value >> 56);
675       buffer[offset + 2] = (byte) (value >> 48);
676       buffer[offset + 3] = (byte) (value >> 40);
677       buffer[offset + 4] = (byte) (value >> 32);
678       buffer[offset + 5] = (byte) (value >> 24);
679       buffer[offset + 6] = (byte) (value >> 16);
680       buffer[offset + 7] = (byte) (value >> 8);
681       buffer[offset + 8] = (byte) (value);
682
683       offset += 9;
684     }
685
686     _offset = offset;
687   }
688
689   /**
690    * Writes a double value to the stream. The double will be written
691    * with the following syntax:
692    *
693    * <code><pre>
694    * D b64 b56 b48 b40 b32 b24 b16 b8
695    * </pre></code>
696    *
697    * @param value the double value to write.
698    */

699   public void writeDouble(double value)
700     throws IOException JavaDoc
701   {
702     int offset = _offset;
703     byte []buffer = _buffer;
704
705     if (SIZE <= offset + 16) {
706       flush();
707       offset = 0;
708     }
709     
710     int intValue = (int) value;
711     
712     if (intValue == value) {
713       if (intValue == 0)
714     buffer[offset++] = (byte) DOUBLE_ZERO;
715       else if (intValue == 1)
716     buffer[offset++] = (byte) DOUBLE_ONE;
717       else if (-0x80 <= intValue && intValue < 0x80) {
718     buffer[offset++] = (byte) DOUBLE_BYTE;
719     buffer[offset++] = (byte) intValue;
720       }
721       else if (-0x8000 <= intValue && intValue < 0x8000) {
722     buffer[offset + 0] = (byte) DOUBLE_SHORT;
723     buffer[offset + 1] = (byte) (intValue >> 8);
724     buffer[offset + 2] = (byte) intValue;
725
726     offset += 3;
727       }
728       else {
729     buffer[offset + 0] = (byte) DOUBLE_INT;
730     buffer[offset + 1] = (byte) (intValue >> 24);
731     buffer[offset + 2] = (byte) (intValue >> 16);
732     buffer[offset + 3] = (byte) (intValue >> 8);
733     buffer[offset + 4] = (byte) intValue;
734
735     offset += 5;
736       }
737
738       _offset = offset;
739       
740       return;
741     }
742
743     double d256 = 256 * value;
744     int i256 = (int) d256;
745
746     if (d256 == i256 && -0x8000 <= i256 && i256 < 0x8000) {
747       buffer[offset + 0] = (byte) (DOUBLE_256_SHORT);
748       buffer[offset + 1] = (byte) (i256 >> 8);
749       buffer[offset + 2] = (byte) (i256);
750
751       _offset = offset + 3;
752
753       return;
754     }
755
756     float f = (float) value;
757
758     if (f == value) {
759       int bits = Float.floatToIntBits(f);
760       
761       buffer[offset + 0] = (byte) (DOUBLE_FLOAT);
762       buffer[offset + 1] = (byte) (bits >> 24);
763       buffer[offset + 2] = (byte) (bits >> 16);
764       buffer[offset + 3] = (byte) (bits >> 8);
765       buffer[offset + 4] = (byte) (bits);
766
767       _offset = offset + 5;
768
769       return;
770     }
771     
772     long bits = Double.doubleToLongBits(value);
773     
774     buffer[offset + 0] = (byte) 'D';
775     buffer[offset + 1] = (byte) (bits >> 56);
776     buffer[offset + 2] = (byte) (bits >> 48);
777     buffer[offset + 3] = (byte) (bits >> 40);
778     buffer[offset + 4] = (byte) (bits >> 32);
779     buffer[offset + 5] = (byte) (bits >> 24);
780     buffer[offset + 6] = (byte) (bits >> 16);
781     buffer[offset + 7] = (byte) (bits >> 8);
782     buffer[offset + 8] = (byte) (bits);
783
784     _offset = offset + 9;
785   }
786
787   /**
788    * Writes a date to the stream.
789    *
790    * <code><pre>
791    * T b64 b56 b48 b40 b32 b24 b16 b8
792    * </pre></code>
793    *
794    * @param time the date in milliseconds from the epoch in UTC
795    */

796   public void writeUTCDate(long time)
797     throws IOException JavaDoc
798   {
799     if (SIZE < _offset + 32)
800       flush();
801
802     int offset = _offset;
803     byte []buffer = _buffer;
804     
805     buffer[offset++] = (byte) ('d');
806     buffer[offset++] = ((byte) (time >> 56));
807     buffer[offset++] = ((byte) (time >> 48));
808     buffer[offset++] = ((byte) (time >> 40));
809     buffer[offset++] = ((byte) (time >> 32));
810     buffer[offset++] = ((byte) (time >> 24));
811     buffer[offset++] = ((byte) (time >> 16));
812     buffer[offset++] = ((byte) (time >> 8));
813     buffer[offset++] = ((byte) (time));
814
815     _offset = offset;
816   }
817
818   /**
819    * Writes a null value to the stream.
820    * The null will be written with the following syntax
821    *
822    * <code><pre>
823    * N
824    * </pre></code>
825    *
826    * @param value the string value to write.
827    */

828   public void writeNull()
829     throws IOException JavaDoc
830   {
831     int offset = _offset;
832     byte []buffer = _buffer;
833
834     if (SIZE <= offset + 16) {
835       flush();
836       offset = 0;
837     }
838
839     buffer[offset++] = 'N';
840
841     _offset = offset;
842   }
843
844   /**
845    * Writes a string value to the stream using UTF-8 encoding.
846    * The string will be written with the following syntax:
847    *
848    * <code><pre>
849    * S b16 b8 string-value
850    * </pre></code>
851    *
852    * If the value is null, it will be written as
853    *
854    * <code><pre>
855    * N
856    * </pre></code>
857    *
858    * @param value the string value to write.
859    */

860   public void writeString(String JavaDoc value)
861     throws IOException JavaDoc
862   {
863     int offset = _offset;
864     byte []buffer = _buffer;
865
866     if (SIZE <= offset + 16) {
867       flush();
868       offset = 0;
869     }
870     
871     if (value == null) {
872       buffer[offset++] = (byte) 'N';
873
874       _offset = offset;
875     }
876     else {
877       int length = value.length();
878       int strOffset = 0;
879       
880       while (length > 0x8000) {
881         int sublen = 0x8000;
882
883     offset = _offset;
884
885     if (SIZE <= offset + 16) {
886       flush();
887       offset = 0;
888     }
889
890     // chunk can't end in high surrogate
891
char tail = value.charAt(strOffset + sublen - 1);
892
893     if (0xd800 <= tail && tail <= 0xdbff)
894       sublen--;
895
896     buffer[offset + 0] = (byte) 's';
897         buffer[offset + 1] = (byte) (sublen >> 8);
898         buffer[offset + 2] = (byte) (sublen);
899
900     _offset = offset + 3;
901
902         printString(value, strOffset, sublen);
903
904         length -= sublen;
905         strOffset += sublen;
906       }
907
908       offset = _offset;
909
910       if (SIZE <= offset + 16) {
911     flush();
912     offset = 0;
913       }
914
915       if (length <= STRING_DIRECT_MAX) {
916     buffer[offset++] = (byte) (STRING_DIRECT + length);
917       }
918       else {
919     buffer[offset++] = (byte) ('S');
920     buffer[offset++] = (byte) (length >> 8);
921     buffer[offset++] = (byte) (length);
922       }
923
924       _offset = offset;
925
926       printString(value, strOffset, length);
927     }
928   }
929
930   /**
931    * Writes a string value to the stream using UTF-8 encoding.
932    * The string will be written with the following syntax:
933    *
934    * <code><pre>
935    * S b16 b8 string-value
936    * </pre></code>
937    *
938    * If the value is null, it will be written as
939    *
940    * <code><pre>
941    * N
942    * </pre></code>
943    *
944    * @param value the string value to write.
945    */

946   public void writeString(char []buffer, int offset, int length)
947     throws IOException JavaDoc
948   {
949     if (buffer == null) {
950       if (SIZE < _offset + 16)
951     flush();
952       
953       _buffer[_offset++] = (byte) ('N');
954     }
955     else {
956       while (length > 0x8000) {
957         int sublen = 0x8000;
958
959     if (SIZE < _offset + 16)
960       flush();
961
962     // chunk can't end in high surrogate
963
char tail = buffer[offset + sublen - 1];
964
965     if (0xd800 <= tail && tail <= 0xdbff)
966       sublen--;
967     
968         _buffer[_offset++] = (byte) 's';
969         _buffer[_offset++] = (byte) (sublen >> 8);
970         _buffer[_offset++] = (byte) (sublen);
971
972         printString(buffer, offset, sublen);
973
974         length -= sublen;
975         offset += sublen;
976       }
977
978       if (SIZE < _offset + 16)
979     flush();
980     
981       if (length <= STRING_DIRECT_MAX) {
982     _buffer[_offset++] = (byte) (STRING_DIRECT + length);
983       }
984       else {
985     _buffer[_offset++] = (byte) ('S');
986     _buffer[_offset++] = (byte) (length >> 8);
987     _buffer[_offset++] = (byte) (length);
988       }
989
990       printString(buffer, offset, length);
991     }
992   }
993
994   /**
995    * Writes a byte array to the stream.
996    * The array will be written with the following syntax:
997    *
998    * <code><pre>
999    * B b16 b18 bytes
1000   * </pre></code>
1001   *
1002   * If the value is null, it will be written as
1003   *
1004   * <code><pre>
1005   * N
1006   * </pre></code>
1007   *
1008   * @param value the string value to write.
1009   */

1010  public void writeBytes(byte []buffer)
1011    throws IOException JavaDoc
1012  {
1013    if (buffer == null) {
1014      if (SIZE < _offset + 16)
1015    flush();
1016
1017      _buffer[_offset++] = 'N';
1018    }
1019    else
1020      writeBytes(buffer, 0, buffer.length);
1021  }
1022  
1023  /**
1024   * Writes a byte array to the stream.
1025   * The array will be written with the following syntax:
1026   *
1027   * <code><pre>
1028   * B b16 b18 bytes
1029   * </pre></code>
1030   *
1031   * If the value is null, it will be written as
1032   *
1033   * <code><pre>
1034   * N
1035   * </pre></code>
1036   *
1037   * @param value the string value to write.
1038   */

1039  public void writeBytes(byte []buffer, int offset, int length)
1040    throws IOException JavaDoc
1041  {
1042    if (buffer == null) {
1043      if (SIZE < _offset + 16)
1044    flush();
1045      
1046      _buffer[_offset++] = (byte) 'N';
1047    }
1048    else {
1049      flush();
1050      
1051      while (length > 0x8000) {
1052        int sublen = 0x8000;
1053        
1054        _os.write('b');
1055        _os.write(sublen >> 8);
1056        _os.write(sublen);
1057
1058        _os.write(buffer, offset, sublen);
1059
1060        length -= sublen;
1061        offset += sublen;
1062      }
1063
1064      if (length < 0x10) {
1065    _os.write(BYTES_DIRECT + length);
1066      }
1067      else {
1068    _os.write('B');
1069    _os.write(length >> 8);
1070    _os.write(length);
1071      }
1072      
1073      _os.write(buffer, offset, length);
1074    }
1075  }
1076  
1077  /**
1078   * Writes a byte buffer to the stream.
1079   *
1080   * <code><pre>
1081   * </pre></code>
1082   */

1083  public void writeByteBufferStart()
1084    throws IOException JavaDoc
1085  {
1086  }
1087  
1088  /**
1089   * Writes a byte buffer to the stream.
1090   *
1091   * <code><pre>
1092   * b b16 b18 bytes
1093   * </pre></code>
1094   */

1095  public void writeByteBufferPart(byte []buffer, int offset, int length)
1096    throws IOException JavaDoc
1097  {
1098    while (length > 0) {
1099      int sublen = length;
1100
1101      if (0x8000 < sublen)
1102    sublen = 0x8000;
1103
1104      flush(); // bypass buffer
1105

1106      _os.write('b');
1107      _os.write(sublen >> 8);
1108      _os.write(sublen);
1109
1110      _os.write(buffer, offset, sublen);
1111
1112      length -= sublen;
1113      offset += sublen;
1114    }
1115  }
1116  
1117  /**
1118   * Writes a byte buffer to the stream.
1119   *
1120   * <code><pre>
1121   * b b16 b18 bytes
1122   * </pre></code>
1123   */

1124  public void writeByteBufferEnd(byte []buffer, int offset, int length)
1125    throws IOException JavaDoc
1126  {
1127    writeBytes(buffer, offset, length);
1128  }
1129
1130  /**
1131   * Writes a reference.
1132   *
1133   * <code><pre>
1134   * R b32 b24 b16 b8
1135   * </pre></code>
1136   *
1137   * @param value the integer value to write.
1138   */

1139  public void writeRef(int value)
1140    throws IOException JavaDoc
1141  {
1142    if (SIZE < _offset + 16)
1143      flush();
1144    
1145    if (value < 0x100) {
1146      _buffer[_offset++] = (byte) (REF_BYTE);
1147      _buffer[_offset++] = (byte) (value);
1148    }
1149    else if (value < 0x10000) {
1150      _buffer[_offset++] = (byte) (REF_SHORT);
1151      _buffer[_offset++] = (byte) (value);
1152    }
1153    else {
1154      _buffer[_offset++] = (byte) ('R');
1155      _buffer[_offset++] = (byte) (value >> 24);
1156      _buffer[_offset++] = (byte) (value >> 16);
1157      _buffer[_offset++] = (byte) (value >> 8);
1158      _buffer[_offset++] = (byte) (value);
1159    }
1160  }
1161
1162  /**
1163   * If the object has already been written, just write its ref.
1164   *
1165   * @return true if we're writing a ref.
1166   */

1167  public boolean addRef(Object JavaDoc object)
1168    throws IOException JavaDoc
1169  {
1170    int ref = _refs.get(object);
1171
1172    if (ref >= 0) {
1173      writeRef(ref);
1174      
1175      return true;
1176    }
1177    else {
1178      _refs.put(object, _refs.size());
1179      
1180      return false;
1181    }
1182  }
1183
1184  /**
1185   * Removes a reference.
1186   */

1187  public boolean removeRef(Object JavaDoc obj)
1188    throws IOException JavaDoc
1189  {
1190    if (_refs != null) {
1191      _refs.remove(obj);
1192
1193      return true;
1194    }
1195    else
1196      return false;
1197  }
1198
1199  /**
1200   * Replaces a reference from one object to another.
1201   */

1202  public boolean replaceRef(Object JavaDoc oldRef, Object JavaDoc newRef)
1203    throws IOException JavaDoc
1204  {
1205    Integer JavaDoc value = (Integer JavaDoc) _refs.remove(oldRef);
1206
1207    if (value != null) {
1208      _refs.put(newRef, value);
1209      return true;
1210    }
1211    else
1212      return false;
1213  }
1214
1215  /**
1216   * Prints a string to the stream, encoded as UTF-8 with preceeding length
1217   *
1218   * @param v the string to print.
1219   */

1220  public void printLenString(String JavaDoc v)
1221    throws IOException JavaDoc
1222  {
1223    if (SIZE < _offset + 16)
1224      flush();
1225    
1226    if (v == null) {
1227      _buffer[_offset++] = (byte) (0);
1228      _buffer[_offset++] = (byte) (0);
1229    }
1230    else {
1231      int len = v.length();
1232      _buffer[_offset++] = (byte) (len >> 8);
1233      _buffer[_offset++] = (byte) (len);
1234
1235      printString(v, 0, len);
1236    }
1237  }
1238
1239  /**
1240   * Prints a string to the stream, encoded as UTF-8
1241   *
1242   * @param v the string to print.
1243   */

1244  public void printString(String JavaDoc v)
1245    throws IOException JavaDoc
1246  {
1247    printString(v, 0, v.length());
1248  }
1249  
1250  /**
1251   * Prints a string to the stream, encoded as UTF-8
1252   *
1253   * @param v the string to print.
1254   */

1255  public void printString(String JavaDoc v, int strOffset, int length)
1256    throws IOException JavaDoc
1257  {
1258    int offset = _offset;
1259    byte []buffer = _buffer;
1260    
1261    for (int i = 0; i < length; i++) {
1262      if (SIZE <= offset + 16) {
1263    _offset = offset;
1264    flush();
1265    offset = 0;
1266      }
1267      
1268      char ch = v.charAt(i + strOffset);
1269
1270      if (ch < 0x80)
1271        buffer[offset++] = (byte) (ch);
1272      else if (ch < 0x800) {
1273        buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
1274        buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1275      }
1276      else {
1277        buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf));
1278        buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
1279        buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1280      }
1281    }
1282
1283    _offset = offset;
1284  }
1285  
1286  /**
1287   * Prints a string to the stream, encoded as UTF-8
1288   *
1289   * @param v the string to print.
1290   */

1291  public void printString(char []v, int strOffset, int length)
1292    throws IOException JavaDoc
1293  {
1294    int offset = _offset;
1295    byte []buffer = _buffer;
1296    
1297    for (int i = 0; i < length; i++) {
1298      if (SIZE <= offset + 16) {
1299    _offset = offset;
1300    flush();
1301    offset = 0;
1302      }
1303      
1304      char ch = v[i + strOffset];
1305
1306      if (ch < 0x80)
1307        buffer[offset++] = (byte) (ch);
1308      else if (ch < 0x800) {
1309        buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
1310        buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1311      }
1312      else {
1313        buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf));
1314        buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
1315        buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
1316      }
1317    }
1318
1319    _offset = offset;
1320  }
1321  
1322  private final void flushIfFull()
1323    throws IOException JavaDoc
1324  {
1325    int offset = _offset;
1326    
1327    if (SIZE < offset + 32) {
1328      _offset = 0;
1329      _os.write(_buffer, 0, offset);
1330    }
1331  }
1332
1333  public final void flush()
1334    throws IOException JavaDoc
1335  {
1336    int offset = _offset;
1337    
1338    if (offset > 0) {
1339      _offset = 0;
1340      _os.write(_buffer, 0, offset);
1341    }
1342  }
1343
1344  public final void close()
1345    throws IOException JavaDoc
1346  {
1347    int offset = _offset;
1348    
1349    if (offset > 0) {
1350      _offset = 0;
1351      _os.write(_buffer, 0, offset);
1352    }
1353  }
1354}
1355
Popular Tags