1 /*
   2  * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.tools.corba.se.logutil;
  26 
  27 import java.io.File;
  28 import java.io.FileNotFoundException;
  29 import java.io.FileOutputStream;
  30 import java.io.IOException;
  31 
  32 import java.util.Arrays;
  33 import java.util.Date;
  34 import java.util.Formatter;
  35 import java.util.List;
  36 import java.util.Queue;
  37 
  38 public class MC {
  39 
  40   private static final String VERSION = "1.0";
  41 
  42   private static final List<String> SUN_EXCEPTION_GROUPS = Arrays.asList(new String[]
  43     { "SUNBASE", "ORBUTIL", "ACTIVATION", "NAMING", "INTERCEPTORS", "POA", "IOR", "UTIL" });
  44 
  45   private static final List<String> EXCEPTIONS = Arrays.asList(new String[]
  46     { "UNKNOWN", "BAD_PARAM", "NO_MEMORY", "IMP_LIMIT", "COMM_FAILURE", "INV_OBJREF", "NO_PERMISSION",
  47       "INTERNAL", "MARSHAL", "INITIALIZE", "NO_IMPLEMENT", "BAD_TYPECODE", "BAD_OPERATION", "NO_RESOURCES",
  48       "NO_RESPONSE", "PERSIST_STORE", "BAD_INV_ORDER", "TRANSIENT", "FREE_MEM", "INV_IDENT", "INV_FLAG",
  49       "INTF_REPOS", "BAD_CONTEXT", "OBJ_ADAPTER", "DATA_CONVERSION", "OBJECT_NOT_EXIST", "TRANSACTION_REQUIRED",
  50       "TRANSACTION_ROLLEDBACK", "INVALID_TRANSACTION", "INV_POLICY", "CODESET_INCOMPATIBLE", "REBIND",
  51       "TIMEOUT", "TRANSACTION_UNAVAILABLE", "BAD_QOS", "INVALID_ACTIVITY", "ACTIVITY_COMPLETED",
  52       "ACTIVITY_REQUIRED" });
  53 
  54   /**
  55    * Read the minor codes from the input file and
  56    * write out a resource file.
  57    *
  58    * @param inFile the file to read the codes from.
  59    * @param outDir the directory to write the resource file to.
  60    * @throws FileNotFoundException if the input file can not be found.
  61    * @throws IOException if an I/O error occurs.
  62    */
  63   private void makeResource(String inFile, String outDir)
  64   throws FileNotFoundException, IOException {
  65     writeResource(outDir, new Input(inFile));
  66   }
  67 
  68   /**
  69    * Create a new Java source file using the specified Scheme input file,
  70    * and writing the result to the given output directory.
  71    *
  72    * @param inFile the file to read the data from.
  73    * @param outDir the directory to write the Java class to.
  74    * @throws FileNotFoundException if the input file can not be found.
  75    * @throws IOException if an I/O error occurs.
  76    */
  77   private void makeClass(String inFile, String outDir)
  78   throws FileNotFoundException, IOException {
  79     writeClass(inFile, outDir, new Input(inFile));
  80   }
  81 
  82   /**
  83    * Writes out a Java source file using the data from the given
  84    * {@link Input} object.  The result is written to {@code outDir}.
  85    * The name of the input file is just used in the header of the
  86    * resulting source file.
  87    *
  88    * @param inFile the name of the file the data was read from.
  89    * @param outDir the directory to write the Java class to.
  90    * @param input the parsed input data.
  91    * @throws FileNotFoundException if the output file can't be written.
  92    */
  93   private void writeClass(String inFile, String outDir, Input input)
  94     throws FileNotFoundException {
  95     String packageName = input.getPackageName();
  96     String className = input.getClassName();
  97     String groupName = input.getGroupName();
  98     Queue<InputException> exceptions = input.getExceptions();
  99     FileOutputStream file = new FileOutputStream(outDir + File.separator + className + ".java");
 100     IndentingPrintWriter pw = new IndentingPrintWriter(file);
 101 
 102     writeClassHeader(inFile, groupName, pw);
 103     pw.printMsg("package @ ;", packageName);
 104     pw.println();
 105     pw.println("import java.util.logging.Logger ;");
 106     pw.println("import java.util.logging.Level ;");
 107     pw.println();
 108     pw.println("import org.omg.CORBA.OMGVMCID ;");
 109     pw.println( "import com.sun.corba.se.impl.util.SUNVMCID ;");
 110     pw.println( "import org.omg.CORBA.CompletionStatus ;");
 111     pw.println( "import org.omg.CORBA.SystemException ;");
 112     pw.println();
 113     pw.println( "import com.sun.corba.se.spi.orb.ORB ;");
 114     pw.println();
 115     pw.println( "import com.sun.corba.se.spi.logging.LogWrapperFactory;");
 116     pw.println();
 117     pw.println( "import com.sun.corba.se.spi.logging.LogWrapperBase;");
 118     pw.println();
 119     writeImports(exceptions, pw);
 120     pw.println();
 121     pw.indent();
 122     pw.printMsg("public class @ extends LogWrapperBase {", className);
 123     pw.println();
 124     pw.printMsg("public @( Logger logger )", className);
 125     pw.indent();
 126     pw.println( "{");
 127     pw.undent();
 128     pw.println( "super( logger ) ;");
 129     pw.println( "}");
 130     pw.println();
 131     pw.flush();
 132     writeFactoryMethod(className, groupName, pw);
 133     writeExceptions(groupName, exceptions, className, pw);
 134     pw.undent();
 135     pw.println( );
 136     pw.println( "}");
 137     pw.flush();
 138     pw.close();
 139   }
 140 
 141   /**
 142    * Writes out the header of a Java source file.
 143    *
 144    * @param inFile the input file the file was generated from.
 145    * @param groupName the group of exceptions the Java source file is for.
 146    * @param pw the print writer used to write the output.
 147    */
 148   private void writeClassHeader(String inFile, String groupName,
 149                                 IndentingPrintWriter pw) {
 150     if (groupName.equals("OMG"))
 151       pw.println("// Log wrapper class for standard exceptions");
 152     else
 153       pw.printMsg("// Log wrapper class for Sun private system exceptions in group @",
 154                   groupName);
 155     pw.println("//");
 156     pw.printMsg("// Generated by MC.java version @, DO NOT EDIT BY HAND!", VERSION);
 157     pw.printMsg("// Generated from input file @ on @", inFile, new Date());
 158     pw.println();
 159   }
 160 
 161   /**
 162    * Write out the import list for the exceptions.
 163    *
 164    * @param groups the exceptions that were parsed.
 165    * @param pw the {@link IndentingPrintWriter} for writing to the file.
 166    */
 167   private void writeImports(Queue<InputException> exceptions,
 168                             IndentingPrintWriter pw) {
 169     if (exceptions == null)
 170       return;
 171     for (InputException e : exceptions)
 172       pw.println("import org.omg.CORBA." + e.getName() + " ;");
 173   }
 174 
 175   /**
 176    * Write out the factory method for this group of exceptions.
 177    *
 178    * @param className the name of the generated class.
 179    * @param groupName the name of this group of exceptions.
 180    * @param pw the {@link IndentingPrintWriter} for writing to the file.
 181    */
 182   private void writeFactoryMethod(String className, String groupName,
 183                                   IndentingPrintWriter pw) {
 184     pw.indent();
 185     pw.println( "private static LogWrapperFactory factory = new LogWrapperFactory() {");
 186     pw.println( "public LogWrapperBase create( Logger logger )" );
 187     pw.indent();
 188     pw.println( "{");
 189     pw.undent();
 190     pw.printMsg("return new @( logger ) ;", className);
 191     pw.undent();
 192     pw.println( "}" );
 193     pw.println( "} ;" );
 194     pw.println();
 195     pw.printMsg("public static @ get( ORB orb, String logDomain )", className);
 196     pw.indent();
 197     pw.println( "{");
 198     pw.indent();
 199     pw.printMsg( "@ wrapper = ", className);
 200     pw.indent();
 201     pw.printMsg( "(@) orb.getLogWrapper( logDomain, ", className);
 202     pw.undent();
 203     pw.undent();
 204     pw.printMsg( "\"@\", factory ) ;", groupName);
 205     pw.undent();
 206     pw.println( "return wrapper ;" );
 207     pw.println( "} " );
 208     pw.println();
 209     pw.printMsg( "public static @ get( String logDomain )", className);
 210     pw.indent();
 211     pw.println( "{");
 212     pw.indent();
 213     pw.printMsg( "@ wrapper = ", className);
 214     pw.indent();
 215     pw.printMsg( "(@) ORB.staticGetLogWrapper( logDomain, ", className);
 216     pw.undent();
 217     pw.undent();
 218     pw.printMsg( "\"@\", factory ) ;", groupName);
 219     pw.undent();
 220     pw.println( "return wrapper ;" );
 221     pw.println( "} " );
 222     pw.println();
 223   }
 224 
 225   /**
 226    * Writes out the exceptions themselves.
 227    *
 228    * @param groupName the name of this group of exceptions.
 229    * @param exceptions the exceptions to write out.
 230    * @param className the name of the generated class.
 231    * @param pw the {@link IndentingPrintWriter} for writing to the file.
 232    */
 233   private void writeExceptions(String groupName, Queue<InputException> exceptions,
 234                                String className, IndentingPrintWriter pw) {
 235     for (InputException e : exceptions) {
 236       pw.println("///////////////////////////////////////////////////////////");
 237       pw.printMsg("// @", e.getName());
 238       pw.println("///////////////////////////////////////////////////////////");
 239       pw.println();
 240       for (InputCode c : e.getCodes())
 241         writeMethods(groupName, e.getName(), c.getName(), c.getCode(),
 242                      c.getLogLevel(), className, StringUtil.countArgs(c.getMessage()), pw);
 243       pw.flush();
 244     }
 245   }
 246 
 247   /**
 248    * Writes out the methods for a particular error.
 249    *
 250    * @param groupName the name of this group of exceptions.
 251    * @param exceptionName the name of this particular exception.
 252    * @param errorName the name of this particular error.
 253    * @param code the minor code for this particular error.
 254    * @param ident the name of the error in mixed-case identifier form.
 255    * @param level the level at which to place log messages.
 256    * @param className the name of the class for this group of exceptions.
 257    * @param numParams the number of parameters the detail message takes.
 258    * @param pw the print writer for writing to the file.
 259    */
 260   private void writeMethods(String groupName, String exceptionName, String errorName,
 261                             int code, String level, String className, int numParams,
 262                             IndentingPrintWriter pw) {
 263     String ident = StringUtil.toMixedCase(errorName);
 264     pw.printMsg("public static final int @ = @ ;", errorName, getBase(groupName, code));
 265     pw.println();
 266     pw.flush();
 267     writeMethodStatusCause(groupName, exceptionName, errorName, ident, level,
 268                            numParams, className, pw);
 269     pw.println();
 270     pw.flush();
 271     writeMethodStatus(exceptionName, ident, numParams, pw);
 272     pw.println();
 273     pw.flush();
 274     writeMethodCause(exceptionName, ident, numParams, pw);
 275     pw.println();
 276     pw.flush();
 277     writeMethodNoArgs(exceptionName, ident, numParams, pw);
 278     pw.println();
 279     pw.flush();
 280   }
 281 
 282   /**
 283    * Writes out a method for an error that takes a
 284    * {@link org.omg.CORBA.CompletionStatus} and a cause.
 285    *
 286    * @param groupName the name of this group of exceptions.
 287    * @param exceptionName the name of this particular exception.
 288    * @param errorName the name of this particular error.
 289    * @param ident the name of the error in mixed-case identifier form.
 290    * @param logLevel the level at which to place log messages.
 291    * @param numParams the number of parameters the detail message takes.
 292    * @param className the name of the class for this group of exceptions.
 293    * @param pw the print writer for writing to the file.
 294    */
 295   private void writeMethodStatusCause(String groupName, String exceptionName,
 296                                       String errorName, String ident,
 297                                       String logLevel, int numParams,
 298                                       String className, IndentingPrintWriter pw) {
 299     pw.indent();
 300     pw.printMsg( "public @ @( CompletionStatus cs, Throwable t@) {", exceptionName,
 301                  ident, makeDeclArgs(true, numParams));
 302     pw.printMsg( "@ exc = new @( @, cs ) ;", exceptionName, exceptionName, errorName);
 303     pw.indent();
 304     pw.println( "if (t != null)" );
 305     pw.undent();
 306     pw.println( "exc.initCause( t ) ;" );
 307     pw.println();
 308     pw.indent();
 309     pw.printMsg( "if (logger.isLoggable( Level.@ )) {", logLevel);
 310     if (numParams > 0) {
 311       pw.printMsg( "Object[] parameters = new Object[@] ;", numParams);
 312       for (int a = 0; a < numParams; ++a)
 313         pw.printMsg("parameters[@] = arg@ ;", a, a);
 314     } else
 315       pw.println( "Object[] parameters = null ;");
 316     pw.indent();
 317     pw.printMsg( "doLog( Level.@, \"@.@\",", logLevel, groupName, ident);
 318     pw.undent();
 319     pw.undent();
 320     pw.printMsg( "parameters, @.class, exc ) ;", className);
 321     pw.println( "}");
 322     pw.println();
 323 
 324     pw.undent();
 325     pw.println( "return exc ;");
 326     pw.println( "}");
 327   }
 328 
 329   /**
 330    * Writes out a method for an error that takes a
 331    * {@link org.omg.CORBA.CompletionStatus}.
 332    *
 333    * @param exceptionName the name of this particular exception.
 334    * @param ident the name of the error in mixed-case identifier form.
 335    * @param numParams the number of parameters the detail message takes.
 336    * @param pw the print writer for writing to the file.
 337    */
 338   private void writeMethodStatus(String exceptionName, String ident,
 339                                  int numParams, IndentingPrintWriter pw) {
 340     pw.indent();
 341     pw.printMsg("public @ @( CompletionStatus cs@) {", exceptionName,
 342                 ident, makeDeclArgs(true, numParams));
 343     pw.undent();
 344     pw.printMsg("return @( cs, null@ ) ;", ident, makeCallArgs(true, numParams));
 345     pw.println("}");
 346   }
 347 
 348   /**
 349    * Writes out a method for an error that takes a cause.
 350    *
 351    * @param exceptionName the name of this particular exception.
 352    * @param ident the name of the error in mixed-case identifier form.
 353    * @param numParams the number of parameters the detail message takes.
 354    * @param pw the print writer for writing to the file.
 355    */
 356   private void writeMethodCause(String exceptionName, String ident,
 357                                 int numParams, IndentingPrintWriter pw) {
 358     pw.indent();
 359     pw.printMsg("public @ @( Throwable t@) {", exceptionName, ident,
 360                 makeDeclArgs(true, numParams));
 361     pw.undent();
 362     pw.printMsg("return @( CompletionStatus.COMPLETED_NO, t@ ) ;", ident,
 363                 makeCallArgs(true, numParams));
 364     pw.println("}");
 365   }
 366 
 367   /**
 368    * Writes out a method for an error that takes no arguments.
 369    *
 370    * @param exceptionName the name of this particular exception.
 371    * @param ident the name of the error in mixed-case identifier form.
 372    * @param numParams the number of parameters the detail message takes.
 373    * @param pw the print writer for writing to the file.
 374    */
 375   private void writeMethodNoArgs(String exceptionName, String ident,
 376                                  int numParams, IndentingPrintWriter pw) {
 377 
 378     pw.indent();
 379     pw.printMsg("public @ @( @) {", exceptionName, ident,
 380                 makeDeclArgs(false, numParams));
 381     pw.undent();
 382     pw.printMsg("return @( CompletionStatus.COMPLETED_NO, null@ ) ;",
 383                 ident, makeCallArgs(true, numParams));
 384     pw.println("}");
 385   }
 386 
 387   /**
 388    * Returns a list of comma-separated arguments with type declarations.
 389    *
 390    * @param leadingComma true if the list should start with a comma.
 391    * @param numArgs the number of arguments to generate.
 392    * @return the generated string.
 393    */
 394   private String makeDeclArgs(boolean leadingComma, int numArgs) {
 395     return makeArgString("Object arg", leadingComma, numArgs);
 396   }
 397 
 398   /**
 399    * Returns a list of comma-separated arguments without type declarations.
 400    *
 401    * @param leadingComma true if the list should start with a comma.
 402    * @param numArgs the number of arguments to generate.
 403    * @return the generated string.
 404    */
 405   private String makeCallArgs(boolean leadingComma, int numArgs) {
 406     return makeArgString("arg", leadingComma, numArgs);
 407   }
 408 
 409   /**
 410    * Returns a list of comma-separated arguments.
 411    *
 412    * @param prefixString the string with which to prefix each argument.
 413    * @param leadingComma true if the list should start with a comma.
 414    * @param numArgs the number of arguments to generate.
 415    * @return the generated string.
 416    */
 417   private String makeArgString(String prefixString, boolean leadingComma,
 418                                int numArgs) {
 419     if (numArgs == 0)
 420       return " ";
 421     if (numArgs == 1) {
 422       if (leadingComma)
 423         return ", " + prefixString + (numArgs - 1);
 424       else
 425         return " " + prefixString + (numArgs - 1);
 426     }
 427     return makeArgString(prefixString, leadingComma, numArgs - 1) +
 428       ", " + prefixString + (numArgs - 1);
 429   }
 430 
 431   /**
 432    * Returns the {@link String} containing the calculation of the
 433    * error code.
 434    *
 435    * @param groupName the group of exception to which the code belongs.
 436    * @param code the minor code number representing the exception within the group.
 437    * @return the unique error code.
 438    */
 439   private String getBase(String groupName, int code) {
 440     if (groupName.equals("OMG"))
 441       return "OMGVMCID.value + " + code;
 442     else
 443       return "SUNVMCID.value + " + (code + getSunBaseNumber(groupName));
 444   }
 445 
 446   /**
 447    * Returns the base number for Sun-specific exceptions.
 448    *
 449    * @return the base number.
 450    */
 451   private int getSunBaseNumber(String groupName) {
 452     return 200 * SUN_EXCEPTION_GROUPS.indexOf(groupName);
 453   }
 454 
 455   /**
 456    * Writes out a resource file using the data from the given
 457    * {@link Input} object.  The result is written to {@code outDir}.
 458    *
 459    * @param outDir the directory to write the Java class to.
 460    * @param input the parsed input data.
 461    * @throws FileNotFoundException if the output file can't be written.
 462    */
 463   private void writeResource(String outDir, Input input)
 464     throws FileNotFoundException {
 465     FileOutputStream file = new FileOutputStream(outDir + File.separator +
 466                                                  input.getClassName() + ".resource");
 467     IndentingPrintWriter pw = new IndentingPrintWriter(file);
 468     String groupName = input.getGroupName();
 469     for (InputException e : input.getExceptions()) {
 470       String exName = e.getName();
 471       for (InputCode c : e.getCodes()) {
 472         String ident = StringUtil.toMixedCase(c.getName());
 473         pw.printMsg("@.@=\"@: (@) @\"", groupName, ident,
 474                     getMessageID(groupName, exName, c.getCode()), exName, c.getMessage());
 475       }
 476       pw.flush();
 477     }
 478     pw.close();
 479   }
 480 
 481   /**
 482    * Returns the message ID corresponding to the given group name,
 483    * exception name and error code.
 484    *
 485    * @param groupName the name of the group of exceptions.
 486    * @param exception the name of the particular exception.
 487    * @param code an error code from the given exception.
 488    * @return the message ID.
 489    */
 490   private String getMessageID(String groupName, String exceptionName, int code) {
 491     if (groupName.equals("OMG"))
 492       return getStandardMessageID(exceptionName, code);
 493     else
 494       return getSunMessageID(groupName, exceptionName, code);
 495   }
 496 
 497   /**
 498    * Returns the standard (OMG) message ID corresponding to the given
 499    * exception name and error code.
 500    *
 501    * @param exceptionName the name of the particular exception.
 502    * @param code an error code from the given exception.
 503    * @return the message ID.
 504    */
 505   private String getStandardMessageID(String exceptionName, int code) {
 506     return new Formatter().format("IOP%s0%04d", getExceptionID(exceptionName),
 507                                   code).toString();
 508   }
 509 
 510   /**
 511    * Returns the Sun message ID corresponding to the given group name,
 512    * exception name and error code.
 513    *
 514    * @param groupName the name of the group of exceptions.
 515    * @param exceptionName the name of the particular exception.
 516    * @param code an error code from the given exception.
 517    * @return the message ID.
 518    */
 519   private String getSunMessageID(String groupName, String exceptionName, int code) {
 520     return new Formatter().format("IOP%s1%04d", getExceptionID(exceptionName),
 521                                   getSunBaseNumber(groupName) + code).toString();
 522   }
 523 
 524   /**
 525    * Returns the exception ID corresponding to the given exception name.
 526    *
 527    * @param exceptionName the name of the particular exception.
 528    * @return the message ID.
 529    */
 530   private String getExceptionID(String exceptionName) {
 531     return new Formatter().format("%03d", EXCEPTIONS.indexOf(exceptionName)).toString();
 532   }
 533 
 534   /**
 535    * Entry point for running the generator from the command
 536    * line.  Users can specify either "make-class" or "make-resource"
 537    * as the first argument to generate the specified type of file.
 538    *
 539    * @param args the command-line arguments.
 540    * @throws FileNotFoundException if the input file can not be found.
 541    * @throws IOException if an I/O error occurs.
 542    */
 543   public static void main(String[] args)
 544     throws FileNotFoundException, IOException
 545   {
 546     if (args.length < 3)
 547       {
 548         System.err.println("(make-class|make-resource) <input file> <output dir>");
 549         System.exit(-1);
 550       }
 551     if (args[0].equals("make-class"))
 552       new MC().makeClass(args[1], args[2]);
 553     else if (args[0].equals("make-resource"))
 554       new MC().makeResource(args[1], args[2]);
 555     else
 556       System.err.println("Invalid command: " + args[0]);
 557   }
 558 
 559 }