/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 2001-2002 (C) Intalio, Inc. All Rights Reserved. */ package org.exolab.javasource; import java.io.File; import java.io.FileWriter; import java.util.Enumeration; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; /** * A representation of the Java Source code for a Java compilation unit. This is * a useful utility when creating in memory source code. This package was * modelled after the Java Reflection API as much as possible to reduce the * learning curve. * * @author <a href="mailto:shea AT gtsdesign DOT com">Gary Shea</a> * @version $Revision$ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $ */ public final class JCompUnit { /** * Initial size for {@link StringBuilder} instances. */ private static final int INITIAL_STRING_BUILDER_SIZE = 32; /** The Id for Source control systems I needed to break the String to prevent * CVS from expanding it here! ;-) */ private static final String DEFAULT_HEADER = "$" + "Id$"; /** Public header. */ private static final String[] PUBLIC_HEADER = { " //-----------------------------/", " //- Public Class / Interface -/", "//-----------------------------/", }; /** Private header. */ private static final String[] NON_PUBLIC_HEADER = { " //-------------------------------------/", " //- Non-Public Classes / Interfaces -/", "//-------------------------------------/", }; /** JavaDoc comment for this compilation unit. */ private JComment _header = null; /** The package for this JCompUnit. */ private String _packageName = null; /** The file to which this JCompUnit will be written. */ private String _fileName = null; /** The set of top-level classes that live in this compilation unit. */ private Vector<JClass> _classes = new Vector<JClass>(); /** The set of top-level interfaces that live in this compilation unit. */ private Vector<JInterface> _interfaces = new Vector<JInterface>(); /** * Creates a new JCompUnit. * * @param packageName The name of the package for this JCompUnit. If packageName is * null or empty, no 'package' line will be generated. * @param fileName The name of the file to which this JCompUnit will be written. */ public JCompUnit(final String packageName, final String fileName) { _packageName = packageName; _fileName = fileName; } /** * Creates a new JCompUnit with the given JClass (which must have been * created with either a full class name or package/local name) as the * public class. Package and file name are taken from jClass. * * @param jClass The public class for this JCompUnit. */ public JCompUnit(final JClass jClass) { _packageName = jClass.getPackageName(); // The outer name is the package plus the simple name of the // outermost enclosing class. The file name is just the // simple name of the outermost enclosing class, so the // package name part must be stripped off. // Commented out until inner-class support has been added. // kvisco - 20021211 // // String outer = jClass.getOuterName(); // int lastDot = outer.lastIndexOf("."); // String filePrefix; // if (lastDot != -1) { // filePrefix = outer.substring (lastDot + 1); // } else { // filePrefix = outer; // } String filePrefix = jClass.getLocalName(); _fileName = filePrefix + ".java"; _classes.add(jClass); } /** * Creates a new JCompUnit with the given JInterface as public interface. * Package and file name are taken from jInterface. * * @param jInterface The public interface for this JCompUnit. */ public JCompUnit(final JInterface jInterface) { _packageName = jInterface.getPackageName(); _fileName = jInterface.getLocalName() + ".java"; _interfaces.add(jInterface); } /** * Sets the header comment for this JCompUnit. * * @param comment The comment to display at the top of the source file when printed. */ public void setHeader(final JComment comment) { _header = comment; } /** * Adds the given JStructure (either a JInterface or a JClass) to this * JCompUnit. * * @param jStructure The JStructure to add. */ public void addStructure(final JStructure jStructure) { if (jStructure instanceof JInterface) { addInterface((JInterface) jStructure); } else if (jStructure instanceof JClass) { addClass((JClass) jStructure); } else { String err = "Unknown JStructure subclass '" + jStructure.getClass().getName() + "'."; throw new IllegalArgumentException(err); } } /** * Adds a JClass to be printed in this file. * * @param jClass The JClass to be printed in this file. */ public void addClass(final JClass jClass) { _classes.add(jClass); } /** * Adds a JInterface to be printed in this file. * * @param jInterface The JInterface to be printed in this file. */ public void addInterface(final JInterface jInterface) { _interfaces.add(jInterface); } /** * Returns a array of String containing all imported classes/packages, also * imports within the same package of this object. * * @return A array of String containing all import classes/packages, also * imports within the same package of this object. */ public SortedSet<String> getImports() { SortedSet<String> allImports = new TreeSet<String>(); // add imports from classes for (int i = 0; i < _classes.size(); ++i) { JClass jClass = _classes.get(i); Enumeration<String> enumeration = jClass.getImports(); while (enumeration.hasMoreElements()) { allImports.add(enumeration.nextElement()); } } for (int i = 0; i < _interfaces.size(); ++i) { JInterface jInterface = _interfaces.get(i); Enumeration<String> enumeration = jInterface.getImports(); while (enumeration.hasMoreElements()) { allImports.add(enumeration.nextElement()); } } return allImports; } /** * Returns the name of the file that this JCompUnit would be printed to, * given a call to {@link #print(String, String)}, or if destDir is null, a * call to {@link #print()}. * * @param destDir The destination directory. This may be null. * @return The name of the file that this JCompUnit would be printed to. */ public String getFilename(final String destDir) { String filename = new String(_fileName); // -- Convert Java package to path string String javaPackagePath = ""; if ((_packageName != null) && (_packageName.length() > 0)) { javaPackagePath = _packageName.replace('.', File.separatorChar); } // -- Create fully qualified path (including 'destDir') to file File pathFile; if (destDir == null) { pathFile = new File(javaPackagePath); } else { pathFile = new File(destDir, javaPackagePath); } if (!pathFile.exists()) { pathFile.mkdirs(); } // -- Prefix filename with path if (pathFile.toString().length() > 0) { filename = pathFile.toString() + File.separator + filename; } return filename; } /** * Returns the name of the package that this JCompUnit is a member of. * * @return The name of the package that this JCompUnit is a member of, or * null if there is no current package name defined. */ public String getPackageName() { return _packageName; } /** * Prints the source code for this JClass in the current directory with the * default line seperator of the the runtime platform. * * @see #print(String, String) */ public void print() { print(null, null); } /** * Prints the source code for this JClass with the default line seperator of * the the runtime platform. * * @param destDir The destination directory to use as the root directory for * source generation. * * @see #print(String, String) */ public void print(final String destDir) { print(destDir, null); } /** * Prints the source code for this JCompUnit using the provided root * directory and line separator. * * @param destDir The destination directory to use as the root directory for * source generation. * @param lineSeparator The line separator to use at the end of each line. If null, * then the default line separator for the runtime platform will be used. */ public void print(final String destDir, final String lineSeparator) { // -- open output file String filename = getFilename(destDir); File file = new File(filename); JSourceWriter jsw = null; try { jsw = new JSourceWriter(new FileWriter(file)); } catch (java.io.IOException ioe) { System.out.println("unable to create compilation unit file: " + filename); return; } if (lineSeparator == null) { jsw.setLineSeparator(System.getProperty("line.separator")); } else { jsw.setLineSeparator(lineSeparator); } print(jsw); jsw.flush(); jsw.close(); } /** * Prints the source code for this JClass to the provided JSourceWriter. * * @param jsw The JSourceWriter to print to. */ public void print(final JSourceWriter jsw) { // Traverse the nested class and interface hiararchy and // update the names to match the compilation unit. StringBuilder buffer = new StringBuilder(INITIAL_STRING_BUILDER_SIZE); // -- write file header if (_header != null) { _header.print(jsw); } else { jsw.writeln("/*"); jsw.writeln(" * " + DEFAULT_HEADER); jsw.writeln("*/"); } jsw.writeln(); jsw.flush(); // -- print package name if ((_packageName != null) && (_packageName.length() > 0)) { buffer.setLength(0); buffer.append("package "); buffer.append(_packageName); buffer.append(';'); jsw.writeln(buffer.toString()); jsw.writeln(); } // -- print imports jsw.writeln(" //---------------------------------------------/"); jsw.writeln(" //- Imported classes, interfaces and packages -/"); jsw.writeln("//---------------------------------------------/"); jsw.writeln(); SortedSet<String> allImports = getImports(); String compUnitPackage = getPackageName(); for (String importName : allImports) { String importsPackage = JNaming.getPackageFromClassName(importName); if ((importsPackage != null) && !importsPackage.equals(compUnitPackage)) { jsw.write("import "); jsw.write(importName); jsw.writeln(';'); } } jsw.writeln(); // Print the public elements, interfaces first, then classes. // There should only be one public element, but if there are // more we let the compiler catch it. printStructures(jsw, true); // Print the remaining non-public elements, interfaces first. printStructures(jsw, false); jsw.flush(); } /** * Print the source code for the contained JClass objects. * * @param jsw The JSourceWriter to print to. * @param printPublic If true, print only public classes; if false, print only * non-public classes. */ public void printStructures(final JSourceWriter jsw, final boolean printPublic) { // -- print class information // -- we need to add some JavaDoc API adding comments boolean isFirst = true; // SortedSet interfaceList = interfaces.sortedOnFullName(); for (JInterface jInterface : _interfaces) { if (jInterface.getModifiers().isPublic() == printPublic) { if (isFirst) { String[] header = printPublic ? PUBLIC_HEADER : NON_PUBLIC_HEADER; for (int j = 0; j < header.length; ++j) { jsw.writeln(header[j]); } jsw.writeln(); isFirst = false; } jInterface.print(jsw, true); jsw.writeln(); } } // SortedSet classList = classes.sortedOnFullName(); for (JClass jClass : _classes) { if (jClass.getModifiers().isPublic() == printPublic) { if (isFirst) { String[] header = printPublic ? PUBLIC_HEADER : NON_PUBLIC_HEADER; for (int j = 0; j < header.length; ++j) { jsw.writeln(header[j]); } jsw.writeln(); isFirst = false; } jClass.print(jsw, true); jsw.writeln(); } } } }