/*
 *   
 *
 * 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 jcc.Util;
import jcc.Const;
import util.*;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

//
// container for stuff about a class.
// can be in a classfile or in a module.
//

public
class ClassInfo
{
    public String		className;
    public int			access;
    public ClassConstant	thisClass;
    public ClassConstant	superClass;
    public ClassInfo		superClassInfo;

    // Tables for all fields, methods and constants of this class
    public FieldInfo		fields[];
    public MethodInfo		methods[];
    public ConstantObject	constants[];
    private ConstantObject	oldConstants[];
    public ConstantObject	symbols[];
    public ClassConstant	interfaces[];
    public FieldConstant	refFieldtable[];
    public MethodConstant	refMethodtable[];

    // In case we lay it all out here
    public FieldInfo		fieldtable[];
    public MethodInfo		methodtable[];
    public UnicodeConstant	fieldtableName;
    public UnicodeConstant	methodtableName;

    // Class attributes that we do not interpret
    public Attribute[]          classAttributes;
    public SourceFileAttribute  sourceFileAttr;

    public vm.ClassClass	vmClass; // used by in-core output writers

    public Vector		allInterfaces;

    protected boolean		verbose;
    protected PrintStream	log = System.out;
    public  ConstantPool	externalPool;
    public  static boolean      classDebug = false;

    public  int			flags;
    public static final int	INCLUDE_ALL = 1;

    public ClassInfo( boolean v ) {
	verbose = v;
	flags = INCLUDE_ALL; // by default, we want all members.
	// what else should be here?
    }

    private String genericNativeName;

    public final String getGenericNativeName() { 
        if (genericNativeName == null) 
	    genericNativeName = createGenericNativeName();
        return genericNativeName;
    }

    // This will be overridden by subclasses
    protected String createGenericNativeName() { 
        return Util.convertToClassName(className );
    }

    // Read in the constants from a classfile
    void readConstantPool( DataInput in ) throws IOException {
	int num = in.readUnsignedShort();

	if(verbose){
	    log.println(Localizer.getString("classinfo.reading_entries_in_constant_pool", Integer.toString(num)));
	}
	constants = new ConstantObject[num];
	for (int i = 1; i < num; i+=constants[i].nSlots) {
	    constants[i] = ConstantObject.readObject( in );
	    constants[i].index = i;
	}
    }

    private void resolveConstants( ) {
	if (verbose){
	    log.println(Localizer.getString("classinfo.>>>resolving_constants"));
	}
	for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
	    constants[i].resolve( symbols );
	}
    }

    
    protected void externalizeConstants( ConstantPool externalPool ){
	if (verbose){
	    log.println(Localizer.getString("classinfo.>>>externalizing_constants"));
	}
	// externalize immediately certain kinds of constants
	// we know to have no direct references.
	for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
	    switch ( constants[i].tag ){
	    case Const.CONSTANT_UTF8:
	    case Const.CONSTANT_NAMEANDTYPE:
		// unquestionably, share these.
		constants[i] = externalPool.add( constants[i] );
		// FALLTHROUGH
	    default:
		constants[i].externalize( externalPool );
		break;
	    }
	}
    }

    /*
     * If we are using an external string table, then
     * we can make our own table smaller. At this point,
     * all non-code references into it are by object reference, NEVER
     * by index -- everything has been resolved! Thus we can
     * compact our table, deleting all the UnicodeConstants.
     * We adjust each constant's index entry accordingly.
     * Naturally, we preserve the null entries.
     *
     */
    public void smashConstantPool(){
	int nOld = constants.length;
	int nNew = 1;
	ConstantObject o;
	// first, count and index.
	for ( int i = 1; i < nOld; i += o.nSlots ){
	    o = constants[i];
	    if ( ! o.shared ){
		if ( o.references == 0 ){
		    o.index = -1; // trouble.
		} else {
		    // we're keeping it.
		    o.index = nNew;
		    nNew += o.nSlots;
		}
	    }
	}
	// now reallocate and copy.
	ConstantObject newConstants[] = new ConstantObject[ nNew ];
	int j = 1;
	for ( int i = 1; i < nOld; i += o.nSlots ){
	    o = constants[i];
	    if ( (! o.shared ) && ( o.references != 0 ) ){
		// we're keeping it.
		newConstants[j] = o;
		j += o.nSlots;
	    }
	}
	oldConstants = constants;
	constants = newConstants;
    }

    // write constants back out, just like we read them in.
    void writeConstantPool( DataOutput out ) throws IOException {
	int num = constants==null ? 0 : constants.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.writing_constant_pool_entries", Integer.toString(num)));
	}
	out.writeShort( num );
	for (int i = 1; i < num; i+=constants[i].nSlots) {
	    constants[i].write( out );
	}
    }

    // Read the list of interfaces this class supports
    void readInterfaces( DataInput in ) throws IOException {
	int count = in.readUnsignedShort();
	if(verbose){
	    log.println(Localizer.getString("classinfo.reading_interfaces_implemented", Integer.toString(count)));
	}
	interfaces = new ClassConstant[count];
	for (int i = 0; i < count; i++) {
	    //interfaces[i] = (ClassConstant) symbols[in.readUnsignedShort()];
	    // interfaces not external -- they use own constant pool!
	    interfaces[i] = (ClassConstant) constants[in.readUnsignedShort()];
	}
    }

    void externalizeInterfaces( ConstantPool p ){
	int count = interfaces==null ? 0 : interfaces.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.>>>externalizing_interfaces_implemented"));
	}
	for (int i = 0; i < count; i++) {
	    interfaces[i] = (ClassConstant)p.dup( interfaces[i] );
	}
    }

    void writeInterfaces( DataOutput out ) throws IOException {
	int count = interfaces==null ? 0 : interfaces.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.writing_interfaces_implemented", Integer.toString(count)));
	}
	out.writeShort( count );
	for (int i = 0; i < count; i++) {
	    out.writeShort( interfaces[i].index );
	}
    }

    // Read the list of fields
    void readFields( DataInput in ) throws IOException {
	int count = in.readUnsignedShort();
	fields = new FieldInfo[count];
	if(verbose){
	    log.println(Localizer.getString("classinfo.reading_field_members", Integer.toString(count)));
	}

	for (int i = 0; i < count; i++) {
	    fields[i] = FieldInfo.readField(in, this);
	    fields[i].index = i;
	}
    }

    void externalizeFields( ConstantPool p ){
	int count = fields==null ? 0 : fields.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.>>>externalizing_field_members"));
	}
	for (int i = 0; i < count; i++) {
	    fields[i].externalize( p );
	}
    }

    void writeFields( DataOutput out ) throws IOException {
	int count = fields==null ? 0 : fields.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.writing_field_members", Integer.toString(count)));
	}
	out.writeShort( count );
	for (int i = 0; i < count; i++) {
	    fields[i].write( out );
	}
    }

    // Read the list of methods from classfile
    void readMethods( DataInput in, boolean readCode ) throws IOException {
	int count = in.readUnsignedShort();
	methods = new MethodInfo[count];

	if(verbose){
	    log.println(Localizer.getString(
			"classinfo.reading_methods", Integer.toString(count)));
	}
	for (int i = 0; i < count; i++) {
	    methods[i] = MethodInfo.readMethod( in, this, readCode );
	    methods[i].index = i;
	}
    }

    void externalizeMethods( ConstantPool p ){
	int count = methods==null ? 0 : methods.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.>>>externalizing_methods"));
	}
	for (int i = 0; i < count; i++) {
	    methods[i].externalize( p );
	}
    }

    void writeMethods( DataOutput out) throws IOException {
	int count = methods==null ? 0 : methods.length;
	if(verbose){
	    log.println(Localizer.getString("classinfo.writing_methods", Integer.toString(count)));
	}
	out.writeShort(count);
	for (int i = 0; i < count; i++) {
	    methods[i].write( out );
	}
    }

    void readAttributes( DataInput in ) throws IOException {
	int count = in.readUnsignedShort();
	Vector clssAttr = new Vector();
	
	if(verbose){
	    log.println(Localizer.getString("classinfo.reading_attributes", Integer.toString(count)));
	}

	for (int i = 0; i < count; i++) {
	    int nameIndex = in.readUnsignedShort();
	    int bytes = in.readInt();
	    UnicodeConstant name = (UnicodeConstant)symbols[nameIndex];
	    if (name.string.equals(/*NOI18N*/"fieldtable")){
		fieldtableName = name;
		if (verbose){
		    log.println(Localizer.getString("classinfo.reading_name",name));
		}
		int n = bytes / 2;
		refFieldtable = new FieldConstant[ n ];
		for( int j = 0; j < n; j++ ){
		    refFieldtable[j] = (FieldConstant)symbols[ in.readUnsignedShort() ];
		}
	    } else if  (name.string.equals(/*NOI18N*/"methodtable")){
		methodtableName = name;
		if (verbose){
		    log.println(Localizer.getString("classinfo.reading_name", name));
		}
		int n = bytes / 2;
		refMethodtable = new MethodConstant[ n ];
		for( int j = 0; j < n; j++ ){
		    refMethodtable[j] = (MethodConstant)symbols[ in.readUnsignedShort() ];
		}
	    } else if  (name.string.equals(/*NOI18N*/"SourceFile")) {
		if (ClassInfo.classDebug) {
		    UnicodeConstant srcName =
			(UnicodeConstant)symbols[in.readUnsignedShort()];
		    sourceFileAttr =
			new SourceFileAttribute(name, bytes, srcName);
		    clssAttr.addElement(sourceFileAttr);
		} else {
		    byte[] b = new byte[bytes];
		    in.readFully(b);
		    clssAttr.addElement
			(new UninterpretedAttribute(name, bytes, b));
		}
	    } else {
		byte[] b = new byte[bytes];
		in.readFully(b);
		clssAttr.addElement
		    (new UninterpretedAttribute(name, bytes, b));
	    }
	}
	int nattr = clssAttr.size();
	if (nattr > 0) {
	    this.classAttributes = new Attribute[nattr];
	    clssAttr.copyInto(classAttributes);
	}
    }

    void externalizeAttributes( ConstantPool p ){
	Attribute.externalizeAttributes(classAttributes, p);
    }

    public void
    allocateFieldsFromFieldtable(){
	if ( refFieldtable == null ) return; // no can do.

	int n = refFieldtable.length;
	int nsuper;
	int fieldoff = 0;
	nsuper = ( superClassInfo == null ) ? 0 : superClassInfo.refFieldtable.length;
	if ( nsuper == 0 ){
	    fieldoff = 0;
	} else {
	    FieldInfo f =  refFieldtable[ nsuper-1 ].find();
	    if ( f.instanceOffset < 0 ){
		superClassInfo.allocateFieldsFromFieldtable();
	    }
	    fieldoff = f.instanceOffset + f.nSlots;
	}
	for ( int i = nsuper ; i < n; i++ ){
	    FieldInfo f = refFieldtable[ i ].find();
	    if ( f == null ){
		// this is not supposed to happen.
		System.out.println( Localizer.getString("classinfo.cannot_find_field", refFieldtable[i]));
		continue;
	    }
	    f.instanceOffset = fieldoff;
	    fieldoff += f.nSlots;
	}
	//
	// and while we're here, do methodtable as well.
	//
	n = refMethodtable.length;
	int methodoff = 0; // 1=>0 EJVM

	for ( int i = 0; i < n ; i++ ){
	    MethodInfo m = refMethodtable[ i ].find();

	    if ( m == null ){
		// this is not supposed to happen.
		System.out.println( Localizer.getString("classinfo.cannot_find_field", refMethodtable[i]));
		continue;
	    } else if ( m.methodTableIndex != methodoff ){
		if ( m.parent != this ){
		    // this method is in a superclass.
		    // which apparently hasn't set up its methodtable yet.
		    // so go do it.
		    superClassInfo.allocateFieldsFromFieldtable();
		} else {
		    // we do it here.?
		    System.out.println("Inconsistent refMethodtable in class "+this.toString());
		    m.methodTableIndex = methodoff;
		}
	    }
	    methodoff += 1;
	}
    }

    void writeTableAttribute( DataOutput out, UnicodeConstant name, FMIrefConstant table[] ) throws IOException {
	if (verbose){
	    log.println(Localizer.getString("classinfo.writing_name", name.string));
	}
	out.writeShort( name.index );
	int n = table.length;
	out.writeInt( 2*n );
	for ( int i = 0; i < n; i++ ){
	    out.writeShort( table[i].index );
	}
    }

    void writeAttributes( DataOutput out ) throws IOException {
	int count = 0;
	if ( fieldtableName != null )
	    count++;
	if ( methodtableName != null )
	    count++;

	if (classAttributes != null) {
	    count += classAttributes.length;
	}
	out.writeShort(count);

	if ( fieldtableName != null ){
	    writeTableAttribute( out, fieldtableName, refFieldtable );
	}
	if ( methodtableName != null ){
	    writeTableAttribute( out, methodtableName, refMethodtable );
	}
	if (classAttributes != null) {
	    for (int k = 0; k < classAttributes.length; k++) {
		classAttributes[k].write( out );
	    }
	}
    }

    // Read in the entire class
    // assume file is open, magic numbers are o.k.
    // read assumes reading from class file
    //
    private void
    doRead( DataInput in, boolean readCode, ConstantPool externalSymbols )
    throws IOException {

	externalPool = externalSymbols; // for convenience, later
	resolveConstants( );

	access = in.readUnsignedShort();
	thisClass = (ClassConstant) symbols[in.readUnsignedShort()];
	int sup = in.readUnsignedShort();
	if ( sup != 0 )
	    superClass = (ClassConstant) symbols[sup];
	className = thisClass.name.string;

	// Read the various parts of the class file
	readInterfaces( in );
	readFields( in );
	readMethods( in, readCode );
	readAttributes( in );
	enterClass( className );
    }

    public void
    read( DataInput in, boolean readCode, ConstantPool externalSymbols )
    throws IOException {
	readConstantPool( in );
	symbols = constants;	// symbol table == constant pool
	doRead( in, readCode, externalSymbols );
    }

    public void
    externalize( ConstantPool p ){
	if (verbose){
	    log.println(Localizer.getString("classinfo.externalizing_class", className));
	}
	externalizeConstants( p );
	thisClass = (ClassConstant)p.dup( thisClass );
	//thisClass.externalize(p);//redundant?
	if ( superClass != null ){
	    superClass = (ClassConstant)p.dup( superClass );
	    //superClass.externalize(p);//redundant?
	}
	//externalizeInterfaces( p ); // interfaces NOT externalized!
	externalizeMethods( p );
	externalizeFields( p );
	externalizeAttributes( p );
    }

    // Compute the fieldtable for a class.  This requires laying
    // out the fieldtable for our parent, then adding any fields
    // that are not inherited.
    public
    void buildFieldtable( ConstantPool cp ){
	if (this.fieldtable != null) return; // already done.
	FieldInfo fieldtable[];
	int n;
	int fieldoff;
	int fieldtableLength = 0;
	FieldInfo candidate[] = this.fields;
	for( int i =0; i < candidate.length; i++ ){
	    if ((candidate[i].access & Const.ACC_STATIC) == 0){
		fieldtableLength++;
	    }
	}
	if ( superClassInfo != null ){
	    superClassInfo.buildFieldtable( cp );
	    n = superClassInfo.fieldtable.length;
	    fieldtableLength += n;
	    fieldoff = (n==0)? 0
		    : (superClassInfo.fieldtable[n-1].instanceOffset +
			superClassInfo.fieldtable[n-1].nSlots);
	    fieldtable = new FieldInfo[ fieldtableLength ];
	    System.arraycopy( superClassInfo.fieldtable, 0, fieldtable, 0, n );
	} else {
	    fieldtable = new FieldInfo[ fieldtableLength ];
	    n = 0;
	    fieldoff = 0;
	}
	for( int i =0; i < candidate.length; i++ ){
	    if ((candidate[i].access & Const.ACC_STATIC) == 0){
		fieldtable[n++] = candidate[i];
		candidate[i].instanceOffset = fieldoff;
		fieldoff += candidate[i].nSlots;
	    }
	}
	this.fieldtable = fieldtable;
	//
	// here, we make the gross assumption that
	// if we're building a fieldtable, we're using a shared
	// external Constant Pool
	fieldtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"fieldtable") );
	    
    }

    private FMIrefConstant buildReference( ClassMemberInfo m, boolean isMethod, ConstantPool cp ){
	ClassConstant c = (ClassConstant) cp.dup( m.parent.thisClass );
	FMIrefConstant x;
	NameAndTypeConstant n = (NameAndTypeConstant) cp.add(
	    new NameAndTypeConstant( 
		(UnicodeConstant)cp.add( m.name), 
		(UnicodeConstant)cp.add( m.type )
	    )
	);
	if (isMethod){
	    x = new MethodConstant( c, n );
	}else{
	    x = new FieldConstant( c, n );
	}
	return (FMIrefConstant)cp.add( x );
    }

    public
    void buildReferenceFieldtable( ConstantPool cp ){
	if ( refFieldtable != null ) return; // already done, it says here.
	if ( fieldtableName == null ){
	    fieldtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"fieldtable") );
	}
	buildFieldtable( cp );
	int n = fieldtable.length;
	refFieldtable = new FieldConstant[ n ];
	for ( int i = 0; i < n; i++ ){
	    refFieldtable[i] = (FieldConstant)buildReference( fieldtable[i], false, cp );
	}
    }

    // Compute the method table for a class.  This requires laying
    // out the method table for our parent, then adding any methods
    // that are not inherited.
    public
    void buildMethodtable( ConstantPool cp ) {
	if ( this.methodtable != null ) return; // already done.
	MethodInfo table[];
	MethodInfo methods[] = this.methods;
	ClassInfo sup = superClassInfo;
	if ((sup != null) && ( (sup.access&Const.ACC_INTERFACE)==(this.access&Const.ACC_INTERFACE) ) ) {
	    sup.buildMethodtable( cp );
	    table = sup.methodtable;
	} else {
	    table = new MethodInfo[0];
	}

	// allocate a temporary table that is certainly large enough.
	MethodInfo newTable[] = new MethodInfo[table.length + methods.length];
	int index = table.length;
	System.arraycopy( table, 0, newTable, 0, index );
	
	if (sup == null) { 
	    // finalize() goes into slot 0 of java.lang.Object
	    // FY: Removed for KVM.  We have no finalize() in slot 0.
		// index++;
	}

    method_loop:
	for (int i = 0; i < methods.length; i++) {
	    if ((methods[i].access & (Const.ACC_STATIC|Const.ACC_PRIVATE)) != 0) {
		continue method_loop;
	    } else if (methods[i].name.string.equals(/*NOI18N*/"<init>")) {
		continue method_loop;
	    } else if (sup == null && methods[i].name.string.equals(/*NOI18N*/"finalize") 
		                   && methods[i].type.string.equals(/*NOI18N*/"()V")) {
	        newTable[0] = methods[i];
		newTable[0].methodTableIndex = 0; // 1=>0 EJVM
		continue method_loop;
	    } 
	    int j;
	    int thisID = methods[i].getID();
	    for ( j = 0; j < table.length; j++) {
		if (thisID == table[j].getID()) {
		    newTable[j] = methods[i];
		    newTable[j].methodTableIndex = j + 0; // 1=>0 EJVM
		    continue method_loop;
		}
	    }
	    // If we're not overriding our parent's method we do add
	    // a new entry to the method table.
	    newTable[index] = methods[i];
	    newTable[index].methodTableIndex = index + 0; // 1=>0 EJVM
	    index++;
	}

	// now allocate a table of the correct size.
	MethodInfo methodTable[] = new MethodInfo[index];
	System.arraycopy( newTable, 0, methodTable, 0, index );

	this.methodtable =  methodTable;
	//
	// here, we make the gross assumption that
	// if we're building a methodtable, we're using a shared
	// external Constant Pool
	methodtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"methodtable") );
    }

    public
    void buildReferenceMethodtable( ConstantPool cp ){
	if ( refMethodtable != null ) return; // already done, it says here.
	if ( methodtableName == null ){
	    methodtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"methodtable") );
	}
	buildMethodtable( cp );
	int n = methodtable.length;
	refMethodtable = new MethodConstant[ n ];
	for ( int i = 0; i < n; i++ ){
	    refMethodtable[i] = (MethodConstant)buildReference( methodtable[i], true, cp );
	}
    }

    private static boolean
    conditionalAdd( Vector v, Object o ){
	if ( v.contains( o ) )
	    return false;
	v.addElement( o );
	return true;
    }
    /*
     * Compute the vector of all interfaces this class implements (or
     * this interface extends). Not only the interfaced declared in
     * the implements clause, which is what the interfaces[] field
     * represents, but all interfaces, including those of our superclasses
     * and those extended/implemented by any interfaces we implement.
     *
     */
    public void
    findAllInterfaces(){
	/*
	 * This works recursively, by computing parent's interface
	 * set first. THIS ASSUMES NON-CIRCULARITY, as does the rest
	 * of the Java system. This assumption will fail loudly, if
	 * at all.
	 */
	if ( superClassInfo == null ){
	    // we must be java.lang.Object!
	    allInterfaces = new Vector(5); // a generous size.
	} else {
	    if ( superClassInfo.allInterfaces == null )
		superClassInfo.findAllInterfaces();
	    allInterfaces = (Vector)(superClassInfo.allInterfaces.clone());
	}
	if ( interfaces == null ) return; // all done!
	for( int i = 0; i < interfaces.length; i++ ){
	    ClassInfo interf = interfaces[i].find();
	    if ( ( interf == null ) || ( (interf.access&Const.ACC_INTERFACE) == 0 ) ){
		System.err.println(Localizer.getString("classinfo.class_which_should_be_an_interface_but_is_not", className, interfaces[i]));
		continue;
	    }
	    if ( interf.allInterfaces == null )
		interf.findAllInterfaces();
	    if ( ! conditionalAdd( allInterfaces, interf ) ){
		// if this interface was already in the set,
		// then all the interfaces that it extend/implement
		// will be, too.
		continue;
	    }
	    Enumeration interfInterf = interf.allInterfaces.elements();
	    while( interfInterf.hasMoreElements() ){
		conditionalAdd( allInterfaces, interfInterf.nextElement() );
	    }
	}
    }

    public boolean
    findReferences(){
	try {
	    for ( int i = 0; i < methods.length; i++ ){
		methods[i].findConstantReferences();
	    }
	} catch ( DataFormatException e ){
	    return false;
	}
	return true;
    }

    public boolean
    countReferences( boolean isRelocatable ){
	thisClass.incReference();
	if ( superClass != null ) superClass.incReference();
	// count interface references
	if ( interfaces!=null ){
	    for ( int i = 0; i < interfaces.length ; i++ ){
		interfaces[i].incReference();
	    }
	}
	// then count references from fields.
	if ( fields != null ){
	    for ( int i = 0; i < fields.length; i++ ){
		fields[i].countConstantReferences( isRelocatable );
	    }
	}
	// then count references from code
	if ( methods != null ){
	    for ( int i = 0; i < methods.length; i++ ){
		methods[i].countConstantReferences( constants, isRelocatable );
	    }
	}
	Attribute.countConstantReferences(classAttributes, isRelocatable);
	return true;
    }

    public boolean
    relocateReferences(){
	try {
	    for ( int i = 0; i < methods.length; i++ ){
		methods[i].relocateConstantReferences( oldConstants );
	    }
	} catch ( DataFormatException e ){
	    return false;
	}
	return true;
    }

    public void
    clearMemberFlags( int flagsToClear ){
	int mask = ~ flagsToClear;
	int n;
	ClassMemberInfo members[];
	if ( fields != null ){
	    members = fields;
	    n = members.length;
	    for ( int i = 0; i < n; i++ ){
		members[i].flags &= mask;
	    }
	}
	if ( fields != null ){
	    members = methods;
	    n = members.length;
	    for ( int i = 0; i < n; i++ ){
		members[i].flags &= mask;
	    }
	}
    }

    public void
    write( DataOutput o ) throws IOException {
	writeConstantPool( o );
	o.writeShort( access );
	o.writeShort( thisClass.index );
	o.writeShort( superClass==null? 0 : superClass.index );
	writeInterfaces( o );
	writeFields( o );
	writeMethods( o );
	writeAttributes( o );
    }

    private static void
    dumpComponentTable( PrintStream o, String title, ClassComponent t[] ){
	int n;
	if ( (t == null) || ((n=t.length) == 0) ) return;
	o.print( title ); o.println(/*NOI18N*/"["+n+"]:");
	for( int i = 0; i < n; i++ ){
	    if ( t[i] != null )
		o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i]);
	}
    }

    private static void
    dumpConstantTable( PrintStream o, String title, ConstantObject t[] ){
	int n;
	if ( (t == null) || ((n=t.length) == 0) ) return;
	o.print( title ); o.println(/*NOI18N*/"["+n+/*NOI18N*/"]:");
	o.println(/*NOI18N*/"\tPosition Index\tNrefs");
	for( int i = 0; i < n; i++ ){
	    if ( t[i] != null )
		o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].index+/*NOI18N*/"\t"+t[i].references+/*NOI18N*/"\t"+t[i]);
	}
    }
    private static void
    dumpMemberTable( PrintStream o, String title, ClassMemberInfo t[] ){
	int n;
	if ( (t == null) || ((n=t.length) == 0) ) return;
	o.print( title ); o.println(/*NOI18N*/":");
	for( int i = 0; i < n; i++ ){
	    if ( t[i] != null )
		o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].qualifiedName() );
	}
    }
    public void
    dump( PrintStream o ){
	o.print(Util.accessToString(access)+/*NOI18N*/"Class "+thisClass);
	if ( superClass != null )
	    o.print(/*NOI18N*/" extends "+superClass);
	if ( interfaces!=null && interfaces.length != 0 ){
	    o.print(/*NOI18N*/" implements");
	    for( int i = 0; i < interfaces.length ; i++ ){
		o.print(" "+interfaces[i]);
	    }
	}
	o.println();
	dumpComponentTable( o, /*NOI18N*/"Methods", methods );
	dumpComponentTable( o, /*NOI18N*/"Fields",  fields  );
	if ( fieldtable != null )
	    dumpMemberTable( o, /*NOI18N*/"Fieldtable", fieldtable );
	else
	    dumpComponentTable( o, /*NOI18N*/"Fieldtable-by-reference", refFieldtable );
	if ( methodtable != null )
	    dumpMemberTable( o, /*NOI18N*/"Methodtable", methodtable );
	else
	    dumpComponentTable( o, /*NOI18N*/"Methodtable-by-reference", refMethodtable );
	dumpConstantTable( o, /*NOI18N*/"Constants", constants );
    }

    /**
     * We keep track of classes by hashing them by name when
     * we read them. They can be looked up using lookupClass,
     * which will take a classname string as parameter.
     */
    public static Hashtable classtable = new Hashtable();

    protected void enterClass( String key ){
	// should check to see if a class of this name is already there...
	if ( classtable.containsKey( className ) ){
	    System.err.println(Localizer.getString("classinfo.class_table_already_contains", className));
	    return;
	}
	classtable.put( key, this );
	// if a classvector has been created, we need to add this.
	// at end should be sufficient.
	vm.ClassClass.appendClassElement( this );
    }

    public static ClassInfo lookupClass( String key ){
	return (ClassInfo)classtable.get( key );
    }

    public static int nClasses(){
	return classtable.size();
    }

    public static Enumeration allClasses(){
	return classtable.elements();
    }

    public static boolean resolveSupers(){
	Enumeration allclasses = allClasses();
	boolean ok = true;
	while( allclasses.hasMoreElements() ){
	    ClassInfo c = (ClassInfo)allclasses.nextElement();
	    if ( c.superClass==null ){
		// only java.lang.Object can be parentless
		if ( ! c.className.equals( /*NOI18N*/"java/lang/Object" ) ){
		    System.out.println(Localizer.getString("classinfo.class_is_parent-less", c.className));
		    ok = false;
		}
	    } else {
		ClassInfo s = ClassInfo.lookupClass( c.superClass.name.string );
		if ( s == null ){
		    System.out.println(Localizer.getString("classinfo.class_is_missing_parent", c.className, c.superClass.name.string ));
		    ok = false;
		} else {
		    c.superClassInfo = s;
		}
	    }
	}
	return ok;
    }

    public String
    toString(){
	return /*NOI18N*/"ClassInfo-\""+className+/*NOI18N*/"\"";
    }

    // Convert ldc to ldc2
    public void relocateAndPackCode() {
        for ( int i = 0; i < methods.length; i++ )
            methods[i].relocateAndPackCode(constants); 
    }
}
