@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { final @Nonnull TypeMirror declaredType = ProcessingUtility.getType(element); final @Nullable Class<?> desiredType = ProcessingUtility.getClass(ProcessingUtility.getAnnotationValue(annotationMirror)); if (desiredType != null && !ProcessingUtility.isRawlyAssignable(declaredType, desiredType)) { errorLogger.log("The value of type $ is not rawly assignable to $:", SourcePosition.of(element), ProcessingUtility.getSimpleName(declaredType), desiredType.getSimpleName()); } }
/** * Returns the component type of the type of the given element if it is an array or an iterable and logs errors with the given logger otherwise. */ @Pure @LogsErrorWhenReturningNull public static @Nullable TypeMirror getComponentType(@Nonnull Element element, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { return getComponentType(getType(element), errorLogger); }
/** * Returns whether the given mirror corresponds to the given type. */ @Pure public static boolean correspond(@Nonnull TypeMirror mirror, @Nonnull Class<?> type) { return getQualifiedName(mirror).equals(type.getCanonicalName()); }
protected FieldInformationImplementation(@Nonnull Element element, @Nonnull TypeMirror type, @Nonnull DeclaredType containingType) { super(element, type, containingType); this.defaultValue = ProcessingUtility.getString(ProcessingUtility.getAnnotationValue(element, Default.class)); }
/** * Returns whether the given element is rawly assignable to the given type. */ @Pure public static boolean isRawlyAssignable(@Nonnull Element element, @Nonnull Class<?> type) { return isRawlyAssignable(getType(element), type); }
/** * Returns the value of the given annotation mirror as a suffix for the method name. */ @Pure protected static @Nonnull String getSuffix(@Nonnull AnnotationMirror annotationMirror) { // The following line is only necessary because the IncorrectUsage testing annotation passes its own annotation mirror. if (ProcessingUtility.getSimpleName(annotationMirror).equals("IncorrectUsage")) { return ""; } final @Nullable AnnotationValue annotationValue = ProcessingUtility.getAnnotationValue(annotationMirror); final @Nullable String string = ProcessingUtility.getString(annotationValue); return string == null ? "" : Strings.uppercaseFirstCharacter(string); }
@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { final @Nonnull TypeMirror declaredType = ProcessingUtility.getType(element); final @Nullable Class<?> desiredType = ProcessingUtility.getClass(ProcessingUtility.getAnnotationValue(annotationMirror)); if (desiredType != null && !ProcessingUtility.isRawSubtype(declaredType, desiredType)) { errorLogger.log("The type $ is not a subtype of $:", SourcePosition.of(element), ProcessingUtility.getSimpleName(declaredType), desiredType.getSimpleName()); } }
final @Nonnull String access = getObjectVariableName() + "." + field.getAccessCode(); if ((type.getKind() == TypeKind.ARRAY || ProcessingUtility.isRawSubtype(type, Iterable.class) || ProcessingUtility.isRawSubtype(type, Map.class)) && !field.hasAnnotation(Nonnull.class)) { ProcessingLog.error("Cannot convert an array, iterable or map that is nullable.", SourcePosition.of(field.getElement())); } else if (ProcessingUtility.isRawSubtype(type, Map.class)) { final @Nullable DeclaredType supertype = ProcessingUtility.getSupertype((DeclaredType) type, Map.class); if (supertype != null) { final boolean nullable = !field.hasAnnotation(NonNullableElements.class); addStatement("encoder.encodeMap" + (nullable ? "WithNullableValues" : "") + "(" + importConverterType(typeArguments.get(0), FiniteIterable.of()) + ", " + importConverterType(typeArguments.get(1), FiniteIterable.of()) + ", " + access + ")"); } else if (type.getKind() == TypeKind.ARRAY || ProcessingUtility.isRawSubtype(type, Iterable.class)) { final @Nullable TypeMirror componentType = ProcessingUtility.getComponentType(type); if (componentType != null) { if (componentType.getKind() == TypeKind.BYTE) { addStatement("encoder.encodeBinary(" + access + ")"); } else { final boolean unordered = ProcessingUtility.isRawSubtype(type, Set.class); final boolean nullable = !field.hasAnnotation(NonNullableElements.class); addStatement("encoder.encode" + (unordered ? "Unordered" : "Ordered") + "Iterable" + (nullable ? "WithNullableElements" : "") + "(" + importConverterType(componentType, FiniteIterable.of()) + ", " + importIfPossible(FiniteIterable.class) + ".of(" + access + "))"); } else if (ProcessingUtility.getTypeElement(type).getKind() == ElementKind.ENUM && StaticProcessingEnvironment.getTypeUtils().isAssignable(type, typeInformation.getType())) { addStatement("encoder.encodeObject(" + importIfPossible("net.digitalid.utility.conversion.converters.String64Converter") + ".INSTANCE, " + access + ")" ); } else {
final @Nullable Class<@Nonnull ?> annotationClass = ProcessingUtility.getClass(annotation.getAnnotationType()); Require.that(annotationClass != null).orThrow("The annotation class {} should not resolve into a null type."); if (alreadyProcessedAnnotations.add(annotationClass)) { final @Nonnull String annotationName = ProcessingUtility.getSimpleName(annotation); final @Nonnull String qualifiedAnnotationName = ProcessingUtility.getQualifiedName(annotation.getAnnotationType()); final @Nonnull String annotationValuesMap = fieldName + Strings.capitalizeFirstLetters(annotationName); if (customAnnotations.length() != 0) { statements.add("final @" + importIfPossible(Nonnull.class) + " " + importIfPossible(Map.class) + Brackets.inPointy(importTypeAnnotationIfPossible(Nonnull.class) + importIfPossible(String.class) + ", " + importTypeAnnotationIfPossible(Nullable.class) + importIfPossible(Object.class)) + " " + annotationValuesMap + " = new " + importIfPossible(HashMap.class) + Brackets.inPointy("") + Brackets.inRound("")); addedMap = true; final @Nonnull Map<@Nonnull String, @Nonnull AnnotationValue> annotationValues = ProcessingUtility.getAnnotationValues(annotation); for (Map.Entry<@Nonnull String, @Nonnull AnnotationValue> entry : annotationValues.entrySet()) { @Nonnull String printValue = ProcessingUtility.getAnnotationValueAsString(entry.getValue(), this); if (printValue.startsWith("{") && printValue.contains(".class")) { final @Nonnull String nameOfVariable = annotationValuesMap + Strings.capitalizeFirstLetters(entry.getKey()) + "Classes";
/** * Returns the annotation handlers of the given type which are found with the given meta-annotation type on the annotations of the given element. */ @Pure @SuppressWarnings("unchecked") public static @Capturable <H extends AnnotationHandler> @Modifiable @Nonnull Map<@Nonnull AnnotationMirror, @Nonnull H> getAnnotationHandlers(@Nonnull Element element, @Nonnull Class<? extends Annotation> metaAnnotationType, @Nonnull Class<H> annotationHandlerType) { final @Nonnull Map<@Nonnull AnnotationMirror, @Nonnull H> result = new LinkedHashMap<>(); for (@Nonnull AnnotationMirror annotationMirror : ProcessingUtility.getAnnotationMirrors(element)) { final @Nullable H annotationHandler; final @Nonnull String cacheKey = ProcessingUtility.getQualifiedName(annotationMirror) + "$" + metaAnnotationType.getCanonicalName(); if (cachedAnnotationHandlers.containsKey(cacheKey)) { final @Nullable AnnotationHandler cachedAnnotationHandler = cachedAnnotationHandlers.get(cacheKey); annotationHandler = annotationHandlerType.isInstance(cachedAnnotationHandler) ? (H) cachedAnnotationHandler : null; } else { final @Nonnull TypeElement annotationElement = (TypeElement) annotationMirror.getAnnotationType().asElement(); final @Nullable AnnotationValue metaAnnotationValue = ProcessingUtility.getAnnotationValue(annotationElement, metaAnnotationType); annotationHandler = ProcessingUtility.getInstance(metaAnnotationValue, annotationHandlerType); cachedAnnotationHandlers.put(cacheKey, annotationHandler); } if (annotationHandler != null) { ProcessingLog.debugging("Found the annotation handler $ for", SourcePosition.of(element, annotationMirror), annotationHandler.getClass().getCanonicalName()); annotationHandler.checkUsage(element, annotationMirror, ErrorLogger.INSTANCE); result.put(annotationMirror, annotationHandler); } } return result; }
/** * Returns the custom type for the given representing field. */ public static String getTypeName(@Nonnull TypeMirror representingFieldType, @Nonnull FiniteIterable<@Nonnull AnnotationMirror> annotations, @Nonnull TypeImporter typeImporter) { @Nonnull CustomType customType = CustomType.of(representingFieldType, annotations); // TODO: ProcessingUtility.getTypeElement(representingFieldType) is null for generic types but checking this only here leads to new problems (namely "The name 'TConverter' has to be qualified."). if (customType == CustomType.TUPLE && ProcessingUtility.getTypeElement(representingFieldType).getKind() == ElementKind.ENUM) { return typeImporter.importStaticallyIfPossible(CustomType.class.getCanonicalName() + "." + customType.getTypeName()) + ".of" + Brackets.inRound(typeImporter.importIfPossible("net.digitalid.utility.conversion.converters.StringConverter") + ".INSTANCE"); } else if (customType == CustomType.SET || customType == CustomType.LIST || customType == CustomType.ARRAY) { final @Nonnull TypeMirror componentType = ProcessingUtility.getComponentType(representingFieldType); return typeImporter.importStaticallyIfPossible(CustomType.class.getCanonicalName() + "." + customType.getTypeName()) + ".of" + Brackets.inRound(getTypeName(componentType, FiniteIterable.of(), typeImporter)); } else if (customType == CustomType.MAP) { final @Nullable List<TypeMirror> componentTypes = ProcessingUtility.getComponentTypes(representingFieldType); Require.that(componentTypes.size() == 2).orThrow("Map type does not have 2 component types."); return typeImporter.importStaticallyIfPossible(CustomType.class.getCanonicalName() + "." + customType.getTypeName()) + ".of" + Brackets.inRound(getTypeName(componentTypes.get(0), FiniteIterable.of(), typeImporter) + ", " + getTypeName(componentTypes.get(1), FiniteIterable.of(), typeImporter)); } else if (representingFieldType.getKind().isPrimitive() || customType == BINARY || customType == BINARY128 || customType == BINARY256) { return typeImporter.importStaticallyIfPossible(CustomType.class.getCanonicalName() + "." + customType.getTypeName()); } else { @Nonnull String typeName = customType.getTypeName(); @Nonnull String qualifiedName = ProcessingUtility.getQualifiedName(representingFieldType); if (!qualifiedName.startsWith("net.digitalid")) { typeName = TUPLE.getTypeName(); } return typeImporter.importStaticallyIfPossible(CustomType.class.getCanonicalName() + "." + typeName) + ".of" + Brackets.inRound(importConverterType(representingFieldType, annotations, typeImporter)); } }
/** * Returns true if the field is annotated with @MaxSize and the value is lower or equal than the given max size parameter value. */ @Pure public boolean hasMaxSize(int maxSize) { boolean hasMaxSize = true; final @Nullable AnnotationMirror first = annotationMirrors.findFirst(annotationMirror -> ProcessingUtility.getQualifiedName(annotationMirror).equals(MaxSize.class.getName())); hasMaxSize = hasMaxSize && first != null; if (first != null) { final @Nullable AnnotationValue annotationValue = ProcessingUtility.getAnnotationValue(first); Require.that(annotationValue != null).orThrow("MaxSize requires an annotation value."); hasMaxSize = hasMaxSize && ((int) annotationValue.getValue()) <= maxSize; } return hasMaxSize; }
@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { ValueAnnotationValidator.super.checkUsage(element, annotationMirror, errorLogger); if (ProcessingUtility.isRawSubtype(element, Class.class) && !typeKinds.containsAll(ProcessingUtility.getEnums(ProcessingUtility.getAnnotationValue(annotationMirror), ElementKind.class))) { errorLogger.log("In case of classes, the annotation '@TypeKind' may only be used with CLASS, INTERFACE, ENUM and ANNOTATION_TYPE:", SourcePosition.of(element, annotationMirror)); } }
/** * Returns the annotation value for the default value method of the given annotation type on the given element or null if not found. */ @Pure public static @Nullable AnnotationValue getAnnotationValue(@Nonnull Element element, @Nonnull Class<? extends Annotation> annotationType) { return getAnnotationValue(element, annotationType, "value"); }
/** * Returns the annotation mirror corresponding to the given annotation type of the given element or null if not found. */ @Pure public static @Nullable AnnotationMirror getAnnotationMirror(@Nonnull Element element, @Nonnull Class<? extends Annotation> annotationType) { return getAnnotationMirrors(element).findFirst(annotationMirror -> getQualifiedName(annotationMirror).equals(annotationType.getCanonicalName())); }
@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { final @Nonnull TypeElement surroundingType = ProcessingUtility.getSurroundingType(element); if (element.getKind() == ElementKind.METHOD) { final @Nonnull ExecutableElement method = (ExecutableElement) element; final @Nonnull Types typeUtils = StaticProcessingEnvironment.getTypeUtils(); if (!element.getModifiers().contains(Modifier.STATIC) || !typeUtils.isSubtype(method.getReturnType(), typeUtils.erasure(surroundingType.asType()))) { errorLogger.log("The annotation $ may only be used on static methods whose return type is a subtype of the surrounding type.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt()); } } else if (element.getKind() == ElementKind.CONSTRUCTOR) { if (ProcessingUtility.getConstructors(surroundingType).isSingle()) { errorLogger.log("The annotation $ may only be used on constructors if there are also other constructors in the same class.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt()); } } else { errorLogger.log("The annotation $ may only be used on methods and constructors.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt()); } for (@Nonnull ExecutableElement executableElement : ProcessingUtility.getConstructors(surroundingType).combine(ProcessingUtility.getMethods(surroundingType))) { if (ProcessingUtility.hasAnnotation(executableElement, Recover.class) && !executableElement.equals(element)) { errorLogger.log("The annotation $ may only be used on at most one executable in the same type.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt()); } } }
@Pure private @Nonnull String getAssignmentPrefix(@Nonnull TypeMirror fieldType, @Nonnull FiniteIterable<@Nonnull AnnotationMirror> annotationMirrors) { if (CustomType.of(fieldType, annotationMirrors).isCompositeType() && ProcessingUtility.getQualifiedName(fieldType).startsWith("net.digitalid.utility.collections.")) { return ProcessingUtility.getSimpleName(fieldType) + ".withElementsOf("; } return ""; }
@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { if (element.getKind() == ElementKind.PARAMETER) { final @Nonnull Element enclosingElement = element.getEnclosingElement(); if (!(enclosingElement.getKind() == ElementKind.CONSTRUCTOR && ProcessingUtility.getConstructors(ProcessingUtility.getSurroundingType(element)).isSingle() || ProcessingUtility.hasAnnotation(enclosingElement, Recover.class))) { errorLogger.log("The generation annotation may only be used on parameters of a unique constructor or an executable annotated with '@Recover'.", SourcePosition.of(element, annotationMirror)); } } else if (element.getKind() == ElementKind.METHOD) { final @Nonnull ExecutableElement method = (ExecutableElement) element; if (!method.getModifiers().contains(Modifier.ABSTRACT)/* || !ProcessingUtility.isGetter(method)*/) { // TODO: @Default also has to be allowed on generated properties. errorLogger.log("The generation annotation may only be used on abstract getters.", SourcePosition.of(element, annotationMirror)); } } else { errorLogger.log("The generation annotation may only be used on parameters and methods.", SourcePosition.of(element, annotationMirror)); } }
@Pure @Override public void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { super.checkUsage(element, annotationMirror, errorLogger); final @Nullable TypeMirror componentType = ProcessingUtility.getComponentType(element, errorLogger); if (componentType != null && !componentType.getKind().isPrimitive() && !ProcessingUtility.isRawSubtype(componentType, Comparable.class)) { errorLogger.log("The annotation $ may only be used on arrays and iterables whose component type is comparable, which is not the case for $:", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt(), componentType); } }
/** * Returns the annotation value for the given method name of the given annotation type on the given element or null if not found. */ @Pure public static @Nullable AnnotationValue getAnnotationValue(@Nonnull Element element, @Nonnull Class<? extends Annotation> annotationType, @Nonnull String methodName) { final @Nullable AnnotationMirror annotationMirror = getAnnotationMirror(element, annotationType); return annotationMirror != null ? getAnnotationValue(annotationMirror, methodName) : null; }