1
package com.eucalyptus.binding;
4
import java.io.FileNotFoundException;
5
import java.io.PrintWriter;
6
import java.lang.reflect.Field;
7
import java.lang.reflect.ParameterizedType;
8
import java.lang.reflect.Type;
9
import java.util.ArrayList;
10
import java.util.Deque;
11
import java.util.HashMap;
12
import java.util.LinkedList;
13
import java.util.List;
15
import org.apache.log4j.Logger;
16
import javassist.Modifier;
17
import com.google.common.base.Predicate;
18
import com.google.common.collect.Iterables;
20
public class InternalSoapBindingGenerator extends BindingGenerator {
21
private static Logger LOG = Logger.getLogger( InternalSoapBindingGenerator.class );
22
private final String ns = "http://msgs.eucalyptus.com";
23
private static String INDENT = "";
24
private final File outFile;
25
private PrintWriter out;
26
private String bindingName;
27
private int indent = 0;
28
private Map<String, TypeBinding> typeBindings = new HashMap<String, TypeBinding>( ) {
30
put( Integer.class.getCanonicalName( ), new IntegerTypeBinding( ) );
31
put( Boolean.class.getCanonicalName( ), new BooleanTypeBinding( ) );
32
put( String.class.getCanonicalName( ), new StringTypeBinding( ) );
33
put( Long.class.getCanonicalName( ), new LongTypeBinding( ) );
34
put( "boolean", new BooleanTypeBinding( ) );
35
put( "int", new IntegerTypeBinding( ) );
36
put( "long", new LongTypeBinding( ) );
37
put( java.util.Date.class.getCanonicalName( ), new StringTypeBinding( ) );
41
private static List<String> badClasses = new ArrayList<String>( ) {
43
add( ".*HttpResponseStatus" );
49
private static List<String> badFields = new ArrayList<String>( ) {
52
add( "\\w*\\$\\w*\\$*.*" );
59
public InternalSoapBindingGenerator( ) {
60
this.outFile = new File( "modules/msgs/src/main/resources/msgs-binding.xml" );
61
if ( outFile.exists( ) ) {
65
this.out = new PrintWriter( outFile );
66
} catch ( FileNotFoundException e ) {
67
e.printStackTrace( System.err );
70
this.bindingName = this.ns.replaceAll( "(http://)|(/$)", "" ).replaceAll( "[./-]", "_" );
71
this.out.write( "<binding xmlns:euca=\"" + ns + "\" name=\"" + bindingName + "\">\n" );
72
this.out.write( " <namespace uri=\"" + ns + "\" default=\"elements\" prefix=\"euca\"/>\n" );
76
public ElemItem peek( ) {
77
return this.elemStack.peek( );
81
public void processClass( Class klass ) {
82
if ( BindingGenerator.DATA_TYPE.isAssignableFrom( klass ) || BindingGenerator.MSG_TYPE.isAssignableFrom( klass ) ) {
83
String mapping = new RootObjectTypeBinding( klass ).process( );
90
public void close( ) {
91
this.out.write( "</binding>" );
96
public TypeBinding getTypeBinding( Field field ) {
97
Class itsType = field.getType( );
98
if ( this.isIgnored( field ) ) {
99
return new NoopTypeBinding( field );
100
} else if ( List.class.isAssignableFrom( itsType ) ) {
101
Class listType = getTypeArgument( field );
102
if ( listType == null ) {
103
System.err.printf( "IGNORE: %-70s [type=%s] NO GENERIC TYPE FOR LIST\n", field.getDeclaringClass( ).getCanonicalName( ) + "."+ field.getName( ), listType );
104
return new NoopTypeBinding( field );
105
} else if ( typeBindings.containsKey( listType.getCanonicalName( ) ) ) {
106
return new CollectionTypeBinding( field.getName( ), typeBindings.get( listType.getCanonicalName( ) ) );
107
} else if ( BindingGenerator.DATA_TYPE.isAssignableFrom( listType ) ) {
108
return new CollectionTypeBinding( field.getName( ), new ObjectTypeBinding( field.getName( ), listType ) );
110
System.err.printf( "IGNORE: %-70s [type=%s] LIST'S GENERIC TYPE DOES NOT CONFORM TO EucalyptusData\n", field.getDeclaringClass( ).getCanonicalName( ) + "."+ field.getName( ), listType.getCanonicalName( ) );
111
return new NoopTypeBinding( field );
113
} else if ( typeBindings.containsKey( itsType.getCanonicalName( ) ) ) {
114
TypeBinding t = typeBindings.get( itsType.getCanonicalName( ) );
116
t = typeBindings.get( itsType.getCanonicalName( ) ).getClass( ).newInstance( );
117
} catch ( Exception e ) {}
118
return t.value( field.getName( ) );
119
} else if ( BindingGenerator.DATA_TYPE.isAssignableFrom( field.getType( ) ) ) {
120
return new ObjectTypeBinding( field );
122
System.err.printf( "IGNORE: %-70s [type=%s] TYPE DOES NOT CONFORM TO EucalyptusData\n", field.getDeclaringClass( ).getCanonicalName( ) + "."+ field.getName( ), field.getType( ).getCanonicalName( ) );
123
return new NoopTypeBinding( field );
127
class RootObjectTypeBinding extends TypeBinding {
131
public RootObjectTypeBinding( Class type ) {
132
InternalSoapBindingGenerator.this.indent = 2;
134
if ( Object.class.equals( type.getSuperclass( ) ) ) {
136
} else if ( type.getSuperclass( ).getSimpleName( ).equals( "EucalyptusData" ) ) {
144
public String getTypeName( ) {
145
return type.getCanonicalName( );
148
public String process( ) {
149
if( this.type.getCanonicalName( ) == null ) {
150
new RuntimeException( "" + this.type ).printStackTrace( );
152
this.elem( Elem.mapping );
154
this.attr( "abstract", "true" );
156
this.attr( "name", this.type.getSimpleName( ) ).attr( "extends", this.type.getSuperclass( ).getCanonicalName( ) );
158
this.attr( "class", this.type.getCanonicalName( ) );
159
if( BindingGenerator.MSG_TYPE.isAssignableFrom( this.type.getSuperclass( ) ) || BindingGenerator.DATA_TYPE.isAssignableFrom( this.type.getSuperclass( ) ) ) {
160
this.elem( Elem.structure ).attr( "map-as", this.type.getSuperclass().getCanonicalName( ) ).end( );
162
for ( Field f : type.getDeclaredFields( ) ) {
163
TypeBinding tb = getTypeBinding( f );
164
if ( !( tb instanceof NoopTypeBinding ) ) {
165
System.out.printf( "BOUND: %-70s [type=%s:%s]\n", f.getDeclaringClass( ).getCanonicalName( ) +"."+ f.getName( ), tb.getTypeName( ), f.getType( ).getCanonicalName( ) );
166
this.append( tb.toString( ) );
171
return this.toString( );
175
@SuppressWarnings( "unchecked" )
176
public static Class getTypeArgument( Field f ) {
177
Type t = f.getGenericType( );
178
if ( t != null && t instanceof ParameterizedType ) {
179
Type tv = ( ( ParameterizedType ) t ).getActualTypeArguments( )[0];
180
if ( tv instanceof Class ) {
181
return ( ( Class ) tv );
187
abstract class TypeBinding {
188
private StringBuilder buf = new StringBuilder( );
190
public abstract String getTypeName( );
192
private TypeBinding reindent( int delta ) {
193
InternalSoapBindingGenerator.this.indent += delta;
195
for ( int i = 0; i < indent; i++ ) {
201
private TypeBinding indent( String addMe ) {
202
this.reindent( +1 ).append( INDENT ).append( addMe );
206
private TypeBinding outdent( String addMe ) {
207
this.reindent( -1 ).append( INDENT ).append( addMe );
211
protected TypeBinding append( Object o ) {
212
this.buf.append( ""+o );
216
protected TypeBinding eolIn( ) {
217
this.append( "\n" ).indent( INDENT );
221
protected TypeBinding eolOut( ) {
222
this.append( "\n" ).outdent( INDENT );
226
protected TypeBinding eol( ) {
227
this.append( "\n" ).append( INDENT );
231
protected TypeBinding value( String name ) {
232
this.elem( Elem.value ).attr( "name", name ).attr( "field", name ).attr( "usage", "optional" ).attr( "style", "element" ).end( );
236
private TypeBinding begin( ) {
237
ElemItem top = InternalSoapBindingGenerator.this.elemStack.peek( );
238
if ( top != null && top.children ) {
240
} else if ( top != null && !top.children ) {
241
this.append( ">" ).eolIn( );
249
protected TypeBinding elem( Elem name ) {
250
this.begin( ).append( "<" ).append( name.toString( ) ).append( " " );
251
InternalSoapBindingGenerator.this.elemStack.push( new ElemItem( name, InternalSoapBindingGenerator.this.indent, false ) );
255
protected TypeBinding end( ) {
256
ElemItem top = InternalSoapBindingGenerator.this.elemStack.pop( );
257
if ( top != null && top.children ) {
258
this.eolOut( ).append( "</" ).append( top.name.toString( ) ).append( ">" );
259
} else if ( top != null && !top.children ) {
267
protected TypeBinding attr( String name, String value ) {
268
this.append( name ).append( "=\"" ).append( value ).append( "\" " );
272
public String toString( ) {
273
String s = buf.toString( );
274
buf = new StringBuilder( buf.capacity( ) );
278
protected TypeBinding collection( String name ) {
279
this.elem( Elem.structure ).attr( "name", name ).attr( "usage", "optional" );
280
this.elem( Elem.collection ).attr( "factory", "com.eucalyptus.binding.Binding.listFactory" ).attr( "field", name )
281
.attr( "item-type", this.getTypeName( ) ).attr( "usage", "required" );
282
this.elem( Elem.structure ).attr( "name", "item" );
283
this.elem( Elem.value ).attr( "name", "entry" ).end( ).end( ).end( ).end( );
289
public boolean isIgnored( final Field field ) {
290
final int mods = field.getModifiers( );
291
final String name = field.getName( );
292
final String type = field.getType( ).getSimpleName( );
293
if ( Modifier.isFinal( mods ) ) {
294
LOG.debug( "Ignoring field with bad type: " + field.getDeclaringClass( ).getCanonicalName( ) + "." + name + " of type " + type
295
+ " due to: final modifier" );
296
} else if ( Modifier.isStatic( mods ) ) {
297
LOG.debug( "Ignoring field with bad type: " + field.getDeclaringClass( ).getCanonicalName( ) + "." + name + " of type " + type
298
+ " due to: static modifier" );
300
boolean ret = Iterables.any( badClasses, new Predicate<String>( ) {
302
public boolean apply( String arg0 ) {
303
if ( type.matches( arg0 ) ) {
304
LOG.debug( "Ignoring field with bad type: " + field.getDeclaringClass( ).getCanonicalName( ) + "." + name + " of type " + type + " due to: " + arg0 );
311
ret |= Iterables.any( badFields, new Predicate<String>( ) {
313
public boolean apply( String arg0 ) {
314
if ( name.matches( arg0 ) ) {
315
LOG.debug( "Ignoring field with bad name: " + field.getDeclaringClass( ).getCanonicalName( ) + "." + name + " of type " + type + " due to: " + arg0 );
326
private class ElemItem {
331
public ElemItem( Elem name, int indent, boolean children ) {
333
this.indent = indent;
334
this.children = children;
338
public String toString( ) {
339
return String.format( "ElemItem [name=%s, indent=%s, children=%s]", this.name, this.indent, Boolean.valueOf( children ) );
344
private Deque<ElemItem> elemStack = new LinkedList<ElemItem>( );
347
structure, collection, value, mapping, binding
349
class IgnoredTypeBinding extends NoopTypeBinding {
351
public IgnoredTypeBinding( Field field ) {
355
class NoopTypeBinding extends TypeBinding {
359
public NoopTypeBinding( Field field ) {
360
this.name = field.getName( );
361
this.type = field.getType( );
365
public String toString( ) {
370
public String getTypeName( ) {
376
class ObjectTypeBinding extends TypeBinding {
380
public ObjectTypeBinding( String name, Class type ) {
385
public ObjectTypeBinding( Field field ) {
386
this.name = field.getName( );
387
this.type = field.getType( );
391
protected TypeBinding collection( String name ) {
392
this.elem( Elem.structure ).attr( "name", name ).attr( "usage", "optional" );
393
this.elem( Elem.collection ).attr( "factory", "com.eucalyptus.binding.Binding.listFactory" ).attr( "field", name ).attr( "usage", "required" );
394
this.elem( Elem.structure ).attr( "name", "item" ).attr( "map-as", type.getCanonicalName( ) );
395
this.end( ).end( ).end( );
400
public String getTypeName( ) {
401
return this.type.getCanonicalName( );
404
public String toString( ) {
405
this.elem( Elem.structure ).attr( "name", this.name ).attr( "field", this.name ).attr( "map-as", this.type.getCanonicalName( ) ).attr( "usage",
407
return super.toString( );
412
class CollectionTypeBinding extends TypeBinding {
413
private TypeBinding type;
416
public CollectionTypeBinding( String name, TypeBinding type ) {
419
LOG.debug( "Found list type: " + type.getClass( ).getCanonicalName( ) );
423
public String getTypeName( ) {
424
return type.getTypeName( );
428
public String toString( ) {
429
LOG.debug( "Found list type: " + this.type.getTypeName( ) + " for name: " + name );
430
String ret = this.type.collection( this.name ).buf.toString( );
431
this.type.collection( this.name ).buf = new StringBuilder( );
437
class IntegerTypeBinding extends TypeBinding {
439
public String getTypeName( ) {
440
return Integer.class.getCanonicalName( );
444
class LongTypeBinding extends TypeBinding {
446
public String getTypeName( ) {
447
return Long.class.getCanonicalName( );
451
class StringTypeBinding extends TypeBinding {
453
public String getTypeName( ) {
454
return String.class.getCanonicalName( );
458
class BooleanTypeBinding extends TypeBinding {
460
public String getTypeName( ) {
461
return Boolean.class.getCanonicalName( );