@ApiStatus.Internal
  @NotNull
  public static <T> T wrap(@NotNull Class<T> ifaceClass, @NotNull final Object wrapper, @NotNull final Class<?> parentToStopSearchAt, @NotNull final T delegateTo) {
    return ifaceClass.cast(Proxy.newProxyInstance(APIWrappers.class.getClassLoader(), new Class<?>[]{ifaceClass, WrapperDelegateAccessor.class}, new InvocationHandler() {
      private final Map<Method, Pair<Method, Object>> myCallHandlers = Collections.synchronizedMap(new HashMap<Method, Pair<Method, Object>>());
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          final Pair<Method, Object> call = getCallHandlerMethod(method);
          return call.getFirst().invoke(call.getSecond(), args);
        }
        catch (InvocationTargetException e) {
          final Throwable cause = e.getCause();
          throw cause != null? cause : e;
        }
      }

      @NotNull
      private Pair<Method, Object> getCallHandlerMethod(Method method) {
        Pair<Method, Object> pair = myCallHandlers.get(method);
        if (pair == null) {
          if (WrapperDelegateAccessor.class.equals(method.getDeclaringClass())) {
            pair = wrapper instanceof WrapperDelegateAccessor? Pair.create(method, wrapper) : Pair.<Method, Object>create(method, new WrapperDelegateAccessor<T>() {
              @Override
              public T getWrapperDelegate() {
                return delegateTo;
              }
            });
          }
          else {
            // important: look for implemented methods starting from the actual class
            Class<?> aClass = wrapper.getClass();
            while (!(parentToStopSearchAt.equals(aClass) || Object.class.equals(aClass))) {
              try {
                pair = Pair.create(aClass.getDeclaredMethod(method.getName(), method.getParameterTypes()), wrapper);
                break;
              }
              catch (NoSuchMethodException e) {
                aClass = aClass.getSuperclass();
              }
            }
            if (pair == null) {
              pair = Pair.<Method, Object>create(method, delegateTo);
            }
          }
          myCallHandlers.put(method, pair);
        }
        return pair;
      }
    }));
  }
@ApiStatus.Internal
  @NotNull
  public static <T> T wrap(@NotNull Class<T> ifaceClass, @NotNull final Object wrapper, @NotNull final Class<?> parentToStopSearchAt, @NotNull final T delegateTo)