1
2
3
4
5
6
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 }