Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/sun/tools/java/ClassDefinition.java

/*
 * @(#)ClassDefinition.java	1.32 95/11/29 Arthur van Hoff
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package sun.tools.java;

import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import java.io.OutputStream;
import java.io.PrintStream;

/**
 * This class is an Java class definition
 */
public
class ClassDefinition implements Constants {
    protected Object source;
    protected int where;
    protected int modifiers;
    protected ClassDeclaration declaration;
    protected ClassDeclaration superClass;
    protected ClassDeclaration interfaces[];
    protected FieldDefinition firstField;
    protected FieldDefinition lastField;
    protected String documentation;
    protected boolean error;
    private Hashtable fieldHash = new Hashtable(31);
    private int abstr;

    /**
     * Constructor
     */
    protected ClassDefinition(Object source, int where, ClassDeclaration declaration,
			      int modifiers, ClassDeclaration superClass, ClassDeclaration interfaces[]) {
	this.source = source;
	this.where = where;
	this.declaration = declaration;
	this.modifiers = modifiers;
	this.superClass = superClass;
	this.interfaces = interfaces;
    }

    /**
     * Get the source of the class
     */
    public final Object getSource() {
	return source;
    }

    /**
     * Check if there were any errors
     */
    public final boolean getError() {
	return error;
    }

    /**
     * Set errors
     */
    public final void setError(boolean error) {
	this.error = error;
    }

    /**
     * Get the position in the input
     */
    public final int getWhere() {
	return where;
    }
    
    /**
     * Get the class declaration
     */
    public final ClassDeclaration getClassDeclaration() {
	return declaration;
    }

    /**
     * Get the class' modifiers
     */
    public final int getModifiers() {
	return modifiers;
    }

    /**
     * Get the class' super class
     */
    public final ClassDeclaration getSuperClass() {
	return superClass;
    }

    /**
     * Get the class' interfaces
     */
    public final ClassDeclaration getInterfaces()[] {
	return interfaces;
    }

    /**
     * Get the class' first field or first match
     */
    public final FieldDefinition getFirstField() {
	return firstField;
    }
    public final FieldDefinition getFirstMatch(Identifier name) {
	return (FieldDefinition)fieldHash.get(name);
    }

    /**
     * Get the class' name
     */
    public final Identifier getName() {
	return declaration.getName();
    }

    /**
     * Get the class' type
     */
    public final Type getType() {
	return declaration.getType();
    }

    /**
     * Get the class' documentation
     */
    public String getDocumentation() {
	return documentation;
    }
    
    /**
     * Checks
     */
    public final boolean isInterface() {
	return (getModifiers() & M_INTERFACE) != 0;
    }
    public final boolean isClass() {
	return (getModifiers() & M_INTERFACE) == 0;
    }
    public final boolean isPublic() {
	return (getModifiers() & M_PUBLIC) != 0;
    }
    public final boolean isFinal() {
	return (getModifiers() & M_FINAL) != 0;
    }
    public final boolean isAbstract() {
	return (getModifiers() & M_ABSTRACT) != 0;
    }
    public final boolean isAbstract(Environment env) throws ClassNotFound {
	if (isAbstract()) {
	    return true;
	}
	if (abstr == 0) {
	    abstr = (isInterface() || hasAbstractFields(env)) ? 1 : -1;
	}
	return abstr == 1;
    }

    /**
     * Return an enumeration of all the abstract methods
     */
    public Enumeration getAbstractFields(Environment env) throws ClassNotFound {
	Vector v = new Vector();
	getAbstractFields(env, this, v);
	return v.elements();
    }

    private void getAbstractFields(Environment env, ClassDefinition c, Vector v)
	   throws ClassNotFound {
	for (FieldDefinition f = c.getFirstField() ;  
	             f != null ; f = f.getNextField()) {
	    if (f.isAbstract()) {
		FieldDefinition m = findMethod(env, f.getName(), f.getType());
		if (m == null) {
		    m = f;
		}
		if (m.isAbstract() && !v.contains(m)) {
		    v.addElement(m);
		}
	    }
	}

	// Super class
	ClassDeclaration sup = c.getSuperClass();
	if (sup != null) { 
	    ClassDefinition supDef = sup.getClassDefinition(env);
	    if (supDef.isAbstract(env))
		getAbstractFields(env, supDef, v);
	}
    }


    /**
     * Return true if this class has abstract fields.
     */
    public boolean hasAbstractFields(Environment env) throws ClassNotFound {
	return hasAbstractFields(env, this);
    }

    private boolean hasAbstractFields(Environment env, ClassDefinition c)
	   throws ClassNotFound {
	for (FieldDefinition f = c.getFirstField() ;  
	             f != null ; f = f.getNextField()) {
	    if (f.isAbstract()) {
		FieldDefinition m = findMethod(env, f.getName(), f.getType());
		if (m == null || m.isAbstract()) 
		    return true;
	    }
	}
	// Super class
	ClassDeclaration sup = c.getSuperClass();
	if (sup != null) { 
	    ClassDefinition supDef = sup.getClassDefinition(env);
	    return (supDef.isAbstract(env) && hasAbstractFields(env, supDef));
	}
	return false;
    }


    /**
     * Check if this is a super class of another class
     */
    public boolean superClassOf(Environment env, ClassDeclaration otherClass) throws ClassNotFound {
	while (otherClass != null) {
	    if (getClassDeclaration().equals(otherClass)) {
		return true;
	    }
	    otherClass = otherClass.getClassDefinition(env).getSuperClass();
	}
	return false;
    }

    /**
     * Check if this is a sub class of another class
     */
    public boolean subClassOf(Environment env, ClassDeclaration otherClass) throws ClassNotFound {
	ClassDeclaration c = getClassDeclaration();
	while (c != null) {
	    if (c.equals(otherClass)) {
		return true;
	    }
	    c = c.getClassDefinition(env).getSuperClass();
	}
	return false;
    }

    /**
     * Check if this class is implemented by another class
     */
    public boolean implementedBy(Environment env, ClassDeclaration c) throws ClassNotFound {
	for (; c != null ; c = c.getClassDefinition(env).getSuperClass()) {
	    if (getClassDeclaration().equals(c)) {
		return true;
	    }
	    ClassDeclaration intf[] = c.getClassDefinition(env).getInterfaces();
	    for (int i = 0 ; i < intf.length ; i++) {
		if (implementedBy(env, intf[i])) {
		    return true;
		}
	    }
	}
	return false;
    }


    /**
     * Check if a class can be accessed from another class
     */
    public boolean canAccess(Environment env, ClassDeclaration c) throws ClassNotFound {
	// Public access is always ok
	if (c.getClassDefinition(env).isPublic()) {
	    return true;
	}

	// It must be in the same package
	return getName().getQualifier().equals(c.getName().getQualifier());
    }

    /**
     * Check if a field can be accessed from a class
     */
    public boolean canAccess(Environment env, FieldDefinition f) 
               throws ClassNotFound {
	// Public access is always ok
	if (f.isPublic()) {
	    return true;
	}
	// Proteced access is ok from a subclass
	if (f.isProtected() && subClassOf(env, f.getClassDeclaration())) {
	    return true;
	}
	// Private access is ok only from the same class
	if (f.isPrivate()) {
	    return getClassDeclaration().equals(f.getClassDeclaration());
	}
	// It must be in the same package
	return getName().getQualifier().equals(f.getClassDeclaration().getName().getQualifier());
    }

 
    /**
     * We know the the field is marked protected (and not public) and that
     * the field is visible (as per canAccess).  Can we access the field as
     * <accessor>.<field>, where <accessor> has the type <accessorType>
     *
     * protected fields can only be accessed when the accessorType is a 
     * subclass of the current class
     */
    public boolean protectedAccess(Environment env, FieldDefinition f,
				   Type accessorType)
        throws ClassNotFound
    { 
               
	return 
	       // static protected fields are accessible
	       f.isStatic() 
	    || // allow array.clone()
	       accessorType.isType(TC_ARRAY)
	    || // <accessorType> is a subtype of the current class
	       (env.getClassDefinition(accessorType.getClassName())
		         .subClassOf(env, getClassDeclaration()))
	    || // we are accessing the field from a friendly class
	       (!f.isPrivate() &&
		  getName().getQualifier()
	           .equals(f.getClassDeclaration().getName().getQualifier()));
    }


   /**
     * Get a variable
     */
    public FieldDefinition getVariable(Environment env, Identifier nm) throws AmbiguousField, ClassNotFound {
	// Check if it is defined in the current class
	for (FieldDefinition field = getFirstMatch(nm) ; field != null ; field = field.getNextMatch()) {
	    if (field.isVariable()) {
		return field;
	    }
	}

	// Get it from the super class
	ClassDeclaration sup = getSuperClass();
	FieldDefinition field = (sup != null) ? sup.getClassDefinition(env).getVariable(env, nm) : null;

	// Get it from an interface
	for (int i = 0 ; i < interfaces.length ; i++) {
	    FieldDefinition f = interfaces[i].getClassDefinition(env).getVariable(env, nm);
	    if (f != null) {
		if ((field != null) && (f != field)) {
		    throw new AmbiguousField(f, field);
		}
		field = f;
	    }
	}
	return field;
    }

    /**
     * Match a method
     */
    private FieldDefinition 
    matchMethod(Environment env, ClassDefinition sourceClass, 
		Identifier nm, Type argTypes[], 
		FieldDefinition tentative, boolean pass1)
	     throws AmbiguousField, ClassNotFound {
		 // look in the current class
	for (FieldDefinition f = getFirstMatch(nm); 
	         f != null ; f = f.getNextMatch()) {
	    if (!env.isApplicable(f, argTypes))
		continue;
	    if (sourceClass != null && !sourceClass.canAccess(env, f))
		continue;
	    if (pass1) {
		if (tentative == null || env.isMoreSpecific(f, tentative)) {
		    tentative = f;
		}
	    } else { 
		// tentative better be more applicable than anything else
		if (f != tentative && !env.isMoreSpecific(tentative, f)) 
		    throw new AmbiguousField(tentative, f);
	    }
	}
	// constructors are not inherited
	if (nm.equals(idInit)) {
	    return tentative;
	}

	// Look in the superclasses
	if (tentative == null || !pass1) { 
	    // If we already have a tentative in pass1, there is no need to
	    // go up the superclass chain.  We're not going to find anything
	    // more specific in the "receiver" argument
	    ClassDeclaration sup = getSuperClass();
	    if (sup != null) { 
		ClassDefinition supDef = sup.getClassDefinition(env);
		tentative = supDef.matchMethod(env, sourceClass, nm, argTypes, 
					       tentative, pass1);
		if (isInterface()) { 
		    ClassDeclaration intDecls[] = getInterfaces();
		    for (int i = 0; i < intDecls.length; i++) {
			tentative = intDecls[i].getClassDefinition(env).
			    matchMethod(env, sourceClass, nm,
					argTypes, tentative, pass1);
		    }
		}
	    }
	}
	return tentative;
    }

    public FieldDefinition matchMethod(Environment env, 
				       ClassDefinition sourceClass, 
				       Identifier nm, Type argTypes[]) 
	    throws AmbiguousField, ClassNotFound {
	// pass1.  Find a "top" in the hierarchy;
	FieldDefinition f = matchMethod(env, sourceClass, 
					nm, argTypes, null, true);
	if (f != null) 
	    matchMethod(env, sourceClass, nm, argTypes, f, false);
	return f;
    }

    public FieldDefinition matchMethod(Environment env, 
				       ClassDefinition sourceClass, 
				       Identifier nm) 
	   throws AmbiguousField, ClassNotFound {
	return matchMethod(env, sourceClass, nm, Type.noArgs);
    }

    /**
     * Find a method, ie: exact match in this class or any of the super classes.
     */
    public FieldDefinition findMethod(Environment env, Identifier nm, Type t) throws ClassNotFound {
	// look in the current class
	FieldDefinition f;
	for (f = getFirstMatch(nm) ; f != null ; f = f.getNextMatch()) {
	    if (f.getType().equalArguments(t)) {
		return f;
	    }
	}

	// constructors are not inherited
	if (nm.equals(idInit)) {
	    return null;
	}

	// look in the super class
	ClassDeclaration sup = getSuperClass();
	if (sup == null) 
	    return null;
	return sup.getClassDefinition(env).findMethod(env, nm, t);
    }

 
    // We create a stub for this.  Source classes do more work.
    protected void basicCheck(Environment env) throws ClassNotFound {}    



   /**
     * Find any method with a given name.
     */
    public FieldDefinition findAnyMethod(Environment env, Identifier nm) throws ClassNotFound {
	FieldDefinition f = getFirstMatch(nm);
	if (f != null) {
	    return f;
	}

	// look in the super class
	ClassDeclaration sup = getSuperClass();
	if (sup == null) 
	    return null;
	return sup.getClassDefinition(env).findAnyMethod(env, nm);
    }
    
    /**
     * Add a field (no checks)
     */
    public void addField(FieldDefinition field) {
	//System.out.println("ADD = " + field);
	if (firstField == null) {
	    firstField = lastField = field;
	} else {
	    lastField.nextField = field;
	    lastField = field;
	    field.nextMatch = (FieldDefinition)fieldHash.get(field.name);
	}
	fieldHash.put(field.name, field);
    }

    /**
     * Add a dependency
     */
    public void addDependency(ClassDeclaration c) {
	throw new CompilerError("addDependency");
    }

    /**
     * Print for debugging
     */
    public void print(PrintStream out) {
	if (isPublic()) {
	    out.print("public ");
	} 
	if (isInterface()) {
	    out.print("interface ");
	} else {
	    out.print("class ");
	}
	out.print(getName() + " ");
	if (getSuperClass() != null) {
	    out.print("extends " + getSuperClass().getName() + " ");
	}
	if (interfaces.length > 0) {
	    out.print("implements ");
	    for (int i = 0 ; i < interfaces.length ; i++) {
		if (i > 0) {
		    out.print(", ");
		}
		out.print(interfaces[i].getName());
		out.print(" ");
	    }
	}
	out.println("{");

	for (FieldDefinition f = getFirstField() ; f != null ; f = f.getNextField()) {
	    out.print("    ");
	    f.print(out);
	}

	out.println("}");
    }

    /**
     * Convert to String
     */
    public String toString() {
    	return getClassDeclaration().toString();
    }
}