/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2003-2007 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.ba.jsr305;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.meta.When;
import edu.umd.cs.findbugs.util.DualKeyHashMap;
/**
* A type qualifier applied to a field, method, parameter, or return value.
*
* @author Bill Pugh
* @author David Hovemeyer
*/
public class TypeQualifierAnnotation {
public final TypeQualifierValue<?> typeQualifier;
public final When when;
private TypeQualifierAnnotation(TypeQualifierValue<?> typeQualifier, When when) {
this.typeQualifier = typeQualifier;
this.when = when;
}
public static final TypeQualifierAnnotation OVERRIDES_BUT_NO_ANNOTATION = new TypeQualifierAnnotation(null, null) {
@Override
public String toString() {
return "Overrides but no annotation";
}
};
// private static DualKeyHashMap <TypeQualifierValue, When,
// TypeQualifierAnnotation> map = new DualKeyHashMap <TypeQualifierValue,
// When, TypeQualifierAnnotation> ();
private static ThreadLocal<DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation>> instance = new ThreadLocal<DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation>>() {
@Override
protected DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation> initialValue() {
return new DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation>();
}
};
public static void clearInstance() {
instance.remove();
}
// public static synchronized @NonNull TypeQualifierAnnotation
// getValue(ClassDescriptor desc, Object value, When when) {
// return getValue(TypeQualifierValue.getValue(desc, value), when);
// }
// When lattice:
//
// In subtypes:
// - return value type must be at least as narrow as supertypes
// - parameter types must be at least as wide as supertypes
//
// TOP TOP is invalid as return type:
// / | \ means that no When value is narrow enough
// / | \ for combination of supertype annotations
// / | \
// Always Unknown Never ^ Narrower
// \ | / |
// \ | / |
// \ | / |
// Maybe v Wider
//
private static final When TOP = null;
private static final When[][] combineReturnValueMatrix = {
// ALWAYS UNKNOWN MAYBE NEVER
/* ALWAYS */{ When.ALWAYS, },
/* UNKNOWN */{ When.ALWAYS, When.UNKNOWN, },
/* MAYBE */{ When.ALWAYS, When.UNKNOWN, When.MAYBE, },
/* NEVER */{ TOP, TOP, When.NEVER, When.NEVER }, };
private static final When[][] combineParameterMatrix = {
// ALWAYS UNKNOWN MAYBE NEVER
/* ALWAYS */{ When.ALWAYS, },
/* UNKNOWN */{ When.UNKNOWN, When.UNKNOWN, },
/* MAYBE */{ When.MAYBE, When.MAYBE, When.MAYBE, },
/* NEVER */{ When.MAYBE, When.UNKNOWN, When.MAYBE, When.NEVER }, };
/**
* Combine return type annotations.
*
* @param a
* a TypeQualifierAnnotation used on a return value
* @param b
* another TypeQualifierAnnotation used on a return value
* @return combined return type annotation that is at least as narrow as
* both <code>a</code> or <code>b</code>, or null if no such
* TypeQualifierAnnotation exists
*/
public static @CheckForNull
TypeQualifierAnnotation combineReturnTypeAnnotations(TypeQualifierAnnotation a, TypeQualifierAnnotation b) {
return combineAnnotations(a, b, combineReturnValueMatrix);
}
/**
*
* @param a
* a TypeQualifierAnnotation used on a method parameter
* @param b
* another TypeQualifierAnnotation used on a method parameter
* @return combined parameter annotation that is at least as wide as both a
* and b
*/
public static @Nonnull
TypeQualifierAnnotation combineParameterAnnotations(TypeQualifierAnnotation a, TypeQualifierAnnotation b) {
return combineAnnotations(a, b, combineParameterMatrix);
}
private static TypeQualifierAnnotation combineAnnotations(TypeQualifierAnnotation a, TypeQualifierAnnotation b,
When[][] mergeMatrix) {
assert a.typeQualifier.equals(b.typeQualifier);
When aWhen = a.when;
When bWhen = b.when;
if (aWhen.ordinal() < bWhen.ordinal()) {
When tmp = aWhen;
aWhen = bWhen;
bWhen = tmp;
}
When combined = mergeMatrix[aWhen.ordinal()][bWhen.ordinal()];
if (combined != null) {
return getValue(a.typeQualifier, combined);
} else {
return null;
}
}
public static @Nonnull
Collection<TypeQualifierAnnotation> getValues(Map<TypeQualifierValue<?>, When> map) {
Collection<TypeQualifierAnnotation> result = new LinkedList<TypeQualifierAnnotation>();
for (Map.Entry<TypeQualifierValue<?>, When> e : map.entrySet()) {
result.add(getValue(e.getKey(), e.getValue()));
}
return result;
}
public static @Nonnull
TypeQualifierAnnotation getValue(TypeQualifierValue<?> desc, When when) {
DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation> map = instance.get();
TypeQualifierAnnotation result = map.get(desc, when);
if (result != null) {
return result;
}
result = new TypeQualifierAnnotation(desc, when);
map.put(desc, when, result);
return result;
}
@Override
public int hashCode() {
return typeQualifier.hashCode() * 37 + when.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof TypeQualifierAnnotation)) {
return false;
}
TypeQualifierAnnotation other = (TypeQualifierAnnotation) o;
return typeQualifier.equals(other.typeQualifier) && when.equals(other.when);
}
@Override
public String toString() {
return typeQualifier + ":" + when;
}
}