/*
 * Copyright (c) 1999, 2004, 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.
 */
/*
 * COMPONENT_NAME: idl.parser
 *
 * ORIGINS: 27
 *
 * Licensed Materials - Property of IBM
 * 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997, 1999
 * RMI-IIOP v1.0
 *
 */

package com.sun.tools.corba.se.idl;

// NOTES:
// -D56351<daz> Update computation of RepositoryIDs to CORBA 2.3 (see spec.).
// -D58319<daz> Display version info. for -version option.

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import com.sun.tools.corba.se.idl.constExpr.ExprFactory;
import com.sun.tools.corba.se.idl.constExpr.DefaultExprFactory;

/**
 * Compiler usage:
 * <br><br>
 *
 * java com.sun.tools.corba.se.idl.toJava.compile [options] <idl file>
 * <br><br>
 *
 * where &lt;idl file&gt; is the name of a file containing IDL definitions,
 * and [options] is any combination of the options listed below.  The options
 * and the idl file name can appear in any order.
 * <br><br>
 *
 * Options:
 * <dl>
 * <dt>-i &lt;include path&gt;
 * <dd>By default, the current directory is scanned for included files.
 *     This option adds another directory.  See also Note 1 below.
 *
 * <dt>-d &lt;symbol&gt;
 * <dd>This is equivalent to the following line in an IDL file: #define &lt;symbol&gt;
 *
 * <dt>-emitAll
 * <dd>Emit all types, including those found in #included files.
 *
 * <dt>-v
 * <dd>Verbose mode.
 * </dl>
 *
 * Note 1:  If you have an include path or paths that you will always be using,
 * it can get tedious putting these on the command with the -i option all the
 * time.  Instead, these can be placed into a config file called idl.config.
 * This file must be in the CLASSPATH.  The format of the includes line is:
 *
 * <pre>
 * includes=<path1>;<path2>;...;<pathN>
 * </pre>
 *
 * Note that the path separator character, here shown as a semicolon, is
 * machine dependent.  For instance, on Windows 95 this character is a
 * semicolon, on UNIX it is a colon.
 *
 * <p>
 * Note 2:  If you are directly invoking the main method on this class (not
 * a subclass), then it will only check that the IDL file is syntactically
 * correct.  It does not generate any files.  Only extensions to this
 * framework generate files, therefore an extension must be invoked if you
 * want files to be generated.
 * <br><br>
 *
 * To Extend the compiler:
 * <br><br>
 *
 * You only need to extend the compiler if you want it to generate something
 * other than what it currently generates.
 * <br><br>
 *
 * Step 1 - Implement the generator interfaces:
 * <br><br>
 *
 * Each generator interface defines one method: generate (Hashtable, XXXEntry, PrintWriter);
 * <br>
 * - The Hashtable is the symbol table; each element is a SymtabEntry (or a
 *   subclass of SymtabEntry) and is keyed by its fully qualified name;
 *   <br>
 * - XXXEntry is the appropriate entry for the type to be generated.  For
 *   example: AttributeGen defines generate (Hashtable, AttributeEntry, PrintWriter);
 *   ConstGen defines generate (Hashtable, ConstEntry, PrintWriter); etc.
 *   <br>
 * - The PrintWriter is a stream to the file being generated.  For the
 *   generators called by the compiler framework, this will be null.  The
 *   generator is responsible for creating and opening files.  But for
 *   generators that are called by other generators - for instance,
 *   MethodGen.generate will most likely be called by InterfaceGen.generate -
 *   this parameter is provided so that the proper file can be written to.
 *   <br><br>
 *
 * Step 2 - Implement the GenFactory interface:
 * <br><br>
 *
 * All of the generators implemented in Step 1 must be created somehow.  There
 * is an interface for a factory, GenFactory, which must be implemented.  The
 * name of this factory must be set in the extension to the Compile class (see
 * Step 3, below).
 * <br><br>
 *
 * Step 3 - Extend com.sun.tools.corba.se.idl.Factories:
 * <br><br>
 *
 * Extend com.sun.tools.corba.se.idl.Factories and override the method genFactory.  This
 * method must return an instance of the factory which you implemented in
 * step 2.  Your extension of this class may also do more, this is only the
 * minimum.  See com.sun.tools.corba.se.idl.Factories for more information.
 * <br><br>
 *
 * Step 4 - Extend com.sun.tools.corba.se.idl.Compile:
 * <br><br>
 *
 * Your extension of com.sun.tools.corba.se.idl.Compile should contain a minimum of
 * two methods:
 * <dl>
 * <dt>protected com.sun.tools.corba.se.idl.Factories ()
 * <dd>This method overrides com.sun.tools.corba.se.idl.Compile.factories and returns your
 *     extension from Step 3.
 *
 * <dt>public static void main (String[] args)
 * <dd>This main method must instantiate this class and call its start method.
 * </dl>
 *
 * Given that the extension of Factories is MyFactories, the extension of
 * Compile could be:
 *
 * <pre>
 * public class MyCompile extends com.sun.tools.corba.se.idl.Compile
 * {
 *   protected com.sun.tools.corba.se.idl.Factories factories ()
 *   {
 *     return new MyFactories ();
 *   }
 *   public static void main (String[] args)
 *   {
 *     MyCompile compile = new MyCompile ();
 *     compile.start (args);
 *   }
 * }
 * </pre>
 *
 * If you would like a bit more control over the processing of the framework,
 * you can replace compile.start with what it calls.  But then you also have
 * to handle the exceptions which start handles for you:
 * <pre>
 * public class MyCompile extends com.sun.tools.corba.se.idl.Compile
 * {
 *   ...
 *
 *   public static void main (String[] args)
 *   {
 *     MyCompile compile = new MyCompile ();
 *     try
 *     {
 *       compile.init (args);
 *       java.util.Enumeration emitList = compile.parse ();
 *       compile.generate ();
 *     }
 *     catch (com.sun.tools.corba.se.idl.InvalidArgument e)
 *     {
 *       System.err.println (e);
 *     }
 *     catch (java.io.IOException e)
 *     {
 *       System.err.println (e);
 *     }
 *   }
 * }
 * </pre>
 *
 * Note that compile.parse returns an enumeration.  This enumerates the
 * SymtabEntry's which should be generated.  If the parse method detects
 * errors, it returns null.  Note that you do not have to check that
 * `emitList' is valid before calling generate (that's done internally), but
 * if you do any processing between parse and generate, emitList should be
 * checked before executing that code.
 * <br><br>
 **/
public class Compile
{
  public Compile ()
  {
    noPragma.init (preprocessor);
    preprocessor.registerPragma (noPragma);

    // <d41197> Reset static variables to allow parsing multiple sources.
    // DO NOT reset SymtabEntry.maxKey because it crashes IDLC.
    ParseException.detected  = false;
    SymtabEntry.includeStack = new java.util.Stack ();
    SymtabEntry.setEmit      = true;
    //SymtabEntry.maxKey     = -1;
    Parser.repIDStack        = new java.util.Stack (); // <d56351>
  } // ctor

  public static void main (String[] args)
  {
    (new Compile ()).start (args);
  } // main

  protected Factories factories ()
  {
    return new Factories ();
  } // genFactoryName

  protected void registerPragma (PragmaHandler handler)
  {
    handler.init (preprocessor);
    preprocessor.registerPragma (handler);
  } // registerPragma

  /**
   * Initialize the framework.
   **/
  protected void init (String[] args) throws InvalidArgument
  {
    initFactories ();
    arguments.parseArgs (args);
    initGenerators ();
    parser = new Parser (preprocessor, arguments, overrideNames, symbolTable, symtabFactory, exprFactory, keywords);
    preprocessor.init (parser);
    parser.includes = includes;
    parser.includeEntries = includeEntries;
  } // init

  /**
   * Parse the IDL file and return an enumeration of the symbols to be
   * generated.  All elements of the Enumeration will be extensions of
   * SymtabEntry.  If any errors were encountered during parsing, null
   * will be returned.
   **/
  protected Enumeration parse () throws IOException
  {
    if (arguments.verbose)
      System.out.println (Util.getMessage ("Compile.parsing", arguments.file));
    parser.parse (arguments.file);
    if ( !ParseException.detected )
    {
      parser.forwardEntryCheck();

      // <46082.03> Revert to "IDL:"-style (i.e., regular) repository ID.
      //parser.updateRepositoryIds();
    }
    if (arguments.verbose)
      System.out.println (Util.getMessage ("Compile.parseDone", arguments.file));
    if (ParseException.detected)
    {
      symbolTable = null;
      emitList    = null;
    }
    else
    {
      symbolTable = parser.symbolTable;
      emitList    = parser.emitList.elements ();
    }
    return emitList;
  } // parse

  /**
   * Invoke the generators.
   **/
  protected void generate () throws IOException
  {
    /*
    // print the symbol table
    Enumeration v = parser.symbolTable.elements ();
    Enumeration k = parser.symbolTable.keys ();
    while (k.hasMoreElements ())
      System.out.println (k.nextElement () + ":  " + v.nextElement ());
    */
    if (ParseException.detected)
      emitList = null;
    else
      emitList = parser.emitList.elements ();
    if (emitList != null)
    {
      // Emit the output files for all of the types in the IDL file
      if (arguments.verbose)
        System.out.println ();
      while (emitList.hasMoreElements ())
      {
        SymtabEntry entry = (SymtabEntry)emitList.nextElement ();
        if (arguments.verbose)
          if (entry.generator () instanceof Noop)
            ; // Nothing will be generated, so don't say so.
          else if (entry.module () . equals (""))
            System.out.println (Util.getMessage ("Compile.generating", entry.name ()));
          else
            System.out.println (Util.getMessage ("Compile.generating", entry.module () + '/' + entry.name ()));
        entry.generate (symbolTable, null);
        if (arguments.verbose)
          if (entry.generator () instanceof Noop)
            ; // Nothing will be generated, so don't say so.
          else if (entry.module () . equals (""))
            System.out.println (Util.getMessage ("Compile.genDone", entry.name ()));
          else
            System.out.println (Util.getMessage ("Compile.genDone", entry.module () + '/' + entry.name ()));
      }
    }
  } // generate

  /**
   * Start the parse/code generation process.  This method calls init,
   * parse, generate.  If more control is desired, rather than call start,
   * those three methods could be called explicitly.
   **/
  public void start (String[] args)
  {
    try
    {
      init (args);
      if (arguments.versionRequest) // <d59319>
        displayVersion ();
      else
      {
        parse ();
        generate ();
      }
    }
    catch (InvalidArgument e)
    {
      System.err.println (e);
    }
    catch (IOException e)
    {
      System.err.println (e);
    }
  } // start

  private void initFactories ()
  {
    // Get the factories.
    Factories factories = factories ();
    if (factories == null) factories = new Factories ();

    // Get the argument processor from the factories.
    Arguments tmpArgs = factories.arguments ();
    if (tmpArgs == null)
      arguments = new Arguments ();
    else
      arguments = tmpArgs;

    // Get the symbol table entry factory from the factories.
    SymtabFactory tmpSTF = factories.symtabFactory ();
    if (tmpSTF == null)
      symtabFactory = new DefaultSymtabFactory ();
    else
      symtabFactory = tmpSTF;

    // Get the expression factory from the factories.
    ExprFactory tmpExpF = factories.exprFactory ();
    if (tmpExpF == null)
      exprFactory = new DefaultExprFactory ();
    else
      exprFactory = tmpExpF;

    // Get the generator factory from the factories.
    GenFactory tmpGenF = factories.genFactory ();
    if (tmpGenF == null)
      genFactory = noop;
    else
      genFactory = tmpGenF;

    // Get the language keywords.
    keywords = factories.languageKeywords ();
    if (keywords == null)
      keywords = new String[0];
  } // initFactories

  private void initGenerators ()
  {
    AttributeGen agen = genFactory.createAttributeGen ();
    AttributeEntry.attributeGen = agen == null ? noop : agen;

    ConstGen cgen = genFactory.createConstGen ();
    ConstEntry.constGen = cgen == null ? noop : cgen;

    EnumGen egen = genFactory.createEnumGen ();
    EnumEntry.enumGen = egen == null ? noop : egen;

    ExceptionGen exgen = genFactory.createExceptionGen ();
    ExceptionEntry.exceptionGen = exgen == null ? noop : exgen;

    ForwardGen fgen = genFactory.createForwardGen ();
    ForwardEntry.forwardGen = fgen == null ? noop : fgen;

    ForwardValueGen fvgen = genFactory.createForwardValueGen ();
    ForwardValueEntry.forwardValueGen = fvgen == null ? noop : fvgen;

    IncludeGen ingen = genFactory.createIncludeGen ();
    IncludeEntry.includeGen = ingen == null ? noop : ingen;

    InterfaceGen igen = genFactory.createInterfaceGen ();
    InterfaceEntry.interfaceGen = igen == null ? noop : igen;

    ValueGen vgen = genFactory.createValueGen ();
    ValueEntry.valueGen = vgen == null ? noop : vgen;

    ValueBoxGen vbgen = genFactory.createValueBoxGen ();
    ValueBoxEntry.valueBoxGen = vbgen == null ? noop : vbgen;

    MethodGen mgen = genFactory.createMethodGen ();
    MethodEntry.methodGen = mgen == null ? noop : mgen;

    ModuleGen modgen = genFactory.createModuleGen ();
    ModuleEntry.moduleGen = modgen == null ? noop : modgen;

    NativeGen ngen = genFactory.createNativeGen ();
    NativeEntry.nativeGen = ngen == null ? noop : ngen;

    ParameterGen pgen = genFactory.createParameterGen ();
    ParameterEntry.parameterGen = pgen == null ? noop : pgen;

    PragmaGen prgen = genFactory.createPragmaGen ();
    PragmaEntry.pragmaGen = prgen == null ? noop : prgen;

    PrimitiveGen primgen = genFactory.createPrimitiveGen ();
    PrimitiveEntry.primitiveGen = primgen == null ? noop : primgen;

    SequenceGen seqgen = genFactory.createSequenceGen ();
    SequenceEntry.sequenceGen = seqgen == null ? noop : seqgen;

    StringGen strgen = genFactory.createStringGen ();
    StringEntry.stringGen = strgen == null ? noop : strgen;

    StructGen sgen = genFactory.createStructGen ();
    StructEntry.structGen = sgen == null ? noop : sgen;

    TypedefGen tgen = genFactory.createTypedefGen ();
    TypedefEntry.typedefGen = tgen == null ? noop : tgen;

    UnionGen ugen = genFactory.createUnionGen ();
    UnionEntry.unionGen = ugen == null ? noop : ugen;
  } // initGenerators

  /**
   * Write the version number of this compiler to standard out.
   **/
  protected void displayVersion ()
  {
    String message = Util.getMessage ("Version.product", Util.getMessage ("Version.number"));
    System.out.println (message);
  }

  /**
   * This is the repository of emitter arguments.
   **/
  public Arguments arguments           = null;
  /**
   * This hashtable contains <real name, alias> pairs.  It is filled in by
   * extenders in cases where they wish to override an IDL type name with
   * some other name.  For instance, when mapping to Java, there could be
   * an overrideNames entry of <"TRUE", "true">.  NOTE:  Do NOT change this
   * variable to a new Hash table.  Just add elements to it.
   **/
  protected Hashtable overrideNames    = new Hashtable ();
  /**
   * This is the symbol table.  It will be empty until the parse method
   * executes.  If errors are encountered, the state of the symbol table
   * is undefined.
   **/
  protected Hashtable symbolTable      = new Hashtable ();
  /**
   * This is a vector of strings of the form "IDLfile" or <IDLfile>.  It is
   * a list of the files included in the given IDL file.  It will be empty
   * until the parse method executes.  If errors are encountered, the state
   * of this vector is undefined.
   **/
  protected Vector includes            = new Vector ();
  /**
   * This is a vector of IncludeEntry's.  It is a list of the files included
   * in the given IDL file.  It mirrors the includes vector.  It will be empty
   * until the parse method executes.  If errors are encountered, the state of
   * this vector is undefined.
   **/
  protected Vector includeEntries      = new Vector ();
  static  Noop          noop           = new Noop ();
  private GenFactory    genFactory     = null;
  private SymtabFactory symtabFactory  = null;
  private ExprFactory   exprFactory    = null;
  private Parser        parser         = null;
          Preprocessor  preprocessor   = new Preprocessor ();
  private NoPragma      noPragma       = new NoPragma ();
  private Enumeration   emitList       = null;
  private String[]      keywords       = null;
} // class Compile
