/** * Returns whether the type of the given element is a raw subtype of the given type. */ @Pure public static boolean isRawSubtype(@Nonnull Element element, @Nonnull Class<?> type) { return isRawSubtype(getType(element), type); }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, BigIntegerNumerical.class)) { return Contract.with("# == null || #.getValue().getLowestSetBit() != 0", "The # has to be null or even but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, BigInteger.class)) { return Contract.with("# == null || #.getLowestSetBit() != 0", "The # has to be null or even but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, LongNumerical.class)) { return Contract.with("# == null || #.getValue() % 2 == 0", "The # has to be null or even but was $.", element); } else { return Contract.with("# % 2 == 0", "The # has to be even but was $.", element); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, BigIntegerNumerical.class)) { return Contract.with("# == null || #.getValue().getLowestSetBit() == 0", "The # has to be null or uneven but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, BigInteger.class)) { return Contract.with("# == null || #.getLowestSetBit() == 0", "The # has to be null or uneven but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, LongNumerical.class)) { return Contract.with("# == null || #.getValue() % 2 == 1", "The # has to be null or uneven but was $.", element); } else { return Contract.with("# % 2 == 1", "The # has to be uneven but was $.", element); } }
@Pure // TODO: Why is this method not in the interface? public boolean isIterable() { return ProcessingUtility.isRawSubtype(getType(), Iterable.class); }
@Pure @Override public @Nonnull String visitDeclared(@Nonnull DeclaredType type, @Nullable Pair<@Nonnull String, @Nonnull JavaFileGenerator> pair) { if (ProcessingUtility.isRawSubtype(type, CharSequence.class)) { return pair.get1().importIfPossible(Quotes.class) + ".inDouble(" + pair.get0() + ")"; } else { return pair.get0(); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, BigIntegerNumerical.class)) { return Contract.with("# == null || #.getValue().mod(" + typeImporter.importIfPossible(BigInteger.class) + ".valueOf(@)).equals(" + typeImporter.importIfPossible(BigInteger.class) + ".ZERO)", "The # has to be null or a multiple of @ but was $.", element, annotationMirror); } else if (ProcessingUtility.isRawSubtype(element, BigInteger.class)) { return Contract.with("# == null || #.mod(" + typeImporter.importIfPossible(BigInteger.class) + ".valueOf(@)).equals(" + typeImporter.importIfPossible(BigInteger.class) + ".ZERO)", "The # has to be null or a multiple of @ but was $.", element, annotationMirror); } else if (ProcessingUtility.isRawSubtype(element, LongNumerical.class)) { return Contract.with("# == null || #.getValue() % @ == 0", "The # has to be a multiple of @ but was $.", element, annotationMirror); } else { return Contract.with("# % @ == 0", "The # has to be a multiple of @ but was $.", element, annotationMirror); } }
@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()); } }
/** * Returns the non-private method with the given name, return type, parameter types and no thrown types in the given type element or null if no such method is found. */ @Pure public static @Nullable ExecutableElement getNonPrivateMethod(@Nonnull TypeElement typeElement, @Nonnull String methodName, @Nonnull Class<?> returnType, @Nonnull @NonNullableElements Class<?>... parameterTypes) { final @Nonnull DeclaredType surroundingType = (DeclaredType) typeElement.asType(); for (@Nonnull ExecutableElement inheritedMethod : getAllMethods(typeElement)) { if (!inheritedMethod.getModifiers().contains(Modifier.PRIVATE) && inheritedMethod.getSimpleName().contentEquals(methodName) && inheritedMethod.getThrownTypes().isEmpty()) { final @Nonnull ExecutableType methodType = (ExecutableType) StaticProcessingEnvironment.getTypeUtils().asMemberOf(surroundingType, inheritedMethod); if (isRawSubtype(methodType.getReturnType(), returnType)) { if (methodType.getParameterTypes().size() == parameterTypes.length) { boolean isAssignable = true; for (int i = 0; i < parameterTypes.length; i++) { final @Nonnull TypeMirror parameterType = methodType.getParameterTypes().get(i); if (parameterType.getKind() == TypeKind.TYPEVAR || !correspond(parameterType, parameterTypes[i])) { isAssignable = false; } } if (isAssignable) { return inheritedMethod; } } } } } return null; }
/** * Returns the condition for the given element depending on the type of the element. */ @Pure public static @Nonnull String getCondition(@Nonnull Element element, @Nonnull ElementKind kind, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, Class.class)) { switch (kind) { case CLASS: return "!#.isInterface() && !#.isEnum()"; case INTERFACE: return "#.isInterface() && !#.isAnnotation()"; case ENUM: return "#.isEnum()"; case ANNOTATION_TYPE: return "#.isAnnotation()"; default: return "false"; } } else { return "#.getKind() == " + typeImporter.importIfPossible(ElementKind.class) + "." + kind.name(); } }
/** * Returns the condition for the given element depending on the type of the element. */ @Pure public static @Nonnull String getCondition(@Nonnull Element element, @Nonnull NestingKind kind, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, Class.class)) { switch (kind) { case ANONYMOUS: return "#.isAnonymousClass()"; case LOCAL: return "#.isLocalClass()"; case MEMBER: return "#.isMemberClass()"; case TOP_LEVEL: return "!#.isAnonymousClass() && !#.isLocalClass() && !#.isMemberClass()"; default: return "false"; } } else { return "#.getNestingKind() == " + typeImporter.importIfPossible(NestingKind.class) + "." + kind.name(); } }
@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 a subtype of $:", SourcePosition.of(element), ProcessingUtility.getSimpleName(declaredType), desiredType.getSimpleName()); } }
@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); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, BigIntegerNumerical.class)) { return Contract.with("# == null || #.getValue().compareTo(" + typeImporter.importIfPossible(BigInteger.class) + ".ZERO) " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName().replace(" ", "-") + " but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, BigInteger.class)) { return Contract.with("# == null || #.compareTo(" + typeImporter.importIfPossible(BigInteger.class) + ".ZERO) " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName().replace(" ", "-") + " but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, LongNumerical.class)) { return Contract.with("# == null || #.getValue() " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName().replace(" ", "-") + " but was $.", element); } else if (ProcessingUtility.isRawSubtype(element, Number.class)) { return Contract.with("# == null || # " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName().replace(" ", "-") + " but was $.", element); } else { return Contract.with("# " + getComparisonOperator() + " 0", "The # has to be " + getDecamelizedAnnotationName().replace(" ", "-") + " but was $.", element); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, CharSequence.class)) { return Contract.with("# == null || #." + getMethodName() + "(\"@\")", "The # has to " + getMessageCondition() + " '@' but was $.", element, annotationMirror); } else { return Contract.with("# == null || #.getAbsoluteFile().getName()." + getMethodName() + "(\"@\")", "The # has to " + getMessageCondition() + " '@' but was $.", element, annotationMirror); } }
@Pure @Override public default void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { if (element.getKind() != ElementKind.METHOD && element.getKind() != ElementKind.CONSTRUCTOR) { errorLogger.log("The method annotation $ may only be used on methods and constructors.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt()); } else if (!ProcessingUtility.isRawSubtype(ProcessingUtility.getSurroundingType(element), getReceiverType())) { errorLogger.log("The method annotation $ can only be used in $.", SourcePosition.of(element, annotationMirror), getAnnotationNameWithLeadingAt(), getReceiverType().getCanonicalName()); } }
@Pure @Override public default void checkUsage(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull ErrorLogger errorLogger) { if (getTargetTypes().matchNone(targetType -> ProcessingUtility.isRawSubtype(element, targetType))) { errorLogger.log("The element $ does not belong to a subtype of a target type of $.", SourcePosition.of(element, annotationMirror), element, getAnnotationNameWithLeadingAt()); } }
@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)); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, CharSequence.class)) { return generateContract("# == null || #.length() " + getSizeComparison(), "The length of the # has to be " + getMessageCondition() + " but was $.", element, annotationMirror, ".length()"); } else if (ProcessingUtility.getType(element).getKind() == TypeKind.ARRAY) { return generateContract("# == null || #.length " + getSizeComparison(), "The length of the # has to be " + getMessageCondition() + " but was $.", element, annotationMirror, ".length"); } else { return generateContract("# == null || #.size() " + getSizeComparison(), "The size of the # has to be " + getMessageCondition() + " but was $.", element, annotationMirror, ".size()"); } }
@Pure @Override public @Nonnull Contract generateContract(@Nonnull Element element, @Nonnull AnnotationMirror annotationMirror, @NonCaptured @Modified @Nonnull TypeImporter typeImporter) { if (ProcessingUtility.isRawSubtype(element, BigIntegerNumerical.class)) { return Contract.with("# == null || #.getValue().compareTo(" + typeImporter.importIfPossible(BigInteger.class) + ".valueOf(@)) " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName() + " @ but was $.", element, annotationMirror); } else if (ProcessingUtility.isRawSubtype(element, BigInteger.class)) { return Contract.with("# == null || #.compareTo(" + typeImporter.importIfPossible(BigInteger.class) + ".valueOf(@)) " + getComparisonOperator() + " 0", "The # has to be null or " + getDecamelizedAnnotationName() + " @ but was $.", element, annotationMirror); } else if (ProcessingUtility.isRawSubtype(element, LongNumerical.class)) { return Contract.with("# == null || #.getValue() " + getComparisonOperator() + " @", "The # has to be null or " + getDecamelizedAnnotationName() + " @ but was $.", element, annotationMirror); } else if (ProcessingUtility.isRawSubtype(element, Number.class)) { return Contract.with("# == null || # " + getComparisonOperator() + " @", "The # has to be null or " + getDecamelizedAnnotationName() + " @ but was $.", element, annotationMirror); } else { return Contract.with("# " + getComparisonOperator() + " @", "The # has to be " + getDecamelizedAnnotationName() + " @ but was $.", element, annotationMirror); } }
/** * Generates a compare-to method. This method should be implemented by the super class if the * comparison algorithm is more complex and if the fields of the type do not implement Comparable. */ protected void generateCompareToMethod() { if (ProcessingUtility.isRawSubtype(typeInformation.getElement(), Comparable.class)) { if (typeInformation instanceof ClassInformation && ((ClassInformation) typeInformation).compareToMethod != null) { return; } // only generated if the compareTo method is defined somewhere, but not yet implemented addAnnotation(Pure.class); addAnnotation(Override.class); beginMethod("public int compareTo(@" + importIfPossible(Nonnull.class) + " " + typeInformation.getName() + " other)"); final @Nonnull FiniteIterable<FieldInformation> representingFieldInformation = typeInformation.getRepresentingFieldInformation(); addStatement("int result = 0"); for (int i = 0; i < representingFieldInformation.size(); i++) { final @Nonnull FieldInformation field = representingFieldInformation.get(i); long magnitude = (10 * (representingFieldInformation.size() - i)) / 10; if (ProcessingUtility.isPrimitive(field.getType())) { addStatement("result += " + Brackets.inRound(field.getAccessCode() + " - other." + field.getAccessCode()) + " * " + magnitude); } else { addStatement("result += " + field.getAccessCode() + ".compareTo(other." + field.getAccessCode() + ") * " + magnitude); } } addStatement("return result"); endMethod(); } }