package com.redhat.ceylon.eclipse.core.model; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.IClassFile; 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.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.core.BinaryType; import org.eclipse.jdt.internal.core.ClassFile; import com.redhat.ceylon.compiler.java.codegen.Naming; import com.redhat.ceylon.model.loader.ModelResolutionException; import com.redhat.ceylon.model.loader.mirror.TypeMirror; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C; public class LookupEnvironmentUtilities { public static ThreadLocal<Object> isSettingInterfaceCompanionClassTL = new ThreadLocal<Object>(); public static Object isSettingInterfaceCompanionClassObj = new Object(); static boolean isSettingInterfaceCompanionClass() { return isSettingInterfaceCompanionClassTL.get() != null; } public static interface Provider { LookupEnvironment getUpToDateLookupEnvironment(); LookupEnvironment getCurrentLookupEnvironment(); LookupEnvironment createLookupEnvironmentForGeneratedCode(); Object getLookupEnvironmentMutex(); void refreshNameEnvironment(); boolean isGetter(MethodBinding methodBinding, String methodName); TypeMirror getCachedTypeMirror(char[] bindingKey); void cacheTypeMirror(char[] bindingKey, TypeMirror mirror); } public static interface ActionOnResolvedType { void doWithBinding(ReferenceBinding referenceBinding); } public static interface ActionOnMethodBinding { void doWithBinding(IType declaringClassModel, ReferenceBinding declaringClassBinding, MethodBinding methodBinding); } public static interface ActionOnClassBinding { void doWithBinding(IType classModel, ReferenceBinding classBinding); } public static boolean doWithReferenceBinding(final IType typeModel, final ReferenceBinding binding, final ActionOnClassBinding action) { if (typeModel == null) { throw new ModelResolutionException("Resolving action requested on a missing declaration"); } if (binding == null) { return false; } PackageBinding packageBinding = binding.getPackage(); if (packageBinding == null) { return false; } LookupEnvironment lookupEnvironment = packageBinding.environment; if (lookupEnvironment == null) { return false; } Provider provider = modelJ2C().getLookupEnvironmentProvider(typeModel); if (provider == null) { throw new ModelResolutionException("The Model Loader corresponding to the type '" + typeModel.getFullyQualifiedName() + "' was not available"); } synchronized (provider.getLookupEnvironmentMutex()) { if (provider.getCurrentLookupEnvironment() != lookupEnvironment) { return false; } action.doWithBinding(typeModel, binding); return true; } } public static boolean doWithMethodBinding(final IType declaringClassModel, final MethodBinding binding, final ActionOnMethodBinding action) { if (declaringClassModel == null) { throw new ModelResolutionException("Resolving action requested on a missing declaration"); } if (binding == null) { return false; } ReferenceBinding declaringClassBinding = binding.declaringClass; if (declaringClassBinding == null) { return false; } PackageBinding packageBinding = declaringClassBinding.getPackage(); if (packageBinding == null) { return false; } LookupEnvironment lookupEnvironment = packageBinding.environment; if (lookupEnvironment == null) { return false; } Provider modelLoader = modelJ2C().getLookupEnvironmentProvider(declaringClassModel); if (modelLoader == null) { throw new ModelResolutionException("The Model Loader corresponding the type '" + declaringClassModel.getFullyQualifiedName() + "' doesn't exist"); } synchronized (modelLoader.getLookupEnvironmentMutex()) { if (modelLoader.getCurrentLookupEnvironment() != lookupEnvironment) { return false; } action.doWithBinding(declaringClassModel, declaringClassBinding, binding); return true; } } public static interface ActionOnResolvedGeneratedType { void doWithBinding(IType classModel, ReferenceBinding classBinding, IBinaryType binaryType); } public static void doOnResolvedGeneratedType(IType typeModel, ActionOnResolvedGeneratedType action) { if (typeModel == null || ! typeModel.exists()) { throw new ModelResolutionException("Resolving action requested on a missing declaration"); } Provider modelLoader = modelJ2C().getLookupEnvironmentProvider(typeModel); if (modelLoader == null) { throw new ModelResolutionException("The Model Loader is not available to resolve type '" + typeModel.getFullyQualifiedName() + "'"); } char[][] compoundName = CharOperation.splitOn('.', typeModel.getFullyQualifiedName().toCharArray()); LookupEnvironment lookupEnvironment = modelLoader.createLookupEnvironmentForGeneratedCode(); ReferenceBinding binding = null; IBinaryType binaryType = null; try { ITypeRoot typeRoot = typeModel.getTypeRoot(); if (typeRoot instanceof IClassFile) { ClassFile classFile = (ClassFile) typeRoot; IFile classFileRsrc = (IFile) classFile.getCorrespondingResource(); if (classFileRsrc!=null && !classFileRsrc.exists()) { //the .class file has been deleted return; } BinaryTypeBinding binaryTypeBinding = null; try { binaryType = classFile.getBinaryTypeInfo(classFileRsrc, true); binaryTypeBinding = lookupEnvironment.cacheBinaryType(binaryType, null); } catch(JavaModelException e) { if (! e.isDoesNotExist()) { throw e; } } if (binaryTypeBinding == null) { ReferenceBinding existingType = lookupEnvironment.getCachedType(compoundName); if (existingType == null || ! (existingType instanceof BinaryTypeBinding)) { return; } binaryTypeBinding = (BinaryTypeBinding) existingType; } binding = binaryTypeBinding; } } catch (JavaModelException e) { throw new ModelResolutionException(e); } if (binaryType != null && binding != null) { action.doWithBinding(typeModel, binding, binaryType); } } public static void doWithResolvedType(IType typeModel, ActionOnResolvedType action) { if (typeModel == null || ! typeModel.exists()) { throw new ModelResolutionException("Resolving action requested on a missing declaration"); } Provider modelLoader = modelJ2C().getLookupEnvironmentProvider(typeModel); if (modelLoader == null) { throw new ModelResolutionException("The Model Loader is not available to resolve type '" + typeModel.getFullyQualifiedName() + "'"); } char[][] compoundName = CharOperation.splitOn('.', typeModel.getFullyQualifiedName().toCharArray()); LookupEnvironment lookupEnvironment = modelLoader.getUpToDateLookupEnvironment(); synchronized (modelLoader.getLookupEnvironmentMutex()) { ReferenceBinding binding; try { binding = toBinding(typeModel, lookupEnvironment, compoundName); } catch (JavaModelException e) { throw new ModelResolutionException(e); } if (binding == null) { throw new ModelResolutionException("Binding not found for type : '" + typeModel.getFullyQualifiedName() + "'"); } action.doWithBinding(binding); } } public static IType toType(ReferenceBinding binding) { ModelLoaderNameEnvironment nameEnvironment = (ModelLoaderNameEnvironment) binding.getPackage().environment.nameEnvironment; char[][] compoundName = ((ReferenceBinding) binding).compoundName; IType typeModel = nameEnvironment.findTypeInNameLookup(compoundName); if (typeModel == null && ! (binding instanceof MissingTypeBinding)) { throw new ModelResolutionException("JDT reference binding without a JDT IType element : " + CharOperation.toString(compoundName)); } return typeModel; } private static final String OLD_PACKAGE_DESCRIPTOR_CLASS_NAME = Naming.PACKAGE_DESCRIPTOR_CLASS_NAME.substring(1); static final char[] packageDescriptorName = Naming.PACKAGE_DESCRIPTOR_CLASS_NAME.toCharArray(); static final char[] moduleDescriptorName = Naming.MODULE_DESCRIPTOR_CLASS_NAME.toCharArray(); static final char[] oldPackageDescriptorName = OLD_PACKAGE_DESCRIPTOR_CLASS_NAME.toCharArray(); static final char[] oldModuleDescriptorName = Naming.OLD_MODULE_DESCRIPTOR_CLASS_NAME.toCharArray(); static final char[][] descriptorClassNames = new char[][] { packageDescriptorName, moduleDescriptorName }; static ReferenceBinding toBinding(IType type, LookupEnvironment theLookupEnvironment, char[][] compoundName) throws JavaModelException { return toBinding(type, theLookupEnvironment, compoundName, null); } static ReferenceBinding toBinding(IType type, LookupEnvironment theLookupEnvironment, char[][] compoundName, ClassFileReader[] readerHolder) throws JavaModelException { ITypeRoot typeRoot = type.getTypeRoot(); if (typeRoot instanceof IClassFile) { ClassFile classFile = (ClassFile) typeRoot; IFile classFileRsrc = (IFile) classFile.getCorrespondingResource(); if (classFileRsrc!=null && !classFileRsrc.exists()) { //the .class file has been deleted return null; } BinaryTypeBinding binaryTypeBinding = null; try { IBinaryType binaryType; if (type instanceof BinaryType) { binaryType = (IBinaryType) ((BinaryType) type).getElementInfo(); } else { binaryType = classFile.getBinaryTypeInfo(classFileRsrc, true); } if (readerHolder != null && readerHolder.length == 1 && binaryType instanceof ClassFileReader) { readerHolder[0] = (ClassFileReader) binaryType; } binaryTypeBinding = theLookupEnvironment.cacheBinaryType(binaryType, null); } catch(JavaModelException e) { if (! e.isDoesNotExist()) { throw e; } } if (binaryTypeBinding == null) { ReferenceBinding existingType = theLookupEnvironment.getCachedType(compoundName); if (existingType == null || ! (existingType instanceof BinaryTypeBinding)) { return null; } binaryTypeBinding = (BinaryTypeBinding) existingType; } return binaryTypeBinding; } else { ReferenceBinding referenceBinding = theLookupEnvironment.getType(compoundName); if (referenceBinding != null && ! (referenceBinding instanceof BinaryTypeBinding)) { if (referenceBinding instanceof ProblemReferenceBinding) { ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) referenceBinding; if (problemReferenceBinding.problemId() == ProblemReasons.InternalNameProvided) { referenceBinding = problemReferenceBinding.closestReferenceMatch(); } else { System.out.println(ProblemReferenceBinding.problemReasonString(problemReferenceBinding.problemId())); return null; } } return referenceBinding; } return null; } } }