package krasa.formatter.plugin;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.mockito.cglib.proxy.Callback;
import org.mockito.cglib.proxy.MethodInterceptor;
import org.mockito.cglib.proxy.MethodProxy;
import org.mockito.internal.util.StringJoiner;
import com.intellij.psi.codeStyle.CodeStyleManager;
public class CodeStyleManagerDelegator implements MethodInterceptor, Callback {
private static final com.intellij.openapi.diagnostic.Logger log = com.intellij.openapi.diagnostic.Logger
.getInstance(CodeStyleManagerDelegator.class.getName());
private final CodeStyleManager delegatedObject;
private final EclipseCodeStyleManager overridingObject;
public <T> CodeStyleManagerDelegator(CodeStyleManager delegatedObject, EclipseCodeStyleManager overridingObject) {
this.delegatedObject = delegatedObject;
this.overridingObject = overridingObject;
}
public CodeStyleManager getDelegatedObject() {
return delegatedObject;
}
@Override
public Object intercept(Object obj, Method invokedMethod, Object[] rawArguments, MethodProxy proxy)
throws Throwable {
try {
Method overridingMethod = getOverridingMethod(invokedMethod);
if (!compatibleReturnTypes(invokedMethod.getReturnType(), overridingMethod.getReturnType())) {
overridingMethodHasWrongReturnType(invokedMethod, overridingMethod, obj, overridingObject);
}
if (log.isDebugEnabled()) {
log.debug("invoking overriding {}({})", invokedMethod.getName(), Arrays.toString(rawArguments));
}
return overridingMethod.invoke(overridingObject, rawArguments);
} catch (NoSuchMethodException e) {
if (log.isDebugEnabled()) {
log.debug("invoking original {}({})", invokedMethod.getName(), Arrays.toString(rawArguments));
}
return PLEASE_REPORT_BUGS_TO_JETBRAINS_IF_IT_FAILS_HERE____ORIGINAL_INTELLIJ_FORMATTER_WAS_USED(
invokedMethod, rawArguments);
} catch (InvocationTargetException e) {
// propagate the original exception from the delegate
throw e.getCause();
}
}
private Object PLEASE_REPORT_BUGS_TO_JETBRAINS_IF_IT_FAILS_HERE____ORIGINAL_INTELLIJ_FORMATTER_WAS_USED(
Method invokedMethod, Object[] rawArguments) throws Throwable {
try {
return invokedMethod.invoke(delegatedObject, rawArguments);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
public void overridingMethodHasWrongReturnType(Method mockMethod, Method overridingMethod, Object mock,
Object overridingObject) {
throw new IllegalStateException(StringJoiner.join(new Object[] {
"IntelliJ API changed, install proper/updated version of Eclipse Formatter plugin.",
"Incompatible return types when calling: " + mockMethod + " on: " + mock.getClass().getSimpleName(),
"return type should be: " + mockMethod.getReturnType().getSimpleName() + ", but was: "
+ overridingMethod.getReturnType().getSimpleName(),
"(delegate instance had type: " + overridingObject.getClass().getSimpleName() + ")" }));
}
private Method getOverridingMethod(Method mockMethod) throws NoSuchMethodException {
return overridingObject.getClass().getMethod(mockMethod.getName(), mockMethod.getParameterTypes());
}
private static boolean compatibleReturnTypes(Class<?> superType, Class<?> subType) {
return superType.equals(subType) || superType.isAssignableFrom(subType);
}
}