001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 2001-2002 (C) Intalio, Inc. All Rights Reserved.
042: */package org.exolab.javasource;
043:
044: import java.io.File;
045: import java.io.FileWriter;
046: import java.util.Enumeration;
047: import java.util.Iterator;
048: import java.util.SortedSet;
049: import java.util.TreeSet;
050: import java.util.Vector;
051:
052: /**
053: * A representation of the Java Source code for a Java compilation unit. This is
054: * a useful utility when creating in memory source code. This package was
055: * modelled after the Java Reflection API as much as possible to reduce the
056: * learning curve.
057: *
058: * @author <a href="mailto:shea AT gtsdesign DOT com">Gary Shea</a>
059: * @version $Revision: 6669 $ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
060: */
061: public final class JCompUnit {
062: //--------------------------------------------------------------------------
063:
064: /** The Id for Source control systems I needed to break the String to prevent
065: * CVS from expanding it here! ;-) */
066: private static final String DEFAULT_HEADER = "$" + "Id$";
067:
068: /** Public header. */
069: private static final String[] PUBLIC_HEADER = {
070: " //-----------------------------/",
071: " //- Public Class / Interface -/",
072: "//-----------------------------/", };
073:
074: /** Private header. */
075: private static final String[] NON_PUBLIC_HEADER = {
076: " //-------------------------------------/",
077: " //- Non-Public Classes / Interfaces -/",
078: "//-------------------------------------/", };
079:
080: //--------------------------------------------------------------------------
081:
082: /** JavaDoc comment for this compilation unit. */
083: private JComment _header = null;
084:
085: /** The package for this JCompUnit. */
086: private String _packageName = null;
087:
088: /** The file to which this JCompUnit will be written. */
089: private String _fileName = null;
090:
091: /** The set of top-level classes that live in this compilation unit. */
092: private Vector _classes = null;
093:
094: /** The set of top-level interfaces that live in this compilation unit. */
095: private Vector _interfaces = null;
096:
097: //--------------------------------------------------------------------------
098:
099: /**
100: * Creates a new JCompUnit.
101: *
102: * @param packageName The name of the package for this JCompUnit. If packageName is
103: * null or empty, no 'package' line will be generated.
104: * @param fileName The name of the file to which this JCompUnit will be written.
105: */
106: public JCompUnit(final String packageName, final String fileName) {
107: _packageName = packageName;
108: _fileName = fileName;
109: init();
110: }
111:
112: /**
113: * Creates a new JCompUnit with the given JClass (which must have been
114: * created with either a full class name or package/local name) as the
115: * public class. Package and file name are taken from jClass.
116: *
117: * @param jClass The public class for this JCompUnit.
118: */
119: public JCompUnit(final JClass jClass) {
120: _packageName = jClass.getPackageName();
121:
122: // The outer name is the package plus the simple name of the
123: // outermost enclosing class. The file name is just the
124: // simple name of the outermost enclosing class, so the
125: // package name part must be stripped off.
126:
127: // Commented out until inner-class support has been added.
128: // kvisco - 20021211
129: //
130: // String outer = jClass.getOuterName();
131: // int lastDot = outer.lastIndexOf(".");
132: // String filePrefix;
133: // if (lastDot != -1) {
134: // filePrefix = outer.substring (lastDot + 1);
135: // } else {
136: // filePrefix = outer;
137: // }
138:
139: String filePrefix = jClass.getLocalName();
140:
141: _fileName = filePrefix + ".java";
142: init();
143: _classes.add(jClass);
144: }
145:
146: /**
147: * Creates a new JCompUnit with the given JInterface as public interface.
148: * Package and file name are taken from jInterface.
149: *
150: * @param jInterface The public interface for this JCompUnit.
151: */
152: public JCompUnit(final JInterface jInterface) {
153: _packageName = jInterface.getPackageName();
154: _fileName = jInterface.getLocalName() + ".java";
155: init();
156: _interfaces.add(jInterface);
157: }
158:
159: /**
160: * Common initialization code.
161: */
162: private void init() {
163: _classes = new Vector();
164: _interfaces = new Vector();
165: }
166:
167: //--------------------------------------------------------------------------
168:
169: /**
170: * Sets the header comment for this JCompUnit.
171: *
172: * @param comment The comment to display at the top of the source file when printed.
173: */
174: public void setHeader(final JComment comment) {
175: _header = comment;
176: }
177:
178: /**
179: * Adds the given JStructure (either a JInterface or a JClass) to this
180: * JCompUnit.
181: *
182: * @param jStructure The JStructure to add.
183: */
184: public void addStructure(final JStructure jStructure) {
185: if (jStructure instanceof JInterface) {
186: addInterface((JInterface) jStructure);
187: } else if (jStructure instanceof JClass) {
188: addClass((JClass) jStructure);
189: } else {
190: String err = "Unknown JStructure subclass '"
191: + jStructure.getClass().getName() + "'.";
192: throw new IllegalArgumentException(err);
193: }
194: }
195:
196: /**
197: * Adds a JClass to be printed in this file.
198: *
199: * @param jClass The JClass to be printed in this file.
200: */
201: public void addClass(final JClass jClass) {
202: _classes.add(jClass);
203: }
204:
205: /**
206: * Adds a JInterface to be printed in this file.
207: *
208: * @param jInterface The JInterface to be printed in this file.
209: */
210: public void addInterface(final JInterface jInterface) {
211: _interfaces.add(jInterface);
212: }
213:
214: /**
215: * Returns a array of String containing all imported classes/packages, also
216: * imports within the same package of this object.
217: *
218: * @return A array of String containing all import classes/packages, also
219: * imports within the same package of this object.
220: */
221: public SortedSet getImports() {
222: SortedSet allImports = new TreeSet();
223:
224: // add imports from classes
225: for (int i = 0; i < _classes.size(); ++i) {
226: JClass jClass = (JClass) _classes.get(i);
227:
228: Enumeration enumeration = jClass.getImports();
229: while (enumeration.hasMoreElements()) {
230: allImports.add(enumeration.nextElement());
231: }
232: }
233:
234: for (int i = 0; i < _interfaces.size(); ++i) {
235: JInterface jInterface = (JInterface) _interfaces.get(i);
236: Enumeration enumeration = jInterface.getImports();
237: while (enumeration.hasMoreElements()) {
238: allImports.add(enumeration.nextElement());
239: }
240: }
241:
242: return allImports;
243: }
244:
245: /**
246: * Returns the name of the file that this JCompUnit would be printed to,
247: * given a call to {@link #print(String, String)}, or if destDir is null, a
248: * call to {@link #print()}.
249: *
250: * @param destDir The destination directory. This may be null.
251: * @return The name of the file that this JCompUnit would be printed to.
252: */
253: public String getFilename(final String destDir) {
254: String filename = new String(_fileName);
255:
256: // -- Convert Java package to path string
257: String javaPackagePath = "";
258: if ((_packageName != null) && (_packageName.length() > 0)) {
259: javaPackagePath = _packageName.replace('.',
260: File.separatorChar);
261: }
262:
263: // -- Create fully qualified path (including 'destDir') to file
264: File pathFile;
265: if (destDir == null) {
266: pathFile = new File(javaPackagePath);
267: } else {
268: pathFile = new File(destDir, javaPackagePath);
269: }
270: if (!pathFile.exists()) {
271: pathFile.mkdirs();
272: }
273:
274: // -- Prefix filename with path
275: if (pathFile.toString().length() > 0) {
276: filename = pathFile.toString() + File.separator + filename;
277: }
278:
279: return filename;
280: }
281:
282: /**
283: * Returns the name of the package that this JCompUnit is a member of.
284: *
285: * @return The name of the package that this JCompUnit is a member of, or
286: * null if there is no current package name defined.
287: */
288: public String getPackageName() {
289: return _packageName;
290: }
291:
292: //--------------------------------------------------------------------------
293:
294: /**
295: * Prints the source code for this JClass in the current directory with the
296: * default line seperator of the the runtime platform.
297: *
298: * @see #print(String, String)
299: */
300: public void print() {
301: print(null, null);
302: }
303:
304: /**
305: * Prints the source code for this JClass with the default line seperator of
306: * the the runtime platform.
307: *
308: * @param destDir The destination directory to use as the root directory for
309: * source generation.
310: *
311: * @see #print(String, String)
312: */
313: public void print(final String destDir) {
314: print(destDir, null);
315: }
316:
317: /**
318: * Prints the source code for this JCompUnit using the provided root
319: * directory and line separator.
320: *
321: * @param destDir The destination directory to use as the root directory for
322: * source generation.
323: * @param lineSeparator The line separator to use at the end of each line. If null,
324: * then the default line separator for the runtime platform will be used.
325: */
326: public void print(final String destDir, final String lineSeparator) {
327: // -- open output file
328: String filename = getFilename(destDir);
329:
330: File file = new File(filename);
331: JSourceWriter jsw = null;
332: try {
333: jsw = new JSourceWriter(new FileWriter(file));
334: } catch (java.io.IOException ioe) {
335: System.out
336: .println("unable to create compilation unit file: "
337: + filename);
338: return;
339: }
340:
341: if (lineSeparator == null) {
342: jsw.setLineSeparator(System.getProperty("line.separator"));
343: } else {
344: jsw.setLineSeparator(lineSeparator);
345: }
346: print(jsw);
347: jsw.flush();
348: jsw.close();
349: }
350:
351: /**
352: * Prints the source code for this JClass to the provided JSourceWriter.
353: *
354: * @param jsw The JSourceWriter to print to.
355: */
356: public void print(final JSourceWriter jsw) {
357: // Traverse the nested class and interface heirarchy and
358: // update the names to match the compilation unit.
359:
360: StringBuffer buffer = new StringBuffer();
361:
362: // -- write file header
363: if (_header != null) {
364: _header.print(jsw);
365: } else {
366: jsw.writeln("/*");
367: jsw.writeln(" * " + DEFAULT_HEADER);
368: jsw.writeln("*/");
369: }
370: jsw.writeln();
371: jsw.flush();
372:
373: // -- print package name
374: if ((_packageName != null) && (_packageName.length() > 0)) {
375: buffer.setLength(0);
376: buffer.append("package ");
377: buffer.append(_packageName);
378: buffer.append(';');
379: jsw.writeln(buffer.toString());
380: jsw.writeln();
381: }
382:
383: // -- print imports
384: jsw
385: .writeln(" //---------------------------------------------/");
386: jsw
387: .writeln(" //- Imported classes, interfaces and packages -/");
388: jsw.writeln("//---------------------------------------------/");
389: jsw.writeln();
390: SortedSet allImports = getImports();
391: String compUnitPackage = getPackageName();
392: for (Iterator iter = allImports.iterator(); iter.hasNext();) {
393: String importName = (String) iter.next();
394: String importsPackage = JNaming
395: .getPackageFromClassName(importName);
396: if ((importsPackage != null)
397: && !importsPackage.equals(compUnitPackage)) {
398: jsw.write("import ");
399: jsw.write(importName);
400: jsw.writeln(';');
401: }
402: }
403: jsw.writeln();
404:
405: // Print the public elements, interfaces first, then classes.
406: // There should only be one public element, but if there are
407: // more we let the compiler catch it.
408: printStructures(jsw, true);
409:
410: // Print the remaining non-public elements, interfaces first.
411: printStructures(jsw, false);
412:
413: jsw.flush();
414: }
415:
416: /**
417: * Print the source code for the contained JClass objects.
418: *
419: * @param jsw The JSourceWriter to print to.
420: * @param printPublic If true, print only public classes; if false, print only
421: * non-public classes.
422: */
423: public void printStructures(final JSourceWriter jsw,
424: final boolean printPublic) {
425: // -- print class information
426: // -- we need to add some JavaDoc API adding comments
427:
428: boolean isFirst = true;
429:
430: // SortedSet interfaceList = interfaces.sortedOnFullName();
431: for (Enumeration e = _interfaces.elements(); e
432: .hasMoreElements();) {
433: JInterface jInterface = (JInterface) e.nextElement();
434: if (jInterface.getModifiers().isPublic() == printPublic) {
435: if (isFirst) {
436: String[] header = printPublic ? PUBLIC_HEADER
437: : NON_PUBLIC_HEADER;
438: for (int j = 0; j < header.length; ++j) {
439: jsw.writeln(header[j]);
440: }
441: jsw.writeln();
442: isFirst = false;
443: }
444: jInterface.print(jsw, true);
445: jsw.writeln();
446: }
447: }
448:
449: // SortedSet classList = classes.sortedOnFullName();
450: for (Enumeration e = _classes.elements(); e.hasMoreElements();) {
451: JClass jClass = (JClass) e.nextElement();
452: if (jClass.getModifiers().isPublic() == printPublic) {
453: if (isFirst) {
454: String[] header = printPublic ? PUBLIC_HEADER
455: : NON_PUBLIC_HEADER;
456: for (int j = 0; j < header.length; ++j) {
457: jsw.writeln(header[j]);
458: }
459: jsw.writeln();
460: isFirst = false;
461: }
462: jClass.print(jsw, true);
463: jsw.writeln();
464: }
465: }
466: }
467:
468: //--------------------------------------------------------------------------
469: }
|