package rocks.inspectit.shared.all.instrumentation.classcache; import java.util.Collection; import java.util.Collections; import java.util.Set; import rocks.inspectit.shared.all.instrumentation.classcache.util.ArraySet; import rocks.inspectit.shared.all.instrumentation.classcache.util.TypeSet; import rocks.inspectit.shared.all.instrumentation.classcache.util.UpdateableSet; /** * Base type of most model types. * * @author Stefan Siegl */ public abstract class Type implements ImmutableType, TypeWithAnnotations, TypeWithModifiers { /** * The FQN of the type. */ protected final String fqn; /** * Marks whether the type is completely initialized. */ protected boolean initialized = false; /** * The hash of the byte code of this class. As we can have multiple version, we can keep a list * of hashes. */ protected Set<String> hashes; /** * The modifiers of the type. */ protected int modifiers; /** * A list of all annotations assigned to this type. */ private UpdateableSet<AnnotationType> annotations = null; /** * Creates a new <code> Type </code> instance. This constructor is usually invoked to add a * <code>Type</code> at a point when the hashes and the modifiers are not yet available. * * @param fqn * fully qualified name. */ public Type(String fqn) { this.fqn = fqn; } /** * Creates a new <code> Type </code> instance. This constructor is usually used when all * information is available. * * @param fqn * fully qualified name. * @param hash * the hash of the bytecode. * @param modifiers * the modifiers of the class. */ public Type(String fqn, String hash, int modifiers) { this.fqn = fqn; this.modifiers = modifiers; addHash(hash); initialized = true; } /** * Removes all references from the type which are only back references. The merging logic will * ensure that a given instance will only have forward references set by executing this method. */ public abstract void cleanUpBackReferences(); /** * Returns depending types of this type considered by the class loading. * <p> * For example, for a class the depending types are super class and all directly realized * interfaces. * * @return Returns depending types of this type */ public abstract Collection<Type> getDependingTypes(); /** * Removes any existing reference of this object from all the references. * <p> * Sub-classes should override to add their own removals. */ public void removeReferences() { for (AnnotationType annotationType : getAnnotations()) { annotationType.removeAnnotatedType(this); } } /** * {@inheritDoc} */ @Override public String getFQN() { return fqn; } /** * {@inheritDoc} */ public boolean hasMultipleVersions() { if (null == hashes) { return false; } return hashes.size() > 1; } /** * Adds a byte code hash. * * @param hash * the byte code hash. */ public final void addHash(String hash) { if (null == hashes) { hashes = new ArraySet<String>(1); } hashes.add(hash); checkInitialized(); } /** * {@inheritDoc} */ @Override public boolean containsHash(String hash) { if (null == hashes) { return false; } return hashes.contains(hash); } /** * {@inheritDoc} */ @Override public Set<String> getHashes() { if (null == hashes) { return Collections.emptySet(); } return hashes; } /** * {@inheritDoc} */ @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; checkInitialized(); } /** * Checks whether the type is completely initialized. */ protected final void checkInitialized() { if (!getHashes().isEmpty() && (0 != modifiers)) { initialized = true; } } /** * {@inheritDoc} */ @Override public boolean isInitialized() { return initialized; } /** * {@inheritDoc} */ @Override public void addAnnotation(AnnotationType annotationType) { addAnnotationNoBidirectionalUpdate(annotationType); annotationType.addAnnotatedType(this); } /** * {@inheritDoc} */ @Override public Set<? extends ImmutableAnnotationType> getImmutableAnnotations() { return getAnnotations(); } /** * {@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 boolean isType() { return true; } /** * {@inheritDoc} */ @Override public boolean isAnnotation() { return AnnotationType.class.isAssignableFrom(this.getClass()); } /** * {@inheritDoc} */ @Override public boolean isInterface() { return InterfaceType.class.isAssignableFrom(this.getClass()); } /** * {@inheritDoc} */ @Override public boolean isClass() { return ClassType.class.isAssignableFrom(this.getClass()); } /** * {@inheritDoc} */ @Override public boolean isMethodType() { return false; } /** * {@inheritDoc} */ @Override public ImmutableType castToType() { return this; } /** * {@inheritDoc} */ @Override public ImmutableClassType castToClass() { return (ImmutableClassType) this; } /** * {@inheritDoc} */ @Override public ImmutableAnnotationType castToAnnotation() { return (ImmutableAnnotationType) this; } /** * {@inheritDoc} */ @Override public ImmutableInterfaceType castToInterface() { return (ImmutableInterfaceType) this; } /** * {@inheritDoc} */ @Override public ImmutableMethodType castToMethodType() { return null; } /** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((fqn == null) ? 0 : fqn.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; } Type other = (Type) obj; if (fqn == null) { if (other.fqn != null) { return false; } } else if (!fqn.equals(other.fqn)) { return false; } return true; } }