diff --git a/generator-web/pom.xml b/generator-web/pom.xml index c03aca64..c6ace661 100644 --- a/generator-web/pom.xml +++ b/generator-web/pom.xml @@ -17,7 +17,6 @@ UTF-8 UTF-8 - 1.8 diff --git a/generator-web/src/main/java/com/softdev/system/generator/entity/NonCaseString.java b/generator-web/src/main/java/com/softdev/system/generator/entity/NonCaseString.java new file mode 100644 index 00000000..4472156b --- /dev/null +++ b/generator-web/src/main/java/com/softdev/system/generator/entity/NonCaseString.java @@ -0,0 +1,152 @@ +package com.softdev.system.generator.entity; + +import lombok.AllArgsConstructor; + +/** + * String 包装类 + *

+ * 忽略大小写 + **考虑增加这个类是, 如果在 StringUtils 中加工具方法, 使用起来代码非常冗长且不方便 + * @author Nisus + * @see String + */ +@AllArgsConstructor +public class NonCaseString implements CharSequence { + private String value; + + public static NonCaseString of(String str) { + assert str != null; + return new NonCaseString(str); + } + + + /** + * {@link String#indexOf(String)} 增强, 忽略大小写 + */ + public int indexOf(String m) { + String text = this.value; + if (text == null || m == null || text.length() < m.length()) { + return -1; + } + return text.toLowerCase().indexOf(m.toLowerCase()); + } + + /** + * {@link String#lastIndexOf(String)} 增强, 忽略大小写 + */ + public int lastIndexOf(String m) { + String text = this.value; + if (text == null || m == null || text.length() < m.length()) { + return -1; + } + return text.toLowerCase().lastIndexOf(m.toLowerCase()); + } + + /** + * 是否包含, 大小写不敏感 + *

 true
+     * "abcxyz" 包含 "ABC" => true
+     * "abcxyz" 包含 "aBC" => true
+     * 
+ * + * @param m 被包含字符串 + */ + public boolean contains(String m) { + String text = this.value; + if (text.length() < m.length()) { + return false; + } + return text.toLowerCase().contains(m.toLowerCase()); + } + + /** + * 任意一个包含返回true + *
+     * containsAny("abcdef", "a", "b)
+     * 等价
+     * "abcdef".contains("a") || "abcdef".contains("b")
+     * 
+ * + * @param matchers 多个要判断的被包含项 + */ + public boolean containsAny(String... matchers) { + for (String matcher : matchers) { + if (contains(matcher)) { + return true; + } + } + return false; + } + + /** + * 所有都包含才返回true + * + * @param matchers 多个要判断的被包含项 + */ + public boolean containsAllIgnoreCase(String... matchers) { + for (String matcher : matchers) { + if (contains(matcher) == false) { + return false; + } + } + return true; + } + + public NonCaseString trim() { + return NonCaseString.of(this.value.trim()); + } + + public NonCaseString replace(char oldChar, char newChar) { + return NonCaseString.of(this.value.replace(oldChar, newChar)); + } + + public NonCaseString replaceAll(String regex, String replacement) { + return NonCaseString.of(this.value.replaceAll(regex, replacement)); + } + + public NonCaseString substring(int beginIndex) { + return NonCaseString.of(this.value.substring(beginIndex)); + } + + public NonCaseString substring(int beginIndex, int endIndex) { + return NonCaseString.of(this.value.substring(beginIndex, endIndex)); + } + + public boolean isNotEmpty() { + return !this.value.isEmpty(); + } + + @Override + public int length() { + return this.value.length(); + } + + @Override + public char charAt(int index) { + return this.value.charAt(index); + } + + + @Override + public CharSequence subSequence(int start, int end) { + return this.value.subSequence(start, end); + } + + public String[] split(String regex) { + return this.value.split(regex); + } + + + /** + * @return 原始字符串 + */ + public String get() { + return this.value; + } + + @Override + public String toString() { + return this.value; + } +} diff --git a/generator-web/src/main/java/com/softdev/system/generator/util/StringUtils.java b/generator-web/src/main/java/com/softdev/system/generator/util/StringUtils.java index f123ed01..061d94ae 100644 --- a/generator-web/src/main/java/com/softdev/system/generator/util/StringUtils.java +++ b/generator-web/src/main/java/com/softdev/system/generator/util/StringUtils.java @@ -40,7 +40,7 @@ public static String underlineToCamelCase(String underscoreName) { boolean flag = false; for (int i = 0; i < underscoreName.length(); i++) { char ch = underscoreName.charAt(i); - if ("_".charAt(0) == ch) { + if ('_' == ch) { flag = true; } else { if (flag) { @@ -54,6 +54,42 @@ public static String underlineToCamelCase(String underscoreName) { } return result.toString(); } + + /** + * 转 user_name 风格 + * + * 不管原始是什么风格 + */ + public static String toUnderline(String str, boolean upperCase) { + if (str == null || str.trim().isEmpty()) { + return str; + } + + StringBuilder result = new StringBuilder(); + boolean preIsUnderscore = false; + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch == '_') { + preIsUnderscore = true; + } else if (ch == '-') { + ch = '_'; + preIsUnderscore = true; // -A -> _a + } else if (ch >= 'A' && ch <= 'Z') { + // A -> _a + if (!preIsUnderscore && i > 0) { // _A -> _a + result.append("_"); + } + preIsUnderscore = false; + } else { + preIsUnderscore = false; + } + result.append(upperCase ? Character.toUpperCase(ch) : Character.toLowerCase(ch)); + } + + return result.toString(); + } + + public static boolean isNotNull(String str){ return org.apache.commons.lang3.StringUtils.isNotEmpty(str); } diff --git a/generator-web/src/main/java/com/softdev/system/generator/util/TableParseUtil.java b/generator-web/src/main/java/com/softdev/system/generator/util/TableParseUtil.java index 979a8a04..7104ad83 100644 --- a/generator-web/src/main/java/com/softdev/system/generator/util/TableParseUtil.java +++ b/generator-web/src/main/java/com/softdev/system/generator/util/TableParseUtil.java @@ -1,18 +1,16 @@ package com.softdev.system.generator.util; -import com.softdev.system.generator.util.mysqlJavaTypeUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.softdev.system.generator.entity.ClassInfo; import com.softdev.system.generator.entity.FieldInfo; +import com.softdev.system.generator.entity.NonCaseString; import com.softdev.system.generator.entity.ParamInfo; import java.io.IOException; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; -import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; @@ -34,7 +32,7 @@ public class TableParseUtil { public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) throws IOException { //process the param - String tableSql = paramInfo.getTableSql(); + NonCaseString tableSql = NonCaseString.of(paramInfo.getTableSql()); String nameCaseType = MapUtil.getString(paramInfo.getOptions(),"nameCaseType"); Boolean isPackageType = MapUtil.getBoolean(paramInfo.getOptions(),"isPackageType"); @@ -42,15 +40,20 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) throw new CodeGenerateException("Table structure can not be empty. 表结构不能为空。"); } //deal with special character - tableSql = tableSql.trim().replaceAll("'", "`").replaceAll("\"", "`").replaceAll(",", ",").toLowerCase(); + tableSql = tableSql.trim() + .replaceAll("'", "`") + .replaceAll("\"", "`") + .replaceAll(",", ",") + // 这里全部转小写, 会让驼峰风格的字段名丢失驼峰信息(真有驼峰sql字段名的呢(* ̄︶ ̄)); 下文使用工具方法处理包含等 + // .toLowerCase() + ; //deal with java string copy \n" tableSql = tableSql.trim().replaceAll("\\\\n`", "").replaceAll("\\+", "").replaceAll("``", "`").replaceAll("\\\\", ""); // table Name String tableName = null; - if (tableSql.contains("TABLE") && tableSql.contains("(")) { - tableName = tableSql.substring(tableSql.indexOf("TABLE") + 5, tableSql.indexOf("(")); - } else if (tableSql.contains("table") && tableSql.contains("(")) { - tableName = tableSql.substring(tableSql.indexOf("table") + 5, tableSql.indexOf("(")); + int tableKwIx = tableSql.indexOf("TABLE"); // 包含判断和位置一次搞定 + if (tableKwIx > -1 && tableSql.contains("(")) { + tableName = tableSql.substring(tableKwIx + 5, tableSql.indexOf("(")).get(); } else { throw new CodeGenerateException("Table structure incorrect.表结构不正确。"); } @@ -88,9 +91,11 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) String classComment = null; //mysql是comment=,pgsql/oracle是comment on table, //2020-05-25 优化表备注的获取逻辑 - if (tableSql.contains("comment=") || tableSql.contains("comment on table")) { - String classCommentTmp = (tableSql.contains("comment=")) ? - tableSql.substring(tableSql.lastIndexOf("comment=") + 8).trim() : tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim(); + if (tableSql.containsAny("comment=", "comment on table")) { + int ix = tableSql.lastIndexOf("comment="); + String classCommentTmp = (ix > -1) ? + tableSql.substring(ix + 8).trim().get() : + tableSql.substring(tableSql.lastIndexOf("comment on table") + 17).trim().get(); if (classCommentTmp.contains("`")) { classCommentTmp = classCommentTmp.substring(classCommentTmp.indexOf("`") + 1); classCommentTmp = classCommentTmp.substring(0, classCommentTmp.indexOf("`")); @@ -109,7 +114,7 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) List fieldList = new ArrayList(); // 正常( ) 内的一定是字段相关的定义。 - String fieldListTmp = tableSql.substring(tableSql.indexOf("(") + 1, tableSql.lastIndexOf(")")); + String fieldListTmp = tableSql.substring(tableSql.indexOf("(") + 1, tableSql.lastIndexOf(")")).get(); // 匹配 comment,替换备注里的小逗号, 防止不小心被当成切割符号切割 String commentPattenStr1 = "comment `(.*?)\\`"; @@ -149,7 +154,8 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) if (fieldLineList.length > 0) { int i = 0; //i为了解决primary key关键字出现的地方,出现在前3行,一般和id有关 - for (String columnLine : fieldLineList) { + for (String columnLine0 : fieldLineList) { + NonCaseString columnLine = NonCaseString.of(columnLine0); i++; columnLine = columnLine.replaceAll("\n", "").replaceAll("\t", "").trim(); // `userid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID', @@ -158,13 +164,10 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) // 2019-2-22 zhengkai 要在条件中使用复杂的表达式 // 2019-4-29 zhengkai 优化对普通和特殊storage关键字的判断(感谢@AhHeadFloating的反馈 ) // 2020-10-20 zhengkai 优化对fulltext/index关键字的处理(感谢@WEGFan的反馈) - boolean specialFlag = (!columnLine.contains("key ") && !columnLine.contains("constraint") && !columnLine.contains("using") && !columnLine.contains("unique ") - && !(columnLine.contains("primary ") && columnLine.indexOf("storage") + 3 > columnLine.indexOf("(")) - && !columnLine.contains("fulltext ") && !columnLine.contains("index ") - && !columnLine.contains("pctincrease") - && !columnLine.contains("buffer_pool") && !columnLine.contains("tablespace") - && !(columnLine.contains("primary ") && i > 3)); - if (specialFlag) { + // 2023-8-27 zhangfei 改用工具方法判断, 且修改变量名(非特殊标识), 方法抽取 + boolean notSpecialFlag = isNotSpecialColumnLine(columnLine, i); + + if (notSpecialFlag) { //如果是oracle的number(x,x),可能出现最后分割残留的,x),这里做排除处理 if (columnLine.length() < 5) { continue; @@ -174,7 +177,7 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) columnLine = columnLine.replaceAll("`", " ").replaceAll("\"", " ").replaceAll("'", "").replaceAll(" ", " ").trim(); //如果遇到username varchar(65) default '' not null,这种情况,判断第一个空格是否比第一个引号前 try { - columnName = columnLine.substring(0, columnLine.indexOf(" ")); + columnName = columnLine.substring(0, columnLine.indexOf(" ")).get(); } catch (StringIndexOutOfBoundsException e) { System.out.println("err happened: " + columnLine); throw e; @@ -182,6 +185,7 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) // field Name // 2019-09-08 yj 添加是否下划线转换为驼峰的判断 + // 2023-8-27 zhangfei 支持原始列名任意命名风格, 不依赖用户是否输入下划线 String fieldName = null; if (ParamInfo.NAME_CASE_TYPE.CAMEL_CASE.equals(nameCaseType)) { fieldName = StringUtils.lowerCaseFirst(StringUtils.underlineToCamelCase(columnName)); @@ -189,9 +193,9 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) fieldName = fieldName.replaceAll("_", ""); } } else if (ParamInfo.NAME_CASE_TYPE.UNDER_SCORE_CASE.equals(nameCaseType)) { - fieldName = StringUtils.lowerCaseFirst(columnName); + fieldName = StringUtils.toUnderline(columnName, false); } else if (ParamInfo.NAME_CASE_TYPE.UPPER_UNDER_SCORE_CASE.equals(nameCaseType)) { - fieldName = StringUtils.lowerCaseFirst(columnName.toUpperCase()); + fieldName = StringUtils.toUnderline(columnName.toUpperCase(), true); } else { fieldName = columnName; } @@ -228,12 +232,12 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) while (columnCommentMatcher.find()) { String columnCommentTmp = columnCommentMatcher.group(); //System.out.println(columnCommentTmp); - fieldComment = tableSql.substring(tableSql.indexOf(columnCommentTmp) + columnCommentTmp.length()).trim(); + fieldComment = tableSql.substring(tableSql.indexOf(columnCommentTmp) + columnCommentTmp.length()).trim().get(); fieldComment = fieldComment.substring(0, fieldComment.indexOf("`")).trim(); } } else if (columnLine.contains(" comment")) { //20200518 zhengkai 修复包含comment关键字的问题 - String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim(); + String commentTmp = columnLine.substring(columnLine.lastIndexOf("comment") + 7).trim().get(); // '用户ID', if (commentTmp.contains("`") || commentTmp.indexOf("`") != commentTmp.lastIndexOf("`")) { commentTmp = commentTmp.substring(commentTmp.indexOf("`") + 1, commentTmp.lastIndexOf("`")); @@ -275,6 +279,24 @@ public static ClassInfo processTableIntoClassInfo(ParamInfo paramInfo) return codeJavaInfo; } + private static boolean isNotSpecialColumnLine(NonCaseString columnLine, int lineSeq) { + return ( + !columnLine.containsAny( + "key ", + "constraint", + "using", + "unique ", + "fulltext ", + "index ", + "pctincrease", + "buffer_pool", + "tablespace" + ) + && !(columnLine.contains("primary ") && columnLine.indexOf("storage") + 3 > columnLine.indexOf("(")) + && !(columnLine.contains("primary ") && lineSeq > 3) + ); + } + /** * 解析JSON生成类信息 * diff --git a/generator-web/src/main/resources/statics/js/main.js b/generator-web/src/main/resources/statics/js/main.js index fd01eb03..369de1c6 100644 --- a/generator-web/src/main/resources/statics/js/main.js +++ b/generator-web/src/main/resources/statics/js/main.js @@ -36,6 +36,9 @@ const vm = new Vue({ " 'user_name' varchar(255) NOT NULL COMMENT '用户名',\n" + " 'status' tinyint(1) NOT NULL COMMENT '状态',\n" + " 'create_time' datetime NOT NULL COMMENT '创建时间',\n" + + //下面可以留着方便开发调试时打开 + // " `updateTime` datetime NOT NULL COMMENT '更新时间',\n" + + // " ABc_under_Line-Hypen-CamelCase varchar comment '乱七八糟的命名风格',\n" + " PRIMARY KEY ('user_id')\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'", options: { diff --git a/generator-web/src/test/java/FooTest.java b/generator-web/src/test/java/FooTest.java new file mode 100644 index 00000000..1a9254fe --- /dev/null +++ b/generator-web/src/test/java/FooTest.java @@ -0,0 +1,25 @@ +import com.softdev.system.generator.util.StringUtils; + +public class FooTest { + + public static void main(String[] args) { + // String updateTime = StringUtils.underlineToCamelCase("updateTime"); + // System.out.println(updateTime); + + + // System.out.println(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "userName")); + // System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "userNAme-UUU")); + + System.out.println(StringUtils.toUnderline("userName",false)); + System.out.println(StringUtils.toUnderline("UserName",false)); + System.out.println(StringUtils.toUnderline("user_NameGgg_x-UUU",false)); + System.out.println(StringUtils.toUnderline("username",false)); + + System.out.println(StringUtils.toUnderline("userName",true)); + System.out.println(StringUtils.toUnderline("UserName",true)); + System.out.println(StringUtils.toUnderline("user_NameGgg_x-UUU",true)); + System.out.println(StringUtils.toUnderline("username",true)); + } + + +}