package com.redhat.ceylon.eclipse.util; import static com.redhat.ceylon.compiler.java.codegen.CodegenUtil.getJavaNameOfDeclaration; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getCeylonClassesOutputFolder; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjects; import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.isExplodeModulesEnabled; import static com.redhat.ceylon.eclipse.core.model.LookupEnvironmentUtilities.doOnResolvedGeneratedType; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_ATTRIBUTE_ANNOTATION; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_CEYLON_ANNOTATION; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_LOCAL_DECLARATION_ANNOTATION; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_METHOD_ANNOTATION; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_NAME_ANNOTATION; import static com.redhat.ceylon.model.loader.AbstractModelLoader.CEYLON_OBJECT_ANNOTATION; import static java.util.Collections.emptyList; import static org.eclipse.jdt.core.IJavaElement.PACKAGE_FRAGMENT; import static org.eclipse.jdt.core.search.IJavaSearchConstants.CLASS_AND_INTERFACE; import static org.eclipse.jdt.core.search.IJavaSearchConstants.FIELD; import static org.eclipse.jdt.core.search.IJavaSearchConstants.METHOD; import static org.eclipse.jdt.core.search.SearchPattern.R_EXACT_MATCH; import static org.eclipse.jdt.core.search.SearchPattern.createOrPattern; import static org.eclipse.jdt.core.search.SearchPattern.createPattern; import static org.eclipse.jdt.internal.core.util.Util.getUnresolvedJavaElement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.IAnnotatable; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ILocalVariable; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IMemberValuePair; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; 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.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.core.JavaElement; import org.eclipse.jdt.internal.corext.util.SearchUtils; import com.redhat.ceylon.compiler.java.codegen.Naming; import com.redhat.ceylon.compiler.java.language.AbstractCallable; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder; import com.redhat.ceylon.eclipse.core.builder.CeylonNature; import com.redhat.ceylon.eclipse.core.model.LookupEnvironmentUtilities.ActionOnResolvedGeneratedType; import com.redhat.ceylon.ide.common.model.BaseIdeModelLoader; import com.redhat.ceylon.ide.common.model.BaseIdeModule; import com.redhat.ceylon.ide.common.model.CeylonBinaryUnit; import com.redhat.ceylon.ide.common.model.CeylonUnit; import com.redhat.ceylon.ide.common.model.IJavaModelAware; import com.redhat.ceylon.ide.common.model.IUnit; import com.redhat.ceylon.ide.common.util.escaping_; import com.redhat.ceylon.model.loader.ModelLoader.DeclarationType; import com.redhat.ceylon.model.loader.NamingBase; import com.redhat.ceylon.model.loader.NamingBase.Prefix; import com.redhat.ceylon.model.loader.NamingBase.Suffix; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Function; import com.redhat.ceylon.model.typechecker.model.FunctionOrValue; import com.redhat.ceylon.model.typechecker.model.ModelUtil; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Modules; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.model.typechecker.model.Scope; import com.redhat.ceylon.model.typechecker.model.Specification; import com.redhat.ceylon.model.typechecker.model.Unit; import com.redhat.ceylon.model.typechecker.model.Value; public class JavaSearch { public static SearchPattern createSearchPattern( Declaration declaration, int limitTo) { String pattern; try { pattern = getJavaNameOfDeclaration(declaration); } catch (IllegalArgumentException iae) { return null; } if (declaration instanceof Function) { return createPattern(pattern, METHOD, limitTo, R_EXACT_MATCH); } else if (declaration instanceof Value) { int loc = pattern.lastIndexOf('.') + 1; if (pattern.substring(loc).startsWith("get")) { String setter = pattern.substring(0,loc) + "set" + pattern.substring(loc+3); SearchPattern getterPattern = createPattern(pattern, METHOD, limitTo, R_EXACT_MATCH); SearchPattern setterPattern = createPattern(setter, METHOD, limitTo, R_EXACT_MATCH); switch (limitTo) { case IJavaSearchConstants.WRITE_ACCESSES: return setterPattern; case IJavaSearchConstants.READ_ACCESSES: return getterPattern; default: return createOrPattern(getterPattern, setterPattern); } } else { SearchPattern fieldPattern = createPattern(pattern, FIELD, limitTo, R_EXACT_MATCH); return fieldPattern; } } else { SearchPattern searchPattern = createPattern(pattern, CLASS_AND_INTERFACE, limitTo, R_EXACT_MATCH); //weirdly, ALL_OCCURRENCES doesn't return all occurrences /*if (limitTo==IJavaSearchConstants.ALL_OCCURRENCES) { searchPattern = createOrPattern(createPattern(pattern, CLASS_AND_INTERFACE, IJavaSearchConstants.IMPLEMENTORS, R_EXACT_MATCH), searchPattern); }*/ return searchPattern; } } public static IProject[] getProjectAndRelatedProjects( IProject project) { try { IProject[] referencingProjects = project.getReferencingProjects(); IProject[] referencedProjects = project.getReferencedProjects(); IProject[] projects = new IProject[referencedProjects.length+ referencingProjects.length+ 1]; projects[0] = project; System.arraycopy(referencingProjects, 0, projects, 1, referencingProjects.length); System.arraycopy(referencedProjects, 0, projects, referencingProjects.length+1, referencedProjects.length); return projects; } catch (Exception e) { e.printStackTrace(); return new IProject[] { project }; } } public static IProject[] getProjectAndReferencingProjects( IProject project) { try { IProject[] referencingProjects = project.getReferencingProjects(); IProject[] projects = new IProject[referencingProjects.length+1]; projects[0] = project; System.arraycopy(referencingProjects, 0, projects, 1, referencingProjects.length); return projects; } catch (Exception e) { e.printStackTrace(); return new IProject[] { project }; } } public static IProject[] getProjectAndReferencedProjects( IProject project) { try { IProject[] referencedProjects = project.getReferencedProjects(); IProject[] projects = new IProject[referencedProjects.length+1]; projects[0] = project; System.arraycopy(referencedProjects, 0, projects, 1, referencedProjects.length); return projects; } catch (Exception e) { e.printStackTrace(); return new IProject[] { project }; } } @SuppressWarnings("deprecation") public static void runSearch( IProgressMonitor pm, SearchEngine searchEngine, SearchPattern searchPattern, IProject[] projects, SearchRequestor requestor) throws OperationCanceledException { try { searchEngine.search(searchPattern, SearchUtils.getDefaultSearchParticipants(), SearchEngine.createJavaSearchScope(projects), requestor, pm); } catch (OperationCanceledException oce) { throw oce; } catch (Exception e) { e.printStackTrace(); } } public static String getJavaQualifiedName(IMember dec) { IPackageFragment packageFragment = (IPackageFragment) dec.getAncestor(PACKAGE_FRAGMENT); IType type = (IType) dec.getAncestor(IJavaElement.TYPE); String qualifier = packageFragment.getElementName(); if (! qualifier.isEmpty()) { qualifier += '.'; } String name = dec.getElementName(); if (dec instanceof IMethod && name.equals("get_")) { return getJavaQualifiedName(type); } else if (dec instanceof IType && name.endsWith("_")) { return qualifier + name.substring(0, name.length()-1); } if (dec instanceof IMethod) { if (name.startsWith("$")) { name = name.substring(1); } else if (name.startsWith("get") || name.startsWith("set")) { name = escaping_.get_().toInitialLowercase(name.substring(3)); } } if (dec!=type) { String fullyQualifiedTypeName = type.getFullyQualifiedName(); String[] parts = fullyQualifiedTypeName.split("\\."); String typeName = parts.length == 0 ? fullyQualifiedTypeName : parts[parts.length-1]; if (typeName.endsWith(name + "_")) { return qualifier + name; } else { return qualifier + typeName + '.' + name; } } else { return qualifier + name; } } private static boolean isCeylon(IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation ceylonAnnotation = annotatable.getAnnotation( CEYLON_CEYLON_ANNOTATION); return ceylonAnnotation.exists(); } return false; } private static boolean isCeylonObject(IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation ceylonAnnotation = annotatable.getAnnotation( CEYLON_OBJECT_ANNOTATION); return ceylonAnnotation.exists(); } return false; } private static boolean isCeylonAttribute(IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation ceylonAnnotation = annotatable.getAnnotation( CEYLON_ATTRIBUTE_ANNOTATION); return ceylonAnnotation.exists(); } return false; } private static boolean isCeylonMethod(IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation ceylonAnnotation = annotatable.getAnnotation( CEYLON_METHOD_ANNOTATION); return ceylonAnnotation.exists(); } return false; } private static String getCeylonNameAnnotationValue( IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation nameAnnotation = annotatable.getAnnotation( CEYLON_NAME_ANNOTATION); if (nameAnnotation.exists()) { try { for (IMemberValuePair mvp: nameAnnotation.getMemberValuePairs()) { if ("value".equals(mvp.getMemberName())) { Object value = mvp.getValue(); if ((value instanceof String) && ! "".equals(value)) { return (String) value; } } } } catch (JavaModelException e) { e.printStackTrace(); } } } return null; } private static String getLocalDeclarationQualifier( IMember element) { if (element instanceof IAnnotatable) { IAnnotatable annotatable = (IAnnotatable) element; IAnnotation nameAnnotation = annotatable.getAnnotation( CEYLON_LOCAL_DECLARATION_ANNOTATION); if (nameAnnotation.exists()) { try { for (IMemberValuePair mvp: nameAnnotation.getMemberValuePairs()) { if ("qualifier".equals( mvp.getMemberName())) { Object value = mvp.getValue(); if ((value instanceof String) && ! "".equals(value)) { return (String) value; } } } } catch (JavaModelException e) { e.printStackTrace(); } } } return null; } public static class JdtDefaultArgumentMethodSearch extends DefaultArgumentMethodSearch<IMethod> { @Override protected String getMethodName(IMethod method) { return method.getElementName(); } @Override protected boolean isMethodPrivate(IMethod method) { try { return (method.getFlags() & Flags.AccPrivate) != 0; } catch (JavaModelException e) { e.printStackTrace(); return false; } } @Override protected List<IMethod> getMethodsOfDeclaringType( IMethod method, String searchedName) { IType declaringType = method.getDeclaringType(); if (declaringType == null) { return emptyList(); } List<IMethod> foundMethods = new ArrayList<>(); try { for (IMethod m: declaringType.getMethods()) { if (searchedName == null || searchedName.equals( m.getElementName())) { foundMethods.add(m); } } } catch (JavaModelException e) { e.printStackTrace(); return emptyList(); } return foundMethods; } @Override protected List<String> getParameterNames(IMethod method) { List<String> paramNames = new ArrayList<>(); try { for (ILocalVariable param: method.getParameters()) { String paramName = null; IAnnotation nameAnnotation = param.getAnnotation( CEYLON_NAME_ANNOTATION); if (nameAnnotation != null && nameAnnotation.exists()) { IMemberValuePair[] valuePairs = nameAnnotation.getMemberValuePairs(); if (valuePairs != null && valuePairs.length > 0) { paramName = (String) valuePairs[0] .getValue(); } } if (paramName == null) { paramName = param.getElementName(); } paramNames.add(paramName); } return Arrays.asList(method.getParameterNames()); } catch (JavaModelException e) { e.printStackTrace(); return Collections.emptyList(); } } } public static abstract class DefaultArgumentMethodSearch<MethodType> { public class Result { public Result(MethodType defaultArgumentMethod, String defaultArgumentName, MethodType overloadedMethod, MethodType implementationMethod) { this.defaultArgumentMethod = defaultArgumentMethod; this.defaultArgumentName = defaultArgumentName; this.overloadedMethod = overloadedMethod; this.implementationMethod = implementationMethod; } public MethodType defaultArgumentMethod; public String defaultArgumentName; public MethodType overloadedMethod; public MethodType implementationMethod; } protected abstract String getMethodName( MethodType method); protected abstract boolean isMethodPrivate( MethodType method); protected abstract List<MethodType> getMethodsOfDeclaringType( MethodType method, String name); protected abstract List<String> getParameterNames( MethodType method); public Result search(MethodType method) { String methodName = getMethodName(method); if (methodName.endsWith( NamingBase.Suffix.$canonical$.name())) { return new Result(null, null, null, method); } String[] parts = methodName.split("\\$"); if (methodName.startsWith("$") && parts.length > 0) { parts[0] = "$" + parts[0]; } String searchedMethodName = ""; boolean canBeADefaultArgumentMethod; if (parts.length == 2 && ! methodName.endsWith("$")) { canBeADefaultArgumentMethod = true; searchedMethodName = parts[0]; if (isMethodPrivate(method)) { searchedMethodName += NamingBase.Suffix.$priv$.name(); } } else { canBeADefaultArgumentMethod = false; searchedMethodName = methodName; } MethodType canonicalMethod = null; List<MethodType> canonicalMethodSearchResult = getMethodsOfDeclaringType(method, searchedMethodName + NamingBase.Suffix.$canonical$.name()); if (canonicalMethodSearchResult != null && ! canonicalMethodSearchResult.isEmpty()) { canonicalMethod = canonicalMethodSearchResult.get(0); } else { List<String> defaultArguments = new ArrayList<>(); List<MethodType> defaultArgumentMethods = new ArrayList<>(); for (MethodType m: getMethodsOfDeclaringType(method, null)) { if (getMethodName(m) .startsWith(parts[0] + "$")) { defaultArgumentMethods.add(m); } } if (! defaultArgumentMethods.isEmpty()) { for (MethodType defaultArgumentMethod: defaultArgumentMethods) { String argumentName = getMethodName( defaultArgumentMethod) .substring(parts[0].length() + 1); if (! argumentName.isEmpty()) { defaultArguments.add(argumentName); } } } if (! defaultArguments.isEmpty()) { List<MethodType> overloadedMethods = getMethodsOfDeclaringType(method, parts[0]); if (overloadedMethods.size() > 1) { for (MethodType overloadedMethod: overloadedMethods) { List<String> argumentNames = getParameterNames( overloadedMethod); if (argumentNames.size() < defaultArguments.size()) { continue; } if (! argumentNames.containsAll( defaultArguments)) { continue; } canonicalMethod = overloadedMethod; break; } } } } if (canonicalMethod != null) { if (canBeADefaultArgumentMethod) { if (getParameterNames(canonicalMethod) .contains(parts[1])) { return new Result(method, parts[1], null, canonicalMethod); } } else { if (canonicalMethod.equals(method)) { return new Result(null, null, null, canonicalMethod); } else { return new Result(null, null, method, canonicalMethod); } } } return new Result(null, null, null, null); } } /* * returns null if it's a method with no Ceylon equivalent * (internal getter of a Ceylon object value) */ public static String getCeylonSimpleName(IMember dec) { String name = dec.getElementName(); String nameAnnotationValue = getCeylonNameAnnotationValue(dec); if (nameAnnotationValue != null) { return nameAnnotationValue; } if (dec instanceof IMethod) { if (name.startsWith(Prefix.$default$.name())) { name = name.substring( Prefix.$default$.name() .length()); } else if (name.equals("get_")) { boolean isStatic = false; int parameterCount = 0; IMethod method = (IMethod) dec; try { isStatic = (method.getFlags() & Flags.AccStatic) != 0; parameterCount = method.getParameterNames() .length; } catch (JavaModelException e) { e.printStackTrace(); } if (isStatic && parameterCount == 0) { name = null; } } else if (name.startsWith("$")) { name = name.substring(1); } else if ((name.startsWith("get") || name.startsWith("set")) && name.length() > 3) { StringBuffer newName = new StringBuffer( Character.toLowerCase( name.charAt(3))); if (name.length() > 4) { newName.append(name.substring(4)); } name = newName.toString(); } else if (name.equals("toString")) { name = "string"; } else if (name.equals("hashCode")) { name = "hash"; } else if (name.contains("$")) { IMethod method = (IMethod) dec; JdtDefaultArgumentMethodSearch.Result searchResult = new JdtDefaultArgumentMethodSearch() .search(method); if (searchResult.defaultArgumentName != null) { name = searchResult.defaultArgumentName; } } if (name.endsWith(Suffix.$canonical$.name())) { name = name.substring(0, name.length() - Suffix.$canonical$.name().length()); } if (name.endsWith(Suffix.$priv$.name())) { name = name.substring(0, name.length() - Suffix.$priv$.name().length()); } } else if (dec instanceof IType) { IType type = (IType) dec; if (name.endsWith("_")) { if (isCeylonObject(type) || isCeylonMethod(type)) { name = name.substring(0, name.length()-1); } } } return name; } public static boolean elementEqualsDeclaration( IMember typeOrMethod, Declaration declaration) { // loadAllAnnotations(typeOrMethod); // Uncomment to easily load all annotations while debugging String javaElementSimpleName = getCeylonSimpleName(typeOrMethod); String ceylonDeclarationSimpleName = declaration.getName(); if (javaElementSimpleName == null) { return false; } if (!javaElementSimpleName.equals( ceylonDeclarationSimpleName)) { if ( javaElementSimpleName.isEmpty()) { try { if (!(typeOrMethod instanceof IType && ((IType)typeOrMethod).isAnonymous() && (declaration instanceof Function) && ! isCeylonObject(typeOrMethod) && AbstractCallable.class.getName() .equals(((IType) typeOrMethod) .getSuperclassName()))) { return false; } } catch (JavaModelException e) { e.printStackTrace(); return false; } } else { return false; } } if (typeOrMethod instanceof IType) { String localDeclarationQualifier = getLocalDeclarationQualifier( typeOrMethod); if (localDeclarationQualifier != null) { if (!localDeclarationQualifier.equals( declaration.getQualifier())) { return false; } } if (isCeylonObject(typeOrMethod) && declaration.isToplevel() && !(declaration instanceof Value)) { return false; } } IMember declaringElement = getDeclaringElement(typeOrMethod); if (declaringElement != null) { declaringElement = toCeylonDeclarationElement( declaringElement); Declaration ceylonContainingDeclaration = getContainingDeclaration(declaration); if (isCeylonObject(declaringElement) && ceylonContainingDeclaration.isToplevel() && !(ceylonContainingDeclaration instanceof Value)) { Scope grandParent = ceylonContainingDeclaration.getContainer(); if (grandParent != null) { ceylonContainingDeclaration = grandParent.getDirectMember( ceylonContainingDeclaration.getName(), null, false); } } if (ceylonContainingDeclaration != null) { return elementEqualsDeclaration( declaringElement, (Declaration) ceylonContainingDeclaration); } } else { Scope scope = declaration.getScope(); if (scope instanceof Package) { String ceylonDeclarationPkgName = scope.getQualifiedNameString(); IPackageFragment pkgFragment = (IPackageFragment) typeOrMethod.getAncestor( PACKAGE_FRAGMENT); String pkgFragmentName = pkgFragment == null ? null : pkgFragment.getElementName(); return ceylonDeclarationPkgName.equals( pkgFragmentName); } } return false; } public static void loadAllAnnotations(IMember typeOrMethod) { Map<String, Map<String, Object>> memberAnnotations = new HashMap<String, Map<String, Object>>(); if (typeOrMethod instanceof IAnnotatable) { try { IAnnotatable annotatable = (IAnnotatable) typeOrMethod; for (IAnnotation annotation: annotatable.getAnnotations()) { String annotationName = annotation.getElementName(); if (!memberAnnotations.containsKey( annotationName)) { memberAnnotations.put(annotationName, new HashMap<String, Object>()); } Map<String, Object> annotationMembers = memberAnnotations.get( annotationName); for (IMemberValuePair pair: annotation.getMemberValuePairs()) { annotationMembers.put( pair.getMemberName(), pair.getValue()); } } } catch (JavaModelException e) { e.printStackTrace(); } } Map<String, Map<String, Object>> typeAnnotations = new HashMap<String, Map<String, Object>>(); if (typeOrMethod instanceof IType) { IType type = (IType) typeOrMethod; try { IAnnotatable annotatable = (IAnnotatable) type; for (IAnnotation annotation: annotatable.getAnnotations()) { String annotationName = annotation.getElementName(); if (! typeAnnotations.containsKey( annotationName)) { typeAnnotations.put(annotationName, new HashMap<String, Object>()); } Map<String, Object> annotationMembers = typeAnnotations.get(annotationName); for (IMemberValuePair pair: annotation.getMemberValuePairs()) { annotationMembers.put( pair.getMemberName(), pair.getValue()); } } } catch (JavaModelException e) { e.printStackTrace(); } } } public static Declaration getContainingDeclaration( Declaration declaration) { Declaration ceylonContainingDeclaration = null; if (declaration == null) { return null; } if (! declaration.isToplevel()) { Scope scope = declaration.getContainer(); while (!(scope instanceof Package)) { if (scope instanceof Declaration) { boolean shouldSkip = false; if (scope instanceof Value) { Value value = (Value) scope; if (! value.isShared() && !value.isTransient() && value.getContainer() instanceof FunctionOrValue) { shouldSkip = true; } } if (!shouldSkip) { ceylonContainingDeclaration = (Declaration) scope; break; } } if (scope instanceof Specification) { Specification specification = (Specification) scope; ceylonContainingDeclaration = specification.getDeclaration(); break; } scope = scope.getContainer(); } } return ceylonContainingDeclaration; } public static IMember getDeclaringElement(IMember typeOrMethod) { // TODO : also manage the case of local declarations that are // wrongly top-level (specific retrieval of the parent IMember) IMember declaringElement = typeOrMethod.getDeclaringType(); if (declaringElement == null) { if (typeOrMethod instanceof IType) { IType type = (IType) typeOrMethod; try { if (type.isLocal() ||type.isAnonymous() || false) { final MethodBinding[] enclosingMethodBinding = new MethodBinding[1]; doOnResolvedGeneratedType(type, new ActionOnResolvedGeneratedType() { @Override public void doWithBinding( IType classModel, ReferenceBinding classBinding, IBinaryType binaryType) { char[] enclosingMethodSignature = binaryType.getEnclosingMethod(); if (enclosingMethodSignature != null && enclosingMethodSignature.length > 0) { ReferenceBinding enclosingType = classBinding.enclosingType(); if (enclosingType != null) { for (MethodBinding method: enclosingType.methods()) { if (CharOperation.equals( CharOperation.concat( method.selector, method.signature()), enclosingMethodSignature)) { enclosingMethodBinding[0] = method; break; } } } } } }); if (enclosingMethodBinding[0] != null) { IMethod enclosingMethod = (IMethod) getUnresolvedJavaElement( enclosingMethodBinding[0], null, null); if (enclosingMethod != null && !enclosingMethod.isResolved()) { JavaElement je = (JavaElement) enclosingMethod; enclosingMethod = (IMethod) je.resolved(enclosingMethodBinding[0]); if (enclosingMethod != null) { declaringElement = enclosingMethod; } } } } } catch (JavaModelException e) { e.printStackTrace(); } } } else { if (typeOrMethod instanceof IMethod) { IMethod method = (IMethod) typeOrMethod; String methodName = method.getElementName(); if (declaringElement.getElementName() != null && declaringElement.getElementName() .equals(methodName + "_") && isCeylonMethod(declaringElement)) { IMember declaringElementDeclaringElement = getDeclaringElement(declaringElement); return declaringElementDeclaringElement; } else if (methodName.contains("$")) { if (declaringElement instanceof IType) { JdtDefaultArgumentMethodSearch.Result searchResult = new JdtDefaultArgumentMethodSearch() .search(method); if (searchResult.defaultArgumentMethod != null) { return searchResult.implementationMethod; } } } } } return declaringElement; } public static IProject[] getProjectsToSearch(IProject project) { if (project == null || project.getName() .equals("Ceylon Source Archives")) { return getProjects().toArray(new IProject[0]); } else { return getProjectAndRelatedProjects(project); } } public static IMember toCeylonDeclarationElement( IMember typeOrMethod) { if (typeOrMethod instanceof IMethod) { IMethod method = (IMethod)typeOrMethod; String methodName = method.getElementName(); if (methodName == null) { return typeOrMethod; } try { IType parentType = method.getDeclaringType(); if (parentType != null) { if ("get_".equals(methodName) && ((method.getFlags() & Flags.AccStatic) > 0) && (isCeylonObject(parentType) || isCeylonAttribute(parentType))) { return toCeylonDeclarationElement( parentType); } if (methodName.equals( parentType.getElementName()) && method.isConstructor() && isCeylon(parentType)) { String constructorName = getCeylonNameAnnotationValue(method); if (constructorName != null) { return method; } else { return toCeylonDeclarationElement( parentType); } } if (methodName.equals(Naming.Unfix.$call$.name()) || methodName.equals(Naming.Unfix.$calltyped$.name()) || methodName.equals(Naming.Unfix.$callvariadic$.name())) { return toCeylonDeclarationElement(parentType); } if (methodName.equals("$evaluate$")) { return toCeylonDeclarationElement( getDeclaringElement(parentType)); } } } catch (JavaModelException e) { e.printStackTrace(); } } if (typeOrMethod instanceof IType) { IType type = (IType) typeOrMethod; String fullyQualifiedTypeName = type.getFullyQualifiedName(); if (fullyQualifiedTypeName.endsWith("$impl")) { IType interfaceType = null; try { String name = fullyQualifiedTypeName.substring(0, fullyQualifiedTypeName.length() - 5); interfaceType = type.getJavaProject() .findType(name, (IProgressMonitor) null); } catch (JavaModelException e) { e.printStackTrace(); } if (interfaceType != null) { return toCeylonDeclarationElement( interfaceType); } } } return typeOrMethod; } public static Declaration toCeylonDeclaration( IJavaElement javaElement, List<? extends PhasedUnit> phasedUnits) { if (! (javaElement instanceof IMember)) { return null; } IMember member = (IMember) javaElement; IMember declarationElement = toCeylonDeclarationElement(member); IProject project = javaElement.getJavaProject() .getProject(); Declaration javaSourceTypeDeclaration = javaSourceElementToTypeDeclaration( javaElement, project); if (javaSourceTypeDeclaration != null && javaSourceTypeDeclaration.isNative()) { Declaration headerDeclaration = ModelUtil.getNativeHeader( javaSourceTypeDeclaration); if (headerDeclaration != null) { if (elementEqualsDeclaration( declarationElement, headerDeclaration)) { return headerDeclaration; } Unit nativeHeaderUnit = headerDeclaration.getUnit(); if (nativeHeaderUnit instanceof CeylonUnit) { CeylonUnit unit = (CeylonUnit) nativeHeaderUnit; PhasedUnit phasedUnit = unit.getPhasedUnit(); if (phasedUnit != null) { phasedUnits = Arrays.asList(phasedUnit); } } } } for (PhasedUnit pu: phasedUnits) { for (Declaration declaration: pu.getDeclarations()) { if (elementEqualsDeclaration( declarationElement, declaration)) { return declaration; } } } return null; } private static boolean belongsToModule( IJavaElement javaElement, BaseIdeModule module) { return javaElement.getAncestor(PACKAGE_FRAGMENT) .getElementName() .startsWith(module.getNameAsString()); } public static Declaration toCeylonDeclaration( IProject project, IJavaElement javaElement) { Set<String> searchedArchives = new HashSet<String>(); for (IProject referencedProject: getProjectAndReferencedProjects(project)) { if (CeylonNature.isEnabled(referencedProject)) { TypeChecker typeChecker = getProjectTypeChecker(referencedProject); if (typeChecker!=null) { List<PhasedUnit> phasedUnits = typeChecker.getPhasedUnits() .getPhasedUnits(); Declaration result = toCeylonDeclaration(javaElement, phasedUnits); if (result!=null) return result; Modules modules = typeChecker.getContext() .getModules(); for (Module m: modules.getListOfModules()) { if (m instanceof BaseIdeModule) { BaseIdeModule module = (BaseIdeModule) m; if (module.getIsCeylonArchive() && !module.getIsProjectModule() && module.getArtifact()!=null) { String archivePath = module.getArtifact() .getAbsolutePath(); if (searchedArchives.add(archivePath) && belongsToModule(javaElement, module)) { result = toCeylonDeclaration(javaElement, module.getPhasedUnitsAsJavaList()); if (result!=null) { return result; } } } } } } } } return null; } public static boolean isCeylonDeclaration( IJavaElement javaElement) { IProject project = javaElement.getJavaProject() .getProject(); IPath path = javaElement.getPath(); if (path!=null) { if (CeylonBuilder.getUnit(javaElement) instanceof CeylonBinaryUnit || isExplodeModulesEnabled(project) && getCeylonClassesOutputFolder(project) .getFullPath() .isPrefixOf(path)) { return true; } Declaration decl = javaSourceElementToTypeDeclaration( javaElement, project); if (decl != null) { return true; } } return false; } private static Declaration javaSourceElementToTypeDeclaration( IJavaElement javaElement, IProject project) { if (javaElement instanceof IMember) { IMember member = (IMember) javaElement; if (member.getTypeRoot() instanceof ICompilationUnit) { IType javaType = null; if (member instanceof IType) { javaType = (IType) member; } else { IJavaElement parent = member.getParent(); while (parent instanceof IMember) { if (parent instanceof IType) { javaType = (IType) parent; break; } parent = parent.getParent(); } } if (javaType != null) { BaseIdeModelLoader modelLoader = CeylonBuilder.getProjectModelLoader( project); if (modelLoader != null) { IJavaModelAware<IProject,ITypeRoot,IJavaElement> javaUnit = CeylonBuilder.getUnit(javaType); if (javaUnit instanceof IUnit) { IUnit unit = (IUnit) javaUnit; BaseIdeModule module = unit.getCeylonModule(); if (module != null) { return modelLoader.convertToDeclaration( module, javaType.getFullyQualifiedName('.'), DeclarationType.TYPE); } } } } } } return null; } }