blob: 2fb85e31ac26dfd99573ce18ca5182f9b0138440 [file] [log] [blame]
package org.checkerframework.framework.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.framework.qual.InvisibleQualifier;
import org.checkerframework.javacutil.BugInCF;
/** A utility for converting AnnotationMirrors to Strings. It omits full package names. */
public class DefaultAnnotationFormatter implements AnnotationFormatter {
/**
* Returns true if, by default, anno should not be printed.
*
* @see org.checkerframework.framework.qual.InvisibleQualifier
* @return true if anno's declaration was qualified by InvisibleQualifier
*/
public static boolean isInvisibleQualified(AnnotationMirror anno) {
TypeElement annoElement = (TypeElement) anno.getAnnotationType().asElement();
return annoElement.getAnnotation(InvisibleQualifier.class) != null;
}
/**
* Creates a String of each annotation in annos separated by a single space character and
* terminated by a space character, obeying the printInvisible parameter.
*
* @param annos a collection of annotations to print
* @param printInvisible whether or not to print "invisible" annotation mirrors
* @return the list of annotations converted to a String
*/
@Override
@SideEffectFree
public String formatAnnotationString(
Collection<? extends AnnotationMirror> annos, boolean printInvisible) {
StringBuilder sb = new StringBuilder();
for (AnnotationMirror obj : annos) {
if (obj == null) {
throw new BugInCF(
"AnnotatedTypeMirror.formatAnnotationString: found null AnnotationMirror");
}
if (isInvisibleQualified(obj) && !printInvisible) {
continue;
}
formatAnnotationMirror(obj, sb);
sb.append(" ");
}
return sb.toString();
}
/**
* Returns the string representation of a single AnnotationMirror, without showing full package
* names.
*
* @param anno the annotation mirror to convert
* @return the string representation of a single AnnotationMirror, without showing full package
* names
*/
@Override
@SideEffectFree
public String formatAnnotationMirror(AnnotationMirror anno) {
StringBuilder sb = new StringBuilder();
formatAnnotationMirror(anno, sb);
return sb.toString();
}
/** A helper method to output a single AnnotationMirror, without showing full package names. */
protected void formatAnnotationMirror(AnnotationMirror am, StringBuilder sb) {
sb.append("@");
sb.append(am.getAnnotationType().asElement().getSimpleName());
Map<ExecutableElement, AnnotationValue> args = removeDefaultValues(am.getElementValues());
if (!args.isEmpty()) {
sb.append("(");
boolean oneValue = false;
if (args.size() == 1) {
Map.Entry<ExecutableElement, AnnotationValue> first = args.entrySet().iterator().next();
if (first.getKey().getSimpleName().contentEquals("value")) {
formatAnnotationMirrorArg(first.getValue(), sb);
oneValue = true;
}
}
if (!oneValue) {
boolean notfirst = false;
for (Map.Entry<ExecutableElement, AnnotationValue> arg : args.entrySet()) {
if (!"{}".equals(arg.getValue().toString())) {
if (notfirst) {
sb.append(", ");
}
notfirst = true;
sb.append(arg.getKey().getSimpleName() + "=");
formatAnnotationMirrorArg(arg.getValue(), sb);
}
}
}
sb.append(")");
}
}
/**
* Returns a new map that only has the values in {@code elementValues} that are not the same as
* the default value.
*
* @param elementValues a mapping of annotation element to annotation value
* @return a new map with only the not default default values of {@code elementValues}
*/
private Map<ExecutableElement, AnnotationValue> removeDefaultValues(
Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues) {
Map<ExecutableElement, AnnotationValue> nonDefaults = new LinkedHashMap<>();
elementValues.forEach(
(element, value) -> {
if (element.getDefaultValue() == null
|| !Objects.equals(value.getValue(), element.getDefaultValue().getValue())) {
nonDefaults.put(element, value);
}
});
return nonDefaults;
}
// A helper method to print AnnotationValues (annotation arguments), without showing full
// package names.
@SuppressWarnings("unchecked")
protected void formatAnnotationMirrorArg(AnnotationValue av, StringBuilder sb) {
Object val = av.getValue();
if (List.class.isAssignableFrom(val.getClass())) {
List<AnnotationValue> vallist = (List<AnnotationValue>) val;
if (vallist.size() == 1) {
formatAnnotationMirrorArg(vallist.get(0), sb);
} else {
sb.append('{');
boolean notfirst = false;
for (AnnotationValue nav : vallist) {
if (notfirst) {
sb.append(", ");
}
notfirst = true;
formatAnnotationMirrorArg(nav, sb);
}
sb.append('}');
}
} else if (VariableElement.class.isAssignableFrom(val.getClass())) {
VariableElement ve = (VariableElement) val;
sb.append(ve.getEnclosingElement().getSimpleName() + "." + ve.getSimpleName());
} else {
sb.append(av.toString());
}
}
}