diff --git a/alis-generator-core/src/main/java/org/alis/generator/domain/model/Content.java b/alis-generator-core/src/main/java/org/alis/generator/domain/model/Content.java new file mode 100644 index 0000000..061a8cd --- /dev/null +++ b/alis-generator-core/src/main/java/org/alis/generator/domain/model/Content.java @@ -0,0 +1,26 @@ +package org.alis.generator.domain.model; + +import lombok.Data; + +/** + * @author lc + * @date 2023/11/22 16:55 + */ +@Data +public class Content { + + /** + * 文件文本 + */ + private String content; + + /** + * 输出的path + */ + private String path; + + /** + * 源配置 + */ + private String source; +} diff --git a/alis-generator-core/src/main/java/org/alis/generator/domain/model/ContextConfig.java b/alis-generator-core/src/main/java/org/alis/generator/domain/model/ContextConfig.java new file mode 100644 index 0000000..27215d2 --- /dev/null +++ b/alis-generator-core/src/main/java/org/alis/generator/domain/model/ContextConfig.java @@ -0,0 +1,52 @@ +package org.alis.generator.domain.model; + +import java.util.Properties; + +/** + * @author lc + * @date 2023/11/22 16:58 + */ +public class ContextConfig extends Properties { + + public String getType() { + return this.getProperty("type"); + } + + public void setType(String type) { + this.put("type", type); + } + + + public void setSource(String source) { + this.put("source", source); + } + + public String getSource() { + return this.getProperty("source"); + } + + public void setPackagePath(String packagePath) { + this.put("packagePath", packagePath); + } + + public String getPackagePath() { + return this.getProperty("packagePath", "org.alis.exam"); + } + + public void setClassName(String className) { + this.put("className", className); + } + + public String getClassName() { + return this.getProperty("className", "Example"); + } + + public void setAuthor(String author) { + this.put("author", author); + } + + public String getAuthor() { + return this.getProperty("author", ""); + } + +} diff --git a/alis-generator-core/src/main/java/org/alis/generator/domain/model/JsonNode.java b/alis-generator-core/src/main/java/org/alis/generator/domain/model/JsonNode.java new file mode 100644 index 0000000..bb63e3c --- /dev/null +++ b/alis-generator-core/src/main/java/org/alis/generator/domain/model/JsonNode.java @@ -0,0 +1,20 @@ +package org.alis.generator.domain.model; + +import lombok.Data; + +import java.util.List; + + +/** + * @author lc + * @date 2023/11/22 18:39 + */ +@Data +public class JsonNode { + private String name; + private String alias; + private String type; + private String absolutePath; + private List nodes; + private Boolean arrayFlag=Boolean.FALSE; +} diff --git a/alis-generator-core/src/main/java/org/alis/generator/factory/Generator.java b/alis-generator-core/src/main/java/org/alis/generator/factory/Generator.java new file mode 100644 index 0000000..26ca64e --- /dev/null +++ b/alis-generator-core/src/main/java/org/alis/generator/factory/Generator.java @@ -0,0 +1,24 @@ +package org.alis.generator.factory; + +import org.alis.generator.domain.model.Content; +import org.alis.generator.domain.model.ContextConfig; + + +/** + * 文件生成类 + * + * @author lc + * @date 2023/11/22 16:20 + */ +public interface Generator { + + + /** + * 核心生成接口 + * + * @param prop 配置项 + * @return 实现 + */ + Content generator(ContextConfig prop); + +} diff --git a/alis-generator-core/src/main/java/org/alis/generator/factory/JsonGenerator.java b/alis-generator-core/src/main/java/org/alis/generator/factory/JsonGenerator.java new file mode 100644 index 0000000..1f156f6 --- /dev/null +++ b/alis-generator-core/src/main/java/org/alis/generator/factory/JsonGenerator.java @@ -0,0 +1,202 @@ +package org.alis.generator.factory; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import org.alis.generator.domain.model.Content; +import org.alis.generator.domain.model.ContextConfig; +import org.alis.generator.domain.model.JsonNode; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.text.MessageFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * @author lc + * @date 2023/11/22 17:01 + */ +public class JsonGenerator implements Generator { + @Override + public Content generator(ContextConfig prop) { + String source = prop.getSource().trim(); + if (!StringUtils.hasLength(source)) { + return null; + } + JSONObject jsonObject; + // 翻译source + if (StringUtils.startsWithIgnoreCase(source, "[")) { + // 如果是数组 只会取第一个对象 + JSONArray array = JSONUtil.parseArray(source); + jsonObject = JSONUtil.parseObj(array.get(0)); + } else { + jsonObject = JSONUtil.parseObj(source); + } + // 使用组合模式 + List fieldNodes = new ArrayList<>(); + // 匿名内部类 + List classNodes = new ArrayList<>(); + + dealMap(jsonObject, fieldNodes, classNodes); + + // merge删除对应的类 + Map merge = merge(classNodes); + + // 生成class 文件 + StringBuilder builder = new StringBuilder(); + + // 包路径 + builder.append("package ").append(prop.getPackagePath()).append(";").append("\n\n"); + // 注解的包 + builder.append("import lombok.Data;").append("\n"); + // 导入所有的包 + List importList = new ArrayList<>(); + fillAllType(fieldNodes, importList); + importList.stream().filter(Objects::nonNull).distinct().forEach(path -> builder.append("import ").append(path).append(";").append("\n\n")); + + // 注解信息 + LocalDateTime now = LocalDateTime.now(); + String localDate = now.toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + String localTime = now.toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm")); + String author = "/**\n * @author {0}\n * @date {1} {2}\n **/"; + String format = MessageFormat.format(author, prop.getAuthor(), localDate, localTime); + builder.append(format).append("\n"); + // 类名 + builder.append("@Data").append("\n"); + builder.append("public class ").append(prop.getClassName()).append("{\n"); + + // 构造函数 + builder.append(" ").append("public ").append(prop.getClassName()).append("(){}\n"); + + // 生成属性 + fieldNodes.forEach(node -> builder.append(" ").append("private ").append(merge.getOrDefault(node.getType(), node.getType())).append(" ").append(node.getName()).append(";\n")); + + // 生成静态属性类 + classNodes.forEach(clazzNode -> { + builder.append(" ").append("@Data").append("\n"); + builder.append(" ").append("public static class ").append(clazzNode.getAlias()).append("{\n"); + List files = clazzNode.getNodes(); + files.forEach(node -> builder.append(" ").append(" ").append("private ").append(node.getType()).append(" ").append(node.getName()).append(";\n")); + builder.append(" ").append("}\n"); + }); + builder.append("}\n"); + + + Content content = new Content(); + content.setPath(prop.getPackagePath()); + content.setSource(prop.getSource()); + content.setContent(builder.toString()); + + return content; + } + + + private void dealMap(JSONObject jsonObject, List fields, List classNodes) { + for (Map.Entry entry : jsonObject.entrySet()) { + Object value = entry.getValue(); + String name = entry.getKey(); + JsonNode node = new JsonNode(); + node.setAlias(name); + node.setName(name); + node.setType(value.getClass().getSimpleName()); + node.setAbsolutePath(value.getClass().getName()); + List nodes = new ArrayList<>(); + + if (value instanceof JSONObject) { + String className = upFirstChar(name) + "DTO"; + node.setType(className); + node.setAlias(className); + classNodes.add(node); + node.setAbsolutePath(null); + dealMap((JSONObject) value, nodes, classNodes); + } else if (value instanceof JSONArray) { + node.setAbsolutePath(null); + node.setAbsolutePath("java.util.List"); + JSONArray jsonArray = (JSONArray) value; + node.setArrayFlag(Boolean.TRUE); + if (CollectionUtils.isEmpty(jsonArray)) { + node.setType("List"); + } else { + Object arrayModel = jsonArray.get(0); + if (arrayModel instanceof JSONObject) { + String className = upFirstChar(name) + "DTO"; + node.setType("List<" + className + ">"); + node.setAlias(className); + classNodes.add(node); + dealMap(JSONUtil.parseObj(jsonArray.get(0)), nodes, classNodes); + } else { + node.setType("List<" + arrayModel.getClass().getSimpleName() + ">"); + node.setAlias(name); +// node.setAbsolutePath(arrayModel.getClass().getName()); + } + + } + + } + + node.setNodes(nodes); + fields.add(node); + } + + } + + + private void fillAllType(List jsonNodes, List typeList) { + if (CollectionUtils.isEmpty(jsonNodes)) { + return; + } + for (JsonNode jsonNode : jsonNodes) { + typeList.add(jsonNode.getAbsolutePath()); + fillAllType(jsonNode.getNodes(), typeList); + } + + } + + + private String upFirstChar(String name) { + String first = name.substring(0, 1); + return name.replaceFirst(first, first.toUpperCase()); + } + + + private Map merge(List classNodes) { + Map mapping = new HashMap<>(); + Map hashTable = new HashMap<>(); + Iterator iterator = classNodes.iterator(); + while (iterator.hasNext()) { + JsonNode next = iterator.next(); + int hash = next.getNodes().stream().map(JsonNode::getName).sorted(String::compareTo) + .collect(Collectors.joining()).hashCode(); + // 如果存在 + if (hashTable.containsKey(hash)) { + mapping.put(next.getType(), hashTable.get(hash)); + if (Boolean.TRUE.equals(next.getArrayFlag())) { + mapping.put(next.getType(), "List<" + hashTable.get(hash) + ">"); + } + iterator.remove(); + } else { + hashTable.put(hash, next.getType()); + } + } + // 如果遇到名称相同的 直接组合成大对象 + Iterator duplicate = classNodes.iterator(); + Map duplicateMap = new HashMap<>(); + while (duplicate.hasNext()) { + JsonNode next = duplicate.next(); + if (duplicateMap.containsKey(next.getType())) { + duplicate.remove(); + List nodes = duplicateMap.get(next.getType()).getNodes(); + Set collect = nodes.stream().map(JsonNode::getName).collect(Collectors.toSet()); + next.getNodes().stream().filter(no -> !collect.contains(no.getName())).forEach(nodes::add); + } else { + duplicateMap.put(next.getType(), next); + } + } + + return mapping; + } +} diff --git a/alis-generator-server/src/test/java/org/alis/generator/Example.java b/alis-generator-server/src/test/java/org/alis/generator/Example.java new file mode 100644 index 0000000..2c90e32 --- /dev/null +++ b/alis-generator-server/src/test/java/org/alis/generator/Example.java @@ -0,0 +1,37 @@ +package org.alis.generator; + +import lombok.Data; +import java.lang.String; + +import java.lang.Integer; + +import java.util.List; + +/** + * @author robin + * @date 2023-11-23 18:31 + **/ +@Data +public class Example{ + public Example(){} + private String userName; + private Integer userId; + private Integer age; + private ExtDTO ext; + private List extOne; + private List extTwo; + @Data + public static class ExtDTO{ + private String idCard; + private BirdDTO bird; + } + @Data + public static class BirdDTO{ + private String idCard; + } + @Data + public static class ExtTwoDTO{ + private Integer mode; + private List x; + } +} diff --git a/alis-generator-server/src/test/java/org/alis/generator/TestCase.java b/alis-generator-server/src/test/java/org/alis/generator/TestCase.java new file mode 100644 index 0000000..c7dc8e3 --- /dev/null +++ b/alis-generator-server/src/test/java/org/alis/generator/TestCase.java @@ -0,0 +1,52 @@ +package org.alis.generator; + +import org.alis.generator.domain.model.Content; +import org.alis.generator.domain.model.ContextConfig; +import org.alis.generator.factory.JsonGenerator; +import org.junit.jupiter.api.Test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + + +class TestCase { + + @Test + void testGeneratorJson() { + ContextConfig contextConfig = new ContextConfig(); + String str = "{\n" + + " \"userName\": \"username\",\n" + + " \"userId\": 1,\n" + + " \"age\": 1,\n" + + " \"ext\": {\n" + + " \"idCard\": \"idCard\",\n" + + " \"bird\":{\n" + + " \"idCard\":\"idCard\"\n" + + " }\n" + + " },\n" + + " \"extOne\": [{\n" + + " \"idCard\": \"idCard\"\n" + + " }],\n" + + " \"extTwo\": [\n" + + " {\n" + + " \"mode\": 1,\n" + + " \"x\": [1\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + contextConfig.setSource(str); + contextConfig.setType("json"); + contextConfig.setAuthor("robin"); + contextConfig.setPackagePath("org.alis.generator"); + JsonGenerator jsonGenerator = new JsonGenerator(); + Content generator = jsonGenerator.generator(contextConfig); + try (FileOutputStream outputStream = new FileOutputStream("src/test/java/org/alis/generator/Example.java")) { + outputStream.write(generator.getContent().getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } +}