

import Jakarta.util.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URI;
import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import java.util.EmptyStackException;
import java.util.Vector;

//**************************************************
// Class AstList
//**************************************************
    
public abstract class AstList extends  AstNode {

    public  AstNode last; // reference to last node on list

    public AstList() {
        // initialize all the standard stuff
        arg     = new  AstNode[1];
        arg[0]  = null;
        last    = null;
    }

    public Object clone() {
        AstList copy;

        copy = null;
        try {
            copy = ( AstList ) getClass().newInstance();
        }
        catch ( Exception e ) {
            fatalError( "Can't clone "+getClass() + e.getMessage() );
        }
        super.initClone( copy );
        initClone( copy ); // last + element up pointers
        return ( copy );
    }

    protected void initClone( AstList copy ) {
        AstNode l = copy.arg[0];

        // Step 1: if the list is empty, exit

        copy.last = null;
        if ( l==null )
            return;

        // Step 2: foreach element on the list, set its up pointer
        //         and find the last element on the list

        l.up = copy;
        while ( l.right != null ) {
            l.up = copy;
            l = l.right;
        }

        // Step 3: found last element - set its up pointer and
        //         set last

        l.up = copy;
        copy.last = l;
    }

    // printorder() normally returns a boolean array that indicates how
    // tokens and arguments are to be interlaced when printing.  AstList
    // nodes have a special printing algorithm, and thus printorder should
    // never be called

    public boolean[] printorder() {
        fatalError( "shouldn't call AstList.printorder()" );
        return ( new boolean[0] );
    }

    // dumpnode() displays the pointer contents (up, left, right, args, last)
    // of a list and its descendents.

    public void dumpnode() { // dump list head node
        AstNode l;

        super.dumpnode();

        //         if (last == null)
        //             ps.println("\tlast:*");
        //         else
        //             ps.println("\tlast:"+last.hashCode());

        if ( arg[0] == null )
            return;
        for ( l=arg[0].right; l!=null; l=l.right )
            l.dumpnode();
    }

    // Delete() will delete the list.  Normally, this simply means
    // discarding all elements of the list (by setting the arg[0] and last
    // pointers to null.  However, if the list itself is an element of
    // another list, i.e., the up is an instanceof AstListNode, then up
    // is deleted (i.e., that element is removed from the enclosing list).

    public void Delete() { // delete the list

        // Step 1: if this list is an element of list, simply delete the
        //         element

        if ( up instanceof  AstListNode ) {
            ( ( AstListNode ) up ).Delete();
            return;
        }

        // Step 2: else remove all children by setting the arg[0], last
        //         references to null

        arg[0] = null;
        last   = null;
    }

    // add(x) adds AstListNode x to the end of the list.  add is generally
    // called only during parsing, and typically is not called by Jakarta
    // users.

    // add x to end of this list
    public  AstList add( AstListNode x ) {
        if ( x == null )
            return ( AstList ) this;

        // Remove x from old list, if it's on one.
        if ( x.up != null )
            x.Delete();

        // Insert x into new list
        if ( last == null ) {
            // empty list
            last    = x;
            arg[0]  = x;
            x.right = null;
            x.left  = null;
        }
        else {
            last.right = x;
            x.left     = last;
            last       = x;
        }
        x.up    = this;
        return ( AstList ) this;
    }

    // same as add() below, except that a separator is introduced
    // between lists.

    public  AstList add( AstList newList, String separator ) {

        // Step 1: if newList is null do nothing

        if ( ( newList == null ) || ( newList.last == null ) )
            return ( AstList ) this;

        // Step 2: newList isn't null -- add a separator in
        //         front of the list

        newList.arg[0].tok = new  AstTokenInterface[1];
        newList.arg[0].tok[0] = new  AstToken().setParms( "",separator,0 );

        // Step 3: compose the lists

        return add( newList );
    }

    public  AstList add( AstList newList ) {
        AstNode n;

        // Test if new list empty
        if ( ( newList == null ) || ( newList.last == null ) )
            return ( AstList ) this;

        // Change parent of inserted list nodes
        for ( n = newList.arg[0]; n != null; n = n.right )
            n.up = this;

        if ( last == null ) {
            // original list is empty
            arg[0] = newList.arg[0];
        }
        else {
            last.right = newList.arg[0];
            newList.arg[0].left = last;
        }
        last = newList.last;

        // Remove any dangling pointers from old list
        newList.arg[0] = null;
        newList.last = null;

        return ( AstList ) this;
    }

    // Same as add() but adds at the head of the list.
    public  AstList addHead( AstListNode x ) {
        x.left = null;
        x.right = arg[0];
        x.up = this;
        arg[0] = x;
        if ( last == null )
            last = x;
        return ( AstList ) this;
    }

    public  AstList addHead( AstList newList ) {
        AstNode n;

        // Test if new list empty
        if ( ( newList == null ) || ( newList.last == null ) )
            return ( AstList ) this;

        // Change parent of inserted list nodes
        for ( n=newList.arg[0]; n != null; n = n.right )
            n.up = this;

        if ( last == null ) {
            // original list is empty
            last = newList.last;
        }
        else {
            newList.last.right = arg[0];
            arg[0].left = newList.last;
        }
        arg[0] = newList.arg[0];

        // Remove any dangling pointers from old list
        newList.arg[0] = null;
        newList.last = null;

        return ( AstList ) this;
    }

    // print() prints the list.  This is a special method that overrides
    // the generic method of AstNode.  The same thing holds for reduce2ast()
    // and the reduce2java() methods.

    public void print() {
        AstNode l;

        // Step 1: return if the list is empty

        if ( arg[0] == null )
            return;

        // Step 2: print the first node differently than the rest

        arg[0].arg[0].print();
        for ( l = arg[0].right; l != null; l = l.right ) {
            l.Print_Only_Tokens();
            l.arg[0].print();
        }
    }

    public void print( AstProperties props ) {
        AstNode l;

        // Step 1: return if the list is empty

        if ( arg[0] == null )
            return;

        // Step 2: print the first node differently than the rest

        // Note: the following code assumes that elements of a list
        //       can reference null constructs - this probably is
        //       wrong, but we're going with it now.  Probably what
        //       needs to be done is to normalize the list so that
        //       union (a,b) (c,d) is (a,b,c,d) rather than (a,b,(c,d)).
        //       see original version of AstList.java - it probably
        //       is right given that lists are normalized...

        for ( l = arg[0]; l != null; l = l.right ) {
            if ( l.arg[0] == null )
                continue;
            l.arg[0].print( props );
            l = l.right;
            break;
        }
        for ( ; l != null; l = l.right ) {
            if ( l.arg[0] == null )
                continue;
            l.Print_Only_Tokens( props );
            l.arg[0].print( props );
        }
    }

    public void reduce2java( AstProperties props ) {
        AstNode l;

        // Step 1: return if the list is empty

        if ( arg[0] == null )
            return;

        // Step 2: print the first node differently than the rest

        // Note: the following code assumes that elements of a list
        //       can reference null constructs - this probably is
        //       wrong, but we're going with it now.  Probably what
        //       needs to be done is to normalize the list so that
        //       union (a,b) (c,d) is (a,b,c,d) rather than (a,b,(c,d)).
        //       see original version of AstList.java - it probably
        //       is right given that lists are normalized...

        for ( l = arg[0]; l != null; l = l.right ) {
            if ( l.arg[0] == null )
                continue;
            l.arg[0].reduce2java( props );
            l = l.right;
            break;
        }
        for ( ; l != null; l = l.right ) {
            if ( l.arg[0] == null )
                continue;
            l.Print_Only_Tokens( props );
            l.arg[0].reduce2java( props );
        }
    }

    public void PDump( String indent ) {
        int     i;
        AstNode l;
        String  id;

        System.out.println( indent + indent.length() + className() );
        id = indent+" ";
        for ( l = arg[0]; l != null; l = l.right )
            l.PDump( id );
    }

    public  AstNode addComment( String comment ) {
        return ( addComment( comment, false ) );
    }

    public  AstNode addComment( String comment, boolean replace ) {
        AstNode l;

        for ( l=arg[0]; l != null; l = l.right )
            if ( l.arg[0] != null )
                if ( l.arg[0].addComment( comment, replace ) != null )
                    return ( AstNode ) this;
        return ( null );
    }
}
