/*
* Copyright (c) 2008, 2009, 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.
*/
package build.tools.logutil;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.Formatter;
import java.util.List;
import java.util.Queue;
public class MC {
private static final String VERSION = "1.0";
private static final List<String> SUN_EXCEPTION_GROUPS = Arrays.asList(new String[]
{ "SUNBASE", "ORBUTIL", "ACTIVATION", "NAMING", "INTERCEPTORS", "POA", "IOR", "UTIL" });
private static final List<String> EXCEPTIONS = Arrays.asList(new String[]
{ "UNKNOWN", "BAD_PARAM", "NO_MEMORY", "IMP_LIMIT", "COMM_FAILURE", "INV_OBJREF", "NO_PERMISSION",
"INTERNAL", "MARSHAL", "INITIALIZE", "NO_IMPLEMENT", "BAD_TYPECODE", "BAD_OPERATION", "NO_RESOURCES",
"NO_RESPONSE", "PERSIST_STORE", "BAD_INV_ORDER", "TRANSIENT", "FREE_MEM", "INV_IDENT", "INV_FLAG",
"INTF_REPOS", "BAD_CONTEXT", "OBJ_ADAPTER", "DATA_CONVERSION", "OBJECT_NOT_EXIST", "TRANSACTION_REQUIRED",
"TRANSACTION_ROLLEDBACK", "INVALID_TRANSACTION", "INV_POLICY", "CODESET_INCOMPATIBLE", "REBIND",
"TIMEOUT", "TRANSACTION_UNAVAILABLE", "BAD_QOS", "INVALID_ACTIVITY", "ACTIVITY_COMPLETED",
"ACTIVITY_REQUIRED" });
/**
* Read the minor codes from the input file and
* write out a resource file.
*
* @param inFile the file to read the codes from.
* @param outDir the directory to write the resource file to.
* @throws FileNotFoundException if the input file can not be found.
* @throws IOException if an I/O error occurs.
*/
private void makeResource(String inFile, String outDir)
throws FileNotFoundException, IOException {
writeResource(outDir, new Input(inFile));
}
/**
* Create a new Java source file using the specified Scheme input file,
* and writing the result to the given output directory.
*
* @param inFile the file to read the data from.
* @param outDir the directory to write the Java class to.
* @throws FileNotFoundException if the input file can not be found.
* @throws IOException if an I/O error occurs.
*/
private void makeClass(String inFile, String outDir)
throws FileNotFoundException, IOException {
writeClass(inFile, outDir, new Input(inFile));
}
/**
* Writes out a Java source file using the data from the given
* {@link Input} object. The result is written to {@code outDir}.
* The name of the input file is just used in the header of the
* resulting source file.
*
* @param inFile the name of the file the data was read from.
* @param outDir the directory to write the Java class to.
* @param input the parsed input data.
* @throws FileNotFoundException if the output file can't be written.
*/
private void writeClass(String inFile, String outDir, Input input)
throws FileNotFoundException {
String packageName = input.getPackageName();
String className = input.getClassName();
String groupName = input.getGroupName();
Queue<InputException> exceptions = input.getExceptions();
FileOutputStream file = new FileOutputStream(outDir + File.separator + className + ".java");
IndentingPrintWriter pw = new IndentingPrintWriter(file);
writeClassHeader(inFile, groupName, pw);
pw.printMsg("package @ ;", packageName);
pw.println();
pw.println("import java.util.logging.Logger ;");
pw.println("import java.util.logging.Level ;");
pw.println();
pw.println("import org.omg.CORBA.OMGVMCID ;");
pw.println( "import com.sun.corba.se.impl.util.SUNVMCID ;");
pw.println( "import org.omg.CORBA.CompletionStatus ;");
pw.println( "import org.omg.CORBA.SystemException ;");
pw.println();
pw.println( "import com.sun.corba.se.spi.orb.ORB ;");
pw.println();
pw.println( "import com.sun.corba.se.spi.logging.LogWrapperFactory;");
pw.println();
pw.println( "import com.sun.corba.se.spi.logging.LogWrapperBase;");
pw.println();
writeImports(exceptions, pw);
pw.println();
pw.indent();
pw.printMsg("public class @ extends LogWrapperBase {", className);
pw.println();
pw.printMsg("public @( Logger logger )", className);
pw.indent();
pw.println( "{");
pw.undent();
pw.println( "super( logger ) ;");
pw.println( "}");
pw.println();
pw.flush();
writeFactoryMethod(className, groupName, pw);
writeExceptions(groupName, exceptions, className, pw);
pw.undent();
pw.println( );
pw.println( "}");
pw.flush();
pw.close();
}
/**
* Writes out the header of a Java source file.
*
* @param inFile the input file the file was generated from.
* @param groupName the group of exceptions the Java source file is for.
* @param pw the print writer used to write the output.
*/
private void writeClassHeader(String inFile, String groupName,
IndentingPrintWriter pw) {
if (groupName.equals("OMG"))
pw.println("// Log wrapper class for standard exceptions");
else
pw.printMsg("// Log wrapper class for Sun private system exceptions in group @",
groupName);
pw.println("//");
pw.printMsg("// Generated by MC.java version @, DO NOT EDIT BY HAND!", VERSION);
pw.printMsg("// Generated from input file @ on @", inFile, new Date());
pw.println();
}
/**
* Write out the import list for the exceptions.
*
* @param groups the exceptions that were parsed.
* @param pw the {@link IndentingPrintWriter} for writing to the file.
*/
private void writeImports(Queue<InputException> exceptions,
IndentingPrintWriter pw) {
if (exceptions == null)
return;
for (InputException e : exceptions)
pw.println("import org.omg.CORBA." + e.getName() + " ;");
}
/**
* Write out the factory method for this group of exceptions.
*
* @param className the name of the generated class.
* @param groupName the name of this group of exceptions.
* @param pw the {@link IndentingPrintWriter} for writing to the file.
*/
private void writeFactoryMethod(String className, String groupName,
IndentingPrintWriter pw) {
pw.indent();
pw.println( "private static LogWrapperFactory factory = new LogWrapperFactory() {");
pw.println( "public LogWrapperBase create( Logger logger )" );
pw.indent();
pw.println( "{");
pw.undent();
pw.printMsg("return new @( logger ) ;", className);
pw.undent();
pw.println( "}" );
pw.println( "} ;" );
pw.println();
pw.printMsg("public static @ get( ORB orb, String logDomain )", className);
pw.indent();
pw.println( "{");
pw.indent();
pw.printMsg( "@ wrapper = ", className);
pw.indent();
pw.printMsg( "(@) orb.getLogWrapper( logDomain, ", className);
pw.undent();
pw.undent();
pw.printMsg( "\"@\", factory ) ;", groupName);
pw.undent();
pw.println( "return wrapper ;" );
pw.println( "} " );
pw.println();
pw.printMsg( "public static @ get( String logDomain )", className);
pw.indent();
pw.println( "{");
pw.indent();
pw.printMsg( "@ wrapper = ", className);
pw.indent();
pw.printMsg( "(@) ORB.staticGetLogWrapper( logDomain, ", className);
pw.undent();
pw.undent();
pw.printMsg( "\"@\", factory ) ;", groupName);
pw.undent();
pw.println( "return wrapper ;" );
pw.println( "} " );
pw.println();
}
/**
* Writes out the exceptions themselves.
*
* @param groupName the name of this group of exceptions.
* @param exceptions the exceptions to write out.
* @param className the name of the generated class.
* @param pw the {@link IndentingPrintWriter} for writing to the file.
*/
private void writeExceptions(String groupName, Queue<InputException> exceptions,
String className, IndentingPrintWriter pw) {
for (InputException e : exceptions) {
pw.println("///////////////////////////////////////////////////////////");
pw.printMsg("// @", e.getName());
pw.println("///////////////////////////////////////////////////////////");
pw.println();
for (InputCode c : e.getCodes())
writeMethods(groupName, e.getName(), c.getName(), c.getCode(),
c.getLogLevel(), className, StringUtil.countArgs(c.getMessage()), pw);
pw.flush();
}
}
/**
* Writes out the methods for a particular error.
*
* @param groupName the name of this group of exceptions.
* @param exceptionName the name of this particular exception.
* @param errorName the name of this particular error.
* @param code the minor code for this particular error.
* @param ident the name of the error in mixed-case identifier form.
* @param level the level at which to place log messages.
* @param className the name of the class for this group of exceptions.
* @param numParams the number of parameters the detail message takes.
* @param pw the print writer for writing to the file.
*/
private void writeMethods(String groupName, String exceptionName, String errorName,
int code, String level, String className, int numParams,
IndentingPrintWriter pw) {
String ident = StringUtil.toMixedCase(errorName);
pw.printMsg("public static final int @ = @ ;", errorName, getBase(groupName, code));
pw.println();
pw.flush();
writeMethodStatusCause(groupName, exceptionName, errorName, ident, level,
numParams, className, pw);
pw.println();
pw.flush();
writeMethodStatus(exceptionName, ident, numParams, pw);
pw.println();
pw.flush();
writeMethodCause(exceptionName, ident, numParams, pw);
pw.println();
pw.flush();
writeMethodNoArgs(exceptionName, ident, numParams, pw);
pw.println();
pw.flush();
}
/**
* Writes out a method for an error that takes a
* {@link org.omg.CORBA.CompletionStatus} and a cause.
*
* @param groupName the name of this group of exceptions.
* @param exceptionName the name of this particular exception.
* @param errorName the name of this particular error.
* @param ident the name of the error in mixed-case identifier form.
* @param logLevel the level at which to place log messages.
* @param numParams the number of parameters the detail message takes.
* @param className the name of the class for this group of exceptions.
* @param pw the print writer for writing to the file.
*/
private void writeMethodStatusCause(String groupName, String exceptionName,
String errorName, String ident,
String logLevel, int numParams,
String className, IndentingPrintWriter pw) {
pw.indent();
pw.printMsg( "public @ @( CompletionStatus cs, Throwable t@) {", exceptionName,
ident, makeDeclArgs(true, numParams));
pw.printMsg( "@ exc = new @( @, cs ) ;", exceptionName, exceptionName, errorName);
pw.indent();
pw.println( "if (t != null)" );
pw.undent();
pw.println( "exc.initCause( t ) ;" );
pw.println();
pw.indent();
pw.printMsg( "if (logger.isLoggable( Level.@ )) {", logLevel);
if (numParams > 0) {
pw.printMsg( "Object[] parameters = new Object[@] ;", numParams);
for (int a = 0; a < numParams; ++a)
pw.printMsg("parameters[@] = arg@ ;", a, a);
} else
pw.println( "Object[] parameters = null ;");
pw.indent();
pw.printMsg( "doLog( Level.@, \"@.@\",", logLevel, groupName, ident);
pw.undent();
pw.undent();
pw.printMsg( "parameters, @.class, exc ) ;", className);
pw.println( "}");
pw.println();
pw.undent();
pw.println( "return exc ;");
pw.println( "}");
}
/**
* Writes out a method for an error that takes a
* {@link org.omg.CORBA.CompletionStatus}.
*
* @param exceptionName the name of this particular exception.
* @param ident the name of the error in mixed-case identifier form.
* @param numParams the number of parameters the detail message takes.
* @param pw the print writer for writing to the file.
*/
private void writeMethodStatus(String exceptionName, String ident,
int numParams, IndentingPrintWriter pw) {
pw.indent();
pw.printMsg("public @ @( CompletionStatus cs@) {", exceptionName,
ident, makeDeclArgs(true, numParams));
pw.undent();
pw.printMsg("return @( cs, null@ ) ;", ident, makeCallArgs(true, numParams));
pw.println("}");
}
/**
* Writes out a method for an error that takes a cause.
*
* @param exceptionName the name of this particular exception.
* @param ident the name of the error in mixed-case identifier form.
* @param numParams the number of parameters the detail message takes.
* @param pw the print writer for writing to the file.
*/
private void writeMethodCause(String exceptionName, String ident,
int numParams, IndentingPrintWriter pw) {
pw.indent();
pw.printMsg("public @ @( Throwable t@) {", exceptionName, ident,
makeDeclArgs(true, numParams));
pw.undent();
pw.printMsg("return @( CompletionStatus.COMPLETED_NO, t@ ) ;", ident,
makeCallArgs(true, numParams));
pw.println("}");
}
/**
* Writes out a method for an error that takes no arguments.
*
* @param exceptionName the name of this particular exception.
* @param ident the name of the error in mixed-case identifier form.
* @param numParams the number of parameters the detail message takes.
* @param pw the print writer for writing to the file.
*/
private void writeMethodNoArgs(String exceptionName, String ident,
int numParams, IndentingPrintWriter pw) {
pw.indent();
pw.printMsg("public @ @( @) {", exceptionName, ident,
makeDeclArgs(false, numParams));
pw.undent();
pw.printMsg("return @( CompletionStatus.COMPLETED_NO, null@ ) ;",
ident, makeCallArgs(true, numParams));
pw.println("}");
}
/**
* Returns a list of comma-separated arguments with type declarations.
*
* @param leadingComma true if the list should start with a comma.
* @param numArgs the number of arguments to generate.
* @return the generated string.
*/
private String makeDeclArgs(boolean leadingComma, int numArgs) {
return makeArgString("Object arg", leadingComma, numArgs);
}
/**
* Returns a list of comma-separated arguments without type declarations.
*
* @param leadingComma true if the list should start with a comma.
* @param numArgs the number of arguments to generate.
* @return the generated string.
*/
private String makeCallArgs(boolean leadingComma, int numArgs) {
return makeArgString("arg", leadingComma, numArgs);
}
/**
* Returns a list of comma-separated arguments.
*
* @param prefixString the string with which to prefix each argument.
* @param leadingComma true if the list should start with a comma.
* @param numArgs the number of arguments to generate.
* @return the generated string.
*/
private String makeArgString(String prefixString, boolean leadingComma,
int numArgs) {
if (numArgs == 0)
return " ";
if (numArgs == 1) {
if (leadingComma)
return ", " + prefixString + (numArgs - 1);
else
return " " + prefixString + (numArgs - 1);
}
return makeArgString(prefixString, leadingComma, numArgs - 1) +
", " + prefixString + (numArgs - 1);
}
/**
* Returns the {@link String} containing the calculation of the
* error code.
*
* @param groupName the group of exception to which the code belongs.
* @param code the minor code number representing the exception within the group.
* @return the unique error code.
*/
private String getBase(String groupName, int code) {
if (groupName.equals("OMG"))
return "OMGVMCID.value + " + code;
else
return "SUNVMCID.value + " + (code + getSunBaseNumber(groupName));
}
/**
* Returns the base number for Sun-specific exceptions.
*
* @return the base number.
*/
private int getSunBaseNumber(String groupName) {
return 200 * SUN_EXCEPTION_GROUPS.indexOf(groupName);
}
/**
* Writes out a resource file using the data from the given
* {@link Input} object. The result is written to {@code outDir}.
*
* @param outDir the directory to write the Java class to.
* @param input the parsed input data.
* @throws FileNotFoundException if the output file can't be written.
*/
private void writeResource(String outDir, Input input)
throws FileNotFoundException {
FileOutputStream file = new FileOutputStream(outDir + File.separator +
input.getClassName() + ".resource");
IndentingPrintWriter pw = new IndentingPrintWriter(file);
String groupName = input.getGroupName();
for (InputException e : input.getExceptions()) {
String exName = e.getName();
for (InputCode c : e.getCodes()) {
String ident = StringUtil.toMixedCase(c.getName());
pw.printMsg("@.@=\"@: (@) @\"", groupName, ident,
getMessageID(groupName, exName, c.getCode()), exName, c.getMessage());
}
pw.flush();
}
pw.close();
}
/**
* Returns the message ID corresponding to the given group name,
* exception name and error code.
*
* @param groupName the name of the group of exceptions.
* @param exception the name of the particular exception.
* @param code an error code from the given exception.
* @return the message ID.
*/
private String getMessageID(String groupName, String exceptionName, int code) {
if (groupName.equals("OMG"))
return getStandardMessageID(exceptionName, code);
else
return getSunMessageID(groupName, exceptionName, code);
}
/**
* Returns the standard (OMG) message ID corresponding to the given
* exception name and error code.
*
* @param exceptionName the name of the particular exception.
* @param code an error code from the given exception.
* @return the message ID.
*/
private String getStandardMessageID(String exceptionName, int code) {
return new Formatter().format("IOP%s0%04d", getExceptionID(exceptionName),
code).toString();
}
/**
* Returns the Sun message ID corresponding to the given group name,
* exception name and error code.
*
* @param groupName the name of the group of exceptions.
* @param exceptionName the name of the particular exception.
* @param code an error code from the given exception.
* @return the message ID.
*/
private String getSunMessageID(String groupName, String exceptionName, int code) {
return new Formatter().format("IOP%s1%04d", getExceptionID(exceptionName),
getSunBaseNumber(groupName) + code).toString();
}
/**
* Returns the exception ID corresponding to the given exception name.
*
* @param exceptionName the name of the particular exception.
* @return the message ID.
*/
private String getExceptionID(String exceptionName) {
return new Formatter().format("%03d", EXCEPTIONS.indexOf(exceptionName)).toString();
}
/**
* Entry point for running the generator from the command
* line. Users can specify either "make-class" or "make-resource"
* as the first argument to generate the specified type of file.
*
* @param args the command-line arguments.
* @throws FileNotFoundException if the input file can not be found.
* @throws IOException if an I/O error occurs.
*/
public static void main(String[] args)
throws FileNotFoundException, IOException
{
if (args.length < 3)
{
System.err.println("(make-class|make-resource) <input file> <output dir>");
System.exit(-1);
}
if (args[0].equals("make-class"))
new MC().makeClass(args[1], args[2]);
else if (args[0].equals("make-resource"))
new MC().makeResource(args[1], args[2]);
else
System.err.println("Invalid command: " + args[0]);
}
}