//==================================================================================================
// J a v a                                                                                     Java
// R e f l e c t i o n
// J i r k C l a s s
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library 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 Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides a class to build the Jirk++ representation of a Java class. */

// Package //---------------------------------------------------------------------------------------
package bpp.java.reflection;

// Importation //-----------------------------------------------------------------------------------
import bpp.standard.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;

@SuppressWarnings("unchecked")

// J i r k C l a s s  Class //----------------------------------------------------------------------
/*CLASS JirkClass */ /* Represents a Java class and its Jirk++ representation. */
public class JirkClass implements Comparable {
 //---------------------------------------------------------------------------------------Attributes
 protected Vector  atAttributeS;   // Public attributes of the class.
 protected Class   atClass;        // Reference of the class.
 protected Vector  atClassS;       // Public inner classes of the class.
 protected Vector  atConstructorS; // Public constructors of the class.
 protected TreeSet atDependencyS;  // Classes the class depends on.
 protected TreeSet atIncludeS;     // Classes to include in the Jirk++ definition.
 protected Vector  atMethodS;      // Public methods of the class.
 protected Vector  atPathClassS;   // Classes forming the name of the class.
 protected Vector  atPathModuleS;  // Modules forming the name of the class.
 protected Vector  atSuperS;       // Superclasses of the class.
 //----------------------------------------------------------------------------------------BuildPath
 // Stores in vectors the different parts of the full name of the class.
 protected void buildPath() {
  int    lcEnd  = 0;
  String lcName = javaName();

  atPathClassS=new Vector();
  atPathModuleS=new Vector();
  if (isArray() || isPrimitive()) return;

  do {
   lcEnd=lcName.indexOf('.');

   if (lcEnd>=0) {
    atPathModuleS.add(lcName.substring(0,lcEnd));
    lcName=lcName.substring(lcEnd+1,lcName.length());
   }
  }
  while (lcEnd>=0);

  do {
   lcEnd=lcName.indexOf('$');
   atPathClassS.add(lcName.substring(0,(lcEnd<0 ? lcName.length() : lcEnd)));
   if (lcEnd>=0) lcName=lcName.substring(lcEnd+1,lcName.length());
  }
  while (lcEnd>=0);
 }
 //---------------------------------------------------------------------------------BuildInheritance
 // Builds a list of the inherited classes and interfaces.
 protected void buildInheritance(TreeSet agDependencyS,TreeSet agIncludeS,
                                 TreeSet agToGenerateS,TreeSet agAlreadyGeneratedS) {
  JirkClass lcClass;

  Class lcClassS[] = atClass.getInterfaces();
  int   lcCounter  = 0;

  atSuperS=new Vector();

  if (atClass.getSuperclass()!=null) {
   lcClass=new JirkClass(atClass.getSuperclass());
   atSuperS.add(lcClass);
   registerClass(lcClass,agDependencyS,agToGenerateS,agAlreadyGeneratedS);
   addInclude(lcClass,agIncludeS);
  }

  while (lcCounter<lcClassS.length) {
   lcClass=new JirkClass(lcClassS[lcCounter]);
   atSuperS.add(lcClass);
   registerClass(lcClass,agDependencyS,agToGenerateS,agAlreadyGeneratedS);
   addInclude(lcClass,agIncludeS);
   ++lcCounter;
  }
 }
 //-------------------------------------------------------------------------------------BuildClasses
 // Builds a list of the inner classes of the class.
 protected void buildClasses(TreeSet agDependencyS,TreeSet agIncludeS,
                             TreeSet agToGenerateS,TreeSet agAlreadyGeneratedS) {
  JirkClass lcClass;

  Class lcClassS[] = atClass.getDeclaredClasses();
  int   lcCounter  = 0;

  atClassS=new Vector();

  while (lcCounter<lcClassS.length) {
   lcClass=new JirkClass(lcClassS[lcCounter]);

   if (lcClass.isPublic()) {
    if (agDependencyS==null)
     lcClass.build(atDependencyS,atIncludeS,agToGenerateS,agAlreadyGeneratedS);
    else lcClass.build(agDependencyS,agIncludeS,agToGenerateS,agAlreadyGeneratedS);

    atClassS.add(lcClass);
   }

   ++lcCounter;
  }

  if (agToGenerateS!=null) sortInnerClasses();
 }
 //---------------------------------------------------------------------------------SortInnerClasses
 // Sorts the list of inner classes according to their interdependency.
 protected void sortInnerClasses() {
  JirkClass lcClass1;
  JirkClass lcClass2;
  int       lcCounter2;

  int lcCounter1 = 0;

  while (lcCounter1<atClassS.size()) {
   lcCounter2=lcCounter1+1;

   while (lcCounter2<atClassS.size()) {
    lcClass1=(JirkClass)atClassS.elementAt(lcCounter1);
    lcClass2=(JirkClass)atClassS.elementAt(lcCounter2);

    if (lcClass2.isBefore(lcClass1)) {
     atClassS.setElementAt(lcClass1,lcCounter2);
     atClassS.setElementAt(lcClass2,lcCounter1);
    }

    ++lcCounter2;
   }

   ++lcCounter1;
  }
 }
 //-----------------------------------------------------------------------------------------IsBefore
 // Indicates if the class must be declared before another one according to their interdependency.
 protected boolean isBefore(JirkClass agClass) {
  JirkClass lcClass;

  int lcCounter = 0;

  while (lcCounter<agClass.atSuperS.size()) {
   lcClass=(JirkClass)agClass.atSuperS.elementAt(lcCounter);
   if (lcClass.atClass==atClass) return (true);
   ++lcCounter;
  }

  return (false);
 }
 //----------------------------------------------------------------------------------BuildAttributes
 // Builds a list of the attributes of the class.
 protected void buildAttributes(TreeSet agDependencyS,TreeSet agToGenerateS,
                                TreeSet agAlreadyGeneratedS) {
  JirkAttribute lcAttribute;

  Field lcAttributeS[] = atClass.getDeclaredFields();
  int   lcCounter      = 0;

  atAttributeS=new Vector();

  while (lcCounter<lcAttributeS.length) {
   lcAttribute=new JirkAttribute(this,lcAttributeS[lcCounter]);

   if (lcAttribute.isPublic()) {
    atAttributeS.add(lcAttribute);

    if (!lcAttribute.type().baseType().isPrimitive())
     registerClass(lcAttribute.type().baseType(),agDependencyS,agToGenerateS,agAlreadyGeneratedS);
   }

   ++lcCounter;
  }
 }
 //--------------------------------------------------------------------------------BuildConstructors
 // Builds a list of the constructors of the class.
 protected void buildConstructors(TreeSet agDependencyS,TreeSet agToGenerateS,
                                  TreeSet agAlreadyGeneratedS) {
  int             lcCounter2;
  JirkConstructor lcConstructor;

  Constructor lcConstructorS[] = atClass.getDeclaredConstructors();
  int         lcCounter1       = 0;

  atConstructorS=new Vector();

  while (lcCounter1<lcConstructorS.length) {
   lcConstructor=new JirkConstructor(this,lcConstructorS[lcCounter1]);

   if (lcConstructor.isPublic()) {
    atConstructorS.add(lcConstructor);
    lcCounter2=0;

    while (lcCounter2<lcConstructor.argumentTypes().length) {
     if (!lcConstructor.argumentTypes()[lcCounter2].baseType().isPrimitive())
      registerClass(lcConstructor.argumentTypes()[lcCounter2].baseType(),agDependencyS,
                    agToGenerateS,agAlreadyGeneratedS);

     ++lcCounter2;
    }
   }

   ++lcCounter1;
  }
 }
 //-------------------------------------------------------------------------------------BuildMethods
 // Builds a list of the methods of the class.
 protected void buildMethods(TreeSet agDependencyS,TreeSet agToGenerateS,
                             TreeSet agAlreadyGeneratedS) {
  int        lcCounter2;
  Iterator   lcIterator;
  JirkMethod lcMethod;
  String     lcSignature;

  int     lcCounter1   = 0;
  Method  lcMethodS[]  = atClass.getDeclaredMethods();
  TreeSet lcSignatureS = new TreeSet();

  atMethodS=new Vector();

  while (lcCounter1<lcMethodS.length) {
   lcMethod=new JirkMethod(this,lcMethodS[lcCounter1]);

   // We use the C++ declaration of a method as its signature. If two methods have the same
   // signature, only one will be considered. For instance, this situation appears in the
   // "io.PrintStream" class of Java 1.5, where the method "append" is inherited from the
   // "Appendable" interface, and is overridden in "PrintStream". However, in "Appendable",
   // the method returns a reference to an "Appendable"; and in "PrintStream", the method
   // returns a reference to a "PrintStream". Using Java reflection, the method will appear
   // twice in the "PrintStream" class, returning either an "Appendable" or a "PrintStream"
   // reference.

   lcSignature="";
   lcIterator=lcMethod.declaration().iterator();
   while (lcIterator.hasNext()) lcSignature+=(String)(lcIterator.next());

   if (lcMethod.isPublic() && !lcSignatureS.contains(lcSignature)) {
    atMethodS.add(lcMethod);
    lcSignatureS.add(lcSignature);

    if (!lcMethod.returnType().baseType().isPrimitive())
     registerClass(lcMethod.returnType().baseType(),agDependencyS,
                   agToGenerateS,agAlreadyGeneratedS);

    lcCounter2=0;

    while (lcCounter2<lcMethod.argumentTypes().length) {
     if (!lcMethod.argumentTypes()[lcCounter2].baseType().isPrimitive())
      registerClass(lcMethod.argumentTypes()[lcCounter2].baseType(),agDependencyS,
                    agToGenerateS,agAlreadyGeneratedS);

     ++lcCounter2;
    }
   }

   ++lcCounter1;
  }
 }
 //------------------------------------------------------------------------------------RegisterClass
 // Registers a class for the generation of its Jirk++ representation.
 protected void registerClass(JirkClass agClass,TreeSet agDependencyS,TreeSet agToGenerateS,
                              TreeSet agAlreadyGeneratedS) {
  while (agClass.isInner()) agClass=new JirkClass(agClass.atClass.getDeclaringClass());
  if (!agAlreadyGeneratedS.contains(agClass)) agToGenerateS.add(agClass);

  if (agDependencyS==null) atDependencyS.add(agClass);
  else agDependencyS.add(agClass);
 }
 //---------------------------------------------------------------------------------------AddInclude
 // Adds a class in the list of classes to include in the Jirk++ definition of the class.
 protected void addInclude(JirkClass agClass,TreeSet agIncludeS) {
  while (agClass.isInner()) agClass=new JirkClass(agClass.atClass.getDeclaringClass());

  if (agIncludeS==null) atIncludeS.add(agClass);
  else agIncludeS.add(agClass);
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD JirkClass */ /* Builds an object representing a Java class from its name. */
 public JirkClass(String agName) throws Exception {
  atAttributeS=null;
  atClassS=null;
  atConstructorS=null;
  atDependencyS=null;
  atIncludeS=null;
  atMethodS=null;
  atSuperS=null;

  try { atClass=Class.forName(agName); }
  catch (Exception e) { throw new Exception("Java/Reflection - Class not found."); }

  buildPath();
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD JirkClass */ /* Builds an object representing a Java class from its reference. */
 public JirkClass(Class agClass) {
  atAttributeS=null;
  atClass=agClass;
  atClassS=null;
  atConstructorS=null;
  atDependencyS=null;
  atIncludeS=null;
  atMethodS=null;
  atSuperS=null;
  buildPath();
 }
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD JirkClass */ /* Builds and copies an object. */
  public JirkClass(JirkClass agClass) {
  atAttributeS=null;
  atClass=agClass.atClass;
  atClassS=null;
  atConstructorS=null;
  atDependencyS=null;
  atIncludeS=null;
  atMethodS=null;
  atSuperS=null;
  buildPath();
 }
 //----------------------------------------------------------------------------------------CompareTo
 /*METHOD JirkClass */
 /* Compares the class to another one according to the lexicographical order of their name. */
 public int compareTo(Object agObject)
 { return (javaName().compareTo(((JirkClass)agObject).javaName())); }
 //--------------------------------------------------------------------------------------------Build
 /*METHOD JirkClass */ /* Builds the whole structure of the class. */
 public void build(TreeSet agDependencyS,TreeSet agIncludeS,TreeSet agToGenerateS,
                   TreeSet agAlreadyGeneratedS) {
  atDependencyS=new TreeSet();
  atIncludeS=new TreeSet();

  if (agToGenerateS==null)
   buildClasses(agDependencyS,agIncludeS,agToGenerateS,agAlreadyGeneratedS);
  else {
   buildInheritance(agDependencyS,agIncludeS,agToGenerateS,agAlreadyGeneratedS);
   buildClasses(agDependencyS,agIncludeS,agToGenerateS,agAlreadyGeneratedS);
   buildAttributes(agDependencyS,agToGenerateS,agAlreadyGeneratedS);
   buildConstructors(agDependencyS,agToGenerateS,agAlreadyGeneratedS);
   buildMethods(agDependencyS,agToGenerateS,agAlreadyGeneratedS);
  }
 }
 //---------------------------------------------------------------------------------------Attributes
 /*METHOD JirkClass */ /* Returns the public attributes of the class. */
 public Vector attributes() { return (atAttributeS); }
 //------------------------------------------------------------------------------------------Classes
 /*METHOD JirkClass */ /* Returns the public inner classes of the class. */
 public Vector classes() { return (atClassS); }
 //-------------------------------------------------------------------------------------Constructors
 /*METHOD JirkClass */ /* Returns the public constructors of the class. */
 public Vector constructors() { return (atConstructorS); }
 //-------------------------------------------------------------------------------------Dependencies
 /*METHOD JirkClass */ /* Returns the classes the class depends on. */
 public TreeSet dependencies() { return (atDependencyS); }
 //-----------------------------------------------------------------------------------------Includes
 /*METHOD JirkClass */ /* Returns the classes to include in the Jirk++ definition of the class. */
 public TreeSet includes() { return (atIncludeS); }
 //------------------------------------------------------------------------------------------IsArray
 /*METHOD JirkClass */ /* Indicates if the class represents an array. */
 public boolean isArray() { return (atClass.isArray()); }
 //------------------------------------------------------------------------------------------IsInner
 /*METHOD JirkClass */ /* Indicates if the class is an inner class. */
 public boolean isInner() { return (atClass.getDeclaringClass()!=null); }
 //--------------------------------------------------------------------------------------IsPrimitive
 /*METHOD JirkClass */ /* Indicates if the class represents a primitive type. */
 public boolean isPrimitive() { return (atClass.isPrimitive()); }
 //-----------------------------------------------------------------------------------------IsPublic
 /*METHOD JirkClass */ /* Indicates if the class is public. */
 public boolean isPublic() { return (Modifier.isPublic(atClass.getModifiers())); }
 //-----------------------------------------------------------------------------------------JavaName
 /*METHOD JirkClass */ /* Returns the Java name of the class. */
 public String javaName() { return (atClass.getName()); }
 //------------------------------------------------------------------------------------------Methods
 /*METHOD JirkClass */ /* Returns the public methods of the class. */
 public Vector methods() { return (atMethodS); }
 //-------------------------------------------------------------------------------------------Supers
 /*METHOD JirkClass */ /* Returns the superclasses of the class. */
 public Vector supers() { return (atSuperS); }
 //----------------------------------------------------------------------------------------Dimension
 /*METHOD JirkClass */
 /* If the class represents an array, returns the number of dimensions of the array. */
 public int dimension() {
  Class lcClass     = atClass;
  int   lcDimension = 0;

  while (lcClass.isArray()) {
   lcClass=lcClass.getComponentType();
   ++lcDimension;
  }

  return (lcDimension);
 }
 //-----------------------------------------------------------------------------------------BaseType
 /*METHOD JirkClass */
 /* If the class represents an array, returns the base type of the array, else it returns the
    class itself. */
 public JirkClass baseType() {
  Class lcClass = atClass;

  while (lcClass.isArray()) lcClass=lcClass.getComponentType();
  return (new JirkClass(lcClass));
 }
 //----------------------------------------------------------------------------------------Signature
 /*METHOD JirkClass */ /* Returns the JNI signature of the class. */
 public String signature() {
  int    lcCounter = 0;
  String lcName    = "L";

  if (isArray()) return ("["+(new JirkClass(atClass.getComponentType())).signature());

  if (isPrimitive()) {
   if (javaName().equals("boolean")) return ("Z");
   else if (javaName().equals("byte")) return ("B");
   else if (javaName().equals("char")) return ("C");
   else if (javaName().equals("short")) return ("S");
   else if (javaName().equals("int")) return ("I");
   else if (javaName().equals("long")) return ("J");
   else if (javaName().equals("float")) return ("F");
   else if (javaName().equals("double")) return ("D");
   else return ("V");
  }

  while (lcCounter<atPathModuleS.size()) {
   lcName+=(String)atPathModuleS.elementAt(lcCounter)+"/";
   ++lcCounter;
  }

  lcCounter=0;

  while (lcCounter<atPathClassS.size()) {
   lcName+=(String)atPathClassS.elementAt(lcCounter);
   ++lcCounter;
   if (lcCounter<atPathClassS.size()) lcName+="$";
  }

  return (lcName+";");
 }
 //---------------------------------------------------------------------------------------------Name
 /*METHOD JirkClass */
 /* Returns a descriptive name of the type the class represents. Used to call methods in Jirk++. */
 public String name() {
  if (isPrimitive()) {
   if (javaName().equals("boolean")) return ("Boolean");
   else if (javaName().equals("byte")) return ("Byte");
   else if (javaName().equals("char")) return ("Character");
   else if (javaName().equals("short")) return ("Short");
   else if (javaName().equals("int")) return ("Integer");
   else if (javaName().equals("long")) return ("Long");
   else if (javaName().equals("float")) return ("Float");
   else if (javaName().equals("double")) return ("Double");
   else return ("Void");
  }
  else return ("Object");
 }
 //----------------------------------------------------------------------------------------ShortName
 /*METHOD JirkClass */ /* Returns the Jirk++ short name of the class. */
 public String shortName() {
  int    lcCounter = 0;
  String lcName    = "ja";

  if (isPrimitive() || isArray()) return ("");

  while (lcCounter<atPathClassS.size()) {
   lcName+=(String)atPathClassS.elementAt(lcCounter);
   ++lcCounter;
   if (lcCounter<atPathClassS.size()) lcName+="_";
  }

  return (lcName);
 }
 //-----------------------------------------------------------------------------------------FullName
 /*METHOD JirkClass */ /* Returns the Jirk++ full name of the class. */
 public String fullName() {
  String lcModule;

  int    lcCounter = 0;
  String lcName    = "jirk";

  if (isArray()) return ("clAbstractArray<"+baseType().fullName()+","+dimension()+">");

  if (isPrimitive()) {
   if (javaName().equals("boolean")) return ("jyBoolean");
   else if (javaName().equals("byte")) return ("jyByte");
   else if (javaName().equals("char")) return ("jyCharacter");
   else if (javaName().equals("short")) return ("jyShort");
   else if (javaName().equals("int")) return ("jyInteger");
   else if (javaName().equals("long")) return ("jyLong");
   else if (javaName().equals("float")) return ("jyFloat");
   else if (javaName().equals("double")) return ("jyDouble");
   else return ("void");
  }

  while (lcCounter<atPathModuleS.size()) {
   lcModule=(String)atPathModuleS.elementAt(lcCounter);
   if (lcModule.equals("function")) lcModule="_function";
   lcName+="::"+lcModule;
   ++lcCounter;
  }

  lcCounter=0;
  lcName+="::ja";

  while (lcCounter<atPathClassS.size()) {
   lcName+=(String)atPathClassS.elementAt(lcCounter);
   ++lcCounter;
   if (lcCounter<atPathClassS.size()) lcName+="_";
  }

  return (lcName);
 }
 //-------------------------------------------------------------------------------------------Folder
 /*METHOD JirkClass */
 /* Returns the folder where the files containing the Jirk++ representation of the class
    are located. */
 public String folder() {
  int    lcCounter = 0;
  String lcName    = "jirk";

  if (isArray() || isPrimitive()) return ("");

  while (lcCounter<atPathModuleS.size()) {
   lcName+="/"+((String)atPathModuleS.elementAt(lcCounter)).toLowerCase();
   ++lcCounter;
  }

  return (lcName);
 }
 //----------------------------------------------------------------------------------------ShortFile
 /*METHOD JirkClass */
 /* Returns the short name (without the extension) of the files in which the Jirk++ representation
    of the class is stored. */
 public String shortFile() { return (((String)atPathClassS.elementAt(0)).toLowerCase()); }
 //---------------------------------------------------------------------------------------------File
 /*METHOD JirkClass */
 /* Returns the full name (without the extension) of the files in which the Jirk++ representation
    of the class is stored. */
 public String file() {
  if (isArray() || isPrimitive()) return ("");
  return (folder()+"/"+shortFile());
 }
 //-----------------------------------------------------------------------------------------Guardian
 // C++ writing of the include file guardian.
 protected String guardian() {
  String lcString;

  int    lcCounter = 0;
  String lcName    = "guJirk";

  if (isArray() || isPrimitive()) return ("");

  while (lcCounter<atPathModuleS.size()) {
   lcString=(String)atPathModuleS.elementAt(lcCounter);
   lcName+=lcString.substring(0,1).toUpperCase()+lcString.substring(1,lcString.length());
   ++lcCounter;
  }

  lcCounter=0;

  while (lcCounter<atPathClassS.size()) {
   lcName+=(String)atPathClassS.elementAt(lcCounter);
   ++lcCounter;
  }

  return (lcName);
 }
 //------------------------------------------------------------------------------------OpenNamespace
 // C++ writing of the namespace opening.
 protected String openNamespace() {
  String lcModule;

  int    lcCounter = 0;
  String lcString  = "namespace jirk {";

  if (isPrimitive() || isArray()) return (null);

  while (lcCounter<atPathModuleS.size()) {
   lcModule=(String)atPathModuleS.elementAt(lcCounter);
   if (lcModule.equals("function")) lcModule="_function";
   lcString+=" namespace "+lcModule+" {";
   ++lcCounter;
  }

  return (lcString);
 }
 //-----------------------------------------------------------------------------------CloseNamespace
 // C++ writing of the namespace closing.
 protected String closeNamespace() {
  int    lcCounter = 0;
  String lcString  = "}";

  if (isPrimitive() || isArray()) return (null);

  while (lcCounter<atPathModuleS.size()) {
   lcString+="}";
   ++lcCounter;
  }

  return (lcString);
 }
 //--------------------------------------------------------------------------------------Inheritance
 // C++ writing of the inheritance of the class.
 protected Vector inheritance() {
  int    lcCounter = 0;
  Vector lcLineS   = new Vector();

  if (supers().size()==0) lcLineS.add(": public virtual clObject {");
  else lcLineS.add(": public virtual clObject,");

  while (lcCounter<supers().size()) {
   if (lcCounter+1==supers().size())
    lcLineS.add("  public virtual "+((JirkClass)supers().elementAt(lcCounter)).fullName()+" {");
   else
    lcLineS.add("  public virtual "+((JirkClass)supers().elementAt(lcCounter)).fullName()+",");

   ++lcCounter;
  }

  return (lcLineS);
 }
 //--------------------------------------------------------------------------WriteForwardDeclaration
 // Writes the Jirk forward declaration of the class into a stream.
 protected void writeForwardDeclaration(OutputTextStream agStream,String agSpace) {
  JirkClass lcClass;
  Vector    lcLineS;

  int lcCounter = 0;

  if (!isInner()) {
   if (classes()==null) buildClasses(null,null,null,null);

   if (classes().size()==0)
    agStream.writeLine(agSpace+openNamespace()+" class "+shortName()+"; "+closeNamespace());
   else {
    agStream.writeLine(agSpace+openNamespace());
    agSpace+=" ";
    agStream.writeLine(agSpace+"class "+shortName()+";");

    while (lcCounter<classes().size()) {
     lcClass=(JirkClass)classes().elementAt(lcCounter);
     lcClass.writeForwardDeclaration(agStream,agSpace);
     ++lcCounter;
    }

    agSpace=agSpace.substring(0,agSpace.length()-1);
    agStream.writeLine(agSpace+closeNamespace());
   }
  }
  else {
   agStream.writeLine(agSpace+"class "+shortName()+";");
   if (classes()==null) buildClasses(null,null,null,null);

   while (lcCounter<classes().size()) {
    lcClass=(JirkClass)classes().elementAt(lcCounter);
    lcClass.writeForwardDeclaration(agStream,agSpace);
    ++lcCounter;
   }
  }
 }
 //---------------------------------------------------------------------------------------WriteLines
 // Writes into a stream the lines stored in a vector.
 protected void writeLines(OutputTextStream agStream,Vector agLineS,String agSpace) {
  int lcCounter = 0;

  while (lcCounter<agLineS.size()) {
   agStream.writeLine(agSpace+(String)agLineS.elementAt(lcCounter));
   ++lcCounter;
  }
 }
 //---------------------------------------------------------------------------------WriteDeclaration
 /*METHOD JirkClass */ /* Writes the Jirk++ declaration of the class into a stream. */
 public void writeDeclaration(OutputTextStream agStream,String agSpace) {
  JirkAttribute   lcAttribute;
  JirkClass       lcClass;
  JirkConstructor lcConstructor;
  int             lcCounter;
  Iterator        lcIterator;
  Vector          lcLineS;
  JirkMethod      lcMethod;

  if (!isInner()) {
   // Head //
   agStream.writeLine(agSpace+"/*");
   agStream.writeLine(agSpace+"  Automatically generated by");
   agStream.writeLine(agSpace+"  B u i l d   J i r k");
   agStream.writeLine(agSpace+"  "+bpp.JavaInformation.copyright());
   agStream.writeLine(agSpace+"  Bruno Bachelet");
   agStream.writeLine(agSpace+"  bruno@nawouak.net");
   agStream.writeLine(agSpace+"  http://www.nawouak.net");
   agStream.writeLine(agSpace+"*/");
   agStream.writeLine();
   agStream.writeLine(agSpace+"// Represents '"+javaName()+"'.");
   agStream.writeLine();

   // Guardian //
   agStream.writeLine(agSpace+"#ifndef "+guardian());
   agStream.writeLine(agSpace+"#define "+guardian());
   agStream.writeLine();

   // B++ Part //
   agStream.writeLine(agSpace+"#include <bpp/java.hpp>");

   // Includes //
   lcIterator=includes().iterator();

   while (lcIterator.hasNext()) {
    lcClass=(JirkClass)lcIterator.next();

    if (!lcClass.javaName().equals(javaName()))
     agStream.writeLine(agSpace+"#include <"+lcClass.file()+".hpp>");
   }

   agStream.writeLine();
   agStream.writeLine(agSpace+"#ifdef JIRK_DLL");
   agStream.writeLine(agSpace+" #define dll_export DLL_EXPORT");
   agStream.writeLine(agSpace+"#else");
   agStream.writeLine(agSpace+" #define dll_export DLL_IMPORT");
   agStream.writeLine(agSpace+"#endif");

   // Dependencies First Declaration //
   lcIterator=dependencies().iterator();

   while (lcIterator.hasNext()) {
    lcClass=(JirkClass)lcIterator.next();

    if (!lcClass.javaName().equals(javaName()) && !includes().contains(lcClass)) {
     agStream.writeLine();
     lcClass.writeForwardDeclaration(agStream,agSpace);
    }
   }

   agStream.writeLine();
   writeForwardDeclaration(agStream,agSpace);

   // Namespace //
   agStream.writeLine();
   agStream.writeLine(agSpace+openNamespace());
   agSpace+=" ";
   agStream.writeLine(agSpace+"using namespace ::bpp::java;");
   agStream.writeLine();
  }

  // Class Definition //
  agStream.writeLine(agSpace+"class "+shortName());
  writeLines(agStream,inheritance(),agSpace);

  // Constructors & Affectation Operators //
  agStream.writeLine(agSpace+" private_property static clInitializerMutex atMutex;");
  agStream.writeLine();
  agStream.writeLine(agSpace+" private_property static jyClass atClassPointer(void);");

  agStream.writeLine(agSpace+" private_property static tcString atClassName(void) "+
                     "{ return (\""+signature().substring(1,signature().length()-1)+"\"); }");

  agStream.writeLine(agSpace+" public_property "+shortName()+"(void) : clObject() {}");

  agStream.writeLine(agSpace+" public_property "+shortName()+"(const "+shortName()+" & agObject) "+
                     ": clObject() { clObject::copy(agObject); }");

  agStream.writeLine(agSpace+" public_property "+shortName()+"(jyObject agPointer) "+
                     ": clObject() { clObject::copy(agPointer); }");

  agStream.writeLine(agSpace+" public_property virtual tcString className(void) const "+
                     "{ return ("+shortName()+"::atClassName()); }");

  agStream.writeLine(agSpace+" public_property virtual jyClass classPointer(void) const "+
                     "{ return ("+shortName()+"::atClassPointer()); }");

  agStream.writeLine(agSpace+" public_property "+shortName()+" & operator = (const "+shortName()+
                     " & agObject) { clObject::copy(agObject); return (*this); }");

  agStream.writeLine(agSpace+" public_property "+shortName()+" & operator = (jyObject agPointer) "+
                     "{ clObject::copy(agPointer); return (*this); }");

  if (javaName().equals("java.lang.String")) {
   agStream.writeLine(agSpace+" public_property "+shortName()+"(tcString agString) "+
                      ": clObject() { clObject::copy(clContext().newString(agString)); }");

   agStream.writeLine(agSpace+" public_property "+shortName()+"(ctString & agString) "+
                      ": clObject() { clObject::copy(clContext().newString(agString.data())); }");

   agStream.writeLine(agSpace+" public_property "+shortName()+" & operator = (tcString agString) "+
                      "{ clObject::copy(clContext().newString(agString)); return (*this); }");

   agStream.writeLine(agSpace+" public_property "+shortName()+" & operator = (ctString & agString) "+
                      "{ clObject::copy(clContext().newString(agString.data())); "+
                      "return (*this); }");

   agStream.writeLine(agSpace+" public_property clString native(void) const;");

   agStream.writeLine(agSpace+" public_property static jyObject j_new(tcString agString) "+
                      "{ return (clContext().newString(agString)); }");

   agStream.writeLine(agSpace+" public_property static jyObject j_new(ctString & agString) "+
                      "{ return (clContext().newString(agString.data())); }");
  }

  agStream.writeLine();
  agStream.writeLine(agSpace+" #ifdef VIRTUAL_INHERITANCE_BUG");

  agStream.writeLine(agSpace+"  public_property virtual ~"+shortName()+"(void) "+
                     "{ unregisterObject(atJniPointer); atJniPointer=nil; }");

  agStream.writeLine(agSpace+" #endif");

  // Attributes Declaration //
  lcCounter=0;

  while (lcCounter<attributes().size()) {
   agStream.writeLine();
   lcAttribute=(JirkAttribute)attributes().elementAt(lcCounter);
   writeLines(agStream,lcAttribute.declaration(),agSpace);
   ++lcCounter;
  }

  // Constructors Declaration //
  if (constructors().size()>0) agStream.writeLine();
  lcCounter=0;

  while (lcCounter<constructors().size()) {
   lcConstructor=(JirkConstructor)constructors().elementAt(lcCounter);
   writeLines(agStream,lcConstructor.declaration(),agSpace);
   ++lcCounter;
  }

  // Methods Declaration //
  if (methods().size()>0) agStream.writeLine();
  lcCounter=0;

  while (lcCounter<methods().size()) {
   lcMethod=(JirkMethod)methods().elementAt(lcCounter);
   writeLines(agStream,lcMethod.declaration(),agSpace);
   ++lcCounter;
  }

  // Class Closing //
  agStream.writeLine(agSpace+"};");

  // Inner Classes Declaration //
  lcCounter=0;

  while (lcCounter<classes().size()) {
   agStream.writeLine();
   lcClass=(JirkClass)classes().elementAt(lcCounter);
   lcClass.writeDeclaration(agStream,agSpace);
   ++lcCounter;
  }

  // End //
  if (!isInner()) {
   agSpace=agSpace.substring(0,agSpace.length()-1);
   agStream.writeLine(agSpace+closeNamespace());
   agStream.writeLine();
   agStream.writeLine("#undef dll_export");
   agStream.writeLine(agSpace+"#endif");
  }
 }
 //----------------------------------------------------------------------------------WriteDefinition
 /*METHOD JirkClass */ /* Writes the Jirk++ definition of the class into a stream. */
 public void writeDefinition(OutputTextStream agStream,String agSpace) {
  JirkAttribute   lcAttribute;
  JirkClass       lcClass;
  JirkConstructor lcConstructor;
  int             lcCounter;
  Iterator        lcIterator;
  Vector          lcLineS;
  JirkMethod      lcMethod;

  if (!isInner()) {
   // Head //
   agStream.writeLine(agSpace+"/*");
   agStream.writeLine(agSpace+"  Automatically generated by");
   agStream.writeLine(agSpace+"  B u i l d   J i r k");
   agStream.writeLine(agSpace+"  "+bpp.JavaInformation.copyright());
   agStream.writeLine(agSpace+"  Bruno Bachelet");
   agStream.writeLine(agSpace+"  bruno@nawouak.net");
   agStream.writeLine(agSpace+"  http://www.nawouak.net");
   agStream.writeLine(agSpace+"*/");
   agStream.writeLine();
   agStream.writeLine(agSpace+"// Represents '"+javaName()+"'.");
   agStream.writeLine();

   // DLL Instruction //
   agStream.writeLine(agSpace+"#define JIRK_DLL");
   agStream.writeLine();

   // Includes //
   lcCounter=0;
   lcIterator=dependencies().iterator();

   while (lcIterator.hasNext()) {
    lcClass=(JirkClass)lcIterator.next();

    if (!lcClass.javaName().equals(javaName())) {
     agStream.writeLine(agSpace+"#include <"+lcClass.file()+".hpp>");
     ++lcCounter;
    }
   }

   agStream.writeLine(agSpace+"#include <"+file()+".hpp>");
   agStream.writeLine();
   agStream.writeLine(agSpace+"#define dll_export DLL_EXPORT");
   agStream.writeLine();

   // Namespace //
   agStream.writeLine(agSpace+openNamespace());
   agSpace+=" ";
   agStream.writeLine(agSpace+"using namespace ::bpp::java;");
   agStream.writeLine();
  }

  // Class Pointer //
  agStream.writeLine(agSpace+"property clInitializerMutex "+shortName()+"::atMutex;");
  agStream.writeLine();
  agStream.writeLine(agSpace+"property jyClass "+shortName()+"::atClassPointer(void) {");
  agStream.writeLine(agSpace+" static jyClass lcClassPointer;");
  agStream.writeLine(agSpace+" if (atMutex.tryLock()) {");
  agStream.write(agSpace+"  lcClassPointer=clContext().globalClass(clContext()");
  agStream.writeLine(".getClass(atClassName()));");
  agStream.writeLine(agSpace+"  atMutex.release();");
  agStream.writeLine(agSpace+" }");
  agStream.writeLine(agSpace+" return (lcClassPointer);");
  agStream.writeLine(agSpace+"}");

  // Native String //
  if (javaName().equals("java.lang.String")) {
   agStream.writeLine();

   agStream.writeLine(agSpace+"property clString jaString::native(void) const "+
                      "{ return (clContext().getString(jniPointer())); }");
  }

  // Inner Classes Definition //
  lcCounter=0;

  while (lcCounter<classes().size()) {
   agStream.writeLine();
   lcClass=(JirkClass)classes().elementAt(lcCounter);
   lcClass.writeDefinition(agStream,agSpace);
   ++lcCounter;
  }

  // Attributes Definition //
  lcCounter=0;

  while (lcCounter<attributes().size()) {
   agStream.writeLine();
   lcAttribute=(JirkAttribute)(attributes().elementAt(lcCounter));
   writeLines(agStream,lcAttribute.definition(),agSpace);
   ++lcCounter;
  }

  // Constructors Definition //
  lcCounter=0;

  while (lcCounter<constructors().size()) {
   agStream.writeLine();
   lcConstructor=(JirkConstructor)(constructors().elementAt(lcCounter));
   writeLines(agStream,lcConstructor.definition(),agSpace);
   ++lcCounter;
  }

  // Methods Definition //
  lcCounter=0;

  while (lcCounter<methods().size()) {
   agStream.writeLine();
   lcMethod=(JirkMethod)(methods().elementAt(lcCounter));
   writeLines(agStream,lcMethod.definition(),agSpace);
   ++lcCounter;
  }

  // End //
  if (!isInner()) {
   agSpace=agSpace.substring(0,agSpace.length()-1);
   agStream.writeLine(agSpace+closeNamespace());
  }
 }
 //--------------------------------------------------------------------------------GenerateHierarchy
 /*METHOD JirkClass */
 /* Generates the Jirk++ hierarchy representing a given list of Java classes and the classes
    they depend on. If the Jirk++ representation of a Java class already exists, nothing is done.
    Only the missing classes are added to the Jirk++ hierarchy. */
 public static void generateHierarchy(String agPath,TreeSet agToGenerateS,
                                      OutputTextStream agStream,
                                      boolean agDisplayed) throws Exception {
  JirkClass      lcClass;
  OutputTextFile lcFile;
  File           lcPath;

  TreeSet lcAlreadyGeneratedS = new TreeSet();
  String  lcRoot              = agPath+"/";

  if (agDisplayed) agStream.writeLine("[>] Generating Jirk++ Hierarchy...");

  while (agToGenerateS.size()>0) {
   lcClass=(JirkClass)agToGenerateS.first();
   agToGenerateS.remove(lcClass);
   lcAlreadyGeneratedS.add(new JirkClass(lcClass));
   lcClass.build(null,null,agToGenerateS,lcAlreadyGeneratedS);

   if (!lcClass.isPrimitive() && !lcClass.isArray()) {
    lcPath=new File(lcRoot+lcClass.folder());
    if (!lcPath.exists()) lcPath.mkdirs();
    lcPath=new File(lcRoot+lcClass.file()+".hpp");

    if (!lcPath.exists()) {
     if (agDisplayed) agStream.writeLine("    "+lcClass.javaName());
     lcFile=new OutputTextFile(lcRoot+lcClass.file()+".hpp");
     lcClass.writeDeclaration(lcFile,"");
     lcFile=new OutputTextFile(lcRoot+lcClass.file()+".cpp");
     lcClass.writeDefinition(lcFile,"");
     lcFile=null;
    }
   }
  }
 }
}

// End //-------------------------------------------------------------------------------------------