package com.redhat.ceylon.eclipse.core.model;
import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import com.redhat.ceylon.eclipse.core.model.mirror.IBindingProvider;
import com.redhat.ceylon.eclipse.core.model.mirror.JDTMethod;
import com.redhat.ceylon.model.loader.model.FieldValue;
import com.redhat.ceylon.model.loader.model.JavaBeanValue;
import com.redhat.ceylon.model.loader.model.JavaMethod;
import com.redhat.ceylon.model.loader.model.LazyClass;
import com.redhat.ceylon.model.loader.model.LazyFunction;
import com.redhat.ceylon.model.loader.model.LazyInterface;
import com.redhat.ceylon.model.loader.model.LazyValue;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Scope;
public class CeylonToJavaMatcher {
private static class ResolvedElements {
public ResolvedElements(
IJavaElement[] modelElements,
IBinding[] bindings) {
this.modelElements = modelElements;
this.bindings= bindings;
}
public IJavaElement[] modelElements;
public IBinding[] bindings;
}
private ITypeRoot typeRoot = null;
private SoftReference<ResolvedElements> resolvedElementsRef = new SoftReference<>(null);
public CeylonToJavaMatcher(ITypeRoot typeRoot) {
this.typeRoot = typeRoot;
}
private synchronized ResolvedElements resolveUnitElements(IProgressMonitor monitor) {
ResolvedElements resolvedElements = resolvedElementsRef.get();
if (resolvedElements == null) {
ASTParser parser = ASTParser.newParser(AST.JLS4);
parser.setProject(typeRoot.getJavaProject());
List<IJavaElement> list = new LinkedList<>();
traverseModel(typeRoot.getPrimaryElement(), list);
IJavaElement[] modelElements = new IJavaElement[list.size()];
modelElements = list.toArray(modelElements);
IBinding[] bindings = parser.createBindings(modelElements, monitor);
if (bindings != null && bindings.length == modelElements.length) {
resolvedElements = new ResolvedElements(modelElements, bindings);
resolvedElementsRef = new SoftReference<>(resolvedElements);
}
}
return resolvedElements;
}
public IJavaElement searchInClass(Declaration ceylonDeclaration, IProgressMonitor monitor) {
ResolvedElements resolvedElements = resolveUnitElements(monitor);
for (IJavaElement javaElement : resolvedElements.modelElements) {
IJavaElement result = null;
try {
if (javaElement instanceof IType) {
result = declarationMatchesIType(ceylonDeclaration, (IType)javaElement, resolvedElements);
}
if (javaElement instanceof IMethod) {
result = declarationMatchesIMethod(ceylonDeclaration, (IMethod)javaElement, resolvedElements);
}
} catch(Exception e) {
e.printStackTrace();
}
if (result != null) {
return result;
}
}
return null;
}
private void traverseModel(IJavaElement element, List<IJavaElement> elements) {
if (element instanceof IType ||
element instanceof IMethod) {
elements.add(element);
}
if (element instanceof IParent) {
IParent parent = (IParent) element;
try {
for (IJavaElement child : parent.getChildren()) {
traverseModel(child, elements);
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
}
private IJavaElement declarationMatchesIType(Declaration ceylonDeclaration, IType javaType, ResolvedElements resolvedElements) {
IBindingProvider mirror = null;
if (ceylonDeclaration instanceof LazyClass) {
LazyClass lazyClass = (LazyClass) ceylonDeclaration;
if (! lazyClass.isAbstraction() && lazyClass.isOverloaded()) {
IBindingProvider constructor = (IBindingProvider) lazyClass.getConstructor();
if (constructor != null) {
mirror = constructor;
}
}
if (mirror == null) {
mirror = (IBindingProvider) lazyClass.classMirror;
}
}
if (ceylonDeclaration instanceof LazyInterface) {
mirror = (IBindingProvider) ((LazyInterface) ceylonDeclaration).classMirror;
}
if (ceylonDeclaration instanceof LazyValue) {
mirror = (IBindingProvider) ((LazyValue) ceylonDeclaration).classMirror;
}
if (ceylonDeclaration instanceof JavaBeanValue) {
JavaBeanValue javaBeanValue = ((JavaBeanValue) ceylonDeclaration);
Scope container = javaBeanValue.getContainer();
if (container instanceof LazyClass) {
mirror = (IBindingProvider) ((LazyClass) container).classMirror;
}
if (container instanceof LazyInterface) {
mirror = (IBindingProvider) ((LazyInterface) container).classMirror;
}
if (container instanceof LazyValue) {
mirror = (IBindingProvider) ((LazyValue) container).classMirror;
}
if (declarationMatched(javaType, mirror, resolvedElements) != null) {
try {
for (IMethod javaMethod : javaType.getMethods()) {
if (javaMethod.getElementName().equals(javaBeanValue.getGetterName())) {
return javaMethod;
}
}
} catch (JavaModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
if (ceylonDeclaration instanceof FieldValue) {
FieldValue fieldValue = ((FieldValue) ceylonDeclaration);
Scope container = fieldValue.getContainer();
if (container instanceof LazyClass) {
mirror = (IBindingProvider) ((LazyClass) container).classMirror;
}
if (container instanceof LazyInterface) {
mirror = (IBindingProvider) ((LazyInterface) container).classMirror;
}
if (container instanceof LazyValue) {
mirror = (IBindingProvider) ((LazyValue) container).classMirror;
}
if (declarationMatched(javaType, mirror, resolvedElements) != null) {
try {
for (IField javaField : javaType.getFields()) {
if (javaField.getElementName().equals(fieldValue.getRealName())) {
return javaField;
}
}
} catch (JavaModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
return declarationMatched(javaType, mirror, resolvedElements);
}
private IJavaElement declarationMatchesIMethod(Declaration ceylonDeclaration, IMethod javaMethod, ResolvedElements resolvedElements) {
IBindingProvider mirror = null;
if (ceylonDeclaration instanceof LazyFunction) {
mirror = (IBindingProvider) ((LazyFunction) ceylonDeclaration).getMethodMirror();
}
if (ceylonDeclaration instanceof JavaMethod) {
mirror = (IBindingProvider) ((JavaMethod) ceylonDeclaration).mirror;
}
return declarationMatched(javaMethod, mirror, resolvedElements);
}
private IJavaElement declarationMatched(
IJavaElement javaElement,
IBindingProvider mirror,
ResolvedElements resolvedElements) {
if (mirror != null && resolvedElements != null) {
IJavaElement[] modelElements = resolvedElements.modelElements;
IBinding[] bindings = resolvedElements.bindings;
int javaElementIndex = -1;
for (int i = 0; i<modelElements.length; i++) {
if (modelElements[i] == javaElement) {
javaElementIndex = i;
break;
}
}
if (javaElementIndex >=0) {
IBinding binding = bindings[javaElementIndex];
if (binding != null) {
if (mirror instanceof JDTMethod && binding instanceof ITypeBinding) {
// Case of a constructor : let's go to the constructor and not to the type.
ITypeBinding typeBinding = (ITypeBinding) binding;
for (IMethodBinding methodBinding : typeBinding.getDeclaredMethods()) {
// if (methodBinding.isConstructor()) {
if (CharOperation.equals(methodBinding.getKey().toCharArray(), mirror.getBindingKey())) {
return methodBinding.getJavaElement();
}
// }
}
}
if (CharOperation.equals(binding.getKey().toCharArray(), mirror.getBindingKey())) {
return javaElement;
}
}
}
}
return null;
}
}