Search before asking
Motivation
This proposal aims to introduce Composable Annotation Support for Fesod-Sheet's Java Model mode, allowing users to create custom, reusable annotation combinations (presets) that group multiple built-in Fesod annotations into a single meta-annotation. This dramatically reduces boilerplate when repetitive style/format configurations are applied across many model classes.
Solution
Content
Composable annotations solve the above problems by letting users define their own annotations that bundle multiple built-in annotations with preset defaults.
Here, two meta-annotations @FesodMarked and @FesodMarked.AliasFor are introduced to implement the following composite annotation strategy:
1. Preset mode: composable-annotations (marked by @FesodMarked) do not declare any attributes themselves; they simply bundle several meta-annotations with fixed default values together into a preset configuration, and users cannot override its attributes.
2. AliasFor mode: composable-annotations (marked with @FesodMarked) can explicitly declare attribute mapping relationships through @FesodMarked.AliasFor, forwarding their own attribute values to the specified attributes of the target annotation. Users can override its attributes.
Both class-level and field-level composable annotations are supported. When a direct annotation and a composable annotation of the same type coexist at the same level, the direct annotation takes priority.
Supported Annotations
In the fesod-sheet module, all built-in annotations except @ExcelIgnore and @ExcelIgnoreUnannotated can be used within composable annotations.
API Usage Example
- Define a composable annotation with
@FesodMarked.AliasFor
Create a custom annotation that forwards attribute values to @ExcelProperty:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@ExcelProperty
@Inherited
public @interface ComposableExcelProperty {
@FesodMarked.AliasFor(annotation = ExcelProperty.class, attribute = "value")
String[] value() default {""};
@FesodMarked.AliasFor(annotation = ExcelProperty.class, attribute = "index")
int index() default -1;
@FesodMarked.AliasFor(annotation = ExcelProperty.class, attribute = "order")
int order() default Integer.MAX_VALUE;
}
public class ExcelModel {
// Same as @ExcelProperty(value = {"Order ID"}, index = 0)
@ComposableExcelProperty(value = {"Order ID"}, index = 0)
private String orderId;
// Same as @ExcelProperty(value = {"Total Amount"}, index = 1)
@ComposableExcelProperty(value = {"Total Amount"}, index = 1)
private BigDecimal amount;
}
- Define a no-attributes style preset
Group multiple annotations with fixed defaults into a single reusable annotation:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@HeadRowHeight(30)
@ContentRowHeight(20)
@OnceAbsoluteMerge(firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 0, lastColumnIndex = 3)
@Inherited
public @interface CommonTableStyle {}
// Same as:
// @HeadRowHeight(30)
// @ContentRowHeight(20)
// @OnceAbsoluteMerge(firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 0, lastColumnIndex = 3)
@CommonTableStyle
public class ExcelModel {
@ExcelProperty("Date")
@DateTimeFormat("yyyy-MM-dd")
private Date date;
@ExcelProperty("Revenue")
@NumberFormat("#,##0.00")
private BigDecimal revenue;
}
- Combine field-level and class-level composable annotations
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@ContentStyle(wrapped = BooleanEnum.TRUE, fillForegroundColor = 10)
@ContentFontStyle(fontName = "Arial", fontHeightInPoints = 12, bold = BooleanEnum.TRUE)
@Inherited
public @interface ContentPreset {}
// Same as:
// @HeadRowHeight(30)
// @ContentRowHeight(20)
// @OnceAbsoluteMerge(firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 0, lastColumnIndex = 3)
@CommonTableStyle
public class ExcelModel {
// Same as @ExcelProperty(value = {"Product Name"})
@ComposableExcelProperty({"Product Name"})
private String product;
// Same as:
// @ContentStyle(wrapped = BooleanEnum.TRUE, fillForegroundColor = 10)
// @ContentFontStyle(fontName = "Arial", fontHeightInPoints = 12, bold = BooleanEnum.TRUE)
@ContentPreset
@NumberFormat("#,##0.00")
private BigDecimal sales;
}
- Enable composable annotation processing
Composable annotations must be explicitly enabled via enableMetaMarked(true):
FesodSheet.write(pathname, ExcelModel.class)
// default false
.enableMetaMarked(true)
.sheet(0)
.doWrite(dataList);
- Direct annotations override composable annotations
When both a direct annotation and a composable annotation of the same type exist at the same level, the direct annotation wins:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@ExcelProperty(value = {"Full Name"})
@Inherited
public @interface FullNamePreset {}
public class ExcelModel {
@ExcelProperty("First Name") // takes priority
@FullNamePreset
private String firstName;
}
@FesodMarked.AliasFor targets must be meta-present
Every @FesodMarked.AliasFor must reference an annotation that is meta-present on the composable.
// INVALID: @ColumnWidth is NOT meta-present on this annotation
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@Inherited
public @interface BadComposable {
// will throw
@FesodMarked.AliasFor(annotation = ColumnWidth.class, attribute = "value")
int width() default -1;
}
- Custom annotation attribute values must be explicitly marked with
@FesodMarked.AliasFor if they need to be forwarded. (For now)
// INVALID: value() value is not forward into @ExcelProperty because there is no mark @FesodMarked.AliasFor.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@FesodMarked
@ExcelProperty
@Inherited
public @interface BadComposable {
// not work
String[] value() default {"Name"};
}
More
Priority:
- Direct annotations on a field > composable annotations on the same field
- Annotations on the model class > annotations inherited from parent annotations
Compatibility:
The feature is opt-in (enableMetaMarked defaults to false). Existing code using only direct Fesod annotations is completely unaffected.
Alternatives
No response
Anything else?
No response
Are you willing to submit a PR?
Search before asking
Motivation
This proposal aims to introduce Composable Annotation Support for Fesod-Sheet's Java Model mode, allowing users to create custom, reusable annotation combinations (presets) that group multiple built-in Fesod annotations into a single meta-annotation. This dramatically reduces boilerplate when repetitive style/format configurations are applied across many model classes.
Solution
Content
Composable annotations solve the above problems by letting users define their own annotations that bundle multiple built-in annotations with preset defaults.
Here, two meta-annotations
@FesodMarkedand@FesodMarked.AliasForare introduced to implement the following composite annotation strategy:1. Preset mode: composable-annotations (marked by
@FesodMarked) do not declare any attributes themselves; they simply bundle several meta-annotations with fixed default values together into a preset configuration, and users cannot override its attributes.2. AliasFor mode: composable-annotations (marked with
@FesodMarked) can explicitly declare attribute mapping relationships through@FesodMarked.AliasFor, forwarding their own attribute values to the specified attributes of the target annotation. Users can override its attributes.Supported Annotations
In the
fesod-sheetmodule, all built-in annotations except@ExcelIgnoreand@ExcelIgnoreUnannotatedcan be used within composable annotations.API Usage Example
@FesodMarked.AliasForCreate a custom annotation that forwards attribute values to
@ExcelProperty:Group multiple annotations with fixed defaults into a single reusable annotation:
Composable annotations must be explicitly enabled via
enableMetaMarked(true):When both a direct annotation and a composable annotation of the same type exist at the same level, the direct annotation wins:
@FesodMarked.AliasFortargets must be meta-presentEvery
@FesodMarked.AliasFormust reference an annotation that is meta-present on the composable.@FesodMarked.AliasForif they need to be forwarded. (For now)More
Priority:
Compatibility:
The feature is opt-in (enableMetaMarked defaults to false). Existing code using only direct Fesod annotations is completely unaffected.
Alternatives
No response
Anything else?
No response
Are you willing to submit a PR?