OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / CORBA / CDR / Vio.java
1 /* Vio.java -- Value type IO operations.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.CORBA.CDR;
40
41 import gnu.CORBA.Minor;
42 import gnu.CORBA.ObjectCreator;
43
44 import gnu.java.lang.CPStringBuilder;
45
46 import org.omg.CORBA.CustomMarshal;
47 import org.omg.CORBA.DataInputStream;
48 import org.omg.CORBA.DataOutputStream;
49 import org.omg.CORBA.MARSHAL;
50 import org.omg.CORBA.NO_IMPLEMENT;
51 import org.omg.CORBA.StringSeqHelper;
52 import org.omg.CORBA.StringValueHelper;
53 import org.omg.CORBA.SystemException;
54 import org.omg.CORBA.WStringValueHelper;
55 import org.omg.CORBA.portable.BoxedValueHelper;
56 import org.omg.CORBA.portable.InputStream;
57 import org.omg.CORBA.portable.OutputStream;
58 import org.omg.CORBA.portable.Streamable;
59 import org.omg.CORBA.portable.ValueFactory;
60
61 import java.io.IOException;
62 import java.io.Serializable;
63 import java.lang.reflect.Constructor;
64 import java.lang.reflect.Modifier;
65 import java.util.StringTokenizer;
66
67 import javax.rmi.CORBA.Util;
68 import javax.rmi.CORBA.ValueHandler;
69
70 /**
71  * A specialised class for reading and writing the value types.
72  * 
73  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
74  */
75 public abstract class Vio
76 {
77   /**
78    * If true, wrap value type data into chunks. This decrease the performance,
79    * and is not required for interoperability with jdk 1.5, but is left in the
80    * implementation as the optional mode for solving possible interoperability
81    * problems with non-Sun CORBA implementations.
82    * 
83    * The current implementation would accept both single chunk or multiple
84    * chunks, but will always send a single chunk (if true) or unchunked data (if
85    * false).
86    */
87   public static boolean USE_CHUNKING = false;
88
89   /**
90    * The first field in the value record. The last octet may contain additional
91    * flags (vf_CODEBASE, vf_ID and vf_MULTIPLE_IDS). The tag value is different
92    * for the indirections (vt_INDIRECTION) and nulls (vt_NULL).
93    */
94   public static final int vt_VALUE_TAG = 0x7fffff00;
95
96   /**
97    * The value tag flag, indicating that the codebase URL is present in the
98    * value tag record.
99    */
100   public static final int vf_CODEBASE = 0x1;
101
102   /**
103    * The value tag flag, indicating that a single repository id is present in
104    * the value tag record.
105    */
106   public static final int vf_ID = 0x2;
107
108   /**
109    * The value tag flag, indicating, that there are multiple repository ids
110    * present in the record. If this flag is set, the flag vf_ID must also be
111    * set, resulting the value of the least significant byte 0x6.
112    */
113   public static final int vf_MULTIPLE_IDS = 0x4;
114
115   /**
116    * The value tag flag, indicating the presence of chunking. Each chunk is
117    * preceeded by a positive int, indicating the number of bytes in the chunk. A
118    * sequence of chunks is terminated by a non positive int.
119    */
120   public static final int vf_CHUNKING = 0x8;
121
122   /**
123    * The indirection tag value. Such tag must be followed by the CORBA long,
124    * indicating the offset in the CORBA message, where the indirected
125    * information is present. This offset is assumed zero at the position where
126    * the mentioned CORBA long starts and can refer both forward (positive
127    * values) and backward (negative values).
128    */
129   public static final int vt_INDIRECTION = 0xffffffff;
130
131   /**
132    * This tag value means that the value object being transferred is equal to
133    * null.
134    */
135   public static final int vt_NULL = 0x0;
136
137   /**
138    * The size of CORBA long (java int).
139    */
140   static final int INT_SIZE = 4;
141
142   /**
143    * The String value helper (one instance is sufficient).
144    */
145   public static final WStringValueHelper m_StringValueHelper = new WStringValueHelper();
146
147   /**
148    * An instance of the value handler.
149    */
150   static ValueHandler handler = Util.createValueHandler();
151
152   /**
153    * Read the value base from the given input stream. Determines the required
154    * class from the repository id. This includes operations that are not
155    * required when an unitialised instance or at least class of the value type
156    * is known. Hence it may be faster to use the alternative methods,
157    * read(InputStream, Class) or read(InputStream, Serializable).
158    * 
159    * @param input a stream to read from.
160    * 
161    * @return the loaded value.
162    * 
163    * @throws MARSHAL if the reading has failed due any reason.
164    */
165   public static Serializable read(InputStream input)
166   {
167     return read(input, (String) null);
168   }
169
170   /**
171    * Read the value base from the given input stream. Determines the required
172    * class from the repository id. This includes operations that are not
173    * required when an unitialised instance or at least class of the value type
174    * is known. Hence it may be faster to use the alternative methods,
175    * read(InputStream, Class) or read(InputStream, Serializable).
176    * 
177    * @param input a stream to read from.
178    * @param repository_id a repository id of the object being read, may be null.
179    * 
180    * @return the loaded value.
181    * 
182    * @throws MARSHAL if the reading has failed due any reason.
183    */
184   public static Serializable read(InputStream input, String repository_id)
185   {
186     try
187       {
188         final int position = getCurrentPosition(input);
189         // We may need to jump back if the value is read via value factory.
190         input.mark(512);
191
192         int value_tag = input.read_long();
193         checkTag(value_tag);
194
195         String codebase = null;
196         String[] ids = null;
197         String id = repository_id;
198
199         // Check for the agreed null value.
200         if (value_tag == vt_NULL)
201           return null;
202         else if (value_tag == vt_INDIRECTION)
203           return readIndirection(input);
204         else
205           {
206             // Read the value.
207             if ((value_tag & vf_CODEBASE) != 0)
208               {
209                 // The codebase is present. The codebase is a space
210                 // separated list of URLs from where the implementing
211                 // code can be downloaded.
212                 codebase = read_string(input);
213               }
214
215             if ((value_tag & vf_MULTIPLE_IDS) != 0)
216               {
217                 // Multiple supported repository ids are present.
218                 ids = read_string_array(input);
219               }
220             else if ((value_tag & vf_ID) != 0)
221               {
222                 // Single supported repository id is present.
223                 id = read_string(input);
224               }
225           }
226
227         BoxedValueHelper helper = getHelper(null, id);
228         // The existing implementing object.
229         java.lang.Object ox = null;
230
231         if (helper != null)
232           ox = null; // Helper will care about the instantiating.
233         else if (id.equals(WStringValueHelper.id()))
234           helper = m_StringValueHelper;
235         else
236           ox = createInstance(id, ids, codebase);
237         return (Serializable) read_instance(input, position, ox, value_tag,
238           helper, id, ids, codebase);
239       }
240     catch (Exception ex)
241       {
242         MARSHAL m = new MARSHAL();
243         m.minor = Minor.Value;        
244         m.initCause(ex);
245         throw m;
246       }
247   }
248
249   /**
250    * Read the value base from the given input stream when the value base class
251    * is available. Hence there is no need to guess it from the repository id.
252    * 
253    * @param input a stream to read from.
254    * @param value_class the class of the value being read.
255    * 
256    * @return the loaded value.
257    * 
258    * @throws MARSHAL if the reading has failed due any reason.
259    */
260   public static Serializable read(InputStream input, Class value_class)
261   {
262     final int position = getCurrentPosition(input);
263
264     String id = null;
265     String[] ids = null;
266     String codebase = null;
267
268     try
269       {
270         int value_tag = input.read_long();
271         checkTag(value_tag);
272
273         // Check for the agreed null value.
274         if (value_tag == vt_NULL)
275           return null;
276         else if (value_tag == vt_INDIRECTION)
277           return readIndirection(input);
278         else
279           {
280             // Read the value.
281             if ((value_tag & vf_CODEBASE) != 0)
282               {
283                 // The codebase is present.
284                 codebase = read_string(input);
285               }
286
287             if ((value_tag & vf_MULTIPLE_IDS) != 0)
288               {
289                 // Multiple supported repository ids are present.
290                 ids = read_string_array(input);
291               }
292             else if ((value_tag & vf_ID) != 0)
293               {
294                 // Single supported repository id is present.
295                 id = read_string(input);
296               }
297           }
298
299         BoxedValueHelper vHelper = id != null ? getHelper(value_class, id)
300           : getHelper(value_class, ids);
301
302         java.lang.Object ox;
303
304         if (vHelper == null)
305           {
306             try
307               {
308                 ox = createInstance(id, ids, codebase);
309               }
310             catch (Exception e)
311               {
312                 ox = null;
313               }
314
315             if (ox != null)
316               {
317                 if (value_class != null
318                   && !value_class.isAssignableFrom(ox.getClass()))
319                   {
320                     MARSHAL m = new MARSHAL(ox.getClass() + " is not a "
321                     + value_class.getName());
322                     m.minor = Minor.ClassCast;
323                     throw m;
324                   }
325               }
326           }
327         else
328           ox = null;
329
330         ox = read_instance(input, position, ox, value_tag, vHelper, id, ids,
331           codebase);
332         return (Serializable) ox;
333       }
334     catch (MARSHAL m)
335       {
336         throw m;
337       }
338     catch (SystemException sysEx)
339       {
340         // OK.
341         throw sysEx;
342       }
343     catch (Exception ex)
344       {
345         MARSHAL m = new MARSHAL("Cant read " + value_class);
346         m.minor = Minor.Value;
347         m.initCause(ex);
348         throw m;
349       }
350   }
351
352   /**
353    * Read the value base from the given input stream when the unitialised
354    * instance is available. Hence there is no need to guess the class from the
355    * repository id and then to instantiate an instance.
356    * 
357    * @param input a stream to read from.
358    * 
359    * @param value_instance an pre-created instance of the value. If the helper
360    * is not null, this parameter is ignored an should be null.
361    * 
362    * @param helper a helper to create an instance and read the object- specific
363    * part of the record. If the value_instance is used instead, this parameter
364    * should be null.
365    * 
366    * @return the loaded value.
367    * 
368    * @throws MARSHAL if the reading has failed due any reason.
369    */
370   public static Object read(InputStream input, Object value_instance,
371     BoxedValueHelper helper)
372   {
373     final int position = getCurrentPosition(input);
374
375     String id = null;
376     String[] ids = null;
377     String codebase = null;
378
379     try
380       {
381         int value_tag = input.read_long();
382         checkTag(value_tag);
383
384         // Check for the agreed null value.
385         if (value_tag == vt_NULL)
386           return null;
387         else if (value_tag == vt_INDIRECTION)
388           return readIndirection(input);
389         else
390           {
391             // Read the value.
392             if ((value_tag & vf_CODEBASE) != 0)
393               {
394                 // The codebase is present.
395                 codebase = read_string(input);
396               }
397
398             if ((value_tag & vf_MULTIPLE_IDS) != 0)
399               {
400                 // Multiple supported repository ids are present.
401                 ids = read_string_array(input);
402               }
403             else if ((value_tag & vf_ID) != 0)
404               {
405                 // Single supported repository id is present.
406                 id = read_string(input);
407               }
408           }
409
410         Class value_class = value_instance == null ? null
411           : value_instance.getClass();
412
413         if (helper == null)
414           helper = id != null ? getHelper(value_class, id) : getHelper(
415             value_class, ids);
416
417         value_instance = read_instance(input, position, value_instance,
418           value_tag, helper, id, ids, codebase);
419         return value_instance;
420       }
421     catch (Exception ex)
422       {
423         MARSHAL m = new MARSHAL();
424         m.minor = Minor.Value;
425         m.initCause(ex);
426         throw m;
427       }
428   }
429
430   /**
431    * Read using provided boxed value helper. This method expects the full value
432    * type header, followed by contents, that are delegated to the provided
433    * helper. It handles null.
434    * 
435    * @param input the stream to read from.
436    * @param helper the helper that reads the type-specific part of the content.
437    * 
438    * @return the value, created by the helper, or null if the header indicates
439    * that null was previously written.
440    */
441   public static Serializable read(InputStream input, BoxedValueHelper helper)
442   {
443     return (Serializable) read(input, null, helper);
444   }
445
446   /**
447    * Fill in the instance fields by the data from the input stream. The method
448    * assumes that the value header, if any, is already behind. The information
449    * from the stream is stored into the passed ox parameter.
450    * 
451    * @param input an input stream to read from.
452    * 
453    * @param value a pre-instantiated value type object, must be either
454    * Streamable or CustomMarshal. If the helper is used, this parameter is
455    * ignored and should be null.
456    * 
457    * @param value_tag the tag that must be read previously.
458    * @param helper the helper for read object specific part; may be null to read
459    * in using other methods.
460    * 
461    * @return the value that was read.
462    */
463   static Object read_instance(InputStream input, final int position,
464     Object value, int value_tag, BoxedValueHelper helper, String id,
465     String[] ids, String codebase)
466   {
467     if (helper != m_StringValueHelper && id != null)
468       if (id.equals(StringValueHelper.id()))
469         {
470           value = null;
471           helper = m_StringValueHelper;
472         }
473
474     try
475       {
476         if ((value_tag & vf_CHUNKING) != 0)
477           {
478             BufferedCdrOutput output = createBuffer(input, 1024);
479             // Read the current (not a nested one) value in this spec case.
480             readNestedValue(value_tag, input, output, -1);
481             BufferredCdrInput ci = new BufferredCdrInput(output.buffer.getBuffer());
482             ci.setRunTime(output.getRunTime());
483
484             input = new HeadlessInput(ci, input);
485           }
486         else
487           {
488             if (input instanceof BufferredCdrInput)
489               {
490                 // Highly probable case.
491                 input = new HeadlessInput((BufferredCdrInput) input, null);
492               }
493             else if (input instanceof HeadlessInput)
494               {
495                 // There is no need to instantiate one more HeadlessInput
496                 // as we can just reset.
497                 ((HeadlessInput) input).subsequentCalls = false;
498               }
499             else
500               {
501                 BufferedCdrOutput bout = new BufferedCdrOutput();
502                 int c;
503                 while ((c = input.read()) >= 0)
504                   bout.write((byte) c);
505                 input = new HeadlessInput(
506                   (BufferredCdrInput) bout.create_input_stream(), input);
507               }
508           }
509       }
510     catch (IOException ex)
511       {
512         MARSHAL m = new MARSHAL("Unable to read chunks");
513         m.minor = Minor.Value;
514         m.initCause(ex);
515         throw m;
516       }
517
518     return readValue(input, position, value, helper, id, ids, codebase);
519   }
520
521   /**
522    * Create a buffer, inheriting critical settings from the passed input stream.
523    */
524   private static BufferedCdrOutput createBuffer(InputStream input, int proposed_size)
525   {
526     BufferedCdrOutput bout;
527     bout = new BufferedCdrOutput(2 * proposed_size + 256);
528
529     if (input instanceof BufferredCdrInput)
530       {
531         BufferredCdrInput in = (BufferredCdrInput) input;
532         bout.setBigEndian(in.isBigEndian());
533       }
534
535     if (input instanceof gnuValueStream)
536       bout.setRunTime(((gnuValueStream) input).getRunTime());
537     else
538       bout.setRunTime(new gnuRuntime(null, null));
539     return bout;
540   }
541
542   /**
543    * Read the chunked nested value from the given input stream, transferring the
544    * contents to the given output stream.
545    * 
546    * @param value_tag the value tag of the value being read.
547    * @param input the input stream from where the remainder of the nested value
548    * must be read.
549    * @param output the output stream where the unchunked nested value must be
550    * copied.
551    * 
552    * @return the tag that ended the nested value.
553    */
554   public static int readNestedValue(int value_tag, InputStream input,
555     BufferedCdrOutput output, int level)
556     throws IOException
557   {
558     String id = null;
559     if (level < -1)
560       {
561         // For the first level, this information is already behind.
562         output.write_long(value_tag - vf_CHUNKING);
563
564         // The nested value should be aways chunked.
565         if ((value_tag & vf_CHUNKING) == 0)
566           {
567             MARSHAL m = new MARSHAL("readNestedValue: must be chunked");
568             m.minor = Minor.Chunks;
569             throw m;
570           }
571         else if (value_tag == vt_NULL)
572           {
573             MARSHAL m = new MARSHAL("readNestedValue: nul");
574             m.minor = Minor.Chunks;
575             throw m;
576           }
577         else if (value_tag == vt_INDIRECTION)
578           {
579             MARSHAL m = new MARSHAL("readNestedValue: indirection");
580             m.minor = Minor.Chunks;
581             throw m;
582           }
583         else
584           {
585             // Read the value.
586             if ((value_tag & vf_CODEBASE) != 0)
587               {
588                 String codebase = read_string(input);
589                 write_string(output, codebase);
590               }
591
592             if ((value_tag & vf_MULTIPLE_IDS) != 0)
593               {
594                 // Multiple supported repository ids are present.
595                 String[] ids = read_string_array(input);
596                 id = ids[0];
597                 write_string_array(output, ids);
598               }
599             else if ((value_tag & vf_ID) != 0)
600               {
601                 id = read_string(input);
602                 write_string(output, id);
603               }
604           }
605       }
606
607     int n = -1;
608
609     // Read all chunks.
610     int chunk_size;
611
612     byte[] r = null;
613
614     while (true)
615       {
616         // Read the size of the next chunk or it may also be the
617         // header of the nested value.
618         chunk_size = input.read_long();
619
620         // End of chunk terminator.
621         if (chunk_size < 0 && chunk_size >= level)
622           return chunk_size;
623         else if (chunk_size >= 0x7FFFFF00)
624           {
625             int onInput = getCurrentPosition(input) - 4;
626             int onOutput = output.getPosition();
627             output.getRunTime().redirect(onInput, onOutput);
628             // Value over 0x7FFFFF00 indicates that the nested value
629             // starts here. Read the nested value, storing it into the output.
630             // First parameter is actually the value tag.
631             chunk_size = readNestedValue(chunk_size, input, output, level - 1);
632             if (chunk_size < 0 && chunk_size >= level)
633               return chunk_size;
634           }
635         else
636           {
637             // The chunk follows.
638             if (r == null || r.length < chunk_size)
639               r = new byte[chunk_size + 256];
640
641             n = 0;
642             while (n < chunk_size)
643               n += input.read(r, n, chunk_size - n);
644             output.write(r, 0, n);
645           }
646       }
647   }
648
649   /**
650    * Read the value (the header must be behind).
651    */
652   public static Serializable readValue(InputStream input, final int position,
653     Object value, BoxedValueHelper helper, String id, String[] ids,
654     String codebase)
655   {
656     gnuRuntime g;
657     gnuValueStream c = ((gnuValueStream) input);
658     if (c.getRunTime() == null)
659       {
660         g = new gnuRuntime(codebase, value);
661         c.setRunTime(g);
662       }
663     else
664       {
665         g = c.getRunTime();
666         g.addCodeBase(codebase);
667         g.target = (Serializable) value;
668       }
669     if (value != null)
670       g.objectWritten(value, position);
671
672     if (input instanceof HeadlessInput)
673       ((HeadlessInput) input).subsequentCalls = false;
674
675     boolean ok = true;
676
677     // The user-defined io operations are implemented.
678     if (value instanceof CustomMarshal)
679       {
680         CustomMarshal marsh = (CustomMarshal) value;
681         marsh.unmarshal((DataInputStream) input);
682       }
683     else
684     // The IDL-generated io operations are implemented.
685     if (value instanceof Streamable)
686       {
687         ((Streamable) value)._read(input);
688       }
689     else if (helper != null)
690       {
691         // If helper is non-null the value should normally be null.
692         value = helper.read_value(input);
693         g.objectWritten(value, position);
694       }
695     else
696       {
697         ok = false;
698         ValueFactory factory = null;
699         org.omg.CORBA_2_3.ORB orb = (org.omg.CORBA_2_3.ORB) input.orb();
700
701         if (id != null)
702           factory = orb.lookup_value_factory(id);
703
704         if (factory == null && ids != null)
705           {
706             for (int i = 0; i < ids.length && factory == null; i++)
707               {
708                 factory = orb.lookup_value_factory(ids[i]);
709               }
710           }
711
712         if (factory != null)
713           {
714             value = factory.read_value((org.omg.CORBA_2_3.portable.InputStream) input);
715             ok = true;
716           }
717       }
718
719     if (!ok && value instanceof Serializable)
720     // Delegate to ValueHandler
721       {
722         if (ids != null && ids.length > 0)
723           id = ids[0];
724
725         value = handler.readValue(input, position, value.getClass(), id, g);
726         ok = true;
727       }
728
729     if (!ok)
730       {
731         if (value != null)
732           {
733             MARSHAL m = new MARSHAL(value.getClass().getName()
734             + " must be Streamable, CustomMarshal or Serializable");
735             m.minor = Minor.UnsupportedValue;
736             throw m;
737           }
738         else
739           {
740             MARSHAL m = new MARSHAL("Unable to instantiate " + id + ":" + list(ids)
741             + " helper " + helper);
742             m.minor = Minor.UnsupportedValue;
743             throw m;
744           }
745       }
746     else
747       return (Serializable) value;
748   }
749
750   /**
751    * Conveniency method to list ids in exception reports.
752    */
753   static String list(String[] s)
754   {
755     if (s == null)
756       return "null";
757     else
758       {
759         CPStringBuilder b = new CPStringBuilder("{");
760         for (int i = 0; i < s.length; i++)
761           {
762             b.append(s[i]);
763             b.append(" ");
764           }
765         b.append("}");
766         return b.toString();
767       }
768   }
769
770   /**
771    * Write the value base into the given stream.
772    * 
773    * @param output a stream to write to.
774    * 
775    * @param value a value type object, must be either Streamable or
776    * CustomMarshal.
777    * 
778    * @throws MARSHAL if the writing failed due any reason.
779    */
780   public static void write(OutputStream output, Serializable value)
781   {
782     // Write null if this is a null value.
783     if (value == null)
784       output.write_long(vt_NULL);
785     else if (value instanceof String)
786       write(output, value, m_StringValueHelper);
787     else
788       write(output, value, value.getClass());
789   }
790
791   /**
792    * Write the value base into the given stream, stating that it is an instance
793    * of the given class.
794    * 
795    * @param output a stream to write to.
796    * 
797    * @param value a value to write.
798    * 
799    * @throws MARSHAL if the writing failed due any reason.
800    */
801   public static void write(OutputStream output, Serializable value,
802     Class substitute)
803   {
804     // Write null if this is a null value.
805     if (value == null)
806       output.write_long(vt_NULL);
807     else if (value instanceof String || substitute == String.class)
808       writeString(output, value);
809     else
810       {
811         String vId = ObjectCreator.getRepositoryId(value.getClass());
812         if (substitute == null || value.getClass().equals(substitute))
813           write_instance(output, value, vId, getHelper(value.getClass(), vId));
814         else
815           {
816             String vC = ObjectCreator.getRepositoryId(substitute);
817             String[] ids = new String[] { vId, vC };
818             BoxedValueHelper h = getHelper(substitute.getClass(), ids);
819             // If the helper is available, it is also responsible for
820             // providing the repository Id. Otherwise, write both
821             // ids.
822             if (h == null)
823               write_instance(output, value, ids, null);
824             else
825               write_instance(output, value, h.get_id(), null);
826           }
827       }
828   }
829
830   /**
831    * Write the value base into the given stream, supplementing it with an array
832    * of the provided repository ids plus the repository id, derived from the
833    * passed value.
834    * 
835    * @param output a stream to write to.
836    * 
837    * @param value a value to write.
838    * 
839    * @throws MARSHAL if the writing failed due any reason.
840    */
841   public static void write(OutputStream output, Serializable value,
842     String[] multiple_ids)
843   {
844     // Write null if this is a null value.
845     if (value == null)
846       output.write_long(vt_NULL);
847     else
848       {
849         String[] ids = new String[multiple_ids.length + 1];
850         ids[0] = ObjectCreator.getRepositoryId(value.getClass());
851         System.arraycopy(multiple_ids, 0, ids, 1, multiple_ids.length);
852         BoxedValueHelper h = getHelper(value.getClass(), ids);
853         write_instance(output, value, ids, h);
854       }
855   }
856
857   /**
858    * Write value when its repository Id is explicitly given. Only this Id is
859    * written, the type of value is not taken into consideration.
860    * 
861    * @param output an output stream to write into.
862    * @param value a value to write.
863    * @param id a value repository id.
864    */
865   public static void write(OutputStream output, Serializable value, String id)
866   {
867     if (value == null)
868       output.write_long(vt_NULL);
869     else
870       write_instance(output, value, id, getHelper(value.getClass(), id));
871   }
872
873   /**
874    * Write standard value type header, followed by contents, produced by the
875    * boxed value helper.
876    * 
877    * @param output the stream to write to.
878    * @param value the value to write, can be null.
879    * @param helper the helper that writes the value content if it is not null
880    * (must be provided for this method).
881    */
882   public static void write(OutputStream output, Serializable value,
883     BoxedValueHelper helper)
884   {
885     if (helper == null)
886       throw new AssertionError("Helper must be provided");
887     if (value == null)
888       output.write_long(vt_NULL);
889     else
890       write_instance(output, value, helper.get_id(), helper);
891   }
892
893   /**
894    * Write the parameter that is surely a string and not null.
895    */
896   private static void writeString(OutputStream output, Serializable string)
897   {
898     write_instance(output, string, m_StringValueHelper.get_id(),
899       m_StringValueHelper);
900   }
901
902   /**
903    * Write value when its repository Id is explicitly given. Does not handle
904    * null.
905    * 
906    * @param output an output stream to write into.
907    * @param value a value to write.
908    * @param ids a value repository id (can be either single string or string
909    * array).
910    * @param helper a helper, writing object - specifical part. Can be null if
911    * the value should be written using other methods.
912    */
913   static void write_instance(OutputStream output, Serializable value,
914     Object ids, BoxedValueHelper helper)
915   {
916     gnuValueStream rout = null;
917     gnuRuntime runtime = null;
918
919     try
920       {
921         if (output instanceof gnuValueStream)
922           {
923             int position;
924             rout = (gnuValueStream) output;
925             runtime = rout.getRunTime();
926
927             if (runtime == null)
928               {
929                 runtime = new gnuRuntime(null, value);
930                 rout.setRunTime(runtime);
931                 rout.getRunTime().objectWritten(value,
932                   position = rout.getPosition());
933               }
934             else if (runtime.target == value)
935               {
936                 if (!writeSelf(output, value))
937                   throw new InternalError("Recursive helper call for "
938                     + value.getClass().getName());
939                 return;
940               }
941             else
942               {
943                 position = runtime.isWrittenAt(value);
944                 if (position >= 0)
945                   {
946                     // The object was already written.
947                     output.write_long(vt_INDIRECTION);
948                     output.write_long(position - rout.getPosition());
949                     // Replacing object write data by indirection reference.
950                     return;
951                   }
952                 else
953                   {
954                     runtime.objectWritten(value, position = rout.getPosition());
955                   }
956               }
957           }
958
959         int value_tag = vt_VALUE_TAG;
960
961         if (ids instanceof String)
962           value_tag |= vf_ID;
963         else if (ids instanceof String[])
964           // OMG standard requires to set both flags.
965           value_tag |= vf_MULTIPLE_IDS | vf_ID;
966
967         int chunkSizeLocation;
968
969         OutputStream outObj;
970
971         if (USE_CHUNKING)
972           {
973             // Wrap the value being written into one chunk (makes sense only for
974             // compatibility reasons).
975             outObj = output;
976             value_tag |= vf_CHUNKING;
977           }
978         else
979           outObj = output;
980
981         output.write_long(value_tag);
982
983         if ((value_tag & vf_MULTIPLE_IDS) != 0)
984           write_string_array(output, (String[]) ids);
985         else if ((value_tag & vf_ID) != 0)
986           write_string(output, (String) ids);
987
988         if (USE_CHUNKING)
989           {
990             // So far, write 0x55555555 instead of the chunk size (alignment may
991             // take place).
992             output.write_long(0x55555555);
993             // If the chunking is involved, the chunk size must be written here.
994             chunkSizeLocation = rout.getPosition() - INT_SIZE;
995           }
996         else
997           // Not in use for this case.
998           chunkSizeLocation = -1;
999
1000         writeValue(outObj, value, helper);
1001
1002         if (USE_CHUNKING)
1003           {
1004             // Write the chunk size where the place for it was reserved.
1005             int chunkSize = rout.getPosition() - chunkSizeLocation - INT_SIZE;
1006             int current = rout.getPosition();
1007             rout.seek(chunkSizeLocation);
1008             output.write_long(chunkSize);
1009             rout.seek(current);
1010
1011             // The end of record marker.
1012             output.write_long(-1);
1013           }
1014       }
1015     finally
1016       {
1017         if (runtime != null)
1018           runtime.target = null;
1019       }
1020   }
1021
1022   /**
1023    * Write value (after header).
1024    */
1025   static void writeValue(OutputStream output, Serializable value,
1026     BoxedValueHelper helper)
1027   {
1028     ((gnuValueStream) output).getRunTime().target = value;
1029     if (helper != null)
1030       helper.write_value(output, value);
1031     else if (!writeSelf(output, value))
1032       {
1033         // Try to find helper via class loader.
1034         boolean ok = false;
1035
1036         if (!ok)
1037           {
1038             if (output instanceof BufferedCdrOutput)
1039               {
1040                 BufferedCdrOutput b = (BufferedCdrOutput) output;
1041                 if (b.runtime == null)
1042                   b.runtime = new gnuRuntime(null, value);
1043               }
1044
1045             handler.writeValue(output, value);
1046           }
1047       }
1048   }
1049
1050   /**
1051    * Try to write value supposing that it implements self-streamable interfaces.
1052    * Return false if it does not or true on success.
1053    */
1054   static boolean writeSelf(OutputStream output, Serializable value)
1055   {
1056     // User defined write method is present.
1057     if (value instanceof CustomMarshal)
1058       {
1059         ((CustomMarshal) value).marshal((DataOutputStream) output);
1060         return true;
1061       }
1062     else if (value instanceof Streamable)
1063       {
1064         ((Streamable) value)._write(output);
1065         return true;
1066       }
1067     return false;
1068   }
1069
1070   /**
1071    * Read the indirection data and return the object that was already written to
1072    * this stream.
1073    * 
1074    * @param an_input the input stream, must be BufferredCdrInput.
1075    */
1076   static Serializable readIndirection(InputStream an_input)
1077   {
1078     if (!(an_input instanceof gnuValueStream))
1079       throw new NO_IMPLEMENT(gnuValueStream.class.getName()
1080         + " expected as parameter");
1081
1082     gnuValueStream in = (gnuValueStream) an_input;
1083
1084     int current_pos = in.getPosition();
1085
1086     int offset = an_input.read_long();
1087     if (offset > -INT_SIZE)
1088       {
1089         MARSHAL m = new MARSHAL("Indirection tag refers to " + offset
1090         + " (must be less than -" + INT_SIZE + ")");
1091         m.minor = Minor.Offset;
1092         throw m;
1093       }
1094
1095     int stored_at = current_pos + offset;
1096
1097     if (in.getRunTime() == null)
1098       {
1099         MARSHAL m = new MARSHAL(stored_at + " offset " + offset + ": not written");
1100         m.minor = Minor.Value;
1101         throw m;
1102       }
1103
1104     return (Serializable) in.getRunTime().isObjectWrittenAt(stored_at, offset);
1105   }
1106
1107   /**
1108    * Check the passed value tag for correctness.
1109    * 
1110    * @param value_tag a tag to check, must be between 0x7fffff00 and 0x7fffffff
1111    * 
1112    * @throws MARSHAL if the tag is outside this interval.
1113    */
1114   static void checkTag(int value_tag)
1115   {
1116     if ((value_tag < 0x7fffff00 || value_tag > 0x7fffffff)
1117       && value_tag != vt_NULL && value_tag != vt_INDIRECTION)
1118       {
1119         MARSHAL m = new MARSHAL("Invalid value record, unsupported header tag: "
1120         + value_tag + " (0x" + Integer.toHexString(value_tag) + ")");
1121         m.minor = Minor.ValueHeaderTag;
1122         throw m;
1123       }
1124
1125     if ((value_tag & vf_MULTIPLE_IDS) != 0 && (value_tag & vf_ID) == 0)
1126       {
1127         MARSHAL m = new MARSHAL("Invalid value record header flag combination (0x"
1128         + Integer.toHexString(value_tag) + ")");
1129         m.minor = Minor.ValueHeaderFlags;
1130         throw m;
1131       }
1132   }
1133
1134   /**
1135    * Throw MARSHAL.
1136    */
1137   static void throwIt(String msg, String id1, String id2, Throwable e)
1138     throws MARSHAL
1139   {
1140     MARSHAL m = new MARSHAL(msg + ":'" + id1 + "' versus '" + id2 + "'");
1141     if (e != null)
1142       m.initCause(e);
1143     m.minor = Minor.Value;
1144     throw m;
1145   }
1146
1147   /**
1148    * Load class by name and create the instance.
1149    */
1150   static Object createInstance(String id, String[] ids, String codebase)
1151   {
1152     Object o = null;
1153
1154     if (id != null)
1155       o = _createInstance(id, codebase);
1156
1157     if (ids != null)
1158       for (int i = 0; i < ids.length && o == null; i++)
1159         o = _createInstance(ids[i], codebase);
1160     return o;
1161   }
1162
1163   static Object _createInstance(String id, String codebase)
1164   {
1165     if (id == null)
1166       return null;
1167     if (id.equals(StringValueHelper.id()))
1168       return "";
1169     StringTokenizer st = new StringTokenizer(id, ":");
1170
1171     String prefix = st.nextToken();
1172     if (prefix.equalsIgnoreCase("IDL"))
1173       return ObjectCreator.Idl2Object(id);
1174     else if (prefix.equalsIgnoreCase("RMI"))
1175       {
1176         String className = st.nextToken();
1177         String hashCode = st.nextToken();
1178         String sid = null;
1179         if (st.hasMoreElements())
1180           sid = st.nextToken();
1181
1182         try
1183           {
1184             Class objectClass = Util.loadClass(className, codebase,
1185               Vio.class.getClassLoader());
1186
1187             String rid = ObjectCreator.getRepositoryId(objectClass);
1188
1189             if (!rid.equals(id))
1190               {
1191                 // If direct string comparison fails, compare by meaning.
1192                 StringTokenizer st2 = new StringTokenizer(rid, ":");
1193                 if (!st2.nextToken().equals("RMI"))
1194                   throw new InternalError("RMI format expected: '" + rid + "'");
1195                 if (!st2.nextToken().equals(className))
1196                   throwIt("Class name mismatch", id, rid, null);
1197
1198                 try
1199                   {
1200                     long h1 = Long.parseLong(hashCode, 16);
1201                     long h2 = Long.parseLong(st2.nextToken(), 16);
1202                     if (h1 != h2)
1203                       throwIt("Hashcode mismatch", id, rid, null);
1204
1205                     if (sid != null && st2.hasMoreTokens())
1206                       {
1207                         long s1 = Long.parseLong(hashCode, 16);
1208                         long s2 = Long.parseLong(st2.nextToken(), 16);
1209                         if (s1 != s2)
1210                           throwIt("serialVersionUID mismatch", id, rid, null);
1211                       }
1212                   }
1213                 catch (NumberFormatException e)
1214                   {
1215                     throwIt("Invalid hashcode or svuid format: ", id, rid, e);
1216                   }
1217               }
1218
1219             // Low - level instantiation required here.
1220             return instantiateAnyWay(objectClass);
1221           }
1222         catch (Exception ex)
1223           {
1224             MARSHAL m = new MARSHAL("Unable to instantiate " + id);
1225             m.minor = Minor.Instantiation;
1226             m.initCause(ex);
1227             throw m;
1228           }
1229       }
1230     else
1231       throw new NO_IMPLEMENT("Unsupported prefix " + prefix + ":");
1232   }
1233
1234   /**
1235    * Read string, expecting the probable indirection.
1236    */
1237   static String read_string(InputStream input)
1238   {
1239     gnuValueStream g = (gnuValueStream) input;
1240     int previous = g.getPosition();
1241     int l = input.read_long();
1242     if (l != vt_INDIRECTION)
1243       {
1244         g.seek(previous);
1245         String s = input.read_string();
1246         if (g.getRunTime() == null)
1247           g.setRunTime(new gnuRuntime(null, null));
1248         g.getRunTime().singleIdWritten(s, previous);
1249         return s;
1250       }
1251     else
1252       {
1253         gnuRuntime r = g.getRunTime();
1254         int base = g.getPosition();
1255         int delta = input.read_long();
1256         if (r == null)
1257           {
1258             previous = g.getPosition();
1259             g.seek(base + delta);
1260             String indir = input.read_string();
1261             g.seek(previous);
1262             return indir;
1263           }
1264         else
1265           {
1266             return (String) r.isObjectWrittenAt(base + delta, delta);
1267           }
1268       }
1269   }
1270
1271   /**
1272    * Read string array, expecting the probable indirection.
1273    */
1274   static String[] read_string_array(InputStream input)
1275   {
1276     gnuValueStream g = (gnuValueStream) input;
1277     int previous = g.getPosition();
1278     int l = input.read_long();
1279     if (l != vt_INDIRECTION)
1280       {
1281         g.seek(previous);
1282         String[] s = StringSeqHelper.read(input);
1283         if (g.getRunTime() == null)
1284           g.setRunTime(new gnuRuntime(null, null));
1285         g.getRunTime().objectWritten(s, previous);
1286         return s;
1287       }
1288     else
1289       {
1290         gnuRuntime r = g.getRunTime();
1291         int base = g.getPosition();
1292         int delta = input.read_long();
1293         if (r == null)
1294           {
1295             previous = g.getPosition();
1296             g.seek(base + delta);
1297             String[] indir = StringSeqHelper.read(input);
1298             g.seek(previous);
1299             return indir;
1300           }
1301         else
1302           {
1303             return (String[]) r.isObjectWrittenAt(base + delta, delta);
1304           }
1305       }
1306   }
1307
1308   /**
1309    * Write repository Id, probably shared.
1310    */
1311   static void write_string(OutputStream output, String id)
1312   {
1313     if (output instanceof gnuValueStream)
1314       {
1315         gnuValueStream b = (gnuValueStream) output;
1316         if (b != null)
1317           {
1318             int written = b.getRunTime().idWrittenAt(id);
1319             if (written >= 0)
1320               {
1321                 // Reuse existing id record.
1322                 output.write_long(vt_INDIRECTION);
1323                 int p = b.getPosition();
1324                 output.write_long(written - p);
1325               }
1326             else
1327               {
1328                 b.getRunTime().singleIdWritten(id, b.getPosition());
1329                 output.write_string(id);
1330               }
1331           }
1332       }
1333     else
1334       output.write_string(id);
1335   }
1336
1337   /**
1338    * Write repository Id, probably shared.
1339    */
1340   static void write_string_array(OutputStream output, String[] ids)
1341   {
1342     if (output instanceof gnuValueStream)
1343       {
1344         gnuValueStream b = (gnuValueStream) output;
1345         if (b != null)
1346           {
1347             int written = b.getRunTime().idWrittenAt(ids);
1348             if (written >= 0)
1349               {
1350                 // Reuse existing id record.
1351                 output.write_long(vt_INDIRECTION);
1352                 int p = b.getPosition();
1353                 output.write_long(written - p);
1354               }
1355             else
1356               {
1357                 b.getRunTime().multipleIdsWritten(ids, b.getPosition());
1358                 StringSeqHelper.write(output, ids);
1359               }
1360           }
1361       }
1362     else
1363       StringSeqHelper.write(output, ids);
1364   }
1365
1366   /**
1367    * Get the helper that could write the given object, or null if no pre-defined
1368    * helper available for this object.
1369    */
1370   public static BoxedValueHelper getHelper(Class x, Object ids)
1371   {
1372     if (x != null && x.equals(String.class))
1373       return m_StringValueHelper;
1374     else if (x != null && x.isArray())
1375       return new ArrayValueHelper(x);
1376     else if (ids instanceof String)
1377       return locateHelper((String) ids);
1378     else if (ids instanceof String[])
1379       {
1380         String[] ia = (String[]) ids;
1381         BoxedValueHelper h;
1382         for (int i = 0; i < ia.length; i++)
1383           {
1384             h = locateHelper(ia[i]);
1385             if (h != null)
1386               return h;
1387           }
1388         return null;
1389       }
1390     else
1391       return null;
1392   }
1393
1394   /**
1395    * Get the helper that could write the given object, or null if no pre-defined
1396    * helper available for this object.
1397    */
1398   public static BoxedValueHelper getHelper(Class x, String id)
1399   {
1400     if (x != null && x.equals(String.class))
1401       return m_StringValueHelper;
1402     else if (x != null && x.isArray())
1403       return new ArrayValueHelper(x);
1404     else
1405       return locateHelper(id);
1406   }
1407
1408   /**
1409    * Try to locate helper from the repository id.
1410    */
1411   static BoxedValueHelper locateHelper(String id)
1412   {
1413     if (id != null)
1414       {
1415         if (id.equals(m_StringValueHelper.get_id()))
1416           return m_StringValueHelper;
1417         else
1418         // Try to locate helper for IDL type.
1419         if (id.startsWith("IDL:"))
1420           {
1421             try
1422               {
1423                 Class helperClass = ObjectCreator.findHelper(id);
1424                 if (BoxedValueHelper.class.isAssignableFrom(helperClass))
1425                   return (BoxedValueHelper) helperClass.newInstance();
1426                 else if (helperClass != null)
1427                   return new IDLTypeHelper(helperClass);
1428                 else
1429                   return null;
1430               }
1431             catch (Exception ex)
1432               {
1433                 return null;
1434               }
1435           }
1436       }
1437     return null;
1438   }
1439
1440   /**
1441    * Get the current position.
1442    */
1443   static int getCurrentPosition(InputStream x)
1444   {
1445     if (x instanceof gnuValueStream)
1446       return ((gnuValueStream) x).getPosition();
1447     else
1448       return 0;
1449   }
1450
1451   /**
1452    * Instantiate an instance of this class anyway; also in the case when it has
1453    * no parameterless or any other constructor. The fields will be assigned
1454    * while reading the class from the stream.
1455    * 
1456    * @param clazz a class for that the instance should be instantiated.
1457    */
1458   public static Object instantiateAnyWay(Class clazz)
1459     throws Exception
1460   {
1461     Class first_nonserial = clazz;
1462
1463     while (Serializable.class.isAssignableFrom(first_nonserial)
1464       || Modifier.isAbstract(first_nonserial.getModifiers()))
1465       first_nonserial = first_nonserial.getSuperclass();
1466
1467     final Class local_constructor_class = first_nonserial;
1468
1469     Constructor constructor = local_constructor_class.getDeclaredConstructor(new Class[0]);
1470
1471     return VMVio.allocateObject(clazz, constructor.getDeclaringClass(),
1472       constructor);
1473   }
1474 }