org.glassfish.pfl.dynamic.codegen.impl.ClassInfoBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pfl-dynamic Show documentation
Show all versions of pfl-dynamic Show documentation
Functionality that may include class generation
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
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