All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.pfl.dynamic.codegen.impl.ClassInfoBase Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.glassfish.pfl.dynamic.codegen.impl;

import java.util.List ;
import java.util.Map ;
import java.util.LinkedHashMap ;
import java.util.Set ;
import java.util.HashSet ;

import java.lang.reflect.Modifier ;

import org.glassfish.pfl.dynamic.codegen.spi.Type ;
import org.glassfish.pfl.dynamic.codegen.spi.Signature ;
import org.glassfish.pfl.dynamic.codegen.spi.ClassInfo ;
import org.glassfish.pfl.dynamic.codegen.spi.MethodInfo ;
import org.glassfish.pfl.dynamic.codegen.spi.FieldInfo ;

public abstract class ClassInfoBase implements ClassInfo {
    // Initialized in the constructor
    private int modifiers ;
    private Type thisType ;
    private String className ;
    private String pkgName ;

    private boolean initComplete ;

    // Initialized in initializeInterface/initializeClass
    private boolean isInterface ;
    private Type superType ;
    private List impls ;

    // Updated by add methods: also affect hashCode
    private Map> methodInfoByName ;
    private Set constructors ;
    private Map fields ;

    // Cached hashCode
    private boolean hashIsCached ;
    private int hashValue ;

    /** Construct a ClassInfoBase representing a class or interface.
     */
    public ClassInfoBase( int modifiers, Type thisType ) {
	this.modifiers = modifiers ;
	this.thisType = thisType ;
	String name = thisType.name() ;
	int index = name.lastIndexOf( '.' ) ;
	if (index == -1) {
	    className = name ;
	    pkgName = "" ;
	} else {
	    className = name.substring( index+1 ) ;
	    pkgName = name.substring( 0, index ) ;
	}

	this.initComplete = false ;

	this.constructors = new HashSet() ;
	this.methodInfoByName = new LinkedHashMap>() ;
	this.fields = new LinkedHashMap() ;

	this.hashValue = 0 ;
	this.hashIsCached = false ;
    }

    private void checkComplete() {
	if (!initComplete)
	    throw new IllegalStateException( 
		"ClassInfoBase initialization is not complete" ) ;
    }

    private void checkReinitialize() {
	if (initComplete)
	    throw new IllegalStateException( 
		"ClassInfoBase cannot be reinitialized" ) ;
    }

    protected void initializeInterface( List exts ) {
	checkReinitialize() ;

	this.isInterface = true ;
	this.superType = null ; // This should match java, and Class.getSuperclass is null for
				// an interface.
	this.impls = exts ;

	this.initComplete = true ;
    }

    protected void initializeClass( Type thisType, Type superType, 
	List impls ) {
	checkReinitialize() ;

	this.isInterface = false ;
	this.thisType = thisType ;
	this.superType = superType ;
	this.impls = impls ;

	this.initComplete = true ;
    }

    protected void addFieldInfo( FieldInfo finfo ) {
	checkComplete() ;
	clearHashCode() ;
	if (isInterface) {
	    int mod = finfo.modifiers() ;
	    if (!(Modifier.isPublic(mod) && Modifier.isFinal(mod) && Modifier.isStatic(mod)))
		throw new IllegalStateException(
		    "Only public static final fields can be added to an interface" ) ;
	}

	fields.put( finfo.name(), finfo ) ;
    }

    protected void addMethodInfo( MethodInfo minfo ) {
	checkComplete() ;
	clearHashCode() ;
	Set minfos = methodInfoByName.get( minfo.name() ) ;
	if (minfos == null) {
	    minfos = new HashSet() ;
	    methodInfoByName.put( minfo.name(), minfos ) ;
	}

	if (isInterface && !Modifier.isAbstract( minfo.modifiers() ))
	    throw new IllegalStateException( 
		"All methods in an interface must be abstract" ) ;

	minfos.add( minfo ) ;
    }

    protected void addConstructorInfo( MethodInfo cinfo ) {
	checkComplete() ;
	clearHashCode() ;
	if (isInterface)
	    throw new IllegalStateException(
		"Cannot add a constructor to an interface" ) ;
	constructors.add( cinfo ) ;
    }

    public Type thisType() {
	return thisType ;
    }

    public boolean isInterface() {
	checkComplete() ;
	return isInterface ;
    }

    public int modifiers() {
	return modifiers ;
    }

    public String name() {
	return thisType.name() ;
    }

    public String className() {
	return className ;
    }

    public String pkgName() {
	return pkgName ;
    }

    public Type superType() {
	checkComplete() ;
	return superType ;
    }

    public List impls() {
	checkComplete() ;
	return impls ;
    }

    public Map fieldInfo() {
	checkComplete() ;
	return fields ;
    }

    public FieldInfo findFieldInfo( String name ) {
	FieldInfo info = fields.get( name ) ;
	if (info == null) {
	    if (superType() == null) 
		return null ;

	    ClassInfo superInfo = superType().classInfo() ;
	    info = superInfo.findFieldInfo( name ) ;
	}

	return info ;
    }

    public Map> methodInfoByName() {
	checkComplete() ;
	return methodInfoByName ;
    }

    public Set constructorInfo() {
	return constructors ;
    }


    private MethodInfo findMethodInfo( Signature sig, Set minfos ) {
	if (minfos != null)
	    for (MethodInfo minfo : minfos)
		if (sig.equals( minfo.signature() ))
		    return minfo ;

	return null ;
    }

    public MethodInfo findMethodInfo( String name, Signature sig ) {
	MethodInfo result = null ;
	// First search this class and all its superclasses.
	// If this class is an interface, only the methods in the
	// interface are searched here, since superType() is null for
	// interfaces.
	ClassInfo current = this ;
	while (current != null) {
	    Set minfos = current.methodInfoByName().get( name ) ;
	    result = findMethodInfo( sig, minfos ) ;
	    if (result != null)
		return result ;

	    if (current.superType() == null)
		current = null ;
	    else
		current = current.superType().classInfo() ;
	}

	// Then search all implemented interfaces recursively
	for (Type type : impls) {
	    result = type.classInfo().findMethodInfo( name, sig ) ;
	    if (result != null)
		return result ;
	}

	return result ;
    }

    public MethodInfo findConstructorInfo( Signature sig ) {
	return findMethodInfo( sig, constructors ) ;
    }

    public boolean isSubclass( ClassInfo info ) {
	// A class is a subclass of itself
	if (this.equals( info ))
	    return true ;

        // All classes are subclasses of java.lang.Object.
        // Note that superType() == null for interfaces.
        if (info.equals( Type._Object().classInfo() ))
            return true ;

	// A class is a subclass of info if the
	// class's superClass is a subclass of info
	if (superType() != null)
	    if (superType().classInfo().isSubclass( info ))
		return true ;

	// A class is a subclass of info if any of
	// its implemented interfaces are subclasses
	// of info
	for (Type t : impls()) {
	    if (t.classInfo().isSubclass( info ))
		return true ;
	}

	return false ;
    }

    public boolean equals( Object obj ) {
	checkComplete() ;
	if (obj == this)
	    return true ;

	if (!(obj instanceof ClassInfo))
	    return false ;
    
	ClassInfo other = ClassInfo.class.cast( obj ) ;

	if (hashCode() != other.hashCode())
	    return false ;

	if (!thisType().equals( other.thisType() ))
	    return false ;

	if (isInterface() != other.isInterface())
	    return false ;

	if (modifiers() != other.modifiers())
	    return false ;

	if (!name().equals( other.name() ))
	    return false ;

	if (superType() == null) {
	    if (other.superType() != null)
		return false ;
	} else if (!superType().equals( other.superType() ))
	    return false ;

	if (!impls().equals( other.impls()))
	    return false ;

	if (!fieldInfo().entrySet().equals( other.fieldInfo().entrySet()))
	    return false ;

	if (!methodInfoByName().equals(other.methodInfoByName()))
	    return false ;

	if (!constructorInfo().equals(other.constructorInfo()))
	    return false ;

	return true ;
    }

    public String toString() {
	checkComplete() ;
	String className = this.getClass().getName() ;
	int lindex = className.lastIndexOf( '.' ) ;
	if (lindex >= 0)
	    className = className.substring( lindex+1) ;

	return className + "[" + name() + "]" ;
    }

    // Clear the hashCode whenever the methods, constructors, or
    // fields are changed.
    private synchronized void clearHashCode() {
	hashIsCached = false ;
	hashValue = 0 ;
    }

    public synchronized int hashCode() {
	checkComplete() ;
	if (!hashIsCached) {
	    hashValue ^= thisType().hashCode() ;
	    hashValue ^= isInterface() ? 0 : 1 ;
	    hashValue ^= modifiers() ;
	    if (superType() != null)
		hashValue ^= superType().hashCode() ;
	    hashValue ^= impls().hashCode() ;
	    hashValue ^= fieldInfo().hashCode() ;
	    hashValue ^= methodInfoByName().hashCode() ;
	    if (constructorInfo() != null)
		hashValue ^= constructorInfo().hashCode() ;

	    hashIsCached = true ;
	}

	return hashValue ;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy