package org.checkerframework.framework.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.framework.source.Result;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
/**
* Represents field invariants. A list of fields that have a specific qualifier in a class where
* these field invariants apply.
*/
public class FieldInvariants {
/** a list of simple filed names. */
private final List<String> fields;
/** A list of qualifiers that apply to the field at the same index in {@code fields}. */
private final List<AnnotationMirror> qualifiers;
public FieldInvariants(List<String> fields, List<AnnotationMirror> qualifiers) {
this(null, fields, qualifiers);
}
/**
* Creates a new object with all the invariant in {@code other}, plus those specified by {@code
* fields} and {@code qualifiers}.
*
* @param other other invariant object, may be null
* @param fields list of fields
* @param qualifiers list of qualifiers
*/
public FieldInvariants(
FieldInvariants other, List<String> fields, List<AnnotationMirror> qualifiers) {
if (fields.size() > qualifiers.size() && qualifiers.size() == 1) {
int difference = fields.size() - qualifiers.size();
for (int i = 0; i < difference; i++) {
qualifiers.add(qualifiers.get(0));
}
}
if (other != null) {
fields.addAll(other.fields);
qualifiers.addAll(other.qualifiers);
}
this.fields = Collections.unmodifiableList(fields);
this.qualifiers = qualifiers;
}
public List<String> getFields() {
return fields;
}
/**
* Returns a list of qualifiers for {@code field}. If field has no qualifiers, then the empty
* list is returned.
*
* @param field Simple field name
* @return a list of qualifiers for {@code field}
*/
public List<AnnotationMirror> getQualifiersFor(CharSequence field) {
String fieldString = field.toString();
if (isWellFormed()) {
int index = fields.indexOf(fieldString);
if (index == -1) {
return Collections.emptyList();
}
List<AnnotationMirror> list = new ArrayList<>();
for (int i = 0; i < fields.size(); i++) {
if (fields.get(i).equals(fieldString)) {
list.add(qualifiers.get(i));
}
}
return list;
}
return Collections.emptyList();
}
/** @return whether or not there is a qualifier for each field */
public boolean isWellFormed() {
return qualifiers.size() == fields.size();
}
/**
* @return null if {@code superInvar} is a super invariant, otherwise returns a Result with the
* error message
*/
public Result isSuperInvariant(FieldInvariants superInvar, AnnotatedTypeFactory factory) {
QualifierHierarchy qualifierHierarchy = factory.getQualifierHierarchy();
if (!this.fields.containsAll(superInvar.fields)) {
List<String> missingFields = new ArrayList<>(superInvar.fields);
missingFields.removeAll(fields);
return Result.failure(
"field.invariant.not.found.superclass", PluginUtil.join(", ", missingFields));
}
for (String field : superInvar.fields) {
List<AnnotationMirror> superQualifiers = superInvar.getQualifiersFor(field);
List<AnnotationMirror> subQualifiers = this.getQualifiersFor(field);
for (AnnotationMirror superA : superQualifiers) {
AnnotationMirror sub =
qualifierHierarchy.findAnnotationInSameHierarchy(subQualifiers, superA);
if (sub == null || !qualifierHierarchy.isSubtype(sub, superA)) {
return Result.failure(
"field.invariant.not.subtype.superclass", field, sub, superA);
}
}
}
return null;
}
}