mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 12:45:32 +08:00
Merge remote-tracking branch
'origin/GP-4744_Dan_PR-6681_h4ck3r-04_update_docs' (Closes #6681)
This commit is contained in:
+20
@@ -20,13 +20,33 @@ import java.lang.annotation.Annotation;
|
|||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
|
import ghidra.util.database.DBAnnotatedObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class for validating annotations on {@link DBAnnotatedObject}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Performs validation checks on annotated fields and their enclosing types.
|
||||||
|
*/
|
||||||
public class AbstractDBAnnotationValidator {
|
public class AbstractDBAnnotationValidator {
|
||||||
protected final ValidationContext ctx;
|
protected final ValidationContext ctx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new {@code AbstractDBAnnotationValidator} with the specified validation context.
|
||||||
|
*
|
||||||
|
* @param ctx the validation context
|
||||||
|
*/
|
||||||
public AbstractDBAnnotationValidator(ValidationContext ctx) {
|
public AbstractDBAnnotationValidator(ValidationContext ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the enclosing type of the annotated field.
|
||||||
|
*
|
||||||
|
* @param annotType the type of the annotation being validated
|
||||||
|
* @param field the field being validated
|
||||||
|
* @param type the enclosing type of the field
|
||||||
|
*/
|
||||||
protected void checkEnclosingType(Class<? extends Annotation> annotType, VariableElement field,
|
protected void checkEnclosingType(Class<? extends Annotation> annotType, VariableElement field,
|
||||||
TypeElement type) {
|
TypeElement type) {
|
||||||
if (type.getKind() != ElementKind.CLASS) {
|
if (type.getKind() != ElementKind.CLASS) {
|
||||||
|
|||||||
+5
-1
@@ -19,6 +19,10 @@ import java.util.Set;
|
|||||||
|
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum to represent different levels of access specifiers (private, package-private, protected,
|
||||||
|
* public) with corresponding access levels
|
||||||
|
*/
|
||||||
public enum AccessSpec {
|
public enum AccessSpec {
|
||||||
PRIVATE(0), PACKAGE(1), PROTECTED(2), PUBLIC(3);
|
PRIVATE(0), PACKAGE(1), PROTECTED(2), PUBLIC(3);
|
||||||
|
|
||||||
@@ -29,7 +33,7 @@ public enum AccessSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the second permits the same or more access than the first
|
* Check if the second permits the same or more access than the first
|
||||||
*
|
*
|
||||||
* @param first the first
|
* @param first the first
|
||||||
* @param second the second
|
* @param second the second
|
||||||
|
|||||||
+29
@@ -22,14 +22,43 @@ import javax.tools.Diagnostic.Kind;
|
|||||||
|
|
||||||
import ghidra.util.database.annot.DBAnnotatedColumn;
|
import ghidra.util.database.annot.DBAnnotatedColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for validating fields annotated with {@link DBAnnotatedColumn}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To ensure fields annotated with {@link DBAnnotatedColumn} comply with the expected criteria for
|
||||||
|
* database columns in Ghidra.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
|
||||||
public class DBAnnotatedColumnValidator extends AbstractDBAnnotationValidator {
|
public class DBAnnotatedColumnValidator extends AbstractDBAnnotationValidator {
|
||||||
final VariableElement column;
|
final VariableElement column;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new {@code DBAnnotatedColumnValidator} with the specified validation context and
|
||||||
|
* the column element.
|
||||||
|
*
|
||||||
|
* @param ctx the validation context
|
||||||
|
* @param column the field representing the column
|
||||||
|
*/
|
||||||
public DBAnnotatedColumnValidator(ValidationContext ctx, VariableElement column) {
|
public DBAnnotatedColumnValidator(ValidationContext ctx, VariableElement column) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
this.column = column;
|
this.column = column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the annotated column field.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It performs the following checks to ensure it meets the requirements for database columns:
|
||||||
|
* <ul>
|
||||||
|
* <li>The field must be of the type specified by {@code ctx.DB_OBJECT_COLUMN_ELEM}.</li>
|
||||||
|
* <li>The field must not be declared as {@code final}.</li>
|
||||||
|
* <li>The field must be declared as {@code static}.</li>
|
||||||
|
* <li>The enclosing type of the field must meet the criteria defined in
|
||||||
|
* {@code checkEnclosingType}.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
public void validate() {
|
public void validate() {
|
||||||
if (!ctx.hasType(column, ctx.DB_OBJECT_COLUMN_ELEM)) {
|
if (!ctx.hasType(column, ctx.DB_OBJECT_COLUMN_ELEM)) {
|
||||||
ctx.messager.printMessage(Kind.ERROR,
|
ctx.messager.printMessage(Kind.ERROR,
|
||||||
|
|||||||
+67
@@ -23,6 +23,14 @@ import javax.tools.Diagnostic.Kind;
|
|||||||
|
|
||||||
import ghidra.util.database.annot.DBAnnotatedField;
|
import ghidra.util.database.annot.DBAnnotatedField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for validating fields annotated with {@link DBAnnotatedField}
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To ensure fields annotated with {@link DBAnnotatedField} meet the criteria required for database
|
||||||
|
* fields in Ghidra. It extends the {@code AbstractDBAnnotationValidator} to provide additional
|
||||||
|
* validation logic specific to database field annotations.
|
||||||
|
*/
|
||||||
public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
||||||
final VariableElement field;
|
final VariableElement field;
|
||||||
final Map<TypeMirror, TypeElement> javaToDBTypeMap;
|
final Map<TypeMirror, TypeElement> javaToDBTypeMap;
|
||||||
@@ -39,6 +47,13 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
|
|
||||||
final TypeElement ENUM_CODEC_ELEM;
|
final TypeElement ENUM_CODEC_ELEM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new {@code DBAnnotatedFieldValidator} with the specified validation context and
|
||||||
|
* field element.
|
||||||
|
*
|
||||||
|
* @param ctx the validation context
|
||||||
|
* @param field the field to validate
|
||||||
|
*/
|
||||||
public DBAnnotatedFieldValidator(ValidationContext ctx, VariableElement field) {
|
public DBAnnotatedFieldValidator(ValidationContext ctx, VariableElement field) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
this.field = field;
|
this.field = field;
|
||||||
@@ -59,6 +74,13 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
ENUM_CODEC_ELEM = ctx.elementUtils.getTypeElement(ENUM_CODEC_NAME);
|
ENUM_CODEC_ELEM = ctx.elementUtils.getTypeElement(ENUM_CODEC_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a primitive type and its boxed type with the specified codec type in the map.
|
||||||
|
*
|
||||||
|
* @param map the map linking java types to their corresponding codec types
|
||||||
|
* @param kind the primitive type kind
|
||||||
|
* @param codecName the fully qualified name of the codec type
|
||||||
|
*/
|
||||||
protected void putPrimitiveTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
|
protected void putPrimitiveTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
|
||||||
String codecName) {
|
String codecName) {
|
||||||
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
|
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
|
||||||
@@ -68,12 +90,26 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
map.put(boxed, codec);
|
map.put(boxed, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a specified class type with the specified codec type in the map.
|
||||||
|
*
|
||||||
|
* @param map the map linking Java types to their corresponding codec types
|
||||||
|
* @param cls the class type
|
||||||
|
* @param codecName the fully qualified name of the codec type
|
||||||
|
*/
|
||||||
protected void putTypeCodec(Map<TypeMirror, TypeElement> map, Class<?> cls, String codecName) {
|
protected void putTypeCodec(Map<TypeMirror, TypeElement> map, Class<?> cls, String codecName) {
|
||||||
TypeMirror type = ctx.elementUtils.getTypeElement(cls.getCanonicalName()).asType();
|
TypeMirror type = ctx.elementUtils.getTypeElement(cls.getCanonicalName()).asType();
|
||||||
TypeElement codec = ctx.elementUtils.getTypeElement(codecName);
|
TypeElement codec = ctx.elementUtils.getTypeElement(codecName);
|
||||||
map.put(type, codec);
|
map.put(type, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a primitive array type with the specified codec type inthe map.
|
||||||
|
*
|
||||||
|
* @param map the map linking Java types to their corresponding codec types
|
||||||
|
* @param kind the primitive type kind
|
||||||
|
* @param codecName the fully qualified name of the codec type
|
||||||
|
*/
|
||||||
protected void putPrimitiveArrayTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
|
protected void putPrimitiveArrayTypeCodec(Map<TypeMirror, TypeElement> map, TypeKind kind,
|
||||||
String codecName) {
|
String codecName) {
|
||||||
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
|
PrimitiveType primitive = ctx.typeUtils.getPrimitiveType(kind);
|
||||||
@@ -82,6 +118,19 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
map.put(array, codec);
|
map.put(array, codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the annotated field to ensure it meets the requirements for database fields.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It performs the following checks:
|
||||||
|
* <ul>
|
||||||
|
* <li>The field must not be declared as {@code final}.</li>
|
||||||
|
* <li>The field must not be declared as {@code static}.</li>
|
||||||
|
* <li>The enclosing type of the field must meet the criteria defined in
|
||||||
|
* {@code checkEnclosingType}.</li>
|
||||||
|
* <li>The codec types for the field must be appropriate.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
public void validate() {
|
public void validate() {
|
||||||
Set<Modifier> mods = field.getModifiers();
|
Set<Modifier> mods = field.getModifiers();
|
||||||
if (mods.contains(Modifier.FINAL)) {
|
if (mods.contains(Modifier.FINAL)) {
|
||||||
@@ -101,6 +150,12 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
checkCodecTypes(type);
|
checkCodecTypes(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the default codec type element for the specified Java type.
|
||||||
|
*
|
||||||
|
* @param javaType the Java type for which the default codec is needed
|
||||||
|
* @return the default codec type element, or {@code null} if no default codec is found
|
||||||
|
*/
|
||||||
protected TypeElement getDefaultCodecType(TypeMirror javaType) {
|
protected TypeElement getDefaultCodecType(TypeMirror javaType) {
|
||||||
if (ctx.isEnumType(javaType)) {
|
if (ctx.isEnumType(javaType)) {
|
||||||
return ENUM_CODEC_ELEM;
|
return ENUM_CODEC_ELEM;
|
||||||
@@ -108,6 +163,12 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
return javaToDBTypeMap.get(javaType);
|
return javaToDBTypeMap.get(javaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the codec type element specified in the {@link DBAnnotatedField} annotation for the
|
||||||
|
* field, or the default codec type if none is specified.
|
||||||
|
*
|
||||||
|
* @return the codec type element for the field
|
||||||
|
*/
|
||||||
protected TypeElement getCodecTypeElement() {
|
protected TypeElement getCodecTypeElement() {
|
||||||
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
|
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
|
||||||
TypeElement codecElem;
|
TypeElement codecElem;
|
||||||
@@ -123,6 +184,12 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
|
|||||||
return codecElem;
|
return codecElem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the codec types associated with the field to ensure they meet the necessary
|
||||||
|
* requirements.
|
||||||
|
*
|
||||||
|
* @param objectType the type of the enclosing object
|
||||||
|
*/
|
||||||
protected void checkCodecTypes(TypeElement objectType) {
|
protected void checkCodecTypes(TypeElement objectType) {
|
||||||
|
|
||||||
TypeElement codecType = getCodecTypeElement();
|
TypeElement codecType = getCodecTypeElement();
|
||||||
|
|||||||
+39
-5
@@ -29,8 +29,10 @@ import ghidra.util.database.annot.*;
|
|||||||
/**
|
/**
|
||||||
* A compile-time annotation processor for {@link DBAnnotatedObject}-related annotations.
|
* A compile-time annotation processor for {@link DBAnnotatedObject}-related annotations.
|
||||||
*
|
*
|
||||||
* Currently just performs compile-time checks. It does not generate any code, but perhaps one day,
|
* <p>
|
||||||
* it will.
|
* This processor performs compile-time validation checks on annotations related to
|
||||||
|
* {@link DBAnnotatedObject}. Currently just performs compile-time checks. It does not generate any
|
||||||
|
* code, but perhaps one day, it will.
|
||||||
*/
|
*/
|
||||||
//@AutoService(Processor.class) // TODO: Evaluate Google's auto-service as a dependency
|
//@AutoService(Processor.class) // TODO: Evaluate Google's auto-service as a dependency
|
||||||
public class DBAnnotatedObjectProcessor extends AbstractProcessor {
|
public class DBAnnotatedObjectProcessor extends AbstractProcessor {
|
||||||
@@ -39,6 +41,11 @@ public class DBAnnotatedObjectProcessor extends AbstractProcessor {
|
|||||||
|
|
||||||
private ValidationContext ctx;
|
private ValidationContext ctx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the processor with the given preprocessing environment.
|
||||||
|
*
|
||||||
|
* @param env the processing environment
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void init(ProcessingEnvironment env) {
|
public synchronized void init(ProcessingEnvironment env) {
|
||||||
//System.err.println("HERE4");
|
//System.err.println("HERE4");
|
||||||
@@ -46,6 +53,14 @@ public class DBAnnotatedObjectProcessor extends AbstractProcessor {
|
|||||||
ctx = new ValidationContext(env);
|
ctx = new ValidationContext(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the specified annotations for the current round of processing..
|
||||||
|
*
|
||||||
|
* @param annotations the set of annotations to process
|
||||||
|
* @param roundEnv the environment for information about the current and prior round
|
||||||
|
* @return {@code true} if the annotations are claimed by this processor, {@code false}
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
Map<TypeElement, DBAnnotatedObjectValidator> types = new LinkedHashMap<>();
|
Map<TypeElement, DBAnnotatedObjectValidator> types = new LinkedHashMap<>();
|
||||||
@@ -76,21 +91,40 @@ public class DBAnnotatedObjectProcessor extends AbstractProcessor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide completion suggestion for the specified element, annotation, and member.
|
||||||
|
*
|
||||||
|
* @param element the element being annotated
|
||||||
|
* @param annotation the annotation being processed
|
||||||
|
* @param member the annotation member being completed
|
||||||
|
* @param userText the text entered by the user
|
||||||
|
* @return an iterable of completions.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Iterable<? extends Completion> getCompletions(Element element,
|
public Iterable<? extends Completion> getCompletions(Element element,
|
||||||
AnnotationMirror annotation, ExecutableElement member, String userText) {
|
AnnotationMirror annotation, ExecutableElement member, String userText) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return super.getCompletions(element, annotation, member, userText);
|
return super.getCompletions(element, annotation, member, userText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest supported source version.
|
||||||
|
*
|
||||||
|
* @return the latest supported source version
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
return SourceVersion.latestSupported();
|
return SourceVersion.latestSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the set of supported annotation types.
|
||||||
|
*
|
||||||
|
* @return the set of supported annotation types
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getSupportedAnnotationTypes() {
|
public Set<String> getSupportedAnnotationTypes() {
|
||||||
return SUPPORTED_ANNOTATIONS.stream().map(Class::getCanonicalName).collect(
|
return SUPPORTED_ANNOTATIONS.stream()
|
||||||
Collectors.toSet());
|
.map(Class::getCanonicalName)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+47
@@ -20,31 +20,62 @@ import java.util.*;
|
|||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
|
import ghidra.util.database.DBAnnotatedObject;
|
||||||
import ghidra.util.database.annot.*;
|
import ghidra.util.database.annot.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate {@link DBAnnotatedObject}-related annotations on a given type element.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class ensures that annotations such as {@link DBAnnotatedField}, {@link DBAnnotatedColumn},
|
||||||
|
* and {@link DBAnnotatedObjectInfo} are applied correctly and consistently on the fields and
|
||||||
|
* columns of a class.
|
||||||
|
*/
|
||||||
public class DBAnnotatedObjectValidator {
|
public class DBAnnotatedObjectValidator {
|
||||||
private final ValidationContext ctx;
|
private final ValidationContext ctx;
|
||||||
private final TypeElement type;
|
private final TypeElement type;
|
||||||
private final Map<String, DBAnnotatedFieldValidator> fieldsByName = new LinkedHashMap<>();
|
private final Map<String, DBAnnotatedFieldValidator> fieldsByName = new LinkedHashMap<>();
|
||||||
private final Map<String, DBAnnotatedColumnValidator> columnsByName = new LinkedHashMap<>();
|
private final Map<String, DBAnnotatedColumnValidator> columnsByName = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new validator for the given type element within the specified validation context
|
||||||
|
*
|
||||||
|
* @param ctx the validation context
|
||||||
|
* @param type the type element to be validated
|
||||||
|
*/
|
||||||
public DBAnnotatedObjectValidator(ValidationContext ctx, TypeElement type) {
|
public DBAnnotatedObjectValidator(ValidationContext ctx, TypeElement type) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a field annotated with {@link DBAnnotatedField} to be validator.
|
||||||
|
*
|
||||||
|
* @param field the field element annotated with {@link DBAnnotatedField}
|
||||||
|
*/
|
||||||
public void addAnnotatedField(VariableElement field) {
|
public void addAnnotatedField(VariableElement field) {
|
||||||
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
|
DBAnnotatedField annotation = field.getAnnotation(DBAnnotatedField.class);
|
||||||
assert annotation != null;
|
assert annotation != null;
|
||||||
fieldsByName.put(annotation.column(), new DBAnnotatedFieldValidator(ctx, field));
|
fieldsByName.put(annotation.column(), new DBAnnotatedFieldValidator(ctx, field));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a column annotated with {@link DBAnnotatedColumn} to the validator.
|
||||||
|
*
|
||||||
|
* @param column the field element annotated with {@link DBAnnotatedColumn}
|
||||||
|
*/
|
||||||
public void addAnnotatedColumn(VariableElement column) {
|
public void addAnnotatedColumn(VariableElement column) {
|
||||||
DBAnnotatedColumn annotation = column.getAnnotation(DBAnnotatedColumn.class);
|
DBAnnotatedColumn annotation = column.getAnnotation(DBAnnotatedColumn.class);
|
||||||
assert annotation != null;
|
assert annotation != null;
|
||||||
columnsByName.put(annotation.value(), new DBAnnotatedColumnValidator(ctx, column));
|
columnsByName.put(annotation.value(), new DBAnnotatedColumnValidator(ctx, column));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the annotated fields, columns, and the type element itself.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Checks for various annotation constraints and consistency rules.
|
||||||
|
*/
|
||||||
public void validate() {
|
public void validate() {
|
||||||
DBAnnotatedObjectInfo annotation = type.getAnnotation(DBAnnotatedObjectInfo.class);
|
DBAnnotatedObjectInfo annotation = type.getAnnotation(DBAnnotatedObjectInfo.class);
|
||||||
if (annotation != null && type.getKind() != ElementKind.CLASS) {
|
if (annotation != null && type.getKind() != ElementKind.CLASS) {
|
||||||
@@ -81,18 +112,27 @@ public class DBAnnotatedObjectValidator {
|
|||||||
checkMissing();
|
checkMissing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate all fields annotated with {@link DBAnnotatedField}.
|
||||||
|
*/
|
||||||
protected void validateFields() {
|
protected void validateFields() {
|
||||||
for (DBAnnotatedFieldValidator fv : fieldsByName.values()) {
|
for (DBAnnotatedFieldValidator fv : fieldsByName.values()) {
|
||||||
fv.validate();
|
fv.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate all columns annotated with {@link DBAnnotatedColumn}.
|
||||||
|
*/
|
||||||
protected void validateColumns() {
|
protected void validateColumns() {
|
||||||
for (DBAnnotatedColumnValidator cv : columnsByName.values()) {
|
for (DBAnnotatedColumnValidator cv : columnsByName.values()) {
|
||||||
cv.validate();
|
cv.validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for missing corresponding annotations between fields and columns.
|
||||||
|
*/
|
||||||
protected void checkMissing() {
|
protected void checkMissing() {
|
||||||
Set<String> names = new LinkedHashSet<>();
|
Set<String> names = new LinkedHashSet<>();
|
||||||
names.addAll(fieldsByName.keySet());
|
names.addAll(fieldsByName.keySet());
|
||||||
@@ -121,6 +161,13 @@ public class DBAnnotatedObjectValidator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the access specifiers of the field and column are compatible.
|
||||||
|
*
|
||||||
|
* @param field the field element
|
||||||
|
* @param column the column element
|
||||||
|
* @param name the name of the column
|
||||||
|
*/
|
||||||
protected void checkAccess(VariableElement field, VariableElement column, String name) {
|
protected void checkAccess(VariableElement field, VariableElement column, String name) {
|
||||||
AccessSpec fieldSpec = AccessSpec.get(field.getModifiers());
|
AccessSpec fieldSpec = AccessSpec.get(field.getModifiers());
|
||||||
AccessSpec columnSpec = AccessSpec.get(column.getModifiers());
|
AccessSpec columnSpec = AccessSpec.get(column.getModifiers());
|
||||||
|
|||||||
+183
-1
@@ -38,6 +38,11 @@ public class ValidationContext {
|
|||||||
final TypeElement DEFAULT_CODEC_ELEM;
|
final TypeElement DEFAULT_CODEC_ELEM;
|
||||||
final TypeElement ENUM_ELEM;
|
final TypeElement ENUM_ELEM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Validation Context with the specified processing environment/
|
||||||
|
*
|
||||||
|
* @param env the processing environment
|
||||||
|
*/
|
||||||
public ValidationContext(ProcessingEnvironment env) {
|
public ValidationContext(ProcessingEnvironment env) {
|
||||||
typeUtils = env.getTypeUtils();
|
typeUtils = env.getTypeUtils();
|
||||||
elementUtils = env.getElementUtils();
|
elementUtils = env.getElementUtils();
|
||||||
@@ -54,14 +59,35 @@ public class ValidationContext {
|
|||||||
ENUM_ELEM = elementUtils.getTypeElement(Enum.class.getCanonicalName());
|
ENUM_ELEM = elementUtils.getTypeElement(Enum.class.getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if t1 is a subclass of t2.
|
||||||
|
*
|
||||||
|
* @param t1 the potential subclass
|
||||||
|
* @param t2 the potential superclass
|
||||||
|
* @return true if t1 is a subclass of t2, false otherwise
|
||||||
|
*/
|
||||||
public boolean isSubclass(TypeElement t1, TypeElement t2) {
|
public boolean isSubclass(TypeElement t1, TypeElement t2) {
|
||||||
return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType()));
|
return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the field has the specified type.
|
||||||
|
*
|
||||||
|
* @param field the field element
|
||||||
|
* @param type the type element
|
||||||
|
* @return true if the field has the specified type, false otherwise
|
||||||
|
*/
|
||||||
public boolean hasType(VariableElement field, TypeElement type) {
|
public boolean hasType(VariableElement field, TypeElement type) {
|
||||||
return hasType(field, type.asType());
|
return hasType(field, type.asType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the field has the specified type.
|
||||||
|
*
|
||||||
|
* @param field the field element
|
||||||
|
* @param type the type mirror
|
||||||
|
* @return true if the field has the specified type, false otherwise
|
||||||
|
*/
|
||||||
public boolean hasType(VariableElement field, TypeMirror type) {
|
public boolean hasType(VariableElement field, TypeMirror type) {
|
||||||
TypeMirror fieldType = field.asType();
|
TypeMirror fieldType = field.asType();
|
||||||
try {
|
try {
|
||||||
@@ -86,9 +112,16 @@ public class ValidationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return typeUtils.isAssignable(fieldType, type);
|
return typeUtils.isAssignable(fieldType, type);
|
||||||
// return typeUtils.isSameType(fieldType, type);
|
// return typeUtils.isSameType(fieldType, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if t1 is capturable by t2.
|
||||||
|
*
|
||||||
|
* @param t1 is the type to check
|
||||||
|
* @param t2 the capture target type
|
||||||
|
* @return true if t1 is capturable by t2, false otherwise
|
||||||
|
*/
|
||||||
public boolean isCapturable(TypeMirror t1, TypeMirror t2) {
|
public boolean isCapturable(TypeMirror t1, TypeMirror t2) {
|
||||||
// TODO: This only works for typevar at top level...
|
// TODO: This only works for typevar at top level...
|
||||||
// TODO: Need to figure out how to check for capture and check
|
// TODO: Need to figure out how to check for capture and check
|
||||||
@@ -105,6 +138,12 @@ public class ValidationContext {
|
|||||||
return typeUtils.isSubtype(t1, t2);
|
return typeUtils.isSubtype(t1, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the type is an enum type.
|
||||||
|
*
|
||||||
|
* @param t the type mirror to check
|
||||||
|
* @return true if the type is an enum type, false otherwise
|
||||||
|
*/
|
||||||
public boolean isEnumType(TypeMirror t) {
|
public boolean isEnumType(TypeMirror t) {
|
||||||
if (t.getKind() != TypeKind.DECLARED) {
|
if (t.getKind() != TypeKind.DECLARED) {
|
||||||
return false;
|
return false;
|
||||||
@@ -113,6 +152,13 @@ public class ValidationContext {
|
|||||||
return typeUtils.isSubtype(t, enumType);
|
return typeUtils.isSubtype(t, enumType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the supertype of a set of declared types that matches the specified super type.
|
||||||
|
*
|
||||||
|
* @param types the set of declared types
|
||||||
|
* @param superType the super type element to match
|
||||||
|
* @return the matching declared type, or null if no match is found
|
||||||
|
*/
|
||||||
protected DeclaredType findSupertype(Set<DeclaredType> types, TypeElement superType) {
|
protected DeclaredType findSupertype(Set<DeclaredType> types, TypeElement superType) {
|
||||||
Set<DeclaredType> next;
|
Set<DeclaredType> next;
|
||||||
while (!types.isEmpty()) {
|
while (!types.isEmpty()) {
|
||||||
@@ -132,14 +178,35 @@ public class ValidationContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the supertype of a declared type that matches the specified super type element.
|
||||||
|
*
|
||||||
|
* @param type the declared type
|
||||||
|
* @param superElem the super type element to match
|
||||||
|
* @return the matching declared type, or null if no match is found
|
||||||
|
*/
|
||||||
public DeclaredType findSupertype(DeclaredType type, TypeElement superElem) {
|
public DeclaredType findSupertype(DeclaredType type, TypeElement superElem) {
|
||||||
return findSupertype(Set.of(type), superElem);
|
return findSupertype(Set.of(type), superElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the supertype of a type element that matches the specified super type element.
|
||||||
|
*
|
||||||
|
* @param elem the type element
|
||||||
|
* @param superElem the super type element to match
|
||||||
|
* @return the matching declared type, or null if no match is found
|
||||||
|
*/
|
||||||
public DeclaredType findSupertype(TypeElement elem, TypeElement superElem) {
|
public DeclaredType findSupertype(TypeElement elem, TypeElement superElem) {
|
||||||
return findSupertype((DeclaredType) elem.asType(), superElem);
|
return findSupertype((DeclaredType) elem.asType(), superElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the type arguments of the super type element to a map.
|
||||||
|
*
|
||||||
|
* @param superElem the super type element
|
||||||
|
* @param superType the declared super type
|
||||||
|
* @return a map of type argument names to their corresponding type mirrors
|
||||||
|
*/
|
||||||
protected Map<String, TypeMirror> toArgsMap(TypeElement superElem, DeclaredType superType) {
|
protected Map<String, TypeMirror> toArgsMap(TypeElement superElem, DeclaredType superType) {
|
||||||
List<? extends TypeParameterElement> typeParameters = superElem.getTypeParameters();
|
List<? extends TypeParameterElement> typeParameters = superElem.getTypeParameters();
|
||||||
List<? extends TypeMirror> typeArguments = superType.getTypeArguments();
|
List<? extends TypeMirror> typeArguments = superType.getTypeArguments();
|
||||||
@@ -151,14 +218,34 @@ public class ValidationContext {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type arguments of a declared type as a map.
|
||||||
|
*
|
||||||
|
* @param type the declared type
|
||||||
|
* @param superElem the super type element
|
||||||
|
* @return a map of type argument names to their corresponding type mirrors
|
||||||
|
*/
|
||||||
public Map<String, TypeMirror> getArguments(DeclaredType type, TypeElement superElem) {
|
public Map<String, TypeMirror> getArguments(DeclaredType type, TypeElement superElem) {
|
||||||
return toArgsMap(superElem, findSupertype(type, superElem));
|
return toArgsMap(superElem, findSupertype(type, superElem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type arguments of a type element as a map.
|
||||||
|
*
|
||||||
|
* @param elem the type element
|
||||||
|
* @param superElem the super type element
|
||||||
|
* @return a map of type argument names to their corresponding type mirrors
|
||||||
|
*/
|
||||||
public Map<String, TypeMirror> getArguments(TypeElement elem, TypeElement superElem) {
|
public Map<String, TypeMirror> getArguments(TypeElement elem, TypeElement superElem) {
|
||||||
return toArgsMap(superElem, findSupertype(elem, superElem));
|
return toArgsMap(superElem, findSupertype(elem, superElem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the given type mirror as a string.
|
||||||
|
*
|
||||||
|
* @param type the type mirror to format
|
||||||
|
* @return the formatted type mirror as a string
|
||||||
|
*/
|
||||||
public String format(TypeMirror type) {
|
public String format(TypeMirror type) {
|
||||||
FormatVisitor vis = new FormatVisitor();
|
FormatVisitor vis = new FormatVisitor();
|
||||||
type.accept(vis, null);
|
type.accept(vis, null);
|
||||||
@@ -166,9 +253,20 @@ public class ValidationContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for formatting {@link TypeMirror} instances into a readable string
|
||||||
|
*/
|
||||||
class FormatVisitor implements TypeVisitor<Void, Void> {
|
class FormatVisitor implements TypeVisitor<Void, Void> {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link TypeMirror}. Delegates to specific visit methods based on the type
|
||||||
|
* kind.
|
||||||
|
*
|
||||||
|
* @param t the type mirror to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visit(TypeMirror t, Void p) {
|
public Void visit(TypeMirror t, Void p) {
|
||||||
switch (t.getKind()) {
|
switch (t.getKind()) {
|
||||||
@@ -207,18 +305,39 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link PrimitiveType}.
|
||||||
|
*
|
||||||
|
* @param t the primitive type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitPrimitive(PrimitiveType t, Void p) {
|
public Void visitPrimitive(PrimitiveType t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link NullType}.
|
||||||
|
*
|
||||||
|
* @param t the null type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitNull(NullType t, Void p) {
|
public Void visitNull(NullType t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link ArrayType}.
|
||||||
|
*
|
||||||
|
* @param t the array type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitArray(ArrayType t, Void p) {
|
public Void visitArray(ArrayType t, Void p) {
|
||||||
visit(t.getComponentType());
|
visit(t.getComponentType());
|
||||||
@@ -226,6 +345,13 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link DeclaredType}.
|
||||||
|
*
|
||||||
|
* @param t the declared type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitDeclared(DeclaredType t, Void p) {
|
public Void visitDeclared(DeclaredType t, Void p) {
|
||||||
buf.append(t.asElement().toString());
|
buf.append(t.asElement().toString());
|
||||||
@@ -242,12 +368,26 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link ErrorType}.
|
||||||
|
*
|
||||||
|
* @param t the error type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitError(ErrorType t, Void p) {
|
public Void visitError(ErrorType t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link TypeVariable}.
|
||||||
|
*
|
||||||
|
* @param t the type variable to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitTypeVariable(TypeVariable t, Void p) {
|
public Void visitTypeVariable(TypeVariable t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
@@ -264,6 +404,13 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link WildcardType}.
|
||||||
|
*
|
||||||
|
* @param t the wildcard type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitWildcard(WildcardType t, Void p) {
|
public Void visitWildcard(WildcardType t, Void p) {
|
||||||
buf.append("?");
|
buf.append("?");
|
||||||
@@ -280,24 +427,52 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link ExecutableType}.
|
||||||
|
*
|
||||||
|
* @param t the executable type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitExecutable(ExecutableType t, Void p) {
|
public Void visitExecutable(ExecutableType t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link NoType}.
|
||||||
|
*
|
||||||
|
* @param t the no-type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitNoType(NoType t, Void p) {
|
public Void visitNoType(NoType t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for unknown {@link TypeMirror} instances.
|
||||||
|
*
|
||||||
|
* @param t the unknown type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitUnknown(TypeMirror t, Void p) {
|
public Void visitUnknown(TypeMirror t, Void p) {
|
||||||
buf.append(t.toString());
|
buf.append(t.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link UnionType}.
|
||||||
|
*
|
||||||
|
* @param t the union type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitUnion(UnionType t, Void p) {
|
public Void visitUnion(UnionType t, Void p) {
|
||||||
Iterator<? extends TypeMirror> it = t.getAlternatives().iterator();
|
Iterator<? extends TypeMirror> it = t.getAlternatives().iterator();
|
||||||
@@ -311,6 +486,13 @@ class FormatVisitor implements TypeVisitor<Void, Void> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit method for {@link IntersectionType}.
|
||||||
|
*
|
||||||
|
* @param t the intersection type to visit
|
||||||
|
* @param p unused parameter (can be {@code null})
|
||||||
|
* @return {@code null}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitIntersection(IntersectionType t, Void p) {
|
public Void visitIntersection(IntersectionType t, Void p) {
|
||||||
Iterator<? extends TypeMirror> it = t.getBounds().iterator();
|
Iterator<? extends TypeMirror> it = t.getBounds().iterator();
|
||||||
|
|||||||
Reference in New Issue
Block a user