framework.src.org.checkerframework.qualframework.poly.format.SurfaceSyntaxQualParamsFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java’s type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
package org.checkerframework.qualframework.poly.format;
import org.checkerframework.javacutil.ErrorReporter;
import org.checkerframework.qualframework.poly.PolyQual;
import org.checkerframework.qualframework.poly.PolyQual.Combined;
import org.checkerframework.qualframework.poly.PolyQual.GroundQual;
import org.checkerframework.qualframework.poly.PolyQual.QualVar;
import org.checkerframework.qualframework.poly.QualParams;
import org.checkerframework.qualframework.poly.Wildcard;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* SurfaceSyntaxQualParamsFormatter formats QualParams qualifiers into their annotation equivalent.
*
* Not all Qualifiers can be converted into annotations that could be written by the user, so the output
* is not exact.
*/
public class SurfaceSyntaxQualParamsFormatter implements QualParamsFormatter {
/* Object to hold the type-system specific information required to conver the quals to annotations. */
private final SurfaceSyntaxFormatterConfiguration config;
public SurfaceSyntaxQualParamsFormatter(SurfaceSyntaxFormatterConfiguration config) {
this.config = config;
}
@Override
public String format(QualParams params) {
return format(params, true, true);
}
@Override
public String format(QualParams params, boolean printInvisible) {
return format(params, true, printInvisible);
}
@Override
public String format(QualParams params, boolean printPrimary, boolean printInvisible) {
StringBuffer sb = new StringBuffer();
// Special exception for the top and bottom of the hierarchy.
if (params == config.getQualTop()) {
AnnotationParts anno = config.getTargetTypeSystemAnnotation(config.getTop());
if (config.shouldPrintAnnotation(anno, printInvisible)) {
return anno.toString();
} else {
return null;
}
} else if (params == config.getQualBottom()) {
AnnotationParts anno = config.getTargetTypeSystemAnnotation(config.getBottom());
if (config.shouldPrintAnnotation(anno, printInvisible)) {
return anno.toString();
} else {
return null;
}
}
// Primary
boolean printedPrimary = false;
if (printPrimary && params.getPrimary() != null) {
List annos = createAnnotations(params.getPrimary(), printInvisible);
for (AnnotationParts anno : annos) {
if (printedPrimary) {
sb.append(" ");
} else {
printedPrimary = true;
}
sb.append(anno.toString());
}
}
// Qualifier Parameters
boolean addSpace = printedPrimary;
for (Entry> entry : params.entrySet()) {
List annos = createAnnotations(entry.getValue(), entry.getKey(), printInvisible);
for (AnnotationParts anno : annos) {
if (addSpace) {
sb.append(" ");
} else {
addSpace = true;
}
sb.append(anno.toString());
}
}
if (sb.length() > 0) {
return sb.toString();
} else {
return null;
}
}
@Override
public String format(PolyQual polyQual, boolean printInvisible) {
StringBuffer sb = new StringBuffer();
List annos = createAnnotations(polyQual, printInvisible);
boolean first = false;
for (AnnotationParts anno : annos) {
if (first) {
first = false;
} else {
sb.append(" ");
}
sb.append(anno);
}
if (sb.length() > 0) {
return sb.toString();
} else {
return null;
}
}
/**
* Transform a wildcard into a List of AnnotationParts.
*
* This method uses {@link #createAnnotations(org.checkerframework.qualframework.poly.PolyQual, boolean)} to create
* the annotation parts for each bounds.
*
* @param wildcard the Wildcard
* @param paramName the name of the qualifier parameter the wildcard was a value for
* @param printInvisible flag to enable printing invisible qualifiers
* @return a List of AnnotationParts that correspond wildcard
*/
private List createAnnotations(Wildcard wildcard, String paramName, boolean printInvisible) {
if (wildcard.isEmpty()) {
ErrorReporter.errorAbort("Unable to convert wildcard: " + wildcard);
}
List results = new ArrayList<>();
List upper = createAnnotations(wildcard.getUpperBound(), printInvisible);
Map bounds =
new HashMap<>();
for (AnnotationParts part: upper) {
part.putQuoted("param", paramName);
bounds.put(part, org.checkerframework.qualframework.poly.qual.Wildcard.EXTENDS);
}
results.addAll(upper);
List lower = createAnnotations(wildcard.getLowerBound(), printInvisible);
for (AnnotationParts part: lower) {
part.putQuoted("param", paramName);
// If we have both an Upper and Lower entry for the annotation, we can omit the wildcard
if (upper.contains(part)) {
bounds.remove(part);
} else {
bounds.put(part, org.checkerframework.qualframework.poly.qual.Wildcard.SUPER);
results.add(part);
}
}
List filteredResults = new ArrayList<>();
for (AnnotationParts anno : results) {
if (bounds.containsKey(anno)) {
anno.put("wildcard", createWildcardString(bounds.get(anno)));
}
if (config.shouldPrintAnnotation(anno, printInvisible)) {
filteredResults.add(anno);
}
}
return filteredResults;
}
/**
* Create a List of AnnotationParts that correspond to a polyQual.
*
* Instances of Combined may result in multiple AnnotationsParts because Combined PolyQuals are created
* when multiple annotations are present on a type.
*
* @param polyQual the PolyQual
* @param printInvisible flag to enable printing invisible qualifiers
* @return a List of AnnotationParts corresponding to PolyQual
*/
private List createAnnotations(PolyQual polyQual, boolean printInvisible) {
List result = new ArrayList<>();
if (polyQual == null) {
return result;
} else if (polyQual instanceof Combined) {
Combined combined = (Combined) polyQual;
for (QualVar var : combined.getVars()) {
List anno = createAnnotations(var, printInvisible);
result.addAll(anno);
}
AnnotationParts anno = config.getTargetTypeSystemAnnotation(combined.getGround());
if (anno != null) {
result.add(anno);
}
} else if (polyQual instanceof GroundQual) {
AnnotationParts anno = config.getTargetTypeSystemAnnotation(((GroundQual) polyQual).getQualifier());
if (anno != null) {
result.add(anno);
}
} else if (polyQual instanceof QualVar) {
QualVar qualVar = (QualVar) polyQual;
String lower = formatQual(qualVar.getLowerBound(), printInvisible);
String upper = formatQual(qualVar.getUpperBound(), printInvisible);
AnnotationParts anno = new AnnotationParts("Var");
anno.putQuoted("arg", qualVar.getName());
// This is a range, there is no real annotation equivalent, so use a "range" field.
if (lower != null || upper != null) {
lower = lower == null? "" : lower;
upper = upper == null? "" : upper;
anno.putQuoted("range", " ∈ [" + lower + ".." + upper + "]");
}
result.add(anno);
} else {
ErrorReporter.errorAbort("Unknown PolyQual Subclass: " + polyQual.getClass());
return result; // Dead code
}
List filteredResults = new ArrayList<>();
for (AnnotationParts anno : result) {
if (config.shouldPrintAnnotation(anno, printInvisible)) {
filteredResults.add(anno);
}
}
return filteredResults;
}
private String formatQual(Q qual, boolean printInvisible) {
AnnotationParts anno = config.getTargetTypeSystemAnnotation(qual);
if (anno != null && config.shouldPrintAnnotation(anno, printInvisible)) {
return anno.toString();
} else {
return null;
}
}
/**
* Format a wildcard enum to include the Wildcard classname.
*/
private String createWildcardString(org.checkerframework.qualframework.poly.qual.Wildcard wildcardType) {
return wildcardType.getDeclaringClass().getSimpleName() + "." + wildcardType;
}
/**
* Object to generate an annotation String from an Annotation name and a map of values.
*
* We generate annotation output for annotations that don't actually exist, so we cant use something like
* AnnotationBuilder which requires the element of the annotation.
*
*/
public static class AnnotationParts {
private String name;
private Map fields = new HashMap<>();
public AnnotationParts(String name) {
this.name = name;
}
public void putQuoted(String key, String value) {
fields.put(key, "\"" + value + "\"");
}
public void put(String key, String value) {
fields.put(key, value);
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AnnotationParts that = (AnnotationParts) o;
if (!fields.equals(that.fields)) return false;
if (!name.equals(that.name)) return false;
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + fields.hashCode();
return result;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("@");
sb.append(name);
if (fields.size() == 1 && fields.get("value") != null) {
sb.append("(");
sb.append(fields.get("value"));
sb.append(")");
} else if (fields.size() > 0) {
sb.append("(");
boolean first = true;
for (Map.Entry entry : fields.entrySet()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(entry.getKey());
sb.append("=");
sb.append(entry.getValue());
}
sb.append(")");
}
return sb.toString();
}
}
}