blob: b1785aa2401eb083825e2a5835b55cae212be360 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 GK Software AG.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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, false);
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, false);
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, false);
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, false);
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;
}
}