/*
* Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Licensed Materials - Property of IBM
* RMI-IIOP v1.0
* Copyright IBM Corp. 1998 1999 All Rights Reserved
*
*/
package sun.rmi.rmic.iiop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import sun.tools.java.Identifier;
import sun.tools.java.ClassPath;
import sun.tools.java.ClassFile;
import sun.tools.java.ClassNotFound;
import sun.tools.java.ClassDefinition;
import sun.tools.java.ClassDeclaration;
import sun.rmi.rmic.IndentingWriter;
import sun.rmi.rmic.Main;
import sun.rmi.rmic.iiop.Util;
import java.util.HashSet;
Generator provides a small framework from which IIOP-specific
generators can inherit. Common logic is implemented here which uses
both abstract methods as well as concrete methods which subclasses may
want to override. The following methods must be present in any subclass:
Default constructor
CompoundType getTopType(BatchEnvironment env, ClassDefinition cdef);
int parseArgs(String argv[], int currentIndex);
boolean requireNewInstance();
OutputType[] getOutputTypesFor(CompoundType topType,
HashSet alreadyChecked);
String getFileNameExtensionFor(OutputType outputType);
void writeOutputFor ( OutputType outputType,
HashSet alreadyChecked,
IndentingWriter writer) throws IOException;
Author: Bryan Atsatt
/**
* Generator provides a small framework from which IIOP-specific
* generators can inherit. Common logic is implemented here which uses
* both abstract methods as well as concrete methods which subclasses may
* want to override. The following methods must be present in any subclass:
* <pre>
* Default constructor
* CompoundType getTopType(BatchEnvironment env, ClassDefinition cdef);
* int parseArgs(String argv[], int currentIndex);
* boolean requireNewInstance();
* OutputType[] getOutputTypesFor(CompoundType topType,
* HashSet alreadyChecked);
* String getFileNameExtensionFor(OutputType outputType);
* void writeOutputFor ( OutputType outputType,
* HashSet alreadyChecked,
* IndentingWriter writer) throws IOException;
* </pre>
* @author Bryan Atsatt
*/
public abstract class Generator implements sun.rmi.rmic.Generator,
sun.rmi.rmic.iiop.Constants {
protected boolean alwaysGenerate = false;
protected BatchEnvironment env = null;
protected ContextStack contextStack = null;
private boolean trace = false;
protected boolean idl = false;
Examine and consume command line arguments.
Params: - argv – The command line arguments. Ignore null
and unknown arguments. Set each consumed argument to null.
- error – Report any errors using the main.error() methods.
Returns: true if no errors, false otherwise.
/**
* Examine and consume command line arguments.
* @param argv The command line arguments. Ignore null
* and unknown arguments. Set each consumed argument to null.
* @param error Report any errors using the main.error() methods.
* @return true if no errors, false otherwise.
*/
public boolean parseArgs(String argv[], Main main) {
for (int i = 0; i < argv.length; i++) {
if (argv[i] != null) {
if (argv[i].equalsIgnoreCase("-always") ||
argv[i].equalsIgnoreCase("-alwaysGenerate")) {
alwaysGenerate = true;
argv[i] = null;
} else if (argv[i].equalsIgnoreCase("-xtrace")) {
trace = true;
argv[i] = null;
}
}
}
return true;
}
Return true if non-conforming types should be parsed.
Params: - stack – The context stack.
/**
* Return true if non-conforming types should be parsed.
* @param stack The context stack.
*/
protected abstract boolean parseNonConforming(ContextStack stack);
Create and return a top-level type.
Params: - cdef – The top-level class definition.
- stack – The context stack.
Returns: The compound type or null if is non-conforming.
/**
* Create and return a top-level type.
* @param cdef The top-level class definition.
* @param stack The context stack.
* @return The compound type or null if is non-conforming.
*/
protected abstract CompoundType getTopType(ClassDefinition cdef, ContextStack stack);
Return an array containing all the file names and types that need to be
generated for the given top-level type. The file names must NOT have an
extension (e.g. ".java").
Params: - topType – The type returned by getTopType().
- alreadyChecked – A set of Types which have already been checked.
Intended to be passed to Type.collectMatching(filter,alreadyChecked).
/**
* Return an array containing all the file names and types that need to be
* generated for the given top-level type. The file names must NOT have an
* extension (e.g. ".java").
* @param topType The type returned by getTopType().
* @param alreadyChecked A set of Types which have already been checked.
* Intended to be passed to Type.collectMatching(filter,alreadyChecked).
*/
protected abstract OutputType[] getOutputTypesFor(CompoundType topType,
HashSet alreadyChecked);
Return the file name extension for the given file name (e.g. ".java").
All files generated with the ".java" extension will be compiled. To
change this behavior for ".java" files, override the compileJavaSourceFile
method to return false.
Params: - outputType – One of the items returned by getOutputTypesFor(...)
/**
* Return the file name extension for the given file name (e.g. ".java").
* All files generated with the ".java" extension will be compiled. To
* change this behavior for ".java" files, override the compileJavaSourceFile
* method to return false.
* @param outputType One of the items returned by getOutputTypesFor(...)
*/
protected abstract String getFileNameExtensionFor(OutputType outputType);
Write the output for the given OutputFileName into the output stream.
Params: - name – One of the items returned by getOutputTypesFor(...)
- alreadyChecked – A set of Types which have already been checked.
Intended to be passed to Type.collectMatching(filter,alreadyChecked).
- writer – The output stream.
/**
* Write the output for the given OutputFileName into the output stream.
* @param name One of the items returned by getOutputTypesFor(...)
* @param alreadyChecked A set of Types which have already been checked.
* Intended to be passed to Type.collectMatching(filter,alreadyChecked).
* @param writer The output stream.
*/
protected abstract void writeOutputFor(OutputType outputType,
HashSet alreadyChecked,
IndentingWriter writer) throws IOException;
Return true if a new instance should be created for each
class on the command line. Subclasses which return true
should override newInstance() to return an appropriately
constructed instance.
/**
* Return true if a new instance should be created for each
* class on the command line. Subclasses which return true
* should override newInstance() to return an appropriately
* constructed instance.
*/
protected abstract boolean requireNewInstance();
Return true if the specified file needs generation.
/**
* Return true if the specified file needs generation.
*/
public boolean requiresGeneration (File target, Type theType) {
boolean result = alwaysGenerate;
if (!result) {
// Get a ClassFile instance for base source or class
// file. We use ClassFile so that if the base is in
// a zip file, we can still get at it's mod time...
ClassFile baseFile;
ClassPath path = env.getClassPath();
String className = theType.getQualifiedName().replace('.',File.separatorChar);
// First try the source file...
baseFile = path.getFile(className + ".source");
if (baseFile == null) {
// Then try class file...
baseFile = path.getFile(className + ".class");
}
// Do we have a baseFile?
if (baseFile != null) {
// Yes, grab baseFile's mod time...
long baseFileMod = baseFile.lastModified();
// Get a File instance for the target. If it is a source
// file, create a class file instead since the source file
// will frequently be deleted...
String targetName = IDLNames.replace(target.getName(),".java",".class");
String parentPath = target.getParent();
File targetFile = new File(parentPath,targetName);
// Does the target file exist?
if (targetFile.exists()) {
// Yes, so grab it's mod time...
long targetFileMod = targetFile.lastModified();
// Set result...
result = targetFileMod < baseFileMod;
} else {
// No, so we must generate...
result = true;
}
} else {
// No, so we must generate...
result = true;
}
}
return result;
}
Create and return a new instance of self. Subclasses
which need to do something other than default construction
must override this method.
/**
* Create and return a new instance of self. Subclasses
* which need to do something other than default construction
* must override this method.
*/
protected Generator newInstance() {
Generator result = null;
try {
result = (Generator) getClass().newInstance();
}
catch (Exception e){} // Should ALWAYS work!
return result;
}
Default constructor for subclasses to use.
/**
* Default constructor for subclasses to use.
*/
protected Generator() {
}
Generate output. Any source files created which need compilation should
be added to the compiler environment using the addGeneratedFile(File)
method.
Params: - env – The compiler environment
- cdef – The definition for the implementation class or interface from
which to generate output
- destDir – The directory for the root of the package hierarchy
for generated files. May be null.
/**
* Generate output. Any source files created which need compilation should
* be added to the compiler environment using the addGeneratedFile(File)
* method.
*
* @param env The compiler environment
* @param cdef The definition for the implementation class or interface from
* which to generate output
* @param destDir The directory for the root of the package hierarchy
* for generated files. May be null.
*/
public void generate(sun.rmi.rmic.BatchEnvironment env, ClassDefinition cdef, File destDir) {
this.env = (BatchEnvironment) env;
contextStack = new ContextStack(this.env);
contextStack.setTrace(trace);
// Make sure the environment knows whether or not to parse
// non-conforming types. This will clear out any previously
// parsed types if necessary...
this.env.setParseNonConforming(parseNonConforming(contextStack));
// Get our top level type...
CompoundType topType = getTopType(cdef,contextStack);
if (topType != null) {
Generator generator = this;
// Do we need to make a new instance?
if (requireNewInstance()) {
// Yes, so make one. 'this' instance is the one instantiated by Main
// and which knows any needed command line args...
generator = newInstance();
}
// Now generate all output files...
generator.generateOutputFiles(topType, this.env, destDir);
}
}
Create and return a new instance of self. Subclasses
which need to do something other than default construction
must override this method.
/**
* Create and return a new instance of self. Subclasses
* which need to do something other than default construction
* must override this method.
*/
protected void generateOutputFiles (CompoundType topType,
BatchEnvironment env,
File destDir) {
// Grab the 'alreadyChecked' HashSet from the environment...
HashSet alreadyChecked = env.alreadyChecked;
// Ask subclass for a list of output types...
OutputType[] types = getOutputTypesFor(topType,alreadyChecked);
// Process each file...
for (int i = 0; i < types.length; i++) {
OutputType current = types[i];
String className = current.getName();
File file = getFileFor(current,destDir);
boolean sourceFile = false;
// Do we need to generate this file?
if (requiresGeneration(file,current.getType())) {
// Yes. If java source file, add to environment so will be compiled...
if (file.getName().endsWith(".java")) {
sourceFile = compileJavaSourceFile(current);
// Are we supposeded to compile this one?
if (sourceFile) {
env.addGeneratedFile(file);
}
}
// Now create an output stream and ask subclass to fill it up...
try {
IndentingWriter out = new IndentingWriter(
new OutputStreamWriter(new FileOutputStream(file)),INDENT_STEP,TAB_SIZE);
long startTime = 0;
if (env.verbose()) {
startTime = System.currentTimeMillis();
}
writeOutputFor(types[i],alreadyChecked,out);
out.close();
if (env.verbose()) {
long duration = System.currentTimeMillis() - startTime;
env.output(Main.getText("rmic.generated", file.getPath(), Long.toString(duration)));
}
if (sourceFile) {
env.parseFile(ClassFile.newClassFile(file));
}
} catch (IOException e) {
env.error(0, "cant.write", file.toString());
return;
}
} else {
// No, say so if we need to...
if (env.verbose()) {
env.output(Main.getText("rmic.previously.generated", file.getPath()));
}
}
}
}
Return the File object that should be used as the output file
for the given OutputType.
Params: - outputType – The type to create a file for.
- destinationDir – The directory to use as the root of the
package heirarchy. May be null, in which case the current
classpath is searched to find the directory in which to create
the output file. If that search fails (most likely because the
package directory lives in a zip or jar file rather than the
file system), the current user directory is used.
/**
* Return the File object that should be used as the output file
* for the given OutputType.
* @param outputType The type to create a file for.
* @param destinationDir The directory to use as the root of the
* package heirarchy. May be null, in which case the current
* classpath is searched to find the directory in which to create
* the output file. If that search fails (most likely because the
* package directory lives in a zip or jar file rather than the
* file system), the current user directory is used.
*/
protected File getFileFor(OutputType outputType, File destinationDir) {
// Calling this method does some crucial initialization
// in a subclass implementation. Don't skip it.
Identifier id = getOutputId(outputType);
File packageDir = null;
if(idl){
packageDir = Util.getOutputDirectoryForIDL(id,destinationDir,env);
} else {
packageDir = Util.getOutputDirectoryForStub(id,destinationDir,env);
}
String classFileName = outputType.getName() + getFileNameExtensionFor(outputType);
return new File(packageDir, classFileName);
}
Return an identifier to use for output.
Params: - outputType – the type for which output is to be generated.
Returns: the new identifier. This implementation returns the input parameter.
/**
* Return an identifier to use for output.
* @param outputType the type for which output is to be generated.
* @return the new identifier. This implementation returns the input parameter.
*/
protected Identifier getOutputId (OutputType outputType) {
return outputType.getType().getIdentifier();
}
Return true if the given file should be compiled.
Params: - outputType – One of the items returned by getOutputTypesFor(...) for
which getFileNameExtensionFor(OutputType) returned ".java".
/**
* Return true if the given file should be compiled.
* @param outputType One of the items returned by getOutputTypesFor(...) for
* which getFileNameExtensionFor(OutputType) returned ".java".
*/
protected boolean compileJavaSourceFile (OutputType outputType) {
return true;
}
//_____________________________________________________________________
// OutputType is a simple wrapper for a name and a Type
//_____________________________________________________________________
public class OutputType {
private String name;
private Type type;
public OutputType (String name, Type type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
}
}