View Javadoc

1   /**
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Intalio, Inc.  For written permission,
18   *    please contact info@codehaus.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Intalio, Inc. Exolab is a registered
23   *    trademark of Intalio, Inc.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.codehaus.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2001-2002 (C) Intalio, Inc. All Rights Reserved.
42   *
43   * Contributors:
44   * --------------
45   * Gary Shea (shea@gtsdesign.com)   - Original Author
46   * Keith Visco (kvisco@intalio.com) - Changed JCompElement references to
47   *                                    JStructure, some additional tweaking
48   *                                    to get it working with the current
49   *                                    Javasource package.
50   *
51   * $Id$
52   */
53  
54  package org.codehaus.modello.plugin.java.javasource;
55  
56  /*
57   * Copyright (c) 2004, Codehaus.org
58   *
59   * Permission is hereby granted, free of charge, to any person obtaining a copy of
60   * this software and associated documentation files (the "Software"), to deal in
61   * the Software without restriction, including without limitation the rights to
62   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
63   * of the Software, and to permit persons to whom the Software is furnished to do
64   * so, subject to the following conditions:
65   *
66   * The above copyright notice and this permission notice shall be included in all
67   * copies or substantial portions of the Software.
68   *
69   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
73   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
74   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
75   * SOFTWARE.
76   */
77  
78  import java.io.File;
79  import java.util.Enumeration;
80  import java.util.SortedSet;
81  import java.util.TreeSet;
82  import java.util.Vector;
83  
84  import org.codehaus.plexus.util.WriterFactory;
85  
86  /**
87   * A representation of the Java Source code for a Java compilation
88   * unit. This is
89   * a useful utility when creating in memory source code.
90   * This package was modelled after the Java Reflection API
91   * as much as possible to reduce the learning curve.
92   * @author <a href="mailto:shea@gtsdesign.com">Gary Shea</a>
93   * @version $Revision$ $Date$
94   **/
95  public class JCompUnit
96  {
97  
98      /**
99       * The Id for Source control systems
100      * I needed to separate this line to prevent CVS from
101      * expanding it here! ;-)
102      **/
103     private static final String DEFAULT_HEADER
104         = "$" + "Id$";
105 
106     private JComment header = null;
107 
108     /**
109      * The package for this JCompUnit
110      **/
111     private String packageName = null;
112 
113     /**
114      * The file to which this JCompUnit will be saved
115      **/
116     private String fileName = null;
117 
118     /**
119      * The set of top-level classes that live in this compilation unit.
120      **/
121     //private TypeList classes = null;
122     private Vector<JClass> classes = null;
123 
124     /**
125      * The set of top-level interfaces that live in this compilation unit.
126      **/
127     //private TypeList interfaces = null;
128     private Vector<JInterface> interfaces = null;
129 
130     /**
131      * Creates a new JCompUnit
132      * @param packageName the name of the package for this JCompUnit.
133      * If packageName is null or empty, no 'package' line will be generated.
134      * @param fileName the name of the file in which this JCompUnit
135      * will be stored
136      **/
137     public JCompUnit( String packageName, String fileName )
138     {
139         this.packageName = packageName;
140         this.fileName = fileName;
141         init();
142     } //-- JCompUnit
143 
144     /**
145      * Creates a new JCompUnit with the given JClass (which must have
146      * been created with either a full class name or package/local
147      * name) as the public class.  Package and file name are taken
148      * from jClass.
149      * @param jClass the public class for this JCompUnit.
150      **/
151     public JCompUnit( JClass jClass )
152     {
153         this.packageName = jClass.getPackageName();
154 
155         // The outer name is the package plus the simple name of the
156         // outermost enclosing class.  The file name is just the
157         // simple name of the outermost enclosing class, so the
158         // package name part must be stripped off.
159 
160         /*
161           Commented out until inner-class support has been added.
162           kvisco - 20021211
163 
164         String outer = jClass.getOuterName();
165         int lastDot = outer.lastIndexOf(".");
166         String filePrefix;
167         if (lastDot != -1) {
168             filePrefix = outer.substring (lastDot + 1);
169         } else {
170             filePrefix = outer;
171         }
172         */
173         String filePrefix = jClass.getLocalName();
174 
175         this.fileName = filePrefix + ".java";
176         init();
177         classes.add( jClass );
178 
179     } //-- JCompUnit
180 
181     /**
182      * Creates a new JCompUnit with the given JInterface as public interface
183      * Package and file name are taken from jInterface.
184      * @param jInterface the public interface for this JCompUnit.
185      **/
186     public JCompUnit( JInterface jInterface )
187     {
188         this.packageName = jInterface.getPackageName();
189         this.fileName = jInterface.getLocalName() + ".java";
190         init();
191         interfaces.add( jInterface );
192     } //-- JCompUnit
193 
194     private void init()
195     {
196         classes = new Vector<JClass>();
197         interfaces = new Vector<JInterface>();
198     }
199 
200     /**
201      * Adds the given JStructure (either a JInterface or
202      * a JClass) to this JCompUnit.
203      *
204      * @param jStructure the JStructure to add
205      * @exception java.lang.IllegalArgumentException when the given
206      * JStructure has the same name of an existing JStructure
207      * or if the class of jStructure is unknown.
208      */
209     public void addStructure( JStructure jStructure )
210         throws IllegalArgumentException
211     {
212         if ( jStructure instanceof JInterface )
213             addInterface( (JInterface) jStructure );
214         else if ( jStructure instanceof JClass )
215             addClass( (JClass) jStructure );
216         else
217         {
218             String err = "Unknown JStructure subclass '"
219                 + jStructure.getClass().getName() + "'.";
220             throw new IllegalArgumentException( err );
221         }
222 
223     } //-- addStructure
224 
225     /**
226      * Adds a JClass which should be printed in this file.
227      * 
228      * @param jClass the JClass to add
229      **/
230     public void addClass( JClass jClass )
231     {
232         classes.add( jClass );
233     } //-- addClass
234 
235     /**
236      * Adds a JInterface which should be printed in this file.
237      * 
238      * @param jInterface the JInterface to add
239      **/
240     public void addInterface( JInterface jInterface )
241     {
242         interfaces.add( jInterface );
243     } //-- addInterface
244 
245     /**
246      * returns a array of String containing all import classes/packages,
247      * also imports within the same package of this object.
248      * @return a array of String containing all import classes/packages,
249      * also imports within the same package of this object.
250      */
251     public SortedSet<String> getImports()
252     {
253 
254         SortedSet<String> allImports = new TreeSet<String>();
255 
256         // add imports from classes
257         for ( JClass jClass : classes )
258         {
259             Enumeration<String> e = jClass.getImports();
260             while ( e.hasMoreElements() )
261             {
262                 allImports.add( e.nextElement() );
263             }
264         }
265 
266         for ( JInterface jInterface : interfaces )
267         {
268             Enumeration<String> e = jInterface.getImports();
269             while ( e.hasMoreElements() )
270             {
271                 allImports.add( e.nextElement() );
272             }
273         }
274 
275         return allImports;
276     }
277 
278     /**
279      * Returns the name of the file that this JCompUnit would be
280      * printed as, given a call to #print.
281      *
282      * @param destDir the destination directory. This may be null.
283      * @return the name of the file that this JCompUnit would be
284      * printed as, given a call to #print.
285      **/
286     public String getFilename( String destDir )
287     {
288 
289         String filename = new String( fileName );
290 
291         //-- Convert Java package to path string
292         String javaPackagePath = "";
293         if ( ( packageName != null ) && ( packageName.length() > 0 ) )
294         {
295             javaPackagePath = packageName.replace( '.', File.separatorChar );
296         }
297 
298         //-- Create fully qualified path (including 'destDir') to file
299         File pathFile;
300         if ( destDir == null )
301             pathFile = new File( javaPackagePath );
302         else
303             pathFile = new File( destDir, javaPackagePath );
304         if ( !pathFile.exists() )
305         {
306             pathFile.mkdirs();
307         }
308 
309         //-- Prefix filename with path
310         if ( pathFile.toString().length() > 0 )
311             filename = pathFile.toString() + File.separator + filename;
312 
313         return filename;
314     } //-- getFilename
315 
316     /**
317      * Returns the name of the package that this JCompUnit is a member of
318      * @return the name of the package that this JCompUnit is a member of,
319      * or null if there is no current package name defined
320      **/
321     public String getPackageName()
322     {
323         return this.packageName;
324     } //-- getPackageName
325 
326     protected static String getPackageFromClassName( String className )
327     {
328         int idx = -1;
329         if ( ( idx = className.lastIndexOf( '.' ) ) > 0 )
330         {
331             return className.substring( 0, idx );
332         }
333         return null;
334     } //-- getPackageFromClassName
335 
336     /**
337      * Prints the source code for this JClass in the current directory
338      * with the default line seperator of the the runtime platform.
339      * @see #print(java.lang.String, java.lang.String)
340      **/
341     public void print()
342     {
343         print( null, null );
344     } //-- print
345 
346     /**
347      * Prints the source code for this JClass
348      * with the default line seperator of the the runtime platform.
349      * @param destDir the destination directory to generate the file.
350      * @see #print(java.lang.String, java.lang.String)
351      **/
352     public void print( String destDir )
353     {
354         print( destDir, null );
355     } //-- print
356 
357     /**
358      * Prints the source code for this JCompUnit.
359      * @param destDir the destination directory to generate the file.
360      * @param lineSeparator the line separator to use at the end of each line.
361      * If null, then the default line separator for the runtime platform will
362      * be used.
363      **/
364     public void print( String destDir, String lineSeparator )
365     {
366 
367         //-- open output file
368         String filename = getFilename( destDir );
369 
370         File file = new File( filename );
371         JSourceWriter jsw = null;
372         try
373         {
374             jsw = new JSourceWriter( WriterFactory.newPlatformWriter( file ) );
375         }
376         catch ( java.io.IOException ioe )
377         {
378             System.out.println( "unable to create compilation unit file: " + filename );
379             return;
380         }
381 
382         if ( lineSeparator == null )
383         {
384             lineSeparator = System.getProperty( "line.separator" );
385         }
386         jsw.setLineSeparator( lineSeparator );
387         print( jsw );
388         jsw.flush();
389         jsw.close();
390     } //-- print
391 
392     /**
393      * Prints the source code for this JClass.
394      * @param jsw the JSourceWriter to print to.
395      **/
396     public void print( JSourceWriter jsw )
397     {
398 
399         // Traverse the nested class and interface heirarchy and
400         // update the names to match the compilation unit.
401 
402         resolveNames();
403         StringBuilder buffer = new StringBuilder();
404 
405         //-- write file header
406         if ( header != null )
407             header.print( jsw );
408         else
409         {
410             jsw.writeln( "/*" );
411             jsw.writeln( " * " + DEFAULT_HEADER );
412             jsw.writeln( "*/" );
413         }
414         jsw.writeln();
415         jsw.flush();
416 
417         //-- print package name
418         if ( ( packageName != null ) && ( packageName.length() > 0 ) )
419         {
420 
421             buffer.setLength( 0 );
422             buffer.append( "package " );
423             buffer.append( packageName );
424             buffer.append( ';' );
425             jsw.writeln( buffer.toString() );
426             jsw.writeln();
427         }
428 
429         //-- print imports
430         jsw.writeln( "  //---------------------------------------------/" );
431         jsw.writeln( " //- Imported classes, interfaces and packages -/" );
432         jsw.writeln( "//---------------------------------------------/" );
433         jsw.writeln();
434         SortedSet<String> allImports = getImports();
435         String compUnitPackage = getPackageName();
436         for ( String importName : allImports )
437         {
438             String importsPackage
439                 = JStructure.getPackageFromClassName( importName );
440             if ( importsPackage != null &&
441                 !importsPackage.equals( compUnitPackage ) )
442             {
443                 jsw.write( "import " );
444                 jsw.write( importName );
445                 jsw.writeln( ';' );
446             }
447         }
448         jsw.writeln();
449 
450         // Print the public elements, interfaces first, then classes.
451         // There should only be one public element, but if there are
452         // more we let the compiler catch it.
453         printStructures( jsw, true );
454 
455         // Print the remaining non-public elements, interfaces first.
456         printStructures( jsw, false );
457 
458         jsw.flush();
459     } //-- print
460 
461     /**
462      * Print the source code for the contained JClass objects.
463      * @param jsw the JSourceWriter to print to.
464      * @param printPublic if true, print only public classes; if
465      * false, print only non-public classes.
466      **/
467     final public void printStructures( JSourceWriter jsw, boolean printPublic )
468     {
469 
470         //-- print class information
471         //-- we need to add some JavaDoc API adding comments
472 
473         boolean isFirst = true;
474 
475         //SortedSet interfaceList = interfaces.sortedOnFullName();
476         for ( JInterface jInterface : interfaces )
477         {
478             if ( jInterface.getModifiers().isPublic() == printPublic )
479             {
480                 if ( isFirst )
481                 {
482                     Header.print( jsw, printPublic );
483                     isFirst = false;
484                 }
485                 jInterface.print( jsw, true );
486                 jsw.writeln();
487             }
488         }
489 
490         //SortedSet classList = classes.sortedOnFullName();
491         for ( JClass jClass : classes )
492         {
493             if ( jClass.getModifiers().isPublic() == printPublic )
494             {
495                 if ( isFirst )
496                 {
497                     Header.print( jsw, printPublic );
498                     isFirst = false;
499                 }
500                 jClass.print( jsw, true );
501                 jsw.writeln();
502             }
503         }
504     } //-- printElements(JSourceWriter, int)
505 
506     /**
507      * Sets the header comment for this JCompUnit
508      * @param comment the comment to display at the top of the source file
509      * when printed
510      **/
511     public void setHeader( JComment comment )
512     {
513         this.header = comment;
514     } //-- setHeader
515 
516     //-------------------/
517     //- Private Methods -/
518     //-------------------/
519 
520     /**
521      * Update the names of nested classes and interfaces.
522      **/
523     private void resolveNames()
524     {
525 
526         /*
527           Commented out until support for inner-classes is added
528           kvisco - 20021211
529 
530         for (int i = 0; i < classes.size(); i++) {
531             JClass jClass = (JClass) classes.get(i);
532             jClass.resolveNames(packageName, null);
533         }
534         for (int i = 0; i < interfaces.size(); i++) {
535             JInterface jInterface = (JInterface) interfaces.get(i);
536             jInterface.resolveNames(packageName, null);
537         }
538         */
539     } //-- resolveNames
540 
541     /**
542      * Test drive method...to be removed or commented out
543      **
544      public static void main(String[] args) {
545      JCompUnit unit = new JCompUnit("com.acme", "Test.java");
546 
547      JClass testClass = new JClass("Test");
548 
549      testClass.addImport("java.util.Vector");
550      testClass.addMember(new JField(JType.Int, "x"));
551 
552      JField field = null;
553      field = new JField(JType.Int, "_z");
554      field.getModifiers().setStatic(true);
555      testClass.addField(field);
556 
557      testClass.getStaticInitializationCode().add("_z = 75;");
558 
559      JClass jcString = new JClass("String");
560      field = new JField(jcString, "myString");
561      field.getModifiers().makePrivate();
562      testClass.addMember(field);
563 
564      //-- create constructor
565      JConstructor cons = testClass.createConstructor();
566      testClass.addConstructor(cons);
567      cons.getSourceCode().add("this.x = 6;");
568 
569      JMethod jMethod = new JMethod(JType.Int, "getX");
570      jMethod.setSourceCode("return this.x;");
571      testClass.addMethod(jMethod);
572 
573      unit.addClass (testClass);
574 
575      unit.print();
576 
577      } //-- main
578      /* */
579 
580 } //-- JCompUnit
581 
582 /**
583  * Print the headers delineating the public and non-public elements of
584  * the compilation unit.
585  **/
586 class Header
587 {
588 
589     private static String[] publicHeader = {
590         "  //-----------------------------/",
591         " //-  Public Class / Interface -/",
592         "//-----------------------------/",
593     };
594     private static String[] nonPublicHeader = {
595         "  //-------------------------------------/",
596         " //-  Non-Public Classes / Interfaces  -/",
597         "//-------------------------------------/",
598     };
599 
600     /**
601      * Print the specified header to the given Writer.
602      * @params JSourceWriter an open JSourceWriter
603      * @params boolean if true print the public header, otherwise
604      * print the non-public header.
605      **/
606     protected static void print( JSourceWriter jsw, boolean printPublic )
607     {
608         String[] header = printPublic ? publicHeader : nonPublicHeader;
609         for ( String aHeader : header )
610         {
611             jsw.writeln( aHeader );
612         }
613         jsw.writeln();
614     }
615 } //-- Header