/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program 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. 
 * 
 * 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. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package components;
import java.io.DataOutput;
import java.io.DataInput;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.io.IOException;
import java.io.PrintStream;
import sun.misc.Compare;

public 
class ConstantPool implements Compare {

    protected Hashtable	h;	// for "quick" lookup
    protected Vector	t;	// for enumeration in order
    protected int		n;
    protected ConstantObject constants[]= null;
    protected boolean	locked = false;
    public boolean   impureConstants = false;

    public ConstantPool(){
	h = new Hashtable( 500, 0.7f );
	t = new Vector();
	t.addElement(null ); // 0th element is string of length 0.
	n = 1;
    }
    
    /**
     * doCompare
     *   
     * @param  obj1 first object to compare.
     * @param  obj2 second object to compare.
     * @return -1 if obj1 > obj2, 0 if obj1 == obj2, 1 if obj1 < obj2.
     */  
    public int doCompare( java.lang.Object o1, java.lang.Object o2) {
        ConstantObject obj1 = (ConstantObject) o1;
        ConstantObject obj2 = (ConstantObject) o2;

        if (obj1.ldcReferences < obj2.ldcReferences) {
	   return 1;
        } else if (obj1.ldcReferences == obj2.ldcReferences)
           return 0;
        return -1;
    }

    public void doSort() {
	int count = 0;
	int nNew = 1;

	// Retrieve the ConstantObject array from the hashtable.
	Enumeration e = h.elements();
	ConstantObject arr[] = new ConstantObject[h.size()];
	while (e.hasMoreElements()) { 
	    arr[count]= (ConstantObject) e.nextElement();
	    count++;
	}

	// Sorting the ConstantObject with descending reference
	// count.	
	sun.misc.Sort.quicksort(arr, this);

	t.removeAllElements();
	t.addElement(null);
	for (int i = 0; i < arr.length; i++) {
	    arr[i].index = nNew;
	    nNew += arr[i].nSlots;
	    t.addElement(arr[i]);
	    for (int j =arr[i].nSlots; j > 1; j-- )
                t.addElement( null ); // place holder
	}
	constants = null;
    }

    /**
     * Return the ConstantObject in constant table corresponding to
     * the given ConstantObject s.
     * Inserts s if it is not already there.
     * The index member of the returned value (which
     * may be the object s!) will be set to its index in our
     * table. There will be no element of index 0.
     */
    public ConstantObject
    add( ConstantObject s ) {
	if ( s.shared && s.containingPool==this ) return s; // this is already it!
	ConstantObject r = (ConstantObject)h.get( s );
	if ( r == null ){
	    if ( locked ){
		throw new Error("add on locked ConstantPool");
	    }
	    if ( s.shared ){
		// already in another constant pool!
		// this copy is not deep enough!!!
		r = (ConstantObject) s.clone();
	    } else {
		r = s;
	    }
	    r.externalize( this ); // Deeper copy, as required.
	    r.index = n;
	    n += r.nSlots;
	    r.shared = true;
	    r.containingPool = this;
	    h.put( r, r );
	    t.addElement( r );
	    for ( int i =r.nSlots; i > 1; i-- )
		t.addElement( null ); // place holder.
	    constants = null; // mark any "constants" as obsolete!
	}
	return r;
    }

    /*
     * Like add() above, but with less management: still
     * to ensure that there's only one entry like this in the pool,
     * but doesn't externalize/share.
     */
   
    public ConstantObject
    appendElement( ConstantObject s ){
	ConstantObject r = (ConstantObject)h.get( s );
	if ( r == null ){
	    if ( locked ){
		throw new Error("add on locked ConstantPool");
	    }
	    r = s;
	    r.index = n;
	    n += r.nSlots;
	    h.put( r, r );
	    t.addElement( r );
	    for ( int i =r.nSlots; i > 1; i-- )
		t.addElement( null ); // place holder.
	    constants = null; // mark any "constants" as obsolete!
	} else { 
	    r.ldcReferences += s.ldcReferences;
	    r.references += s.references;
	}
	return r;
    }

    public ConstantObject
    dup( ConstantObject s ) {
	if ( s.shared && s.containingPool==this ) return s;
	return this.add( (ConstantObject) s.clone() );
    }

    public ConstantObject[]
    getConstants(){
	if ( constants != null )
	    return constants;
	constants = new ConstantObject[ t.size() ];
	t.copyInto( constants );
	return constants;
    }

    public Enumeration
    getEnumeration(){
	return t.elements();
    }

    public void
    lock(){ locked = true; }

    public void
    unlock(){ locked = false; }

    public int
    read( DataInput in ) throws IOException {
	int n = in.readUnsignedShort();
	ConstantObject c[] = new ConstantObject[n];
	for (int i = 1; i < n; i+=c[i].nSlots ){
	    c[i] = ConstantObject.readObject(in);
	}
	//System.err.println("DEBUG CONSTANTPOOL DUMP" );
	//for (int i = 1; i < n; i+=c[i].nSlots ){
	//    System.err.println("\t#"+i+"\t"+c[i].toString() );
	//}
	for (int i = 1; i < n; i+=c[i].nSlots ){
	    c[i].resolve( c );
	}
	for (int i = 1; i < n; i+=c[i].nSlots ){
	    add( c[i] );
	}
	constants = c; // IMPL_NOTE: consider whether this should be fixed 
	return n;
    }

    public void
    clearAllReferences(){
	ConstantObject c;
	for( int i=1; i< n; i+=c.nSlots){
	    c = (ConstantObject)t.elementAt(i);
	    c.clearReference();
	}
    }

    /*
     * If we are not loading entire classes, then there is
     * some chance that unreferenced constants have sneaked into
     * this pool. They can be deleted and the table made smaller.
     * This is a waste of time when partial class loading is not done.
     *
     * Naturally, we preserve the null entries.
     *
     */
    public void smashConstantPool(){
	int nNew = 1;
	ConstantObject o;
	// first, count and index.
	for ( int i = 1; i < n; i += o.nSlots ){
	    o = (ConstantObject)t.elementAt(i);
	    if ( o.references == 0 ){
		o.index = -1;
		h.remove( o );
	    } else {
		// we're keeping it.
		o.index = nNew;
		nNew += o.nSlots;
	    }
	}
	if ( nNew == n ) return; // all done!

	// copy live ones from old vector to new.
	Vector newConstants = new Vector( nNew );
	newConstants.addElement( null );
	for ( int i = 1; i < n; i += o.nSlots ){
	    o = (ConstantObject)t.elementAt(i);
	    if ( o.references != 0 ){
		// we're keeping it.
		newConstants.addElement(o);
		for ( int j =o.nSlots; j > 1; j-- )
		    newConstants.addElement( null ); // place holder.
	    }
	}
	t = newConstants;
	n = nNew;
	constants = null; // mark as obsolete
    }

    /**
     * Write out the number of constants to write.
     * Then write out the constants, in order.
     * Returns total number of constants written.
     */
    public int
    write( DataOutput o ) throws IOException {
	o.writeShort(n);
	ConstantObject ob;
	for (int i = 1; i < n; i+=ob.nSlots ){
	    ob = (ConstantObject)t.elementAt(i);
	    if ( ob != null )
		ob.write(o);
	}
	return n;
    }

    public void
    dump( PrintStream o ){
	ConstantObject c;
	for( int i=1; i< n; i+=c.nSlots){
	    c = (ConstantObject)t.elementAt(i);
	    o.println("\t["+c.index+"]\t"+c.references+"\t"+c.toString() );
	}
    }
}
