blob: 2ab63d1ab96d1dd39f2592ed37d088c86cd958fa [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* 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 com.android.jack.ir.ast;
import com.android.jack.Jack;
import com.android.jack.ir.SourceInfo;
import com.android.jack.load.ClassOrInterfaceLoader;
import com.android.jack.load.NopClassOrInterfaceLoader;
import com.android.jack.lookup.JMethodIdLookupException;
import com.android.jack.lookup.JMethodLookupException;
import com.android.jack.lookup.JMethodWithReturnLookupException;
import com.android.jack.util.NamingTools;
import com.android.sched.item.Description;
import com.android.sched.marker.Marker;
import com.android.sched.util.config.Location;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Base class for any reference type.
*/
@Description("Declared type")
public abstract class JDefinedClassOrInterface extends JDefinedReferenceType
implements JClassOrInterface, Annotable, CanBeAbstract, CanBeFinal {
private static final long serialVersionUID = 1L;
protected transient List<JField> fields = new ArrayList<JField>();
protected transient List<JMethod> methods = new ArrayList<JMethod>();
/**
* The type which originally enclosed this type. Null if this class was a
* top-level type. Note that all classes are converted to top-level types in
* {@code com.android.gwt.dev.jjs.impl.GenerateJavaAST}; this information is
* for tracking purposes.
*/
private JClassOrInterface enclosingType;
/**
* List of inner types i.e. the types whose enclosed by this type.
*/
@Nonnull
private final List<JClassOrInterface> inners = new ArrayList<JClassOrInterface>();
/**
* True if this type is defined in the bootclasspath or the classpath but not in a sourcepath.
*/
private boolean isExternal = true;
/**
* This type's modifier.
*/
private int modifier;
protected final AnnotationSet annotations = new AnnotationSet();
@Nonnull
private JPackage enclosingPackage;
@Nonnull
protected List<JMethodId> phantomMethods = new ArrayList<JMethodId>();
@Nonnull
protected List<JFieldId> phantomFields = new ArrayList<JFieldId>();
@Nonnull
protected final ClassOrInterfaceLoader loader;
@Nonnull
private final Location location;
public JDefinedClassOrInterface(@Nonnull SourceInfo info, @Nonnull String name, int modifier,
@Nonnull JPackage enclosingPackage) {
this(info, name, modifier, enclosingPackage, NopClassOrInterfaceLoader.INSTANCE);
assert NamingTools.isIdentifier(name);
}
public JDefinedClassOrInterface(@Nonnull SourceInfo info, @Nonnull String name, int modifier,
@Nonnull JPackage enclosingPackage, @Nonnull ClassOrInterfaceLoader loader) {
super(info, name);
assert NamingTools.isIdentifier(name) || "package-info".equals(name);
assert JModifier.isTypeModifier(modifier);
assert JModifier.isValidTypeModifier(modifier);
this.modifier = modifier;
this.enclosingPackage = enclosingPackage;
this.enclosingPackage.addType(this);
this.loader = loader;
location = loader.getLocation(this);
}
public void setModifier(int modifier) {
this.modifier = modifier;
}
/**
* Adds a field to this type.
*/
public void addField(@Nonnull JField field) {
assert field.getEnclosingType() == this;
assert getPhantomField(field.getName(), field.getType(), field.getId().getKind()) == null;
fields.add(field);
}
@Override
@CheckForNull
public <T extends Marker> T getMarker(@Nonnull Class<T> cls) {
loader.ensureMarker(this, cls);
return super.getMarker(cls);
}
@Override
@Nonnull
public Collection<Marker> getAllMarkers() {
loader.ensureMarkers(this);
return super.getAllMarkers();
}
@Override
public <T extends Marker> boolean containsMarker(@Nonnull Class<T> cls) {
loader.ensureMarker(this, cls);
return super.containsMarker(cls);
}
@Override
public <T extends Marker> T removeMarker(@Nonnull Class<T> cls) {
loader.ensureMarker(this, cls);
return super.removeMarker(cls);
}
/**
* Adds an implemented interface to this type.
*/
public void addImplements(JInterface superInterface) {
superInterfaces.add(superInterface);
}
public void removeImplements(int index) {
superInterfaces.remove(index);
}
public void setImplements(@Nonnull List<JInterface> superInterfaces) {
this.superInterfaces = superInterfaces;
}
@Override
@Nonnull
public List<JInterface> getImplements() {
loader.ensureHierarchy(this);
return super.getImplements();
}
@Override
public void setEnclosingPackage(@CheckForNull JPackage enclosingPackage) {
assert enclosingPackage != null;
this.enclosingPackage = enclosingPackage;
}
/**
* Adds a method to this type.
*/
public void addMethod(JMethod method) {
assert method.getEnclosingType() == this;
assert getPhantomMethod(method.getName(), method.getMethodId().getParamTypes(),
method.getMethodId().getKind()) == null;
methods.add(method);
}
/**
* Returns the type which encloses this type.
*
* @return The enclosing type. May be {@code null}.
*/
public JClassOrInterface getEnclosingType() {
loader.ensureEnclosing(this);
return enclosingType;
}
public JSession getSession() {
return enclosingPackage.getSession();
}
/**
* Returns this type's fields;does not include fields defined in a super type
* unless they are overridden by this type.
*/
public List<JField> getFields() {
loader.ensureFields(this);
return fields;
}
@Nonnull
public List<JField> getFields(@Nonnull String fieldName) {
loader.ensureFields(this, fieldName);
List<JField> fieldsFound = new ArrayList<JField>();
for (JField field : getFields()) {
if (field.getName().equals(fieldName)) {
fieldsFound.add(field);
}
}
return fieldsFound;
}
@Nonnull
@Override
public Collection<JFieldId> getPhantomFields() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(phantomFields);
}
@Override
@Nonnull
public JPackage getEnclosingPackage() {
return enclosingPackage;
}
/**
* Returns this type's declared methods; does not include methods defined in a
* super type unless they are overridden by this type.
*/
public List<JMethod> getMethods() {
loader.ensureMethods(this);
return methods;
}
@Nonnull
@Override
public Collection<JMethodId> getPhantomMethods() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(phantomMethods);
}
/**
* Returns the {@link JMethod} with the signature {@code signature} declared for this type.
*
* @return Returns the matching method if any, throws a {@link JMethodLookupException} otherwise.
*/
@Nonnull
public JMethod getMethod(@Nonnull String name, @Nonnull JType returnType,
@Nonnull List<? extends JType> args) throws JMethodLookupException {
loader.ensureMethod(this, name, args, returnType);
for (JMethod m : methods) {
if (m.getMethodId().equals(name, args) && (m.getType() == returnType)) {
return m;
}
}
throw new JMethodWithReturnLookupException(this, name, args, returnType);
}
/**
* Returns the {@link JMethod} with the signature {@code signature} declared for this type.
*
* @return Returns the matching method if any, throws a {@link JMethodLookupException} otherwise.
*/
public JMethod getMethod(@Nonnull String name, @Nonnull JType returnType,
@Nonnull JType... args) throws JMethodLookupException {
return (getMethod(name, returnType, Arrays.asList(args)));
}
/**
* Returns this type's super class, or <code>null</code> if this type is
* {@link Object} or an interface.
*/
@CheckForNull
public JClass getSuperClass() {
return null;
}
@Override
public boolean isExternal() {
return isExternal;
}
/**
* Removes the field at the specified index.
*/
public void removeField(int i) {
assert !isExternal() : "External types can not be modiified.";
fields.remove(i);
}
/**
* Removes the method at the specified index.
*/
public void removeMethod(int i) {
assert !isExternal() : "External types can not be modiified.";
methods.remove(i);
}
/**
* Sets the type which encloses this types.
*
* @param enclosingType May be {@code null}.
*/
public void setEnclosingType(JClassOrInterface enclosingType) {
this.enclosingType = enclosingType;
}
public void setExternal(boolean isExternal) {
this.isExternal = isExternal;
}
public int getModifier() {
loader.ensureModifier(this);
return modifier;
}
public boolean isPublic() {
return JModifier.isPublic(getModifier());
}
public boolean isProtected() {
return JModifier.isProtected(getModifier());
}
public boolean isPrivate() {
return JModifier.isPrivate(getModifier());
}
public boolean isStatic() {
return JModifier.isStatic(getModifier());
}
public boolean isStrictfp() {
return JModifier.isStrictfp(getModifier());
}
@Override
public boolean isAbstract() {
return JModifier.isAbstract(getModifier());
}
public void setAbstract() {
modifier = getModifier() | JModifier.ABSTRACT;
}
@Override
public boolean isFinal() {
return JModifier.isFinal(getModifier());
}
public void setFinal() {
modifier = getModifier() | JModifier.FINAL;
}
@Override
public void addAnnotation(@Nonnull JAnnotationLiteral annotation) {
annotations.addAnnotation(annotation);
}
@Override
@CheckForNull
public JAnnotationLiteral getAnnotation(@Nonnull JAnnotation annotationType) {
loader.ensureAnnotation(this, annotationType);
return annotations.getAnnotation(annotationType);
}
@Override
@Nonnull
public Collection<JAnnotationLiteral> getAnnotations() {
loader.ensureAnnotations(this);
return annotations.getAnnotations();
}
@Nonnull
public List<JClassOrInterface> getMemberTypes() {
loader.ensureInners(this);
return Jack.getUnmodifiableCollections().getUnmodifiableList(inners);
}
public void addMemberType(@Nonnull JClassOrInterface jDeclaredType) {
inners.add(jDeclaredType);
}
public void removeMemberType(@Nonnull JClassOrInterface jDeclaredType) {
int index = inners.indexOf(jDeclaredType);
if (index != -1) {
inners.remove(index);
}
}
@Override
protected void transform(@Nonnull JNode existingNode, @CheckForNull JNode newNode,
@Nonnull Transformation transformation) throws UnsupportedOperationException {
if (!transform(inners, existingNode, (JClassOrInterface) newNode, transformation)) {
if (!annotations.transform(existingNode, newNode, transformation)) {
super.transform(existingNode, newNode, transformation);
}
}
}
@Nonnull
@Override
public JMethodId getMethodId(@Nonnull String name, @Nonnull List<? extends JType> argsType,
@Nonnull MethodKind kind)
throws JMethodLookupException {
assert !(name.contains("(") || name.contains(")"));
loader.ensureMethods(this);
for (JMethod method : methods) {
JMethodId id = method.getMethodId();
if (id.equals(name, argsType)) {
return id;
}
}
for (JInterface jType : getImplements()) {
try {
return jType.getMethodId(name, argsType, kind);
} catch (JMethodLookupException e) {
// search next
}
}
JClass superClass = getSuperClass();
if (superClass != null) {
try {
return superClass.getMethodId(name, argsType, kind);
} catch (JMethodLookupException e) {
// let the following exception be thrown
}
}
throw new JMethodIdLookupException(this, name, argsType);
}
@Nonnull
@Override
public JMethodId getOrCreateMethodId(@Nonnull String name,
@Nonnull List<? extends JType> argsType,
@Nonnull MethodKind kind) {
try {
return getMethodId(name, argsType, kind);
} catch (JMethodLookupException e) {
JMethodId id = getPhantomMethod(name, argsType, kind);
if (id == null) {
id = new JMethodId(name, argsType, kind);
phantomMethods.add(id);
}
return id;
}
}
@Override
@Nonnull
public JFieldId getOrCreateFieldId(@Nonnull String name, @Nonnull JType type,
@Nonnull FieldKind kind) {
try {
return getFieldId(name, type, kind);
} catch (JFieldLookupException e) {
synchronized (phantomFields) {
JFieldId id = getPhantomField(name, type, kind);
if (id == null) {
id = new JFieldId(name, type, kind);
phantomFields.add(id);
}
return id;
}
}
}
@Override
@Nonnull
public JFieldId getFieldId(
@Nonnull String name, @Nonnull JType type,
@Nonnull FieldKind kind) {
loader.ensureFields(this);
for (JField field : fields) {
JFieldId id = field.getId();
if (id.equals(name, type, kind)) {
return id;
}
}
for (JInterface jType : getImplements()) {
try {
return jType.getFieldId(name, type, kind);
} catch (JFieldLookupException e) {
// search next
}
}
JClass superClass = getSuperClass();
if (superClass != null) {
try {
return superClass.getFieldId(name, type, kind);
} catch (JFieldLookupException e) {
// let the following exception be thrown
}
}
throw new JFieldLookupException(this, name, type);
}
@CheckForNull
private JMethodId getPhantomMethod(@Nonnull String name, @Nonnull List<? extends JType> argsType,
@Nonnull MethodKind kind) {
synchronized (phantomMethods) {
for (JMethodId id : phantomMethods) {
if (id.equals(name, argsType)) {
assert id.getKind() == kind;
return id;
}
}
}
return null;
}
@CheckForNull
private JFieldId getPhantomField(@Nonnull String name, @Nonnull JType type,
@Nonnull FieldKind kind) {
synchronized (phantomFields) {
for (JFieldId id : phantomFields) {
if (id.equals(name, type, kind)) {
return id;
}
}
}
return null;
}
@Nonnull
public ClassOrInterfaceLoader getLoader() {
return loader;
}
@Override
@CheckForNull
public JPrimitiveType getWrappedType() {
return getWrappedType(this);
}
@Nonnull
public Location getLocation() {
return location;
}
}