/*
 * 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.toJava
 *
 * 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.toJavaPortable;

// NOTES:
// -F46082.51<daz> Remove -stateful feature.
// -D57118   <klr> Fix "narrow" in helper for abstract interface
// -D58889   <klr> re-Fix "narrow" in helper for abstract interface
// -D59383   <klr> 'get_class' in value helper returns value class, not helper.
// -D59413   <klr> Remove Helper interface references for non-value types.
// -D59435   <klr> Remove read_Object, write_Object completely.
// -D59418   <klr> Move read_Value, write_Value to generator's helperRead.

import java.io.PrintWriter;

import java.util.Enumeration;
import java.util.Vector;

import com.sun.tools.corba.se.idl.GenFileStream;
import com.sun.tools.corba.se.idl.InterfaceEntry;
import com.sun.tools.corba.se.idl.MethodEntry;
import com.sun.tools.corba.se.idl.ParameterEntry;
import com.sun.tools.corba.se.idl.SymtabEntry;
import com.sun.tools.corba.se.idl.ValueEntry;
import com.sun.tools.corba.se.idl.ValueBoxEntry;
import com.sun.tools.corba.se.idl.TypedefEntry;
import com.sun.tools.corba.se.idl.InterfaceState;
import com.sun.tools.corba.se.idl.PrimitiveEntry;
import com.sun.tools.corba.se.idl.StructEntry;

/**
 *
 **/
public class Helper implements AuxGen
{
  /**
   * Public zero-argument constructor.
   **/
  public Helper ()
  {
  } // ctor

  /**
   * Generate the helper class.  Provides general algorithm
   * for auxiliary binding generation:
   *
   * 1.) Initialize symbol table and symbol table entry members,
   *     common to all generators.
   * 2.) Initialize members unique to this generator.
   * 3.) Open print stream
   * 4.) Write class heading: package, prologue, class statement, open curly
   * 5.) Write class body: member data and methods
   * 6.) Write class closing: close curly
   * 7.) Close the print stream
   **/
  public void generate (java.util.Hashtable symbolTable, com.sun.tools.corba.se.idl.SymtabEntry entry)
  {
    this.symbolTable = symbolTable;
    this.entry       = entry;
    init ();

    openStream ();
    if (stream == null)
      return;
    writeHeading ();
    writeBody ();
    writeClosing ();
    closeStream ();
  } // generate

  /**
   * Initialize variables unique to this generator.
   **/
  protected void init ()
  {
    helperClass = entry.name () + "Helper";
    if (entry instanceof ValueBoxEntry)
    {
      ValueBoxEntry v = (ValueBoxEntry) entry;
      TypedefEntry member = ((InterfaceState) v.state ().elementAt (0)).entry;
      SymtabEntry mType =  member.type ();

      if (mType instanceof PrimitiveEntry)
        helperType = Util.javaName (entry);
      else
        helperType = Util.javaName (mType);
    }
    else
      helperType = Util.javaName (entry);
  } // init

  /**
   * Open the print stream for subsequent output.
   **/
  protected void openStream ()
  {
    stream = Util.stream (entry, "Helper.java");
  } // openStream

  /**
   * Generate the heading, including package, imports, class statements,
   * and open curly.
   **/
  protected void writeHeading ()
  {
    Util.writePackage (stream, entry, Util.HelperFile);
    Util.writeProlog (stream, stream.name ());

    // Transfer comment to target <30jul1997daz>.
    if (entry.comment () != null)
      entry.comment ().generate ("", stream);

    stream.print ("public final class " + helperClass);
    if (entry instanceof ValueEntry)
      stream.println (" implements org.omg.CORBA.portable.ValueHelper");
    else
      stream.println ();
    stream.println ('{');
  }

  /**
   * Generate members of this class.
   **/
  protected void writeBody ()
  {
    writeInstVars ();
    writeCtors ();
    writeInsert ();
    writeExtract ();
    writeType ();
    writeID ();
    writeRead ();
    writeWrite ();
    if (entry instanceof InterfaceEntry && !(entry instanceof ValueEntry)) {
      writeNarrow ();
      writeUncheckedNarrow ();
    }
    writeHelperInterface ();
    if (entry instanceof ValueEntry)
      writeValueHelperInterface ();
  } // writeBody

  /**
   * Generate members of the Helper interface.
   **/
  protected void writeHelperInterface ()
  {
  } // writeHelperInterface

  /**
   * Generate members of the ValueHelper interface.
   **/
  protected void writeValueHelperInterface ()
  {
    writeGetID ();       // moved for <d59413>
    writeGetType ();     // moved for <d59413>
    writeGetInstance (); // not in ValueHelper interface
    writeGetClass ();
    writeGetSafeBaseIds ();
  } // writeHelperInterface

  /**
   * Generate the closing statements.
   **/
  protected void writeClosing ()
  {
    stream.println ('}');
  }

  /**
   * Write the stream to file by closing the print stream.
   **/
  protected void closeStream ()
  {
    stream.close ();
  }

  /**
   * Generate the instance variables.
   **/
  protected void writeInstVars ()
  {
    stream.println ("  private static String  _id = \"" + Util.stripLeadingUnderscoresFromID (entry.repositoryID ().ID ()) + "\";");
    if (entry instanceof ValueEntry)
    {
      stream.println ();
      stream.println ("  private static " + helperClass + " helper = new " + helperClass + " ();");
      stream.println ();
      stream.println ("  private static String[] _truncatable_ids = {");
      stream.print   ("    _id");

      // Any safe ValueEntry must have a concete value parent.
      // The topmost parent cannot be safe since it doesn't have
      // a concrete parent.
      ValueEntry child = (ValueEntry) entry;
      while (child.isSafe ())
      {
        stream.println(",");
        ValueEntry parent = (ValueEntry)child.derivedFrom ().elementAt (0);
        stream.print("    \"" + Util.stripLeadingUnderscoresFromID (parent.repositoryID ().ID ()) + "\"");
        child = parent;
      }
      stream.println("   };");
    }
    stream.println ();
  } // writeInstVars

  /**
   * Generate the constructors.
   **/
  protected void writeCtors ()
  {
    stream.println ("  public " + helperClass + "()");
    stream.println ("  {");
    stream.println ("  }");
    stream.println ();
  } // writeCtors

  /**
   * Generate the insert method.
   **/
  protected void writeInsert ()
  {
    stream.println ("  public static void insert (org.omg.CORBA.Any a, " + helperType + " that)");
    stream.println ("  {");
    stream.println ("    org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();");
    stream.println ("    a.type (type ());");
    stream.println ("    write (out, that);");
    stream.println ("    a.read_value (out.create_input_stream (), type ());");
    stream.println ("  }");
    stream.println ();
  } // writeInsert

  /**
   * Generate the extract method.
   **/
  protected void writeExtract ()
  {
    stream.println ("  public static " + helperType + " extract (org.omg.CORBA.Any a)");
    stream.println ("  {");
    stream.println ("    return read (a.create_input_stream ());");
    stream.println ("  }");
    stream.println ();
  } // writeExtract

  /**
   * Generate the typecode variable and type method.
   **/
  protected void writeType ()
  {
    boolean canRecurse = entry instanceof ValueEntry
        || entry instanceof ValueBoxEntry
        || entry instanceof StructEntry;
    stream.println ("  private static org.omg.CORBA.TypeCode __typeCode = null;");
    if (canRecurse)
      stream.println ("  private static boolean __active = false;");
    stream.println ("  synchronized public static org.omg.CORBA.TypeCode type ()");
    stream.println ("  {");
    stream.println ("    if (__typeCode == null)");
    stream.println ("    {");
    if (canRecurse) {
    stream.println ("      synchronized (org.omg.CORBA.TypeCode.class)");
    stream.println ("      {");
    stream.println ("        if (__typeCode == null)");
    stream.println ("        {");
    stream.println ("          if (__active)");
    stream.println ("          {");
    stream.println ("            return org.omg.CORBA.ORB.init().create_recursive_tc ( _id );");
    stream.println ("          }");
    stream.println ("          __active = true;");
    ((JavaGenerator)entry.generator ()).helperType (0, "          ", new TCOffsets (), "__typeCode", entry, stream);
    }
    else
    ((JavaGenerator)entry.generator ()).helperType (0, "      ", new TCOffsets (), "__typeCode", entry, stream);

    // Generate body of type() method

    if (canRecurse) {
    stream.println ("          __active = false;");
    stream.println ("        }");
    stream.println ("      }");
    }
    stream.println ("    }");
    stream.println ("    return __typeCode;");
    stream.println ("  }");
    stream.println ();
  } // writeType

  /**
   * Generate the ID method.
   **/
  protected void writeID ()
  {
    stream.println ("  public static String id ()");
    stream.println ("  {");
    stream.println ("    return _id;");
    stream.println ("  }");
    stream.println ();
  } // writeID

  /**
   * Generate the read method.
   **/
  protected void writeRead ()
  {

    boolean isLocalInterface = false;

    if (entry instanceof InterfaceEntry) {
        InterfaceEntry ie = (InterfaceEntry) entry;

        // for #pragma sun_local or sun_localservant, or actual local
        // local interface, set the flag by checking on both
        isLocalInterface = ie.isLocal() | ie.isLocalServant();
    }

    stream.println ("  public static " + helperType + " read (org.omg.CORBA.portable.InputStream istream)");
    stream.println ("  {");
    if ( !isLocalInterface ) { // nonLocal Interface and other types
      ((JavaGenerator)entry.generator ()).helperRead (helperType, entry, stream);
    } else { //Local interface should throw exception
      stream.println ("      throw new org.omg.CORBA.MARSHAL ();");
    }
    stream.println ("  }");
    stream.println ();
  } // writeRead

  /**
   * Generate the write method.
   **/
  protected void writeWrite ()
  {

    boolean isLocalInterface = false;

    if (entry instanceof InterfaceEntry) {
        InterfaceEntry ie = (InterfaceEntry) entry;

        // for #pragma sun_local or sun_localservant, or actual local
        // local interface, set the flag by checking on both
        isLocalInterface = ie.isLocal() | ie.isLocalServant();
    }

    stream.println ("  public static void write (org.omg.CORBA.portable.OutputStream ostream, " + helperType + " value)");
    stream.println ("  {");
    if ( !isLocalInterface ) { // nonLocal Interface and other types
      ((JavaGenerator)entry.generator ()).helperWrite (entry, stream);
    } else { //Local interface should throw exception
      stream.println ("      throw new org.omg.CORBA.MARSHAL ();");
    }
    stream.println ("  }");
    stream.println ();
  } // writeWrite


  /**
   * Generate the narrow method.
   **/
  protected void writeNarrow ()
  {
    writeRemoteNarrow ();
    stream.println ();
  }

  /**
   * Write the narrow() method for a remotable object.
   **/
  protected void writeRemoteNarrow ()
  {
    InterfaceEntry ie = (InterfaceEntry) entry;

    // narrow for LocalObject interface
    if (ie.isLocal ()) {
        writeRemoteNarrowForLocal (false);
        return;
    }

    // narrow for Abstract interface
    if (ie.isAbstract ()) {
        writeRemoteNarrowForAbstract (false);
        return;
    } else {
        // Determine if the non-abstract interface has any abstract parents
        for (int i = 0; i < ie.derivedFrom ().size (); i++) {
            SymtabEntry parent = (SymtabEntry) ie.derivedFrom ().elementAt (i);
            if (((InterfaceEntry) parent).isAbstract ()) {
                writeRemoteNarrowForAbstract (true);
                break;
            }
        }
    }

    stream.println ("  public static " + helperType + " narrow (org.omg.CORBA.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    stream.println ("    else if (obj instanceof " + helperType + ')');
    stream.println ("      return (" + helperType + ")obj;");
    stream.println ("    else if (!obj._is_a (id ()))");
    stream.println ("      throw new org.omg.CORBA.BAD_PARAM ();");
    stream.println ("    else");
    stream.println ("    {");
    stream.println ("      org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();");
    String stubNameofEntry = stubName ((InterfaceEntry)entry);
    stream.println ("      " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();");
    stream.println ("      stub._set_delegate(delegate);");
    stream.println ("      return stub;");
    stream.println ("    }");
    stream.println ("  }");
  } // writeRemoteNarrow

  /**
   * Write the narrow() method for local interface.
   **/
  private void writeRemoteNarrowForLocal (boolean hasAbstractParent)
  {
    stream.println ("  public static " + helperType + " narrow (org.omg.CORBA.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    stream.println ("    else if (obj instanceof " + helperType + ')');
    stream.println ("      return (" + helperType + ")obj;");
    stream.println ("    else");
    stream.println ("      throw new org.omg.CORBA.BAD_PARAM ();");
    stream.println ("  }");
  } // writeRemoteNarrowForLocal

  /**
   * Write the narrow() method for abstract interface.
   **/
  private void writeRemoteNarrowForAbstract (boolean hasAbstractParent)
  {
    stream.print ("  public static " + helperType + " narrow (java.lang.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    if (hasAbstractParent)
    {
      stream.println ("    else if (obj instanceof org.omg.CORBA.Object)");
      stream.println ("      return narrow ((org.omg.CORBA.Object) obj);");
    }
    else
    {
      stream.println ("    else if (obj instanceof " + helperType + ')');
      stream.println ("      return (" + helperType + ")obj;");
    }

    // If hasAbstractParent is false, then THIS entry must be abstract.
    // This method is also called in case THIS entry is not abstract, but
    // there is an abstract parent.  If this entry is not abstract,
    // it can never narrow to a CORBA object reference.
    if (!hasAbstractParent) { // <d58889>
      String stubNameofEntry = stubName ((InterfaceEntry)entry);

      stream.println ("    else if ((obj instanceof org.omg.CORBA.portable.ObjectImpl) &&");
      stream.println ("             (((org.omg.CORBA.Object)obj)._is_a (id ()))) {");
      stream.println ("      org.omg.CORBA.portable.ObjectImpl impl = (org.omg.CORBA.portable.ObjectImpl)obj ;" ) ;
      stream.println ("      org.omg.CORBA.portable.Delegate delegate = impl._get_delegate() ;" ) ;
      stream.println ("      " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();");
      stream.println ("      stub._set_delegate(delegate);");
      stream.println ("      return stub;" ) ;
      stream.println ("    }" ) ;
    };
    // end <d57118 - check for remotable - klr>

    stream.println ("    throw new org.omg.CORBA.BAD_PARAM ();");
    stream.println ("  }");
    stream.println ();
  } // writeRemoteNarrowForAbstract


  /**
   * Generate the unchecked narrow method.
   **/
  protected void writeUncheckedNarrow ()
  {
    writeUncheckedRemoteNarrow ();
    stream.println ();
  }

  /**
   * Write the unchecked narrow() method for a remotable object.
   **/
  protected void writeUncheckedRemoteNarrow ()
  {
    InterfaceEntry ie = (InterfaceEntry) entry;

    // unchecked narrow for LocalObject interface
    if (ie.isLocal ()) {
        writeRemoteUncheckedNarrowForLocal (false);
        return;
    }

    // unchecked narrow for Abstract interface
    if (ie.isAbstract ()) {
        writeRemoteUncheckedNarrowForAbstract (false);
        return;
    } else {
        // Determine if the non-abstract interface has any abstract parents
        for (int i = 0; i < ie.derivedFrom ().size (); i++) {
            SymtabEntry parent = (SymtabEntry) ie.derivedFrom ().elementAt (i);
            if (((InterfaceEntry) parent).isAbstract ()) {
                writeRemoteUncheckedNarrowForAbstract (true);
                break;
            }
        }
    }

    stream.println ("  public static " + helperType + " unchecked_narrow (org.omg.CORBA.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    stream.println ("    else if (obj instanceof " + helperType + ')');
    stream.println ("      return (" + helperType + ")obj;");
    stream.println ("    else");
    stream.println ("    {");
    stream.println ("      org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();");
    String stubNameofEntry = stubName ((InterfaceEntry)entry);
    stream.println ("      " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();");
    stream.println ("      stub._set_delegate(delegate);");
    stream.println ("      return stub;");
    stream.println ("    }");
    stream.println ("  }");
  } // writeUncheckedRemoteNarrow

  /**
   * Write the unchecked narrow() method for local interface.
   **/
  private void writeRemoteUncheckedNarrowForLocal (boolean hasAbstractParent)
  {
    stream.println ("  public static " + helperType + " unchecked_narrow (org.omg.CORBA.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    stream.println ("    else if (obj instanceof " + helperType + ')');
    stream.println ("      return (" + helperType + ")obj;");
    stream.println ("    else");
    stream.println ("      throw new org.omg.CORBA.BAD_PARAM ();");
    stream.println ("  }");
  } // writeRemoteUncheckedNarrowForLocal

  /**
   * Write the unchecked narrow() method for abstract interface.
   **/
  private void writeRemoteUncheckedNarrowForAbstract (boolean hasAbstractParent)
  {
    stream.print ("  public static " + helperType + " unchecked_narrow (java.lang.Object obj)");
    stream.println ("  {");
    stream.println ("    if (obj == null)");
    stream.println ("      return null;");
    if (hasAbstractParent)
    {
      stream.println ("    else if (obj instanceof org.omg.CORBA.Object)");
      stream.println ("      return unchecked_narrow ((org.omg.CORBA.Object) obj);");
    }
    else
    {
      stream.println ("    else if (obj instanceof " + helperType + ')');
      stream.println ("      return (" + helperType + ")obj;");
    }

    if (!hasAbstractParent) {
      String stubNameofEntry = stubName ((InterfaceEntry)entry);

      stream.println ("    else if (obj instanceof org.omg.CORBA.portable.ObjectImpl) {");
      stream.println ("      org.omg.CORBA.portable.ObjectImpl impl = (org.omg.CORBA.portable.ObjectImpl)obj ;" ) ;
      stream.println ("      org.omg.CORBA.portable.Delegate delegate = impl._get_delegate() ;" ) ;
      stream.println ("      " + stubNameofEntry + " stub = new " + stubNameofEntry + " ();");
      stream.println ("      stub._set_delegate(delegate);");
      stream.println ("      return stub;" ) ;
      stream.println ("    }" ) ;
    };

    stream.println ("    throw new org.omg.CORBA.BAD_PARAM ();");
    stream.println ("  }");
    stream.println ();
  } // writeRemoteUncheckedNarrowForAbstract


  /**
   * Generate the GetID method.
   **/
  protected void writeGetID ()
  {
    if ( !Util.IDLEntity (entry))
      return;
    stream.println ("  public String get_id ()");
    stream.println ("  {");
    stream.println ("    return _id;");
    stream.println ("  }");
    stream.println ();
  } // writeGetID

  /**
   * Generate the GetType method.
   **/
  protected void writeGetType ()
  {
    if ( !Util.IDLEntity (entry))
      return;
    stream.println ("  public org.omg.CORBA.TypeCode get_type ()");
    stream.println ("  {");
    stream.println ("    return type ();");
    stream.println ("  }");
    stream.println ();
  } // writeGetID

  /**
   * Generate the get_class method.
   **/
  protected void writeGetClass ()
  {
    stream.println ("  public Class get_class ()");
    stream.println ("  {");
    stream.println ("    return " + helperType + ".class;"); //<d59383>
    stream.println ("  }");
    stream.println ();
  } // writeGetClass

  /**
   * Generate the get_instance method.
   **/
  protected void writeGetInstance ()
  {
    stream.println ("  public static org.omg.CORBA.portable.ValueHelper get_instance ()");
    stream.println ("  {");
    stream.println ("    return helper;");
    stream.println ("  }");
    stream.println ();
  } // writeGetInstance

  /**
   * Generate the GetSafeBaseIds method.
   **/
  protected void writeGetSafeBaseIds ()
  {
    stream.println ("  public String[] get_truncatable_base_ids ()");
    stream.println ("  {");
    stream.println ("    return _truncatable_ids;");
    stream.println ("  }");
    stream.println ();
  } // writeGetSafeBaseIds

  /**
   * Return the stub name for the interface entry.
   **/
  protected String stubName (InterfaceEntry entry)
  {
    String name;
    if (entry.container ().name ().equals (""))
      name =  '_' + entry.name () + "Stub";
    else
    {
      name = Util.containerFullName (entry.container ()) + "._" + entry.name () + "Stub";
    }
    return name.replace ('/', '.');
  } // stubName

  protected java.util.Hashtable     symbolTable;
  protected com.sun.tools.corba.se.idl.SymtabEntry entry;
  protected GenFileStream           stream;

  // Unique to this generator
  protected String helperClass;
  protected String helperType;
} // class Helper