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    */
read(InputStream input)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    */
read(InputStream input, String repository_id)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    */
read(InputStream input, Class value_class)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    */
read(InputStream input, Object value_instance, BoxedValueHelper helper)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    */
read(InputStream input, BoxedValueHelper helper)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    */
read_instance(InputStream input, final int position, Object value, int value_tag, BoxedValueHelper helper, String id, String[] ids, String codebase)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    */
createBuffer(InputStream input, int proposed_size)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    */
readNestedValue(int value_tag, InputStream input, BufferedCdrOutput output, int level)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    */
readValue(InputStream input, final int position, Object value, BoxedValueHelper helper, String id, String[] ids, String codebase)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    */
list(String[] s)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    */
write(OutputStream output, Serializable value)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    */
write(OutputStream output, Serializable value, Class substitute)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    */
write(OutputStream output, Serializable value, String[] multiple_ids)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    */
write(OutputStream output, Serializable value, String id)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    */
write(OutputStream output, Serializable value, BoxedValueHelper helper)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    */
writeString(OutputStream output, Serializable string)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    */
write_instance(OutputStream output, Serializable value, Object ids, BoxedValueHelper helper)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    */
writeValue(OutputStream output, Serializable value, BoxedValueHelper helper)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    */
writeSelf(OutputStream output, Serializable value)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    */
readIndirection(InputStream an_input)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    */
checkTag(int value_tag)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    */
throwIt(String msg, String id1, String id2, Throwable e)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    */
createInstance(String id, String[] ids, String codebase)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 
_createInstance(String id, String codebase)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    */
read_string(InputStream input)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    */
read_string_array(InputStream input)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    */
write_string(OutputStream output, String id)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    */
write_string_array(OutputStream output, String[] ids)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    */
getHelper(Class x, Object ids)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    */
getHelper(Class x, String id)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    */
locateHelper(String id)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    */
getCurrentPosition(InputStream x)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    */
instantiateAnyWay(Class clazz)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 }
1475