/******************************************************************************* * Copyright (c) 2016 GK Software AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stephan Herrmann - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; import org.eclipse.jdt.internal.compiler.util.Messages; /** * Used for superimposing external annotations (served by an {@link ITypeAnnotationWalker}) * over signatures of a {@link SourceTypeBinding}. */ class ExternalAnnotationSuperimposer extends TypeBindingVisitor { public static void apply(SourceTypeBinding typeBinding, String externalAnnotationPath) { ZipFile zipFile = null; try { File annotationBase = new File(externalAnnotationPath); if (annotationBase.exists()) { String binaryTypeName = String.valueOf(typeBinding.constantPoolName()); String relativeFileName = binaryTypeName.replace('.', '/')+ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; InputStream input; if (annotationBase.isDirectory()) { input = new FileInputStream(externalAnnotationPath+'/'+relativeFileName); } else { zipFile = new ZipFile(externalAnnotationPath); ZipEntry zipEntry = zipFile.getEntry(relativeFileName); if (zipEntry == null) return; input = zipFile.getInputStream(zipEntry); } annotateType(typeBinding, new ExternalAnnotationProvider(input, binaryTypeName), typeBinding.environment); } } catch (FileNotFoundException e) { // file not found is expected } catch (IOException e) { typeBinding.scope.problemReporter().abortDueToInternalError(Messages.bind(Messages.abort_externaAnnotationFile, new String[] {String.valueOf(typeBinding.readableName()), externalAnnotationPath, e.getMessage()})); } finally { if (zipFile != null) try { zipFile.close(); } catch (IOException e) { // nothing } } } static void annotateType(SourceTypeBinding binding, ExternalAnnotationProvider provider, LookupEnvironment environment) { ITypeAnnotationWalker typeWalker = provider.forTypeHeader(environment); if (typeWalker != null && typeWalker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) { ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); TypeVariableBinding[] typeParameters = binding.typeVariables(); for (int i = 0; i < typeParameters.length; i++) { if (visitor.go(typeWalker.toTypeParameter(true, i))) typeParameters[i] = visitor.superimpose(typeParameters[i], TypeVariableBinding.class); } } binding.externalAnnotationProvider = provider; // for superimposing method signatures } public static void annotateFieldBinding(FieldBinding field, ExternalAnnotationProvider provider, LookupEnvironment environment) { char[] fieldSignature = field.genericSignature(); if (fieldSignature == null && field.type != null) fieldSignature = field.type.signature(); ITypeAnnotationWalker walker = provider.forField(field.name, fieldSignature, environment); ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); if (visitor.go(walker)) field.type = visitor.superimpose(field.type, TypeBinding.class); } public static void annotateMethodBinding(MethodBinding method, ExternalAnnotationProvider provider, LookupEnvironment environment) { char[] methodSignature = method.genericSignature(); if (methodSignature == null) methodSignature = method.signature(); ITypeAnnotationWalker walker = provider.forMethod(method.selector, methodSignature, environment); if (walker != null && walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) { ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); TypeVariableBinding[] typeParams = method.typeVariables; for (short i = 0; i < typeParams.length; i++) { if (visitor.go(walker.toTypeParameter(false, i))) typeParams[i] = visitor.superimpose(typeParams[i], TypeVariableBinding.class); } if (!method.isConstructor()) { if (visitor.go(walker.toMethodReturn())) method.returnType = visitor.superimpose(method.returnType, TypeBinding.class); } TypeBinding[] parameters = method.parameters; for (short i = 0; i < parameters.length; i++) { if (visitor.go(walker.toMethodParameter(i))) parameters[i] = visitor.superimpose(parameters[i], TypeBinding.class); } } } private ITypeAnnotationWalker currentWalker; private TypeBinding typeReplacement; private LookupEnvironment environment; private boolean isReplacing; ExternalAnnotationSuperimposer(LookupEnvironment environment) { this.environment = environment; } /** for constructing a memento of the superimposer's current state. */ private ExternalAnnotationSuperimposer(TypeBinding typeReplacement, boolean isReplacing, ITypeAnnotationWalker walker) { this.typeReplacement = typeReplacement; this.isReplacing = isReplacing; this.currentWalker = walker; } private ExternalAnnotationSuperimposer snapshot() { ExternalAnnotationSuperimposer memento = new ExternalAnnotationSuperimposer(this.typeReplacement, this.isReplacing, this.currentWalker); // soft reset: this.typeReplacement = null; this.isReplacing = false; return memento; } private void restore(ExternalAnnotationSuperimposer memento) { this.isReplacing = memento.isReplacing; this.currentWalker = memento.currentWalker; } boolean go(ITypeAnnotationWalker walker) { // hard reset: reset(); this.typeReplacement = null; this.isReplacing = false; // and start anew: this.currentWalker = walker; return walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER; } <T extends TypeBinding> T superimpose(T type, Class<? extends T> cl) { TypeBindingVisitor.visit(this, type); if (cl.isInstance(this.typeReplacement)) return cl.cast(this.typeReplacement); return type; } private TypeBinding goAndSuperimpose(ITypeAnnotationWalker walker, TypeBinding type) { // no reset here if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) return type; this.currentWalker = walker; TypeBindingVisitor.visit(this, type); if (this.typeReplacement == null) return type; this.isReplacing = true; TypeBinding answer = this.typeReplacement; this.typeReplacement = null; return answer; } @Override public boolean visit(ArrayBinding arrayBinding) { ExternalAnnotationSuperimposer memento = snapshot(); try { int dims = arrayBinding.dimensions; AnnotationBinding[][] annotsOnDims = new AnnotationBinding[dims][]; ITypeAnnotationWalker walker = this.currentWalker; for (int i = 0; i < dims; i++) { IBinaryAnnotation[] binaryAnnotations = walker.getAnnotationsAtCursor(arrayBinding.id); if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { annotsOnDims[i] = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); this.isReplacing = true; } else { annotsOnDims[i] = Binding.NO_ANNOTATIONS; } walker = walker.toNextArrayDimension(); } TypeBinding leafComponentType = goAndSuperimpose(walker, arrayBinding.leafComponentType()); if (this.isReplacing) { this.typeReplacement = this.environment.createArrayType(leafComponentType, dims, AnnotatableTypeSystem.flattenedAnnotations(annotsOnDims)); } } finally { restore(memento); } return false; } @Override public boolean visit(BaseTypeBinding baseTypeBinding) { return false; // no null annotations } @Override public boolean visit(IntersectionTypeBinding18 intersectionTypeBinding18) { return false; // shouldn't occur in declarations } @Override public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) { ExternalAnnotationSuperimposer memento = snapshot(); try { IBinaryAnnotation[] binaryAnnotations = this.currentWalker.getAnnotationsAtCursor(parameterizedTypeBinding.id); AnnotationBinding[] annotations = Binding.NO_ANNOTATIONS; if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { annotations = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); this.isReplacing = true; } TypeBinding[] typeArguments = parameterizedTypeBinding.typeArguments(); TypeBinding[] newArguments = new TypeBinding[typeArguments.length]; for (int i = 0; i < typeArguments.length; i++) { newArguments[i] = goAndSuperimpose(memento.currentWalker.toTypeArgument(i), typeArguments[i]); } if (this.isReplacing) this.typeReplacement = this.environment.createParameterizedType(parameterizedTypeBinding.genericType(), newArguments, parameterizedTypeBinding.enclosingType(), annotations); return false; } finally { restore(memento); } } @Override public boolean visit(RawTypeBinding rawTypeBinding) { return visit((ReferenceBinding)rawTypeBinding); } @Override public boolean visit(ReferenceBinding referenceBinding) { IBinaryAnnotation[] binaryAnnotations = this.currentWalker.getAnnotationsAtCursor(referenceBinding.id); if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) this.typeReplacement = this.environment.createAnnotatedType(referenceBinding, BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null)); return false; } @Override public boolean visit(TypeVariableBinding typeVariable) { return visit((ReferenceBinding) typeVariable); } @Override public boolean visit(WildcardBinding wildcardBinding) { TypeBinding bound = wildcardBinding.bound; ExternalAnnotationSuperimposer memento = snapshot(); try { if (bound != null) { bound = goAndSuperimpose(memento.currentWalker.toWildcardBound(), bound); } IBinaryAnnotation[] binaryAnnotations = memento.currentWalker.getAnnotationsAtCursor(-1); if (this.isReplacing || binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { TypeBinding[] otherBounds = wildcardBinding.otherBounds; if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { AnnotationBinding[] annotations = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); this.typeReplacement = this.environment.createWildcard(wildcardBinding.genericType, wildcardBinding.rank, bound, otherBounds, wildcardBinding.boundKind, annotations); } else { this.typeReplacement = this.environment.createWildcard(wildcardBinding.genericType, wildcardBinding.rank, bound, otherBounds, wildcardBinding.boundKind); } } } finally { restore(memento); } return false; } }