View Javadoc

1   /*
2    * Copyright (C) The MetaClass Group. All rights reserved.
3    *
4    * This software is published under the terms of the Spice
5    * Software License version 1.1, a copy of which has been included
6    * with this distribution in the LICENSE.txt file.
7    */
8   package org.codehaus.metaclass.io;
9   
10  import java.io.DataInputStream;
11  import java.io.DataOutputStream;
12  import java.io.File;
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.OutputStream;
16  import java.util.ArrayList;
17  import java.util.Properties;
18  import org.codehaus.metaclass.model.Attribute;
19  import org.codehaus.metaclass.model.ClassDescriptor;
20  import org.codehaus.metaclass.model.FieldDescriptor;
21  import org.codehaus.metaclass.model.MethodDescriptor;
22  import org.codehaus.metaclass.model.ParameterDescriptor;
23  
24  /***
25   * This is a utility class that writes out a Attributes object to a stream using
26   * binary format outlined in documentation.
27   *
28   * @author Peter Donald
29   * @author Doug Hagan
30   * @version $Revision: 1.25 $ $Date: 2003/12/11 08:41:50 $
31   */
32  public class MetaClassIOBinary
33      extends AbstractMetaClassIO
34  {
35      /*** Constant with instance of MetaClassIO. */
36      public static final MetaClassIOBinary IO = new MetaClassIOBinary();
37  
38      /*** Extension of metadata files that are in binary format. */
39      public static final String EXTENSION = "-meta.binary";
40  
41      /*** The current version of Attributes object. */
42      static final int VERSION = 2;
43  
44      /***
45       * @see MetaClassIO#getResourceName(String)
46       */
47      public String getResourceName( final String classname )
48      {
49          return classname.replace( '.', File.separatorChar ) + EXTENSION;
50      }
51  
52      /***
53       * @see MetaClassIO#deserializeClass
54       */
55      public ClassDescriptor deserializeClass( final InputStream input )
56          throws IOException
57      {
58          final DataInputStream data = new DataInputStream( input );
59          checkVersionHeader( data );
60          final String classname = data.readUTF();
61          final Attribute[] classAttributes = readAttributes( data );
62  
63          final FieldDescriptor[] fields = readFields( data );
64          final MethodDescriptor[] methods = readMethods( data );
65  
66          return
67              new ClassDescriptor( classname,
68                                   classAttributes,
69                                   classAttributes,
70                                   fields,
71                                   methods );
72      }
73  
74      /***
75       * @see AbstractMetaClassIO#serializeClass(OutputStream, ClassDescriptor)
76       */
77      public void serializeClass( final OutputStream output,
78                                  final ClassDescriptor descriptor )
79          throws IOException
80      {
81          final DataOutputStream data = new DataOutputStream( output );
82          try
83          {
84              data.writeInt( VERSION );
85              data.writeUTF( descriptor.getName() );
86              writeAttributes( data, descriptor.getAttributes() );
87              writeFields( data, descriptor.getFields() );
88              writeMethods( data, descriptor.getMethods() );
89          }
90          finally
91          {
92              data.flush();
93          }
94      }
95  
96      /***
97       * Write out a set of fields.
98       *
99       * @param data the output stream
100      * @param fields the fields
101      * @throws IOException if unable to write fields
102      */
103     void writeFields( final DataOutputStream data,
104                       final FieldDescriptor[] fields )
105         throws IOException
106     {
107         data.writeInt( fields.length );
108         for( int i = 0; i < fields.length; i++ )
109         {
110             final FieldDescriptor field = fields[ i ];
111             writeField( data, field );
112         }
113     }
114 
115     /***
116      * Write out a field.
117      *
118      * @param data the output stream
119      * @param field the field
120      * @throws IOException if unable to write field
121      */
122     private void writeField( final DataOutputStream data,
123                              final FieldDescriptor field )
124         throws IOException
125     {
126         data.writeUTF( field.getName() );
127         data.writeUTF( field.getType() );
128         writeAttributes( data, field.getAttributes() );
129     }
130 
131     /***
132      * Write out a set of methods.
133      *
134      * @param data the output stream
135      * @param methods the methods
136      * @throws IOException if unable to write methods
137      */
138     void writeMethods( final DataOutputStream data,
139                        final MethodDescriptor[] methods )
140         throws IOException
141     {
142         data.writeInt( methods.length );
143         for( int i = 0; i < methods.length; i++ )
144         {
145             final MethodDescriptor method = methods[ i ];
146             writeMethod( data, method );
147         }
148     }
149 
150     /***
151      * Write out a method.
152      *
153      * @param data the output stream
154      * @param method the method
155      * @throws IOException if unable to write method
156      */
157     private void writeMethod( final DataOutputStream data,
158                               final MethodDescriptor method )
159         throws IOException
160     {
161         data.writeUTF( method.getName() );
162         data.writeUTF( method.getReturnType() );
163         writeParameters( data, method.getParameters() );
164         writeAttributes( data, method.getAttributes() );
165     }
166 
167     /***
168      * Read in a set of methods.
169      *
170      * @param data the input
171      * @return the methods
172      * @throws IOException if unable to read methods
173      */
174     MethodDescriptor[] readMethods( final DataInputStream data )
175         throws IOException
176     {
177         final int count = data.readInt();
178         if( 0 == count )
179         {
180             return MethodDescriptor.EMPTY_SET;
181         }
182         final ArrayList methodSet = new ArrayList();
183         for( int i = 0; i < count; i++ )
184         {
185             methodSet.add( readMethod( data ) );
186         }
187 
188         return (MethodDescriptor[])methodSet.
189             toArray( new MethodDescriptor[ methodSet.size() ] );
190     }
191 
192     /***
193      * Read in a method.
194      *
195      * @param data the input
196      * @return the method
197      * @throws IOException if unable to read method
198      */
199     private MethodDescriptor readMethod( final DataInputStream data )
200         throws IOException
201     {
202         final String name = data.readUTF();
203         final String type = data.readUTF();
204         final ParameterDescriptor[] parameters = readParameters( data );
205         final Attribute[] attributes = readAttributes( data );
206         return
207             new MethodDescriptor( name,
208                                   type,
209                                   parameters,
210                                   attributes,
211                                   attributes );
212     }
213 
214     /***
215      * Read in a set of fields.
216      *
217      * @param data the input
218      * @return the fields
219      * @throws IOException if unable to read fields
220      */
221     FieldDescriptor[] readFields( final DataInputStream data )
222         throws IOException
223     {
224         final int count = data.readInt();
225         if( 0 == count )
226         {
227             return FieldDescriptor.EMPTY_SET;
228         }
229 
230         final ArrayList fieldSet = new ArrayList();
231         for( int i = 0; i < count; i++ )
232         {
233             fieldSet.add( readField( data ) );
234         }
235         return (FieldDescriptor[])fieldSet.
236             toArray( new FieldDescriptor[ fieldSet.size() ] );
237     }
238 
239     /***
240      * Read in a field.
241      *
242      * @param data the input
243      * @return the field
244      * @throws IOException if unable to read field
245      */
246     private FieldDescriptor readField( final DataInputStream data )
247         throws IOException
248     {
249         final String name = data.readUTF();
250         final String type = data.readUTF();
251         final Attribute[] attributes = readAttributes( data );
252         return new FieldDescriptor( name, type, attributes, attributes );
253     }
254 
255     /***
256      * Read in a set of method parameters.
257      *
258      * @param data the input
259      * @return the method parameters
260      * @throws IOException if unable to read parameters
261      */
262     ParameterDescriptor[] readParameters( final DataInputStream data )
263         throws IOException
264     {
265         final int count = data.readInt();
266         if( 0 == count )
267         {
268             return ParameterDescriptor.EMPTY_SET;
269         }
270 
271         final ArrayList parameters = new ArrayList();
272         for( int i = 0; i < count; i++ )
273         {
274             parameters.add( readParameter( data ) );
275         }
276         return (ParameterDescriptor[])parameters.
277             toArray( new ParameterDescriptor[ parameters.size() ] );
278     }
279 
280     /***
281      * Read in a method parameter.
282      *
283      * @param data the input
284      * @return the method parameter
285      * @throws IOException if unable to read parameter
286      */
287     private ParameterDescriptor readParameter( final DataInputStream data )
288         throws IOException
289     {
290         final String name = data.readUTF();
291         final String type = data.readUTF();
292         return new ParameterDescriptor( name, type );
293     }
294 
295     /***
296      * Write out a set of method parameters.
297      *
298      * @param data the output stream
299      * @param parameters the method parameters
300      * @throws IOException if unable to write parameters
301      */
302     void writeParameters( final DataOutputStream data,
303                           final ParameterDescriptor[] parameters )
304         throws IOException
305     {
306         data.writeInt( parameters.length );
307         for( int i = 0; i < parameters.length; i++ )
308         {
309             final ParameterDescriptor parameter = parameters[ i ];
310             writeParameter( data, parameter );
311         }
312     }
313 
314     /***
315      * Write out a method parameter.
316      *
317      * @param data the output stream
318      * @param parameter the method parameter
319      * @throws IOException if unable to write parameter
320      */
321     private void writeParameter( final DataOutputStream data,
322                                  final ParameterDescriptor parameter )
323         throws IOException
324     {
325         data.writeUTF( parameter.getName() );
326         data.writeUTF( parameter.getType() );
327     }
328 
329     /***
330      * Read in a set of attributes.
331      *
332      * @param data the input stream
333      * @return the attributes
334      * @throws IOException if unable to read attributes
335      */
336     Attribute[] readAttributes( final DataInputStream data )
337         throws IOException
338     {
339         final int count = data.readInt();
340         if( 0 == count )
341         {
342             return Attribute.EMPTY_SET;
343         }
344         final ArrayList attributeSet = new ArrayList();
345         for( int i = 0; i < count; i++ )
346         {
347             final String name = data.readUTF();
348             final String value = data.readUTF();
349             final Properties properties = readAttributeParameters( data );
350 
351             final boolean valuePresent = null != value && value.length() > 0;
352             if( valuePresent &&
353                 properties.size() > 0 )
354             {
355                 final String message =
356                     "Cannot read attributes containing both " +
357                     "text and parameters.";
358                 throw new IOException( message );
359             }
360 
361             final Attribute attribute;
362             if( valuePresent )
363             {
364                 attribute = new Attribute( name, value );
365             }
366             else
367             {
368                 attribute = new Attribute( name, properties );
369             }
370             attributeSet.add( attribute );
371         }
372 
373         return (Attribute[])attributeSet.toArray(
374             new Attribute[ attributeSet.size() ] );
375     }
376 
377     /***
378      * Read in a set of attribute parameters.
379      *
380      * @param data the input
381      * @return the parameters
382      * @throws IOException if unable to read attribute parameters
383      */
384     Properties readAttributeParameters( final DataInputStream data )
385         throws IOException
386     {
387         final Properties parameters = new Properties();
388 
389         final int count = data.readInt();
390         for( int i = 0; i < count; i++ )
391         {
392             final String name = data.readUTF();
393             final String value = data.readUTF();
394             parameters.setProperty( name, value );
395         }
396 
397         return parameters;
398     }
399 
400     /***
401      * Write out the specified attributes.
402      *
403      * @param data the output
404      * @param attributes the attributes
405      * @throws IOException if unable to write attributes
406      */
407     void writeAttributes( final DataOutputStream data,
408                           final Attribute[] attributes )
409         throws IOException
410     {
411         data.writeInt( attributes.length );
412         for( int i = 0; i < attributes.length; i++ )
413         {
414             final Attribute attribute = attributes[ i ];
415             data.writeUTF( attribute.getName() );
416 
417             final String value = attribute.getValue();
418             if( null != value )
419             {
420                 data.writeUTF( value );
421                 writeAttributeParameters( data, null );
422             }
423             else
424             {
425                 data.writeUTF( "" );
426                 writeAttributeParameters( data, attribute );
427             }
428         }
429     }
430 
431     /***
432      * Write out the parameters of an attribute.
433      *
434      * @param data the output
435      * @param attribute the attribute
436      * @throws IOException if unable to write attribute parameters
437      */
438     void writeAttributeParameters( final DataOutputStream data,
439                                    final Attribute attribute )
440         throws IOException
441     {
442         if( null == attribute )
443         {
444             data.writeInt( 0 );
445         }
446         else
447         {
448             final String[] names = attribute.getParameterNames();
449             data.writeInt( names.length );
450             for( int i = 0; i < names.length; i++ )
451             {
452                 final String name = names[ i ];
453                 final String value = attribute.getParameter( name );
454                 data.writeUTF( name );
455                 data.writeUTF( value );
456             }
457         }
458     }
459 
460     /***
461      * Read version header of descriptor to make sure it is something we can
462      * handle and if not throw an exception.
463      *
464      * @param data the input stream
465      * @throws IOException if unable to handle version
466      */
467     private void checkVersionHeader( final DataInputStream data )
468         throws IOException
469     {
470         final int version = data.readInt();
471         if( VERSION != version )
472         {
473             final String message =
474                 "Version mismatch." +
475                 " Expected: " +
476                 VERSION +
477                 " Actual: " + version;
478             throw new IOException( message );
479         }
480     }
481 }