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     public void addClass( JClass jClass )
229     {
230         classes.add( jClass );
231     } //-- addClass
232 
233     /**
234      * Adds a JInterface which should be printed in this file.
235      **/
236     public void addInterface( JInterface jInterface )
237     {
238         interfaces.add( jInterface );
239     } //-- addInterface
240 
241     /**
242      * returns a array of String containing all import classes/packages,
243      * also imports within the same package of this object.
244      * @return a array of String containing all import classes/packages,
245      * also imports within the same package of this object.
246      */
247     public SortedSet<String> getImports()
248     {
249 
250         SortedSet<String> allImports = new TreeSet<String>();
251 
252         // add imports from classes
253         for ( JClass jClass : classes )
254         {
255             Enumeration<String> e = jClass.getImports();
256             while ( e.hasMoreElements() )
257             {
258                 allImports.add( e.nextElement() );
259             }
260         }
261 
262         for ( JInterface jInterface : interfaces )
263         {
264             Enumeration<String> e = jInterface.getImports();
265             while ( e.hasMoreElements() )
266             {
267                 allImports.add( e.nextElement() );
268             }
269         }
270 
271         return allImports;
272     }
273 
274     /**
275      * Returns the name of the file that this JCompUnit would be
276      * printed as, given a call to #print.
277      *
278      * @param destDir the destination directory. This may be null.
279      * @return the name of the file that this JCompUnit would be
280      * printed as, given a call to #print.
281      **/
282     public String getFilename( String destDir )
283     {
284 
285         String filename = new String( fileName );
286 
287         //-- Convert Java package to path string
288         String javaPackagePath = "";
289         if ( ( packageName != null ) && ( packageName.length() > 0 ) )
290         {
291             javaPackagePath = packageName.replace( '.', File.separatorChar );
292         }
293 
294         //-- Create fully qualified path (including 'destDir') to file
295         File pathFile;
296         if ( destDir == null )
297             pathFile = new File( javaPackagePath );
298         else
299             pathFile = new File( destDir, javaPackagePath );
300         if ( !pathFile.exists() )
301         {
302             pathFile.mkdirs();
303         }
304 
305         //-- Prefix filename with path
306         if ( pathFile.toString().length() > 0 )
307             filename = pathFile.toString() + File.separator + filename;
308 
309         return filename;
310     } //-- getFilename
311 
312     /**
313      * Returns the name of the package that this JCompUnit is a member of
314      * @return the name of the package that this JCompUnit is a member of,
315      * or null if there is no current package name defined
316      **/
317     public String getPackageName()
318     {
319         return this.packageName;
320     } //-- getPackageName
321 
322     protected static String getPackageFromClassName( String className )
323     {
324         int idx = -1;
325         if ( ( idx = className.lastIndexOf( '.' ) ) > 0 )
326         {
327             return className.substring( 0, idx );
328         }
329         return null;
330     } //-- getPackageFromClassName
331 
332     /**
333      * Prints the source code for this JClass in the current directory
334      * with the default line seperator of the the runtime platform.
335      * @see print(java.lang.String, java.lang.String)
336      **/
337     public void print()
338     {
339         print( null, null );
340     } //-- print
341 
342     /**
343      * Prints the source code for this JClass
344      * with the default line seperator of the the runtime platform.
345      * @param destDir the destination directory to generate the file.
346      * @see print(java.lang.String, java.lang.String)
347      **/
348     public void print( String destDir )
349     {
350         print( destDir, null );
351     } //-- print
352 
353     /**
354      * Prints the source code for this JCompUnit.
355      * @param destDir the destination directory to generate the file.
356      * @param lineSeparator the line separator to use at the end of each line.
357      * If null, then the default line separator for the runtime platform will
358      * be used.
359      **/
360     public void print( String destDir, String lineSeparator )
361     {
362 
363         //-- open output file
364         String filename = getFilename( destDir );
365 
366         File file = new File( filename );
367         JSourceWriter jsw = null;
368         try
369         {
370             jsw = new JSourceWriter( WriterFactory.newPlatformWriter( file ) );
371         }
372         catch ( java.io.IOException ioe )
373         {
374             System.out.println( "unable to create compilation unit file: " + filename );
375             return;
376         }
377 
378         if ( lineSeparator == null )
379         {
380             lineSeparator = System.getProperty( "line.separator" );
381         }
382         jsw.setLineSeparator( lineSeparator );
383         print( jsw );
384         jsw.flush();
385         jsw.close();
386     } //-- print
387 
388     /**
389      * Prints the source code for this JClass.
390      * @param jsw the JSourceWriter to print to.
391      **/
392     public void print( JSourceWriter jsw )
393     {
394 
395         // Traverse the nested class and interface heirarchy and
396         // update the names to match the compilation unit.
397 
398         resolveNames();
399         StringBuilder buffer = new StringBuilder();
400 
401         //-- write file header
402         if ( header != null )
403             header.print( jsw );
404         else
405         {
406             jsw.writeln( "/*" );
407             jsw.writeln( " * " + DEFAULT_HEADER );
408             jsw.writeln( "*/" );
409         }
410         jsw.writeln();
411         jsw.flush();
412 
413         //-- print package name
414         if ( ( packageName != null ) && ( packageName.length() > 0 ) )
415         {
416 
417             buffer.setLength( 0 );
418             buffer.append( "package " );
419             buffer.append( packageName );
420             buffer.append( ';' );
421             jsw.writeln( buffer.toString() );
422             jsw.writeln();
423         }
424 
425         //-- print imports
426         jsw.writeln( "  //---------------------------------------------/" );
427         jsw.writeln( " //- Imported classes, interfaces and packages -/" );
428         jsw.writeln( "//---------------------------------------------/" );
429         jsw.writeln();
430         SortedSet<String> allImports = getImports();
431         String compUnitPackage = getPackageName();
432         for ( String importName : allImports )
433         {
434             String importsPackage
435                 = JStructure.getPackageFromClassName( importName );
436             if ( importsPackage != null &&
437                 !importsPackage.equals( compUnitPackage ) )
438             {
439                 jsw.write( "import " );
440                 jsw.write( importName );
441                 jsw.writeln( ';' );
442             }
443         }
444         jsw.writeln();
445 
446         // Print the public elements, interfaces first, then classes.
447         // There should only be one public element, but if there are
448         // more we let the compiler catch it.
449         printStructures( jsw, true );
450 
451         // Print the remaining non-public elements, interfaces first.
452         printStructures( jsw, false );
453 
454         jsw.flush();
455     } //-- print
456 
457     /**
458      * Print the source code for the contained JClass objects.
459      * @param jsw the JSourceWriter to print to.
460      * @param printPublic if true, print only public classes; if
461      * false, print only non-public classes.
462      **/
463     final public void printStructures( JSourceWriter jsw, boolean printPublic )
464     {
465 
466         //-- print class information
467         //-- we need to add some JavaDoc API adding comments
468 
469         boolean isFirst = true;
470 
471         //SortedSet interfaceList = interfaces.sortedOnFullName();
472         for ( JInterface jInterface : interfaces )
473         {
474             if ( jInterface.getModifiers().isPublic() == printPublic )
475             {
476                 if ( isFirst )
477                 {
478                     Header.print( jsw, printPublic );
479                     isFirst = false;
480                 }
481                 jInterface.print( jsw, true );
482                 jsw.writeln();
483             }
484         }
485 
486         //SortedSet classList = classes.sortedOnFullName();
487         for ( JClass jClass : classes )
488         {
489             if ( jClass.getModifiers().isPublic() == printPublic )
490             {
491                 if ( isFirst )
492                 {
493                     Header.print( jsw, printPublic );
494                     isFirst = false;
495                 }
496                 jClass.print( jsw, true );
497                 jsw.writeln();
498             }
499         }
500     } //-- printElements(JSourceWriter, int)
501 
502     /**
503      * Sets the header comment for this JCompUnit
504      * @param comment the comment to display at the top of the source file
505      * when printed
506      **/
507     public void setHeader( JComment comment )
508     {
509         this.header = comment;
510     } //-- setHeader
511 
512     //-------------------/
513     //- Private Methods -/
514     //-------------------/
515 
516     /**
517      * Update the names of nested classes and interfaces.
518      **/
519     private void resolveNames()
520     {
521 
522         /*
523           Commented out until support for inner-classes is added
524           kvisco - 20021211
525 
526         for (int i = 0; i < classes.size(); i++) {
527             JClass jClass = (JClass) classes.get(i);
528             jClass.resolveNames(packageName, null);
529         }
530         for (int i = 0; i < interfaces.size(); i++) {
531             JInterface jInterface = (JInterface) interfaces.get(i);
532             jInterface.resolveNames(packageName, null);
533         }
534         */
535     } //-- resolveNames
536 
537     /**
538      * Test drive method...to be removed or commented out
539      **
540      public static void main(String[] args) {
541      JCompUnit unit = new JCompUnit("com.acme", "Test.java");
542 
543      JClass testClass = new JClass("Test");
544 
545      testClass.addImport("java.util.Vector");
546      testClass.addMember(new JField(JType.Int, "x"));
547 
548      JField field = null;
549      field = new JField(JType.Int, "_z");
550      field.getModifiers().setStatic(true);
551      testClass.addField(field);
552 
553      testClass.getStaticInitializationCode().add("_z = 75;");
554 
555      JClass jcString = new JClass("String");
556      field = new JField(jcString, "myString");
557      field.getModifiers().makePrivate();
558      testClass.addMember(field);
559 
560      //-- create constructor
561      JConstructor cons = testClass.createConstructor();
562      testClass.addConstructor(cons);
563      cons.getSourceCode().add("this.x = 6;");
564 
565      JMethod jMethod = new JMethod(JType.Int, "getX");
566      jMethod.setSourceCode("return this.x;");
567      testClass.addMethod(jMethod);
568 
569      unit.addClass (testClass);
570 
571      unit.print();
572 
573      } //-- main
574      /* */
575 
576 } //-- JCompUnit
577 
578 /**
579  * Print the headers delineating the public and non-public elements of
580  * the compilation unit.
581  **/
582 class Header
583 {
584 
585     private static String[] publicHeader = {
586         "  //-----------------------------/",
587         " //-  Public Class / Interface -/",
588         "//-----------------------------/",
589     };
590     private static String[] nonPublicHeader = {
591         "  //-------------------------------------/",
592         " //-  Non-Public Classes / Interfaces  -/",
593         "//-------------------------------------/",
594     };
595 
596     /**
597      * Print the specified header to the given Writer.
598      * @params JSourceWriter an open JSourceWriter
599      * @params boolean if true print the public header, otherwise
600      * print the non-public header.
601      **/
602     protected static void print( JSourceWriter jsw, boolean printPublic )
603     {
604         String[] header = printPublic ? publicHeader : nonPublicHeader;
605         for ( String aHeader : header )
606         {
607             jsw.writeln( aHeader );
608         }
609         jsw.writeln();
610     }
611 } //-- Header