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 [email protected].
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 ([email protected]) - Original Author
46 * Keith Visco ([email protected]) - 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 package org.codehaus.modello.plugin.java.javasource;
54
55 /*
56 * Copyright (c) 2004, Codehaus.org
57 *
58 * Permission is hereby granted, free of charge, to any person obtaining a copy of
59 * this software and associated documentation files (the "Software"), to deal in
60 * the Software without restriction, including without limitation the rights to
61 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
62 * of the Software, and to permit persons to whom the Software is furnished to do
63 * so, subject to the following conditions:
64 *
65 * The above copyright notice and this permission notice shall be included in all
66 * copies or substantial portions of the Software.
67 *
68 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
69 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
73 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
74 * SOFTWARE.
75 */
76
77 import java.io.File;
78 import java.nio.file.Files;
79 import java.util.ArrayList;
80 import java.util.Enumeration;
81 import java.util.List;
82 import java.util.SortedSet;
83 import java.util.TreeSet;
84
85 /**
86 * A representation of the Java Source code for a Java compilation
87 * unit. This is
88 * a useful utility when creating in memory source code.
89 * This package was modelled after the Java Reflection API
90 * as much as possible to reduce the learning curve.
91 * @author <a href="mailto:[email protected]">Gary Shea</a>
92 * @version $Revision$ $Date$
93 **/
94 public class JCompUnit {
95
96 /**
97 * The Id for Source control systems
98 * I needed to separate this line to prevent CVS from
99 * expanding it here! ;-)
100 **/
101 private static final String DEFAULT_HEADER = "$" + "Id$";
102
103 private JComment header = null;
104
105 /**
106 * The package for this JCompUnit
107 **/
108 private String packageName = null;
109
110 /**
111 * The file to which this JCompUnit will be saved
112 **/
113 private String fileName = null;
114
115 /**
116 * The set of top-level classes that live in this compilation unit.
117 **/
118 // private TypeList classes = null;
119 private List<JClass> classes = null;
120
121 /**
122 * The set of top-level interfaces that live in this compilation unit.
123 **/
124 // private TypeList interfaces = null;
125 private List<JInterface> interfaces = null;
126
127 /**
128 * Creates a new JCompUnit
129 * @param packageName the name of the package for this JCompUnit.
130 * If packageName is null or empty, no 'package' line will be generated.
131 * @param fileName the name of the file in which this JCompUnit
132 * will be stored
133 **/
134 public JCompUnit(String packageName, String fileName) {
135 this.packageName = packageName;
136 this.fileName = fileName;
137 init();
138 } // -- JCompUnit
139
140 /**
141 * Creates a new JCompUnit with the given JClass (which must have
142 * been created with either a full class name or package/local
143 * name) as the public class. Package and file name are taken
144 * from jClass.
145 * @param jClass the public class for this JCompUnit.
146 **/
147 public JCompUnit(JClass jClass) {
148 this.packageName = jClass.getPackageName();
149
150 // The outer name is the package plus the simple name of the
151 // outermost enclosing class. The file name is just the
152 // simple name of the outermost enclosing class, so the
153 // package name part must be stripped off.
154
155 /*
156 Commented out until inner-class support has been added.
157 kvisco - 20021211
158
159 String outer = jClass.getOuterName();
160 int lastDot = outer.lastIndexOf(".");
161 String filePrefix;
162 if (lastDot != -1) {
163 filePrefix = outer.substring (lastDot + 1);
164 } else {
165 filePrefix = outer;
166 }
167 */
168 String filePrefix = jClass.getLocalName();
169
170 this.fileName = filePrefix + ".java";
171 init();
172 classes.add(jClass);
173 } // -- JCompUnit
174
175 /**
176 * Creates a new JCompUnit with the given JInterface as public interface
177 * Package and file name are taken from jInterface.
178 * @param jInterface the public interface for this JCompUnit.
179 **/
180 public JCompUnit(JInterface jInterface) {
181 this.packageName = jInterface.getPackageName();
182 this.fileName = jInterface.getLocalName() + ".java";
183 init();
184 interfaces.add(jInterface);
185 } // -- JCompUnit
186
187 private void init() {
188 classes = new ArrayList<JClass>();
189 interfaces = new ArrayList<JInterface>();
190 }
191
192 /**
193 * Adds the given JStructure (either a JInterface or
194 * a JClass) to this JCompUnit.
195 *
196 * @param jStructure the JStructure to add
197 * @exception java.lang.IllegalArgumentException when the given
198 * JStructure has the same name of an existing JStructure
199 * or if the class of jStructure is unknown.
200 */
201 public void addStructure(JStructure jStructure) throws IllegalArgumentException {
202 if (jStructure instanceof JInterface) addInterface((JInterface) jStructure);
203 else if (jStructure instanceof JClass) addClass((JClass) jStructure);
204 else {
205 String err = "Unknown JStructure subclass '" + jStructure.getClass().getName() + "'.";
206 throw new IllegalArgumentException(err);
207 }
208 } // -- addStructure
209
210 /**
211 * Adds a JClass which should be printed in this file.
212 *
213 * @param jClass the JClass to add
214 **/
215 public void addClass(JClass jClass) {
216 classes.add(jClass);
217 } // -- addClass
218
219 /**
220 * Adds a JInterface which should be printed in this file.
221 *
222 * @param jInterface the JInterface to add
223 **/
224 public void addInterface(JInterface jInterface) {
225 interfaces.add(jInterface);
226 } // -- addInterface
227
228 /**
229 * returns a array of String containing all import classes/packages,
230 * also imports within the same package of this object.
231 * @return a array of String containing all import classes/packages,
232 * also imports within the same package of this object.
233 */
234 public SortedSet<String> getImports() {
235
236 SortedSet<String> allImports = new TreeSet<String>();
237
238 // add imports from classes
239 for (JClass jClass : classes) {
240 Enumeration<String> e = jClass.getImports();
241 while (e.hasMoreElements()) {
242 allImports.add(e.nextElement());
243 }
244 }
245
246 for (JInterface jInterface : interfaces) {
247 Enumeration<String> e = jInterface.getImports();
248 while (e.hasMoreElements()) {
249 allImports.add(e.nextElement());
250 }
251 }
252
253 return allImports;
254 }
255
256 /**
257 * Returns the name of the file that this JCompUnit would be
258 * printed as, given a call to #print.
259 *
260 * @param destDir the destination directory. This may be null.
261 * @return the name of the file that this JCompUnit would be
262 * printed as, given a call to #print.
263 **/
264 public String getFilename(String destDir) {
265
266 String filename = new String(fileName);
267
268 // -- Convert Java package to path string
269 String javaPackagePath = "";
270 if ((packageName != null) && (packageName.length() > 0)) {
271 javaPackagePath = packageName.replace('.', File.separatorChar);
272 }
273
274 // -- Create fully qualified path (including 'destDir') to file
275 File pathFile;
276 if (destDir == null) pathFile = new File(javaPackagePath);
277 else pathFile = new File(destDir, javaPackagePath);
278 if (!pathFile.exists()) {
279 pathFile.mkdirs();
280 }
281
282 // -- Prefix filename with path
283 if (pathFile.toString().length() > 0) filename = pathFile.toString() + File.separator + filename;
284
285 return filename;
286 } // -- getFilename
287
288 /**
289 * Returns the name of the package that this JCompUnit is a member of
290 * @return the name of the package that this JCompUnit is a member of,
291 * or null if there is no current package name defined
292 **/
293 public String getPackageName() {
294 return this.packageName;
295 } // -- getPackageName
296
297 protected static String getPackageFromClassName(String className) {
298 int idx = -1;
299 if ((idx = className.lastIndexOf('.')) > 0) {
300 return className.substring(0, idx);
301 }
302 return null;
303 } // -- getPackageFromClassName
304
305 /**
306 * Prints the source code for this JClass in the current directory
307 * with the default line seperator of the the runtime platform.
308 * @see #print(java.lang.String, java.lang.String)
309 **/
310 public void print() {
311 print(null, null);
312 } // -- print
313
314 /**
315 * Prints the source code for this JClass
316 * with the default line seperator of the the runtime platform.
317 * @param destDir the destination directory to generate the file.
318 * @see #print(java.lang.String, java.lang.String)
319 **/
320 public void print(String destDir) {
321 print(destDir, null);
322 } // -- print
323
324 /**
325 * Prints the source code for this JCompUnit.
326 * @param destDir the destination directory to generate the file.
327 * @param lineSeparator the line separator to use at the end of each line.
328 * If null, then the default line separator for the runtime platform will
329 * be used.
330 **/
331 public void print(String destDir, String lineSeparator) {
332
333 // -- open output file
334 String filename = getFilename(destDir);
335
336 File file = new File(filename);
337 JSourceWriter jsw = null;
338 try {
339 jsw = new JSourceWriter(Files.newBufferedWriter(file.toPath()));
340 } catch (java.io.IOException ioe) {
341 System.out.println("unable to create compilation unit file: " + filename);
342 return;
343 }
344
345 if (lineSeparator == null) {
346 lineSeparator = System.getProperty("line.separator");
347 }
348 jsw.setLineSeparator(lineSeparator);
349 print(jsw);
350 jsw.flush();
351 jsw.close();
352 } // -- print
353
354 /**
355 * Prints the source code for this JClass.
356 * @param jsw the JSourceWriter to print to.
357 **/
358 public void print(JSourceWriter jsw) {
359
360 // Traverse the nested class and interface heirarchy and
361 // update the names to match the compilation unit.
362
363 resolveNames();
364 StringBuilder buffer = new StringBuilder();
365
366 // -- write file header
367 if (header != null) header.print(jsw);
368 else {
369 jsw.writeln("/*");
370 jsw.writeln(" * " + DEFAULT_HEADER);
371 jsw.writeln("*/");
372 }
373 jsw.writeln();
374 jsw.flush();
375
376 // -- print package name
377 if ((packageName != null) && (packageName.length() > 0)) {
378
379 buffer.setLength(0);
380 buffer.append("package ");
381 buffer.append(packageName);
382 buffer.append(';');
383 jsw.writeln(buffer.toString());
384 jsw.writeln();
385 }
386
387 // -- print imports
388 jsw.writeln(" //---------------------------------------------/");
389 jsw.writeln(" //- Imported classes, interfaces and packages -/");
390 jsw.writeln("//---------------------------------------------/");
391 jsw.writeln();
392 SortedSet<String> allImports = getImports();
393 String compUnitPackage = getPackageName();
394 for (String importName : allImports) {
395 String importsPackage = JStructure.getPackageFromClassName(importName);
396 if (importsPackage != null && !importsPackage.equals(compUnitPackage)) {
397 jsw.write("import ");
398 jsw.write(importName);
399 jsw.writeln(';');
400 }
401 }
402 jsw.writeln();
403
404 // Print the public elements, interfaces first, then classes.
405 // There should only be one public element, but if there are
406 // more we let the compiler catch it.
407 printStructures(jsw, true);
408
409 // Print the remaining non-public elements, interfaces first.
410 printStructures(jsw, false);
411
412 jsw.flush();
413 } // -- print
414
415 /**
416 * Print the source code for the contained JClass objects.
417 * @param jsw the JSourceWriter to print to.
418 * @param printPublic if true, print only public classes; if
419 * false, print only non-public classes.
420 **/
421 public final void printStructures(JSourceWriter jsw, boolean printPublic) {
422
423 // -- print class information
424 // -- we need to add some JavaDoc API adding comments
425
426 boolean isFirst = true;
427
428 // SortedSet interfaceList = interfaces.sortedOnFullName();
429 for (JInterface jInterface : interfaces) {
430 if (jInterface.getModifiers().isPublic() == printPublic) {
431 if (isFirst) {
432 Header.print(jsw, printPublic);
433 isFirst = false;
434 }
435 jInterface.print(jsw, true);
436 jsw.writeln();
437 }
438 }
439
440 // SortedSet classList = classes.sortedOnFullName();
441 for (JClass jClass : classes) {
442 if (jClass.getModifiers().isPublic() == printPublic) {
443 if (isFirst) {
444 Header.print(jsw, printPublic);
445 isFirst = false;
446 }
447 jClass.print(jsw, true);
448 jsw.writeln();
449 }
450 }
451 } // -- printElements(JSourceWriter, int)
452
453 /**
454 * Sets the header comment for this JCompUnit
455 * @param comment the comment to display at the top of the source file
456 * when printed
457 **/
458 public void setHeader(JComment comment) {
459 this.header = comment;
460 } // -- setHeader
461
462 // -------------------/
463 // - Private Methods -/
464 // -------------------/
465
466 /**
467 * Update the names of nested classes and interfaces.
468 **/
469 private void resolveNames() {
470
471 /*
472 Commented out until support for inner-classes is added
473 kvisco - 20021211
474
475 for (int i = 0; i < classes.size(); i++) {
476 JClass jClass = (JClass) classes.get(i);
477 jClass.resolveNames(packageName, null);
478 }
479 for (int i = 0; i < interfaces.size(); i++) {
480 JInterface jInterface = (JInterface) interfaces.get(i);
481 jInterface.resolveNames(packageName, null);
482 }
483 */
484 } // -- resolveNames
485 } // -- JCompUnit
486
487 /**
488 * Print the headers delineating the public and non-public elements of
489 * the compilation unit.
490 **/
491 class Header {
492
493 private static String[] publicHeader = {
494 " //-----------------------------/", " //- Public Class / Interface -/", "//-----------------------------/",
495 };
496 private static String[] nonPublicHeader = {
497 " //-------------------------------------/",
498 " //- Non-Public Classes / Interfaces -/",
499 "//-------------------------------------/",
500 };
501
502 /**
503 * Print the specified header to the given Writer.
504 * @params JSourceWriter an open JSourceWriter
505 * @params boolean if true print the public header, otherwise
506 * print the non-public header.
507 **/
508 protected static void print(JSourceWriter jsw, boolean printPublic) {
509 String[] header = printPublic ? publicHeader : nonPublicHeader;
510 for (String aHeader : header) {
511 jsw.writeln(aHeader);
512 }
513 jsw.writeln();
514 }
515 } // -- Header