This is an automated email from the ASF dual-hosted git repository.
dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git
The following commit(s) were added to refs/heads/master by this push:
new 006029809 [INLONG-7820][Manager] Support style and font when exporting
Excel file (#7822)
006029809 is described below
commit 0060298093dcd775bb0c64f1ceb252fa991274d7
Author: feat <[email protected]>
AuthorDate: Wed Apr 12 21:28:02 2023 +0800
[INLONG-7820][Manager] Support style and font when exporting Excel file
(#7822)
---
.../manager/common/tool/excel/ExcelTool.java | 195 ++++++++++++------
.../common/tool/excel/annotation/ExcelField.java | 5 +-
.../annotation/{ExcelField.java => Font.java} | 35 ++--
.../annotation/{ExcelField.java => Style.java} | 50 ++---
.../common/tool/excel/tuple/ImmutableQuartet.java | 155 +++++++++++++++
.../manager/common/tool/excel/tuple/Quartet.java | 217 +++++++++++++++++++++
.../inlong/manager/pojo/stream/StreamField.java | 16 +-
7 files changed, 561 insertions(+), 112 deletions(-)
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/ExcelTool.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/ExcelTool.java
index fd0df0297..e8173488f 100644
---
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/ExcelTool.java
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/ExcelTool.java
@@ -24,15 +24,22 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.inlong.manager.common.tool.excel.annotation.ExcelEntity;
import org.apache.inlong.manager.common.tool.excel.annotation.ExcelField;
+import org.apache.inlong.manager.common.tool.excel.annotation.Font;
+import org.apache.inlong.manager.common.tool.excel.annotation.Style;
import
org.apache.inlong.manager.common.tool.excel.validator.ExcelCellValidator;
import org.apache.inlong.manager.common.util.Preconditions;
+import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFDataValidationConstraint;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
+import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
@@ -49,13 +56,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
import static org.apache.inlong.manager.common.util.Preconditions.expectTrue;
@@ -65,32 +70,20 @@ import static
org.apache.inlong.manager.common.util.Preconditions.expectTrue;
public class ExcelTool {
private static final int CONSTRAINT_MAX_LENGTH = 255;
+
private ExcelTool() {
}
private static final Logger LOGGER =
LoggerFactory.getLogger(ExcelTool.class);
- private static final int DEFAULT_COLUMN_WIDTH = 10000;
+ private static final String DEFAULT_SHEET_NAME = "Sheet 1";
+ private static final int DEFAULT_ROW_COUNT = 30;
/**
* Extracts the header row from a given class and returns it as a list of
strings.
*/
- public static <E> List<String> extractHeader(Class<E> e1Class) {
- List<String> list = new LinkedList<>();
- Field[] fields = e1Class.getDeclaredFields();
- if (fields.length > 0) {
- for (Field field : fields) {
- field.setAccessible(true);
- ExcelField excel = field.getAnnotation(ExcelField.class);
- if (excel != null) {
- String excelName = excel.name();
- list.add(excelName);
- }
- }
- return !list.isEmpty() ? list : Collections.emptyList();
- } else {
- return Collections.emptyList();
- }
+ public static List<String> extractHeader(List<Pair<Field, ExcelField>>
fieldMetas) {
+ return fieldMetas.stream().map(fieldMeta ->
fieldMeta.getRight().name()).collect(Collectors.toList());
}
/**
@@ -109,32 +102,51 @@ public class ExcelTool {
public static <T> void doWrite(List<Map<String, String>> maps, Class<T>
clazz, OutputStream out)
throws IOException {
- List<String> heads = extractHeader(clazz);
- if (heads.isEmpty()) {
+ Field[] fields = clazz.getDeclaredFields();
+ List<Pair<Field, ExcelField>> fieldMetas = extractFieldMetas(fields);
+ if (fieldMetas.isEmpty()) {
throw new IllegalArgumentException(
"At least one field must be marked as Excel Field by
annotation @ExcelField in class " + clazz);
}
+ List<String> headNames = extractHeader(fieldMetas);
try (XSSFWorkbook hwb = new XSSFWorkbook()) {
- XSSFSheet sheet = hwb.createSheet("Sheet 1");
-
- for (int index = 0; index < heads.size(); index++) {
- sheet.setColumnWidth(index, DEFAULT_COLUMN_WIDTH);
+ // Set sheet name
+ ExcelEntity excelEntity = clazz.getAnnotation(ExcelEntity.class);
+ String sheetName = excelEntity.name();
+ if (StringUtils.isBlank(sheetName)) {
+ sheetName = DEFAULT_SHEET_NAME;
}
- fillSheetHeader(sheet.createRow(0), heads);
+ XSSFSheet sheet = hwb.createSheet(sheetName);
+ // Set width of column
+ for (int index = 0; index < fieldMetas.size(); index++) {
+ Pair<Field, ExcelField> fieldMeta = fieldMetas.get(index);
+ sheet.setColumnWidth(index,
fieldMeta.getRight().style().width());
+ }
+ // Fill header with cellStyle
+ fillSheetHeader(sheet.createRow(0), headNames);
// Fill validation
- Field[] fields = clazz.getDeclaredFields();
- fillSheetValidation(clazz, sheet, fields);
+ fillSheetValidation(sheet, fieldMetas, clazz.getCanonicalName());
// Fill content if data exist.
+ List<XSSFCellStyle> contentStyles = createContentCellStyle(hwb,
fieldMetas);
if (CollectionUtils.isNotEmpty(maps)) {
- fillSheetContent(sheet, heads, maps);
+ fillSheetContent(sheet, headNames, maps, contentStyles);
+ } else {
+ fillEmptySheetContent(sheet, headNames.size(), contentStyles);
}
-
hwb.write(out);
}
out.close();
LOGGER.info("Database export succeeded");
}
+ private static List<Pair<Field, ExcelField>> extractFieldMetas(Field[]
fields) {
+ return Arrays.stream(fields)
+ .peek(field -> field.setAccessible(true))
+ .map(field -> Pair.of(field,
field.getAnnotation(ExcelField.class)))
+ .filter(fieldMeta -> fieldMeta.getRight() != null)
+ .collect(Collectors.toList());
+ }
+
/**
* Fills the output stream with the provided class meta.
*/
@@ -143,52 +155,109 @@ public class ExcelTool {
doWrite(null, clazz, out);
}
+ private static List<XSSFCellStyle> createContentCellStyle(XSSFWorkbook
workbook,
+ List<Pair<Field, ExcelField>> fieldMetas) {
+ return fieldMetas.stream().map(fieldMeta -> {
+ XSSFCellStyle style = workbook.createCellStyle();
+ ExcelField excelField = fieldMeta.getRight();
+ Style excelStyle = excelField.style();
+ Font excelFont = excelField.font();
+ // Set foreground color
+ style.setFillForegroundColor(excelStyle.bgColor().getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ // Set font
+ XSSFFont font = workbook.createFont();
+ font.setFontName(excelFont.name());
+ font.setColor(excelFont.color().getIndex());
+ font.setFontHeightInPoints(excelFont.size());
+ font.setBold(excelFont.bold());
+ font.setItalic(excelFont.italic());
+ style.setFont(font);
+ // Set border
+ BorderStyle borderBottom = excelStyle.bottomBorderStyle();
+ BorderStyle borderTop = excelStyle.topBorderStyle();
+ BorderStyle borderLeft = excelStyle.leftBorderStyle();
+ BorderStyle borderRight = excelStyle.rightBorderStyle();
+ BorderStyle borderAll = excelStyle.allBorderStyle();
+ if (borderAll != BorderStyle.NONE) {
+ borderBottom = borderTop = borderLeft = borderRight =
borderAll;
+ }
+ style.setBorderBottom(borderBottom);
+ style.setBorderTop(borderTop);
+ style.setBorderLeft(borderLeft);
+ style.setBorderRight(borderRight);
+ // Set border color
+ IndexedColors bottomBorderColor = excelStyle.bottomBorderColor();
+ IndexedColors topBorderColor = excelStyle.topBorderColor();
+ IndexedColors leftBorderColor = excelStyle.leftBorderColor();
+ IndexedColors rightBorderColor = excelStyle.rightBorderColor();
+ IndexedColors allBorderColor = excelStyle.allBorderColor();
+ if (allBorderColor != IndexedColors.BLACK) {
+ bottomBorderColor = topBorderColor = leftBorderColor =
rightBorderColor = allBorderColor;
+ }
+ style.setBottomBorderColor(bottomBorderColor.getIndex());
+ style.setTopBorderColor(topBorderColor.getIndex());
+ style.setLeftBorderColor(leftBorderColor.getIndex());
+ style.setRightBorderColor(rightBorderColor.getIndex());
+ return style;
+ }).collect(Collectors.toList());
+
+ }
+
+ private static void fillEmptySheetContent(XSSFSheet sheet, int colCount,
List<XSSFCellStyle> contentCellStyles) {
+ for (int index = 1; index < DEFAULT_ROW_COUNT; index++) {
+ XSSFRow row = sheet.createRow(index);
+ for (int colIndex = 0; colIndex < colCount; colIndex++) {
+ XSSFCell cell = row.createCell(colIndex);
+ cell.setCellStyle(contentCellStyles.get(colIndex));
+ }
+ }
+ }
+
/**
* Fills the content rows of a given sheet with the provided content maps
and headers.
*/
- private static void fillSheetContent(XSSFSheet sheet, List<String> heads,
List<Map<String, String>> contents) {
+ private static void fillSheetContent(XSSFSheet sheet, List<String> heads,
List<Map<String, String>> contents,
+ List<XSSFCellStyle> contentStyles) {
Optional.ofNullable(contents)
- .ifPresent(c -> IntStream.range(0, c.size())
- .forEach(lineId -> {
- Map<String, String> line = contents.get(lineId);
- Row row = sheet.createRow(lineId + 1);
- IntStream.range(0, heads.size()).forEach(colId -> {
- String title = heads.get(colId);
- String originValue = line.get(title);
- String value =
StringUtils.isNotBlank(originValue) ? originValue : "";
- Cell cell = row.createCell(colId);
- cell.setCellValue(value);
- });
- }));
+ .ifPresent(content -> {
+ int rowSize = content.size();
+ for (int lineId = 0; lineId < rowSize; lineId++) {
+ Map<String, String> line = contents.get(lineId);
+ Row row = sheet.createRow(lineId + 1);
+ int headSize = heads.size();
+ for (int colId = 0; colId < headSize; colId++) {
+ String title = heads.get(colId);
+ String originValue = line.get(title);
+ String value = StringUtils.isNotBlank(originValue)
? originValue : "";
+ Cell cell = row.createCell(colId);
+ cell.setCellValue(value);
+ cell.setCellStyle(contentStyles.get(colId));
+ }
+ }
+ });
}
private static void fillSheetHeader(XSSFRow row, List<String> heads) {
- IntStream.range(0, heads.size()).forEach(index -> {
+ int headSize = heads.size();
+ for (int index = 0; index < headSize; index++) {
XSSFCell cell = row.createCell(index);
cell.setCellValue(new XSSFRichTextString(heads.get(index)));
- });
+ }
}
+
/**
- * Fills the validation constraints for a given sheet based on the
provided class and fields.
+ * Fills the validation constraints for a given sheet based on the
provided class and fieldMetas.
*
- * @param clazz the class to use for validation constraints
- * @param sheet the sheet to fill with validation constraints
- * @param fields the fields to use for validation constraints
+ * @param sheet the sheet to fill with validation constraints
+ * @param fieldMetas the fieldMetas to use for validation constraints
+ * @param className the class to use for validation constraints
*/
- private static <T> void fillSheetValidation(Class<T> clazz, XSSFSheet
sheet, Field[] fields) {
- List<Pair<String, ExcelField>> excelFields = new ArrayList<>();
-
- for (Field field : fields) {
- field.setAccessible(true);
- ExcelField excelField = field.getAnnotation(ExcelField.class);
- if (excelField != null) {
- excelFields.add(Pair.of(field.getName(), excelField));
- }
- }
-
- int bound = excelFields.size();
+ private static void fillSheetValidation(XSSFSheet sheet, List<Pair<Field,
ExcelField>> fieldMetas,
+ String className) {
+ int bound = fieldMetas.size();
for (int index = 0; index < bound; index++) {
- Pair<String, ExcelField> excelFieldPair = excelFields.get(index);
+ Pair<Field, ExcelField> excelFieldPair = fieldMetas.get(index);
Class<? extends ExcelCellValidator> validator =
excelFieldPair.getRight().validator();
Optional<List<String>> optionalList =
Optional.ofNullable(validator)
@@ -208,7 +277,7 @@ public class ExcelTool {
}
if (String.join("\n", valueOfCol).length() >
CONSTRAINT_MAX_LENGTH) {
throw new IllegalArgumentException(
- "field '" + excelFieldPair.getLeft() + "' in class '"
+ clazz.getCanonicalName()
+ "field '" + excelFieldPair.getLeft().getName() + "' in
class '" + className
+ "' valid message length must be less than
255 characters");
}
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
index fa836d5c3..4a7f1375a 100644
---
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
@@ -41,9 +41,12 @@ public @interface ExcelField {
* Data transfer method from Excel to Object
*/
ExcelCellDataTransfer x2oTransfer() default ExcelCellDataTransfer.NONE;
-
/**
* Validator for the field
*/
Class<? extends ExcelCellValidator> validator() default
ExcelCellValidator.class;
+
+ Font font() default @Font;
+
+ Style style() default @Style;
}
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Font.java
similarity index 64%
copy from
inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
copy to
inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Font.java
index fa836d5c3..f97d1a951 100644
---
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Font.java
@@ -17,33 +17,24 @@
package org.apache.inlong.manager.common.tool.excel.annotation;
-import org.apache.inlong.manager.common.tool.excel.ExcelCellDataTransfer;
-import
org.apache.inlong.manager.common.tool.excel.validator.ExcelCellValidator;
+import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Annotation for Excel field
- */
-@Target({ElementType.FIELD})
+@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
-public @interface ExcelField {
-
- /**
- * Name of the field in Excel
- */
- String name();
-
- /**
- * Data transfer method from Excel to Object
- */
- ExcelCellDataTransfer x2oTransfer() default ExcelCellDataTransfer.NONE;
-
- /**
- * Validator for the field
- */
- Class<? extends ExcelCellValidator> validator() default
ExcelCellValidator.class;
+public @interface Font {
+
+ short size() default 12;
+
+ IndexedColors color() default IndexedColors.BLACK;
+
+ String name() default "Arial";
+
+ boolean bold() default false;
+
+ boolean italic() default false;
}
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Style.java
similarity index 54%
copy from
inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
copy to
inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Style.java
index fa836d5c3..90ad50e8c 100644
---
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/ExcelField.java
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/annotation/Style.java
@@ -17,33 +17,39 @@
package org.apache.inlong.manager.common.tool.excel.annotation;
-import org.apache.inlong.manager.common.tool.excel.ExcelCellDataTransfer;
-import
org.apache.inlong.manager.common.tool.excel.validator.ExcelCellValidator;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.IndexedColors;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Annotation for Excel field
- */
-@Target({ElementType.FIELD})
+@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
-public @interface ExcelField {
-
- /**
- * Name of the field in Excel
- */
- String name();
-
- /**
- * Data transfer method from Excel to Object
- */
- ExcelCellDataTransfer x2oTransfer() default ExcelCellDataTransfer.NONE;
-
- /**
- * Validator for the field
- */
- Class<? extends ExcelCellValidator> validator() default
ExcelCellValidator.class;
+public @interface Style {
+
+ IndexedColors bgColor() default IndexedColors.BLACK;
+
+ IndexedColors allBorderColor() default IndexedColors.BLACK;
+
+ BorderStyle allBorderStyle() default BorderStyle.NONE;
+
+ IndexedColors bottomBorderColor() default IndexedColors.BLACK;
+
+ IndexedColors topBorderColor() default IndexedColors.BLACK;
+
+ IndexedColors leftBorderColor() default IndexedColors.BLACK;
+
+ IndexedColors rightBorderColor() default IndexedColors.BLACK;
+
+ BorderStyle bottomBorderStyle() default BorderStyle.NONE;
+
+ BorderStyle topBorderStyle() default BorderStyle.NONE;
+
+ BorderStyle leftBorderStyle() default BorderStyle.NONE;
+
+ BorderStyle rightBorderStyle() default BorderStyle.NONE;
+
+ int width() default 5000;
}
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/ImmutableQuartet.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/ImmutableQuartet.java
new file mode 100644
index 000000000..9259311ab
--- /dev/null
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/ImmutableQuartet.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.common.tool.excel.tuple;
+
+/**
+ * <p>An immutable quartet consisting of four {@code Object} elements.</p>
+ *
+ * <p>Although the implementation is immutable, there is no restriction on the
objects
+ * that may be stored. If mutable objects are stored in the quartet, then the
quartet
+ * itself effectively becomes mutable. The class is also {@code final}, so a
subclass
+ * can not add undesirable behavior.</p>
+ *
+ * <p>#ThreadSafe# if all four objects are thread-safe</p>
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param <S> the f4 element type
+ */
+public final class ImmutableQuartet<L, M, R, S> extends Quartet<L, M, R, S> {
+
+ /**
+ * An empty array.
+ * <p>
+ * Consider using {@link #emptyArray()} to avoid generics warnings.
+ * </p>
+ */
+ public static final ImmutableQuartet<?, ?, ?, ?>[] EMPTY_ARRAY = new
ImmutableQuartet[0];
+
+ /**
+ * An immutable quartet of nulls.
+ */
+ private static final ImmutableQuartet NULL = of(null, null, null, null);
+
+ /**
+ * Serialization version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Returns the empty array singleton that can be assigned without compiler
warning.
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param <S> the f4 element type
+ * @return the empty array singleton that can be assigned without compiler
warning.
+ */
+ @SuppressWarnings("unchecked")
+ public static <L, M, R, S> ImmutableQuartet<L, M, R, S>[] emptyArray() {
+ return (ImmutableQuartet<L, M, R, S>[]) EMPTY_ARRAY;
+ }
+
+ /**
+ * Returns an immutable quartet of nulls.
+ *
+ * @param <L> the f1 element of this quartet. Value is {@code null}.
+ * @param <M> the f2 element of this quartet. Value is {@code null}.
+ * @param <R> the f3 element of this quartet. Value is {@code null}.
+ * @param <S> the f4 element of this quartet. Value is {@code null}.
+ * @return an immutable quartet of nulls.
+ */
+ public static <L, M, R, S> ImmutableQuartet<L, M, R, S> nullTriple() {
+ return NULL;
+ }
+
+ /**
+ * <p>Obtains an immutable quartet of four objects inferring the generic
types.</p>
+ *
+ * <p>This factory allows the quartet to be created using inference to
+ * obtain the generic types.</p>
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param <S> the f4 element type
+ * @param f1 the f1 element, may be null
+ * @param f2 the f2 element, may be null
+ * @param f3 the f3 element, may be null
+ * @param f4 the f4 element, may be null
+ * @return a quartet formed from the four parameters, not null
+ */
+ public static <L, M, R, S> ImmutableQuartet<L, M, R, S> of(final L f1,
final M f2, final R f3, final S f4) {
+ return new ImmutableQuartet<>(f1, f2, f3, f4);
+ }
+
+ /**
+ * F1 object
+ */
+ public final L f1;
+ /**
+ * F2 object
+ */
+ public final M f2;
+
+ /**
+ * F3 object
+ */
+ public final R f3;
+
+ /**
+ * F4 object
+ */
+ public final S f4;
+
+ /**
+ * Create a new quartet instance.
+ *
+ * @param f1 the f1 value, may be null
+ * @param f2 the f2 value, may be null
+ * @param f3 the f3 value, may be null
+ */
+ public ImmutableQuartet(final L f1, final M f2, final R f3, final S f4) {
+ super();
+ this.f1 = f1;
+ this.f2 = f2;
+ this.f3 = f3;
+ this.f4 = f4;
+ }
+
+ @Override
+ public L getF1() {
+ return f1;
+ }
+
+ @Override
+ public M getF2() {
+ return f2;
+ }
+
+ @Override
+ public R getF3() {
+ return f3;
+ }
+
+ @Override
+ public S getF4() {
+ return f4;
+ }
+}
diff --git
a/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/Quartet.java
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/Quartet.java
new file mode 100644
index 000000000..34a65a170
--- /dev/null
+++
b/inlong-manager/manager-common/src/main/java/org/apache/inlong/manager/common/tool/excel/tuple/Quartet.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.inlong.manager.common.tool.excel.tuple;
+
+import org.apache.commons.lang3.builder.CompareToBuilder;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * <p>A triple consisting of four elements.</p>
+ *
+ * <p>This class is an abstract implementation defining the basic API.
+ * It refers to the elements as 'f1', 'f2' , 'f3' and 'f4'.</p>
+ *
+ * <p>Subclass implementations may be mutable or immutable.
+ * However, there is no restriction on the type of the stored objects that may
be stored.
+ * If mutable objects are stored in the triple, then the triple itself
effectively becomes mutable.</p>
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param <S> the f4 element type
+ */
+public abstract class Quartet<L, M, R, S> implements Comparable<Quartet<L, M,
R, S>>, Serializable {
+
+ private static final class QuarterAdapter<L, M, R, S> extends Quartet<L,
M, R, S> {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public L getF1() {
+ return null;
+ }
+
+ @Override
+ public M getF2() {
+ return null;
+ }
+
+ @Override
+ public R getF3() {
+ return null;
+ }
+
+ @Override
+ public S getF4() {
+ return null;
+ }
+ }
+
+ /**
+ * Serialization version
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * An empty array.
+ * <p>
+ * Consider using {@link #emptyArray()} to avoid generics warnings.
+ * </p>
+ *
+ */
+ public static final QuarterAdapter<?, ?, ?, ?>[] EMPTY_ARRAY = new
QuarterAdapter[0];
+
+ /**
+ * Returns the empty array singleton that can be assigned without compiler
warning.
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param <S> the f4 element type
+ * @return the empty array singleton that can be assigned without compiler
warning.
+ */
+ @SuppressWarnings("unchecked")
+ public static <L, M, R, S> Quartet<L, M, R, S>[] emptyArray() {
+ return (QuarterAdapter<L, M, R, S>[]) EMPTY_ARRAY;
+ }
+
+ /**
+ * <p>Obtains an immutable triple of four objects inferring the generic
types.</p>
+ *
+ * <p>This factory allows the triple to be created using inference to
+ * obtain the generic types.</p>
+ *
+ * @param <L> the f1 element type
+ * @param <M> the f2 element type
+ * @param <R> the f3 element type
+ * @param f1 the f1 element, may be null
+ * @param f2 the f2 element, may be null
+ * @param f3 the f3 element, may be null
+ * @return a triple formed from the four parameters, not null
+ */
+ public static <L, M, R, S> Quartet<L, M, R, S> of(final L f1, final M f2,
final R f3, final S f4) {
+ return new ImmutableQuartet<>(f1, f2, f3, f4);
+ }
+
+ // -----------------------------------------------------------------------
+
+ /**
+ * <p>Compares the triple based on the f1 element, followed by the f2
element and f3 element,
+ * finally the f4 element.
+ * The types must be {@code Comparable}.</p>
+ *
+ * @param other the other triple, not null
+ * @return negative if this is less, zero if equal, positive if greater
+ */
+ @Override
+ public int compareTo(final Quartet<L, M, R, S> other) {
+ return new CompareToBuilder().append(getF1(), other.getF1())
+ .append(getF2(), other.getF2())
+ .append(getF3(), other.getF3())
+ .append(getF4(), other.getF4())
+ .toComparison();
+ }
+
+ /**
+ * <p>Compares this triple to another based on the four elements.</p>
+ *
+ * @param obj the object to compare to, null returns false
+ * @return true if the elements of the triple are equal
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof Quartet<?, ?, ?, ?>) {
+ final Quartet<?, ?, ?, ?> other = (Quartet<?, ?, ?, ?>) obj;
+ return Objects.equals(getF1(), other.getF1())
+ && Objects.equals(getF2(), other.getF2())
+ && Objects.equals(getF3(), other.getF3())
+ && Objects.equals(getF4(), other.getF4());
+ }
+ return false;
+ }
+
+ /**
+ * <p>Gets the f1 element from this triple.</p>
+ *
+ * @return the f1 element, may be null
+ */
+ public abstract L getF1();
+
+ /**
+ * <p>Gets the f2 element from this triple.</p>
+ *
+ * @return the f2 element, may be null
+ */
+ public abstract M getF2();
+
+ /**
+ * <p>Gets the f3 element from this triple.</p>
+ *
+ * @return the f3 element, may be null
+ */
+ public abstract R getF3();
+
+ /**
+ * <p>Gets the f4 element from this triple.</p>
+ *
+ * @return the f4 element, may be null
+ */
+ public abstract S getF4();
+
+ /**
+ * <p>Returns a suitable hash code.</p>
+ *
+ * @return the hash code
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(getF1()) ^ Objects.hashCode(getF2()) ^
Objects.hashCode(getF3())
+ ^ Objects.hashCode(getF4());
+ }
+
+ /**
+ * <p>Returns a String representation of this triple using the format
{@code ($f1,$f2,$f3,$4)}.</p>
+ *
+ * @return a string describing this object, not null
+ */
+ @Override
+ public String toString() {
+ return "(" + getF1() + "," + getF2() + "," + getF3() + "," + getF4() +
")";
+ }
+
+ /**
+ * <p>Formats the receiver using the given format.</p>
+ *
+ * <p>This uses {@link java.util.Formattable} to perform the formatting.
Three variables may
+ * be used to embed the f1 and f3 elements. Use {@code %1$s} for the f1
+ * element, {@code %2$s} for the f2 , {@code %3$s} for the f3 element and
{@code %4$s} for the f4 element.
+ * The default format used by {@code toString()} is {@code
(%1$s,%2$s,%3$s,%4$s)}.</p>
+ *
+ * @param format the format string, optionally containing {@code %1$s},
{@code %2$s} and {@code %3$s}, not null
+ * @return the formatted string, not null
+ */
+ public String toString(final String format) {
+ return String.format(format, getF1(), getF2(), getF3(), getF4());
+ }
+
+}
diff --git
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/stream/StreamField.java
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/stream/StreamField.java
index 68dcbe5af..fffe2a213 100644
---
a/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/stream/StreamField.java
+++
b/inlong-manager/manager-pojo/src/main/java/org/apache/inlong/manager/pojo/stream/StreamField.java
@@ -25,7 +25,11 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.inlong.manager.common.tool.excel.annotation.ExcelEntity;
import org.apache.inlong.manager.common.tool.excel.annotation.ExcelField;
+import org.apache.inlong.manager.common.tool.excel.annotation.Font;
+import org.apache.inlong.manager.common.tool.excel.annotation.Style;
import
org.apache.inlong.manager.common.tool.excel.validator.NonEmptyCellValidator;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.IndexedColors;
import java.io.Serializable;
@@ -37,7 +41,7 @@ import java.io.Serializable;
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("Stream field configuration")
-@ExcelEntity(name = "StreamField excel template")
+@ExcelEntity(name = "InLong-StreamField-Template")
public class StreamField implements Serializable {
@ApiModelProperty("Field index")
@@ -49,15 +53,19 @@ public class StreamField implements Serializable {
@ApiModelProperty(value = "inlong stream id", required = true)
private String inlongStreamId;
- @ExcelField(name = "Field name", validator = NonEmptyCellValidator.class)
+ @ExcelField(name = "Field name", validator = NonEmptyCellValidator.class,
+
+ font = @Font(size = 16), style = @Style(bgColor =
IndexedColors.LIGHT_TURQUOISE, width = 9000, allBorderColor =
IndexedColors.BLUE, allBorderStyle = BorderStyle.THIN))
@ApiModelProperty(value = "Field name", required = true)
private String fieldName;
- @ExcelField(name = "Field type", validator =
StreamFieldTypeCellValidator.class)
+ @ExcelField(name = "Field type", validator =
StreamFieldTypeCellValidator.class,
+
+ font = @Font(size = 16), style = @Style(bgColor =
IndexedColors.LIGHT_TURQUOISE, width = 6000, allBorderColor =
IndexedColors.BLUE, allBorderStyle = BorderStyle.THIN))
@ApiModelProperty(value = "Field type", required = true)
private String fieldType;
- @ExcelField(name = "Field comment")
+ @ExcelField(name = "Field comment", font = @Font(size = 16), style =
@Style(bgColor = IndexedColors.LIGHT_TURQUOISE, width = 10000, allBorderColor =
IndexedColors.BLUE, allBorderStyle = BorderStyle.THIN))
@ApiModelProperty("Field comment")
private String fieldComment;