package rocks.inspectit.shared.all.instrumentation.classcache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import rocks.inspectit.shared.all.instrumentation.classcache.util.TypeSet;
import rocks.inspectit.shared.all.instrumentation.classcache.util.UpdateableSet;
import rocks.inspectit.shared.all.instrumentation.config.impl.MethodInstrumentationConfig;
/**
* Models a method used in classes and interfaces.
*
* @author Stefan Siegl
* @author Ivan Senic
*/
public class MethodType implements TypeWithAnnotations, TypeWithModifiers, ImmutableMethodType {
/**
* The type of the method.
*
* @author Stefan Siegl
*/
public enum Character {
/** A method. */
METHOD,
/** A constructor. */
CONSTRUCTOR,
/** Static constructor. */
STATIC_CONSTRUCTOR;
}
/**
* The name of the method.
*/
private String name;
/**
* The modifiers of the method.
*/
private int modifiers;
/**
* Return type of the method.
*/
private String returnType;
/**
* Ordered list of all parameters of this method.
*/
private List<String> parameters = null;
/**
* List of all exceptions this method throws.
*/
private UpdateableSet<ClassType> exceptions = null;
/**
* List of annotations of this method.
*/
private UpdateableSet<AnnotationType> annotations = null;
/**
* The class this method belongs to.
*/
private TypeWithMethods classOrInterface;
/**
* {@link MethodInstrumentationConfig} holding this method instrumentation properties.
*/
private MethodInstrumentationConfig methodInstrumentationConfig;
/**
* Gets {@link #classType}.
*
* @return {@link #classType}
*/
public TypeWithMethods getClassOrInterfaceType() {
return classOrInterface;
}
/**
* Returns {@link ImmutableTypeWithMethods} being this method's class or interface.
*
* @return Returns {@link ImmutableTypeWithMethods} being this method's class or interface.
*/
@Override
public ImmutableTypeWithMethods getImmutableClassOrInterfaceType() {
return classOrInterface;
}
/**
* Sets {@link #classType}.
*
* @param type
* New value for {@link #classType}
*/
public void setClassOrInterfaceType(TypeWithMethods type) {
setClassOrInterfaceTypeNoBidirectionalUpdate(type);
type.addMethodNoBidirectionalUpdate(this);
}
/**
* Sets {@link #classType} without updating the back reference.
*
* @param type
* New value for {@link #classType}
*/
public void setClassOrInterfaceTypeNoBidirectionalUpdate(TypeWithMethods type) {
this.classOrInterface = type;
}
/**
* Gets {@link #name}.
*
* @return {@link #name}
*/
@Override
public String getName() {
return name;
}
/**
* Sets {@link #name}.
*
* @param name
* New value for {@link #name}
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets {@link #modifiers}.
*
* @return {@link #modifiers}
*/
@Override
public int getModifiers() {
return modifiers;
}
/**
* Sets {@link #modifiers}.
*
* @param modifiers
* New value for {@link #modifiers}
*/
@Override
public void setModifiers(int modifiers) {
this.modifiers = modifiers;
}
/**
* {@inheritDoc}
*/
@Override
public Character getMethodCharacter() {
if ("<init>".equals(name)) {
return Character.CONSTRUCTOR;
} else if ("<clinit>".equals(name)) {
return Character.STATIC_CONSTRUCTOR;
} else {
return Character.METHOD;
}
}
/**
* Gets {@link #returnType}.
*
* @return {@link #returnType}
*/
public String getReturnType() {
return returnType;
}
/**
* Sets {@link #returnType}.
*
* @param returnType
* New value for {@link #returnType}
*/
public void setReturnType(String returnType) {
this.returnType = returnType;
}
/**
* Gets {@link #parameters}.
*
* @return {@link #parameters}
*/
@Override
public List<String> getParameters() {
if (null == parameters) {
return Collections.emptyList();
}
return parameters;
}
/**
* Sets {@link #parameters}.
*
* @param parameters
* New value for {@link #parameters}
*/
public void setParameters(List<String> parameters) {
this.parameters = parameters;
}
/**
* sets a parameter at the specified index.
*
* @param index
* index
* @param type
* type.
*/
public void setParameterAt(int index, String type) {
if (null == parameters) {
initParameters();
}
parameters.set(index, type);
}
/**
* Init {@link #parameters}.
*/
private void initParameters() {
parameters = new ArrayList<String>(1);
}
/**
* Adds an exception thrown by this method and ensures that the back-reference on the referred
* entity is set as well.
*
* @param type
* the exception that is thrown.
*/
public void addException(ClassType type) {
addExceptionNoBidirectionalUpdate(type);
type.addMethodThrowingExceptionNoBidirectionalUpdate(this);
}
/**
* Adds an exception thrown by this method WITHOUT setting the back-reference. Please be aware
* that this method should only be called internally as this might mess up the bidirectional
* structure.
*
* @param type
* the exception that is thrown.
*/
public void addExceptionNoBidirectionalUpdate(ClassType type) {
if (null == exceptions) {
exceptions = new TypeSet<ClassType>();
}
exceptions.addOrUpdate(type);
}
/**
* Gets {@link #exceptions} as an unmodifiableSet. If you want to add something to the list, use
* the provided adders, as they ensure that the bidirectional links are created.
*
* @return {@link #exceptions}
*/
public Set<ClassType> getExceptions() {
if (null == exceptions) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(exceptions);
}
/**
* Removes the exception class from the type.
*
* @param type
* {@link ClassType} to remove.
*/
public void removeException(ClassType type) {
if (null == exceptions) {
return;
}
exceptions.remove(type);
}
/**
* {@inheritDoc}
*/
@Override
public Set<? extends ImmutableClassType> getImmutableExceptions() {
return getExceptions();
}
/**
* {@inheritDoc}
*/
@Override
public void addAnnotation(AnnotationType annotationType) {
addAnnotationNoBidirectionalUpdate(annotationType);
annotationType.addAnnotatedType(this);
}
/**
* {@inheritDoc}
*/
@Override
public void addAnnotationNoBidirectionalUpdate(AnnotationType annotationType) {
if (null == annotations) {
annotations = new TypeSet<AnnotationType>();
}
annotations.addOrUpdate(annotationType);
}
/**
* {@inheritDoc}
*/
@Override
public Set<AnnotationType> getAnnotations() {
if (null == annotations) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(annotations);
}
/**
* {@inheritDoc}
*/
@Override
public void removeAnnotation(AnnotationType annotationType) {
if (null == annotations) {
return;
}
annotations.remove(annotationType);
}
/**
* {@inheritDoc}
*/
@Override
public Set<? extends ImmutableAnnotationType> getImmutableAnnotations() {
return getAnnotations();
}
/**
* Gets {@link #methodInstrumentationConfig}.
*
* @return {@link #methodInstrumentationConfig}
*/
public MethodInstrumentationConfig getMethodInstrumentationConfig() {
return methodInstrumentationConfig;
}
/**
* Sets {@link #methodInstrumentationConfig}.
*
* @param methodInstrumentationConfig
* New value for {@link #methodInstrumentationConfig}
*/
public void setMethodInstrumentationConfig(MethodInstrumentationConfig methodInstrumentationConfig) {
this.methodInstrumentationConfig = methodInstrumentationConfig;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isType() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMethodType() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public ImmutableType castToType() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public ImmutableMethodType castToMethodType() {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((name == null) ? 0 : name.hashCode());
result = (prime * result) + ((parameters == null) ? 0 : parameters.hashCode());
result = (prime * result) + ((returnType == null) ? 0 : returnType.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MethodType other = (MethodType) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (parameters == null) {
if (other.parameters != null) {
return false;
}
} else if (!parameters.equals(other.parameters)) {
return false;
}
if (returnType == null) {
if (other.returnType != null) {
return false;
}
} else if (!returnType.equals(other.returnType)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "MethodType [name=" + name + ", modifiers=" + modifiers + ", methodCharacter=" + getMethodCharacter() + ", returnType=" + returnType + ", parameters=" + parameters + "]";
}
}