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