// Generated by delombok at Sun Feb 26 12:31:38 KST 2017
package scouter.bytebuddy.dynamic.scaffold;
import scouter.bytebuddy.description.method.MethodDescription;
import scouter.bytebuddy.description.method.MethodList;
import scouter.bytebuddy.description.modifier.Visibility;
import scouter.bytebuddy.description.type.TypeDefinition;
import scouter.bytebuddy.description.type.TypeDescription;
import scouter.bytebuddy.matcher.ElementMatcher;
import scouter.bytebuddy.matcher.FilterableList;
import scouter.bytebuddy.jar.asm.Opcodes;
import scouter.bytebuddy.matcher.ElementMatchers;
import java.util.*;
/**
* A method graph represents a view on a set of methods as they are seen from a given type. Any method is represented as a node that represents
* a method, its bridge methods, its resolution state and information on if it was made visible by a visibility bridge.
*/
public interface MethodGraph {
/**
* Locates a node in this graph which represents the provided method token.
*
* @param token A method token that represents the method to be located.
* @return The node representing the given token.
*/
Node locate(MethodDescription.SignatureToken token);
/**
* Lists all nodes of this method graph.
*
* @return A list of all nodes of this method graph.
*/
NodeList listNodes();
/**
* A canonical implementation of an empty method graph.
*/
enum Empty implements MethodGraph.Linked, MethodGraph.Compiler {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public Node locate(MethodDescription.SignatureToken token) {
return Node.Unresolved.INSTANCE;
}
@Override
public NodeList listNodes() {
return new NodeList(Collections.<Node>emptyList());
}
@Override
public MethodGraph getSuperClassGraph() {
return this;
}
@Override
public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
return this;
}
@Override
public Linked compile(TypeDescription typeDescription) {
return this;
}
@Override
public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
return this;
}
}
/**
* A linked method graph represents a view that additionally exposes information of a given type's super type view and a
* view on this graph's directly implemented interfaces.
*/
interface Linked extends MethodGraph {
/**
* Returns a graph representing the view on this represented type's super type.
*
* @return A graph representing the view on this represented type's super type.
*/
MethodGraph getSuperClassGraph();
/**
* Returns a graph representing the view on this represented type's directly implemented interface type.
*
* @param typeDescription The interface type for which a view is to be returned.
* @return A graph representing the view on this represented type's directly implemented interface type.
*/
MethodGraph getInterfaceGraph(TypeDescription typeDescription);
/**
* A simple implementation of a linked method graph that exposes views by delegation to given method graphs.
*/
class Delegation implements Linked {
/**
* The represented type's method graph.
*/
private final MethodGraph methodGraph;
/**
* The super class's method graph.
*/
private final MethodGraph superClassGraph;
/**
* A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
*/
private final Map<TypeDescription, MethodGraph> interfaceGraphs;
/**
* Creates a new delegation method graph.
*
* @param methodGraph The represented type's method graph.
* @param superClassGraph The super class's method graph.
* @param interfaceGraphs A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
*/
public Delegation(MethodGraph methodGraph, MethodGraph superClassGraph, Map<TypeDescription, MethodGraph> interfaceGraphs) {
this.methodGraph = methodGraph;
this.superClassGraph = superClassGraph;
this.interfaceGraphs = interfaceGraphs;
}
@Override
public MethodGraph getSuperClassGraph() {
return superClassGraph;
}
@Override
public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
MethodGraph interfaceGraph = interfaceGraphs.get(typeDescription);
return interfaceGraph == null ? Empty.INSTANCE : interfaceGraph;
}
@Override
public Node locate(MethodDescription.SignatureToken token) {
return methodGraph.locate(token);
}
@Override
public NodeList listNodes() {
return methodGraph.listNodes();
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Linked.Delegation)) return false;
final MethodGraph.Linked.Delegation other = (MethodGraph.Linked.Delegation) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$methodGraph = this.methodGraph;
final java.lang.Object other$methodGraph = other.methodGraph;
if (this$methodGraph == null ? other$methodGraph != null : !this$methodGraph.equals(other$methodGraph)) return false;
final java.lang.Object this$superClassGraph = this.getSuperClassGraph();
final java.lang.Object other$superClassGraph = other.getSuperClassGraph();
if (this$superClassGraph == null ? other$superClassGraph != null : !this$superClassGraph.equals(other$superClassGraph)) return false;
final java.lang.Object this$interfaceGraphs = this.interfaceGraphs;
final java.lang.Object other$interfaceGraphs = other.interfaceGraphs;
if (this$interfaceGraphs == null ? other$interfaceGraphs != null : !this$interfaceGraphs.equals(other$interfaceGraphs)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Linked.Delegation;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $methodGraph = this.methodGraph;
result = result * PRIME + ($methodGraph == null ? 43 : $methodGraph.hashCode());
final java.lang.Object $superClassGraph = this.getSuperClassGraph();
result = result * PRIME + ($superClassGraph == null ? 43 : $superClassGraph.hashCode());
final java.lang.Object $interfaceGraphs = this.interfaceGraphs;
result = result * PRIME + ($interfaceGraphs == null ? 43 : $interfaceGraphs.hashCode());
return result;
}
}
}
/**
* Represents a node within a method graph.
*/
interface Node {
/**
* Returns the sort of this node.
*
* @return The sort of this node.
*/
Sort getSort();
/**
* Returns the method that is represented by this node.
*
* @return The method that is represented by this node.
*/
MethodDescription getRepresentative();
/**
* Returns a set of type tokens that this method represents. This set contains the actual method's type including the
* types of all bridge methods.
*
* @return A set of type tokens that this method represents.
*/
Set<MethodDescription.TypeToken> getMethodTypes();
/**
* Returns the minimal method visibility of all methods that are represented by this node.
*
* @return The minimal method visibility of all methods that are represented by this node.
*/
Visibility getVisibility();
/**
* Represents a {@link MethodGraph.Node}'s state.
*/
enum Sort {
/**
* Represents a resolved node that was made visible by a visibility bridge.
*/
VISIBLE(true, true, true), /**
* Represents a resolved node that was not made visible by a visibility bridge.
*/
RESOLVED(true, true, false), /**
* Represents an ambiguous node, i.e. a node that might refer to several methods.
*/
AMBIGUOUS(true, false, false), /**
* Represents an unresolved node.
*/
UNRESOLVED(false, false, false);
/**
* {@code true} if this sort represents a resolved node.
*/
private final boolean resolved;
/**
* {@code true} if this sort represents a non-ambiguous node.
*/
private final boolean unique;
/**
* {@code true} if this sort represents a node that was made by a visibility bridge.
*/
private final boolean madeVisible;
/**
* Creates a new sort.
*
* @param resolved {@code true} if this sort represents a resolved node.
* @param unique {@code true} if this sort represents a non-ambiguous node.
* @param madeVisible {@code true} if this sort represents a node that was made by a visibility bridge.
*/
Sort(boolean resolved, boolean unique, boolean madeVisible) {
this.resolved = resolved;
this.unique = unique;
this.madeVisible = madeVisible;
}
/**
* Verifies if this sort represents a resolved node.
*
* @return {@code true} if this sort represents a resolved node.
*/
public boolean isResolved() {
return resolved;
}
/**
* Verifies if this sort represents a non-ambiguous node.
*
* @return {@code true} if this sort represents a non-ambiguous node.
*/
public boolean isUnique() {
return unique;
}
/**
* Verifies if this sort represents a node that was made visible by a visibility bridge.
*
* @return {@code true} if this sort represents a node that was made visible by a visibility bridge.
*/
public boolean isMadeVisible() {
return madeVisible;
}
}
/**
* A canonical implementation of an unresolved node.
*/
enum Unresolved implements Node {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public Sort getSort() {
return Sort.UNRESOLVED;
}
@Override
public MethodDescription getRepresentative() {
throw new IllegalStateException("Cannot resolve the method of an illegal node");
}
@Override
public Set<MethodDescription.TypeToken> getMethodTypes() {
throw new IllegalStateException("Cannot resolve bridge method of an illegal node");
}
@Override
public Visibility getVisibility() {
throw new IllegalStateException("Cannot resolve visibility of an illegal node");
}
}
/**
* A simple implementation of a resolved node of a method without bridges.
*/
class Simple implements Node {
/**
* The represented method.
*/
private final MethodDescription methodDescription;
/**
* Creates a simple node.
*
* @param methodDescription The represented method.
*/
public Simple(MethodDescription methodDescription) {
this.methodDescription = methodDescription;
}
@Override
public Sort getSort() {
return Sort.RESOLVED;
}
@Override
public MethodDescription getRepresentative() {
return methodDescription;
}
@Override
public Set<MethodDescription.TypeToken> getMethodTypes() {
return Collections.emptySet();
}
@Override
public Visibility getVisibility() {
return methodDescription.getVisibility();
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Node.Simple)) return false;
final MethodGraph.Node.Simple other = (MethodGraph.Node.Simple) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$methodDescription = this.methodDescription;
final java.lang.Object other$methodDescription = other.methodDescription;
if (this$methodDescription == null ? other$methodDescription != null : !this$methodDescription.equals(other$methodDescription)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Node.Simple;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $methodDescription = this.methodDescription;
result = result * PRIME + ($methodDescription == null ? 43 : $methodDescription.hashCode());
return result;
}
}
}
/**
* A compiler to produce a {@link MethodGraph} from a given type.
*/
interface Compiler {
/**
* The default compiler for compiling Java methods.
*/
Compiler DEFAULT = MethodGraph.Compiler.Default.forJavaHierarchy();
/**
* Compiles the given type into a method graph considering the type to be the viewpoint.
*
* @param typeDescription The type to be compiled.
* @return A linked method graph representing the given type.
*/
MethodGraph.Linked compile(TypeDescription typeDescription);
/**
* Compiles the given type into a method graph.
*
* @param typeDefinition The type to be compiled.
* @param viewPoint The view point that determines the method's visibility.
* @return A linked method graph representing the given type.
*/
MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint);
/**
* A flat compiler that simply returns the methods that are declared by the instrumented type.
*/
enum ForDeclaredMethods implements Compiler {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public Linked compile(TypeDescription typeDescription) {
return compile(typeDescription, typeDescription);
}
@Override
public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
for (MethodDescription methodDescription : typeDefinition.getDeclaredMethods().filter(ElementMatchers.isVirtual().and(ElementMatchers.not(ElementMatchers.isBridge())).and(ElementMatchers.isVisibleTo(viewPoint)))) {
nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
}
return new Linked.Delegation(new MethodGraph.Simple(nodes), Empty.INSTANCE, Collections.<TypeDescription, MethodGraph>emptyMap());
}
}
/**
* An abstract base implementation of a method graph compiler.
*/
abstract class AbstractBase implements Compiler {
@Override
public Linked compile(TypeDescription typeDescription) {
return compile(typeDescription, typeDescription);
}
}
/**
* A default implementation of a method graph.
*
* @param <T> The type of the harmonizer token to be used for linking methods of different types.
*/
class Default<T> extends AbstractBase {
/**
* The harmonizer to be used.
*/
private final Harmonizer<T> harmonizer;
/**
* The merger to be used.
*/
private final Merger merger;
/**
* A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
*/
private final TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor;
/**
* Creates a new default method graph compiler.
*
* @param harmonizer The harmonizer to be used.
* @param merger The merger to be used.
* @param visitor A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
*/
protected Default(Harmonizer<T> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
this.harmonizer = harmonizer;
this.merger = merger;
this.visitor = visitor;
}
/**
* Creates a default compiler using the given harmonizer and merger. All raw types are reified before analyzing their properties.
*
* @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
* @param merger The merger to be used for identifying a method to represent an ambiguous method resolution.
* @param <S> The type of the harmonizer token.
* @return A default compiler for the given harmonizer and merger.
*/
public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger) {
return new Default<S>(harmonizer, merger, TypeDescription.Generic.Visitor.Reifying.INITIATING);
}
/**
* Creates a default compiler using the given harmonizer and merger.
*
* @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
* @param merger The merger to be used for identifying a method to represent an ambiguous method resolution.
* @param visitor A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
* @param <S> The type of the harmonizer token.
* @return A default compiler for the given harmonizer and merger.
*/
public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
return new Default<S>(harmonizer, merger, visitor);
}
/**
* <p>
* Creates a default compiler for a method hierarchy following the rules of the Java programming language. According
* to these rules, two methods of the same name are only different if their parameter types represent different raw
* types. The return type is not considered as a part of the signature.
* </p>
* <p>
* Ambiguous methods are merged by considering the method that was discovered first.
* </p>
*
* @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
*/
public static Compiler forJavaHierarchy() {
return of(Harmonizer.ForJavaMethod.INSTANCE, Merger.Directional.LEFT);
}
/**
* <p>
* Creates a default compiler for a method hierarchy following the rules of the Java virtual machine. According
* to these rules, two methods of the same name are different if their parameter types and return types represent
* different type erasures.
* </p>
* <p>
* Ambiguous methods are merged by considering the method that was discovered first.
* </p>
*
* @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
*/
public static Compiler forJVMHierarchy() {
return of(Harmonizer.ForJVMMethod.INSTANCE, Merger.Directional.LEFT);
}
@Override
public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
Map<TypeDefinition, Key.Store<T>> snapshots = new HashMap<TypeDefinition, Key.Store<T>>();
Key.Store<?> rootStore = doAnalyze(typeDefinition, snapshots, ElementMatchers.isVirtual().and(ElementMatchers.isVisibleTo(viewPoint)));
TypeDescription.Generic superClass = typeDefinition.getSuperClass();
List<TypeDescription.Generic> interfaceTypes = typeDefinition.getInterfaces();
Map<TypeDescription, MethodGraph> interfaceGraphs = new HashMap<TypeDescription, MethodGraph>();
for (TypeDescription.Generic interfaceType : interfaceTypes) {
interfaceGraphs.put(interfaceType.asErasure(), snapshots.get(interfaceType).asGraph(merger));
}
return new Linked.Delegation(rootStore.asGraph(merger), superClass == null ? Empty.INSTANCE : snapshots.get(superClass).asGraph(merger), interfaceGraphs);
}
/**
* Analyzes the given type description without checking if the end of the type hierarchy was reached.
*
* @param typeDefinition The type to analyze.
* @param key The type in its original form before applying the visitor.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> analyze(TypeDefinition typeDefinition, TypeDefinition key, Map<TypeDefinition, Key.Store<T>> snapshots, ElementMatcher<? super MethodDescription> relevanceMatcher) {
Key.Store<T> store = snapshots.get(key);
if (store == null) {
store = doAnalyze(typeDefinition, snapshots, relevanceMatcher);
snapshots.put(key, store);
}
return store;
}
/**
* Analyzes the given type description.
*
* @param typeDescription The type to analyze.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> analyzeNullable(TypeDescription.Generic typeDescription, Map<TypeDefinition, Key.Store<T>> snapshots, ElementMatcher<? super MethodDescription> relevanceMatcher) {
return typeDescription == null ? new Key.Store<T>() : analyze(typeDescription.accept(visitor), typeDescription, snapshots, relevanceMatcher);
}
/**
* Analyzes the given type description without checking if it is already presented in the key store.
*
* @param typeDefinition The type to analyze.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> doAnalyze(TypeDefinition typeDefinition, Map<TypeDefinition, Key.Store<T>> snapshots, ElementMatcher<? super MethodDescription> relevanceMatcher) {
Key.Store<T> store = analyzeNullable(typeDefinition.getSuperClass(), snapshots, relevanceMatcher);
Key.Store<T> interfaceStore = new Key.Store<T>();
for (TypeDescription.Generic interfaceType : typeDefinition.getInterfaces()) {
interfaceStore = interfaceStore.combineWith(analyze(interfaceType.accept(visitor), interfaceType, snapshots, relevanceMatcher));
}
store = store.inject(interfaceStore);
for (MethodDescription methodDescription : typeDefinition.getDeclaredMethods().filter(relevanceMatcher)) {
store = store.registerTopLevel(methodDescription, harmonizer);
}
return store;
}
/**
* A harmonizer is responsible for creating a token that identifies a method's relevant attributes for considering
* two methods of being equal or not.
*
* @param <S> The type of the token that is created by the implementing harmonizer.
*/
public interface Harmonizer<S> {
/**
* Harmonizes the given type token.
*
* @param typeToken The type token to harmonize.
* @return A token representing the given type token.
*/
S harmonize(MethodDescription.TypeToken typeToken);
/**
* A harmonizer for the Java programming language that identifies a method by its parameter types only.
*/
enum ForJavaMethod implements Harmonizer<ForJavaMethod.Token> {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public Token harmonize(MethodDescription.TypeToken typeToken) {
return new Token(typeToken);
}
/**
* A token that identifies a Java method's type by its parameter types only.
*/
protected static class Token {
/**
* The represented type token.
*/
private final MethodDescription.TypeToken typeToken;
/**
* Creates a new type token for a Java method.
*
* @param typeToken The represented type token.
*/
protected Token(MethodDescription.TypeToken typeToken) {
this.typeToken = typeToken;
}
@Override
public boolean equals(Object other) {
return this == other || (other instanceof Token && typeToken.getParameterTypes().equals(((Token) other).typeToken.getParameterTypes()));
}
@Override
public int hashCode() {
return typeToken.getParameterTypes().hashCode();
}
@Override
public String toString() {
return typeToken.getParameterTypes().toString();
}
}
}
/**
* A harmonizer for the Java virtual machine's method dispatching rules that identifies a method by its parameter types and return type.
*/
enum ForJVMMethod implements Harmonizer<ForJVMMethod.Token> {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public Token harmonize(MethodDescription.TypeToken typeToken) {
return new Token(typeToken);
}
/**
* A token that identifies a Java method's type by its parameter types and return type.
*/
protected static class Token {
/**
* The represented type token.
*/
private final MethodDescription.TypeToken typeToken;
/**
* Creates a new type token for a JVM method.
*
* @param typeToken The represented type token.
*/
public Token(MethodDescription.TypeToken typeToken) {
this.typeToken = typeToken;
}
@Override
public boolean equals(Object other) {
return this == other || other instanceof Token && typeToken.getReturnType().equals(((Token) other).typeToken.getReturnType()) && typeToken.getParameterTypes().equals(((Token) other).typeToken.getParameterTypes());
}
@Override
public int hashCode() {
return typeToken.getReturnType().hashCode() + 31 * typeToken.getParameterTypes().hashCode();
}
@Override
public String toString() {
return typeToken.toString();
}
}
}
}
/**
* Implementations are responsible for identifying a representative method for a {@link MethodGraph.Node}
* between several ambiguously resolved methods.
*/
public interface Merger {
/**
* Merges two ambiguously resolved methods to yield a single representative.
*
* @param left The left method description, i.e. the method that was discovered first or was previously merged.
* @param right The right method description, i.e. the method that was discovered last.
* @return A method description compatible to both method's types that is used as a representative.
*/
MethodDescription merge(MethodDescription left, MethodDescription right);
/**
* A directional merger that always returns either the left or right method description.
*/
enum Directional implements Merger {
/**
* A merger that always returns the left method, i.e. the method that was discovered first or was previously merged.
*/
LEFT(true),
/**
* A merger that always returns the right method, i.e. the method that was discovered last.
*/
RIGHT(false);
/**
* {@code true} if the left method should be returned when merging methods.
*/
private final boolean left;
/**
* Creates a directional merger.
*
* @param left {@code true} if the left method should be returned when merging methods.
*/
Directional(boolean left) {
this.left = left;
}
@Override
public MethodDescription merge(MethodDescription left, MethodDescription right) {
return this.left ? left : right;
}
}
}
/**
* A key represents a collection of methods within a method graph to later yield a node representing a collection of methods,
* i.e. a method representative including information on the required method bridges.
*
* @param <S> The type of the token used for deciding on method equality.
*/
protected static abstract class Key<S> {
/**
* The internal name of the method this key identifies.
*/
protected final String internalName;
/**
* Creates a new key.
*
* @param internalName The internal name of the method this key identifies.
*/
protected Key(String internalName) {
this.internalName = internalName;
}
/**
* Returns a set of all identifiers of this key.
*
* @return A set of all identifiers of this key.
*/
protected abstract Set<S> getIdentifiers();
@Override
public boolean equals(Object other) {
return other == this || (other instanceof Key && internalName.equals(((Key) other).internalName) && !Collections.disjoint(getIdentifiers(), ((Key) other).getIdentifiers()));
}
@Override
public int hashCode() {
return internalName.hashCode();
}
/**
* A harmonized key represents a key where equality is decided based on tokens that are returned by a
* {@link MethodGraph.Compiler.Default.Harmonizer}.
*
* @param <V> The type of the tokens yielded by a harmonizer.
*/
protected static class Harmonized<V> extends Key<V> {
/**
* A mapping of identifiers to the type tokens they represent.
*/
private final Map<V, Set<MethodDescription.TypeToken>> identifiers;
/**
* Creates a new harmonized key.
*
* @param internalName The internal name of the method this key identifies.
* @param identifiers A mapping of identifiers to the type tokens they represent.
*/
protected Harmonized(String internalName, Map<V, Set<MethodDescription.TypeToken>> identifiers) {
super(internalName);
this.identifiers = identifiers;
}
/**
* Creates a new harmonized key for the given method description.
*
* @param methodDescription The method description to represent as a harmonized key.
* @param harmonizer The harmonizer to use.
* @param <Q> The type of the token yielded by a harmonizer.
* @return A harmonized key representing the provided method.
*/
protected static <Q> Harmonized<Q> of(MethodDescription methodDescription, Harmonizer<Q> harmonizer) {
MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
return new Harmonized<Q>(methodDescription.getInternalName(), Collections.singletonMap(harmonizer.harmonize(typeToken), Collections.<MethodDescription.TypeToken>emptySet()));
}
/**
* Creates a detached version of this key.
*
* @param typeToken The type token of the representative method.
* @return The detached version of this key.
*/
protected Detached detach(MethodDescription.TypeToken typeToken) {
Set<MethodDescription.TypeToken> identifiers = new HashSet<MethodDescription.TypeToken>();
for (Set<MethodDescription.TypeToken> typeTokens : this.identifiers.values()) {
identifiers.addAll(typeTokens);
}
identifiers.add(typeToken);
return new Detached(internalName, identifiers);
}
/**
* Combines this key with the given key.
*
* @param key The key to be merged with this key.
* @return A harmonized key representing the merger of this key and the given key.
*/
protected Harmonized<V> combineWith(Harmonized<V> key) {
Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
for (Map.Entry<V, Set<MethodDescription.TypeToken>> entry : key.identifiers.entrySet()) {
Set<MethodDescription.TypeToken> typeTokens = identifiers.get(entry.getKey());
if (typeTokens == null) {
identifiers.put(entry.getKey(), entry.getValue());
} else {
typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
typeTokens.addAll(entry.getValue());
identifiers.put(entry.getKey(), typeTokens);
}
}
return new Harmonized<V>(internalName, identifiers);
}
/**
* Extends this key by the given method description.
*
* @param methodDescription The method to extend this key with.
* @param harmonizer The harmonizer to use for determining method equality.
* @return The harmonized key representing the extension of this key with the provided method.
*/
protected Harmonized<V> extend(MethodDescription.InDefinedShape methodDescription, Harmonizer<V> harmonizer) {
Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
V identifier = harmonizer.harmonize(typeToken);
Set<MethodDescription.TypeToken> typeTokens = identifiers.get(identifier);
if (typeTokens == null) {
identifiers.put(identifier, Collections.singleton(typeToken));
} else {
typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
typeTokens.add(typeToken);
identifiers.put(identifier, typeTokens);
}
return new Harmonized<V>(internalName, identifiers);
}
@Override
protected Set<V> getIdentifiers() {
return identifiers.keySet();
}
}
/**
* A detached version of a key that identifies methods by their JVM signature, i.e. parameter types and return type.
*/
protected static class Detached extends Key<MethodDescription.TypeToken> {
/**
* The type tokens represented by this key.
*/
private final Set<MethodDescription.TypeToken> identifiers;
/**
* Creates a new detached key.
*
* @param internalName The internal name of the method this key identifies.
* @param identifiers The type tokens represented by this key.
*/
protected Detached(String internalName, Set<MethodDescription.TypeToken> identifiers) {
super(internalName);
this.identifiers = identifiers;
}
/**
* Creates a new detached key of the given method token.
*
* @param token The method token to represent as a key.
* @return A detached key representing the given method token..
*/
protected static Detached of(MethodDescription.SignatureToken token) {
return new Detached(token.getName(), Collections.singleton(token.asTypeToken()));
}
@Override
protected Set<MethodDescription.TypeToken> getIdentifiers() {
return identifiers;
}
}
/**
* A store for collected methods that are identified by keys.
*
* @param <V> The type of the token used for deciding on method equality.
*/
protected static class Store<V> {
/**
* A mapping of harmonized keys to their represented entry.
*/
private final LinkedHashMap<Harmonized<V>, Entry<V>> entries;
/**
* Creates an empty store.
*/
protected Store() {
this(new LinkedHashMap<Harmonized<V>, Entry<V>>());
}
/**
* Creates a new store representing the given entries.
*
* @param entries A mapping of harmonized keys to their represented entry.
*/
private Store(LinkedHashMap<Harmonized<V>, Entry<V>> entries) {
this.entries = entries;
}
/**
* Combines the two given stores.
*
* @param left The left store to be combined.
* @param right The right store to be combined.
* @param <W> The type of the harmonized key of both stores.
* @return An entry representing the combination of both stores.
*/
private static <W> Entry<W> combine(Entry<W> left, Entry<W> right) {
Set<MethodDescription> leftMethods = left.getCandidates();
Set<MethodDescription> rightMethods = right.getCandidates();
LinkedHashSet<MethodDescription> combined = new LinkedHashSet<MethodDescription>(leftMethods.size() + rightMethods.size());
combined.addAll(leftMethods);
combined.addAll(rightMethods);
for (MethodDescription leftMethod : leftMethods) {
TypeDescription leftType = leftMethod.getDeclaringType().asErasure();
for (MethodDescription rightMethod : rightMethods) {
TypeDescription rightType = rightMethod.getDeclaringType().asErasure();
if (leftType.equals(rightType)) {
break;
} else if (leftType.isAssignableTo(rightType)) {
combined.remove(rightMethod);
break;
} else if (leftType.isAssignableFrom(rightType)) {
combined.remove(leftMethod);
break;
}
}
}
Key.Harmonized<W> key = left.getKey().combineWith(right.getKey());
Visibility visibility = left.getVisibility().expandTo(right.getVisibility());
return combined.size() == 1 ? new Entry.Resolved<W>(key, combined.iterator().next(), visibility, Entry.Resolved.NOT_MADE_VISIBLE) : new Entry.Ambiguous<W>(key, combined, visibility);
}
/**
* Registers a new top level method within this store.
*
* @param methodDescription The method to register.
* @param harmonizer The harmonizer to use for determining method equality.
* @return A store with the given method registered as a top-level method.
*/
protected Store<V> registerTopLevel(MethodDescription methodDescription, Harmonizer<V> harmonizer) {
Harmonized<V> key = Harmonized.of(methodDescription, harmonizer);
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
Entry<V> currentEntry = entries.remove(key);
Entry<V> extendedEntry = (currentEntry == null ? new Entry.Initial<V>(key) : currentEntry).extendBy(methodDescription, harmonizer);
entries.put(extendedEntry.getKey(), extendedEntry);
return new Store<V>(entries);
}
/**
* Combines this store with the given store.
*
* @param store The store to combine with this store.
* @return A store representing a combination of this store and the given store.
*/
protected Store<V> combineWith(Store<V> store) {
Store<V> combinedStore = this;
for (Entry<V> entry : store.entries.values()) {
combinedStore = combinedStore.combineWith(entry);
}
return combinedStore;
}
/**
* Combines this store with the given entry.
*
* @param entry The entry to combine with this store.
* @return A store representing a combination of this store and the given entry.
*/
protected Store<V> combineWith(Entry<V> entry) {
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
Entry<V> previousEntry = entries.remove(entry.getKey());
Entry<V> injectedEntry = previousEntry == null ? entry : combine(previousEntry, entry);
entries.put(injectedEntry.getKey(), injectedEntry);
return new Store<V>(entries);
}
/**
* Injects the given store into this store.
*
* @param store The key store to inject into this store.
* @return A store that represents this store with the given store injected.
*/
protected Store<V> inject(Store<V> store) {
Store<V> injectedStore = this;
for (Entry<V> entry : store.entries.values()) {
injectedStore = injectedStore.inject(entry);
}
return injectedStore;
}
/**
* Injects the given entry into this store.
*
* @param entry The entry to be injected into this store.
* @return A store that represents this store with the given entry injected.
*/
protected Store<V> inject(Entry<V> entry) {
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
Entry<V> dominantEntry = entries.remove(entry.getKey());
Entry<V> injectedEntry = dominantEntry == null ? entry : dominantEntry.inject(entry.getKey(), entry.getVisibility());
entries.put(injectedEntry.getKey(), injectedEntry);
return new Store<V>(entries);
}
/**
* Transforms this store into a method graph by applying the given merger.
*
* @param merger The merger to apply for resolving the representative for ambiguous resolutions.
* @return The method graph that represents this key store.
*/
protected MethodGraph asGraph(Merger merger) {
LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries = new LinkedHashMap<Key<MethodDescription.TypeToken>, Node>();
for (Entry<V> entry : this.entries.values()) {
Node node = entry.asNode(merger);
entries.put(entry.getKey().detach(node.getRepresentative().asTypeToken()), node);
}
return new Graph(entries);
}
/**
* An entry of a key store.
*
* @param <W> The type of the harmonized token used for determining method equality.
*/
protected interface Entry<W> {
/**
* Returns the harmonized key of this entry.
*
* @return The harmonized key of this entry.
*/
Harmonized<W> getKey();
/**
* Returns all candidate methods represented by this entry.
*
* @return All candidate methods represented by this entry.
*/
Set<MethodDescription> getCandidates();
/**
* Returns the minimal visibility of this entry.
*
* @return The minimal visibility of this entry.
*/
Visibility getVisibility();
/**
* Extends this entry by the given method.
*
* @param methodDescription The method description to extend this entry with.
* @param harmonizer The harmonizer to use for determining method equality.
* @return This key extended by the given method.
*/
Entry<W> extendBy(MethodDescription methodDescription, Harmonizer<W> harmonizer);
/**
* Injects the given key into this entry.
*
* @param key The key to inject into this entry.
* @param visibility The entry's minimal visibility.
* @return This entry extended with the given key.
*/
Entry<W> inject(Harmonized<W> key, Visibility visibility);
/**
* Transforms this entry into a node.
*
* @param merger The merger to use for determining the representative method of an ambiguous node.
* @return The resolved node.
*/
Node asNode(Merger merger);
/**
* An entry in its initial state before registering any method as a representative.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
class Initial<U> implements Entry<U> {
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
/**
* Creates a new initial key.
*
* @param key The harmonized key this entry represents.
*/
protected Initial(Harmonized<U> key) {
this.key = key;
}
@Override
public Harmonized<U> getKey() {
throw new IllegalStateException("Cannot extract key from initial entry:" + this);
}
@Override
public Set<MethodDescription> getCandidates() {
throw new IllegalStateException("Cannot extract method from initial entry:" + this);
}
@Override
public Visibility getVisibility() {
throw new IllegalStateException("Cannot extract visibility from initial entry:" + this);
}
@Override
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
return new Resolved<U>(key.extend(methodDescription.asDefined(), harmonizer), methodDescription, methodDescription.getVisibility(), Resolved.NOT_MADE_VISIBLE);
}
@Override
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
throw new IllegalStateException("Cannot inject into initial entry without a registered method: " + this);
}
@Override
public Node asNode(Merger merger) {
throw new IllegalStateException("Cannot transform initial entry without a registered method: " + this);
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass()) && key.equals(((Initial<?>) other).key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}
/**
* An entry representing a non-ambiguous node resolution.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
class Resolved<U> implements Entry<U> {
/**
* Indicates that a type's methods are already globally visible, meaning that a bridge method is not added
* with the intend of creating a visibility bridge.
*/
private static final int MADE_VISIBLE = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
/**
* Indicates that the entry was not made visible.
*/
private static final boolean NOT_MADE_VISIBLE = false;
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
/**
* The non-ambiguous, representative method of this entry.
*/
private final MethodDescription methodDescription;
/**
* The minimal required visibility for this method.
*/
private final Visibility visibility;
/**
* {@code true} if this entry's representative was made visible by a visibility bridge.
*/
private final boolean madeVisible;
/**
* Creates a new resolved entry.
*
* @param key The harmonized key this entry represents.
* @param methodDescription The non-ambiguous, representative method of this entry.
* @param visibility The minimal required visibility for this method.
* @param madeVisible {@code true} if this entry's representative was made visible by a visibility bridge.
*/
protected Resolved(Harmonized<U> key, MethodDescription methodDescription, Visibility visibility, boolean madeVisible) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
this.madeVisible = madeVisible;
}
/**
* Creates an entry for an override where a method overrides another method within a super class.
*
* @param key The merged key for both methods.
* @param override The method declared by the extending type, potentially a bridge method.
* @param original The method that is overridden by the extending type.
* @param visibility The minimal required visibility for this entry.
* @param <V> The type of the harmonized key to determine method equality.
* @return An entry representing the merger of both methods.
*/
private static <V> Entry<V> of(Harmonized<V> key, MethodDescription override, MethodDescription original, Visibility visibility) {
visibility = visibility.expandTo(original.getVisibility()).expandTo(override.getVisibility());
return override.isBridge() ? new Resolved<V>(key, original, visibility, (original.getDeclaringType().getModifiers() & MADE_VISIBLE) == 0) : new Resolved<V>(key, override, visibility, NOT_MADE_VISIBLE);
}
@Override
public Harmonized<U> getKey() {
return key;
}
@Override
public Set<MethodDescription> getCandidates() {
return Collections.singleton(methodDescription);
}
@Override
public Visibility getVisibility() {
return visibility;
}
@Override
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
Visibility visibility = this.visibility.expandTo(methodDescription.getVisibility());
return methodDescription.getDeclaringType().equals(this.methodDescription.getDeclaringType()) ? Ambiguous.of(key, methodDescription, this.methodDescription, visibility) : Resolved.of(key, methodDescription, this.methodDescription, visibility);
}
@Override
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
return new Resolved<U>(this.key.combineWith(key), methodDescription, this.visibility.expandTo(visibility), madeVisible);
}
@Override
public MethodGraph.Node asNode(Merger merger) {
return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility, madeVisible);
}
/**
* A node implementation representing a non-ambiguous method.
*/
protected static class Node implements MethodGraph.Node {
/**
* The detached key representing this node.
*/
private final Detached key;
/**
* The representative method of this node.
*/
private final MethodDescription methodDescription;
/**
* The node's minimal visibility.
*/
private final Visibility visibility;
/**
* {@code true} if the represented method was made explicitly visible by a visibility bridge.
*/
private final boolean visible;
/**
* Creates a new node.
*
* @param key The detached key representing this node.
* @param methodDescription The representative method of this node.
* @param visibility The node's minimal visibility.
* @param visible {@code true} if the represented method was made explicitly visible by a visibility bridge.
*/
protected Node(Detached key, MethodDescription methodDescription, Visibility visibility, boolean visible) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
this.visible = visible;
}
@Override
public Sort getSort() {
return visible ? Sort.VISIBLE : Sort.RESOLVED;
}
@Override
public MethodDescription getRepresentative() {
return methodDescription;
}
@Override
public Set<MethodDescription.TypeToken> getMethodTypes() {
return key.getIdentifiers();
}
@Override
public Visibility getVisibility() {
return visibility;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Resolved.Node)) return false;
final MethodGraph.Compiler.Default.Key.Store.Entry.Resolved.Node other = (MethodGraph.Compiler.Default.Key.Store.Entry.Resolved.Node) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$key = this.key;
final java.lang.Object other$key = other.key;
if (this$key == null ? other$key != null : !this$key.equals(other$key)) return false;
final java.lang.Object this$methodDescription = this.methodDescription;
final java.lang.Object other$methodDescription = other.methodDescription;
if (this$methodDescription == null ? other$methodDescription != null : !this$methodDescription.equals(other$methodDescription)) return false;
final java.lang.Object this$visibility = this.getVisibility();
final java.lang.Object other$visibility = other.getVisibility();
if (this$visibility == null ? other$visibility != null : !this$visibility.equals(other$visibility)) return false;
if (this.visible != other.visible) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Resolved.Node;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $key = this.key;
result = result * PRIME + ($key == null ? 43 : $key.hashCode());
final java.lang.Object $methodDescription = this.methodDescription;
result = result * PRIME + ($methodDescription == null ? 43 : $methodDescription.hashCode());
final java.lang.Object $visibility = this.getVisibility();
result = result * PRIME + ($visibility == null ? 43 : $visibility.hashCode());
result = result * PRIME + (this.visible ? 79 : 97);
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Resolved)) return false;
final MethodGraph.Compiler.Default.Key.Store.Entry.Resolved<?> other = (MethodGraph.Compiler.Default.Key.Store.Entry.Resolved<?>) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$key = this.getKey();
final java.lang.Object other$key = other.getKey();
if (this$key == null ? other$key != null : !this$key.equals(other$key)) return false;
final java.lang.Object this$methodDescription = this.methodDescription;
final java.lang.Object other$methodDescription = other.methodDescription;
if (this$methodDescription == null ? other$methodDescription != null : !this$methodDescription.equals(other$methodDescription)) return false;
final java.lang.Object this$visibility = this.getVisibility();
final java.lang.Object other$visibility = other.getVisibility();
if (this$visibility == null ? other$visibility != null : !this$visibility.equals(other$visibility)) return false;
if (this.madeVisible != other.madeVisible) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Resolved;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $key = this.getKey();
result = result * PRIME + ($key == null ? 43 : $key.hashCode());
final java.lang.Object $methodDescription = this.methodDescription;
result = result * PRIME + ($methodDescription == null ? 43 : $methodDescription.hashCode());
final java.lang.Object $visibility = this.getVisibility();
result = result * PRIME + ($visibility == null ? 43 : $visibility.hashCode());
result = result * PRIME + (this.madeVisible ? 79 : 97);
return result;
}
}
/**
* An entry representing an ambiguous node resolution.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
class Ambiguous<U> implements Entry<U> {
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
/**
* A set of ambiguous methods that this entry represents.
*/
private final LinkedHashSet<MethodDescription> methodDescriptions;
/**
* The minimal required visibility for this method.
*/
private final Visibility visibility;
/**
* Creates a new ambiguous entry.
*
* @param key The harmonized key this entry represents.
* @param methodDescriptions A set of ambiguous methods that this entry represents.
* @param visibility The minimal required visibility for this method.
*/
protected Ambiguous(Harmonized<U> key, LinkedHashSet<MethodDescription> methodDescriptions, Visibility visibility) {
this.key = key;
this.methodDescriptions = methodDescriptions;
this.visibility = visibility;
}
/**
* Creates a new ambiguous entry if both provided entries are not considered to be a bridge of one another.
*
* @param key The key of the entry to be created.
* @param left The left method to be considered.
* @param right The right method to be considered.
* @param visibility The entry's minimal visibility.
* @param <Q> The type of the token of the harmonized key to determine method equality.
* @return The entry representing both methods.
*/
protected static <Q> Entry<Q> of(Harmonized<Q> key, MethodDescription left, MethodDescription right, Visibility visibility) {
visibility = visibility.expandTo(left.getVisibility()).expandTo(right.getVisibility());
return left.isBridge() ^ right.isBridge() ? new Resolved<Q>(key, left.isBridge() ? right : left, visibility, Resolved.NOT_MADE_VISIBLE) : new Ambiguous<Q>(key, new LinkedHashSet<MethodDescription>(Arrays.asList(left, right)), visibility);
}
@Override
public Harmonized<U> getKey() {
return key;
}
@Override
public Set<MethodDescription> getCandidates() {
return methodDescriptions;
}
@Override
public Visibility getVisibility() {
return visibility;
}
@Override
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
LinkedHashSet<MethodDescription> methodDescriptions = new LinkedHashSet<MethodDescription>(this.methodDescriptions.size() + 1);
TypeDescription declaringType = methodDescription.getDeclaringType().asErasure();
boolean bridge = methodDescription.isBridge();
Visibility visibility = this.visibility;
for (MethodDescription extendedMethod : this.methodDescriptions) {
if (extendedMethod.getDeclaringType().asErasure().equals(declaringType)) {
if (extendedMethod.isBridge() ^ bridge) {
methodDescriptions.add(bridge ? extendedMethod : methodDescription);
} else {
methodDescriptions.add(methodDescription);
methodDescriptions.add(extendedMethod);
}
}
visibility = visibility.expandTo(extendedMethod.getVisibility());
}
if (methodDescriptions.isEmpty()) {
return new Resolved<U>(key, methodDescription, visibility, bridge);
} else if (methodDescriptions.size() == 1) {
return new Resolved<U>(key, methodDescriptions.iterator().next(), visibility, Resolved.NOT_MADE_VISIBLE);
} else {
return new Ambiguous<U>(key, methodDescriptions, visibility);
}
}
@Override
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
return new Ambiguous<U>(this.key.combineWith(key), methodDescriptions, this.visibility.expandTo(visibility));
}
@Override
public MethodGraph.Node asNode(Merger merger) {
Iterator<MethodDescription> iterator = methodDescriptions.iterator();
MethodDescription methodDescription = iterator.next();
while (iterator.hasNext()) {
methodDescription = merger.merge(methodDescription, iterator.next());
}
return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility);
}
/**
* A node implementation representing an ambiguous method resolution.
*/
protected static class Node implements MethodGraph.Node {
/**
* The detached key representing this node.
*/
private final Detached key;
/**
* The representative method of this node.
*/
private final MethodDescription methodDescription;
/**
* The node's minimal visibility.
*/
private final Visibility visibility;
/**
* @param key The detached key representing this node.
* @param methodDescription The representative method of this node.
* @param visibility The node's minimal visibility.
*/
protected Node(Detached key, MethodDescription methodDescription, Visibility visibility) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
}
@Override
public Sort getSort() {
return Sort.AMBIGUOUS;
}
@Override
public MethodDescription getRepresentative() {
return methodDescription;
}
@Override
public Set<MethodDescription.TypeToken> getMethodTypes() {
return key.getIdentifiers();
}
@Override
public Visibility getVisibility() {
return visibility;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous.Node)) return false;
final MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous.Node other = (MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous.Node) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$key = this.key;
final java.lang.Object other$key = other.key;
if (this$key == null ? other$key != null : !this$key.equals(other$key)) return false;
final java.lang.Object this$methodDescription = this.methodDescription;
final java.lang.Object other$methodDescription = other.methodDescription;
if (this$methodDescription == null ? other$methodDescription != null : !this$methodDescription.equals(other$methodDescription)) return false;
final java.lang.Object this$visibility = this.getVisibility();
final java.lang.Object other$visibility = other.getVisibility();
if (this$visibility == null ? other$visibility != null : !this$visibility.equals(other$visibility)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous.Node;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $key = this.key;
result = result * PRIME + ($key == null ? 43 : $key.hashCode());
final java.lang.Object $methodDescription = this.methodDescription;
result = result * PRIME + ($methodDescription == null ? 43 : $methodDescription.hashCode());
final java.lang.Object $visibility = this.getVisibility();
result = result * PRIME + ($visibility == null ? 43 : $visibility.hashCode());
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous)) return false;
final MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous<?> other = (MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous<?>) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$key = this.getKey();
final java.lang.Object other$key = other.getKey();
if (this$key == null ? other$key != null : !this$key.equals(other$key)) return false;
final java.lang.Object this$methodDescriptions = this.methodDescriptions;
final java.lang.Object other$methodDescriptions = other.methodDescriptions;
if (this$methodDescriptions == null ? other$methodDescriptions != null : !this$methodDescriptions.equals(other$methodDescriptions)) return false;
final java.lang.Object this$visibility = this.getVisibility();
final java.lang.Object other$visibility = other.getVisibility();
if (this$visibility == null ? other$visibility != null : !this$visibility.equals(other$visibility)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store.Entry.Ambiguous;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $key = this.getKey();
result = result * PRIME + ($key == null ? 43 : $key.hashCode());
final java.lang.Object $methodDescriptions = this.methodDescriptions;
result = result * PRIME + ($methodDescriptions == null ? 43 : $methodDescriptions.hashCode());
final java.lang.Object $visibility = this.getVisibility();
result = result * PRIME + ($visibility == null ? 43 : $visibility.hashCode());
return result;
}
}
}
/**
* A graph implementation based on a key store.
*/
protected static class Graph implements MethodGraph {
/**
* A mapping of a node's type tokens to the represented node.
*/
private final LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries;
/**
* Creates a new graph.
*
* @param entries A mapping of a node's type tokens to the represented node.
*/
protected Graph(LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries) {
this.entries = entries;
}
@Override
public Node locate(MethodDescription.SignatureToken token) {
Node node = entries.get(Detached.of(token));
return node == null ? Node.Unresolved.INSTANCE : node;
}
@Override
public NodeList listNodes() {
return new NodeList(new ArrayList<Node>(entries.values()));
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store.Graph)) return false;
final MethodGraph.Compiler.Default.Key.Store.Graph other = (MethodGraph.Compiler.Default.Key.Store.Graph) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$entries = this.entries;
final java.lang.Object other$entries = other.entries;
if (this$entries == null ? other$entries != null : !this$entries.equals(other$entries)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store.Graph;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $entries = this.entries;
result = result * PRIME + ($entries == null ? 43 : $entries.hashCode());
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default.Key.Store)) return false;
final MethodGraph.Compiler.Default.Key.Store<?> other = (MethodGraph.Compiler.Default.Key.Store<?>) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$entries = this.entries;
final java.lang.Object other$entries = other.entries;
if (this$entries == null ? other$entries != null : !this$entries.equals(other$entries)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default.Key.Store;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $entries = this.entries;
result = result * PRIME + ($entries == null ? 43 : $entries.hashCode());
return result;
}
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Compiler.Default)) return false;
final MethodGraph.Compiler.Default<?> other = (MethodGraph.Compiler.Default<?>) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$harmonizer = this.harmonizer;
final java.lang.Object other$harmonizer = other.harmonizer;
if (this$harmonizer == null ? other$harmonizer != null : !this$harmonizer.equals(other$harmonizer)) return false;
final java.lang.Object this$merger = this.merger;
final java.lang.Object other$merger = other.merger;
if (this$merger == null ? other$merger != null : !this$merger.equals(other$merger)) return false;
final java.lang.Object this$visitor = this.visitor;
final java.lang.Object other$visitor = other.visitor;
if (this$visitor == null ? other$visitor != null : !this$visitor.equals(other$visitor)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Compiler.Default;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $harmonizer = this.harmonizer;
result = result * PRIME + ($harmonizer == null ? 43 : $harmonizer.hashCode());
final java.lang.Object $merger = this.merger;
result = result * PRIME + ($merger == null ? 43 : $merger.hashCode());
final java.lang.Object $visitor = this.visitor;
result = result * PRIME + ($visitor == null ? 43 : $visitor.hashCode());
return result;
}
}
}
/**
* A list of nodes.
*/
class NodeList extends FilterableList.AbstractBase<Node, NodeList> {
/**
* The represented nodes.
*/
private final List<? extends Node> nodes;
/**
* Creates a list of nodes.
*
* @param nodes The represented nodes.
*/
public NodeList(List<? extends Node> nodes) {
this.nodes = nodes;
}
@Override
public Node get(int index) {
return nodes.get(index);
}
@Override
public int size() {
return nodes.size();
}
@Override
protected NodeList wrap(List<Node> values) {
return new NodeList(values);
}
/**
* Transforms this list of nodes into a list of the node's representatives.
*
* @return A list of these node's representatives.
*/
public MethodList<?> asMethodList() {
List<MethodDescription> methodDescriptions = new ArrayList<MethodDescription>(size());
for (Node node : nodes) {
methodDescriptions.add(node.getRepresentative());
}
return new MethodList.Explicit<MethodDescription>(methodDescriptions);
}
}
/**
* A simple implementation of a method graph.
*/
class Simple implements MethodGraph {
/**
* The nodes represented by this method graph.
*/
private final LinkedHashMap<MethodDescription.SignatureToken, Node> nodes;
/**
* Creates a new simple method graph.
*
* @param nodes The nodes represented by this method graph.
*/
public Simple(LinkedHashMap<MethodDescription.SignatureToken, Node> nodes) {
this.nodes = nodes;
}
/**
* Returns a method graph that contains all of the provided methods as simple nodes.
*
* @param methodDescriptions A list of method descriptions to be represented as simple nodes.
* @return A method graph that represents all of the provided methods as simple nodes.
*/
public static MethodGraph of(List<? extends MethodDescription> methodDescriptions) {
LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
for (MethodDescription methodDescription : methodDescriptions) {
nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
}
return new Simple(nodes);
}
@Override
public Node locate(MethodDescription.SignatureToken token) {
Node node = nodes.get(token);
return node == null ? Node.Unresolved.INSTANCE : node;
}
@Override
public NodeList listNodes() {
return new NodeList(new ArrayList<Node>(nodes.values()));
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodGraph.Simple)) return false;
final MethodGraph.Simple other = (MethodGraph.Simple) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$nodes = this.nodes;
final java.lang.Object other$nodes = other.nodes;
if (this$nodes == null ? other$nodes != null : !this$nodes.equals(other$nodes)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodGraph.Simple;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $nodes = this.nodes;
result = result * PRIME + ($nodes == null ? 43 : $nodes.hashCode());
return result;
}
}
}