blob: 7744a32888fdc625ea98f713c389dde75aa52168 [file] [log] [blame]
package com.intellij.util.xml.impl;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Key;
import com.intellij.pom.Navigatable;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.xml.*;
import org.jetbrains.annotations.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author peter
*/
public class InvocationCache {
private static final Map<JavaMethodSignature, Invocation> ourCoreInvocations = new HashMap<JavaMethodSignature, Invocation>();
private final Map<Method, Invocation> myInvocations = new ConcurrentFactoryMap<Method, Invocation>() {
@Override
protected Invocation create(Method key) {
return ourCoreInvocations.get(new JavaMethodSignature(key));
}
};
private final Map<Method, JavaMethod> myJavaMethods = new ConcurrentFactoryMap<Method, JavaMethod>() {
@Override
protected JavaMethod create(Method key) {
return JavaMethod.getMethod(myType, key);
}
};
private final Map<JavaMethod, Boolean> myGetters = new ConcurrentFactoryMap<JavaMethod, Boolean>() {
@Override
protected Boolean create(JavaMethod key) {
return DomImplUtil.isTagValueGetter(key);
}
};
private final Map<JavaMethod, Boolean> mySetters = new ConcurrentFactoryMap<JavaMethod, Boolean>() {
@Override
protected Boolean create(JavaMethod key) {
return DomImplUtil.isTagValueSetter(key);
}
};
private final Map<JavaMethod, Map<Class, Object>> myMethodAnnotations = new ConcurrentFactoryMap<JavaMethod, Map<Class, Object>>() {
@Override
protected Map<Class, Object> create(final JavaMethod method) {
return new ConcurrentFactoryMap<Class, Object>() {
@Override
protected Object create(Class annoClass) {
return method.getAnnotation(annoClass);
}
};
}
};
private final Map<Class, Object> myClassAnnotations = new ConcurrentFactoryMap<Class, Object>() {
@Override
protected Object create(Class annoClass) {
return myType.getAnnotation(annoClass);
}
};
private final Class myType;
static {
addCoreInvocations(DomElement.class);
addCoreInvocations(Navigatable.class);
addCoreInvocations(AnnotatedElement.class);
addCoreInvocations(Object.class);
ourCoreInvocations.put(new JavaMethodSignature("getUserData", Key.class), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return handler.getUserData((Key<?>)args[0]);
}
});
ourCoreInvocations.put(new JavaMethodSignature("putUserData", Key.class, Object.class), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
//noinspection unchecked
handler.putUserData((Key)args[0], args[1]);
return null;
}
});
ourCoreInvocations.put(new JavaMethodSignature("getXmlElement"), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return handler.getXmlElement();
}
});
ourCoreInvocations.put(new JavaMethodSignature("getXmlTag"), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return handler.getXmlTag();
}
});
ourCoreInvocations.put(new JavaMethodSignature("getParent"), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return handler.getParent();
}
});
ourCoreInvocations.put(new JavaMethodSignature("accept", DomElementVisitor.class), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
handler.accept((DomElementVisitor)args[0]);
return null;
}
});
ourCoreInvocations.put(new JavaMethodSignature("acceptChildren", DomElementVisitor.class), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
handler.acceptChildren((DomElementVisitor)args[0]);
return null;
}
});
ourCoreInvocations.put(new JavaMethodSignature("getAnnotation", Class.class), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
//noinspection unchecked
return handler.getAnnotation((Class<Annotation>)args[0]);
}
});
ourCoreInvocations.put(new JavaMethodSignature("getRawText"), new Invocation() {
public final Object invoke(final DomInvocationHandler<?, ?> handler, final Object[] args) throws Throwable {
return handler.getValue();
}
});
ourCoreInvocations.put(new JavaMethodSignature("getXmlAttribute"), new Invocation() {
public final Object invoke(final DomInvocationHandler<?, ?> handler, final Object[] args) throws Throwable {
return handler.getXmlElement();
}
});
ourCoreInvocations.put(new JavaMethodSignature("getXmlAttributeValue"), new Invocation() {
@Nullable
public final Object invoke(final DomInvocationHandler<?, ?> handler, final Object[] args) throws Throwable {
final XmlAttribute attribute = (XmlAttribute)handler.getXmlElement();
return attribute != null ? attribute.getValueElement() : null;
}
});
ourCoreInvocations.put(new JavaMethodSignature("getConverter"), new Invocation() {
public final Object invoke(final DomInvocationHandler<?, ?> handler, final Object[] args) throws Throwable {
try {
return handler.getScalarConverter();
}
catch (Throwable e) {
final Throwable cause = e.getCause();
if (cause instanceof ProcessCanceledException) {
throw(ProcessCanceledException)cause;
}
throw new RuntimeException(e);
}
}
});
}
private static void addCoreInvocations(final Class<?> aClass) {
for (final Method method : ReflectionUtil.getClassDeclaredMethods(aClass)) {
if ("equals".equals(method.getName())) {
ourCoreInvocations.put(new JavaMethodSignature(method), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
final DomElement proxy = handler.getProxy();
final Object arg = args[0];
if (proxy == arg) return true;
if (arg == null) return false;
if (arg instanceof DomElement) {
final DomInvocationHandler handler1 = DomManagerImpl.getDomInvocationHandler(proxy);
return handler1 != null && handler1.equals(DomManagerImpl.getDomInvocationHandler((DomElement)arg));
}
return false;
}
});
}
else if ("hashCode".equals(method.getName())) {
ourCoreInvocations.put(new JavaMethodSignature(method), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return handler.hashCode();
}
});
}
else {
ourCoreInvocations.put(new JavaMethodSignature(method), new Invocation() {
public Object invoke(DomInvocationHandler<?, ?> handler, Object[] args) throws Throwable {
return method.invoke(handler, args);
}
});
}
}
}
public InvocationCache(Class type) {
myType = type;
}
@Nullable
public Invocation getInvocation(Method method) {
return myInvocations.get(method);
}
public JavaMethod getInternedMethod(Method method) {
return myJavaMethods.get(method);
}
public void putInvocation(Method method, Invocation invocation) {
myInvocations.put(method, invocation);
}
public boolean isTagValueGetter(JavaMethod method) {
return myGetters.get(method);
}
public boolean isTagValueSetter(JavaMethod method) {
return mySetters.get(method);
}
@Nullable
public <T extends Annotation> T getMethodAnnotation(JavaMethod method, Class<T> annoClass) {
return (T)myMethodAnnotations.get(method).get(annoClass);
}
@Nullable
public <T extends Annotation> T getClassAnnotation(Class<T> annoClass) {
return (T)myClassAnnotations.get(annoClass);
}
}