| /******************************************************************************* |
| * 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; |
| } |
| } |