/*
 * Copyright 2000-2007 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.util;

import com.intellij.util.concurrency.JBLock;
import com.intellij.util.concurrency.JBReentrantReadWriteLock;
import com.intellij.util.concurrency.LockFactory;
import com.intellij.util.containers.ConcurrentFactoryMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;

/**
 * @author peter
 */
public class ReflectionCache {
  private static final boolean CACHE_DISABLED = System.getProperty("java.version", "1.5").indexOf("1.5") < 0 || Runtime.getRuntime().availableProcessors() > 4;

  private static final ConcurrentFactoryMap<Class,Class> ourSuperClasses = new ConcurrentFactoryMap<Class, Class>() {
    protected Class create(final Class key) {
      return key.getSuperclass();
    }
  };
  private static final ConcurrentFactoryMap<Class,Class[]> ourInterfaces = new ConcurrentFactoryMap<Class, Class[]>() {
    @NotNull
    protected Class[] create(final Class key) {
      return key.getInterfaces();
    }
  };
  private static final ConcurrentFactoryMap<Class, Method[]> ourMethods = new ConcurrentFactoryMap<Class, Method[]>() {
    @NotNull
    protected Method[] create(final Class key) {
      return key.getMethods();
    }
  };

  private static final Map<Class, Map<Class,Boolean>> ourAssignables = new THashMap<Class, Map<Class, Boolean>>();
  private static final JBReentrantReadWriteLock cacheLock = LockFactory.createReadWriteLock();
  private static final JBLock cacheRead = cacheLock.readLock();
  private static final JBLock cacheWrite = cacheLock.writeLock();

  private static final ConcurrentFactoryMap<Class,Boolean> ourIsInterfaces = new ConcurrentFactoryMap<Class, Boolean>() {
    @NotNull
    protected Boolean create(final Class key) {
      return key.isInterface();
    }
  };
  private static final ConcurrentFactoryMap<Class, TypeVariable[]> ourTypeParameters = new ConcurrentFactoryMap<Class, TypeVariable[]>() {
    @NotNull
    protected TypeVariable[] create(final Class key) {
      return key.getTypeParameters();
    }
  };
  private static final ConcurrentFactoryMap<Class, Type[]> ourGenericInterfaces = new ConcurrentFactoryMap<Class, Type[]>() {
    @NotNull
    protected Type[] create(final Class key) {
      return key.getGenericInterfaces();
    }
  };
  private static final ConcurrentFactoryMap<ParameterizedType, Type[]> ourActualTypeArguments = new ConcurrentFactoryMap<ParameterizedType, Type[]>() {
    @NotNull
    protected Type[] create(final ParameterizedType key) {
      return key.getActualTypeArguments();
    }
  };

  private ReflectionCache() {
  }

  public static Class getSuperClass(Class aClass) {
    return ourSuperClasses.get(aClass);
  }

  public static Class[] getInterfaces(Class aClass) {
    return ourInterfaces.get(aClass);
  }

  public static Method[] getMethods(Class aClass) {
    return ourMethods.get(aClass);
  }

  public static boolean isAssignable(Class ancestor, Class descendant) {
    if (ancestor == descendant) return true;
    if (CACHE_DISABLED) {
      return ancestor.isAssignableFrom(descendant);
    }

    cacheRead.lock();
    try {
      Map<Class, Boolean> map = ourAssignables.get(ancestor);
      if (map == null) {
        cacheRead.unlock();
        cacheWrite.lock();
        try {
          map = new THashMap<Class, Boolean>();
          ourAssignables.put(ancestor, map);
        }
        finally {
          cacheWrite.unlock();
          cacheRead.lock();
        }
      }

      Boolean result = map.get(descendant);
      if (result != null) {
        return result.booleanValue();
      }

      cacheRead.unlock();
      cacheWrite.lock();
      try {
        result = ancestor.isAssignableFrom(descendant);
        map.put(descendant, result);
        return result.booleanValue();
      }
      finally {
        cacheWrite.unlock();
        cacheRead.lock();
      }
    }
    finally {
      cacheRead.unlock();
    }
  }

  public static boolean isInstance(Object instance, Class clazz) {
    return isAssignable(clazz, instance.getClass());
  }

  public static boolean isInterface(Class aClass) {
    return ourIsInterfaces.get(aClass);
  }

  public static <T> TypeVariable<Class<T>>[] getTypeParameters(Class<T> aClass) {
    return ourTypeParameters.get(aClass);
  }

  public static Type[] getGenericInterfaces(Class aClass) {
    return ourGenericInterfaces.get(aClass);
  }

  public static Type[] getActualTypeArguments(ParameterizedType type) {
    return ourActualTypeArguments.get(type);
  }

}

@@ -626,166 +626,8 @@
 l;%0A%0A
-import com.intellij.util.concurrency.JBLock;%0Aimport com.intellij.util.concurrency.JBReentrantReadWriteLock;%0Aimport com.intellij.util.concurrency.LockFactory;%0A
 impo
@@ -684,35 +684,8 @@
 ap;%0A
-import gnu.trove.THashMap;%0A
 impo
@@ -873,30 +873,8 @@
 ble;
-%0Aimport java.util.Map;
 %0A%0A/*
@@ -931,169 +931,8 @@
 e %7B%0A
-  private static final boolean CACHE_DISABLED = System.getProperty(%22java.version%22, %221.5%22).indexOf(%221.5%22) %3C 0 %7C%7C Runtime.getRuntime().availableProcessors() %3E 4;%0A%0A
   pr
@@ -1591,349 +1591,8 @@
 %7D;%0A%0A
-  private static final Map%3CClass, Map%3CClass,Boolean%3E%3E ourAssignables = new THashMap%3CClass, Map%3CClass, Boolean%3E%3E();%0A  private static final JBReentrantReadWriteLock cacheLock = LockFactory.createReadWriteLock();%0A  private static final JBLock cacheRead = cacheLock.readLock();%0A  private static final JBLock cacheWrite = cacheLock.writeLock();%0A%0A
   pr
@@ -2988,972 +2988,79 @@
     
-if (ancestor == descendant) return true;%0A    if (CACHE_DISABLED) %7B%0A      return ancestor.isAssignableFrom(descendant);%0A    %7D%0A%0A    cacheRead.lock();%0A    try %7B%0A      Map%3CClass, Boolean%3E map = ourAssignables.get(ancestor);%0A      if (map == null) %7B%0A        cacheRead.unlock();%0A        cacheWrite.lock();%0A        try %7B%0A          map = new THashMap%3CClass, Boolean%3E();%0A          ourAssignables.put(ancestor, map);%0A        %7D%0A        finally %7B%0A          cacheWrite.unlock();%0A          cacheRead.lock();%0A        %7D%0A      %7D%0A%0A      Boolean result = map.get(descendant);%0A      if (result != null) %7B%0A        return result.booleanValue();%0A      %7D%0A%0A      cacheRead.unlock();%0A      cacheWrite.lock();%0A      try %7B%0A        result = ancestor.isAssignableFrom(descendant);%0A        map.put(descendant, result);%0A        return result.booleanValue();%0A      %7D%0A      finally %7B%0A        cacheWrite.unlock();%0A        cacheRead.lock();%0A      %7D%0A    %7D%0A    finally %7B%0A      cacheRead.unlock();%0A    %7D
+return ancestor == descendant %7C%7C ancestor.isAssignableFrom(descendant);
 %0A  %7D

Disable ReflectionCache for isAssignable.