bengbengbalabalabeng opened a new issue, #918:
URL: https://github.com/apache/fesod/issues/918

   ### Search before asking
   
   - [x] I searched in the [issues](https://github.com/apache/fesod/issues) and 
found nothing similar.
   
   
   ### 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
   
   1. Define a composable annotation with `@FesodMarked.AliasFor`
   
   Create a custom annotation that forwards attribute values to 
`@ExcelProperty`:
   
   ```java
   @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;
   }
   ```
   
   ```java
   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;
   }
   ```
   
   2. Define a no-attributes style preset
   
   Group multiple annotations with fixed defaults into a single reusable 
annotation:
   
   ```java
   @Target({ElementType.TYPE})
   @Retention(RetentionPolicy.RUNTIME)
   @FesodMarked
   @HeadRowHeight(30)
   @ContentRowHeight(20)
   @OnceAbsoluteMerge(firstRowIndex = 0, lastRowIndex = 0, firstColumnIndex = 
0, lastColumnIndex = 3)
   @Inherited
   public @interface CommonTableStyle {}
   ```
   
   ```java
   // 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;
   }
   ```
   
   3. Combine field-level and class-level composable annotations
   
   ```java
   @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 {}
   ```
   
   ```java
   // 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;
   }
   ```
   
   4. Enable composable annotation processing
   
   Composable annotations must be explicitly enabled via 
`enableMetaMarked(true)`:
   
   ```java
   FesodSheet.write(pathname, ExcelModel.class)
       // default false
       .enableMetaMarked(true)
       .sheet(0)
       .doWrite(dataList);
   ```
   
   5. 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:
   
   ```java
   @Target({ElementType.FIELD})
   @Retention(RetentionPolicy.RUNTIME)
   @FesodMarked
   @ExcelProperty(value = {"Full Name"})
   @Inherited
   public @interface FullNamePreset {}
   ```
   
   ```java
   public class ExcelModel {
   
     @ExcelProperty("First Name")   // takes priority
     @FullNamePreset
     private String firstName;
   }
   ```
   
   6. `@FesodMarked.AliasFor` targets must be meta-present
   
   Every `@FesodMarked.AliasFor` must reference an annotation that is 
meta-present on the composable.
   
   ```java
   // 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;
   }
   ```
   
   7. Custom annotation attribute values must be explicitly marked with 
`@FesodMarked.AliasFor` if they need to be forwarded. (For now)
   
   ```java
   // 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?
   
   - [x] I'm willing to submit a PR!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to