Écriture du configurator, et tests avec murderator
This commit is contained in:
parent
a369ab678f
commit
ef20b6f897
@ -1,35 +1,115 @@
|
|||||||
package com.bernard.configurator;
|
package com.bernard.configurator;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.amihaiemil.eoyaml.Yaml;
|
import com.amihaiemil.eoyaml.Yaml;
|
||||||
import com.amihaiemil.eoyaml.YamlInput;
|
import com.amihaiemil.eoyaml.YamlMapping;
|
||||||
import com.amihaiemil.eoyaml.YamlMappingBuilder;
|
import com.amihaiemil.eoyaml.YamlMappingBuilder;
|
||||||
import com.amihaiemil.eoyaml.YamlNode;
|
import com.amihaiemil.eoyaml.YamlNode;
|
||||||
|
import com.amihaiemil.eoyaml.exceptions.YamlReadingException;
|
||||||
import com.bernard.configurator.annotations.ConfigClass;
|
import com.bernard.configurator.annotations.ConfigClass;
|
||||||
import com.bernard.configurator.annotations.Option;
|
import com.bernard.configurator.annotations.Option;
|
||||||
import com.bernard.util.ParseUtils;
|
import com.bernard.util.ParseUtils;
|
||||||
|
import com.bernard.util.YamlUtils;
|
||||||
|
|
||||||
public class Configurator {
|
public class Configurator {
|
||||||
|
|
||||||
private static Map<Class<?>,YamlInterface> yamlInterfaces = new HashMap<>();
|
public static Map<Predicate<Type>,YamlInterface> yamlInterfaces = new HashMap<>();
|
||||||
|
|
||||||
|
//TODO: Ajouter le support des @Contrainte
|
||||||
|
|
||||||
|
static {
|
||||||
|
genInterfaces();
|
||||||
|
}
|
||||||
|
|
||||||
public static void genInterfaces() {
|
public static void genInterfaces() {
|
||||||
yamlInterfaces.put(null, null);
|
addStringedInterface(byte.class, b -> b.toString(), Byte::parseByte);
|
||||||
|
addStringedInterface(short.class, s -> s.toString(), Short::parseShort);
|
||||||
|
addStringedInterface(int.class,i -> i.toString(), Integer::parseInt);
|
||||||
|
addStringedInterface(long.class, l -> l.toString(), Long::parseLong);
|
||||||
|
addStringedInterface(float.class, f -> f.toString(), Float::parseFloat);
|
||||||
|
addStringedInterface(double.class, d -> d.toString(), Double::parseDouble);
|
||||||
|
addStringedInterface(boolean.class, b -> b.toString(), s -> s.equalsIgnoreCase("true"));
|
||||||
|
|
||||||
|
addStringedInterface(Byte.class, b -> b.toString(), Byte::parseByte);
|
||||||
|
addStringedInterface(Short.class, s -> s.toString(), Short::parseShort);
|
||||||
|
addStringedInterface(Integer.class,i -> i.toString(), Integer::parseInt);
|
||||||
|
addStringedInterface(Long.class, l -> l.toString(), Long::parseLong);
|
||||||
|
addStringedInterface(Float.class, f -> f.toString(), Float::parseFloat);
|
||||||
|
addStringedInterface(Double.class, d -> d.toString(), Double::parseDouble);
|
||||||
|
addStringedInterface(Boolean.class, b -> b.toString(), s -> s.equalsIgnoreCase("true"));
|
||||||
|
|
||||||
|
addStringedInterface(String.class, Function.identity(), Function.identity());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addStringedInterface(Color.class, c -> String.format("%#08x", c.getRGB()) , s -> new Color(Integer.parseUnsignedInt(s.substring(2),16)));
|
||||||
|
|
||||||
|
addStringedInterface(Dimension.class,
|
||||||
|
d -> d.width+"x"+d.height,
|
||||||
|
s -> {String[] spt = s.split("x");
|
||||||
|
return new Dimension(Integer.parseInt(spt[0]), Integer.parseUnsignedInt(spt[1]));
|
||||||
|
});
|
||||||
|
|
||||||
|
addInterface(Font.class,
|
||||||
|
f -> Yaml.createYamlMappingBuilder()
|
||||||
|
.add("name", f.getName())
|
||||||
|
.add("style", Integer.toString(f.getStyle()))
|
||||||
|
.add("size", Integer.toString(f.getSize()))
|
||||||
|
.build(),
|
||||||
|
n -> new Font(n.asMapping().string("name"),
|
||||||
|
n.asMapping().integer("style"),
|
||||||
|
n.asMapping().integer("size")));
|
||||||
|
|
||||||
|
|
||||||
|
// Types composés
|
||||||
|
// On est obligés de faire des prédicats plus compliqués
|
||||||
|
yamlInterfaces.put(t -> List.class.isAssignableFrom(t.getClass()),new ListYamlInterface());
|
||||||
|
yamlInterfaces.put(t -> Map.class.isAssignableFrom(t.getClass()),new MapYamlInterface());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void addInterface(Class<T> clz,
|
||||||
|
Function<T, YamlNode> writer,
|
||||||
|
Function<YamlNode, T> reader) {
|
||||||
|
yamlInterfaces.put(t -> ((Class<?>)t).isAssignableFrom(clz), new YamlInterfaceImpl<>(writer, (n,c)->reader.apply(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void addStringedInterface(Class<T> clz,
|
||||||
|
Function<T, String> writer,
|
||||||
|
Function<String, T> reader) {
|
||||||
|
yamlInterfaces.put(t -> ((Class<?>)t).isAssignableFrom(clz), new YamlInterfaceImpl<>(
|
||||||
|
o -> Yaml.createYamlScalarBuilder().addLine(writer.apply(o)).buildPlainScalar(),
|
||||||
|
(n,c) -> reader.apply(n.asScalar().value())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static YamlNode objectToNode(Object o) {
|
public static YamlNode objectToNode(Object o) {
|
||||||
Class<?> theClass = o.getClass();
|
Class<?> theClass = o.getClass();
|
||||||
if(yamlInterfaces.containsKey(theClass)) {
|
Optional<Predicate<Type>> tt = yamlInterfaces.keySet().stream().filter(p -> p.test(theClass)).findAny();
|
||||||
return yamlInterfaces.get(theClass).writeToNode(o);
|
System.out.println("La classe est "+theClass);
|
||||||
|
if(tt.isPresent()) {
|
||||||
|
return yamlInterfaces.get(tt.get()).writeToNode(o);
|
||||||
}else {
|
}else {
|
||||||
|
|
||||||
ConfigClass configClass = theClass.getAnnotation(ConfigClass.class);
|
ConfigClass configClass = theClass.getAnnotation(ConfigClass.class);
|
||||||
|
|
||||||
if(configClass!=null) {
|
if(configClass!=null) {
|
||||||
|
|
||||||
// On fait un mapping
|
// On fait un mapping
|
||||||
@ -38,6 +118,9 @@ public class Configurator {
|
|||||||
|
|
||||||
Field[] fields = theClass.getFields(); // Seulement les accéssibles
|
Field[] fields = theClass.getFields(); // Seulement les accéssibles
|
||||||
for(Field f : fields) {
|
for(Field f : fields) {
|
||||||
|
int fm = f.getModifiers();
|
||||||
|
if(!Modifier.isPublic(fm) || Modifier.isStatic(fm) || Modifier.isTransient(fm))
|
||||||
|
continue;
|
||||||
try {
|
try {
|
||||||
Option option = f.getAnnotation(Option.class);
|
Option option = f.getAnnotation(Option.class);
|
||||||
if(option!=null) {
|
if(option!=null) {
|
||||||
@ -59,8 +142,10 @@ public class Configurator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//Traitement récursif
|
//Traitement récursif
|
||||||
|
Object of = f.get(o);
|
||||||
|
if(of != null)
|
||||||
ymb = ymb.add(f.getName(), objectToNode(f.get(o)));
|
ymb = ymb.add(f.getName(), objectToNode(f.get(o)));
|
||||||
|
//Sinon, on ne met pas le champ et c'est la valeur par défaut
|
||||||
|
|
||||||
} catch (InstantiationException
|
} catch (InstantiationException
|
||||||
| IllegalAccessException
|
| IllegalAccessException
|
||||||
@ -68,8 +153,6 @@ public class Configurator {
|
|||||||
| InvocationTargetException
|
| InvocationTargetException
|
||||||
| NoSuchMethodException
|
| NoSuchMethodException
|
||||||
| SecurityException e) {
|
| SecurityException e) {
|
||||||
|
|
||||||
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,15 +169,192 @@ public class Configurator {
|
|||||||
.buildFoldedBlockScalar("Objet écrit en base 64");
|
.buildFoldedBlockScalar("Objet écrit en base 64");
|
||||||
|
|
||||||
}else {
|
}else {
|
||||||
|
System.err.println("Impossible de traduire cette classe "+o.getClass());
|
||||||
//XXX: prévenir peut-être
|
//XXX: prévenir peut-être
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T readYaml(YamlInput in, Class<? extends T> outClass) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T readYaml(YamlNode in, Type theType) {
|
||||||
|
|
||||||
|
Optional<Predicate<Type>> tt = yamlInterfaces.keySet().stream().filter(p -> p.test(theType)).findAny();
|
||||||
|
System.out.println("La classe est "+theType);
|
||||||
|
if(tt.isPresent()) {
|
||||||
|
try {
|
||||||
|
return (T) yamlInterfaces.get(tt.get()).readNode(in,theType);
|
||||||
|
}catch(ClassCastException e) {
|
||||||
|
System.err.println("La classe "+theType.getTypeName()+" est associée à une mauvaise interface !!!");
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
|
||||||
|
Class<?> theClass = (Class<?>)theType;
|
||||||
|
|
||||||
|
ConfigClass configClass = theClass.getAnnotation(ConfigClass.class);
|
||||||
|
|
||||||
|
if(configClass!=null) {
|
||||||
|
|
||||||
|
// On fait un mapping
|
||||||
|
YamlMapping ym;
|
||||||
|
try {
|
||||||
|
ym = in.asMapping();
|
||||||
|
}catch (YamlReadingException e){
|
||||||
|
System.err.println("Une classe étant une @ConfigClass ne peut être représentée que par un mapping !");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field[] fields = theClass.getFields(); //Seulement les accéssibles
|
||||||
|
T theObject = null;
|
||||||
|
try {
|
||||||
|
theObject = (T) theClass.getConstructor().newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||||
|
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||||
|
System.err.println("Impossible de créer l'objet de base, il est censé nous fournir"
|
||||||
|
+ "un constructeur vide utilisable.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
for(Field f : fields) {
|
||||||
|
int fm = f.getModifiers();
|
||||||
|
if(!Modifier.isPublic(fm) || Modifier.isStatic(fm) || Modifier.isTransient(fm))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Option option = f.getAnnotation(Option.class);
|
||||||
|
if(option!=null) {
|
||||||
|
|
||||||
|
if(option.yamlInterface() != YamlInterface.class) {
|
||||||
|
YamlInterface theInterface;
|
||||||
|
theInterface = option.yamlInterface().getConstructor().newInstance();
|
||||||
|
String name;
|
||||||
|
if(option.name().isBlank())
|
||||||
|
name = f.getName();
|
||||||
|
else
|
||||||
|
name = option.name();
|
||||||
|
YamlNode theNode = ym.value(name);
|
||||||
|
Object of = theInterface.readNode(theNode,f.getDeclaringClass());
|
||||||
|
f.set(theObject, of);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//On ne continue pas, même traitement que sans option
|
||||||
|
}else {
|
||||||
|
if(configClass.requireOption())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Traitement récursif
|
||||||
|
Object of = readYaml(ym.value(f.getName()),f.getType());
|
||||||
|
f.set(theObject,of);
|
||||||
|
|
||||||
|
} catch (InstantiationException
|
||||||
|
| IllegalAccessException
|
||||||
|
| IllegalArgumentException
|
||||||
|
| InvocationTargetException
|
||||||
|
| NoSuchMethodException
|
||||||
|
| SecurityException e) {
|
||||||
|
|
||||||
|
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return theObject;
|
||||||
|
|
||||||
|
}else if(Serializable.class.isAssignableFrom(theClass)) {
|
||||||
|
|
||||||
|
// On doit récupérer un dump en Base64
|
||||||
|
String b64desc = in.asScalar().value();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (T) ParseUtils.fromBase64(b64desc);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
System.err.println("Ce dump n'a pas la bonne version");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}else {
|
||||||
|
System.err.println("Impossible de comprendre ce type de classe !");
|
||||||
|
//XXX: prévenir peut-être
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class YamlInterfaceImpl<T> implements YamlInterface{
|
||||||
|
|
||||||
|
Function<T,YamlNode> writer;
|
||||||
|
BiFunction<YamlNode,Class<?>,T> reader;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public YamlInterfaceImpl(Function<T, YamlNode> writer, BiFunction<YamlNode,Class<?>, T> reader) {
|
||||||
|
this.writer = writer;
|
||||||
|
this.reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public YamlNode writeToNode(Object o) {
|
||||||
|
return writer.apply((T) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object readNode(YamlNode node, Type clazz) {
|
||||||
|
return reader.apply(node,clazz.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private static class ListYamlInterface implements YamlInterface{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public YamlNode writeToNode(Object o) {
|
||||||
|
List<?> l = (List<?>) o;
|
||||||
|
return l.stream().map(Configurator::objectToNode).collect(YamlUtils.sequenceCollector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object readNode(YamlNode node, Type theClass) {
|
||||||
|
if(node.asSequence().size()==0)
|
||||||
|
return new ArrayList<>();
|
||||||
|
return YamlUtils.stream(node.asSequence())
|
||||||
|
.map(nn -> Configurator.readYaml(nn, ((ParameterizedType)theClass).getActualTypeArguments()[0]))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MapYamlInterface implements YamlInterface{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public YamlNode writeToNode(Object o) {
|
||||||
|
Map<?,?> m = (Map<?,?>) o;
|
||||||
|
|
||||||
|
YamlMappingBuilder ymb = Yaml.createYamlMappingBuilder();
|
||||||
|
for(Entry<?,?> e : m.entrySet()) {
|
||||||
|
ymb = ymb.add(
|
||||||
|
Configurator.objectToNode(e.getKey()),
|
||||||
|
Configurator.objectToNode(e.getValue())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ymb.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object readNode(YamlNode node, Type theClass) {
|
||||||
|
Set<YamlNode> keys = node.asMapping().keys();
|
||||||
|
|
||||||
|
Map<? extends Object,? extends Object> theMap = new HashMap<>();
|
||||||
|
for(YamlNode key : keys) {
|
||||||
|
theMap.put(
|
||||||
|
Configurator.readYaml(key, ((ParameterizedType)theClass).getActualTypeArguments()[0]) ,
|
||||||
|
Configurator.readYaml(node.asMapping().value(key),
|
||||||
|
((ParameterizedType)theClass).getActualTypeArguments()[1])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return theMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
package com.bernard.configurator;
|
package com.bernard.configurator;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
import com.amihaiemil.eoyaml.YamlNode;
|
import com.amihaiemil.eoyaml.YamlNode;
|
||||||
|
|
||||||
public interface YamlInterface {
|
public interface YamlInterface {
|
||||||
|
|
||||||
YamlNode writeToNode(Object o);
|
YamlNode writeToNode(Object o);
|
||||||
|
|
||||||
Object readNode(YamlNode node);
|
Object readNode(YamlNode node,Type theType);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,11 @@ package com.bernard.configurator.annotations;
|
|||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(TYPE)
|
@Target(TYPE)
|
||||||
public @interface ConfigClass {
|
public @interface ConfigClass {
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,12 @@ package com.bernard.configurator.annotations;
|
|||||||
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(FIELD)
|
@Target(FIELD)
|
||||||
public @interface Contrainte {
|
public @interface Contrainte {
|
||||||
|
|
||||||
|
|||||||
@ -2,10 +2,13 @@ package com.bernard.configurator.annotations;
|
|||||||
|
|
||||||
import static java.lang.annotation.ElementType.FIELD;
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import com.bernard.configurator.YamlInterface;
|
import com.bernard.configurator.YamlInterface;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(FIELD)
|
@Target(FIELD)
|
||||||
public @interface Option {
|
public @interface Option {
|
||||||
|
|
||||||
|
|||||||
@ -8,5 +8,15 @@ public class FuncUtils {
|
|||||||
return f.compose(g);
|
return f.compose(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Class<?> unprimitive(Class<?> clz){
|
||||||
|
if(clz==byte.class)return Byte.class;
|
||||||
|
if(clz==short.class)return Short.class;
|
||||||
|
if(clz==int.class)return Integer.class;
|
||||||
|
if(clz==long.class)return Long.class;
|
||||||
|
if(clz==float.class)return Float.class;
|
||||||
|
if(clz==double.class)return Double.class;
|
||||||
|
if(clz==boolean.class)return Boolean.class;
|
||||||
|
return clz;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,15 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.Spliterators;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collector;
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import com.amihaiemil.eoyaml.Yaml;
|
import com.amihaiemil.eoyaml.Yaml;
|
||||||
import com.amihaiemil.eoyaml.YamlMapping;
|
import com.amihaiemil.eoyaml.YamlMapping;
|
||||||
@ -61,6 +65,10 @@ public class YamlUtils {
|
|||||||
return ysbb;
|
return ysbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final Stream<YamlNode> stream(YamlSequence seq) {
|
||||||
|
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(seq.iterator(), Spliterator.ORDERED), false);
|
||||||
|
}
|
||||||
|
|
||||||
public static final CollectorImpl<YamlNode, YamlSequenceBuilder, YamlSequence> sequenceCollector =
|
public static final CollectorImpl<YamlNode, YamlSequenceBuilder, YamlSequence> sequenceCollector =
|
||||||
new CollectorImpl<>(
|
new CollectorImpl<>(
|
||||||
Yaml::createYamlSequenceBuilder,
|
Yaml::createYamlSequenceBuilder,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user