表结构文档导出

This commit is contained in:
lzy
2023-09-12 15:36:13 +08:00
parent 0c1d38e036
commit f1ecfbed87
35 changed files with 1676 additions and 12 deletions

View File

@ -56,6 +56,23 @@
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!--poi-tl-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.5</version>
</dependency>
<!--pdf-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,90 @@
package ai.chat2db.server.web.api.controller.rdb;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.param.TablePageQueryParam;
import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.domain.api.param.TableSelector;
import ai.chat2db.server.domain.api.service.TableService;
import ai.chat2db.server.tools.base.wrapper.result.PageResult;
import ai.chat2db.server.tools.common.util.EasyEnumUtils;
import ai.chat2db.server.web.api.aspect.ConnectionInfoAspect;
import ai.chat2db.server.web.api.controller.rdb.converter.RdbWebConverter;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.factory.ExportServiceFactory;
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
import ai.chat2db.server.web.api.controller.rdb.vo.TableVO;
import ai.chat2db.spi.model.Table;
import ai.chat2db.spi.sql.Chat2DBContext;
import ai.chat2db.spi.util.JdbcUtils;
import cn.hutool.core.date.DatePattern;
import com.alibaba.druid.DbType;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Constructor;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
/**
* RdbDocController
*
* @author lzy
**/
@ConnectionInfoAspect
@RequestMapping("/api/rdb/doc")
@Controller
@Slf4j
public class RdbDocController {
@Autowired
private TableService tableService;
@Autowired
private RdbWebConverter rdbWebConverter;
/**
* export data
*
* @param request
*/
@PostMapping("/export")
public void export(@Valid @RequestBody DataExportRequest request, HttpServletResponse response) throws Exception {
ExportTypeEnum exportType = EasyEnumUtils.getEnum(ExportTypeEnum.class, request.getExportType());
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(
request.getDatabaseName() + "_" + LocalDateTime.now().format(DatePattern.PURE_DATETIME_FORMATTER),
StandardCharsets.UTF_8)
.replaceAll("\\+", "%20");
TablePageQueryParam queryParam = rdbWebConverter.tablePageRequest2param(request);
queryParam.setPageNo(1);
queryParam.setPageSize(Integer.MAX_VALUE);
TableSelector tableSelector = new TableSelector();
tableSelector.setColumnList(true);
tableSelector.setIndexList(true);
PageResult<Table> tableDTOPageResult = tableService.pageQuery(queryParam, tableSelector);
List<TableVO> tableVOS = rdbWebConverter.tableDto2vo(tableDTOPageResult.getData());
TableQueryParam param = rdbWebConverter.tableRequest2param(request);
for (TableVO tableVO: tableVOS) {
param.setTableName(tableVO.getName());
tableVO.setColumnList(rdbWebConverter.columnDto2vo(tableService.queryColumns(param)));
tableVO.setIndexList(rdbWebConverter.indexDto2vo(tableService.queryIndexes(param)));
}
Class<?> targetClass = ExportServiceFactory.get(exportType.getCode());
Constructor<?> constructor = targetClass.getDeclaredConstructor();
DatabaseExportService databaseExportService = (DatabaseExportService) constructor.newInstance();
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + databaseExportService.getSuffix());
response.setContentType(databaseExportService.getContentType());
// 设置数据集合
databaseExportService.setExportList(tableVOS);
databaseExportService.generate(request.getDatabaseName(), response.getOutputStream(), new ExportOptions());
}
}

View File

@ -9,6 +9,7 @@ import ai.chat2db.server.domain.api.param.ShowCreateTableParam;
import ai.chat2db.server.domain.api.param.TablePageQueryParam;
import ai.chat2db.server.domain.api.param.TableQueryParam;
import ai.chat2db.server.web.api.controller.data.source.vo.DatabaseVO;
import ai.chat2db.server.web.api.controller.rdb.request.DataExportRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlCountRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlExportRequest;
import ai.chat2db.server.web.api.controller.rdb.request.DdlRequest;
@ -100,6 +101,20 @@ public abstract class RdbWebConverter {
* @return
*/
public abstract TablePageQueryParam tablePageRequest2param(TableBriefQueryRequest request);
/**
* 参数转换
*
* @param request
* @return
*/
public abstract TablePageQueryParam tablePageRequest2param(DataExportRequest request);
/**
* 参数转换
*
* @param request
* @return
*/
public abstract TableQueryParam tableRequest2param(DataExportRequest request);
/**
* 参数转换

View File

@ -0,0 +1,167 @@
package ai.chat2db.server.web.api.controller.rdb.doc;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.IndexInfo;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.vo.ColumnVO;
import ai.chat2db.server.web.api.controller.rdb.vo.IndexVO;
import ai.chat2db.server.web.api.controller.rdb.vo.TableVO;
import ai.chat2db.server.web.api.util.StringUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.val;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* DatabaseExportService
*
* @author lzy
**/
public class DatabaseExportService {
protected ExportTypeEnum exportTypeEnum;
@Getter
public String suffix;
@Getter
public String contentType;
/**
* 导出excel 集合
**/
@Setter
@Getter
public List<TableVO> exportList;
/**
* 导出word、excel 表信息 集合
**/
public static Map<String, List<TableParameter>> listMap = new LinkedHashMap<>();
/**
* 导出word 索引 集合
**/
public static Map<String, List<IndexInfo>> indexMap = new HashMap<>(0);
/**
* 连接符
**/
public final static String JOINER = "---";
private void init() {
listMap.clear();
indexMap.clear();
}
public void generate(String databaseName, OutputStream outputStream, ExportOptions exportOptions) {
init();
exportList.forEach(item -> {
dataAssemble(databaseName, exportOptions, item);
});
try {
export(outputStream, exportOptions);
} catch (Exception e) {
throw new RuntimeException("导出失败请联系开发者邮箱963565242@qq.com" + e);
}
init();
}
/**
* 数据处理
*
* @param exportOptions 配置信息
**/
public void dataAssemble(String databaseName, ExportOptions exportOptions, TableVO item) {
boolean isExportIndex = Optional.ofNullable(exportOptions.getIsExportIndex()).orElse(false);
val t = new TableParameter();
t.setFieldName(item.getName() + "[" + StringUtils.isNull(item.getComment()) + "]");
List<TableParameter> colForTable = new LinkedList<>();
for (ColumnVO info : item.getColumnList()) {
val p = new TableParameter();
p.setFieldName(info.getName()).setColumnDefault(info.getDefaultValue())
.setColumnComment(info.getComment())
.setColumnType(info.getColumnType())
.setLength(String.valueOf(info.getCharacterMaximumLength())).setIsNullAble(String.valueOf(info.getNullable()))
.setDecimalPlaces(String.valueOf(info.getNumericPrecision()));
colForTable.add(p);
}
String key = databaseName + JOINER + t.getFieldName();
listMap.put(key, colForTable);
if (isExportIndex) {
int index = key.lastIndexOf("[");
String str = key.substring(0, index);
indexMap.put(str, vo2Info(item.getIndexList()));
}
//赋值序号
for (Map.Entry<String, List<TableParameter>> map : listMap.entrySet()) {
//赋值序号
List<TableParameter> list = map.getValue();
IntStream.range(0, list.size()).forEach(x -> {
list.get(x).setNo(String.valueOf(x + 1));
});
}
}
private List<IndexInfo> vo2Info(List<IndexVO> indexList) {
return indexList.stream().map(v -> {
IndexInfo info = new IndexInfo();
info.setName(v.getName());
info.setColumnName(v.getColumns());
info.setIndexType(v.getType());
info.setComment(v.getComment());
return info;
}).collect(Collectors.toList());
}
/**
* 导出
*
* @param outputStream 文件流
**/
public void export(OutputStream outputStream, ExportOptions exportOptions) {
}
/**
* 处理空串或null字符
*
* @param source 源字符
* @return java.lang.String
**/
public String dealWith(String source) {
return StringUtils.isNullOrEmpty(source);
}
@SneakyThrows
public Object[] getIndexValues(IndexInfo indexInfoVO) {
Object[] values = new Object[IndexInfo.class.getDeclaredFields().length];
values[0] = dealWith(indexInfoVO.getName());
values[1] = dealWith(indexInfoVO.getColumnName());
values[2] = dealWith(indexInfoVO.getIndexType());
values[3] = dealWith(indexInfoVO.getIndexMethod());
values[4] = dealWith(indexInfoVO.getComment());
return values;
}
@SneakyThrows
public Object[] getColumnValues(TableParameter tableParameter) {
Object[] values = new Object[TableParameter.class.getDeclaredFields().length];
values[0] = StringUtils.isNull(tableParameter.getNo());
values[1] = StringUtils.isNull(tableParameter.getFieldName());
values[2] = StringUtils.isNull(tableParameter.getColumnType());
values[3] = StringUtils.isNull(tableParameter.getLength());
values[4] = StringUtils.isNull(tableParameter.getIsNullAble());
values[5] = StringUtils.isNull(tableParameter.getColumnDefault());
values[6] = StringUtils.isNull(tableParameter.getDecimalPlaces());
values[7] = StringUtils.isNull(tableParameter.getColumnComment());
return values;
}
}

View File

@ -0,0 +1,46 @@
package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;
import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import java.util.Iterator;
/**
* CustomCellWriteHeightConfig
*
* @author lzy
**/
public class CustomCellWriteHeightConfig extends AbstractRowHeightStyleStrategy {
/**
* 默认高度
*/
private static final Integer DEFAULT_HEIGHT = 300;
@Override
protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
}
@Override
protected void setContentColumnHeight(Row row, int relativeRowIndex) {
Iterator<Cell> cellIterator = row.cellIterator();
if (!cellIterator.hasNext()) {
return;
}
// 默认为 1行高度
int maxHeight = 1;
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
if (cell.getCellType() == CellType.STRING) {
if (cell.getStringCellValue().contains("\n")) {
int length = cell.getStringCellValue().split("\n").length;
maxHeight = Math.max(maxHeight, length);
}
}
}
row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
}
}

View File

@ -0,0 +1,79 @@
package ai.chat2db.server.web.api.controller.rdb.doc.adaptive;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* CustomCellWriteWidthConfig
*
* @author lzy
**/
public class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy {
private Map<String, Map<Integer, Integer>> CACHE = new HashMap<>();
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetName(), k -> new HashMap<>(0));
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
if (columnWidth >= 0) {
if (columnWidth > 119) {
columnWidth = 120;
}
Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
Sheet sheet = writeSheetHolder.getSheet();
sheet.setColumnWidth(cell.getColumnIndex(), Math.min(Double.valueOf(columnWidth * 256 * 1.8).intValue(), 16384));
}
}
}
}
/**
* 计算长度
* @param cellDataList cell数据
* @param cell cell
* @param isHead 是否是标题
* @return
*/
private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData<?> cellData = cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
} else {
switch (type) {
case STRING:
// 换行符(数据需要提前解析好)
int index = cellData.getStringValue().indexOf("\n");
return index != -1 ?
cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;
case BOOLEAN:
return cellData.getBooleanValue().toString().getBytes().length;
case NUMBER:
return cellData.getNumberValue().toString().getBytes().length;
default:
return -1;
}
}
}
}
}

View File

@ -0,0 +1,25 @@
package ai.chat2db.server.web.api.controller.rdb.doc.conf;
import lombok.Data;
/**
* 生成选项
*
* @author lzy
*/
@Data
public class ExportOptions {
/**
* 是否导出多sheet
*/
private Boolean isExportMoreSheet = Boolean.FALSE;
/**
* 是否导出索引
*/
private Boolean isExportIndex = Boolean.FALSE;
/**
* 导出文件后缀
**/
private String fileSuffix;
}

View File

@ -0,0 +1,31 @@
package ai.chat2db.server.web.api.controller.rdb.doc.constant;
import ai.chat2db.server.tools.common.util.I18nUtils;
import lombok.extern.java.Log;
/**
* CommonConstant
*
* @author lzy
**/
@Log
public final class CommonConstant {
/**
* 表head
**/
public static String[] INDEX_HEAD_NAMES =
{I18nUtils.getMessage("main.indexName"),
I18nUtils.getMessage("main.indexFieldName"),
I18nUtils.getMessage("main.indexType"),
I18nUtils.getMessage("main.indexMethod"),
I18nUtils.getMessage("main.indexNote")};
public static String[] COLUMN_HEAD_NAMES =
{I18nUtils.getMessage("main.fieldNo"),
I18nUtils.getMessage("main.fieldName"),
I18nUtils.getMessage("main.fieldType"),
I18nUtils.getMessage("main.fieldLength"),
I18nUtils.getMessage("main.fieldIfEmpty"),
I18nUtils.getMessage("main.fieldDefault"),
I18nUtils.getMessage("main.fieldDecimalPlaces"),
I18nUtils.getMessage("main.fieldNote")};
}

View File

@ -0,0 +1,51 @@
package ai.chat2db.server.web.api.controller.rdb.doc.constant;
import ai.chat2db.server.tools.common.util.I18nUtils;
/**
* PatternConstant
*
* @author lzy
**/
public final class PatternConstant {
/**
* 公共
*/
public static final String MD_SPLIT = "|";
/**
* Markdown
*/
public static final String TITLE = "# %s";
public static final String CATALOG = "## %s";
public static final String ALL_TABLE_HEADER = MD_SPLIT + I18nUtils.getMessage("main.fieldNo") + MD_SPLIT +
I18nUtils.getMessage("main.fieldName") + MD_SPLIT +
I18nUtils.getMessage("main.fieldType") + MD_SPLIT +
I18nUtils.getMessage("main.fieldLength") + MD_SPLIT +
I18nUtils.getMessage("main.fieldIfEmpty") + MD_SPLIT +
I18nUtils.getMessage("main.fieldDefault") + MD_SPLIT +
I18nUtils.getMessage("main.fieldDecimalPlaces") + MD_SPLIT +
I18nUtils.getMessage("main.fieldNote") + MD_SPLIT;
public static String TABLE_BODY = "|%s|%s|%s|%s|%s|%s|%s|%s|";
public static String TABLE_SEPARATOR = "|:----:|----|----|----|----|----|----|----|";
public static final String ALL_INDEX_TABLE_HEADER = MD_SPLIT + I18nUtils.getMessage("main.indexName") + MD_SPLIT +
I18nUtils.getMessage("main.indexFieldName") + MD_SPLIT +
I18nUtils.getMessage("main.indexType") + MD_SPLIT +
I18nUtils.getMessage("main.indexMethod") + MD_SPLIT +
I18nUtils.getMessage("main.indexNote") + MD_SPLIT;
public static String INDEX_TABLE_BODY = "|%s|%s|%s|%s|";
public static String INDEX_TABLE_SEPARATOR = "|:----:|----|----|----|";
/**
* Html
*/
public static final String HTML_TITLE = "<h1 id=\"{0}\">{0}</h1>";
public static final String HTML_CATALOG = "<h2 id=\"{0}\">{1}</h2>";
public static final String HTML_INDEX_ITEM = "<a href=\"#{0}\" title=\"{0}\">{1}</a>";
public static String HTML_TABLE_HEADER = "<tr><th>序号</th><th>字段名</th><th>类型</th><th>长度</th><th>是否为空</th><th>默认值</th><th>小数位</th><th>注释</th></tr>";
public static String HTML_TABLE_BODY = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>";
public static String HTML_INDEX_TABLE_HEADER = "<tr><th>名称</th><th>字段</th><th>DDL</th><th>注释</th></tr>";
public static String HTML_INDEX_TABLE_BODY = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>";
}

View File

@ -0,0 +1,54 @@
package ai.chat2db.server.web.api.controller.rdb.doc.export;
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteHeightConfig;
import ai.chat2db.server.web.api.controller.rdb.doc.adaptive.CustomCellWriteWidthConfig;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.doc.merge.MyMergeExcel;
import ai.chat2db.server.web.api.controller.rdb.doc.style.CustomExcelStyle;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import lombok.SneakyThrows;
import lombok.val;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* ExportExcelService
*
* @author lzy
**/
public class ExportExcelService extends DatabaseExportService {
public ExportExcelService() {
exportTypeEnum = ExportTypeEnum.EXCEL;
suffix = ExportFileSuffix.EXCEL.getSuffix();
contentType = "text/csv";
}
@SneakyThrows
@Override
public void export(OutputStream outputStream, ExportOptions exportOptions) {
List<TableParameter> export = new ArrayList<>();
for (Map.Entry<String, List<TableParameter>> item : listMap.entrySet()) {
val t = new TableParameter();
t.setNo(item.getKey()).setColumnComment(MyMergeExcel.NAME);
export.add(t);
export.addAll(item.getValue());
}
EasyExcel.write(outputStream)
.registerWriteHandler(new HorizontalCellStyleStrategy(CustomExcelStyle.getHeadStyle(), CustomExcelStyle.getContentWriteCellStyle()))
.registerWriteHandler(new CustomCellWriteHeightConfig())
.registerWriteHandler(new CustomCellWriteWidthConfig())
.registerWriteHandler(new MyMergeExcel())
.sheet("表结构")
.doWrite(export);
}
}

View File

@ -0,0 +1,109 @@
package ai.chat2db.server.web.api.controller.rdb.doc.export;
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.IndexInfo;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.tools.common.config.GlobalDict;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;
import ai.chat2db.server.web.api.util.StringUtils;
import lombok.SneakyThrows;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* ExportHtmlService
*
* @author lzy
**/
public class ExportHtmlService extends DatabaseExportService {
public ExportHtmlService() {
exportTypeEnum = ExportTypeEnum.HTML;
suffix = ExportFileSuffix.HTML.getSuffix();
contentType = "text/html";
}
@SneakyThrows
@Override
public void export(OutputStream outputStream, ExportOptions exportOptions) {
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
StringBuilder htmlText = new StringBuilder();
StringBuilder catalogue = new StringBuilder();
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
//数据库名
String database = myMap.getKey();
String title = MessageFormat.format(PatternConstant.HTML_TITLE, I18nUtils.getMessage("main.databaseText") + database);
//数据库名-目录
catalogue.append("<li>").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, I18nUtils.getMessage("main.databaseText")
+ database, I18nUtils.getMessage("main.databaseText") + database)).append("<ol>");
htmlText.append(title).append("\n");
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
//表名
String tableName = parameterMap.getKey().split("---")[1];
//表名-目录
catalogue.append("<li>").append(MessageFormat.format(PatternConstant.HTML_INDEX_ITEM, database + tableName, tableName));
htmlText.append(MessageFormat.format(PatternConstant.HTML_CATALOG, database + tableName, tableName)).append("\n<p></p>");
//索引Table
if (!indexMap.isEmpty()) {
htmlText.append("<table>\n");
htmlText.append(PatternConstant.HTML_INDEX_TABLE_HEADER);
String name = parameterMap.getKey().split("\\[")[0];
List<IndexInfo> indexInfoVOList = indexMap.get(name);
for (IndexInfo indexInfo : indexInfoVOList) {
htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(indexInfo)));
}
htmlText.append("</table>\n");
htmlText.append("\n<p></p>");
} else {
htmlText.append(String.format(PatternConstant.HTML_INDEX_TABLE_BODY, getIndexValues(new IndexInfo())));
}
//字段Table
htmlText.append("<table>\n");
htmlText.append(PatternConstant.HTML_TABLE_HEADER);
List<TableParameter> exportList = parameterMap.getValue();
for (TableParameter tableParameter : exportList) {
htmlText.append(String.format(PatternConstant.HTML_TABLE_BODY, getColumnValues(tableParameter)));
}
htmlText.append("</table>\n");
}
htmlText.append("<p></p>");
catalogue.append("</ol>");
}
catalogue.append("</li>");
String filePath = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(0);
try (FileInputStream inputStream = new FileInputStream(filePath);
ByteArrayOutputStream result = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
String str = result.toString(String.valueOf(StandardCharsets.UTF_8));
str = str.replace("${data}", htmlText).replace("${catalogue}", catalogue);
writer.write(str);
}
}
}
@Override
public String dealWith(String source) {
return StringUtils.isNullForHtml(source);
}
}

View File

@ -0,0 +1,93 @@
package ai.chat2db.server.web.api.controller.rdb.doc.export;
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.IndexInfo;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.doc.constant.PatternConstant;
import ai.chat2db.server.web.api.util.StringUtils;
import lombok.SneakyThrows;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* ExportMarkdownService
*
* @author lzy
**/
public class ExportMarkdownService extends DatabaseExportService {
public ExportMarkdownService() {
exportTypeEnum = ExportTypeEnum.MARKDOWN;
suffix = ExportFileSuffix.MARKDOWN.getSuffix();
contentType = "text/plain";
}
@SneakyThrows
@Override
public void export(OutputStream outputStream, ExportOptions exportOptions) {
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
try (BufferedWriter fileWriter = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
//数据库名
String database = myMap.getKey();
String title = String.format(PatternConstant.TITLE, "数据库:" + database);
fileWriter.write(title);
writeLineSeparator(fileWriter, 2);
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
//表名
String tableName = parameterMap.getKey().split("---")[1];
fileWriter.write(String.format(PatternConstant.CATALOG, tableName));
writeLineSeparator(fileWriter, 1);
//索引Table
if (!indexMap.isEmpty()) {
fileWriter.write(PatternConstant.ALL_INDEX_TABLE_HEADER);
writeLineSeparator(fileWriter, 1);
fileWriter.write(PatternConstant.INDEX_TABLE_SEPARATOR);
writeLineSeparator(fileWriter, 1);
String name = parameterMap.getKey().split("\\[")[0];
List<IndexInfo> indexInfoVOList = indexMap.get(name);
for (int j = 0; j < indexInfoVOList.size(); j++) {
fileWriter.write(String.format(PatternConstant.INDEX_TABLE_BODY, getIndexValues(indexInfoVOList.get(j))));
writeLineSeparator(fileWriter, 1);
}
writeLineSeparator(fileWriter, 1);
}
writeLineSeparator(fileWriter, 2);
fileWriter.write(PatternConstant.ALL_TABLE_HEADER);
writeLineSeparator(fileWriter, 1);
fileWriter.write(PatternConstant.TABLE_SEPARATOR);
writeLineSeparator(fileWriter, 1);
//字段Table
List<TableParameter> exportList = parameterMap.getValue();
for (TableParameter tableParameter : exportList) {
fileWriter.write(String.format(PatternConstant.TABLE_BODY, getColumnValues(tableParameter)));
writeLineSeparator(fileWriter, 1);
}
writeLineSeparator(fileWriter, 2);
}
}
}
}
private void writeLineSeparator(BufferedWriter fileWriter, int number) throws IOException {
for (int i = 0; i < number; i++) {
fileWriter.write(System.lineSeparator());
}
}
@Override
public String dealWith(String source) {
return StringUtils.isNullForHtml(source);
}
}

View File

@ -0,0 +1,115 @@
package ai.chat2db.server.web.api.controller.rdb.doc.export;
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.IndexInfo;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import lombok.SneakyThrows;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* ExportPdfService
*
* @author lzy
**/
public class ExportPdfService extends DatabaseExportService {
public ExportPdfService() {
exportTypeEnum = ExportTypeEnum.PDF;
suffix = ExportFileSuffix.PDF.getSuffix();
contentType = "application/pdf";
}
@SneakyThrows
@Override
public void export(OutputStream outputStream, ExportOptions exportOptions) {
boolean isExportIndex = exportOptions.getIsExportIndex();
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
Document document = new Document();
PdfWriter pdfWriter = PdfWriter.getInstance(document, outputStream);
pdfWriter.setStrictImageSequence(true);
// 字体设置
BaseFont baseFont =BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// 创建字体对象
Font font = new Font(baseFont, 10, Font.NORMAL);
Font headFont = new Font(baseFont, 12, Font.NORMAL);
Font titleFont = new Font(baseFont, 14, Font.BOLD);
document.open();
//遍历数据
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
//数据库名
String database = myMap.getKey();
String title = I18nUtils.getMessage("main.databaseText") + database;
Paragraph p = new Paragraph(title, titleFont);
document.add(p);
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
//表名
String tableName = parameterMap.getKey().split("---")[1];
Paragraph tableParagraph = new Paragraph(tableName, font);
document.add(tableParagraph);
//索引Table
if (isExportIndex && !indexMap.isEmpty()) {
PdfPTable table = new PdfPTable(CommonConstant.INDEX_HEAD_NAMES.length);
process(table, CommonConstant.INDEX_HEAD_NAMES, font);
String name = parameterMap.getKey().split("\\[")[0];
List<IndexInfo> indexInfoVOList = indexMap.get(name);
for (IndexInfo indexInfo : indexInfoVOList) {
process(table, getIndexValues(indexInfo), font);
}
table.setPaddingTop(5);
document.add(table);
}
document.add(new Paragraph());
//字段Table
List<TableParameter> exportList = parameterMap.getValue();
PdfPTable table = new PdfPTable(CommonConstant.COLUMN_HEAD_NAMES.length);
//标题、内容
process(table, CommonConstant.COLUMN_HEAD_NAMES, headFont);
for (TableParameter tableParameter : exportList) {
process(table, getColumnValues(tableParameter), font);
}
// 设置表格上方的空白间距,即向下移动的效果
table.setSpacingBefore(10f);
table.setSpacingAfter(20f);
//居左对齐
table.setHorizontalAlignment(PdfPTable.ALIGN_LEFT);
document.add(table);
//分页
//document.newPage();
}
}
document.close();
}
//设置表格内容
public static <T> void process(PdfPTable table, T[] line, Font font) {
for (T s : line) {
if (Objects.isNull(s)) {
return;
}
PdfPCell cell = new PdfPCell(new Paragraph(s.toString(), font));
cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
cell.setVerticalAlignment(PdfPCell.ALIGN_CENTER);
cell.setPaddingTop(5);
cell.setPaddingBottom(5);
table.addCell(cell);
}
}
}

View File

@ -0,0 +1,122 @@
package ai.chat2db.server.web.api.controller.rdb.doc.export;
import ai.chat2db.server.domain.api.enums.ExportFileSuffix;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.domain.api.model.IndexInfo;
import ai.chat2db.server.domain.api.model.TableParameter;
import ai.chat2db.server.tools.common.config.GlobalDict;
import ai.chat2db.server.web.api.controller.rdb.doc.DatabaseExportService;
import ai.chat2db.server.web.api.controller.rdb.doc.conf.ExportOptions;
import ai.chat2db.server.web.api.controller.rdb.doc.constant.CommonConstant;
import ai.chat2db.server.web.api.util.AddToTopic;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.Includes;
import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.data.Rows;
import com.deepoove.poi.data.Tables;
import lombok.SneakyThrows;
import java.io.File;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* WordSuperActionListener
*
* @author lzy
**/
public class ExportWordSuperService extends DatabaseExportService {
public ExportWordSuperService() {
exportTypeEnum = ExportTypeEnum.WORD;
suffix = ExportFileSuffix.WORD.getSuffix();
contentType = "application/msword";
}
/**
* Word导出
**/
@SneakyThrows
@Override
public void export(OutputStream outputStream, ExportOptions exportOptions) {
boolean isExportIndex = exportOptions.getIsExportIndex();
String filePath = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(1);
String subFile = GlobalDict.templateDir + GlobalDict.TEMPLATE_FILE.get(2);
File importWordFile = new File(filePath);
Map<String, List<Map.Entry<String, List<TableParameter>>>> allMap = listMap.entrySet()
.stream().collect(Collectors.groupingBy(v -> v.getKey().split("---")[0]));
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> myDataMap = new HashMap<>(2);
//索引表头
RowRenderData indexHeaderRow = Rows.of(CommonConstant.INDEX_HEAD_NAMES).center().textBold().textColor("000000").bgColor("bfbfbf").create();
//字段表头
RowRenderData tableHeaderRow = Rows.of(CommonConstant.COLUMN_HEAD_NAMES).center().textBold().textColor("000000").bgColor("bfbfbf").create();
for (Map.Entry<String, List<Map.Entry<String, List<TableParameter>>>> myMap : allMap.entrySet()) {
//数据库名
String database = myMap.getKey();
int i = 1;
for (Map.Entry<String, List<TableParameter>> parameterMap : myMap.getValue()) {
//初始化容量 3/0.75 + 1
Map<String, Object> tableData = new HashMap<>(8);
//索引Table
if (isExportIndex) {
String name = parameterMap.getKey().split("\\[")[0];
List<IndexInfo> indexInfoVOList = indexMap.get(name);
List<RowRenderData> rowList = getIndexValues(indexInfoVOList, indexHeaderRow);
tableData.put("indexTable", Tables.create(rowList.toArray(new RowRenderData[0])));
}
if (i == 1) {
Map<String, String> map = new HashMap<>(2);
map.put("dataBase", database);
tableData.put("ifDatabase", map);
}
//表名
String tableName = parameterMap.getKey().split("---")[1];
tableData.put("number", i);
tableData.put("name", tableName);
List<TableParameter> tableParameterList = parameterMap.getValue();
List<RowRenderData> rowList = getColumnValues(tableParameterList, tableHeaderRow);
tableData.put("table", Tables.create(rowList.toArray(new RowRenderData[0])));
i++;
list.add(tableData);
}
}
myDataMap.put("mydata", Includes.ofLocal(subFile).setRenderModel(list).create());
/*根据模板生成文档*/
XWPFTemplate template = XWPFTemplate.compile(importWordFile).render(myDataMap);
AddToTopic.generateTOC(template.getXWPFDocument(), outputStream);
}
@SneakyThrows
public List<RowRenderData> getColumnValues(List<TableParameter> list, RowRenderData tableHeaderRow) {
List<RowRenderData> rowRenderDataList = new ArrayList<>();
rowRenderDataList.add(tableHeaderRow);
for (TableParameter tableParameter : list) {
String[] values = Arrays.stream(getColumnValues(tableParameter)).toArray(String[]::new);
rowRenderDataList.add(Rows.of(values).center().create());
}
return rowRenderDataList;
}
@SneakyThrows
public List<RowRenderData> getIndexValues(List<IndexInfo> list, RowRenderData tableHeaderRow) {
List<RowRenderData> rowRenderDataList = new ArrayList<>();
rowRenderDataList.add(tableHeaderRow);
if (list.isEmpty()) {
String[] values = Arrays.stream(getIndexValues(new IndexInfo())).toArray(String[]::new);
rowRenderDataList.add(Rows.of(values).center().create());
return rowRenderDataList;
}
for (IndexInfo indexInfo : list) {
String[] values = Arrays.stream(getIndexValues(indexInfo)).toArray(String[]::new);
rowRenderDataList.add(Rows.of(values).center().create());
}
return rowRenderDataList;
}
}

View File

@ -0,0 +1,47 @@
package ai.chat2db.server.web.api.controller.rdb.doc.merge;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
/**
* MyMergeExcel
*
* @author lzy
**/
public class MyMergeExcel extends AbstractMergeStrategy {
public static final String NAME = "isTableNameBlank";
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (NAME.equals(cell.getStringCellValue())) {
CellRangeAddress region = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), 0, 7);
sheet.addMergedRegion(region);
Row row = sheet.getRow(cell.getRowIndex());
cell = row.getCell(0);
Workbook workbook = sheet.getWorkbook();
// 生成一个样式
CellStyle style = workbook.createCellStyle();
// 设置这些样式
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderTop(BorderStyle.THIN);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置填充方案
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//设置自定义填充颜色
style.setFillForegroundColor(IndexedColors.GREEN.getIndex());
// 生成一个字体
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 14);
// 把字体应用到当前的样式
style.setFont(font);
cell.setCellStyle(style);
}
}
}

View File

@ -0,0 +1,60 @@
package ai.chat2db.server.web.api.controller.rdb.doc.style;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
/**
* CustomExcelStyle
*
* @author lzy
**/
public class CustomExcelStyle {
public static WriteCellStyle getContentWriteCellStyle() {
//内容样式策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//垂直居中,水平居中
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
//设置 自动换行
contentWriteCellStyle.setWrapped(true);
// 字体策略
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 11);
contentWriteFont.setFontName("宋体");
contentWriteFont.setColor(IndexedColors.BLACK.getIndex());
contentWriteCellStyle.setWriteFont(contentWriteFont);
return contentWriteCellStyle;
}
public static WriteCellStyle getHeadStyle() {
//头策略使用默认 设置字体大小
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
headWriteCellStyle.setBorderTop(BorderStyle.THIN);
headWriteCellStyle.setBorderRight(BorderStyle.THIN);
headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
headWriteCellStyle.setWrapped(false);
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 14);
headWriteFont.setBold(true);
headWriteFont.setFontName("宋体");
headWriteCellStyle.setWriteFont(headWriteFont);
headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
return headWriteCellStyle;
}
}

View File

@ -0,0 +1,45 @@
package ai.chat2db.server.web.api.controller.rdb.factory;
import ai.chat2db.server.domain.api.enums.ExportTypeEnum;
import ai.chat2db.server.web.api.controller.rdb.doc.export.*;
import lombok.SneakyThrows;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* ExportServiceFactory
*
* @author lzy
**/
public class ExportServiceFactory {
/**
* Export实现类缓存池
*/
private static final Map<String, Class<?>> REPORT_POOL = new ConcurrentHashMap<>(8);
static {
REPORT_POOL.put(ExportTypeEnum.EXCEL.name(), ExportExcelService.class);
REPORT_POOL.put(ExportTypeEnum.WORD.name(), ExportWordSuperService.class);
REPORT_POOL.put(ExportTypeEnum.MARKDOWN.name(), ExportMarkdownService.class);
REPORT_POOL.put(ExportTypeEnum.HTML.name(), ExportHtmlService.class);
REPORT_POOL.put(ExportTypeEnum.PDF.name(), ExportPdfService.class);
}
/**
* 获取对应接口
*
* @param type 报表类型
* @return Class
*/
@SneakyThrows
public static Class<?> get(String type) {
Class<?> dataResult = REPORT_POOL.get(type);
if (dataResult == null) {
throw new ClassNotFoundException("no ExportUI was found");
} else {
return dataResult;
}
}
}

View File

@ -0,0 +1,45 @@
package ai.chat2db.server.web.api.util;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
import java.io.IOException;
import java.io.OutputStream;
/**
* AddToTopic
*
* @author lzy
**/
public class AddToTopic {
public static void generateTOC(XWPFDocument document, OutputStream out) throws IOException {
String findText = "目录哈哈";
String replaceText = "";
for (XWPFParagraph p : document.getParagraphs()) {
for (XWPFRun r : p.getRuns()) {
int pos = r.getTextPosition();
String text = r.getText(pos);
if (text != null && text.contains(findText)) {
text = text.replace(findText, replaceText);
r.setText(text, 0);
addField(p);
// addField(p, "TOC \\h");
break;
}
}
}
document.write(out);
}
private static void addField(XWPFParagraph paragraph) {
CTSimpleField ctSimpleField = paragraph.getCTP().addNewFldSimple();
ctSimpleField.setInstr("TOC \\o \"1-3\" \\h \\z \\u");
ctSimpleField.setDirty(STOnOff.TRUE);
ctSimpleField.addNewR().addNewT().setStringValue("<>");
}
}

View File

@ -0,0 +1,96 @@
package ai.chat2db.server.web.api.util;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* 添加字符串工具类为了兼容JB的各种产品尽量不要用第三方工具包
*
* @author lzy
*/
@SuppressWarnings("WeakerAccess")
public class StringUtils {
private static final String EMPTY_STR = "null";
/**
* 首字母处理方法
*/
private static final BiFunction<String, Function<Integer, Integer>, String> FIRST_CHAR_HANDLER_FUN = (str, firstCharFun) -> {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return str;
}
final int firstCodepoint = str.codePointAt(0);
final int newCodePoint = firstCharFun.apply(firstCodepoint);
if (firstCodepoint == newCodePoint) {
// already capitalized
return str;
}
// cannot be longer than the char array
final int[] newCodePoints = new int[strLen];
int outOffset = 0;
// copy the first codepoint
newCodePoints[outOffset++] = newCodePoint;
for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen; ) {
final int codepoint = str.codePointAt(inOffset);
// copy the remaining ones
newCodePoints[outOffset++] = codepoint;
inOffset += Character.charCount(codepoint);
}
return new String(newCodePoints, 0, outOffset);
};
public static String isNullOrEmpty(String str) {
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
return "";
}
return str;
}
public static String isNull(String str) {
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
return "--";
}
return str;
}
public static String isNullForHtml(String str) {
if (StringUtils.isEmpty(str) || EMPTY_STR.equals(str)) {
return "<br>";
}
return str;
}
/**
* 判断是空字符串
*
* @param cs 字符串
* @return 是否为空
*/
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* 首字母大写方法
*
* @param str 字符串
* @return 首字母大写结果
*/
public static String capitalize(final String str) {
return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toTitleCase);
}
/**
* 首字母小写方法
*
* @param str 字符串
* @return 首字母小写结果
*/
public static String uncapitalize(final String str) {
return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toLowerCase);
}
}