001    /*
002      Copyright (C) 2001-2003 Renaud Pawlak.
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.util;
019    
020    import java.io.*;
021    import java.util.*;
022    import java.lang.reflect.*;
023    
024    /**
025     * This class generates the <code>jac.lib</code> delegators
026     * source-files from the <code>jac.prop</code> file found in the
027     * current directory (exclude all other options).
028     *
029     * @author Renaud Pawlak */
030    
031    public class WrapLib {
032    
033       public static String listAsString(List l) {
034          String s="[";
035          for(int i=0;i<l.size();i++) {
036             s=s+l.get(i);
037             if(i+1<l.size()) s=s+",";
038          }
039          return s;
040       }
041    
042       /**
043        * The entry point of the wraplib program.
044        *
045        * @param args none arguments are expected (parametrization is done
046        * by the <code>jac.prop</code> file in the current directory --
047        * user should define the jac.toWrap property as a list of classes
048        * to wrap */
049    
050       public static void main(String[] args) throws Throwable {
051          String propFileName = "jac.prop";
052          String toAdaptProp = "jac.toWrap";
053          Properties props;
054          Vector classesToAdapt = new Vector();
055          /** Try to load a jac.prop file from the current directory */
056          try {
057            
058             FileInputStream fis = new FileInputStream( propFileName );
059             
060             props = new Properties();
061             props.load( fis );
062             String prop = props.getProperty(toAdaptProp);
063             
064             if ( prop == null ) {
065                System.out.println( "-- ERROR: no property jac.toAdapt found in property file "+propFileName );
066                System.exit(-1);
067                
068             } else {
069                
070                StringTokenizer st = new StringTokenizer( prop );
071                while ( st.hasMoreElements() )
072                   classesToAdapt.add( st.nextElement() );
073             }
074             
075             
076                for ( int i = 0; i < classesToAdapt.size(); i++ ) {
077                   createDelegator( Class.forName( (String) classesToAdapt.get(i) ));
078                }
079                
080          }
081          catch( FileNotFoundException e ) {
082             System.out.println( "-- ERROR: property file "+propFileName+" not found" );
083             System.exit(-1);
084          }
085          catch( Exception e ) { e.printStackTrace(); }
086       }
087    
088       /**
089        * Creates a class that present the same interface that the
090        * original class but that delegates all the work to an instance of
091        * the original class.
092        * 
093        * <p>This feature is implemented to be able to easily wrap
094        * libraries that would have been hardly wrappable on the "as is"
095        * classes, for instance the jdk classes (see the -g option of Jac).
096        *
097        * <p>As a result, this method creates a Java source file in the
098        * src/org/objectweb/jac/lib directory of the JAC distribution. This file should
099        * then be compiled as a regular file.
100        *
101        * @param c the original class */
102    
103       protected static void createDelegator( Class c ) {
104          try {
105             System.out.println("-- Generating delegator version of " + c + ".");
106             File f = new File( 
107                "src/org/objectweb/jac/lib/" + c.getName().replace('.','/') + ".java" );
108             if ( f.exists() )
109                f.delete();
110             f.getParentFile().mkdirs();
111             f.createNewFile();
112             String shortName = c.getName().substring( c.getName().lastIndexOf('.') + 1 );
113             FileWriter fw = new FileWriter( f );
114             fw.write( "/**\n" +
115                       " * This class delegates to " + c.getName() + "\n" +
116                       " * This file was automatically generated by JAC (-g option)\n" +
117                       " * DO NOT MODIFY\n" +
118                       " * Author: Renaud Pawlak (pawlak@cnam.fr)\n" +
119                       " */\n" );
120             fw.write( "\npackage jac.lib." + c.getPackage().getName() + ";\n" );
121             fw.write( "\npublic class " + shortName + ((c.getInterfaces().length == 0)? "" : " implements " +
122                       arrayToString( createArray( c.getInterfaces() ) )) );
123             fw.write( " {\n" );
124             fw.write( "\n    private " + c.getName() + " delegate = new " + c.getName() + "();\n" );
125             fw.write( "\n    public Object clone() {\n        Object result=null;\n        try { result=super.clone(); } catch(Exception e) {};\n        (("+ shortName +")result).delegate=("+c.getName()+")delegate.clone();\n        return result;\n    }\n");
126             fw.write( "\n    public boolean equals(Object o1) {\n        return (delegate==null?super.equals(o1):delegate.equals(o1));\n    }\n");
127             //fw.write( "\n    public String toString() {\n        return delegate.toString();\n    }\n");
128             Method[] ms = c.getMethods();
129             for ( int i = 0; i < ms.length; i++ ) {
130                int mod = ms[i].getModifiers();
131                if( Modifier.isPrivate(mod) || Modifier.isAbstract(mod) ||
132                    Modifier.isInterface(mod) || Modifier.isProtected(mod) ||
133                    Modifier.isStatic(mod) ) continue;
134                if( ms[i].getName().equals("clone") ) continue;
135                //if( ms[i].getName().equals("hashCode") ) continue;
136                if( ms[i].getDeclaringClass() == Object.class ||
137                    ms[i].getDeclaringClass().isInterface() ) continue;
138                
139                try {
140                   Object.class.getMethod( ms[i].getName(), ms[i].getParameterTypes() );
141                   continue;
142                } catch ( Exception e ) {}
143    
144                fw.write( "\n    " + getMethodPrototype( ms[i] ) + " {\n" );
145                if ( ms[i].getReturnType().getName().equals( "void" ) ) {
146                   //               fw.write( "        if ( delegate == null ) return;\n" );
147                   fw.write( "        delegate." + ms[i].getName() + "(" +
148                   arrayToString( createParameterNames( ms[i].getParameterTypes() ) ) + ");\n" );
149                } else {
150                   //               fw.write( "        if ( delegate == null ) return " + 
151                   //                         getDefaultValueFor( ms[i].getReturnType() ) + ";\n" );
152                   fw.write( "        return delegate." + ms[i].getName() + "(" +
153                   arrayToString( createParameterNames( ms[i].getParameterTypes() ) ) + ");\n" );
154                }
155                fw.write( "    }\n" );
156             }
157             fw.write( "}\n" );
158             fw.close();
159          } catch ( Exception e ) {
160             e.printStackTrace();
161          }
162       }
163    
164       /**
165        * Return the default string value that should return a method with
166        * a <code>c</code> return type.
167        *
168        * <p>By default:
169        *
170        * <ul><pre>
171        * - c == boolean => "false"
172        * - c == char => "''"
173        * - c == byte => "0"
174        * - c == all other primitive types => "-1"
175        * - c is an object => "null"
176        * </pre></ul>
177        *
178        * @param c the type
179        * @return the default string value for this type */
180    
181       private static String getDefaultValueFor( Class c ) {
182          if ( c.isPrimitive() ) {
183             if (c == Boolean.TYPE )
184                return "false";
185             if (c == Character.TYPE )
186                return "''";
187             if ( c == Byte.TYPE ) 
188                return "0";
189             if (c == Short.TYPE || c == Integer.TYPE || 
190                 c == Long.TYPE || c == Float.TYPE || c == Double.TYPE )
191                return "-1";
192          }
193          return "null";
194       }
195    
196       /**
197        * Return a Java-syntax-compiliant string representation of the
198        * given method.
199        *
200        * @param m the method
201        * @return the string representation
202        */
203    
204       private static String getMethodPrototype( Method m ) {
205          return "public " + createStringFor( m.getReturnType() ) + " " + m.getName() + 
206             " ( " + arrayToString( createTypedParameterNames( m.getParameterTypes() ) ) +
207             " ) " + arrayToString( createArray( m.getExceptionTypes() ) );
208       }
209       
210       /**
211        * Return a comma-separated string for a string array.
212        *
213        * <p>For instance:
214        *
215        * <ul><pre>
216        * - {"a", "b", "c"} => "a, b, c"
217        * - {"a"} => "a"
218        * - {} => ""
219        * </pre></ul>
220        *
221        * @param array the given array
222        * @return its string representation
223        */
224    
225       private static String arrayToString( String[] array ) {
226          if ( array.length == 0 ) return "";
227          String ls = java.util.Arrays.asList(array).toString();
228          return ls.substring( 1, ls.length() - 1 );
229       }
230    
231       /**
232        * Create an array of Java-syntax-compiliant string from an array of types.
233        *
234        * <p>For instance:
235        *
236        * <ul><pre>
237        * - {class int, class java.lang.object} => {"int", "java.lang.Object"}
238        * - {class [java.lang.object} => {"java.lang.Object[]"}
239        * </pre></ul>
240        * 
241        * @param array the types
242        * @return the corresponding strings
243        *
244        * @see #createStringFor(Class)
245        */
246       
247       private static String[] createArray( Class[] array ) {
248          String[] res = new String[array.length];
249          for ( int i = 0; i < array.length; i ++ ) {
250             res[i] = createStringFor( array[i] );
251          }
252          return res;
253       }
254    
255       /**
256        * Create a Java-syntax-compiliant string from a type.
257        *
258        * <p>For instance:
259        *
260        * <ul><pre>
261        * - class java.lang.object => "java.lang.Object"
262        * - class [java.lang.object => "java.lang.Object[]"
263        * </pre></ul>
264        * 
265        * @param c the type
266        * @return the corresponding string
267        */
268    
269       private static String createStringFor ( Class c ) {
270          if ( c.isArray() ) {
271             return c.getComponentType().getName() + "[]";
272          } else {
273             return c.getName();
274          }
275       }
276    
277       /**
278        * Create a Java-syntax-compiliant string for a method that would
279        * take a set of parameters defined in <code>array</code>.
280        *
281        * <p>The parameter names are generated to "p[0-array.length]". For
282        * instance:
283        *
284        * <ul><pre>
285        * - {class int, class byte} => {"int p0", "byte p1"}
286        * </pre></ul>
287        *
288        * @param array the types
289        * @param the resulting strings
290        *
291        * @see #createParametersName( array ) */
292    
293       private static String[] createTypedParameterNames ( Class[] array ) {
294          String[] res = new String[array.length];
295          for ( int i = 0; i < array.length; i ++ ) {
296             res[i] = createStringFor( array[i] ) + " p" + i;
297          }
298          return res;
299       }
300    
301       /**
302        * Create untyped paramameter names that correspond to the names
303        * that would have been given by the
304        * <code>createTypedParameterNames</code>.
305        *
306        * <p>For instance, an array of 2 types always returns:
307        *
308        * <ul><pre>
309        * {"p0, "p1"}
310        * </pre></ul>
311        *
312        * @param array the types
313        * @param the resulting strings
314        *
315        * @see #createTypedParametersName( array ) */
316    
317       private static String[] createParameterNames ( Class[] array ) {
318          String[] res = new String[array.length];
319          for ( int i = 0; i < array.length; i ++ ) {
320             res[i] = "p" + i;
321          }
322          return res;
323       }
324       
325    }
326