CDROutputStream_1_0.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * RMI-IIOP v1.0
28 * Copyright IBM Corp. 1998 1999  All Rights Reserved
29 *
30 */
31
32package com.sun.corba.se.impl.encoding;
33
34import java.io.ByteArrayOutputStream;
35import java.io.IOException;
36import java.io.Serializable;
37import java.io.ByteArrayOutputStream;
38import java.io.ObjectOutputStream;
39import java.io.IOException;
40import java.lang.reflect.Method;
41import java.lang.reflect.InvocationTargetException;
42import java.math.BigDecimal;
43import java.nio.ByteBuffer;
44import java.rmi.Remote;
45import java.security.AccessController;
46import java.security.PrivilegedExceptionAction;
47import java.security.PrivilegedActionException;
48import java.util.Hashtable;
49import java.util.Stack;
50
51import javax.rmi.CORBA.Util;
52import javax.rmi.CORBA.ValueHandler;
53import javax.rmi.CORBA.ValueHandlerMultiFormat;
54
55import org.omg.CORBA.CustomMarshal;
56import org.omg.CORBA.DataOutputStream;
57import org.omg.CORBA.TypeCodePackage.BadKind;
58import org.omg.CORBA.SystemException;
59import org.omg.CORBA.CompletionStatus;
60import org.omg.CORBA.Object;
61import org.omg.CORBA.Principal;
62import org.omg.CORBA.TypeCode;
63import org.omg.CORBA.Any;
64import org.omg.CORBA.VM_CUSTOM;
65import org.omg.CORBA.VM_TRUNCATABLE;
66import org.omg.CORBA.VM_NONE;
67import org.omg.CORBA.portable.IDLEntity;
68import org.omg.CORBA.portable.CustomValue;
69import org.omg.CORBA.portable.StreamableValue;
70import org.omg.CORBA.portable.BoxedValueHelper;
71import org.omg.CORBA.portable.OutputStream;
72import org.omg.CORBA.portable.ValueBase;
73
74import com.sun.org.omg.CORBA.portable.ValueHelper;
75
76import com.sun.corba.se.pept.protocol.MessageMediator;
77import com.sun.corba.se.pept.transport.ByteBufferPool;
78
79import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
80import com.sun.corba.se.spi.ior.IOR;
81import com.sun.corba.se.spi.ior.IORFactories;
82import com.sun.corba.se.spi.orb.ORB;
83import com.sun.corba.se.spi.orb.ORBVersionFactory;
84import com.sun.corba.se.spi.orb.ORBVersion;
85import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
86import com.sun.corba.se.spi.logging.CORBALogDomains;
87
88import com.sun.corba.se.impl.encoding.ByteBufferWithInfo;
89import com.sun.corba.se.impl.encoding.MarshalOutputStream;
90import com.sun.corba.se.impl.encoding.CodeSetConversion;
91import com.sun.corba.se.impl.corba.TypeCodeImpl;
92import com.sun.corba.se.impl.orbutil.CacheTable;
93import com.sun.corba.se.impl.orbutil.ORBUtility;
94import com.sun.corba.se.impl.orbutil.RepositoryIdStrings;
95import com.sun.corba.se.impl.orbutil.RepositoryIdUtility;
96import com.sun.corba.se.impl.orbutil.RepositoryIdFactory;
97import com.sun.corba.se.impl.util.Utility;
98import com.sun.corba.se.impl.logging.ORBUtilSystemException;
99
100public class CDROutputStream_1_0 extends CDROutputStreamBase
101{
102    private static final int INDIRECTION_TAG = 0xffffffff;
103
104    protected boolean littleEndian;
105    protected BufferManagerWrite bufferManagerWrite;
106    ByteBufferWithInfo bbwi;
107
108    protected ORB orb;
109    protected ORBUtilSystemException wrapper ;
110
111    protected boolean debug = false;
112
113    protected int blockSizeIndex = -1;
114    protected int blockSizePosition = 0;
115
116    protected byte streamFormatVersion;
117
118    private static final int DEFAULT_BUFFER_SIZE = 1024;
119    private static final String kWriteMethod = "write";
120
121    // Codebase cache
122    private CacheTable codebaseCache = null;
123
124    // Value cache
125    private CacheTable valueCache = null;
126
127    // Repository ID cache
128    private CacheTable repositoryIdCache = null;
129
130    // Write end flag
131    private int end_flag = 0;
132
133    // Beginning with the resolution to interop issue 3526,
134    // only enclosing chunked valuetypes are taken into account
135    // when computing the nesting level.  However, we still need
136    // the old computation around for interoperability with our
137    // older ORBs.
138    private int chunkedValueNestingLevel = 0;
139
140    private boolean mustChunk = false;
141
142    // In block marker
143    protected boolean inBlock = false;
144
145    // Last end tag position
146    private int end_flag_position = 0;
147    private int end_flag_index = 0;
148
149    // ValueHandler
150    private ValueHandler valueHandler = null;
151
152    // Repository ID handlers
153    private RepositoryIdUtility repIdUtil;
154    private RepositoryIdStrings repIdStrs;
155
156    // Code set converters (created when first needed)
157    private CodeSetConversion.CTBConverter charConverter;
158    private CodeSetConversion.CTBConverter wcharConverter;
159
160    // REVISIT - This should be re-factored so that including whether
161    // to use pool byte buffers or not doesn't need to be known.
162    public void init(org.omg.CORBA.ORB orb,
163                        boolean littleEndian,
164                        BufferManagerWrite bufferManager,
165                        byte streamFormatVersion,
166                        boolean usePooledByteBuffers)
167    {
168        // ORB must not be null.  See CDROutputStream constructor.
169        this.orb = (ORB)orb;
170        this.wrapper = ORBUtilSystemException.get( this.orb,
171            CORBALogDomains.RPC_ENCODING ) ;
172        debug = this.orb.transportDebugFlag;
173
174        this.littleEndian = littleEndian;
175        this.bufferManagerWrite = bufferManager;
176        this.bbwi = new ByteBufferWithInfo(orb, bufferManager, usePooledByteBuffers);
177        this.streamFormatVersion = streamFormatVersion;
178
179        createRepositoryIdHandlers();
180    }
181
182    public void init(org.omg.CORBA.ORB orb,
183                        boolean littleEndian,
184                        BufferManagerWrite bufferManager,
185                        byte streamFormatVersion)
186   {
187       init(orb, littleEndian, bufferManager, streamFormatVersion, true);
188   }
189
190    private final void createRepositoryIdHandlers()
191    {
192        repIdUtil = RepositoryIdFactory.getRepIdUtility();
193        repIdStrs = RepositoryIdFactory.getRepIdStringsFactory();
194    }
195
196    public BufferManagerWrite getBufferManager()
197    {
198        return bufferManagerWrite;
199    }
200
201    public byte[] toByteArray() {
202        byte[] it;
203
204        it = new byte[bbwi.position()];
205
206        // Micro-benchmarks show ByteBuffer.get(int) out perform the bulk
207        // ByteBuffer.get(byte[], offset, length).
208        for (int i = 0; i < bbwi.position(); i++)
209            it[i] = bbwi.byteBuffer.get(i);
210
211        return it;
212    }
213
214    public GIOPVersion getGIOPVersion() {
215        return GIOPVersion.V1_0;
216    }
217
218    // Called by Request and Reply message. Valid for GIOP versions >= 1.2 only.
219    // Illegal for GIOP versions < 1.2.
220    void setHeaderPadding(boolean headerPadding) {
221        throw wrapper.giopVersionError();
222    }
223
224    protected void handleSpecialChunkBegin(int requiredSize)
225    {
226        // No-op for GIOP 1.0
227    }
228
229    protected void handleSpecialChunkEnd()
230    {
231        // No-op for GIOP 1.0
232    }
233
234    protected final int computeAlignment(int align) {
235        if (align > 1) {
236            int incr = bbwi.position() & (align - 1);
237            if (incr != 0)
238                return align - incr;
239        }
240
241        return 0;
242    }
243
244    protected void alignAndReserve(int align, int n) {
245
246        bbwi.position(bbwi.position() + computeAlignment(align));
247
248        if (bbwi.position() + n  > bbwi.buflen)
249            grow(align, n);
250    }
251
252    //
253    // Default implementation of grow.  Subclassers may override this.
254    // Always grow the single buffer. This needs to delegate
255    // fragmentation policy for IIOP 1.1.
256    //
257    protected void grow(int align, int n)
258    {
259        bbwi.needed = n;
260
261        bufferManagerWrite.overflow(bbwi);
262    }
263
264    public final void putEndian() throws SystemException {
265        write_boolean(littleEndian);
266    }
267
268    public final boolean littleEndian() {
269        return littleEndian;
270    }
271
272    void freeInternalCaches() {
273        if (codebaseCache != null)
274            codebaseCache.done();
275
276        if (valueCache != null)
277            valueCache.done();
278
279        if (repositoryIdCache != null)
280            repositoryIdCache.done();
281    }
282
283    // No such type in java
284    public final void write_longdouble(double x)
285    {
286        throw wrapper.longDoubleNotImplemented(
287            CompletionStatus.COMPLETED_MAYBE ) ;
288    }
289
290    public void write_octet(byte x)
291    {
292        // The 'if' stmt is commented out since we need the alignAndReserve to
293        // be called, particularly when the first body byte is written,
294        // to induce header padding to align the body on a 8-octet boundary,
295        // for GIOP versions 1.2 and above. Refer to internalWriteOctetArray()
296        // method that also has a similar change.
297        //if (bbwi.position() + 1 > bbwi.buflen)
298            alignAndReserve(1, 1);
299
300//      REVISIT - Should just use ByteBuffer.put(byte) and let it
301//                increment the ByteBuffer position. This is true
302//                for all write operations in this file.
303
304        bbwi.byteBuffer.put(bbwi.position(), x);
305        bbwi.position(bbwi.position() + 1);
306
307    }
308
309    public final void write_boolean(boolean x)
310    {
311        write_octet(x? (byte)1:(byte)0);
312    }
313
314    public void write_char(char x)
315    {
316        CodeSetConversion.CTBConverter converter = getCharConverter();
317
318        converter.convert(x);
319
320        // CORBA formal 99-10-07 15.3.1.6: "In the case of multi-byte encodings
321        // of characters, a single instance of the char type may only
322        // hold one octet of any multi-byte character encoding."
323        if (converter.getNumBytes() > 1)
324            throw wrapper.invalidSingleCharCtb(CompletionStatus.COMPLETED_MAYBE);
325
326        write_octet(converter.getBytes()[0]);
327    }
328
329    // These wchar methods are only used when talking to
330    // legacy ORBs, now.
331    private final void writeLittleEndianWchar(char x) {
332        bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF));
333        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF));
334        bbwi.position(bbwi.position() + 2);
335    }
336
337    private final void writeBigEndianWchar(char x) {
338        bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 8) & 0xFF));
339        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)(x & 0xFF));
340        bbwi.position(bbwi.position() + 2);
341    }
342
343    private final void writeLittleEndianShort(short x) {
344        bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF));
345        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF));
346        bbwi.position(bbwi.position() + 2);
347    }
348
349    private final void writeBigEndianShort(short x) {
350        bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 8) & 0xFF));
351        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)(x & 0xFF));
352        bbwi.position(bbwi.position() + 2);
353    }
354
355    private final void writeLittleEndianLong(int x) {
356        bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF));
357        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF));
358        bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 16) & 0xFF));
359        bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 24) & 0xFF));
360        bbwi.position(bbwi.position() + 4);
361    }
362
363    private final void writeBigEndianLong(int x) {
364        bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 24) & 0xFF));
365        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 16) & 0xFF));
366        bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 8) & 0xFF));
367        bbwi.byteBuffer.put(bbwi.position() + 3, (byte)(x & 0xFF));
368        bbwi.position(bbwi.position() + 4);
369    }
370
371    private final void writeLittleEndianLongLong(long x) {
372        bbwi.byteBuffer.put(bbwi.position(), (byte)(x & 0xFF));
373        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 8) & 0xFF));
374        bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 16) & 0xFF));
375        bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 24) & 0xFF));
376        bbwi.byteBuffer.put(bbwi.position() + 4, (byte)((x >>> 32) & 0xFF));
377        bbwi.byteBuffer.put(bbwi.position() + 5, (byte)((x >>> 40) & 0xFF));
378        bbwi.byteBuffer.put(bbwi.position() + 6, (byte)((x >>> 48) & 0xFF));
379        bbwi.byteBuffer.put(bbwi.position() + 7, (byte)((x >>> 56) & 0xFF));
380        bbwi.position(bbwi.position() + 8);
381    }
382
383    private final void writeBigEndianLongLong(long x) {
384        bbwi.byteBuffer.put(bbwi.position(), (byte)((x >>> 56) & 0xFF));
385        bbwi.byteBuffer.put(bbwi.position() + 1, (byte)((x >>> 48) & 0xFF));
386        bbwi.byteBuffer.put(bbwi.position() + 2, (byte)((x >>> 40) & 0xFF));
387        bbwi.byteBuffer.put(bbwi.position() + 3, (byte)((x >>> 32) & 0xFF));
388        bbwi.byteBuffer.put(bbwi.position() + 4, (byte)((x >>> 24) & 0xFF));
389        bbwi.byteBuffer.put(bbwi.position() + 5, (byte)((x >>> 16) & 0xFF));
390        bbwi.byteBuffer.put(bbwi.position() + 6, (byte)((x >>> 8) & 0xFF));
391        bbwi.byteBuffer.put(bbwi.position() + 7, (byte)(x & 0xFF));
392        bbwi.position(bbwi.position() + 8);
393    }
394
395    public void write_wchar(char x)
396    {
397        // Don't allow transmission of wchar/wstring data with
398        // foreign ORBs since it's against the spec.
399        if (ORBUtility.isForeignORB(orb)) {
400            throw wrapper.wcharDataInGiop10(CompletionStatus.COMPLETED_MAYBE);
401        }
402
403        // If it's one of our legacy ORBs, do what they did:
404        alignAndReserve(2, 2);
405
406        if (littleEndian) {
407            writeLittleEndianWchar(x);
408        } else {
409            writeBigEndianWchar(x);
410        }
411    }
412
413    public void write_short(short x)
414    {
415        alignAndReserve(2, 2);
416
417        if (littleEndian) {
418            writeLittleEndianShort(x);
419        } else {
420            writeBigEndianShort(x);
421        }
422    }
423
424    public final void write_ushort(short x)
425    {
426        write_short(x);
427    }
428
429    public void write_long(int x)
430    {
431        alignAndReserve(4, 4);
432
433        if (littleEndian) {
434            writeLittleEndianLong(x);
435        } else {
436            writeBigEndianLong(x);
437        }
438    }
439
440    public final void write_ulong(int x)
441    {
442        write_long(x);
443    }
444
445    public void write_longlong(long x)
446    {
447        alignAndReserve(8, 8);
448
449        if (littleEndian) {
450            writeLittleEndianLongLong(x);
451        } else {
452            writeBigEndianLongLong(x);
453        }
454    }
455
456    public final void write_ulonglong(long x)
457    {
458        write_longlong(x);
459    }
460
461    public final void write_float(float x)
462    {
463        write_long(Float.floatToIntBits(x));
464    }
465
466    public final void write_double(double x)
467    {
468        write_longlong(Double.doubleToLongBits(x));
469    }
470
471    public void write_string(String value)
472    {
473      writeString(value);
474    }
475
476    protected int writeString(String value)
477    {
478        if (value == null) {
479            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
480        }
481
482        CodeSetConversion.CTBConverter converter = getCharConverter();
483
484        converter.convert(value);
485
486        // A string is encoded as an unsigned CORBA long for the
487        // number of bytes to follow (including a terminating null).
488        // There is only one octet per character in the string.
489        int len = converter.getNumBytes() + 1;
490
491        handleSpecialChunkBegin(computeAlignment(4) + 4 + len);
492
493        write_long(len);
494        int indirection = get_offset() - 4;
495
496        internalWriteOctetArray(converter.getBytes(), 0, converter.getNumBytes());
497
498        // Write the null ending
499        write_octet((byte)0);
500
501        handleSpecialChunkEnd();
502        return indirection;
503    }
504
505    public void write_wstring(String value)
506    {
507        if (value == null)
508            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
509
510        // Don't allow transmission of wchar/wstring data with
511        // foreign ORBs since it's against the spec.
512        if (ORBUtility.isForeignORB(orb)) {
513            throw wrapper.wcharDataInGiop10(CompletionStatus.COMPLETED_MAYBE);
514        }
515
516        // When talking to our legacy ORBs, do what they did:
517        int len = value.length() + 1;
518
519        // This will only have an effect if we're already chunking
520        handleSpecialChunkBegin(4 + (len * 2) + computeAlignment(4));
521
522        write_long(len);
523
524        for (int i = 0; i < len - 1; i++)
525            write_wchar(value.charAt(i));
526
527        // Write the null ending
528        write_short((short)0);
529
530        // This will only have an effect if we're already chunking
531        handleSpecialChunkEnd();
532    }
533
534    // Performs no checks and doesn't tamper with chunking
535    void internalWriteOctetArray(byte[] value, int offset, int length)
536    {
537        int n = offset;
538
539        // This flag forces the alignAndReserve method to be called the
540        // first time an octet is written. This is necessary to ensure
541        // that the body is aligned on an 8-octet boundary. Note the 'if'
542        // condition inside the 'while' loop below. Also, refer to the
543        // write_octet() method that has a similar change.
544        boolean align = true;
545
546        while (n < length+offset) {
547            int avail;
548            int bytes;
549            int wanted;
550
551            if ((bbwi.position() + 1 > bbwi.buflen) || align) {
552                align = false;
553                alignAndReserve(1, 1);
554            }
555            avail = bbwi.buflen - bbwi.position();
556            wanted = (length + offset) - n;
557            bytes = (wanted < avail) ? wanted : avail;
558            for (int i = 0; i < bytes; i++)
559                bbwi.byteBuffer.put(bbwi.position() + i, value[n+i]);
560            bbwi.position(bbwi.position() + bytes);
561            n += bytes;
562        }
563    }
564
565    public final void write_octet_array(byte b[], int offset, int length)
566    {
567        if ( b == null )
568            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
569
570        // This will only have an effect if we're already chunking
571        handleSpecialChunkBegin(length);
572
573        internalWriteOctetArray(b, offset, length);
574
575        // This will only have an effect if we're already chunking
576        handleSpecialChunkEnd();
577    }
578
579    public void write_Principal(Principal p)
580    {
581        write_long(p.name().length);
582        write_octet_array(p.name(), 0, p.name().length);
583    }
584
585    public void write_any(Any any)
586    {
587        if ( any == null )
588            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
589
590        write_TypeCode(any.type());
591        any.write_value(parent);
592    }
593
594    public void write_TypeCode(TypeCode tc)
595    {
596        if ( tc == null ) {
597            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
598        }
599        TypeCodeImpl tci;
600        if (tc instanceof TypeCodeImpl) {
601            tci = (TypeCodeImpl)tc;
602        }
603        else {
604            tci = new TypeCodeImpl(orb, tc);
605        }
606
607        tci.write_value((org.omg.CORBA_2_3.portable.OutputStream)parent);
608    }
609
610    public void write_Object(org.omg.CORBA.Object ref)
611    {
612        if (ref == null) {
613            IOR nullIOR = IORFactories.makeIOR( orb ) ;
614            nullIOR.write(parent);
615            return;
616        }
617
618        // IDL to Java formal 01-06-06 1.21.4.2
619        if (ref instanceof org.omg.CORBA.LocalObject)
620            throw wrapper.writeLocalObject(CompletionStatus.COMPLETED_MAYBE);
621
622        IOR ior = ORBUtility.connectAndGetIOR( orb, ref ) ;
623        ior.write(parent);
624        return;
625    }
626
627    // ------------ RMI related methods --------------------------
628
629    public void write_abstract_interface(java.lang.Object obj) {
630        boolean corbaObject = false; // Assume value type.
631        org.omg.CORBA.Object theObject = null;
632
633        // Is it a CORBA.Object?
634
635        if (obj != null && obj instanceof org.omg.CORBA.Object) {
636
637            // Yes.
638
639            theObject = (org.omg.CORBA.Object)obj;
640            corbaObject = true;
641        }
642
643        // Write our flag...
644
645        write_boolean(corbaObject);
646
647        // Now write out the object...
648
649        if (corbaObject) {
650            write_Object(theObject);
651        } else {
652            try {
653                write_value((java.io.Serializable)obj);
654            } catch(ClassCastException cce) {
655                if (obj instanceof java.io.Serializable)
656                    throw cce;
657                else
658                    ORBUtility.throwNotSerializableForCorba(obj.getClass().getName());
659            }
660        }
661    }
662
663    public void write_value(Serializable object, Class clz) {
664
665        write_value(object);
666    }
667
668    private void writeWStringValue(String string) {
669
670        int indirection = writeValueTag(mustChunk, true, null);
671
672        // Write WStringValue's repository ID
673        write_repositoryId(repIdStrs.getWStringValueRepId());
674
675        // Add indirection for object to indirection table
676        updateIndirectionTable(indirection, string, string);
677
678        // Write Value chunk
679        if (mustChunk) {
680            start_block();
681            end_flag--;
682            chunkedValueNestingLevel--;
683        } else
684            end_flag--;
685
686        write_wstring(string);
687
688        if (mustChunk)
689            end_block();
690
691        // Write end tag
692        writeEndTag(mustChunk);
693    }
694
695    private void writeArray(Serializable array, Class clazz) {
696
697        if (valueHandler == null)
698            valueHandler = ORBUtility.createValueHandler(); //d11638
699
700        // Write value_tag
701        int indirection = writeValueTag(mustChunk, true,
702                                        Util.getCodebase(clazz));
703
704        // Write repository ID
705        write_repositoryId(repIdStrs.createSequenceRepID(clazz));
706
707        // Add indirection for object to indirection table
708        updateIndirectionTable(indirection, array, array);
709
710        // Write Value chunk
711        if (mustChunk) {
712            start_block();
713            end_flag--;
714            chunkedValueNestingLevel--;
715        } else
716            end_flag--;
717
718        if (valueHandler instanceof ValueHandlerMultiFormat) {
719            ValueHandlerMultiFormat vh = (ValueHandlerMultiFormat)valueHandler;
720            vh.writeValue(parent, array, streamFormatVersion);
721        } else
722            valueHandler.writeValue(parent, array);
723
724        if (mustChunk)
725            end_block();
726
727        // Write end tag
728        writeEndTag(mustChunk);
729    }
730
731    private void writeValueBase(org.omg.CORBA.portable.ValueBase object,
732                                Class clazz) {
733        // _REVISIT_ could check to see whether chunking really needed
734        mustChunk = true;
735
736        // Write value_tag
737        int indirection = writeValueTag(true, true, Util.getCodebase(clazz));
738
739        // Get rep id
740        String repId = ((ValueBase)object)._truncatable_ids()[0];
741
742        // Write rep id
743        write_repositoryId(repId);
744
745        // Add indirection for object to indirection table
746        updateIndirectionTable(indirection, object, object);
747
748        // Write Value chunk
749        start_block();
750        end_flag--;
751        chunkedValueNestingLevel--;
752        writeIDLValue(object, repId);
753        end_block();
754
755        // Write end tag
756        writeEndTag(true);
757    }
758
759    private void writeRMIIIOPValueType(Serializable object, Class clazz) {
760        if (valueHandler == null)
761            valueHandler = ORBUtility.createValueHandler(); //d11638
762
763        Serializable key = object;
764
765        // Allow the ValueHandler to call writeReplace on
766        // the Serializable (if the method is present)
767        object = valueHandler.writeReplace(key);
768
769        if (object == null) {
770            // Write null tag and return
771            write_long(0);
772            return;
773        }
774
775        if (object != key) {
776            if (valueCache != null && valueCache.containsKey(object)) {
777                writeIndirection(INDIRECTION_TAG, valueCache.getVal(object));
778                return;
779            }
780
781            clazz = object.getClass();
782        }
783
784        if (mustChunk || valueHandler.isCustomMarshaled(clazz)) {
785            mustChunk = true;
786        }
787
788        // Write value_tag
789        int indirection = writeValueTag(mustChunk, true, Util.getCodebase(clazz));
790
791        // Write rep. id
792        write_repositoryId(repIdStrs.createForJavaType(clazz));
793
794        // Add indirection for object to indirection table
795        updateIndirectionTable(indirection, object, key);
796
797        if (mustChunk) {
798            // Write Value chunk
799            end_flag--;
800            chunkedValueNestingLevel--;
801            start_block();
802        } else
803            end_flag--;
804
805        if (valueHandler instanceof ValueHandlerMultiFormat) {
806            ValueHandlerMultiFormat vh = (ValueHandlerMultiFormat)valueHandler;
807            vh.writeValue(parent, object, streamFormatVersion);
808        } else
809            valueHandler.writeValue(parent, object);
810
811        if (mustChunk)
812            end_block();
813
814        // Write end tag
815        writeEndTag(mustChunk);
816    }
817
818    public void write_value(Serializable object, String repository_id) {
819
820        // Handle null references
821        if (object == null) {
822            // Write null tag and return
823            write_long(0);
824            return;
825        }
826
827        // Handle shared references
828        if (valueCache != null && valueCache.containsKey(object)) {
829            writeIndirection(INDIRECTION_TAG, valueCache.getVal(object));
830            return;
831        }
832
833        Class clazz = object.getClass();
834        boolean oldMustChunk = mustChunk;
835
836        if (mustChunk)
837            mustChunk = true;
838
839        if (inBlock)
840            end_block();
841
842        if (clazz.isArray()) {
843            // Handle arrays
844            writeArray(object, clazz);
845        } else if (object instanceof org.omg.CORBA.portable.ValueBase) {
846            // Handle IDL Value types
847            writeValueBase((org.omg.CORBA.portable.ValueBase)object, clazz);
848        } else if (shouldWriteAsIDLEntity(object)) {
849            writeIDLEntity((IDLEntity)object);
850        } else if (object instanceof java.lang.String) {
851            writeWStringValue((String)object);
852        } else if (object instanceof java.lang.Class) {
853            writeClass(repository_id, (Class)object);
854        } else {
855            // RMI-IIOP value type
856            writeRMIIIOPValueType(object, clazz);
857        }
858
859        mustChunk = oldMustChunk;
860
861        // Check to see if we need to start another block for a
862        // possible outer value
863        if (mustChunk)
864            start_block();
865
866    }
867
868    public void write_value(Serializable object)
869    {
870        write_value(object, (String)null);
871    }
872
873    public void write_value(Serializable object, org.omg.CORBA.portable.BoxedValueHelper factory)
874    {
875        // Handle null references
876        if (object == null) {
877            // Write null tag and return
878            write_long(0);
879            return;
880        }
881
882        // Handle shared references
883        if ((valueCache != null) && valueCache.containsKey(object)) {
884            writeIndirection(INDIRECTION_TAG, valueCache.getVal(object));
885            return;
886        }
887
888        boolean oldMustChunk = mustChunk;
889
890        boolean isCustom = false;
891        if (factory instanceof ValueHelper) {
892            short modifier;
893            try {
894                modifier = ((ValueHelper)factory).get_type().type_modifier();
895            } catch(BadKind ex) {  // tk_value_box
896                modifier = VM_NONE.value;
897            }
898            if (object instanceof CustomMarshal &&
899                modifier == VM_CUSTOM.value) {
900                isCustom = true;
901                mustChunk = true;
902            }
903            if (modifier == VM_TRUNCATABLE.value)
904                mustChunk = true;
905        }
906
907        if (mustChunk) {
908
909            if (inBlock)
910                end_block();
911
912            // Write value_tag
913            int indirection = writeValueTag(true,
914                                            orb.getORBData().useRepId(),
915                                            Util.getCodebase(object.getClass())
916                                           );
917
918            if (orb.getORBData().useRepId()) {
919                write_repositoryId(factory.get_id());
920            }
921
922            // Add indirection for object to indirection table
923            updateIndirectionTable(indirection, object, object);
924
925            // Write Value chunk
926            start_block();
927            end_flag--;
928            chunkedValueNestingLevel--;
929            if (isCustom)
930                ((CustomMarshal)object).marshal(parent);
931            else
932                factory.write_value(parent, object);
933            end_block();
934
935            // Write end tag
936            writeEndTag(true);
937        }
938        else {
939            // Write value_tag
940            int indirection = writeValueTag(false,
941                                            orb.getORBData().useRepId(),
942                                            Util.getCodebase(object.getClass())
943                                           );
944
945            if (orb.getORBData().useRepId()) {
946                write_repositoryId(factory.get_id());
947            }
948
949            // Add indirection for object to indirection table
950            updateIndirectionTable(indirection, object, object);
951
952            // Write Value chunk
953            end_flag--;
954            // no need to test for custom on the non-chunked path
955            factory.write_value(parent, object);
956
957            // Write end tag
958            writeEndTag(false);
959        }
960
961        mustChunk = oldMustChunk;
962
963        // Check to see if we need to start another block for a
964        // possible outer value
965        if (mustChunk)
966            start_block();
967
968    }
969
970    public int get_offset() {
971        return bbwi.position();
972    }
973
974    public void start_block() {
975        if (debug) {
976            dprint("CDROutputStream_1_0 start_block, position" + bbwi.position());
977        }
978
979        //Move inBlock=true to after write_long since write_long might
980        //trigger grow which will lead to erroneous behavior with a
981        //missing blockSizeIndex.
982        //inBlock = true;
983
984        // Save space in the buffer for block size
985        write_long(0);
986
987        //Has to happen after write_long since write_long could
988        //trigger grow which is overridden by supper classes to
989        //depend on inBlock.
990        inBlock = true;
991
992        blockSizePosition = get_offset();
993
994        // Remember where to put the size of the endblock less 4
995        blockSizeIndex = bbwi.position();
996
997        if (debug) {
998            dprint("CDROutputStream_1_0 start_block, blockSizeIndex "
999                   + blockSizeIndex);
1000        }
1001
1002    }
1003
1004    // Utility method which will hopefully decrease chunking complexity
1005    // by allowing us to end_block and update chunk lengths without
1006    // calling alignAndReserve.  Otherwise, it's possible to get into
1007    // recursive scenarios which lose the chunking state.
1008    protected void writeLongWithoutAlign(int x) {
1009        if (littleEndian) {
1010            writeLittleEndianLong(x);
1011        } else {
1012            writeBigEndianLong(x);
1013        }
1014    }
1015
1016    public void end_block() {
1017        if (debug) {
1018            dprint("CDROutputStream_1_0.java end_block");
1019        }
1020
1021        if (!inBlock)
1022            return;
1023
1024        if (debug) {
1025            dprint("CDROutputStream_1_0.java end_block, in a block");
1026        }
1027
1028        inBlock = false;
1029
1030        // Test to see if the block was of zero length
1031        // If so, remove the block instead of ending it
1032        // (This can happen if the last field written
1033        //  in a value was another value)
1034        if (get_offset() == blockSizePosition) {
1035            // Need to assert that blockSizeIndex == bbwi.position()?  REVISIT
1036
1037            bbwi.position(bbwi.position() - 4);
1038            blockSizeIndex = -1;
1039            blockSizePosition = -1;
1040            return;
1041        }
1042
1043        int oldSize = bbwi.position();
1044        bbwi.position(blockSizeIndex - 4);
1045
1046        writeLongWithoutAlign(oldSize - blockSizeIndex);
1047
1048        bbwi.position(oldSize);
1049        blockSizeIndex = -1;
1050        blockSizePosition = -1;
1051
1052        // System.out.println("      post end_block: " + get_offset() + " " + bbwi.position());
1053    }
1054
1055    public org.omg.CORBA.ORB orb()
1056    {
1057        return orb;
1058    }
1059
1060    // ------------ End RMI related methods --------------------------
1061
1062    public final void write_boolean_array(boolean[]value, int offset, int length) {
1063        if ( value == null )
1064            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1065
1066        // This will only have an effect if we're already chunking
1067        handleSpecialChunkBegin(length);
1068
1069        for (int i = 0; i < length; i++)
1070            write_boolean(value[offset + i]);
1071
1072        // This will only have an effect if we're already chunking
1073        handleSpecialChunkEnd();
1074    }
1075
1076    public final void write_char_array(char[]value, int offset, int length) {
1077        if ( value == null )
1078            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1079
1080        // This will only have an effect if we're already chunking
1081        handleSpecialChunkBegin(length);
1082
1083        for (int i = 0; i < length; i++)
1084            write_char(value[offset + i]);
1085
1086        // This will only have an effect if we're already chunking
1087        handleSpecialChunkEnd();
1088    }
1089
1090    public void write_wchar_array(char[]value, int offset, int length) {
1091        if ( value == null )
1092            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1093
1094        // This will only have an effect if we're already chunking
1095        handleSpecialChunkBegin(computeAlignment(2) + (length * 2));
1096
1097        for (int i = 0; i < length; i++)
1098            write_wchar(value[offset + i]);
1099
1100        // This will only have an effect if we're already chunking
1101        handleSpecialChunkEnd();
1102    }
1103
1104    public final void write_short_array(short[]value, int offset, int length) {
1105        if ( value == null )
1106            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1107
1108        // This will only have an effect if we're already chunking
1109        handleSpecialChunkBegin(computeAlignment(2) + (length * 2));
1110
1111        for (int i = 0; i < length; i++)
1112            write_short(value[offset + i]);
1113
1114        // This will only have an effect if we're already chunking
1115        handleSpecialChunkEnd();
1116    }
1117
1118    public final void write_ushort_array(short[]value, int offset, int length) {
1119        write_short_array(value, offset, length);
1120    }
1121
1122    public final void write_long_array(int[]value, int offset, int length) {
1123        if ( value == null )
1124            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1125
1126        // This will only have an effect if we're already chunking
1127        handleSpecialChunkBegin(computeAlignment(4) + (length * 4));
1128
1129        for (int i = 0; i < length; i++)
1130            write_long(value[offset + i]);
1131
1132        // This will only have an effect if we're already chunking
1133        handleSpecialChunkEnd();
1134    }
1135
1136    public final void write_ulong_array(int[]value, int offset, int length) {
1137        write_long_array(value, offset, length);
1138    }
1139
1140    public final void write_longlong_array(long[]value, int offset, int length) {
1141        if ( value == null )
1142            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1143
1144        // This will only have an effect if we're already chunking
1145        handleSpecialChunkBegin(computeAlignment(8) + (length * 8));
1146
1147        for (int i = 0; i < length; i++)
1148            write_longlong(value[offset + i]);
1149
1150        // This will only have an effect if we're already chunking
1151        handleSpecialChunkEnd();
1152    }
1153
1154    public final void write_ulonglong_array(long[]value, int offset, int length) {
1155        write_longlong_array(value, offset, length);
1156    }
1157
1158    public final void write_float_array(float[]value, int offset, int length) {
1159        if ( value == null )
1160            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1161
1162        // This will only have an effect if we're already chunking
1163        handleSpecialChunkBegin(computeAlignment(4) + (length * 4));
1164
1165        for (int i = 0; i < length; i++)
1166            write_float(value[offset + i]);
1167
1168        // This will only have an effect if we're already chunking
1169        handleSpecialChunkEnd();
1170    }
1171
1172    public final void write_double_array(double[]value, int offset, int length) {
1173        if ( value == null )
1174            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1175
1176        // This will only have an effect if we're already chunking
1177        handleSpecialChunkBegin(computeAlignment(8) + (length * 8));
1178
1179        for (int i = 0; i < length; i++)
1180            write_double(value[offset + i]);
1181
1182        // This will only have an effect if we're already chunking
1183        handleSpecialChunkEnd();
1184    }
1185
1186    public void write_string_array(String[] value, int offset, int length) {
1187        if ( value == null )
1188            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1189
1190        for(int i = 0; i < length; i++)
1191            write_string(value[offset + i]);
1192    }
1193
1194    public void write_wstring_array(String[] value, int offset, int length) {
1195        if ( value == null )
1196            throw wrapper.nullParam(CompletionStatus.COMPLETED_MAYBE);
1197
1198        for(int i = 0; i < length; i++)
1199            write_wstring(value[offset + i]);
1200    }
1201
1202    public final void write_any_array(org.omg.CORBA.Any value[], int offset, int length)
1203    {
1204        for(int i = 0; i < length; i++)
1205            write_any(value[offset + i]);
1206    }
1207
1208    //--------------------------------------------------------------------//
1209    // CDROutputStream state management.
1210    //
1211
1212    public void writeTo(java.io.OutputStream s)
1213        throws java.io.IOException
1214    {
1215        byte[] tmpBuf = null;
1216
1217        if (bbwi.byteBuffer.hasArray())
1218        {
1219            tmpBuf = bbwi.byteBuffer.array();
1220        }
1221        else
1222        {
1223            int size = bbwi.position();
1224            tmpBuf = new byte[size];
1225            // Micro-benchmarks are showing a loop of ByteBuffer.get(int) is
1226            // faster than ByteBuffer.get(byte[], offset, length)
1227            for (int i = 0; i < size; i++)
1228                tmpBuf[i] = bbwi.byteBuffer.get(i);
1229        }
1230
1231        s.write(tmpBuf, 0, bbwi.position());
1232    }
1233
1234    public void writeOctetSequenceTo(org.omg.CORBA.portable.OutputStream s) {
1235
1236        byte[] buf = null;
1237
1238        if (bbwi.byteBuffer.hasArray())
1239        {
1240            buf = bbwi.byteBuffer.array();
1241        }
1242        else
1243        {
1244            int size = bbwi.position();
1245            buf = new byte[size];
1246            // Micro-benchmarks are showing a loop of ByteBuffer.get(int) is
1247            // faster than ByteBuffer.get(byte[], offset, length)
1248            for (int i = 0; i < size; i++)
1249                buf[i] = bbwi.byteBuffer.get(i);
1250        }
1251
1252        s.write_long(bbwi.position());
1253        s.write_octet_array(buf, 0, bbwi.position());
1254
1255    }
1256
1257    public final int getSize() {
1258        return bbwi.position();
1259    }
1260
1261    public int getIndex() {
1262        return bbwi.position();
1263    }
1264
1265    public boolean isLittleEndian() {
1266        return littleEndian;
1267    }
1268
1269    public void setIndex(int value) {
1270        bbwi.position(value);
1271    }
1272
1273    public ByteBufferWithInfo getByteBufferWithInfo() {
1274        return bbwi;
1275    }
1276
1277    public void setByteBufferWithInfo(ByteBufferWithInfo bbwi) {
1278        this.bbwi = bbwi;
1279    }
1280
1281    public ByteBuffer getByteBuffer() {
1282        ByteBuffer result = null;;
1283        if (bbwi != null) {
1284            result = bbwi.byteBuffer;
1285        }
1286        return result;
1287    }
1288
1289    public void setByteBuffer(ByteBuffer byteBuffer) {
1290        bbwi.byteBuffer = byteBuffer;
1291    }
1292
1293    private final void updateIndirectionTable(int indirection, java.lang.Object object,
1294                                              java.lang.Object key) {
1295        // int indirection = get_offset();
1296        if (valueCache == null)
1297            valueCache = new CacheTable(orb,true);
1298        valueCache.put(object, indirection);
1299        if (key != object)
1300            valueCache.put(key, indirection);
1301    }
1302
1303    private final void write_repositoryId(String id) {
1304        // Use an indirection if available
1305        if (repositoryIdCache != null && repositoryIdCache.containsKey(id)) {
1306            writeIndirection(INDIRECTION_TAG, repositoryIdCache.getVal(id));
1307            return;
1308        }
1309
1310        // Write it as a string.  Note that we have already done the
1311        // special case conversion of non-Latin-1 characters to escaped
1312        // Latin-1 sequences in RepositoryId.
1313
1314        // It's not a good idea to cache them now that we can have
1315        // multiple code sets.
1316        int indirection = writeString(id);
1317
1318        // Add indirection for id to indirection table
1319        if (repositoryIdCache == null)
1320        repositoryIdCache = new CacheTable(orb,true);
1321        repositoryIdCache.put(id, indirection);
1322    }
1323
1324    private void write_codebase(String str, int pos) {
1325        if (codebaseCache != null && codebaseCache.containsKey(str)) {
1326            writeIndirection(INDIRECTION_TAG, codebaseCache.getVal(str));
1327        }
1328        else {
1329            write_string(str);
1330            if (codebaseCache == null)
1331                codebaseCache = new CacheTable(orb,true);
1332            codebaseCache.put(str, pos);
1333        }
1334    }
1335
1336    private final int writeValueTag(boolean chunkIt, boolean useRepId,
1337                                    String codebase) {
1338        int indirection = 0;
1339        if (chunkIt && !useRepId){
1340            if (codebase == null) {
1341                write_long(repIdUtil.getStandardRMIChunkedNoRepStrId());
1342                indirection = get_offset() - 4;
1343            } else {
1344                write_long(repIdUtil.getCodeBaseRMIChunkedNoRepStrId());
1345                indirection = get_offset() - 4;
1346                write_codebase(codebase, get_offset());
1347            }
1348        } else if (chunkIt && useRepId){
1349            if (codebase == null) {
1350                write_long(repIdUtil.getStandardRMIChunkedId());
1351                indirection = get_offset() - 4;
1352            } else {
1353                write_long(repIdUtil.getCodeBaseRMIChunkedId());
1354                indirection = get_offset() - 4;
1355                write_codebase(codebase, get_offset());
1356            }
1357        } else if (!chunkIt && !useRepId) {
1358            if (codebase == null) {
1359                write_long(repIdUtil.getStandardRMIUnchunkedNoRepStrId());
1360                indirection = get_offset() - 4;
1361            } else {
1362                write_long(repIdUtil.getCodeBaseRMIUnchunkedNoRepStrId());
1363                indirection = get_offset() - 4;
1364                write_codebase(codebase, get_offset());
1365            }
1366        } else if (!chunkIt && useRepId) {
1367            if (codebase == null) {
1368                write_long(repIdUtil.getStandardRMIUnchunkedId());
1369                indirection = get_offset() - 4;
1370            } else {
1371                write_long(repIdUtil.getCodeBaseRMIUnchunkedId());
1372                indirection = get_offset() - 4;
1373                write_codebase(codebase, get_offset());
1374            }
1375        }
1376        return indirection;
1377    }
1378
1379    private void writeIDLValue(Serializable object, String repID)
1380    {
1381        if (object instanceof StreamableValue) {
1382            ((StreamableValue)object)._write(parent);
1383
1384        } else if (object instanceof CustomValue) {
1385            ((CustomValue)object).marshal(parent);
1386
1387        } else {
1388            BoxedValueHelper helper = Utility.getHelper(object.getClass(), null, repID);
1389            boolean isCustom = false;
1390            if (helper instanceof ValueHelper && object instanceof CustomMarshal) {
1391                try {
1392                    if (((ValueHelper)helper).get_type().type_modifier() == VM_CUSTOM.value)
1393                        isCustom = true;
1394                } catch(BadKind ex) {
1395                    throw wrapper.badTypecodeForCustomValue( CompletionStatus.COMPLETED_MAYBE,
1396                        ex ) ;
1397                }
1398            }
1399            if (isCustom)
1400                ((CustomMarshal)object).marshal(parent);
1401            else
1402                helper.write_value(parent, object);
1403        }
1404    }
1405
1406    // Handles end tag compaction...
1407    private void writeEndTag(boolean chunked){
1408
1409        if (chunked) {
1410            if (get_offset() == end_flag_position) {
1411
1412                if (bbwi.position() == end_flag_index) {
1413
1414                    // We are exactly at the same position and index as the
1415                    // end of the last end tag.  Thus, we can back up over it
1416                    // and compact the tags.
1417                    bbwi.position(bbwi.position() - 4);
1418
1419                } else {
1420
1421                    // Special case in which we're at the beginning of a new
1422                    // fragment, but the position is the same.  We can't back up,
1423                    // so we just write the new end tag without compaction.  This
1424                    // occurs when a value ends and calls start_block to open a
1425                    // continuation chunk, but it's called at the very end of
1426                    // a fragment.
1427                }
1428            }
1429
1430            writeNestingLevel();
1431
1432            // Remember the last index and position.  These are only used when chunking.
1433            end_flag_index = bbwi.position();
1434            end_flag_position = get_offset();
1435
1436            chunkedValueNestingLevel++;
1437        }
1438
1439        // Increment the nesting level
1440        end_flag++;
1441    }
1442
1443    /**
1444     * Handles ORB versioning of the end tag.  Should only
1445     * be called if chunking.
1446     *
1447     * If talking to our older ORBs (Standard Extension,
1448     * Kestrel, and Ladybird), write the end flag that takes
1449     * into account all enclosing valuetypes.
1450     *
1451     * If talking a newer or foreign ORB, or if the orb
1452     * instance is null, write the end flag that only takes
1453     * into account the enclosing chunked valuetypes.
1454     */
1455    private void writeNestingLevel() {
1456        if (orb == null ||
1457            ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) ||
1458            ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) {
1459
1460            write_long(chunkedValueNestingLevel);
1461
1462        } else {
1463            write_long(end_flag);
1464        }
1465    }
1466
1467    private void writeClass(String repository_id, Class clz) {
1468
1469        if (repository_id == null)
1470            repository_id = repIdStrs.getClassDescValueRepId();
1471
1472        // Write value_tag
1473        int indirection = writeValueTag(mustChunk, true, null);
1474        updateIndirectionTable(indirection, clz, clz);
1475
1476        write_repositoryId(repository_id);
1477
1478        if (mustChunk) {
1479            // Write Value chunk
1480            start_block();
1481            end_flag--;
1482            chunkedValueNestingLevel--;
1483        } else
1484            end_flag--;
1485
1486        writeClassBody(clz);
1487
1488        if (mustChunk)
1489            end_block();
1490
1491        // Write end tag
1492        writeEndTag(mustChunk);
1493    }
1494
1495    // Pre-Merlin/J2EE 1.3 ORBs wrote the repository ID
1496    // and codebase strings in the wrong order.  This handles
1497    // backwards compatibility.
1498    private void writeClassBody(Class clz) {
1499        if (orb == null ||
1500            ORBVersionFactory.getFOREIGN().equals(orb.getORBVersion()) ||
1501            ORBVersionFactory.getNEWER().compareTo(orb.getORBVersion()) <= 0) {
1502
1503            write_value(Util.getCodebase(clz));
1504            write_value(repIdStrs.createForAnyType(clz));
1505        } else {
1506
1507            write_value(repIdStrs.createForAnyType(clz));
1508            write_value(Util.getCodebase(clz));
1509        }
1510    }
1511
1512    // Casts and returns an Object as a Serializable
1513    // This is required for JDK 1.1 only to avoid VerifyErrors when
1514    // passing arrays as Serializable
1515    // private java.io.Serializable make_serializable(java.lang.Object object)
1516    // {
1517    //  return (java.io.Serializable)object;
1518    // }
1519
1520    private boolean shouldWriteAsIDLEntity(Serializable object)
1521    {
1522        return ((object instanceof IDLEntity) && (!(object instanceof ValueBase)) &&
1523                (!(object instanceof org.omg.CORBA.Object)));
1524
1525    }
1526
1527    private void writeIDLEntity(IDLEntity object) {
1528
1529        // _REVISIT_ could check to see whether chunking really needed
1530        mustChunk = true;
1531
1532        String repository_id = repIdStrs.createForJavaType(object);
1533        Class clazz = object.getClass();
1534        String codebase = Util.getCodebase(clazz);
1535
1536        // Write value_tag
1537        int indirection = writeValueTag(true, true, codebase);
1538        updateIndirectionTable(indirection, object, object);
1539
1540        // Write rep. id
1541        write_repositoryId(repository_id);
1542
1543        // Write Value chunk
1544        end_flag--;
1545        chunkedValueNestingLevel--;
1546        start_block();
1547
1548        // Write the IDLEntity using reflection
1549        try {
1550            ClassLoader clazzLoader = (clazz == null ? null : clazz.getClassLoader());
1551            final Class helperClass = Utility.loadClassForClass(clazz.getName()+"Helper", codebase,
1552                                                   clazzLoader, clazz, clazzLoader);
1553            final Class argTypes[] = {org.omg.CORBA.portable.OutputStream.class, clazz};
1554            // getDeclaredMethod requires RuntimePermission accessDeclaredMembers
1555            // if a different class loader is used (even though the javadoc says otherwise)
1556            Method writeMethod = null;
1557            try {
1558                writeMethod = (Method)AccessController.doPrivileged(
1559                    new PrivilegedExceptionAction() {
1560                        public java.lang.Object run() throws NoSuchMethodException {
1561                            return helperClass.getDeclaredMethod(kWriteMethod, argTypes);
1562                        }
1563                    }
1564                );
1565            } catch (PrivilegedActionException pae) {
1566                // this gets caught below
1567                throw (NoSuchMethodException)pae.getException();
1568            }
1569            java.lang.Object args[] = {parent, object};
1570            writeMethod.invoke(null, args);
1571        } catch (ClassNotFoundException cnfe) {
1572            throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, cnfe ) ;
1573        } catch(NoSuchMethodException nsme) {
1574            throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, nsme ) ;
1575        } catch(IllegalAccessException iae) {
1576            throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, iae ) ;
1577        } catch(InvocationTargetException ite) {
1578            throw wrapper.errorInvokingHelperWrite( CompletionStatus.COMPLETED_MAYBE, ite ) ;
1579        }
1580        end_block();
1581
1582        // Write end tag
1583        writeEndTag(true);
1584    }
1585
1586    /* DataOutputStream methods */
1587
1588    public void write_Abstract (java.lang.Object value) {
1589        write_abstract_interface(value);
1590    }
1591
1592    public void write_Value (java.io.Serializable value) {
1593        write_value(value);
1594    }
1595
1596    // This will stay a custom add-on until the java-rtf issue is resolved.
1597    // Then it should be declared in org.omg.CORBA.portable.OutputStream.
1598    //
1599    // Pads the string representation of bigDecimal with zeros to fit the given
1600    // digits and scale before it gets written to the stream.
1601    public void write_fixed(java.math.BigDecimal bigDecimal, short digits, short scale) {
1602        String string = bigDecimal.toString();
1603        String integerPart;
1604        String fractionPart;
1605        StringBuffer stringBuffer;
1606
1607        // Get rid of the sign
1608        if (string.charAt(0) == '-' || string.charAt(0) == '+') {
1609            string = string.substring(1);
1610        }
1611
1612        // Determine integer and fraction parts
1613        int dotIndex = string.indexOf('.');
1614        if (dotIndex == -1) {
1615            integerPart = string;
1616            fractionPart = null;
1617        } else if (dotIndex == 0 ) {
1618            integerPart = null;
1619            fractionPart = string;
1620        } else {
1621            integerPart = string.substring(0, dotIndex);
1622            fractionPart = string.substring(dotIndex + 1);
1623        }
1624
1625        // Pad both parts with zeros as necessary
1626        stringBuffer = new StringBuffer(digits);
1627        if (fractionPart != null) {
1628            stringBuffer.append(fractionPart);
1629        }
1630        while (stringBuffer.length() < scale) {
1631            stringBuffer.append('0');
1632        }
1633        if (integerPart != null) {
1634            stringBuffer.insert(0, integerPart);
1635        }
1636        while (stringBuffer.length() < digits) {
1637            stringBuffer.insert(0, '0');
1638        }
1639
1640        // This string contains no sign or dot
1641        this.write_fixed(stringBuffer.toString(), bigDecimal.signum());
1642    }
1643
1644    // This method should be remove by the java-rtf issue.
1645    // Right now the scale and digits information of the type code is lost.
1646    public void write_fixed(java.math.BigDecimal bigDecimal) {
1647        // This string might contain sign and/or dot
1648        this.write_fixed(bigDecimal.toString(), bigDecimal.signum());
1649    }
1650
1651    // The string may contain a sign and dot
1652    public void write_fixed(String string, int signum) {
1653        int stringLength = string.length();
1654        // Each octet contains (up to) two decimal digits
1655        byte doubleDigit = 0;
1656        char ch;
1657        byte digit;
1658
1659        // First calculate the length of the string without optional sign and dot
1660        int numDigits = 0;
1661        for (int i=0; i<stringLength; i++) {
1662            ch = string.charAt(i);
1663            if (ch == '-' || ch == '+' || ch == '.')
1664                continue;
1665            numDigits++;
1666        }
1667        for (int i=0; i<stringLength; i++) {
1668            ch = string.charAt(i);
1669            if (ch == '-' || ch == '+' || ch == '.')
1670                continue;
1671            digit = (byte)Character.digit(ch, 10);
1672            if (digit == -1) {
1673                throw wrapper.badDigitInFixed( CompletionStatus.COMPLETED_MAYBE ) ;
1674            }
1675            // If the fixed type has an odd number of decimal digits,
1676            // then the representation begins with the first (most significant) digit.
1677            // Otherwise, this first half-octet is all zero, and the first digit
1678            // is in the second half-octet.
1679            if (numDigits % 2 == 0) {
1680                doubleDigit |= digit;
1681                this.write_octet(doubleDigit);
1682                doubleDigit = 0;
1683            } else {
1684                doubleDigit |= (digit << 4);
1685            }
1686            numDigits--;
1687        }
1688        // The sign configuration, in the last half-octet of the representation,
1689        // is 0xD for negative numbers and 0xC for positive and zero values
1690        if (signum == -1) {
1691            doubleDigit |= 0xd;
1692        } else {
1693            doubleDigit |= 0xc;
1694        }
1695        this.write_octet(doubleDigit);
1696    }
1697
1698    private final static String _id = "IDL:omg.org/CORBA/DataOutputStream:1.0";
1699    private final static String[] _ids = { _id };
1700
1701    public String[] _truncatable_ids() {
1702        if (_ids == null)
1703            return null;
1704
1705        return (String[])_ids.clone();
1706    }
1707
1708    /* for debugging */
1709
1710    public void printBuffer() {
1711        CDROutputStream_1_0.printBuffer(this.bbwi);
1712    }
1713
1714    public static void printBuffer(ByteBufferWithInfo bbwi) {
1715
1716        System.out.println("+++++++ Output Buffer ++++++++");
1717        System.out.println();
1718        System.out.println("Current position: " + bbwi.position());
1719        System.out.println("Total length : " + bbwi.buflen);
1720        System.out.println();
1721
1722        char[] charBuf = new char[16];
1723
1724        try {
1725
1726            for (int i = 0; i < bbwi.position(); i += 16) {
1727
1728                int j = 0;
1729
1730                // For every 16 bytes, there is one line
1731                // of output.  First, the hex output of
1732                // the 16 bytes with each byte separated
1733                // by a space.
1734                while (j < 16 && j + i < bbwi.position()) {
1735                    int k = bbwi.byteBuffer.get(i + j);
1736                    if (k < 0)
1737                        k = 256 + k;
1738                    String hex = Integer.toHexString(k);
1739                    if (hex.length() == 1)
1740                        hex = "0" + hex;
1741                    System.out.print(hex + " ");
1742                    j++;
1743                }
1744
1745                // Add any extra spaces to align the
1746                // text column in case we didn't end
1747                // at 16
1748                while (j < 16) {
1749                    System.out.print("   ");
1750                    j++;
1751                }
1752
1753                // Now output the ASCII equivalents.  Non-ASCII
1754                // characters are shown as periods.
1755                int x = 0;
1756
1757                while (x < 16 && x + i < bbwi.position()) {
1758                    if (ORBUtility.isPrintable((char)bbwi.byteBuffer.get(i + x)))
1759                        charBuf[x] = (char)bbwi.byteBuffer.get(i + x);
1760                    else
1761                        charBuf[x] = '.';
1762                    x++;
1763                }
1764                System.out.println(new String(charBuf, 0, x));
1765            }
1766        } catch (Throwable t) {
1767            t.printStackTrace();
1768        }
1769        System.out.println("++++++++++++++++++++++++++++++");
1770    }
1771
1772    public void writeIndirection(int tag, int posIndirectedTo)
1773    {
1774        // Must ensure that there are no chunks between the tag
1775        // and the actual indirection value.  This isn't talked about
1776        // in the spec, but seems to cause headaches in our code.
1777        // At the very least, this method isolates the indirection code
1778        // that was duplicated so often.
1779
1780        handleSpecialChunkBegin(computeAlignment(4) + 8);
1781
1782        // write indirection tag
1783        write_long(tag);
1784
1785        // write indirection
1786        // Use parent.getRealIndex() so that it can be overridden by TypeCodeOutputStreams
1787/*
1788        System.out.println("CDROutputStream_1_0 writing indirection pos " + posIndirectedTo +
1789                           " - real index " + parent.getRealIndex(get_offset()) + " = " +
1790                           (posIndirectedTo - parent.getRealIndex(get_offset())));
1791*/
1792        write_long(posIndirectedTo - parent.getRealIndex(get_offset()));
1793
1794        handleSpecialChunkEnd();
1795    }
1796
1797    protected CodeSetConversion.CTBConverter getCharConverter() {
1798        if (charConverter == null)
1799            charConverter = parent.createCharCTBConverter();
1800
1801        return charConverter;
1802    }
1803
1804    protected CodeSetConversion.CTBConverter getWCharConverter() {
1805        if (wcharConverter == null)
1806            wcharConverter = parent.createWCharCTBConverter();
1807
1808        return wcharConverter;
1809    }
1810
1811    protected void dprint(String msg) {
1812        if (debug)
1813            ORBUtility.dprint(this, msg);
1814    }
1815
1816    void alignOnBoundary(int octetBoundary) {
1817        alignAndReserve(octetBoundary, 0);
1818    }
1819
1820    public void start_value(String rep_id) {
1821
1822        if (debug) {
1823            dprint("start_value w/ rep id "
1824                   + rep_id
1825                   + " called at pos "
1826                   + get_offset()
1827                   + " position "
1828                   + bbwi.position());
1829        }
1830
1831        if (inBlock)
1832            end_block();
1833
1834        // Write value_tag
1835        writeValueTag(true, true, null);
1836
1837        // Write rep. id
1838        write_repositoryId(rep_id);
1839
1840        // Write Value chunk
1841        end_flag--;
1842        chunkedValueNestingLevel--;
1843
1844        // Make sure to chunk the custom data
1845        start_block();
1846    }
1847
1848    public void end_value() {
1849
1850        if (debug) {
1851            dprint("end_value called at pos "
1852                   + get_offset()
1853                   + " position "
1854                   + bbwi.position());
1855        }
1856
1857        end_block();
1858
1859        writeEndTag(true);
1860
1861        // Check to see if we need to start another block for a
1862        // possible outer value.  Since we're in the stream
1863        // format 2 custom type contained by another custom
1864        // type, mustChunk should always be true.
1865        //
1866        // Here's why we need to open a continuation chunk:
1867        //
1868        // We need to enclose the default data of the
1869        // next subclass down in chunks.  There won't be
1870        // an end tag separating the superclass optional
1871        // data and the subclass's default data.
1872
1873        if (debug) {
1874            dprint("mustChunk is " + mustChunk);
1875        }
1876
1877        if (mustChunk) {
1878            start_block();
1879        }
1880    }
1881
1882    public void close() throws IOException
1883    {
1884        // tell BufferManagerWrite to release any ByteBuffers
1885        getBufferManager().close();
1886
1887        // It's possible bbwi.byteBuffer is shared between
1888        // this OutputStream and an InputStream. Thus, we check
1889        // if the Input/Output streams are using the same ByteBuffer.
1890        // If they sharing the same ByteBuffer we need to ensure only
1891        // one of those ByteBuffers are released to the ByteBufferPool.
1892
1893        if (getByteBufferWithInfo() != null && getByteBuffer() != null)
1894        {
1895            MessageMediator messageMediator = parent.getMessageMediator();
1896            if (messageMediator != null)
1897            {
1898                CDRInputObject inputObj =
1899                               (CDRInputObject)messageMediator.getInputObject();
1900                if (inputObj != null)
1901                {
1902                    if (inputObj.isSharing(getByteBuffer()))
1903                    {
1904                        // Set InputStream's ByteBuffer and bbwi to null
1905                        // so its ByteBuffer cannot be released to the pool
1906                        inputObj.setByteBuffer(null);
1907                        inputObj.setByteBufferWithInfo(null);
1908                    }
1909                }
1910            }
1911
1912            // release this stream's ByteBuffer to the pool
1913            ByteBufferPool byteBufferPool = orb.getByteBufferPool();
1914            if (debug)
1915            {
1916                // print address of ByteBuffer being released
1917                int bbAddress = System.identityHashCode(bbwi.byteBuffer);
1918                StringBuffer sb = new StringBuffer(80);
1919                sb.append(".close - releasing ByteBuffer id (");
1920                sb.append(bbAddress).append(") to ByteBufferPool.");
1921                String msg = sb.toString();
1922                dprint(msg);
1923             }
1924             byteBufferPool.releaseByteBuffer(getByteBuffer());
1925             bbwi.byteBuffer = null;
1926             bbwi = null;
1927        }
1928    }
1929}
1930