package org.mutabilitydetector.asmoverride;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.objectweb.asm.Type;
import org.objectweb.asm.tree.analysis.TypeHierarchyReader;

public class IsAssignableFromCachingTypeHierarchyReader extends TypeHierarchyReader {

    private final ConcurrentMap<TypeAssignability, Boolean> isAssignableFromCache;
    private final TypeHierarchyReader baseReader;

    public IsAssignableFromCachingTypeHierarchyReader(TypeHierarchyReader baseReader) {
        this.baseReader = baseReader;
        this.isAssignableFromCache =  new ConcurrentHashMap<TypeAssignability, Boolean>();
    }
    
    @Override
    public TypeHierarchy hierarchyOf(Type t) {
        return baseReader.hierarchyOf(t);
    }
    
    @Override
    public Type getSuperClass(Type t) {
        return baseReader.getSuperClass(t);
    }
    
    @Override
    public boolean isInterface(Type t) {
        return baseReader.isInterface(t);
    }
    
    @Override
    public boolean isAssignableFrom(final Type t, final Type u) {
        TypeAssignability assignability = new TypeAssignability(t, u);
        if (!isAssignableFromCache.containsKey(assignability)) {
            isAssignableFromCache.put(assignability, baseReader.isAssignableFrom(t, u));
        }
        return isAssignableFromCache.get(assignability);
    }
    
    private static class TypeAssignability {
        public final Type toType, fromType;
        private int hashCode;
        
        public TypeAssignability(Type toType, Type fromType) {
            this.toType = toType;
            this.fromType = fromType;
            this.hashCode = calculateHashCode();
        }

        private int calculateHashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * fromType.hashCode();
            result = prime * toType.hashCode();
            return result;
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            } else if (obj == null) {
                return false;
            } else if (getClass() != obj.getClass()) {
                return false;
            }
            
            TypeAssignability other = (TypeAssignability) obj;
            return toType.equals(other.toType) && fromType.equals(other.fromType);
        }
        
    }
    
}

@@ -49,47 +49,92 @@
 ort 
-java.util.concurrent.ConcurrentHashMap;
+org.objectweb.asm.Type;%0Aimport org.objectweb.asm.tree.analysis.TypeHierarchyReader;%0A
 %0Aimp
@@ -172,103 +172,58 @@
 rent
+Hash
 Map;%0A
-%0A
 import 
-org.objectweb.asm.Type;%0Aimport org.objectweb.asm.tree.analysis.TypeHierarchyReader
+java.util.concurrent.ConcurrentMap
 ;%0A%0Ap
@@ -1438,21 +1438,22 @@
        p
-ublic
+rivate
  final T
@@ -1486,24 +1486,30 @@
     private 
+final 
 int hashCode
@@ -1523,31 +1523,24 @@
    %0A        
-public 
 TypeAssignab
@@ -1765,91 +1765,23 @@
     
-final int prime = 31;%0A            int result = 1;%0A            result = prime * from
+int result = to
 Type
@@ -1818,18 +1818,26 @@
 t = 
-prime * to
+31 * result + from
 Type

Fix hashcode method.