package com.redhat.ceylon.eclipse.core.model; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.ISourceType; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; 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.PackageBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; public final class ModelLoaderTypeRequestor implements ITypeRequestor { private Parser basicParser; private LookupEnvironment lookupEnvironment; private CompilerOptions compilerOptions; public ModelLoaderTypeRequestor(CompilerOptions compilerOptions) { this.compilerOptions = compilerOptions; } public void initialize(LookupEnvironment lookupEnvironment) { this.lookupEnvironment = lookupEnvironment; } @Override public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { // case of SearchableEnvironment of an IJavaProject is used ISourceType sourceType = sourceTypes[0]; while (sourceType.getEnclosingType() != null) sourceType = sourceType.getEnclosingType(); if (sourceType instanceof SourceTypeElementInfo) { // get source SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) sourceType; IType type = elementInfo.getHandle(); ICompilationUnit sourceUnit = (ICompilationUnit) type.getCompilationUnit(); accept(sourceUnit, accessRestriction); } else { CompilationResult result = new CompilationResult(sourceType.getFileName(), 1, 1, 0); CompilationUnitDeclaration unit = SourceTypeConverter.buildCompilationUnit( sourceTypes, SourceTypeConverter.FIELD_AND_METHOD // need field and methods | SourceTypeConverter.MEMBER_TYPE, // need member types // no need for field initialization lookupEnvironment.problemReporter, result); lookupEnvironment.buildTypeBindings(unit, accessRestriction); lookupEnvironment.completeTypeBindings(unit, true); } } private static IBinaryAnnotation[] noAnnots = new IBinaryAnnotation[0]; private static char[] dummyClassFileName = "unknown".toCharArray(); private static Class<?> dummyClassFileNameClass = dummyClassFileName.getClass(); private static boolean hasClassFileNameParameter = false; private static Method getParameterAnnotationsMethod = null; private static void loadGetParameterAnnotationsMethod() throws NoSuchMethodException, SecurityException { if (getParameterAnnotationsMethod == null) { Method m = null; try { m = IBinaryMethod.class.getMethod("getParameterAnnotations", Integer.TYPE); } catch (NoSuchMethodException e) { m = IBinaryMethod.class.getMethod("getParameterAnnotations", Integer.TYPE, dummyClassFileNameClass); hasClassFileNameParameter = true; } m.setAccessible(true); getParameterAnnotationsMethod = m; } } private static IBinaryAnnotation[] getParameterAnnotations(IBinaryMethod methodInfo, int index) { try { loadGetParameterAnnotationsMethod(); if (hasClassFileNameParameter) { return (IBinaryAnnotation[]) getParameterAnnotationsMethod.invoke(methodInfo, index, dummyClassFileName); } else { return (IBinaryAnnotation[]) getParameterAnnotationsMethod.invoke(methodInfo, index); } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { e.printStackTrace(); return noAnnots; } } @Override public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { BinaryTypeBinding btb = lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); if (btb.isNestedType() && !btb.isStatic()) { for (MethodBinding method : btb.methods()) { if (method.isConstructor() && method.parameters.length > 0) { char[] signature = method.signature(); for (IBinaryMethod methodInfo : binaryType.getMethods()) { if (methodInfo.isConstructor()) { char[] methodInfoSignature = methodInfo.getMethodDescriptor(); if (new String(signature).equals(new String(methodInfoSignature))) { IBinaryAnnotation[] binaryAnnotation = getParameterAnnotations(methodInfo, 0); if (binaryAnnotation == null) { if (methodInfo.getAnnotatedParametersCount() == method.parameters.length + 1) { AnnotationBinding[][] newParameterAnnotations = new AnnotationBinding[method.parameters.length][]; for (int i=0; i<method.parameters.length; i++) { IBinaryAnnotation[] goodAnnotations = null; try { goodAnnotations = getParameterAnnotations(methodInfo, i + 1); } catch(IndexOutOfBoundsException e) { break; } if (goodAnnotations != null) { AnnotationBinding[] parameterAnnotations = BinaryTypeBinding.createAnnotations(goodAnnotations, lookupEnvironment, new char[][][] {}); newParameterAnnotations[i] = parameterAnnotations; } } method.setParameterAnnotations(newParameterAnnotations); } } } } } } } } } @Override public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { // Switch the current policy and compilation result for this unit to the requested one. CompilationResult unitResult = new CompilationResult(sourceUnit, 1, 1, compilerOptions.maxProblemsPerUnit); try { CompilationUnitDeclaration parsedUnit = basicParser().dietParse(sourceUnit, unitResult); lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); lookupEnvironment.completeTypeBindings(parsedUnit, true); } catch (AbortCompilationUnit e) { // at this point, currentCompilationUnitResult may not be sourceUnit, but some other // one requested further along to resolve sourceUnit. if (unitResult.compilationUnit == sourceUnit) { // only report once //requestor.acceptResult(unitResult.tagAsAccepted()); } else { throw e; // want to abort enclosing request to compile } } // Display unit error in debug mode if (BasicSearchEngine.VERBOSE) { if (unitResult.problemCount > 0) { System.out.println(unitResult); } } } private Parser basicParser() { if (this.basicParser == null) { ProblemReporter problemReporter = new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, new DefaultProblemFactory()); this.basicParser = new Parser(problemReporter, false); this.basicParser.reportOnlyOneSyntaxError = true; } return this.basicParser; } }