/*
 * Trans.java	1.00 2002/03/23 'Southern Arava'
 * Newest version available at http://paneris.org/~peterk/trans/
 * Working with JavaCC 2.1, 2.0, 1.2
 *
 * Copyright (C) 2000-2002 by Peter Kehl
 * Bussiness contact only: peterk (@) paneris.org
 *
 * The author accepts no responsibility for the use of this software and
 * provides it on an ``as is'' basis without express or implied warranty.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and due credit is given
 * to the original author and the contributors.
 *
 * This program 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.
 *
 * Credits
 *
 * Author used this class in translator from VHDL-AMS to C++ that is
 * available at http://paneris.org/~peterk/vhdlams/
 * It consists of about 100 productions and language itself is 'interesting'.
 *
 * Author has Java, DHTML, scripting and C++ experience in
 * Israel, UK, Germany and Slovakia.
 *
 */

import java.util.*;
import java.io.*;

/**
Trans is a <i>container of indented text objects</i> used alone or with
JavaCC tool from <a href="http://www.webgain.com/">WebGain</a>
<i>for easy and rapid design
and development of high-to-high language translators, scripting and
template engines,</i> macro expanders, format transformers...

<h3>Profit for JavaCC teams</h3>
<ul>
<li> Team development involves <i>distribution of production groups to
 responsible authors</i>, collecting those parts and updating whole grammar.
<li> <code>'Trans</code><i>formized'</i> grammar seems hard to read, because it's
 <i>packed and powerfull.
 If good source code standards are used, it is consistent and easy to change.</i>
 Jjdoc-generated html grammar tree is used for orientation in whole grammar.
</ul>

<h3>General use</h3>
<ul>
<li> <i>Compose container object</i>
 by concatenating/adding strings, tokens, identifiers
 and other containers - subnodes. Content of added item starts on new line,
 concatenated one on last line of previous block. Four general methods and their
 partners for all primitive types <nobr>are used:</nobr>
 <nobr><code>add(Object), conc(Object),</nobr> <nobr>preadd(Object),</nobr> <nobr>preconc(Object)</code></nobr>
<li> <i>Any other objects can be put</i>, result of their <code>toString()</code> is
 <i>indented by depth of their level</i>
 and <i>newline character(s) are replaced by system newline character(s).</i>
<li> <i>Constructed main container object is transformed
 by</i> <code>toString()</code> and saved to file, written from servlet to TCP packet...
<li> <i>Container can be de/serialized and transported by RMI.</i>
<li> <i>Code is short and powerfull,</i> because <i>add/conc functions
 support concatenated calls</i> (as I/O stream operators in C++)
 by returning <code>this</code> <nobr>Trans object.</nobr><br>
 <nobr><code>ret.add( "//Assignment on next line" ).</nobr><nobr>conc( arg1 ).conc( "=" ).conc( arg2 );</code></nobr>
<li><i>Parameter of add/conc methods parameter can be null</i>,
 then it's not put to container.
<li> <i><code>preadd(..), preconc(..)</code> should be used with care and not very
 often</i>, otherwise they decrease readability. 'Local root' container
 of unshifted subcontainers is used for more complicated compositions.
<li> <i>Elements are put by reference, not by value.</i> Their string representation
 is evaluated during call to <nobr><code>toString(),</nobr> <nobr>write(..),</nobr>
 <nobr>writeFile(String).</code></nobr>
 So if an object is put to Trans container and its value/representation is changed later
 (possibly when it was put again to the same or another container), it will be
 transformed to its actual - latest representation in all places where it appeared.
 This can be used for
 <i>'reservation' - allocated Trans subitem is put
 to main Trans 'stream', and its content is filled later.</i> However, <i>the reference
 mustn't be changed, otherwise we 'loose the place'.</i>
 If reservated subitems are commented and not used often, they increase
 readability of code, otherwise its mortality.
<li> <code>equals(Object)</code> checks whether parameter is Trans and has
 equal text represenation. Because result of toString() is uncachable, this
 <i>shouldn't be used often.</i>
<li> It's usefull to define standard methods and objects for often used containers,
 as <nobr><code>Trans empty(), unshifted(), error(String), unfinished;</code></nobr>
 <br>If those functions and objects were static, this involved typing long
 <code>Trans.empty()...</code> or subclassing Trans. So it's left up to user.
<li> Add/conc methods for primitive values put their String representation to container.
<li> <i>Constructor with arguments of primitive types are not defined,</i>
 because <code>Trans(int)</code> is used to specify offset other than default.
 <code>Trans(Object)</code> is used to specify first Object, String or Trans item.
<li> <i>Trans is independent of JavaCC</i> (does anyone know better Compiler Compiler?)
</ul>

<h3>Specific use with JavaCC</h3>
<ul>
<li> JavaCC grammar "methods" - <i>production rules return Trans or
 a customized class,</i> or a primitive type. Unused/unimplemented statements
 can return <code>void</code>.
<li> <i>Standard name of returned container is usefull,
 as</i> <nobr><code>Trans ret= new Trans().</code> Constructor for <code>ret</code></nobr>
 is required if it is used first time in optional or zero times repeatable statement.
<li> Assign tokens and results of productions to variables of type Token
 and Trans.
<li> <i>For safety, return constructed container</i> <code>ret</code>
 <i>always from end of production.</i> Even if translation result is
 finally known before getting to end, where next optional
 statements are 'not important' or actual statement can be
 repeated more time, result must be returned after parsing those parts. Otherwise,
 translator will 'sometimes' fails when those 'unimportant' parts appear,
 because parser won't consume them after production method already returned.
<li> '<code>Trans</code><i>formize</i>' higher statements first,
 <i>starting from root</i> and its direct substatements, to
 develop and debug root parts. <i>And/Or</i> write debugging results
 to standard output from leaf statements if <i>designing
 customized classes for them first,</i> or when using <i>up-down approach</i>.
<li> <i>Call JavaCC generated method for main production</i>
 with stream parameter from your Java application, and use generated
 Trans container it returnes.
<li> <code>Token</code> objects are not handled specially by default, as they
 have good default <code>toString()</code> method.
<li> When translating from case-unsensitive language to case-sensitive,
 change <code>Token.toString()</code> to return
 <code>item.toLowerCase()</code> or <code>item.toUpperCase()</code>, according
 to contract you choose.
<li>There is possibility for faster <code>equals(Object)</code> that compares
 container's items as objects. See comments in source.
</ul>

<h3>Trans compared to Jjtree (and partially to JTB)</h3>
<ul>
<li> <i>1 source file</i> - execution flow code is a part of grammar definition
<li> Jjtree makes a lot of generated files, hard to manage. If they're
 customized and grammar is changed later (split, merged, updated...),
 they must be compared and fixed.
<li> <i>no Jjtree typecasting neither elements accessed by indices</i>
<li> Processing optional/repeated grammar statements in Jjtree generated files
 involves writing a conditional/loop code that checks and follows those
 statements. <i>Trans uses java code specified around grammar statements,</i>
 executed only if those statements are parsed. <i>Tokens and substatements
 are simply accessed and type safe.</i>
<li> <i>Trans leaves semantic checking and optimisation </i>
 for target language compiler/interpreter.
 <i>Specialised classes carry additional information</i>
 between a set of productions - 'local contract' -
 when returned up or passed to called productions as parameters. They
 define interface between separated parts and support team work.
<li> Similiar classes containing execution flow information suit for
 <i>interpreters.</i> Their anonymous subclasses (closures) are defined directly
 in grammar file, carring parsed fields without need to
 create another class with its API (constructor parameters, field names...).
</ul> <p>

*/

public class Trans implements Serializable, Cloneable {

/** System newline character(s) that are used when Trans is transformed
to String. It can changed by user. Its value is used during calls to
 <code>toString(), write(..)</code>, not when containers are composed.
*/
   static public final String cr= System.getProperty("line.separator","\n");

/** Internal flag object for concatenation of items, never to be printed itself
 neither used by user.
 Next element will be concatenated to previous one, its
 first line will be concatenated to last line of previous text. Otherwise,
 when an object is added to container, its text starts on new line.
 Nothing wrong happens if it's added several times in sequence in subclassed code
 - then whole sequence of references is treated as one, that 'concatenates'
 last line of previous and first line of next object.
 It's resolved to its actual static representation at deserialization.
*/
   static protected final Trans concatenate= new Concatenate();

/** Default number of spaces before each nested level for all new containers.
 Its value can be changed by user (initially 3).
@see #Trans(int). */
   static public int defaultOffset= 3;

/** Number of spaces written before each line of this container.
*/
	protected int offset= defaultOffset;

/** Physical container. No need for synchronized Vector.
User can synchronize on Trans object itself.
*/
   protected ArrayList list= new ArrayList();

/** Creates container with default offset.
@see #defaultOffset
*/
	public Trans() {}

/** Creates container with specified offset.
@param offs sets number of spaces before each line of this container
*/
	public Trans( int offs)	{ offset= offs;}

/** Created container with default offset and specified item, if not null.
@param obj first item
*/

	public Trans( Object obj) {
      if( obj!=null)
         list.add( obj);
	}

/** Adds object to the end of container, if not null. Its content
 is indented and starts on
 new line. Next item starts on new line, unless it's concatenated.
 @param obj item to be added
*/
	public Trans add( Object next)
	{
      if( next!=null)
   		list.add( next);
		return this;
	}

/** Adds object at the first place of container, if not null.
 Its content is indented and
 starts on new line. Former first object starts on new line, so their contents
 are not concatenated together. Name "preadd" is more clear than "ins" or "insert",
 because it seemed like 'insert it here' in streamed call. Try:
<p><code>new Trans("Third line").preadd("Second line").preadd("First line")</code>
 @param obj item to be added.
*/

   public Trans preadd( Object next)
   {
      if( next!=null)
         list.add( 0, next);
      return this;
   }

/** Object is concatenated, if not null. Whole content is indented
 except its first line, that is appended to last line of previous item -
 concatenated.
@param obj item to be added at end of container
*/
	public Trans conc( Object next)
	{
      if( next!=null) {
         if( list.size() >0)
            // If concatenate was always added, preadd() didn;t work correctly.
            list.add( concatenate);
		   list.add( next);
      }
		return this;
	}

/** Object is preconcatenated, if not null.
 Whole content is indented and it starts on new line.
 Last line is concatenated in front of first line of former first object.
 @param obj item to be added at first place of container
*/

   public Trans preconc( Object next)
   {
      if( next!=null) {
         list.add( 0, next);
         list.add( 1, concatenate);
      }
      return this;
   }

/** Adds system newline character(s). In fact, it adds empty string,
 and <code>write(..)</code> engine puts there <code>cr</code> newline
 character(s) when it is called.
*/
   public Trans newLine() {
      list.add( "" );
      return this;
   }

   public Trans add(boolean par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(boolean par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(boolean par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(boolean par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(byte par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(byte par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(byte par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(byte par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(char par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(char par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(char par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(char par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(short par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(short par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(short par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(short par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(int par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(int par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(int par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(int par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(long par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(long par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(long par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(long par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(float par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(float par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(float par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(float par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

   public Trans add(double par) {
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preadd(double par) {
      list.add( 0,String.valueOf(par) );
      return this;
   }
   public Trans conc(double par) {
      if( list.size() >0) list.add( concatenate);
      list.add( String.valueOf(par) );
      return this;
   }
   public Trans preconc(double par) {
         list.add( 0, String.valueOf(par) );
         list.add( 1, concatenate);
         return this;
   }

/** Transforms to String, no first level offset made in this function, so
 direct items aren't indented. It can't be cached,  see <code>equals(Object).</code>
 @see #equals(Object)
*/
   public String toString() {
      synchronized( getClass() ) { //Because it uses static cr.
         return write( new StringBuffer(), -offset, false).toString();
      }
   }


/**Transforms container's content - all its items to String in recursion,
 managing indentation level and concatenation.
 Transforms alone characters <code>"\n", "\r"</code> and couples of
 <code>"\n\r", "\r\n"</code>to one presence of system
 newline character(s). Couples are transformed together only if they are
 printed from the same Object with its <code>toString()</code>.
@param buffer Buffer of upper container (caller), ending by newline character.
@return Passed<code>buffer</code> with added content, newline character at
 end of each line, but not of last one (so concatenation can be made
 to it by the successor).
*/
	protected StringBuffer write( StringBuffer buffer, int offs, boolean conc)
/* Because this is protected, it doesn't synchronise( getClass() ) for efficiency,
 even it uses static cr. Thus all methods that call it should synchronize this way.
*/
	{
		int c= list.size()-1;

		for( int i=0; i<= c; i++)
		{
			Object what= list.get( i);
			Object prev, next;

         if( what==null) //Hm, can this happen? Some old code, oldies are good.
            continue;

			if ( what instanceof Trans)
			{
				Trans twhat= (Trans) what;
				if( twhat==concatenate )
					continue;

				if( i>1 && ((prev= list.get(i-1)) instanceof Trans) &&
             prev==concatenate || i==0 && conc)
					twhat.write( buffer, offset+offs, true);
				else
					twhat.write( buffer, offset+offs, false);
			}
			else // not a Trans, call its toString(), parse to lines and shift them
			{
            boolean concFirstLine=
				   i>1 && ((prev= list.get(i-1)) instanceof Trans) &&
               prev==concatenate || i==0 && conc;

            char spcs[]= new char[offset+offs];
            for( int j=0; j<offset+offs; j++)
               spcs[j]= ' ';

            String spaces= new String(spcs);

            String crlf= ""; //Consists of first of "\r\n" or "\n\r" couple char
				StringTokenizer toker= new StringTokenizer(
               what.toString(), "\n\r", true);
            while( toker.hasMoreElements() ) {
               String line= toker.nextToken();
               if( !line.equals( "\n") && !line.equals("\r") ) {
                  if(!concFirstLine)
      					buffer.append(spaces);
                  buffer.append( line );
               }
               else
                  if( !(crlf.equals("\n") && line.equals("\r")) &&
                       !(crlf.equals("\r") && line.equals("\n")) ) {
                     buffer.append(cr);
                     crlf= line;
                  }
                  else
                     crlf= "";
               concFirstLine= false;
            } //while
			}

			if( i<c && ( !((next= list.get(i+1)) instanceof Trans) || next!=concatenate ))
					buffer.append( cr);
		} // for

		return buffer;
	}

/** Writes content to end of passed buffer, and appends system newline
  character(s) to it.
 @param buffer Buffer to add content.
 @return Passed <code>buffer</code> with added content.
 @see #write(StringBuffer,int,boolean)
*/
	public StringBuffer write( StringBuffer buffer)
	{
      synchronized( getClass() ) { //Because it uses static cr.
   		return write( buffer, 0, false).append( cr);
      }
	}

/** Writes content to file, and appends system newline
  character(s) to it.
 @param file File to write content.
*/
   public void writeFile( String name) throws IOException {
         FileOutputStream fout= new FileOutputStream( name);
         PrintStream out= new PrintStream (fout);
         out.print( write(new StringBuffer() ) );
         out.flush();
         fout.close();
   }

/** Compares whether parameter is also Trans and their toString() representation
 is same. This is usefull because default equals() checks equality
 of references. Implementation uses result of <code>toString()</code>
 and it can't be cached, because Trans subnodes can be accessed and
 changed later by <i>'reservation'.</i> There is no way to cache
 equality or object status in principle. Unchanged <code>hashCode()</code>
 means only that object probably didn't change. Anyway, it's not used
 often. See source for faster restricted version that compares subnodes as Objects.
<p><i>User can redefine <code>equals(Object)</code> in any way</i>, as
 standard Trans mechanism that generates its string representation
 (<code>write(...)</code>) doesn't use it.
 */

   public boolean equals( Object obj) {
      return obj instanceof Trans && toString().equals( obj.toString() );
      //Faster restricted JavaCC version, see also notes below:
      //return obj instanceof Trans && list.equals( ((Trans)obj).list );
   }

/*
 Commented faster version of equals(Object)
 It compares subnodes as Objects, and is usefull for comparing
 leaf Trans nodes, consisting of identifiers and fixed strings.
 It requires commented version of hashCode(), see java.lang.Object.

 It needs specific changes when used in JavaCC:
 <ul>
  <li>To define <code>equals()</code> and <code>hashCode()</code>
   methods for class <code>Token</code> that call the same methods on
   Token's <code>item</code> field.
  <li>Or to define other methods <code>add(Token), conc(Token)...</code>
   that add/conc/.. argument's <code>item</code> field. General methods
   <code>add(Object), conc(Object)...</code> don't check whether
   argument is of class <code>Token</code>, so they shouldn't be used in way
   <code>ResultTrans.add( (Object)SomeToken )</code>...
   Trans couldn't be used without JavaCC, or it required a dummy Token class then...
 <li>Commented version of <code>hashCode()</code> can be used then.
 </ul>
 @see #hashCode()
*/

/** Returns a constant (0). Otherwise it had to return <code>hashCode()</code>
of string representation generated by toString().
@see #equals(Object)
*/

   public int hashCode() {
      return 0;
      //For restricted equals():
      //return list.hashCode();
   }

//} // end of class Trans

/**
 Deserialized concatenate object is resolved by its actual static representation.
 Thus there is always exactly the one concatenate object in JVM, so references
 can be compared to it by operator <code>==</code> instead of slow
 <code>equals()</code>. */
   private static final class Concatenate extends Trans {
      Concatenate() { super("ERROR. CoNcAtEnAtE - never to be printed!!" ); }

      public Object readResolve() {
         return concatenate;
      }
   }
} //end of Trans
