package com.vincent.rsf.framework.generators; import com.vincent.rsf.framework.common.Cools; import com.vincent.rsf.framework.generators.constant.SqlOsType; import com.vincent.rsf.framework.generators.domain.Column; import com.vincent.rsf.framework.generators.utils.GeneratorUtils; import org.springframework.core.io.ClassPathResource; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; public class RsfDesignGenerator { private static final String BASE_DIR = "src/main/"; private static final String JAVA_DIR = BASE_DIR + "java/"; private static final String MODULES_DIR = "modules/"; private static final List MANAGED_FIELDS = Arrays.asList( "deleted", "tenantId", "createBy", "createTime", "updateBy", "updateTime", "version" ); private static final List SEARCH_EXCLUDED_FIELDS = Arrays.asList( "deleted", "tenantId", "createBy", "createTime", "updateBy", "updateTime", "version" ); public String url; public String username; public String password; public String table; public String tableDesc; public String packagePath; public boolean controller = true; public boolean service = true; public boolean mapper = true; public boolean entity = true; public boolean xml = true; public boolean frontend = true; public boolean sql = true; public SqlOsType sqlOsType; public String backendPrefixPath; public String frontendPrefixPath; public String frontendViewPath; public String frontendApiModule; private List columns = new ArrayList<>(); private String fullEntityName; private String simpleEntityName; private String kebabEntityName; private String constantPrefix; private String primaryKeyColumn; private String majorColumn; private String itemName; private String normalizedFrontendViewPath; private String normalizedFrontendApiModule; public void build() throws Exception { init(); buildBackendArtifacts(); if (controller) { writeTemplate("Controller", resolveControllerDirectory(), fullEntityName + "Controller.java"); } if (frontend) { buildFrontendArtifacts(); } } private void init() throws Exception { validateBaseConfig(); gainDbInfo(); fullEntityName = GeneratorUtils.getNameSpace(table); simpleEntityName = GeneratorUtils.firstCharConvert(fullEntityName, true); kebabEntityName = humpToKebab(simpleEntityName); constantPrefix = GeneratorUtils.humpToLine(simpleEntityName).toUpperCase(); primaryKeyColumn = resolvePrimaryKeyColumn(); majorColumn = resolveMajorColumn(); String[] packagePathSplit = packagePath.split("\\."); itemName = packagePathSplit[packagePathSplit.length - 1]; normalizedFrontendViewPath = normalizePath(frontendViewPath); normalizedFrontendApiModule = normalizeApiModule(frontendApiModule); } private void validateBaseConfig() { if (this.sqlOsType == null) { throw new RuntimeException("请选择sqlOsType!"); } if (Cools.isEmpty(this.table)) { throw new RuntimeException("请输入table!"); } if (Cools.isEmpty(this.tableDesc)) { throw new RuntimeException("请输入tableDesc!"); } if (Cools.isEmpty(this.packagePath)) { throw new RuntimeException("请输入packagePath!"); } if (frontend) { if (Cools.isEmpty(frontendPrefixPath)) { throw new RuntimeException("请输入frontendPrefixPath!"); } if (Cools.isEmpty(frontendViewPath)) { throw new RuntimeException("请输入frontendViewPath!"); } if (Cools.isEmpty(frontendApiModule)) { throw new RuntimeException("请输入frontendApiModule!"); } } } private void gainDbInfo() throws Exception { Connection connection = null; try { switch (this.sqlOsType) { case MYSQL: Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); connection = DriverManager.getConnection("jdbc:mysql://" + url, username, password); this.columns = ReactGenerator.getMysqlColumns(connection, table, true, sqlOsType); break; case SQL_SERVER: Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver").newInstance(); connection = DriverManager.getConnection("jdbc:sqlserver://" + url, username, password); this.columns = ReactGenerator.getSqlServerColumns(connection, table, true, sqlOsType); break; default: throw new RuntimeException("请指定数据库类型!"); } } finally { if (connection != null) { try { connection.close(); } catch (Exception ignore) { } } } } private void buildBackendArtifacts() throws Exception { ReactGenerator generator = new ReactGenerator(); generator.url = url; generator.username = username; generator.password = password; generator.table = table; generator.tableDesc = tableDesc; generator.packagePath = packagePath; generator.controller = false; generator.service = service; generator.mapper = mapper; generator.entity = entity; generator.xml = xml; generator.react = false; generator.sql = sql; generator.sqlOsType = sqlOsType; generator.backendPrefixPath = backendPrefixPath; generator.frontendPrefixPath = frontendPrefixPath; generator.build(); } private void buildFrontendArtifacts() throws IOException { String pageDirectory = resolveFrontendPageDirectory(); String modulesDirectory = pageDirectory + MODULES_DIR; writeTemplate("Index", pageDirectory, "index.vue"); writeTemplate("PageHelpers", pageDirectory, simpleEntityName + "Page.helpers.js"); writeTemplate("TableColumns", pageDirectory, simpleEntityName + "Table.columns.js"); writeTemplate("Search", modulesDirectory, kebabEntityName + "-search.vue"); writeTemplate("EditDialog", modulesDirectory, kebabEntityName + "-edit-dialog.vue"); writeTemplate("Api", resolveFrontendApiDirectory(), normalizedFrontendApiModule + ".js"); } private String resolveControllerDirectory() { return ensureTrailingSlash(backendPrefixPath) + JAVA_DIR + packagePath.replace(".", "/") + "/controller/"; } private String resolveFrontendPageDirectory() { return ensureTrailingSlash(frontendPrefixPath) + "src/views/" + normalizedFrontendViewPath + "/"; } private String resolveFrontendApiDirectory() { String directory = ensureTrailingSlash(frontendPrefixPath) + "src/api/"; int index = normalizedFrontendApiModule.lastIndexOf('/'); if (index < 0) { return directory; } return directory + normalizedFrontendApiModule.substring(0, index + 1); } private void writeTemplate(String templateName, String directory, String fileName) throws IOException { String content = readTemplate(templateName); writeFile(applyReplacements(content), directory, fileName, templateName); } private String readTemplate(String templateName) throws IOException { StringBuilder builder = new StringBuilder(); ClassPathResource classPath = new ClassPathResource("templates/rsf-design/" + templateName + ".txt"); try (BufferedReader reader = new BufferedReader( new InputStreamReader(classPath.getInputStream(), StandardCharsets.UTF_8))) { String lineContent; while ((lineContent = reader.readLine()) != null) { builder.append(lineContent).append("\n"); } } return builder.toString(); } private String applyReplacements(String content) { Map replacements = buildReplacements(); String resolved = content; for (Map.Entry entry : replacements.entrySet()) { resolved = resolved.replace("@{" + entry.getKey() + "}", Objects.toString(entry.getValue(), "")); } return resolved; } private Map buildReplacements() { Map replacements = new LinkedHashMap<>(); replacements.put("TABLENAME", table); replacements.put("TABLEDESC", safeText(tableDesc)); replacements.put("ENTITYNAME", fullEntityName); replacements.put("SIMPLEENTITYNAME", simpleEntityName); replacements.put("UENTITYNAME", GeneratorUtils.firstCharConvert(simpleEntityName, false)); replacements.put("KEBABENTITYNAME", kebabEntityName); replacements.put("COMPANYNAME", packagePath); replacements.put("ITEMNAME", itemName); replacements.put("PRIMARYKEYCOLUMN", GeneratorUtils.firstCharConvert(primaryKeyColumn, false)); replacements.put("PRIMARYKEYCOLUMN0", GeneratorUtils.firstCharConvert(primaryKeyColumn, true)); replacements.put("MAJORCOLUMN", GeneratorUtils.firstCharConvert(majorColumn, false)); replacements.put("MAJORCOLUMN0", GeneratorUtils.firstCharConvert(majorColumn, true)); replacements.put("ENTITYPREFIX", constantPrefix); replacements.put("APIMODULE", normalizedFrontendApiModule); replacements.put("SEARCHSTATECONTENT", buildSearchStateContent()); replacements.put("FORMSTATECONTENT", buildFormStateContent()); replacements.put("FIELDOPTIONSCONTENT", buildFieldOptionsContent()); replacements.put("SEARCHPARAMSCONTENT", buildSearchParamsContent()); replacements.put("SAVEPAYLOADCONTENT", buildSavePayloadContent()); replacements.put("DIALOGMODELCONTENT", buildDialogModelContent()); replacements.put("NORMALIZEROWCONTENT", buildNormalizeRowContent()); replacements.put("REPORTCOLUMNSCONTENT", buildReportColumnsContent()); replacements.put("REPORTSOURCEALIASCONTENT", buildReportSourceAliasContent()); replacements.put("SEARCHITEMSCONTENT", buildSearchItemsContent()); replacements.put("FORMITEMSCONTENT", buildFormItemsContent()); replacements.put("RULESCONTENT", buildRulesContent()); replacements.put("TABLECOLUMNSCONTENT", buildTableColumnsContent()); replacements.put("EXPORTROWCONTENT", buildExportRowContent()); replacements.put("SAVEINITCONTENT", buildSaveInitContent()); replacements.put("UPDATEINITCONTENT", buildUpdateInitContent()); return replacements; } private void writeFile(String content, String directory, String fileName, String templateName) throws IOException { File codeDirectory = new File(directory); if (!codeDirectory.exists()) { codeDirectory.mkdirs(); } File writerFile = new File(directory + fileName); if (writerFile.exists()) { System.out.println(fullEntityName + templateName + " 源文件已经存在创建失败!"); return; } writerFile.createNewFile(); try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(writerFile), StandardCharsets.UTF_8))) { writer.write(content); writer.flush(); } System.out.println(fullEntityName + templateName + " 源文件创建成功!"); } private String resolvePrimaryKeyColumn() { for (Column column : columns) { if (column.isPrimaryKey() || column.isMainKey()) { return column.getHumpName(); } } return "id"; } private String resolveMajorColumn() { for (Column column : columns) { if (column.isMajor()) { return column.getHumpName(); } } for (String preferred : Arrays.asList("name", "code", "title", "uuid")) { Column column = findColumn(preferred); if (column != null) { return column.getHumpName(); } } for (Column column : columns) { if (!MANAGED_FIELDS.contains(column.getHumpName()) && !column.isPrimaryKey()) { return column.getHumpName(); } } return primaryKeyColumn; } private Column findColumn(String humpName) { for (Column column : columns) { if (humpName.equals(column.getHumpName())) { return column; } } return null; } private boolean hasColumn(String humpName) { return findColumn(humpName) != null; } private boolean isManagedColumn(Column column) { return MANAGED_FIELDS.contains(column.getHumpName()); } private boolean isSearchExcludedColumn(Column column) { return SEARCH_EXCLUDED_FIELDS.contains(column.getHumpName()); } private boolean isBooleanColumn(Column column) { return "Boolean".equals(column.getType()); } private boolean isNumericColumn(Column column) { return "Short".equals(column.getType()) || "Integer".equals(column.getType()) || "Long".equals(column.getType()) || "Double".equals(column.getType()); } private boolean isDateColumn(Column column) { return "Date".equals(column.getType()); } private boolean hasEnumOptions(Column column) { return !Cools.isEmpty(column.getEnums()); } private boolean hasForeignDisplay(Column column) { return !Cools.isEmpty(column.getForeignKeyMajor()); } private boolean isStatusColumn(Column column) { return "status".equals(column.getHumpName()); } private boolean isMemoColumn(Column column) { return "memo".equals(column.getHumpName()); } private boolean isSelectableColumn(Column column) { return isBooleanColumn(column) || hasEnumOptions(column); } private boolean isDisplayTextColumn(Column column) { return isBooleanColumn(column) || isDateColumn(column) || hasEnumOptions(column) || hasForeignDisplay(column); } private List getSearchColumns() { List result = new ArrayList<>(); for (Column column : columns) { if (column.isPrimaryKey() || isSearchExcludedColumn(column) || isDateColumn(column)) { continue; } result.add(column); } return result; } private List getFormColumns() { List result = new ArrayList<>(); for (Column column : columns) { if (column.isPrimaryKey() || isManagedColumn(column)) { continue; } result.add(column); } return result; } private List getListColumns() { List result = new ArrayList<>(); for (Column column : columns) { if (column.isPrimaryKey()) { continue; } if ("deleted".equals(column.getHumpName()) || "tenantId".equals(column.getHumpName())) { continue; } result.add(column); } return result; } private List getReportColumns() { return getListColumns(); } private List getPayloadColumns() { List result = new ArrayList<>(); Column primaryColumn = findColumn(primaryKeyColumn); if (primaryColumn != null) { result.add(primaryColumn); } Column versionColumn = findColumn("version"); if (versionColumn != null) { result.add(versionColumn); } result.addAll(getFormColumns()); return result; } private String buildSearchStateContent() { StringBuilder sb = new StringBuilder(); for (Column column : getSearchColumns()) { sb.append(" ").append(column.getHumpName()).append(": '',\n"); } return trimTrailingLineBreak(sb); } private String buildFormStateContent() { StringBuilder sb = new StringBuilder(); for (Column column : getPayloadColumns()) { sb.append(" ") .append(column.getHumpName()) .append(": ") .append(resolveFormDefaultValue(column)) .append(",\n"); } return trimTrailingLineBreak(sb); } private String buildFieldOptionsContent() { StringBuilder sb = new StringBuilder(); sb.append("{\n"); boolean hasOptions = false; for (Column column : columns) { if (!isSelectableColumn(column)) { continue; } hasOptions = true; sb.append(" ").append(column.getHumpName()).append(": [\n"); for (Map option : buildColumnOptions(column)) { sb.append(" { label: '") .append(escapeJs(String.valueOf(option.get("label")))) .append("', value: ") .append(option.get("value")) .append(" },\n"); } sb.append(" ],\n"); } if (!hasOptions) { return "{}"; } sb.append("}"); return sb.toString(); } private String buildSearchParamsContent() { StringBuilder sb = new StringBuilder(); for (Column column : getSearchColumns()) { sb.append(" ") .append(column.getHumpName()) .append(": ") .append(resolveParamNormalizer("params", column)) .append(",\n"); } return trimTrailingLineBreak(sb); } private String buildSavePayloadContent() { StringBuilder sb = new StringBuilder(); for (Column column : getPayloadColumns()) { sb.append(resolvePayloadLine("formData", column)); } return trimTrailingLineBreak(sb); } private String buildDialogModelContent() { StringBuilder sb = new StringBuilder(); for (Column column : getPayloadColumns()) { sb.append(" ") .append(column.getHumpName()) .append(": ") .append(resolveDialogValue(column)) .append(",\n"); } return trimTrailingLineBreak(sb); } private String buildNormalizeRowContent() { StringBuilder sb = new StringBuilder(); Column statusColumn = findColumn("status"); if (statusColumn != null) { sb.append(" statusBool: record.statusBool !== void 0 ? Boolean(record.statusBool) : statusMeta.bool,\n") .append(" statusText: normalizeText(record.statusText || record.status$ || statusMeta.text),\n") .append(" statusType: statusMeta.type,\n"); } for (Column column : getReportColumns()) { if (isStatusColumn(column)) { continue; } if (isBooleanColumn(column)) { sb.append(" ") .append(column.getHumpName()) .append("Text: formatBooleanText(toOptionalBoolean(record.") .append(column.getHumpName()) .append(")),\n"); continue; } if (isDisplayTextColumn(column)) { sb.append(" ") .append(column.getHumpName()) .append("Text: normalizeText(record.") .append(column.getHumpName()) .append("$ || record.") .append(column.getHumpName()) .append("Text || record.") .append(column.getHumpName()) .append("),\n"); } } return trimTrailingLineBreak(sb); } private String buildReportColumnsContent() { StringBuilder sb = new StringBuilder(); for (Column column : getReportColumns()) { sb.append(" { source: '") .append(resolveReportSource(column)) .append("', label: '") .append(escapeJs(resolveFieldLabel(column))) .append("' },\n"); } return trimTrailingLineBreak(sb); } private String buildReportSourceAliasContent() { Column statusColumn = findColumn("status"); if (statusColumn == null) { return ""; } return " status: 'statusText'"; } private String buildSearchItemsContent() { StringBuilder sb = new StringBuilder(); for (Column column : getSearchColumns()) { if (isSelectableColumn(column)) { sb.append(" createSelectSearchItem('") .append(escapeJs(resolveFieldLabel(column))) .append("', '") .append(column.getHumpName()) .append("', '请选择") .append(escapeJs(resolveFieldLabel(column))) .append("', get") .append(fullEntityName) .append("FieldOptions('") .append(column.getHumpName()) .append("')),\n"); } else { sb.append(" createInputSearchItem('") .append(escapeJs(resolveFieldLabel(column))) .append("', '") .append(column.getHumpName()) .append("', '请输入") .append(escapeJs(resolveFieldLabel(column))) .append("'),\n"); } } return trimTrailingLineBreak(sb); } private String buildFormItemsContent() { StringBuilder sb = new StringBuilder(); for (Column column : getFormColumns()) { String label = escapeJs(resolveFieldLabel(column)); String key = column.getHumpName(); if (isSelectableColumn(column)) { sb.append(" createSelectFormItem('") .append(label) .append("', '") .append(key) .append("', '请选择") .append(label) .append("', get") .append(fullEntityName) .append("FieldOptions('") .append(key) .append("')),\n"); continue; } if (isMemoColumn(column) || (column.getLength() != null && column.getLength() > 255)) { sb.append(" createInputFormItem('") .append(label) .append("', '") .append(key) .append("', '请输入") .append(label) .append("', { type: 'textarea', rows: 3 }, { span: 24 }),\n"); continue; } if (isNumericColumn(column)) { sb.append(" createInputFormItem('") .append(label) .append("', '") .append(key) .append("', '请输入") .append(label) .append("', { type: 'number' }),\n"); continue; } sb.append(" createInputFormItem('") .append(label) .append("', '") .append(key) .append("', '请输入") .append(label) .append("'),\n"); } return trimTrailingLineBreak(sb); } private String buildRulesContent() { StringBuilder sb = new StringBuilder(); for (Column column : getFormColumns()) { if (!column.isNotNull()) { continue; } String label = escapeJs(resolveFieldLabel(column)); String message = (isSelectableColumn(column) ? "请选择" : "请输入") + label; String trigger = isSelectableColumn(column) ? "change" : "blur"; sb.append(" ") .append(column.getHumpName()) .append(": [{ required: true, message: '") .append(message) .append("', trigger: '") .append(trigger) .append("' }],\n"); } return trimTrailingLineBreak(sb); } private String buildTableColumnsContent() { StringBuilder sb = new StringBuilder(); for (Column column : getListColumns()) { if (isStatusColumn(column)) { sb.append(" createTagColumn('status', '") .append(escapeJs(resolveFieldLabel(column))) .append("', 120, (row) => get") .append(fullEntityName) .append("StatusMeta(row.statusBool ?? row.status)),\n"); continue; } if (isNumericColumn(column)) { sb.append(" createNumberColumn('") .append(column.getHumpName()) .append("', '") .append(escapeJs(resolveFieldLabel(column))) .append("', 120),\n"); continue; } if (isDisplayTextColumn(column)) { sb.append(" createTextColumn('") .append(column.getHumpName()) .append("Text', '") .append(escapeJs(resolveFieldLabel(column))) .append("', ") .append(resolveTextColumnWidth(column)) .append("),\n"); continue; } sb.append(" createTextColumn('") .append(column.getHumpName()) .append("', '") .append(escapeJs(resolveFieldLabel(column))) .append("', ") .append(resolveTextColumnWidth(column)) .append("),\n"); } return trimTrailingLineBreak(sb); } private String buildExportRowContent() { StringBuilder sb = new StringBuilder(); for (Column column : getReportColumns()) { sb.append(" row.put(\"") .append(resolveReportSource(column)) .append("\", ") .append(resolveExportExpression(column)) .append(");\n"); } return trimTrailingLineBreak(sb); } private String buildSaveInitContent() { StringBuilder sb = new StringBuilder(); if (hasColumn("createBy")) { sb.append(" ").append(simpleEntityName).append(".setCreateBy(getLoginUserId());\n"); } if (hasColumn("createTime")) { sb.append(" ").append(simpleEntityName).append(".setCreateTime(new Date());\n"); } if (hasColumn("updateBy")) { sb.append(" ").append(simpleEntityName).append(".setUpdateBy(getLoginUserId());\n"); } if (hasColumn("updateTime")) { sb.append(" ").append(simpleEntityName).append(".setUpdateTime(new Date());\n"); } return trimTrailingLineBreak(sb); } private String buildUpdateInitContent() { StringBuilder sb = new StringBuilder(); if (hasColumn("updateBy")) { sb.append(" ").append(simpleEntityName).append(".setUpdateBy(getLoginUserId());\n"); } if (hasColumn("updateTime")) { sb.append(" ").append(simpleEntityName).append(".setUpdateTime(new Date());\n"); } return trimTrailingLineBreak(sb); } private List> buildColumnOptions(Column column) { List> options = new ArrayList<>(); if (isBooleanColumn(column) && Cools.isEmpty(column.getEnums())) { options.add(buildOption("是", "true")); options.add(buildOption("否", "false")); return options; } if (Cools.isEmpty(column.getEnums())) { return options; } for (Map item : column.getEnums()) { for (Map.Entry entry : item.entrySet()) { options.add(buildOption(String.valueOf(entry.getValue()), toJsValue(column, String.valueOf(entry.getKey())))); } } return options; } private Map buildOption(String label, String jsValue) { Map option = new LinkedHashMap<>(); option.put("label", label); option.put("value", jsValue); return option; } private String resolveFormDefaultValue(Column column) { if (column == null) { return "void 0"; } if (isStatusColumn(column) && isSelectableColumn(column)) { List> options = buildColumnOptions(column); if (!options.isEmpty()) { return String.valueOf(options.get(0).get("value")); } } if (isBooleanColumn(column) || isNumericColumn(column)) { return "void 0"; } return "''"; } private String resolveParamNormalizer(String sourceName, Column column) { String field = sourceName + "." + column.getHumpName(); if (isBooleanColumn(column)) { return "toOptionalBoolean(" + field + ")"; } if (isNumericColumn(column)) { return "toOptionalNumber(" + field + ")"; } return "normalizeText(" + field + ")"; } private String resolvePayloadLine(String sourceName, Column column) { String field = sourceName + "." + column.getHumpName(); if (isBooleanColumn(column)) { return " ...(hasValue(" + field + ") ? { " + column.getHumpName() + ": toOptionalBoolean(" + field + ") } : {}),\n"; } if (isNumericColumn(column)) { return " ...buildNumberField('" + column.getHumpName() + "', " + field + "),\n"; } return " " + column.getHumpName() + ": normalizeText(" + field + ") || '',\n"; } private String resolveDialogValue(Column column) { String field = "record." + column.getHumpName(); if (isStatusColumn(column) && isSelectableColumn(column)) { List> options = buildColumnOptions(column); String fallback = options.isEmpty() ? "void 0" : String.valueOf(options.get(0).get("value")); if (isBooleanColumn(column)) { return "hasValue(" + field + ") ? toOptionalBoolean(" + field + ") : " + fallback; } if (isNumericColumn(column)) { return "hasValue(" + field + ") ? toOptionalNumber(" + field + ") : " + fallback; } return "normalizeText(" + field + ") || " + fallback; } if (isBooleanColumn(column)) { return "toOptionalBoolean(" + field + ")"; } if (isNumericColumn(column)) { return "toOptionalNumber(" + field + ")"; } return "normalizeText(" + field + " || '')"; } private String resolveFieldLabel(Column column) { if (column == null || Cools.isEmpty(column.getComment())) { return column == null ? "" : column.getHumpName(); } return column.getComment().trim(); } private int resolveTextColumnWidth(Column column) { if (isMemoColumn(column)) { return 220; } if (isDateColumn(column)) { return 180; } return 160; } private String resolveReportSource(Column column) { if (isStatusColumn(column) || isDisplayTextColumn(column)) { return column.getHumpName() + "Text"; } return column.getHumpName(); } private String resolveExportExpression(Column column) { String getter = "record." + getterName(column); if (isBooleanColumn(column)) { return "Boolean.TRUE.equals(" + getter + "()) ? \"是\" : Boolean.FALSE.equals(" + getter + "()) ? \"否\" : \"\""; } if (isStatusColumn(column) || isDisplayTextColumn(column)) { return getter + "$()"; } return getter + "()"; } private String getterName(Column column) { return "get" + GeneratorUtils.firstCharConvert(column.getHumpName(), false); } private String toJsValue(Column column, String rawValue) { if (isBooleanColumn(column)) { if ("1".equals(rawValue) || "true".equalsIgnoreCase(rawValue)) { return "true"; } if ("0".equals(rawValue) || "false".equalsIgnoreCase(rawValue)) { return "false"; } } if (isNumericColumn(column)) { return rawValue; } return "'" + escapeJs(rawValue) + "'"; } private String trimTrailingLineBreak(StringBuilder sb) { if (sb.length() == 0) { return ""; } while (sb.length() > 0 && (sb.charAt(sb.length() - 1) == '\n' || sb.charAt(sb.length() - 1) == '\r')) { sb.deleteCharAt(sb.length() - 1); } if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ',') { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } private String safeText(String value) { return value == null ? "" : value.trim(); } private String escapeJs(String value) { return value .replace("\\", "\\\\") .replace("'", "\\'"); } private String humpToKebab(String value) { if (Cools.isEmpty(value)) { return ""; } return value.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(); } private String ensureTrailingSlash(String prefix) { if (Cools.isEmpty(prefix)) { return ""; } return prefix.endsWith("/") ? prefix : prefix + "/"; } private String normalizePath(String value) { if (Cools.isEmpty(value)) { return ""; } String normalized = value.replace("\\", "/"); while (normalized.startsWith("/")) { normalized = normalized.substring(1); } while (normalized.endsWith("/")) { normalized = normalized.substring(0, normalized.length() - 1); } return normalized; } private String normalizeApiModule(String value) { String normalized = normalizePath(value); if (normalized.endsWith(".js")) { return normalized.substring(0, normalized.length() - 3); } return normalized; } }