blob: 1d4c8a99536623f457b2c0bb24095c1452bcd2f1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 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.aspectj.org.eclipse.jdt.internal.compiler.classfmt;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
/**
* A type annotation walker that adds missing NonNull annotations according to the current default.
*/
public class NonNullDefaultAwareTypeAnnotationWalker extends TypeAnnotationWalker {
private int defaultNullness;
private boolean atDefaultLocation;
private boolean nextIsDefaultLocation;
private boolean isEmpty;
IBinaryAnnotation nonNullAnnotation;
/** Create initial walker with non-empty type annotations. */
public NonNullDefaultAwareTypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations,
int defaultNullness, LookupEnvironment environment) {
super(typeAnnotations);
this.nonNullAnnotation = getNonNullAnnotation(environment);
this.defaultNullness = defaultNullness;
}
/** Create an initial walker without 'real' type annotations, but with a nonnull default. */
public NonNullDefaultAwareTypeAnnotationWalker(int defaultNullness, LookupEnvironment environment) {
this(defaultNullness, getNonNullAnnotation(environment), false);
}
/** Get restricted walker, still with non-empty type annotations. */
NonNullDefaultAwareTypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations, long newMatches, int newPathPtr,
int defaultNullness, IBinaryAnnotation nonNullAnnotation, boolean atDefaultLocation) {
super(typeAnnotations, newMatches, newPathPtr);
this.defaultNullness = defaultNullness;
this.nonNullAnnotation = nonNullAnnotation;
this.atDefaultLocation = atDefaultLocation;
}
/** Create a restricted walker without 'real' type annotations, but with a nonnull default. */
NonNullDefaultAwareTypeAnnotationWalker(int defaultNullness, IBinaryAnnotation nonNullAnnotation, boolean atDefaultLocation) {
super(null, 0, 0);
this.nonNullAnnotation = nonNullAnnotation;
this.defaultNullness = defaultNullness;
this.atDefaultLocation = atDefaultLocation;
this.isEmpty = true;
}
private static IBinaryAnnotation getNonNullAnnotation(LookupEnvironment environment) {
final char[] nonNullAnnotationName = CharOperation.concat(
'L', CharOperation.concatWith(environment.getNonNullAnnotationName(), '/'), ';');
// create the synthetic annotation:
return new IBinaryAnnotation() {
@Override
public char[] getTypeName() {
return nonNullAnnotationName;
}
@Override
public IBinaryElementValuePair[] getElementValuePairs() {
return null;
}
};
}
protected TypeAnnotationWalker restrict(long newMatches, int newPathPtr) {
// considers nextIsDefaultLocation as the new atDefaultLocation
try {
// do we have any change at all?
if (this.matches == newMatches && this.pathPtr == newPathPtr && this.atDefaultLocation == this.nextIsDefaultLocation)
return this;
// are we running out of real type annotations?
if (newMatches == 0 || this.typeAnnotations == null || this.typeAnnotations.length == 0)
return new NonNullDefaultAwareTypeAnnotationWalker(this.defaultNullness, this.nonNullAnnotation, this.nextIsDefaultLocation);
// proceed as normal, but pass on our specific fields, too:
return new NonNullDefaultAwareTypeAnnotationWalker(this.typeAnnotations, newMatches, newPathPtr,
this.defaultNullness, this.nonNullAnnotation, this.nextIsDefaultLocation);
} finally {
this.nextIsDefaultLocation = false; // expire
}
}
@Override
public TypeAnnotationWalker toMethodParameter(short index) {
// don't set nextIsDefaultLocation, because signature-level nullness is handled by ImplicitNullAnnotationVerifier
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toMethodParameter(index);
}
@Override
public TypeAnnotationWalker toMethodReturn() {
// don't set nextIsDefaultLocation, because signature-level nullness is handled by ImplicitNullAnnotationVerifier
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toMethodReturn();
}
@Override
public TypeAnnotationWalker toTypeBound(short boundIndex) {
this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0;
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toTypeBound(boundIndex);
}
@Override
public TypeAnnotationWalker toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) {
this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeBound) != 0;
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toTypeParameterBounds(isClassTypeParameter, parameterRank);
}
@Override
public TypeAnnotationWalker toTypeArgument(int rank) {
this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeArgument) != 0;
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toTypeArgument(rank);
}
@Override
public TypeAnnotationWalker toTypeParameter(boolean isClassTypeParameter, int rank) {
this.nextIsDefaultLocation = (this.defaultNullness & Binding.DefaultLocationTypeParameter) != 0;
if (this.isEmpty) return restrict(this.matches, this.pathPtr);
return super.toTypeParameter(isClassTypeParameter, rank);
}
@Override
public IBinaryAnnotation[] getAnnotationsAtCursor() {
IBinaryAnnotation[] normalAnnotations = this.isEmpty ? null : super.getAnnotationsAtCursor();
if (this.atDefaultLocation) {
if (normalAnnotations == null || normalAnnotations.length == 0)
return new IBinaryAnnotation[] { this.nonNullAnnotation };
int len = normalAnnotations.length;
IBinaryAnnotation[] newAnnots = new IBinaryAnnotation[len+1];
System.arraycopy(normalAnnotations, 0, newAnnots, 0, len);
newAnnots[len] = this.nonNullAnnotation;
return newAnnots;
}
return normalAnnotations;
}
}