diff --git a/README.md b/README.md index 008bb2b28854086fcabe1ae30b7540f22fb05485..13044c8a7b47bc19b407f95f52331585e65ec335 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # needcoke-ioc +参考文档: https://www.kuangstudy.com/bbs/1438400389824167938 + +使用demo: https://gitee.com/needcoke/needcoke-ioc-demo +#### VER-0.3 +支持无缝集成needcoke-aop #### 介绍 实现类似于Spring的容器管理工具,因为项目中不需要使用很重的Spring,而右希望能够拥有它的IOC,DI @@ -16,13 +21,15 @@ @Init 被标注了@Component的方法在执行完构造函数后会调用@Init标注的方法初始化bean +@Value 配置文件注入 + ContextBean.java 容器对bean的封装 ContextHolder.java 容器 ContextRun.java 容器启动器,只有调用其中的run方法,容器才能够正常启动 #### 安装教程 -下载jar包: https://gitee.com/needcoke/needcoke-ioc/tree/master/jar/VER-0.1 +下载jar包: https://gitee.com/needcoke/needcoke-ioc/tree/master/jar/VER-0.3 下载完成后在IDEA - ProjectSettings - modules中点击+号导入java选择jar包就可以 #### 使用说明 @@ -38,7 +45,6 @@ public class Main { } } ``` - #### 参与贡献 1. Fork 本仓库 diff --git a/jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar b/jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..11ea1c6110e4f7aa433a0656abedff6801368414 Binary files /dev/null and b/jar/VER-0.2/needcoke-ioc-VER-0.2-sources.jar differ diff --git a/jar/VER-0.2/needcoke-ioc-VER-0.2.jar b/jar/VER-0.2/needcoke-ioc-VER-0.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..507860a004784b38601034d980247b2371841f55 Binary files /dev/null and b/jar/VER-0.2/needcoke-ioc-VER-0.2.jar differ diff --git a/jar/VER-0.3/needcoke-ioc.jar b/jar/VER-0.3/needcoke-ioc.jar new file mode 100644 index 0000000000000000000000000000000000000000..65810419395d907e622382024e65b1d3f3871b58 Binary files /dev/null and b/jar/VER-0.3/needcoke-ioc.jar differ diff --git a/pom.xml b/pom.xml index 7dba2686e5d679ebdfb288f59ff14f6fa8f28afd..25ded6ed7233843a56694a3ff649a506c470c652 100644 --- a/pom.xml +++ b/pom.xml @@ -4,9 +4,11 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - groupId + org.needcoke needcoke-ioc - VER-0.1 + pom + VER-0.3 + 8 @@ -19,5 +21,41 @@ hutool-all 5.5.1 + + org.yaml + snakeyaml + 1.27 + + + + com.amihaiemil.web + eo-yaml + 5.2.3 + + + org.projectlombok + lombok + 1.18.20 + + + com.alibaba + fastjson + 1.2.57 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + UTF-8 + + + + \ No newline at end of file diff --git a/src/main/java/org/needcoke/ioc/ContextRun.java b/src/main/java/org/needcoke/ioc/ContextRun.java index 00923424ec7603b54e739b31a8179376912dd672..6d1754da252080696a0d2b7d2c6a9013bae46326 100644 --- a/src/main/java/org/needcoke/ioc/ContextRun.java +++ b/src/main/java/org/needcoke/ioc/ContextRun.java @@ -4,127 +4,119 @@ import cn.hutool.core.util.StrUtil; import org.needcoke.ioc.annotation.*; import org.needcoke.ioc.core.ContextBean; import org.needcoke.ioc.core.ContextHolder; -import org.needcoke.ioc.util.MethodUtil; +import org.needcoke.ioc.handle.*; import org.needcoke.ioc.util.PackageUtil; - +import org.needcoke.ioc.util.ReflectUtil; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; -import java.util.Set; +import java.lang.reflect.Type; +import java.util.*; public class ContextRun { - private static Object init(Class clz) { - try { - return clz.newInstance(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - public static ContextHolder run(Class t, String[] args) { ContextHolder holder = new ContextHolder(); + ContextBean bean = new ContextBean() + .setName("needCokeContext") + .setClz(holder.getClass()) + .setValue(holder); + holder.putBean(bean); BeanScanner annotation = t.getAnnotation(BeanScanner.class); if (null != annotation) { - String[] paths = annotation.path(); - boolean recursion = annotation.recursion();/* 是否递归加载类 */ - for (String path : paths) { - /* 获取包路径下的所有类 */ - Set> clzFromPkg = PackageUtil.getClzFromPkg(path); - for (Class aClass : clzFromPkg) { - if (aClass.getAnnotation(Component.class) != null) { - Object component = init(aClass); - Method[] methods = aClass.getDeclaredMethods(); - for (Method method : methods) { - method.setAccessible(true); - Bean methodBean = method.getAnnotation(Bean.class); - if (null != methodBean) { - if (StrUtil.isNotBlank(methodBean.name()) && holder.nameBeanContext.containsKey(methodBean.name())) { - throw new RuntimeException("already has a bean name #" + methodBean.name() + "#"); - } else { - /* 通过反射执行方法,并获取反射值,注解有声明名称则用注解,注解无则使用方法名,存入context */ - ContextBean beanFromMethod = MethodUtil.runMethod(method, component, holder); - if (StrUtil.isBlank(methodBean.name())) { - beanFromMethod.name = method.getName(); - } - holder.putBean(beanFromMethod); - } - } - } - ContextBean componentBean = new ContextBean(); - String componentName = aClass.getAnnotation(Component.class).name(); - if (StrUtil.isBlank(componentName)) { - componentName = aClass.getSimpleName(); + Set> clzFromPkg = new HashSet<>(); + String[] packages = annotation.path(); + for (String packName : packages) { + clzFromPkg = PackageUtil.getClzFromPkg(packName); + } + /* 配置文件读取 */ + Handle configReaderHandle = null; + /* 配置文件注入 */ + Handle configInjectHandle = null; + /* bean扫描 */ + Handle beanScanHandle = null; + /* bean注入 */ + Handle beanInjectHandle = null; + /* component调用初始化方法 */ + Handle componentInitHandle = null; + + List otherHandles = new ArrayList<>(); + for (Class aClass : clzFromPkg) { + for (Type genericInterface : aClass.getGenericInterfaces()) { + if (genericInterface.getTypeName().equals(Handle.class.getTypeName())) { + Handle handle = ReflectUtil.getBean(aClass); + if (handle instanceof ConfigReaderHandle) { + configReaderHandle = handle; + } else if (handle instanceof ConfigInjectHandle) { + configInjectHandle = handle; + } else if (handle instanceof BeanScanHandle) { + beanScanHandle = handle; + } else if (handle instanceof BeanInjectHandle) { + beanInjectHandle = handle; + } else if (handle instanceof ComponentInitHandle) { + componentInitHandle = handle; + }else{ + otherHandles.add(handle); } - componentBean.name = componentName; - componentBean.clz = aClass; - componentBean.value = component; - holder.putComponent(componentBean); } } + Map configMap = new HashMap<>(); + Object component = null; + Component annotationComponent = aClass.getAnnotation(Component.class); + if (null != annotationComponent && annotationComponent.inject()) { + component = ReflectUtil.getBean(aClass); + holder.componentBuffer.put(aClass, component); + holder.componentNameBuffer.put(aClass, StrUtil.isNotBlank(annotationComponent.name()) ? + annotationComponent.name() : aClass.getSimpleName()); + Field[] fields = aClass.getDeclaredFields(); + for (Field field : fields) { + /* 需要被注入bean的缓冲区 */ + if (null != field.getAnnotation(Autowired.class)) { + if(!holder.needInjectBeanField.containsKey(aClass)){ + holder.needInjectBeanField.put(aClass,new ArrayList<>()); + } + holder.needInjectBeanField.get(aClass).add(field); + } - } + /* 需要被注入配置的缓冲区 */ + if (null != field.getAnnotation(Value.class)) { + Value annotationValue = field.getAnnotation(Value.class); + configMap.put(field, annotationValue.name()); + } + } + holder.needInjectConfigField.put(aClass, configMap); - Set> componentClasses = holder.componentMap.keySet(); - for (Class componentClass : componentClasses) { - Object component = holder.componentMap.get(componentClass); - Field[] fields = componentClass.getDeclaredFields(); - for (Field field : fields) { - field.setAccessible(true); - Autowired autowiredAnnotation = field.getAnnotation(Autowired.class); - if (null != autowiredAnnotation) { - ContextBean beanAutowired = null; - /* Autowired的name属性不为空的话,就通过名称注入 */ - if (StrUtil.isNotBlank(autowiredAnnotation.name())) { - beanAutowired = holder.nameBeanContext.get(autowiredAnnotation.name()); - if (null != beanAutowired && beanAutowired.clz.equals(field.getType())) { - try { - field.set(component, beanAutowired.value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } else { - /* 名称为空,则通过类型来匹配 */ - if (holder.typeBeanContext.containsKey(field.getType())) { - beanAutowired = holder.typeBeanContext.get(field.getType()).get(0); - try { - field.set(component, beanAutowired.value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + Method[] methods = aClass.getDeclaredMethods(); + for (Method method : methods) { + /* 将自定义bean注入到缓冲区 */ + if (null != method.getAnnotation(Bean.class)) { + Bean annotationMethod = method.getAnnotation(Bean.class); + if(!holder.beanMethodBuffer.containsKey(aClass)){ + holder.beanMethodBuffer.put(aClass,new HashMap<>()); } + holder.beanMethodBuffer.get(aClass).put(StrUtil.isNotBlank(annotationMethod.name()) ? + annotationMethod.name() : method.getName(), + method); + } + /* 需要调用的初始化方法缓冲区 */ + if (null != method.getAnnotation(Init.class)) { + holder.initMethodBuffer.put(aClass, method); } } + } } - for (Class componentClass : componentClasses) { - Method[] methods = componentClass.getDeclaredMethods(); - for (Method method : methods) { - method.setAccessible(true); - Init initBean = method.getAnnotation(Init.class); - /* 执行初始化方法 */ - if (null != initBean) { - try { - Object o = holder.componentMap.get(componentClass); - method.invoke(o); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - } + configReaderHandle.handle(holder); + configInjectHandle.handle(holder); + beanScanHandle.handle(holder); + beanInjectHandle.handle(holder); + componentInitHandle.handle(holder); + for (Handle otherHandle : otherHandles) { + otherHandle.handle(holder); } } return holder; - } } diff --git a/src/main/java/org/needcoke/ioc/annotation/Component.java b/src/main/java/org/needcoke/ioc/annotation/Component.java index a6e815ff4b494b58baf8fa1bd1e123f61395d179..b0686d518191bc662a2f4086a7df75ce5ea9b67c 100644 --- a/src/main/java/org/needcoke/ioc/annotation/Component.java +++ b/src/main/java/org/needcoke/ioc/annotation/Component.java @@ -8,4 +8,6 @@ import java.lang.annotation.*; public @interface Component { @Note("名称") String name() default ""; + + boolean inject() default true; } diff --git a/src/main/java/org/needcoke/ioc/annotation/Value.java b/src/main/java/org/needcoke/ioc/annotation/Value.java new file mode 100644 index 0000000000000000000000000000000000000000..6eaa70e0cefdc3924473df0a578ef3afaa7a9392 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/annotation/Value.java @@ -0,0 +1,15 @@ +package org.needcoke.ioc.annotation; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD,ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Value { + + @Note("指定配置文件的映射路径") + String target() default ""; + + @Note("指定映射配置文件地址") + String name() default ""; +} diff --git a/src/main/java/org/needcoke/ioc/core/ConfigBean.java b/src/main/java/org/needcoke/ioc/core/ConfigBean.java new file mode 100644 index 0000000000000000000000000000000000000000..bd9479106ff10d11109e1935ab9a7cbd7eb0afe2 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/core/ConfigBean.java @@ -0,0 +1,23 @@ +package org.needcoke.ioc.core; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** +* 配置文件的Bean +**/ +@Data +@Accessors(chain = true) +public class ConfigBean { + + private String beanKey; + + private Object beanValue; + + /** + * 该配置源自哪个配置文件 + **/ + private String readByFile; + + +} diff --git a/src/main/java/org/needcoke/ioc/core/ContextBean.java b/src/main/java/org/needcoke/ioc/core/ContextBean.java index 3b70fcd2c789db56ff1466787447c2ef7fa62bb5..da83d57034e02fe16819f619895b7db59dd8d918 100644 --- a/src/main/java/org/needcoke/ioc/core/ContextBean.java +++ b/src/main/java/org/needcoke/ioc/core/ContextBean.java @@ -1,8 +1,13 @@ package org.needcoke.ioc.core; +import lombok.Data; +import lombok.experimental.Accessors; + /** * ioc容器的封装bean **/ +@Data +@Accessors(chain = true) public class ContextBean { public Class clz; @@ -13,6 +18,9 @@ public class ContextBean { public String id; + /* 是否是代理的 */ + public Boolean proxy; + public E getValue(){ return (E) value; } diff --git a/src/main/java/org/needcoke/ioc/core/ContextHolder.java b/src/main/java/org/needcoke/ioc/core/ContextHolder.java index 189d1e640bd4b27e24442b500aa8f071313dc047..7d31160b229fee66afe4915f6be862fbb22b2876 100644 --- a/src/main/java/org/needcoke/ioc/core/ContextHolder.java +++ b/src/main/java/org/needcoke/ioc/core/ContextHolder.java @@ -1,13 +1,25 @@ package org.needcoke.ioc.core; import cn.hutool.core.util.StrUtil; +import org.needcoke.ioc.annotation.Component; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -public class ContextHolder { +/** + * ioc容器 + * + * @author Gilgamesh + * @since VER-0.1 + **/ +@Component(inject = false) +public class ContextHolder implements NeedCokeContext { /** * id计数器 @@ -19,6 +31,11 @@ public class ContextHolder { **/ public Map, Object> componentMap = new ConcurrentHashMap<>(); + /** + * 代理component的bean池 + **/ + public Map, ContextBean> proxyComponentMap = new ConcurrentHashMap<>(); + /** * ioc容器的id索引 **/ @@ -28,13 +45,53 @@ public class ContextHolder { **/ public Map nameBeanContext = new ConcurrentHashMap<>(); + public Map> fullNameBeanContext = new ConcurrentHashMap<>(); + /** * ioc容器的类型索引 **/ public Map, List> typeBeanContext = new ConcurrentHashMap<>(); + /** + * properties配置文件缓冲区 + **/ + public Map propertiesBuffer = new HashMap<>(); + + /** + * component缓冲区 + **/ + public Map, Object> componentBuffer = new HashMap<>(); + + public Map, String> componentNameBuffer = new HashMap<>(); + + /** + * 函数缓存@Bean + **/ + public Map, Map> beanMethodBuffer = new HashMap<>(); + + /** + * 函数缓冲@init + **/ + public Map, Method> initMethodBuffer = new HashMap<>(); + + /** + * Autowired 标记的字段 + **/ + public Map, List> needInjectBeanField = new HashMap<>(); + + public Map, Map> needInjectConfigField = new HashMap<>(); + + public List getBean(Class type) { List ts = new ArrayList<>(); + if (!typeBeanContext.containsKey(type)) { + for (ContextBean value : idBeanMap.values()) { + if (type.isInstance(value.value)) { + ts.add((T) value.value); + } + } + return ts; + } List contextBeans = typeBeanContext.get(type); for (ContextBean contextBean : contextBeans) { ts.add((T) contextBean.value); @@ -73,6 +130,12 @@ public class ContextHolder { } idBeanMap.put(bean.id, bean); } + if(bean.clz != null){ + if(!fullNameBeanContext.containsKey(bean.clz.getName())){ + fullNameBeanContext.put(bean.clz.getName(),new ArrayList<>()); + } + fullNameBeanContext.get(bean.clz.getName()).add(bean); + } } /** @@ -87,4 +150,5 @@ public class ContextHolder { putBean(bean); } + } diff --git a/src/main/java/org/needcoke/ioc/core/NeedCokeContext.java b/src/main/java/org/needcoke/ioc/core/NeedCokeContext.java new file mode 100644 index 0000000000000000000000000000000000000000..a22008edea0cfc16a89d0dad83226e698ce6b576 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/core/NeedCokeContext.java @@ -0,0 +1,4 @@ +package org.needcoke.ioc.core; + +public interface NeedCokeContext { +} diff --git a/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java b/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..c1b72430210187f9fb0e7cb88e74417c0a6df5ff --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/BeanInjectHandle.java @@ -0,0 +1,35 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.collection.CollUtil; +import org.needcoke.ioc.annotation.Autowired; +import org.needcoke.ioc.core.ContextBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Set; + +public class BeanInjectHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Set> clzSet = holder.needInjectBeanField.keySet(); + for (Class aClass : clzSet) { + Object component = holder.componentMap.get(aClass); + List fields = holder.needInjectBeanField.get(aClass); + for (Field field : fields) { + Autowired annotationAutoWired = field.getAnnotation(Autowired.class); + String name = annotationAutoWired.name(); + ContextBean contextBean = holder.nameBeanContext.get(name); + if (null != contextBean && contextBean.getClz().equals(field.getType())) { + ReflectUtil.setField(component, field, contextBean.value); + } else { + List bean = holder.getBean(field.getType()); + if (CollUtil.isNotEmpty(bean)) { + ReflectUtil.setField(component, field, bean.get(0)); + } + } + } + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java b/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..8cf038acfc905ea948688d174c81ebdc2db85e48 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/BeanScanHandle.java @@ -0,0 +1,119 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.collection.CollUtil; +import org.needcoke.ioc.annotation.Value; +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; + +/** + * Bean创建处理器 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class BeanScanHandle implements Handle { + + private ContextHolder holder; + + @Override + public int handle(ContextHolder holder, Object... params) { + this.holder = holder; + initBeanMethodWithoutParameter(); + initComponent(); + initBeanMethodHasParameter(); + return 1; + } + + public void initBeanMethodWithoutParameter() { + Set> clzSet = holder.beanMethodBuffer.keySet(); + Map, Map> waitBuffer = new HashMap<>(); + for (Class aClass : clzSet) { + Map stringMethodMap = holder.beanMethodBuffer.get(aClass); + for (String beanName : stringMethodMap.keySet()) { + Method method = stringMethodMap.get(beanName); + Object component = holder.componentBuffer.get(aClass); + Parameter[] parameters = method.getParameters(); + /* 如果是有参构造函数直接启动,如果有参就先加入等待区 */ + if (parameters.length > 0) { + if (!waitBuffer.containsKey(aClass)) { + waitBuffer.put(aClass, new HashMap<>()); + } + waitBuffer.get(aClass).put(beanName, method); + } else { + Object o = ReflectUtil.invokeMethod(component, method, null); + ContextBean contextBean = new ContextBean() + .setName(beanName) + .setClz(method.getReturnType()) + .setValue(o); + holder.putBean(contextBean); + } + } + } + holder.beanMethodBuffer = waitBuffer; + } + + public void initBeanMethodHasParameter() { + Set> clzSet = holder.beanMethodBuffer.keySet(); + Map, Map> waitBuffer = new HashMap<>(); + for (Class aClass : clzSet) { + Map stringMethodMap = holder.beanMethodBuffer.get(aClass); + for (String beanName : stringMethodMap.keySet()) { + Method method = stringMethodMap.get(beanName); + + Object component = holder.componentBuffer.get(aClass); + Parameter[] parameters = method.getParameters(); + Object[] params = new Object[parameters.length]; + int i = 0; + /* 执行有参函数 */ + for (Parameter parameter : parameters) { + /* 参数来自配置文件 */ + if (null != parameter.getAnnotation(Value.class)) { + Value annotationValue = parameter.getAnnotation(Value.class); + ConfigBean configBean = holder.propertiesBuffer.get(annotationValue.name()); + if (null != configBean) { + params[i] = configBean.getBeanValue(); + } + } else { + List bean = holder.getBean(parameter.getType()); + if (CollUtil.isNotEmpty(bean)) { + params[i] = bean.get(0); + } else { + params[i] = null; + } + } + i++; + } + Object o = ReflectUtil.invokeMethod(component, method, params); + ContextBean contextBean = new ContextBean() + .setName(beanName) + .setClz(method.getReturnType()) + .setValue(o); + holder.putBean(contextBean); + holder.beanMethodBuffer = null; + } + } + } + + public void initComponent() { + for (Class aClass : holder.componentBuffer.keySet()) { + String componentName = holder.componentNameBuffer.get(aClass); + componentName = componentName.substring(0, 1).toLowerCase() + componentName.substring(1, componentName.length()); + + ContextBean bean = new ContextBean() + .setName(componentName) + .setValue(holder.componentBuffer.get(aClass)) + .setClz(aClass); + holder.putComponent(bean); +// /* 清空缓冲区 */ +// holder.componentNameBuffer = null; +// holder.componentBuffer = null; + } + } + +} diff --git a/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java b/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..86b146b5802fa901a33062a9944cfba06de05da4 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ComponentInitHandle.java @@ -0,0 +1,20 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.ReflectUtil; +import java.lang.reflect.Method; +import java.util.Set; + +public class ComponentInitHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Set> classes = holder.initMethodBuffer.keySet(); + for (Class aClass : classes) { + Method method = holder.initMethodBuffer.get(aClass); + ReflectUtil.invokeMethod(holder.componentMap.get(aClass), + method,null); + + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java b/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..5b275ab4ad41413b3a47e3d0f6fa36ef28398f43 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ConfigInjectHandle.java @@ -0,0 +1,64 @@ +package org.needcoke.ioc.handle; + +import cn.hutool.core.util.StrUtil; +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextHolder; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +/** + * 该处理类负责将配置文件的内容注入到@Component中 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class ConfigInjectHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... params) { + Map, Map> needInjectConfigField = holder.needInjectConfigField; + Set> keySet = needInjectConfigField.keySet(); + for (Class aClass : keySet) { + Object component = holder.componentBuffer.get(aClass); + Map fieldMap = needInjectConfigField.get(aClass); + for (Field field : fieldMap.keySet()) { + ConfigBean configBean = holder.propertiesBuffer.get(fieldMap.get(field)); + if (null == configBean) { + continue; + } + try { + field.setAccessible(true); + String value = configBean.getBeanValue().toString(); + if (field.getType().getTypeName().equals(Integer.class.getTypeName())) { + field.set(component, Integer.parseInt(value)); + } else if (field.getType().getTypeName().equals(Double.class.getTypeName())) { + field.set(component, Double.parseDouble(value)); + } else if (field.getType().getTypeName().equals(Long.class.getTypeName())) { + field.set(component, Long.parseLong(value)); + } else if (field.getType().getTypeName().equals(Boolean.class.getTypeName())) { + field.set(component, Boolean.parseBoolean(value)); + } else if (field.getType().getTypeName().equals(Short.class.getTypeName())) { + field.set(component, Short.parseShort(value)); + } else if (field.getType().getTypeName().equals(Byte.class.getTypeName())) { + field.set(component, Byte.parseByte(value)); + } else if (field.getType().getTypeName().equals(Character.class.getTypeName())) { + field.set(component, value); + } else if (field.getType().getTypeName().equals(String.class.getTypeName())) { + field.set(component, value); + } else if (field.getType().getTypeName().equals("int")) { + field.set(component, Integer.parseInt(value)); + } else if (field.getType().getTypeName().equals("double")) { + field.set(component, Double.parseDouble(value)); + } else if (field.getType().getTypeName().equals("boolean")) { + field.set(component, Boolean.parseBoolean(value)); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + } + return 1; + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java b/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java new file mode 100644 index 0000000000000000000000000000000000000000..121144136c389eef68b3cc9e93dd2c521ec582ec --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/ConfigReaderHandle.java @@ -0,0 +1,78 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ConfigBean; +import org.needcoke.ioc.core.ContextHolder; +import org.needcoke.ioc.util.YamlUtil; +import org.yaml.snakeyaml.Yaml; +import java.io.*; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * 配置文件阅读处理 + * + * @author Gilgamesh + * @since VER-0.2 + **/ +public class ConfigReaderHandle implements Handle { + @Override + public int handle(ContextHolder holder, Object... o) { + readYaml(holder, "/application.yaml"); + readYaml(holder, "/application.yml"); + readProperties(holder, "/application.properties"); + return 0; + } + + private void readYaml(ContextHolder holder, String fileName) { + InputStream inputStream = this.getClass().getResourceAsStream(fileName); + if (inputStream == null) { + return; + } + Yaml yaml = new Yaml(); + Map map = null; + try { + map = yaml.loadAs(new InputStreamReader(inputStream, "UTF-8"), Map.class); + } catch (Exception e) { + e.printStackTrace(); + } + String yamlContent = YamlUtil.mapToYaml(map); + String propertiesContent = YamlUtil.yamlToProperties(yamlContent); + String[] lines = propertiesContent.split("\n"); + for (String line : lines) { + try { + String[] ss = line.split("="); + if (ss.length == 2) { + ConfigBean configBean = new ConfigBean() + .setBeanKey(ss[0]) + .setBeanValue(ss[1]) + .setReadByFile(fileName); + holder.propertiesBuffer.put(ss[0], configBean); + } + } catch (Throwable throwable) { + throw new RuntimeException("配置文件解析失败:application.yml"); + } + } + } + + private void readProperties(ContextHolder holder, String fileName) { + InputStream inputStream = this.getClass().getResourceAsStream(fileName); + if (null == inputStream) { + return; + } + Properties properties = new Properties(); + try { + properties.load(new InputStreamReader(inputStream,"UTF-8")); + Set set = properties.stringPropertyNames(); + for (String s : set) { + ConfigBean configBean = new ConfigBean() + .setBeanKey(s) + .setBeanValue(properties.getProperty(s)) + .setReadByFile(fileName); + holder.propertiesBuffer.put(s, configBean); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/needcoke/ioc/handle/Handle.java b/src/main/java/org/needcoke/ioc/handle/Handle.java new file mode 100644 index 0000000000000000000000000000000000000000..37c7d6e8da0b021be968e8f33300aaf5d36588b7 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/handle/Handle.java @@ -0,0 +1,15 @@ +package org.needcoke.ioc.handle; + +import org.needcoke.ioc.core.ContextHolder; + +/** +* 处理接口 +**/ +public interface Handle { + /** + * 处理函数接口 + * @param holder ioc容器 + * @param params 参数 + **/ + int handle(ContextHolder holder,Object... params); +} diff --git a/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java b/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..50cc5ebba625cc35ae7ab4e1a9aa0deaa5f9c77e --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/ConfigValueTypeEnum.java @@ -0,0 +1,58 @@ +package org.needcoke.ioc.util; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author shizi + * @since 2020/9/14 9:58 上午 + */ +@AllArgsConstructor +public enum ConfigValueTypeEnum { + + /** + * yml配置 + */ + YAML("yml配置"), + /** + * properties配置 + */ + PROPERTIES("properties配置"), + /** + * 打包中 + */ + JSON("json配置"), + /** + * string字符配置 + */ + STRING("string字符配置"); + + @Getter + private final String desc; + + private static final Map indexEnumMap; + private static final Map nameEnumMap; + + static { + indexEnumMap = Arrays.stream(ConfigValueTypeEnum.values()).collect(Collectors.toMap(ConfigValueTypeEnum::ordinal, e -> e)); + nameEnumMap = Arrays.stream(ConfigValueTypeEnum.values()).collect(Collectors.toMap(ConfigValueTypeEnum::name, e -> e)); + } + + public static ConfigValueTypeEnum parse(Integer index) { + if (!indexEnumMap.containsKey(index)) { + throw new RuntimeException("不支持下标: " + index); + } + return indexEnumMap.get(index); + } + + public static ConfigValueTypeEnum parse(String name) { + if (!nameEnumMap.containsKey(name)) { + throw new RuntimeException("不支持name: " + name); + } + return nameEnumMap.get(name); + } +} diff --git a/src/main/java/org/needcoke/ioc/util/Pair.java b/src/main/java/org/needcoke/ioc/util/Pair.java new file mode 100644 index 0000000000000000000000000000000000000000..f81ef31f68149084508e493c6daa0703e7f547bb --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/Pair.java @@ -0,0 +1,20 @@ +package org.needcoke.ioc.util; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author shizi + * @since 2020/3/30 7:35 PM + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public final class Pair implements Serializable { + + private K key; + private V value; +} diff --git a/src/main/java/org/needcoke/ioc/util/ReflectUtil.java b/src/main/java/org/needcoke/ioc/util/ReflectUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..7eafb580fa1b0497633d9b292eb55941ce31881c --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/ReflectUtil.java @@ -0,0 +1,76 @@ +package org.needcoke.ioc.util; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +public class ReflectUtil { + + public static T getBean(Class clz) { + try { + return (T) clz.newInstance(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 调用函数 + **/ + public static Object invokeMethod(Object component, Method method, Object... params) { + Parameter[] parameters = method.getParameters(); + if (null != params && params.length > 0) { + for (int i = 0; i < params.length; i++) { + if (params[i] != null) { + String value = (String) params[i]; + if (parameters[i].getType().getTypeName().equals(Integer.class.getTypeName())) { + params[i] = Integer.parseInt(value); + } else if (parameters[i].getType().getTypeName().equals(Double.class.getTypeName())) { + params[i] = Double.parseDouble(value); + } else if (parameters[i].getType().getTypeName().equals(Long.class.getTypeName())) { + params[i] = Long.parseLong(value); + } else if (parameters[i].getType().getTypeName().equals(Boolean.class.getTypeName())) { + params[i] = Boolean.parseBoolean(value); + } else if (parameters[i].getType().getTypeName().equals(Short.class.getTypeName())) { + params[i] = Short.parseShort(value); + } else if (parameters[i].getType().getTypeName().equals(Byte.class.getTypeName())) { + params[i] = Byte.parseByte(value); + } else if (parameters[i].getType().getTypeName().equals(Character.class.getTypeName())) { + params[i] = value; + } else if (parameters[i].getType().getTypeName().equals(String.class.getTypeName())) { + params[i] = value; + } else if (parameters[i].getType().getTypeName().equals("int")) { + params[i] = Integer.parseInt(value); + } else if (parameters[i].getType().getTypeName().equals("double")) { + params[i] = Double.parseDouble(value); + } else if (parameters[i].getType().getTypeName().equals("boolean")) { + params[i] = Boolean.parseBoolean(value); + } + } + } + } + try { + if (params == null) + return method.invoke(component); + return method.invoke(component, params); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static void setField(Object component, Field field, Object o) { + try { + field.setAccessible(true); + field.set(component, o); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/needcoke/ioc/util/YamlUtil.java b/src/main/java/org/needcoke/ioc/util/YamlUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..0616d9fe2c313b7a7bc652a90f2a7db0bb089822 --- /dev/null +++ b/src/main/java/org/needcoke/ioc/util/YamlUtil.java @@ -0,0 +1,1521 @@ +package org.needcoke.ioc.util; + +import com.alibaba.fastjson.JSON; +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.YamlMapping; +import lombok.Data; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.yaml.snakeyaml.DumperOptions; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * yaml与其他各种格式之间互转 + *

+ *

    + *
  • 1.yaml <---> properties
  • + *
  • 2.yaml <---> json
  • + *
  • 3.yaml <---> map
  • + *
  • 4.yaml <---> list
  • + *
  • 5.yaml <---> kvList
  • + *
+ * + * @author shizi + * @since 2020/9/14 3:17 下午 + */ +@SuppressWarnings("all") +@UtilityClass +public class YamlUtil { + + /** + * 换行符 + */ + private final String NEW_LINE = "\n"; + /** + * 注释标识 + */ + private final String REMARK_PRE = "# "; + /** + * 缩进空格 + */ + private final String INDENT_BLANKS = " "; + /** + * 分号连接符 + */ + private final String SIGN_SEMICOLON = ":"; + /** + * 等号连接符 + */ + private final String SIGN_EQUAL = "="; + /** + * 点 + */ + private final String DOT = "."; + /** + * 数组缩进 + */ + private final String ARRAY_BLANKS = "- "; + /** + * yaml的value换行符 + */ + private final String yaml_NEW_LINE_DOM = "|\n"; + private final Pattern rangePattern = Pattern.compile("^(.*)\\[(\\d*)\\]$"); + /** + * 格式转换的缓存 + */ + private final Map typeContentMap = new ConcurrentHashMap<>(); + + /** + * 判断类型是否是yaml类型 + * + * @param content yaml 内容 + * @return true:是yaml,false:不是 + */ + public boolean isYaml(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isYaml", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return false; + } + + try { + checkYaml(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是yaml类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkYaml(String content) { + if (isEmpty(content)) { + throw new RuntimeException("yaml内容为空"); + } + if (!content.contains(":") && !content.contains("-")) { + throw new RuntimeException("yaml内容不包含\":\"也不包含\"-\""); + } + if (content.contains("---\n")) { + throw new RuntimeException("yaml内容不支持 --- 的导入"); + } + + try { + yamlToProperties(content); + } catch (RuntimeException e) { + throw new RuntimeException("内容不是严格yaml类型;" + e.getMessage()); + } + } + + /** + * 判断是否是properties类型 + * + * @param content 内容 + * @return true:是properties类型,false:不是properties类型 + */ + public boolean isProperties(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isProperties", content, () -> { + if (!content.contains("=")) { + return false; + } + + try { + checkProperties(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是properties类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkProperties(String content) { + if (isEmpty(content)) { + throw new RuntimeException("properties内容为空"); + } + if (!content.contains("=")) { + throw new RuntimeException("properties内容不包含\"=\""); + } + try { + checkYaml(propertiesToYaml(content)); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格properties类型;" + e.getMessage()); + } + } + + /** + * 判断是否是json类型 + * + * @param content 内容 + * @return true:是json类型,false:不是json类型 + */ + public boolean isJson(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJson", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return false; + } + + try { + checkJson(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json对象类型 + * + * @param content 内容 + * @return true:是json对象类型,false:不是json对象类型 + */ + public boolean isJsonObject(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJsonObject", content, () -> { + try { + checkJsonObject(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json数组类型 + * + * @param content 内容 + * @return true:是json数组类型,false:不是json数组类型 + */ + public boolean isJsonArray(String content) { + if (isEmpty(content)) { + return false; + } + return cacheCompute("isJsonArray", content, () -> { + try { + checkJsonArray(content); + return true; + } catch (RuntimeException e) { + return false; + } + }); + } + + /** + * 判断是否是json类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJson(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json类型,因为内容为空"); + } + + // 先核查是否是object + if (content.startsWith("{")) { + try { + JSON.parseObject(content); + } catch (Throwable e) { + throw new RuntimeException("json内容不是严格json对象类型;" + e.getMessage()); + } + } else if (content.startsWith("[")) { + try { + JSON.parseArray(content); + } catch (Throwable e) { + throw new RuntimeException("json内容不是严格json数组类型;" + e.getMessage()); + } + } else { + throw new RuntimeException("json内容不是json类型,因为没有\"{\"也没有\"[\"开头"); + } + } + + /** + * 判断是否是json对象类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJsonObject(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json对象类型,因为内容为空"); + } + try { + JSON.parseObject(content); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格json对象类型;" + e.getMessage()); + } + } + + /** + * 判断是否是json数组类型 + * + * @param content 内容 + * @throws RuntimeException 核查异常 + */ + public void checkJsonArray(String content) { + if (isEmpty(content)) { + throw new RuntimeException("json内容不是严格json数组类型,因为内容为空"); + } + try { + JSON.parseArray(content); + } catch (Throwable e) { + throw new RuntimeException("内容不是严格json对象类型;" + e.getMessage()); + } + } + + /** + * yaml格式转properties + * + * @param key key + * @param content 对应的yaml内容 + * @return properties内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToProperties(String key, String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", key, content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + Map dataMap = new HashMap<>(); + dataMap.put(key, yamlToList(content)); + return yamlToProperties(mapToYaml(dataMap)); + } + + return propertiesAppendPrefixKey(key, yamlToProperties(content)); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 properties异常:" + e.getMessage()); + } + }); + } + + /** + * yaml格式转换到properties + * + * @param content yaml内容 + * @return properties内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToProperties(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + throw new RuntimeException("不支持数组的yaml转properties"); + } + + List propertiesList = new ArrayList<>(); + Map remarkMap = new LinkedHashMap<>(); + Map valueMap = yamlToMap(content); + + // 读取yaml的注释 + yamlToRemarkMap(remarkMap, Yaml.createYamlInput(content).readYamlMapping(), ""); + formatYamlToProperties(propertiesList, remarkMap, valueMap, ""); + return propertiesList.stream().filter(e -> null != e && !"".equals(e)).reduce((a, b) -> a + NEW_LINE + b).orElse(""); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + public Properties yamlToPropertiesValue(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToProperties", content, () -> { + try { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + return null; + } + + Properties properties = new Properties(); + Map remarkMap = new LinkedHashMap<>(); + Map valueMap = yamlToMap(content); + + // 读取yaml的注释 + yamlToRemarkMap(remarkMap, Yaml.createYamlInput(content).readYamlMapping(), ""); + formatYamlToPropertiesValue(properties, remarkMap, valueMap, ""); + return properties; + } catch (Throwable e) { + throw new RuntimeException(e); + } + }); + } + + /** + * properties 转换到 yaml + * + * @param properties properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToYaml(Properties properties) { + if (properties.isEmpty()) { + return null; + } + return cacheCompute("propertiesToYaml", properties, () -> { + StringBuilder stringBuilder = new StringBuilder(); + properties.forEach((k, v) -> stringBuilder.append(k).append("=").append(v).append(NEW_LINE)); + return propertiesToYaml(stringBuilder.toString()); + }); + } + + /** + * properties类型转换到json + * + * @param properties properties 内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToJson(Properties properties) { + return yamlToJson(propertiesToYaml(properties)); + } + + /** + * properties内容转换到json + * + * @param content properties内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToJson(String content) { + return yamlToJson(propertiesToYaml(content)); + } + + /** + * properties内容转换到map + * + * @param content properties内容 + * @return map内容 + * @throws RuntimeException 转换异常 + */ + public Map propertiesToMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("propertiesToMap", content, () -> { + if (!content.contains("=")) { + return null; + } + + Map resultMap = new HashMap<>(); + List propertiesLineWordList = getPropertiesItemLineList(content); + + for (String line : propertiesLineWordList) { + String lineTem = line.trim(); + if (!"".equals(lineTem)) { + int index = lineTem.indexOf("="); + if (index > -1) { + String key = lineTem.substring(0, index); + String value = lineTem.substring(index + 1); + + // 对于yaml中换行的添加|用于保留换行 + if (value.contains("\n")) { + value = yaml_NEW_LINE_DOM + value; + } + resultMap.put(key, value); + } + } + } + return resultMap; + }); + } + + /** + * properties 转换到 yaml + * + * @param content properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String propertiesToYaml(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("propertiesToYaml", content, () -> { + if (!content.contains("=")) { + return null; + } + try { + List yamlLineList = new ArrayList<>(); + List propertiesLineWordList = getPropertiesItemLineList(content); + + List YamlNodes = new ArrayList<>(); + StringBuilder projectRemark = new StringBuilder(); + StringBuilder remark = new StringBuilder(); + for (String line : propertiesLineWordList) { + String lineTem = line.trim(); + if (!"".equals(lineTem)) { + if (lineTem.startsWith("#")) { + if (0 != remark.length()) { + projectRemark.append(remark.toString()); + remark.delete(0, remark.length()); + } + remark.append(lineTem); + continue; + } + int index = lineTem.indexOf("="); + if (index > -1) { + String key = lineTem.substring(0, index); + String value = lineTem.substring(index + 1); + // 对于yaml中换行的添加|用于保留换行 + if (value.contains("\n")) { + value = yaml_NEW_LINE_DOM + value; + } + + final List lineWordList = new ArrayList<>(Arrays.asList(key.split("\\."))); + wordToNode(lineWordList, YamlNodes, null, false, null, appendSpaceForArrayValue(value), projectRemark.toString(), remark.toString()); + } + // 删除本地保留 + remark.delete(0, remark.length()); + projectRemark.delete(0, projectRemark.length()); + } + } + formatPropertiesToYaml(yamlLineList, YamlNodes, false, ""); + return yamlLineList.stream().reduce((a, b) -> a + "\n" + b).orElse("") + "\n"; + } catch (Throwable e) { + throw new RuntimeException("properties 转换到 yaml异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转换到 对象 + * + * @param content properties内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public Object yamlToObject(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToObject", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + if (content.trim().startsWith("-")) { + return yamlToList(content); + } + + return yamlToMap(content); + }); + } + + /** + * yaml 转 map + * + * 由于eo-yaml对map转换支持会默认将一些key添加字符,这里就用snakeyaml工具做 + * @return map 对象 + * @throws RuntimeException 转换异常 + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map yamlToMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToMap", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + Map resultMap = new HashMap<>(); + org.yaml.snakeyaml.Yaml yml = new org.yaml.snakeyaml.Yaml(); + Map result = yml.loadAs(content, Map.class); + Set> entrySet = result.entrySet(); + for (Map.Entry entry : entrySet) { + resultMap.put(String.valueOf(entry.getKey()), entry.getValue()); + } + return resultMap; + } catch (Throwable e) { + throw new RuntimeException("yml 转换到 map 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转 map + * + * @param content yaml内容 + * @return 集合内容 + * @throws RuntimeException 转换异常 + */ + public List yamlToList(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToList", content, () -> { + if (!content.trim().startsWith("-")) { + return null; + } + + try { + org.yaml.snakeyaml.Yaml yml = new org.yaml.snakeyaml.Yaml(); + return yml.load(content); + } catch (Throwable e) { + throw new RuntimeException("yml 转换到 map 异常:" + e.getMessage()); + } + }); + } + + /** + * map 转 yaml + * + * @param contentMap map内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String mapToYaml(Map contentMap) { + if (isEmpty(contentMap)) { + return null; + } + return cacheCompute("yamlToList", contentMap, () -> { + try { + org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(); + String originalYaml = yaml.dumpAsMap(contentMap); + // 由于snakeyaml对数组缩进支持不够好,这里做一层缩进 + return yamlFormatForMap(originalYaml); + } catch (Throwable e) { + throw new RuntimeException("map 转换到 yml 异常:" + e.getMessage()); + } + }); + } + + /** + * 集合内容转yaml + * + * @param contentList 集合内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String listToYaml(List contentList) { + if (isEmpty(contentList)) { + return null; + } + return cacheCompute("listToYaml", contentList, () -> { + try { + org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml(); + return yaml.dumpAs(contentList, null, DumperOptions.FlowStyle.BLOCK); + } catch (Throwable e) { + throw new RuntimeException("map 转换到 yml 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml 转 json + * + * @param content yaml内容 + * @return json内容 + * @throws RuntimeException 转换异常 + */ + public String yamlToJson(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToJson", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + return JSON.toJSONString(yamlToObject(content)); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 json 异常:" + e.getMessage()); + } + }); + } + + /** + * json 转 对象 + * + * @param content json内容 + * @return object内容 + * @throws RuntimeException 转换异常 + */ + public Object jsonToObject(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("jsonToObject", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return null; + } + + try { + if (isJsonObject(content)) { + return JSON.parseObject(content); + } else if (isJsonArray(content)) { + return JSON.parseArray(content); + } + throw new RuntimeException("content 不是json类型"); + } catch (Throwable e) { + throw new RuntimeException("json 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + /** + * json 转 yaml + * + * @param content json内容 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String jsonToYaml(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("jsonToYaml", content, () -> { + if (!content.startsWith("{") && !content.startsWith("[")) { + return null; + } + + try { + if (isJsonObject(content)) { + return mapToYaml(JSON.parseObject(content)); + } else if (isJsonArray(content)) { + return listToYaml(JSON.parseArray(content)); + } + throw new RuntimeException("content 不是json类型"); + } catch (Throwable e) { + throw new RuntimeException("json 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + /** + * yaml类型转换为 k-v的集合 + * + * @param content yaml内容 + * @return kv集合 + * @throws RuntimeException 转换异常 + */ + public List> yamlToKVList(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlToKVList", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + try { + String propertiesContent = yamlToProperties(content); + return getPropertiesItemLineList(propertiesContent).stream().map(e -> { + int index = e.indexOf("="); + String key = e.substring(0, index); + String value = e.substring(index + 1); + return new AbstractMap.SimpleEntry<>(key, value); + }).collect(Collectors.toList()); + } catch (Throwable e) { + throw new RuntimeException("yaml 转换到 kv-list 异常:" + e.getMessage()); + } + }); + } + + /** + * k-v的集合类型转yaml + * + * @param kvStringList kv集合 + * @return yaml内容 + * @throws RuntimeException 转换异常 + */ + public String kvListToYaml(List> kvStringList) { + if (isEmpty(kvStringList)) { + return null; + } + return cacheCompute("kvListToYaml", kvStringList, () -> { + try { + String propertiesContent = kvStringList.stream().map(e -> e.getKey() + "=" + e.getValue()).reduce((a, b) -> a + "\n" + b).orElse(""); + return propertiesToYaml(propertiesContent); + } catch (Throwable e) { + throw new RuntimeException("kv-list 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + public String kvToYaml(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToYaml", key, value, () -> { + try { + return propertiesToYaml(kvToProperties(key, value, valueTypeEnum)); + } catch (Throwable e) { + throw new RuntimeException("kv 转换到 yaml 异常:" + e.getMessage()); + } + }); + } + + public Map kvToMap(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToMap", key, value, () -> propertiesToMap(kvToProperties(key, value, valueTypeEnum))); + } + + public String kvToProperties(String key, String value, ConfigValueTypeEnum valueTypeEnum) { + return cacheCompute("kvToProperties", key, value, () -> kvToProperties(key, value, null, valueTypeEnum)); + } + + /** + * k-v的String类型转properties + * + *

其中key可能是a.b.c这种,而value可能是各种各样的类型,我们这里通过valueType进行区分 + * + * @param key 主键 + * @param value 待转换的值 + * @param desc 注释 + * @param valueTypeEnum 值的类型,0:yaml,1:properties,2:json,3:string + * @return 转换之后的yaml类型 + * @throws RuntimeException 转换异常 + */ + public String kvToProperties(String key, String value, String desc, ConfigValueTypeEnum valueTypeEnum) { + if (isEmpty(key)) { + return null; + } + return cacheCompute("kvToProperties", key, value, () -> { + try { + // 将value对应的值先转换为properties类型,然后对key进行拼接,最后再统一转化为yaml格式 + StringBuilder propertiesResult = new StringBuilder(); + if (null != desc && !"".equals(desc)) { + propertiesResult.append("# ").append(desc).append("\n"); + } + + String propertiesContent; + switch (valueTypeEnum) { + case YAML: + propertiesContent = yamlToProperties(key, value); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case JSON: + propertiesContent = yamlToProperties(key, jsonToYaml(value)); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case PROPERTIES: + propertiesContent = propertiesAppendPrefixKey(key, value); + if (!isEmpty(propertiesContent)) { + propertiesResult.append(propertiesContent); + } + return propertiesResult.toString(); + case STRING: + propertiesResult.append(key).append("=").append(appendSpaceForArrayValue(value)); + return propertiesResult.toString(); + default: + break; + } + + return propertiesResult.toString(); + } catch (Throwable e) { + throw new RuntimeException("kv 转换到 properties 异常: " + e.getMessage()); + } + }); + } + + public List getPropertiesItemLineList(String content) { + if (isEmpty(content)) { + return Collections.emptyList(); + } + return cacheCompute("getPropertiesItemLineList", content, () -> { + if (!content.contains("=")) { + return Collections.emptyList(); + } + + String[] lineList = content.split(NEW_LINE); + List itemLineList = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(); + for (String line : lineList) { + // 处理多行数据 + if (line.endsWith("\\")) { + stringBuilder.append(line).append("\n"); + } else { + stringBuilder.append(line); + itemLineList.add(stringBuilder.toString()); + stringBuilder.delete(0, stringBuilder.length()); + } + } + return itemLineList; + }); + } + + private String propertiesAppendPrefixKey(String key, String propertiesContent) { + return getPropertiesItemLineList(propertiesContent).stream().filter(e -> e.contains("=")).map(e -> { + int index = e.indexOf("="); + if (index > -1) { + String keyTem = e.substring(0, index).trim(); + String valueTem = e.substring(index + 1).trim(); + return key + DOT + keyTem + "=" + valueTem; + } + return null; + }).filter(Objects::nonNull).reduce((a, b) -> a + NEW_LINE + b).orElse(null); + } + + /** + * 针对有些yaml格式不严格,这里做不严格向严格的eo-yaml解析的转换 + *

+ * 对{@code + * test: + * - k1: 12 + * - k2: 22 + * } + * 这种做一层缩进,由于snake的map转yaml后有缩进问题 + */ + private String yamlFormatForMap(String content) { + if (isEmpty(content)) { + return null; + } + return cacheCompute("yamlFormatForMap", content, () -> { + if (!content.contains(":") && !content.contains("-")) { + return null; + } + + StringBuilder stringBuilder = new StringBuilder(); + String[] items = content.split("\n"); + Integer blankSize = null; + // 判断是否在数组中 + boolean inArray = false; + for (String item : items) { + int innerBlankSize = item.substring(0, item.indexOf(item.trim())).length(); + // 数组 + if (item.trim().startsWith("- ")) { + if (inArray) { + // 多重数组,则多层嵌套 + if (innerBlankSize > blankSize) { + stringBuilder.append(INDENT_BLANKS).append(INDENT_BLANKS).append(item).append("\n"); + continue; + } + } + inArray = true; + blankSize = innerBlankSize; + } else { + // 其他的字符 + if (null != blankSize) { + if (innerBlankSize <= blankSize) { + inArray = false; + } + } + } + + if (inArray) { + stringBuilder.append(INDENT_BLANKS).append(item).append("\n"); + } else { + stringBuilder.append(item).append("\n"); + } + } + return stringBuilder.toString(); + }); + } + + /** + * 将key转换为yaml节点 + * + * @param lineWordList 待转换的key,比如{@code k1.k2.k3=123} + * @param nodeList 已经保存的节点数据 + * @param lastNodeArrayFlag 上一个节点是否数组类型 + * @param index 索引下标 + * @param value 解析的值 + * @param remark 当前value对应的注释 + */ + private void wordToNode(List lineWordList, List nodeList, YamlNode parentNode, Boolean lastNodeArrayFlag, Integer index, String value, String projectRemark, + String remark) { + if (lineWordList.isEmpty()) { + if (lastNodeArrayFlag) { + YamlNode node = new YamlNode(); + node.setValue(value); + node.setRemark(remark); + nodeList.add(node); + } + } else { + String nodeName = lineWordList.get(0); + + Pair nameAndIndex = peelArray(nodeName); + nodeName = nameAndIndex.getKey(); + Integer nextIndex = nameAndIndex.getValue(); + + YamlNode node = new YamlNode(); + node.setName(nodeName); + node.setProjectRemark(projectRemark); + node.setParent(parentNode); + node.setRemark(remark); + node.setLastNodeIndex(index); + lineWordList.remove(0); + + //如果节点下面的子节点数量为0,则为终端节点,也就是赋值节点 + if (lineWordList.size() == 0) { + if (null == nextIndex) { + node.setRemark(remark); + node.setValue(value); + } + } + + // nextIndex 不空,表示当前节点为数组,则之后的数据为他的节点数据 + if (null != nextIndex) { + node.setArrayFlag(true); + boolean hasEqualsName = false; + //遍历查询节点是否存在 + for (YamlNode YamlNode : nodeList) { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName()) && YamlNode.getArrayFlag()) { + Integer yamlNodeIndex = YamlNode.getLastNodeIndex(); + if (null == yamlNodeIndex || index.equals(yamlNodeIndex)) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getValueList(), node.getParent(), true, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } + } + //如果遍历结果为节点名称不存在,则递归添加剩下的数据节点,并把新节点添加到上级yamlTree的子节点中 + if (!hasEqualsName) { + wordToNode(lineWordList, node.getValueList(), node.getParent(), true, nextIndex, appendSpaceForArrayValue(value), null, remark); + nodeList.add(node); + } + } else { + boolean hasEqualsName = false; + //遍历查询节点是否存在 + for (YamlNode YamlNode : nodeList) { + if (!lastNodeArrayFlag) { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName())) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getChildren(), YamlNode, false, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } else { + //如果节点名称已存在,则递归添加剩下的数据节点 + if (nodeName.equals(YamlNode.getName())) { + Integer yamlNodeIndex = YamlNode.getLastNodeIndex(); + if (null == yamlNodeIndex || index.equals(yamlNodeIndex)) { + hasEqualsName = true; + wordToNode(lineWordList, YamlNode.getChildren(), YamlNode, true, nextIndex, appendSpaceForArrayValue(value), null, remark); + } + } + } + } + //如果遍历结果为节点名称不存在,则递归添加剩下的数据节点,并把新节点添加到上级yamlTree的子节点中 + if (!hasEqualsName) { + wordToNode(lineWordList, node.getChildren(), node, false, nextIndex, appendSpaceForArrayValue(value), null, remark); + nodeList.add(node); + } + } + } + } + + /** + * 获取yaml中的注释 + * + * @param remarkMap 解析后填充的注释map:key为a.b.c.d,value为对应的注释,去除掉前缀#后的数据 + * @param mapping yaml解析后数据 + * @param prefix 前缀 + */ + private void yamlToRemarkMap(Map remarkMap, YamlMapping mapping, String prefix) { + if (null == mapping) { + return; + } + for (com.amihaiemil.eoyaml.YamlNode node : mapping.keys()) { + String nodeName = node.asScalar().value(); + String remark = mapping.value(node).comment().value(); + + if (null != remark && !"".equals(remark)) { + remarkMap.put(wrapKey(prefix, nodeName), remark); + } + + yamlToRemarkMap(remarkMap, mapping.yamlMapping(node), wrapKey(prefix, nodeName)); + } + } + + private String wrapKey(String prefix, String value) { + if (isEmpty(prefix)) { + return prefix + "." + value; + } + return value; + } + + /** + * 解析节点名字,为数组则返回数组名和节点下标 + *

+ * name.test[0] 将test和0进行返回 + * + * @param nodeName 界面的名字 + * @return 如果是数组,则将数组名和解析后的下标返回 + */ + private Pair peelArray(String nodeName) { + String name = nodeName; + Integer index = null; + Matcher matcher = rangePattern.matcher(nodeName); + if (matcher.find()) { + String indexStr = matcher.group(2); + if (null != indexStr) { + index = Integer.valueOf(indexStr); + } + name = matcher.group(1); + } + + return new Pair<>(name, index); + } + + /** + * 将yaml对应的这种value进行添加前缀空格,其中value为key1对应的value + * {@code + * test: + * key1: | + * value1 + * value2 + * value3 + * } + * 对应的值 + * {@code + * | + * value1 + * value2 + * value3 + * } + * + * @param value 待转换的值比如{@code + * test: + * key1: | + * value1 + * value2 + * value3 + * } + * @return 添加前缀空格之后的处理 + * {@code + * | + * value1 + * value2 + * value3 + * } + */ + private String appendSpaceForArrayValue(String value) { + if (!value.startsWith(yaml_NEW_LINE_DOM)) { + return value; + } + String valueTem = value.substring(yaml_NEW_LINE_DOM.length()); + return yaml_NEW_LINE_DOM + Arrays.stream(valueTem.split("\\n")).map(e -> { + String tem = e; + if (e.endsWith("\\")) { + tem = e.substring(0, e.length() - 1); + } + return INDENT_BLANKS + tem; + }).reduce((a, b) -> a + "\n" + b).orElse(valueTem); + } + + private void formatPropertiesToYaml(List yamlLineList, List YamlNodes, Boolean lastNodeArrayFlag, String blanks) { + Integer beforeNodeIndex = null; + String equalSign; + for (YamlNode YamlNode : YamlNodes) { + String value = YamlNode.getValue(); + String remark = YamlNode.getRemark(); + + equalSign = SIGN_SEMICOLON; + if (null == value || "".equals(value)) { + value = ""; + } else { + equalSign = SIGN_SEMICOLON + " "; + } + YamlNode.resortValue(); + + String name = YamlNode.getName(); + if (lastNodeArrayFlag) { + if (null == name) { + yamlLineList.add(blanks + ARRAY_BLANKS + stringValueWrap(value)); + } else { + if (null != beforeNodeIndex && beforeNodeIndex.equals(YamlNode.getLastNodeIndex())) { + yamlLineList.add(blanks + INDENT_BLANKS + name + equalSign + stringValueWrap(value)); + } else { + yamlLineList.add(blanks + ARRAY_BLANKS + name + equalSign + stringValueWrap(value)); + } + } + beforeNodeIndex = YamlNode.getLastNodeIndex(); + } else { + // 父节点为空,表示,当前为顶层 + if (null == YamlNode.getParent()) { + String remarkTem = getRemarkProject(YamlNode.getProjectRemark()); + if (!"".equals(remarkTem)) { + yamlLineList.add(blanks + getRemarkProject(YamlNode.getProjectRemark())); + } + } + + // 自己节点为数组,则添加对应的注释 + if (YamlNode.getArrayFlag()) { + if (null != remark && !"".equals(remark)) { + yamlLineList.add(blanks + remark); + } + } + yamlLineList.add(blanks + name + equalSign + stringValueWrap(value, remark)); + } + + if (YamlNode.getArrayFlag()) { + if (lastNodeArrayFlag) { + formatPropertiesToYaml(yamlLineList, YamlNode.getValueList(), true, INDENT_BLANKS + INDENT_BLANKS + blanks); + } else { + formatPropertiesToYaml(yamlLineList, YamlNode.getValueList(), true, INDENT_BLANKS + blanks); + } + } else { + if (lastNodeArrayFlag) { + formatPropertiesToYaml(yamlLineList, YamlNode.getChildren(), false, INDENT_BLANKS + INDENT_BLANKS + blanks); + } else { + formatPropertiesToYaml(yamlLineList, YamlNode.getChildren(), false, INDENT_BLANKS + blanks); + } + } + } + } + + @SuppressWarnings("rawtypes") + private void formatYamlToPropertiesValue(Properties properties, Map remarkMap, Object object, String prefix) { + if (null == object) { + return; + } + if (object instanceof Map) { + Map map = (Map) object; + Set set = map.keySet(); + for (Object key : set) { + Object value = map.get(key); + if (null == value) { + value = ""; + } + if (value instanceof Map) { + formatYamlToPropertiesValue(properties, remarkMap, value, prefixWithDOT(prefix) + key); + } else if (value instanceof Collection) { + Collection collection = (Collection) value; + if (!collection.isEmpty()) { + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToPropertiesValue(properties, remarkMap, valueObject, prefixWithDOT(prefix) + key + "[" + index + "]"); + index = index + 1; + } + } + } else if (value instanceof String) { + String valueStr = (String) value; + valueStr = valueStr.trim(); + valueStr = valueStr.replace("\n", "\\\n"); + properties.put(prefixWithDOT(prefix) + key, valueStr); + } else { + properties.put(prefixWithDOT(prefix) + key, value); + } + } + } else if (object instanceof Collection) { + Collection collection = (Collection) object; + if (!collection.isEmpty()) { + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToPropertiesValue(properties, remarkMap, valueObject, prefix + "[" + index + "]"); + index = index + 1; + } + } + } else if (object.getClass().isArray()) { + Object[] array = (Object[]) object; + for (int index = 0; index < array.length; index++) { + formatYamlToPropertiesValue(properties, remarkMap, array[index], prefix + "[" + index + "]"); + } + } else if (object instanceof String) { + String valueObject = (String) object; + valueObject = valueObject.replace("\n", "\\\n"); + properties.put(prefix, valueObject); + } else { + properties.put(prefix, object); + } + } + + @SuppressWarnings("rawtypes") + private void formatYamlToProperties(List propertiesLineList, Map remarkMap, Object object, String prefix) { + if (null == object) { + return; + } + if (object instanceof Map) { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + Map map = (Map) object; + Set set = map.keySet(); + for (Object key : set) { + Object value = map.get(key); + if (null == value) { + value = ""; + } + if (value instanceof Map) { + formatYamlToProperties(propertiesLineList, remarkMap, value, prefixWithDOT(prefix) + key); + } else if (value instanceof Collection) { + Collection collection = (Collection) value; + if (!collection.isEmpty()) { + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToProperties(propertiesLineList, remarkMap, valueObject, prefixWithDOT(prefix) + key + "[" + index + "]"); + index = index + 1; + } + } + } else if (value instanceof String) { + String valueStr = (String) value; + valueStr = valueStr.trim(); + valueStr = valueStr.replace("\n", "\\\n"); + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + propertiesLineList.add(prefixWithDOT(prefix) + key + SIGN_EQUAL + valueStr); + } else { + // 填充注释 + if (remarkMap.containsKey(prefixWithDOT(prefix) + key)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefixWithDOT(prefix) + key)); + } + + propertiesLineList.add(prefixWithDOT(prefix) + key + SIGN_EQUAL + value); + } + } + } else if (object instanceof Collection) { + Collection collection = (Collection) object; + if (!collection.isEmpty()) { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + Iterator iterator = collection.iterator(); + int index = 0; + while (iterator.hasNext()) { + Object valueObject = iterator.next(); + formatYamlToProperties(propertiesLineList, remarkMap, valueObject, prefix + "[" + index + "]"); + index = index + 1; + } + } + } else if (object.getClass().isArray()) { + Object[] array = (Object[]) object; + for (int index = 0; index < array.length; index++) { + formatYamlToProperties(propertiesLineList, remarkMap, array[index], prefix + "[" + index + "]"); + } + } else if (object instanceof String) { + String valueObject = (String) object; + valueObject = valueObject.replace("\n", "\\\n"); + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + propertiesLineList.add(prefix + SIGN_EQUAL + valueObject); + } else { + // 填充注释 + if (remarkMap.containsKey(prefix)) { + propertiesLineList.add(REMARK_PRE + remarkMap.get(prefix)); + } + + propertiesLineList.add(prefix + SIGN_EQUAL + object); + } + } + + private String prefixWithDOT(String prefix) { + if ("".equals(prefix)) { + return prefix; + } + return prefix + DOT; + } + + private String stringValueWrap(String value) { + if (isEmpty(value)) { + return ""; + } + // 对数组的数据进行特殊处理 + if (value.startsWith("[") && value.endsWith("]")) { + return "'" + value + "'"; + } + return value; + } + + private String stringValueWrap(String value, String remark) { + if (isEmpty(value)) { + return ""; + } + // 对数组的数据进行特殊处理 + if (value.startsWith("[") && value.endsWith("]")) { + return "'" + value + "'" + getRemark(remark); + } + + return value + getRemark(remark); + } + + private String getRemark(String remark) { + if (null != remark && !"".endsWith(remark) && remark.startsWith("#")) { + return " # " + remark.substring(1).trim(); + } else { + return ""; + } + } + + private String getRemarkProject(String remark) { + if (null != remark && !"".endsWith(remark) && remark.startsWith("#")) { + return " # " + remark.substring(1).trim(); + } else { + return ""; + } + } + + /** + * 数据存在则返回,不存在则计算后添加到缓存中 + */ + @SuppressWarnings("unchecked") + private T cacheCompute(String funName, Object key, Supplier biFunction) { + String cacheKey = buildCacheKey(funName, key); + if (typeContentMap.containsKey(cacheKey)) { + return (T) typeContentMap.get(cacheKey); + } + T result = biFunction.get(); + if (null != result) { + typeContentMap.put(cacheKey, result); + } + return result; + } + + @SuppressWarnings("unchecked") + private T cacheCompute(String funName, Object key, Object value, Supplier biFunction) { + String cacheKey = buildCacheKey(funName, key, value); + if (typeContentMap.containsKey(cacheKey)) { + return (T) typeContentMap.get(cacheKey); + } + T result = biFunction.get(); + if (null != result) { + typeContentMap.put(cacheKey, result); + } + return result; + } + + private String buildCacheKey(String funName, Object... parameters) { + StringBuilder stringBuilder = new StringBuilder(funName); + for (Object parameter : parameters) { + if (null != parameter) { + stringBuilder.append(":").append(parameter.toString()); + } + } + return stringBuilder.toString(); + } + + private boolean isEmpty(String string) { + return null == string || "".endsWith(string); + } + + private boolean isEmpty(Collection collection) { + return null == collection || collection.isEmpty(); + } + + private boolean isEmpty(Map map) { + return null == map || map.isEmpty(); + } + + @Data + class YamlNode { + + private YamlNode parent; + /** + * 只有parent为null时候,该值才可能有值 + */ + private String projectRemark; + /** + * name + */ + private String name; + /** + * value + */ + private String value; + /** + * 注释 + */ + private String remark; + + /** + * 子节点 + */ + private List children = new ArrayList<>(); + + /** + * 数组标示: + */ + private Boolean arrayFlag = false; + /** + * 存储的数组中的前一个节点的下标 + */ + private Integer lastNodeIndex; + /** + * 只有数组标示为true,下面的value才有值 + */ + private List valueList = new ArrayList<>(); + + /** + * 将其中的value按照index下标顺序进行重拍 + */ + public void resortValue() { + if (!arrayFlag || valueList.isEmpty()) { + return; + } + + // 升序 + valueList.sort((a, b) -> { + if (null == a.getLastNodeIndex() || null == b.getLastNodeIndex()) { + return 0; + } + + return a.getLastNodeIndex() - b.getLastNodeIndex(); + }); + + // 是数组的节点也循环下 + valueList.forEach(YamlNode::resortValue); + } + } +}