package rocks.inspectit.shared.all.instrumentation.classcache; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import rocks.inspectit.shared.all.instrumentation.classcache.util.MethodTypeSet; 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 Java class. * * @author Stefan Siegl * @author Ivan Senic */ public class ClassType extends Type implements TypeWithMethods, ImmutableClassType { /** * static final reference to the class name of the java.lang.Throwable class. */ private static final String FQN_THROWABLE = Throwable.class.getName(); /** * The super-classes of this class. As we want to combine several versions of the same class * into the same entity object, class can have more super-classes due to the changes over time. */ private UpdateableSet<ClassType> superClasses = null; /** * A list of all sub classes of this class. */ private UpdateableSet<ClassType> subClasses = null; /** * A list of all interfaces realized by this class. */ private UpdateableSet<AbstractInterfaceType> realizedInterfaces = null; /** * A list of all methods of this interface. */ private Set<MethodType> methods = null; /** * A list of all methods that throw this exception. Only filled if this class is an exception. */ private Set<MethodType> methodsThrowingThisException = null; /** * No-arg constructor for serialization. */ protected ClassType() { super(null); } /** * Creates a new <code> ClassType </code> without setting the hash and the modifiers. This * constructor is usually used if you want to add the entity without the class being loaded. * * @param fqn * fully qualified name. */ public ClassType(String fqn) { super(fqn); } /** * Creates a new <code> ClassType </code>. This constructor is usually used if the annotation is * loaded. * * @param fqn * fully qualified name. * @param hash * the hash of the byte code. * @param modifiers * the modifiers of the annotation. */ public ClassType(String fqn, String hash, int modifiers) { super(fqn, hash, modifiers); } /** * Adds a class that is annotated with this annotation and ensures that the back-reference on * the referred entity is set as well. * * @param type * the class that uses this annotation. */ public void addInterface(AbstractInterfaceType type) { addInterfaceNoBidirectionalUpdate(type); type.addRealizingClassNoBidirectionalUpdate(this); } /** * Adds a class that is annotated with this annotation 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 class that uses this annotation. */ public void addInterfaceNoBidirectionalUpdate(AbstractInterfaceType type) { if (null == realizedInterfaces) { realizedInterfaces = new TypeSet<AbstractInterfaceType>(); } realizedInterfaces.addOrUpdate(type); } /** * Gets {@link #realizedInterfaces} 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 #realizedInterfaces} */ public Set<AbstractInterfaceType> getRealizedInterfaces() { if (null == realizedInterfaces) { return Collections.emptySet(); } return Collections.unmodifiableSet(realizedInterfaces); } /** * Removes the given interface from the type. * * @param type * {@link AbstractInterfaceType} to remove. */ public void removeInterface(AbstractInterfaceType type) { if (null == realizedInterfaces) { return; } realizedInterfaces.remove(type); } /** * {@inheritDoc} */ @Override public Set<? extends ImmutableAbstractInterfaceType> getImmutableRealizedInterfaces() { return getRealizedInterfaces(); } /** * Adds a method that this class contains and ensures that the back-reference on the referred * entity is set as well. * * @param type * the method that is defined in this class. */ @Override public void addMethod(MethodType type) { addMethodNoBidirectionalUpdate(type); type.setClassOrInterfaceTypeNoBidirectionalUpdate(this); } /** * Adds a method that this class contains 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 method that is defined in this class. */ @Override public void addMethodNoBidirectionalUpdate(MethodType type) { if (methods == null) { methods = new MethodTypeSet(); } methods.add(type); } /** * Gets {@link #methods} 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 #methods} */ @Override public Set<MethodType> getMethods() { if (null == methods) { return Collections.emptySet(); } return Collections.unmodifiableSet(methods); } /** * {@inheritDoc} */ @Override public Set<? extends ImmutableMethodType> getImmutableMethods() { return getMethods(); } /** * Adds a superclass of this class and ensures that the back-reference on the referred entity is * set as well. * * @param type * the superclass of this class. */ public void addSuperClass(ClassType type) { addSuperClassNoBidirectionalUpdate(type); type.addSubclassNoBidirectionalUpdate(this); } /** * Adds the superclass of this class 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 method that is defined in this class. */ public void addSuperClassNoBidirectionalUpdate(ClassType type) { if (null == superClasses) { superClasses = new TypeSet<ClassType>(); } superClasses.addOrUpdate(type); } /** * Gets {@link #superClasses} 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 #superClasses} */ public Set<ClassType> getSuperClasses() { if (null == superClasses) { return Collections.emptySet(); } return Collections.unmodifiableSet(superClasses); } /** * Removes the super class from the type. * * @param type * {@link ClassType} to remove. */ public void removeSuperClass(ClassType type) { if (null == superClasses) { return; } superClasses.remove(type); } /** * {@inheritDoc} */ @Override public Set<? extends ImmutableClassType> getImmutableSuperClasses() { return getSuperClasses(); } /** * Adds a subclass of this class and ensures that the back-reference on the referred entity is * set as well. * * @param type * the subclass of this class. */ public void addSubclass(ClassType type) { addSubclassNoBidirectionalUpdate(type); type.addSuperClassNoBidirectionalUpdate(this); } /** * Adds a subclass of this class 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 subclass of this class. */ public void addSubclassNoBidirectionalUpdate(ClassType type) { if (null == subClasses) { subClasses = new TypeSet<ClassType>(); } subClasses.addOrUpdate(type); } /** * Gets {@link #subClasses} as an unmodifiableSer. If you want to add something to the list, use * the provided adders, as they ensure that the bidirectional links are created. * * @return {@link #subClasses} */ public Set<ClassType> getSubClasses() { if (null == subClasses) { return Collections.emptySet(); } return Collections.unmodifiableSet(subClasses); } /** * Removes the sub class from the type. * * @param type * {@link ClassType} to remove. */ public void removeSubClass(ClassType type) { if (null == subClasses) { return; } subClasses.remove(type); } /** * {@inheritDoc} */ @Override public Set<? extends ImmutableClassType> getImmutableSubClasses() { return getSubClasses(); } /** * Adds a method that is throwing this class as exception and ensures that the back-reference on * the referred entity is set as well. * * @param type * a method that is throwing this class as exception */ public void addMethodThrowingException(MethodType type) { addMethodThrowingExceptionNoBidirectionalUpdate(type); type.addExceptionNoBidirectionalUpdate(this); } /** * Adds a method that is throwing this class as exception 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 * a method that is throwing this class as exception */ public void addMethodThrowingExceptionNoBidirectionalUpdate(MethodType type) { if (null == methodsThrowingThisException) { methodsThrowingThisException = new MethodTypeSet(); } methodsThrowingThisException.add(type); } /** * Gets {@link #methodsThrowingThisException} 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 #methodsThrowingThisException} */ public Set<MethodType> getMethodsThrowingThisException() { if (null == methodsThrowingThisException) { return Collections.emptySet(); } return Collections.unmodifiableSet(methodsThrowingThisException); } /** * {@inheritDoc} */ @Override public void cleanUpBackReferences() { if (null != methodsThrowingThisException) { methodsThrowingThisException.clear(); } if (null != subClasses) { subClasses.clear(); } } /** * {@inheritDoc} */ @Override public Collection<Type> getDependingTypes() { Collection<Type> types = new ArrayList<Type>(0); if (CollectionUtils.isNotEmpty(superClasses)) { types.addAll(superClasses); } if (CollectionUtils.isNotEmpty(realizedInterfaces)) { types.addAll(realizedInterfaces); } return types; } /** * {@inheritDoc} */ @Override public void removeReferences() { super.removeReferences(); for (AbstractInterfaceType interfaceType : getRealizedInterfaces()) { interfaceType.removeRealizingClass(this); } for (ClassType superClass : getSuperClasses()) { superClass.removeSubClass(this); } for (ClassType subClass : getSubClasses()) { subClass.removeSuperClass(this); } for (MethodType methodType : getMethodsThrowingThisException()) { methodType.removeException(this); } } /** * {@inheritDoc} */ @Override public boolean isException() { return isSubClassOf(FQN_THROWABLE); } /** * {@inheritDoc} */ @Override public boolean isSubClassOf(String superClassFqn) { if (null == superClassFqn) { return false; } if (CollectionUtils.isNotEmpty(superClasses)) { for (ClassType superClass : superClasses) { if (superClassFqn.equals(superClass.getFQN())) { return true; } else if (superClass.isSubClassOf(superClassFqn)) { return true; } } } return false; } /** * {@inheritDoc} */ @Override public boolean hasInstrumentationPoints() { if (CollectionUtils.isEmpty(methods)) { return false; } for (MethodType methodType : methods) { if (null != methodType.getMethodInstrumentationConfig()) { return true; } } return false; } /** * {@inheritDoc} */ @Override public Collection<MethodInstrumentationConfig> getInstrumentationPoints() { if (CollectionUtils.isEmpty(methods)) { return Collections.emptyList(); } Collection<MethodInstrumentationConfig> instrumentationPoints = new ArrayList<MethodInstrumentationConfig>(); for (MethodType methodType : methods) { CollectionUtils.addIgnoreNull(instrumentationPoints, methodType.getMethodInstrumentationConfig()); } return instrumentationPoints; } /** * {@inheritDoc} */ @Override public String toString() { return "ClassType [fqn=" + fqn + ", hashes=" + hashes + "]"; } }