blob: 5108fd1780cdf4fd91982cd13c07aa55d5619e55 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.jam.internal.elements;
import org.apache.xmlbeans.impl.jam.*;
import org.apache.xmlbeans.impl.jam.provider.JamClassPopulator;
import org.apache.xmlbeans.impl.jam.visitor.MVisitor;
import org.apache.xmlbeans.impl.jam.visitor.JVisitor;
import org.apache.xmlbeans.impl.jam.mutable.*;
import org.apache.xmlbeans.impl.jam.internal.classrefs.JClassRef;
import org.apache.xmlbeans.impl.jam.internal.classrefs.JClassRefContext;
import org.apache.xmlbeans.impl.jam.internal.classrefs.QualifiedJClassRef;
import org.apache.xmlbeans.impl.jam.internal.classrefs.UnqualifiedJClassRef;
import org.apache.xmlbeans.impl.jam.internal.JamClassLoaderImpl;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
/**
* <p>Implementation of JClass and MClass.</p>
*
* @author Patrick Calahan <jdclasspcal@bea.com>
*/
public class ClassImpl extends MemberImpl implements MClass,
JClassRef, JClassRefContext
{
// ========================================================================
// Constants
public static final int NEW = 1;
public static final int UNPOPULATED = 2;
public static final int POPULATING = 3;
public static final int UNINITIALIZED = 4;
public static final int INITIALIZING = 5;
public static final int LOADED = 6;
// ========================================================================
// Variables
private int mState = NEW;
private boolean mIsAnnotationType = false;
private boolean mIsInterface = false;
private boolean mIsEnum = false;
private String mPackageName = null;
private JClassRef mSuperClassRef = null; // classrefs to the class we extend
private ArrayList mInterfaceRefs = null; // refs to interfaces we elements.
private ArrayList mFields = null;
private ArrayList mMethods = null;
private ArrayList mConstructors = null;
private ArrayList mProperties = null;
private ArrayList mDeclaredProperties = null;
private ArrayList mInnerClasses = null;
private String[] mImports = null;
private JamClassPopulator mPopulator;
// FIXME implement this - we should only create one UnqualifiedJClassRef
// for each unqualified name so as to avoid resolving them over and over.
//private Map mName2Uqref = null;
// ========================================================================
// Constructors
public ClassImpl(String packageName,
String simpleName,
ElementContext ctx,
String[] importSpecs,
JamClassPopulator populator) {
super(ctx);
super.setSimpleName(simpleName);
mPackageName = packageName.trim();
mImports = importSpecs;
mPopulator = populator;
setState(UNPOPULATED);
}
public ClassImpl(String packageName,
String simpleName,
ElementContext ctx,
String[] importSpecs)
{
super(ctx);
super.setSimpleName(simpleName);
mPackageName = packageName.trim();
mImports = importSpecs;
mPopulator = null;
setState(UNINITIALIZED);
}
// ========================================================================
// JClass implementation
public JPackage getContainingPackage() {
return getClassLoader().getPackage(mPackageName);
}
public JClass getSuperclass() {
ensureLoaded();
if (mSuperClassRef == null) {
return null;
} else {
return mSuperClassRef.getRefClass();
}
}
public JClass[] getInterfaces() {
ensureLoaded();
if (mInterfaceRefs == null || mInterfaceRefs.size() == 0) {
return new JClass[0];
} else {
JClass[] out = new JClass[mInterfaceRefs.size()];
for(int i=0; i<out.length; i++) {
out[i] = ((JClassRef)mInterfaceRefs.get(i)).getRefClass();
}
return out;
}
}
public JField[] getFields() {
ensureLoaded();
List list = new ArrayList();
addFieldsRecursively(this, list);
JField[] out = new JField[list.size()];
list.toArray(out);
return out;
}
public JField[] getDeclaredFields() {
ensureLoaded();
return getMutableFields();
}
public JMethod[] getMethods() {
ensureLoaded();
List list = new ArrayList();
addMethodsRecursively(this, list);
JMethod[] out = new JMethod[list.size()];
list.toArray(out);
return out;
}
public JProperty[] getProperties() {
ensureLoaded();
if (mProperties == null) return new JProperty[0];
JProperty[] out = new JProperty[mProperties.size()];
mProperties.toArray(out);
return out;
}
public JProperty[] getDeclaredProperties() {
ensureLoaded();
if (mDeclaredProperties == null) return new JProperty[0];
JProperty[] out = new JProperty[mDeclaredProperties.size()];
mDeclaredProperties.toArray(out);
return out;
}
public JMethod[] getDeclaredMethods() {
ensureLoaded();
return getMutableMethods();
}
public JConstructor[] getConstructors() {
ensureLoaded();
return getMutableConstructors();
}
public boolean isInterface() {
ensureLoaded();
return mIsInterface;
}
public boolean isAnnotationType() {
ensureLoaded();
return mIsAnnotationType;
}
public boolean isEnumType() {
ensureLoaded();
return mIsEnum;
}
public int getModifiers() {
ensureLoaded();
return super.getModifiers();
}
public boolean isFinal() { return Modifier.isFinal(getModifiers()); }
public boolean isStatic() { return Modifier.isStatic(getModifiers()); }
public boolean isAbstract() { return Modifier.isAbstract(getModifiers()); }
public boolean isAssignableFrom(JClass arg) {
ensureLoaded();
if (isPrimitiveType() || arg.isPrimitiveType()) {
return getQualifiedName().equals(arg.getQualifiedName());
}
return isAssignableFromRecursively(arg);
}
public JClass[] getClasses() {
ensureLoaded();
return new JClass[0];//FIXME
}
public String getFieldDescriptor() {
return getQualifiedName();
}
public JClass forName(String name) {
return getClassLoader().loadClass(name);
}
public JPackage[] getImportedPackages() {
ensureLoaded();
Set set = new TreeSet();
JClass[] importedClasses = getImportedClasses();
for(int i=0; i<importedClasses.length; i++) {
JPackage c = importedClasses[i].getContainingPackage();
if (c != null) set.add(c);
}
String[] imports = getImportSpecs();
if (imports != null) {
for(int i=0; i<imports.length; i++) {
if (imports[i].endsWith(".*")) {
set.add(getClassLoader().
getPackage(imports[i].substring(0,imports[i].length()-2)));
}
}
}
JPackage[] array = new JPackage[set.size()];
set.toArray(array);
return array;
}
public JClass[] getImportedClasses() {
ensureLoaded();
String[] imports = getImportSpecs();
if (imports == null) return new JClass[0];
List list = new ArrayList();
for(int i=0; i<imports.length; i++) {
if (imports[i].endsWith("*")) continue;
list.add(getClassLoader().loadClass(imports[i]));
}
JClass[] out = new JClass[list.size()];
list.toArray(out);
return out;
}
public void accept(MVisitor visitor) { visitor.visit(this); }
public void accept(JVisitor visitor) { visitor.visit(this); }
public void setSimpleName(String name) {
throw new UnsupportedOperationException("Class names cannot be changed");
}
public Class getPrimitiveClass() { return null; }
public boolean isPrimitiveType() { return false; }
public boolean isBuiltinType() { return false; }
public boolean isVoidType() { return false; }
public boolean isUnresolvedType() { return false; }
public boolean isObjectType() {
return getQualifiedName().equals("java.lang.Object");
}
public boolean isArrayType() { return false; }
public JClass getArrayComponentType() { return null; }
public int getArrayDimensions() { return 0; }
// ========================================================================
// AnnotatedElementImpl implementation - just to add ensureLoaded()
public JAnnotation[] getAnnotations() {
ensureLoaded();
return super.getAnnotations();
}
public JAnnotation getAnnotation(Class proxyClass) {
ensureLoaded();
return super.getAnnotation(proxyClass);
}
public JAnnotation getAnnotation(String named) {
ensureLoaded();
return super.getAnnotation(named);
}
public JAnnotationValue getAnnotationValue(String valueId) {
ensureLoaded();
return super.getAnnotationValue(valueId);
}
public Object getAnnotationProxy(Class proxyClass) {
ensureLoaded();
return super.getAnnotationProxy(proxyClass);
}
public JComment getComment() {
ensureLoaded();
return super.getComment();
}
public JAnnotation[] getAllJavadocTags() {
ensureLoaded();
return super.getAllJavadocTags();
}
// ========================================================================
// Element implementation - just to add ensureLoaded()
public JSourcePosition getSourcePosition() {
ensureLoaded();
return super.getSourcePosition();
}
// ========================================================================
// MClass implementation
//FIXME should add assertiong here that state is not LOADED
public void setSuperclass(String qualifiedClassName) {
if (qualifiedClassName == null) {
mSuperClassRef = null;
} else {
if (qualifiedClassName.equals(getQualifiedName())) {
throw new IllegalArgumentException
("A class cannot be it's own superclass: '"+qualifiedClassName+"'");
}
mSuperClassRef = QualifiedJClassRef.create(qualifiedClassName, this);
}
}
public void setSuperclassUnqualified(String unqualifiedClassName) {
mSuperClassRef = UnqualifiedJClassRef.create(unqualifiedClassName,this);
}
public void setSuperclass(JClass clazz) {
if (clazz == null) {
mSuperClassRef = null;
} else {
setSuperclass(clazz.getQualifiedName());
}
}
public void addInterface(JClass interf) {
if (interf == null) throw new IllegalArgumentException("null interf");
addInterface(interf.getQualifiedName());
}
public void addInterface(String qcName) {
if (mInterfaceRefs == null) mInterfaceRefs = new ArrayList();
if (qcName.equals(getQualifiedName())) {
throw new IllegalArgumentException
("A class cannot implement itself: '"+qcName+"'");
}
mInterfaceRefs.add(QualifiedJClassRef.create(qcName,this));
}
public void addInterfaceUnqualified(String ucname) {
if (mInterfaceRefs == null) mInterfaceRefs = new ArrayList();
mInterfaceRefs.add(UnqualifiedJClassRef.create(ucname,this));
}
public void removeInterface(JClass interf) {
if (interf == null) throw new IllegalArgumentException("null interf");
removeInterface(interf.getQualifiedName());
}
public void removeInterface(String qcname) {
//REVIEW this is quite inefficient, but maybe it doesnt matter so much
if (qcname == null) throw new IllegalArgumentException("null classname");
if (mInterfaceRefs == null) return;
for(int i=0; i<mInterfaceRefs.size(); i++) {
if (qcname.equals
(((JClassRef)mInterfaceRefs.get(i)).getQualifiedName())) {
mInterfaceRefs.remove(i);
}
}
}
public MConstructor addNewConstructor() {
if (mConstructors == null) mConstructors = new ArrayList();
MConstructor out = new ConstructorImpl(this);
mConstructors.add(out);
return out;
}
public void removeConstructor(MConstructor constr) {
if (mConstructors == null) return;
mConstructors.remove(constr);
}
public MConstructor[] getMutableConstructors() {
if (mConstructors == null || mConstructors.size() == 0) {
return new MConstructor[0];
}
MConstructor[] out = new MConstructor[mConstructors.size()];
mConstructors.toArray(out);
return out;
}
public MField addNewField() {
if (mFields == null) mFields = new ArrayList();
MField out = new FieldImpl(defaultName(mFields.size()),
this,"java.lang.Object");
mFields.add(out);
return out;
}
public void removeField(MField field) {
if (mFields == null) return;
mFields.remove(field);
}
public MField[] getMutableFields() {
if (mFields == null || mFields.size() == 0) {
return new MField[0];
}
MField[] out = new MField[mFields.size()];
mFields.toArray(out);
return out;
}
public MMethod addNewMethod() {
if (mMethods == null) mMethods = new ArrayList();
MMethod out = new MethodImpl(defaultName(mMethods.size()),this);
mMethods.add(out);
return out;
}
public void removeMethod(MMethod method) {
if (mMethods == null) return;
mMethods.remove(method);
}
public MMethod[] getMutableMethods() {
if (mMethods == null || mMethods.size() == 0) {
return new MMethod[0];
}
MMethod[] out = new MMethod[mMethods.size()];
mMethods.toArray(out);
return out;
}
public JProperty addNewProperty(String name, JMethod getter, JMethod setter) {
if (mProperties == null) mProperties = new ArrayList();
JProperty out = new PropertyImpl(name,getter,setter,
getter.getReturnType().getFieldDescriptor());
mProperties.add(out);
return out;
}
public void removeProperty(JProperty p) {
if (mProperties != null) mProperties.remove(p);
}
public JProperty addNewDeclaredProperty(String name, JMethod getter, JMethod setter) {
if (mDeclaredProperties == null) mDeclaredProperties = new ArrayList();
JProperty out = new PropertyImpl(name,getter,setter,
getter.getReturnType().getFieldDescriptor());
mDeclaredProperties.add(out);
return out;
}
public void removeDeclaredProperty(JProperty p) {
if (mDeclaredProperties != null) mDeclaredProperties.remove(p);
}
public MClass addNewInnerClass(String name) {
MClass inner = new ClassImpl(mPackageName,
name,
getContext(),
getImportSpecs());
if (mInnerClasses == null) mInnerClasses = new ArrayList();
mInnerClasses.add(inner);
return inner;
}
public void removeInnerClass(MClass clazz) {
if (mInnerClasses == null) return;
mInnerClasses.remove(clazz);
}
public void setIsInterface(boolean b) { mIsInterface = b; }
public void setIsAnnotationType(boolean b) { mIsAnnotationType = b; }
public void setIsEnumType(boolean b) { mIsEnum = b; }
public String getQualifiedName() {
return ((mPackageName.length() > 0) ? (mPackageName + '.') : "") +
mSimpleName;
}
// ========================================================================
// JClassRef implementation (to accommodate direct references)
public JClass getRefClass() { return this; }
// ========================================================================
// JClassRefContext implementation
public String getPackageName() {
return mPackageName;
}
public String[] getImportSpecs() {
ensureLoaded();
if (mImports == null) return new String[0];
return mImports;
}
// ========================================================================
// Public methods for internal use only
public void setState(int state) { mState = state; }
// ========================================================================
// Public static utility methods
/**
* Throws an IllegalArgument exception if the given string is not a valid
* class name. Useful for parameter checking in several places.
*/
public static void validateClassName(String className)
throws IllegalArgumentException
{
if (className == null) {
throw new IllegalArgumentException("null class name specified");
}
if (!Character.isJavaIdentifierStart(className.charAt(0))) {
throw new IllegalArgumentException
("Invalid first character in class name: "+className);
}
for(int i=1; i<className.length(); i++) {
char c = className.charAt(i);
if (c == '.') {
if (className.charAt(i-1) == '.') {
throw new IllegalArgumentException
("'..' not allowed in class name: "+className);
}
if (i == className.length()-1) {
throw new IllegalArgumentException
("'.' not allowed at end of class name: "+className);
}
} else {
if (!Character.isJavaIdentifierPart(c)) {
throw new IllegalArgumentException
("Illegal character '"+c+"' in class name: "+className);
}
}
}
}
// ========================================================================
// Private methods
private boolean isAssignableFromRecursively(JClass arg) {
if (this.getQualifiedName().equals(arg.getQualifiedName())) return true;
// check all of arg's implemented interfaces, recursively
JClass[] interfaces = arg.getInterfaces();
if (interfaces != null) {
for (int i=0; i<interfaces.length; i++) {
if (isAssignableFromRecursively(interfaces[i])) return true;
}
}
// check arg's superclass, recursively
arg = arg.getSuperclass();
if (arg != null) {
if (isAssignableFromRecursively(arg)) return true;
}
return false;
}
private void addFieldsRecursively(JClass clazz, Collection out) {
JField[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) out.add(fields[i]);
JClass[] ints = clazz.getInterfaces();
for (int i = 0; i < ints.length; i++) {
addFieldsRecursively(ints[i], out);
}
clazz = clazz.getSuperclass();
if (clazz != null) addFieldsRecursively(clazz, out);
}
private void addMethodsRecursively(JClass clazz, Collection out) {
JMethod[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) out.add(methods[i]);
JClass[] ints = clazz.getInterfaces();
for (int i = 0; i < ints.length; i++) {
addMethodsRecursively(ints[i], out);
}
clazz = clazz.getSuperclass();
if (clazz != null) addMethodsRecursively(clazz, out);
}
private void ensureLoaded() {
if (mState == LOADED) return;
if (mState == UNPOPULATED) {
if (mPopulator == null) throw new IllegalStateException("null populator");
setState(POPULATING);
mPopulator.populate(this);
setState(UNINITIALIZED);
}
if (mState == UNINITIALIZED) {
setState(INITIALIZING);
((JamClassLoaderImpl)getClassLoader()).initialize(this);
}
setState(ClassImpl.LOADED);
}
}