From e4408182b7c5271be25cf9bd2387900ec890b984 Mon Sep 17 00:00:00 2001 From: Olga Stepanova Date: Mon, 12 May 2014 17:47:36 +0600 Subject: [PATCH 001/114] Added generics support. Added possibility to never write classname tags --- pom.xml | 3 +- src/com/esotericsoftware/yamlbeans/Beans.java | 199 ++++++--- .../yamlbeans/WriteClassName.java | 5 + .../yamlbeans/YamlConfig.java | 12 +- .../yamlbeans/YamlReader.java | 381 +++++++++++------- .../yamlbeans/YamlWriter.java | 105 +++-- 6 files changed, 443 insertions(+), 262 deletions(-) create mode 100644 src/com/esotericsoftware/yamlbeans/WriteClassName.java diff --git a/pom.xml b/pom.xml index fe3c8e7..3e029fa 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.05 + 1.05-generics jar Java object graphs, to and from YAML @@ -63,4 +63,5 @@ + diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 7aa2a59..8e99413 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -25,7 +25,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -38,23 +40,27 @@ /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ class Beans { - private Beans () { + private Beans() { } - static public boolean isScalar (Class c) { + static public boolean isScalar(Class c) { return c.isPrimitive() || c == String.class || c == Integer.class || c == Boolean.class || c == Float.class - || c == Long.class || c == Double.class || c == Short.class || c == Byte.class || c == Character.class; + || c == Long.class || c == Double.class || c == Short.class || c == Byte.class || c == Character.class; } - static public DeferredConstruction getDeferredConstruction (Class type, YamlConfig config) { + static public DeferredConstruction getDeferredConstruction(Class type, YamlConfig config) { ConstructorParameters parameters = config.readConfig.constructorParameters.get(type); - if (parameters != null) return new DeferredConstruction(parameters.constructor, parameters.parameterNames); + if (parameters != null) { + return new DeferredConstruction(parameters.constructor, parameters.parameterNames); + } try { Class constructorProperties = Class.forName("java.beans.ConstructorProperties"); for (Constructor typeConstructor : type.getConstructors()) { Annotation annotation = typeConstructor.getAnnotation(constructorProperties); - if (annotation == null) continue; - String[] parameterNames = (String[])constructorProperties.getMethod("value").invoke(annotation, (Object[])null); + if (annotation == null) { + continue; + } + String[] parameterNames = (String[]) constructorProperties.getMethod("value").invoke(annotation, (Object[]) null); return new DeferredConstruction(typeConstructor, parameterNames); } } catch (Exception ignored) { @@ -62,17 +68,21 @@ static public DeferredConstruction getDeferredConstruction (Class type, YamlConf return null; } - static private boolean canInitializeProperty (Class type, PropertyDescriptor property, YamlConfig config) { - if (property.getWriteMethod() != null) return true; + static private boolean canInitializeProperty(Class type, PropertyDescriptor property, YamlConfig config) { + if (property.getWriteMethod() != null) { + return true; + } // Check if the property can be initialized through the constructor. DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) return true; + if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) { + return true; + } return false; } - static public Object createObject (Class type, boolean privateConstructors) throws InvocationTargetException { + static public Object createObject(Class type, boolean privateConstructors) throws InvocationTargetException { // Use no-arg constructor. Constructor constructor = null; for (Constructor typeConstructor : type.getConstructors()) { @@ -107,8 +117,9 @@ static public Object createObject (Class type, boolean privateConstructors) thro } } - if (constructor == null) + if (constructor == null) { throw new InvocationTargetException(null, "Unable to find a no-arg constructor for class: " + type.getName()); + } try { return constructor.newInstance(); @@ -117,20 +128,28 @@ static public Object createObject (Class type, boolean privateConstructors) thro } } - static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { - if (type == null) throw new IllegalArgumentException("type cannot be null."); + static public Set getProperties(Class type, boolean beanProperties, boolean privateFields, YamlConfig config) + throws IntrospectionException { + if (type == null) { + throw new IllegalArgumentException("type cannot be null."); + } Set properties = new TreeSet(); if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) + for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { + if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) { properties.add(new MethodProperty(type, property)); + } + } } for (Field field : getAllFields(type)) { int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { + continue; + } if (!Modifier.isPublic(modifiers)) { - if (!privateFields) continue; + if (!privateFields) { + continue; + } field.setAccessible(true); } properties.add(new FieldProperty(field)); @@ -138,32 +157,46 @@ static public Set getProperties (Class type, boolean beanProperties, b return properties; } - static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { - if (type == null) throw new IllegalArgumentException("type cannot be null."); - if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); + static public Property getProperty(Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) + throws IntrospectionException { + if (type == null) { + throw new IllegalArgumentException("type cannot be null."); + } + if (name == null || name.length() == 0) { + throw new IllegalArgumentException("name cannot be null or empty."); + } if (beanProperties) { for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { if (property.getName().equals(name)) { - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) + if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) { + Type genericType = property.getReadMethod().getGenericReturnType(); + return new MethodProperty(type, property); + } break; } } } for (Field field : getAllFields(type)) { int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { + continue; + } if (!Modifier.isPublic(modifiers)) { - if (!privateFields) continue; + if (!privateFields) { + continue; + } field.setAccessible(true); } - if (field.getName().equals(name)) return new FieldProperty(field); + if (field.getName().equals(name)) { + return new FieldProperty(field); + } + Type genericType = field.getGenericType(); } return null; } - static private ArrayList getAllFields (Class type) { + static private ArrayList getAllFields(Class type) { ArrayList allFields = new ArrayList(); Class nextClass = type; while (nextClass != Object.class) { @@ -176,20 +209,20 @@ static private ArrayList getAllFields (Class type) { static public class MethodProperty extends Property { private final PropertyDescriptor property; - public MethodProperty (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { + public MethodProperty(Class declaringClass, PropertyDescriptor property) throws IntrospectionException { super(declaringClass, property); this.property = property; } - public void set (Object object, Object value) throws Exception { + public void set(Object object, Object value) throws Exception { if (object instanceof DeferredConstruction) { - ((DeferredConstruction)object).storeProperty(this, value); + ((DeferredConstruction) object).storeProperty(this, value); return; } property.getWriteMethod().invoke(object, value); } - public Object get (Object object) throws Exception { + public Object get(Object object) throws Exception { return property.getReadMethod().invoke(object); } } @@ -197,20 +230,20 @@ public Object get (Object object) throws Exception { static public class FieldProperty extends Property { private final Field field; - public FieldProperty (Field field) { - super(field.getDeclaringClass(), field.getName(), field.getType()); + public FieldProperty(Field field) { + super(field.getDeclaringClass(), field.getName(), field.getType(), field.getGenericType()); this.field = field; } - public void set (Object object, Object value) throws Exception { + public void set(Object object, Object value) throws Exception { if (object instanceof DeferredConstruction) { - ((DeferredConstruction)object).storeProperty(this, value); + ((DeferredConstruction) object).storeProperty(this, value); return; } field.set(object, value); } - public Object get (Object object) throws Exception { + public Object get(Object object) throws Exception { return field.get(object); } } @@ -219,21 +252,25 @@ static public abstract class Property implements Comparable { private final Class declaringClass; private final String name; private final Class type; + private final Type genericType; - Property (Class declaringClass, String name, Class type) { + Property(Class declaringClass, String name, Class type, Type genericType) { this.declaringClass = declaringClass; this.name = name; this.type = type; + this.genericType = genericType; } - Property (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { + Property(Class declaringClass, PropertyDescriptor property) throws IntrospectionException { this.declaringClass = declaringClass; this.name = property.getName(); try { // The PropertyDescriptor returns the wrong type if the getter is an implementation of a interface method with a // generic return value. - type = property.getReadMethod().getDeclaringClass() - .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]).getReturnType(); + Method readMethod = property.getReadMethod().getDeclaringClass() + .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]); + type = readMethod.getReturnType(); + genericType = readMethod.getGenericReturnType(); } catch (Exception ex) { IntrospectionException introspectionEx = new IntrospectionException("Error getting "); introspectionEx.initCause(ex); @@ -241,62 +278,92 @@ static public abstract class Property implements Comparable { } } - public int hashCode () { + public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((genericType == null) ? 0 : genericType.hashCode()); return result; } - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - Property other = (Property)obj; + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Property other = (Property) obj; if (declaringClass == null) { - if (other.declaringClass != null) return false; - } else if (!declaringClass.equals(other.declaringClass)) return false; + if (other.declaringClass != null) { + return false; + } + } else if (!declaringClass.equals(other.declaringClass)) { + return false; + } if (name == null) { - if (other.name != null) return false; - } else if (!name.equals(other.name)) return false; - if (type == null) { - if (other.type != null) return false; - } else if (!type.equals(other.type)) return false; + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (genericType == null) { + if (other.genericType != null) { + return false; + } + } else if (!genericType.equals(other.genericType)) { + return false; + } return true; } - public Class getDeclaringClass () { + public Class getDeclaringClass() { return declaringClass; } - public Class getType () { + public Type getGenericType() { + return genericType; + } + + public Class getType() { return type; } - public String getName () { + public String getName() { return name; } - public String toString () { + public String toString() { return name; } - public int compareTo (Property o) { + public int compareTo(Property o) { int comparison = name.compareTo(o.name); if (comparison != 0) { // Sort id and name above all other fields. - if (name.equals("id")) return -1; - if (o.name.equals("id")) return 1; - if (name.equals("name")) return -1; - if (o.name.equals("name")) return 1; + if (name.equals("id")) { + return -1; + } + if (o.name.equals("id")) { + return 1; + } + if (name.equals("name")) { + return -1; + } + if (o.name.equals("name")) { + return 1; + } } return comparison; } - abstract public void set (Object object, Object value) throws Exception; + abstract public void set(Object object, Object value) throws Exception; - abstract public Object get (Object object) throws Exception; + abstract public Object get(Object object) throws Exception; } } diff --git a/src/com/esotericsoftware/yamlbeans/WriteClassName.java b/src/com/esotericsoftware/yamlbeans/WriteClassName.java new file mode 100644 index 0000000..dca3355 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/WriteClassName.java @@ -0,0 +1,5 @@ +package com.esotericsoftware.yamlbeans; + +public enum WriteClassName { + ALWAYS, NEVER, AUTO +} diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 3a12ef0..935a546 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -91,9 +91,9 @@ public void setPropertyElementType (Class type, String propertyName, Class eleme throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, cause); } - if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) + if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom((Class) property.getGenericType())) throw new IllegalArgumentException("The '" + propertyName + "' property on the " + type.getName() - + " class must be a Collection or Map: " + property.getType()); + + " class must be a Collection or Map: " + property.getGenericType()); propertyToElementType.put(property, elementType); } @@ -139,7 +139,9 @@ static public class WriteConfig { boolean writeRootTags = true; boolean writeRootElementTags = true; boolean autoAnchor = true; - boolean alwaysWriteClassName = false; + WriteClassName writeClassName = WriteClassName.AUTO; + + //boolean alwaysWriteClassName = false; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { @@ -216,8 +218,8 @@ public void setEscapeUnicode (boolean escapeUnicode) { } /** If true, class name tags will always be output. */ - public void setAlwaysWriteClassname (boolean write) { - alwaysWriteClassName = write; + public void setWriteClassname (WriteClassName write) { + writeClassName = write; } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index a302b9a..d3fa10a 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -16,8 +16,6 @@ package com.esotericsoftware.yamlbeans; -import static com.esotericsoftware.yamlbeans.parser.EventType.*; - import com.esotericsoftware.yamlbeans.Beans.Property; import com.esotericsoftware.yamlbeans.parser.AliasEvent; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; @@ -34,12 +32,20 @@ import java.io.StringReader; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import static com.esotericsoftware.yamlbeans.parser.EventType.DOCUMENT_START; +import static com.esotericsoftware.yamlbeans.parser.EventType.MAPPING_END; +import static com.esotericsoftware.yamlbeans.parser.EventType.SCALAR; +import static com.esotericsoftware.yamlbeans.parser.EventType.SEQUENCE_END; +import static com.esotericsoftware.yamlbeans.parser.EventType.STREAM_END; + /** Deserializes Java objects from YAML. * @author Nathan Sweet */ public class YamlReader { @@ -47,60 +53,66 @@ public class YamlReader { Parser parser; private final Map anchors = new HashMap(); - public YamlReader (Reader reader) { + public YamlReader(Reader reader) { this(reader, new YamlConfig()); } - public YamlReader (Reader reader, YamlConfig config) { + public YamlReader(Reader reader, YamlConfig config) { this.config = config; parser = new Parser(reader, config.readConfig.defaultVersion); } - public YamlReader (String yaml) { + public YamlReader(String yaml) { this(new StringReader(yaml)); } - public YamlReader (String yaml, YamlConfig config) { + public YamlReader(String yaml, YamlConfig config) { this(new StringReader(yaml), config); } - public YamlConfig getConfig () { + public YamlConfig getConfig() { return config; } /** Return the object with the given alias, or null. This is only valid after objects have been read and before {@link #close()} */ - public Object get (String alias) { + public Object get(String alias) { return anchors.get(alias); } - public void close () throws IOException { + public void close() throws IOException { parser.close(); anchors.clear(); } /** Reads the next YAML document and deserializes it into an object. The type of object is defined by the YAML tag. If there is * no YAML tag, the object will be an {@link ArrayList}, {@link HashMap}, or String. */ - public Object read () throws YamlException { + public Object read() throws YamlException { return read(null); } /** Reads an object of the specified type from YAML. * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ - public T read (Class type) throws YamlException { + public T read(Class type) throws YamlException { return read(type, null); } /** Reads an array, Map, List, or Collection object of the specified type from YAML, using the specified element type. * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ - public T read (Class type, Class elementType) throws YamlException { + public T read(Class type, Class elementType) throws YamlException { try { while (true) { Event event = parser.getNextEvent(); - if (event == null) return null; - if (event.type == STREAM_END) return null; - if (event.type == DOCUMENT_START) break; + if (event == null) { + return null; + } + if (event.type == STREAM_END) { + return null; + } + if (event.type == DOCUMENT_START) { + break; + } } - return (T)readValue(type, elementType, null); + return (T) readValue(type, elementType, null); } catch (ParserException ex) { throw new YamlException("Error parsing YAML.", ex); } catch (TokenizerException ex) { @@ -109,38 +121,41 @@ public T read (Class type, Class elementType) throws YamlException { } /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. */ - protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, - TokenizerException { + protected Object readValue(Class type, Class elementType, Class defaultType) throws YamlException, ParserException, + TokenizerException { String tag = null, anchor = null; Event event = parser.peekNextEvent(); switch (event.type) { - case ALIAS: - parser.getNextEvent(); - anchor = ((AliasEvent)event).anchor; - Object value = anchors.get(anchor); - if (value == null) throw new YamlReaderException("Unknown anchor: " + anchor); - return value; - case MAPPING_START: - case SEQUENCE_START: - tag = ((CollectionStartEvent)event).tag; - anchor = ((CollectionStartEvent)event).anchor; - break; - case SCALAR: - tag = ((ScalarEvent)event).tag; - anchor = ((ScalarEvent)event).anchor; - break; - default: + case ALIAS: + parser.getNextEvent(); + anchor = ((AliasEvent) event).anchor; + Object value = anchors.get(anchor); + if (value == null) { + throw new YamlReaderException("Unknown anchor: " + anchor); + } + return value; + case MAPPING_START: + case SEQUENCE_START: + tag = ((CollectionStartEvent) event).tag; + anchor = ((CollectionStartEvent) event).anchor; + break; + case SCALAR: + tag = ((ScalarEvent) event).tag; + anchor = ((ScalarEvent) event).anchor; + break; + default: } if (tag != null) { type = config.tagToClass.get(tag); if (type == null) { try { - if (config.readConfig.classLoader != null) + if (config.readConfig.classLoader != null) { type = Class.forName(tag, true, config.readConfig.classLoader); - else + } else { type = Class.forName(tag); + } } catch (ClassNotFoundException ex) { throw new YamlReaderException("Unable to find class specified by tag: " + tag); } @@ -152,38 +167,43 @@ protected Object readValue (Class type, Class elementType, Class defaultType) th return readValueInternal(type, elementType, anchor); } - private Object readValueInternal (Class type, Class elementType, String anchor) throws YamlException, ParserException, - TokenizerException { + private Object readValueInternal(Class type, Class elementType, String anchor) throws YamlException, ParserException, + TokenizerException { if (type == null || type == Object.class) { Event event = parser.peekNextEvent(); switch (event.type) { - case MAPPING_START: - type = HashMap.class; - break; - case SCALAR: - type = String.class; - break; - case SEQUENCE_START: - type = ArrayList.class; - break; - default: - throw new YamlReaderException("Expected scalar, sequence, or mapping but found: " + event.type); + case MAPPING_START: + type = HashMap.class; + break; + case SCALAR: + type = String.class; + break; + case SEQUENCE_START: + type = ArrayList.class; + break; + default: + throw new YamlReaderException("Expected scalar, sequence, or mapping but found: " + event.type); } } if (type == String.class) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for String type but found: " + event.type); - String value = ((ScalarEvent)event).value; - if (anchor != null) anchors.put(anchor, value); + if (event.type != SCALAR) { + throw new YamlReaderException("Expected scalar for String type but found: " + event.type); + } + String value = ((ScalarEvent) event).value; + if (anchor != null) { + anchors.put(anchor, value); + } return value; } if (Beans.isScalar(type)) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) + if (event.type != SCALAR) { throw new YamlReaderException("Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); - String value = ((ScalarEvent)event).value; + } + String value = ((ScalarEvent) event).value; try { Object convertedValue; if (type == String.class) { @@ -220,9 +240,12 @@ private Object readValueInternal (Class type, Class elementType, String anchor) convertedValue = value.length() == 0 ? 0 : Byte.decode(value); } else if (type == Byte.class) { convertedValue = value.length() == 0 ? null : Byte.decode(value); - } else + } else { throw new YamlException("Unknown field type."); - if (anchor != null) anchors.put(anchor, convertedValue); + } + if (anchor != null) { + anchors.put(anchor, convertedValue); + } return convertedValue; } catch (Exception ex) { throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); @@ -231,9 +254,13 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (Enum.class.isAssignableFrom(type)) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); - String enumValueName = ((ScalarEvent)event).value; - if (enumValueName.length() == 0) return null; + if (event.type != SCALAR) { + throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); + } + String enumValueName = ((ScalarEvent) event).value; + if (enumValueName.length() == 0) { + return null; + } try { return Enum.valueOf(type, enumValueName); } catch (Exception ex) { @@ -245,135 +272,185 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (entry.getKey().isAssignableFrom(type)) { ScalarSerializer serializer = entry.getValue(); Event event = parser.getNextEvent(); - if (event.type != SCALAR) + if (event.type != SCALAR) { throw new YamlReaderException("Expected scalar for type '" + type + "' to be deserialized by scalar serializer '" - + serializer.getClass().getName() + "' but found: " + event.type); - Object value = serializer.read(((ScalarEvent)event).value); - if (anchor != null) anchors.put(anchor, value); + + serializer.getClass().getName() + "' but found: " + event.type); + } + Object value = serializer.read(((ScalarEvent) event).value); + if (anchor != null) { + anchors.put(anchor, value); + } return value; } } Event event = parser.peekNextEvent(); switch (event.type) { - case MAPPING_START: { - // Must be a map or an object. - event = parser.getNextEvent(); - Object object; - try { - object = createObject(type); - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); - } - if (anchor != null) anchors.put(anchor, object); - while (true) { - if (parser.peekNextEvent().type == MAPPING_END) { - parser.getNextEvent(); - break; + case MAPPING_START: { + // Must be a map or an object. + event = parser.getNextEvent(); + Object object; + try { + object = createObject(type); + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); } - Object key = readValue(null, null, null); - // Explicit key/value pairs (using "? key\n: value\n") will come back as a map. - boolean isExplicitKey = key instanceof Map; - Object value = null; - if (isExplicitKey) { - Entry nameValuePair = (Entry)((Map)key).entrySet().iterator().next(); - key = nameValuePair.getKey(); - value = nameValuePair.getValue(); + if (anchor != null) { + anchors.put(anchor, object); } - if (object instanceof Map) { - // Add to map. - if (!isExplicitKey) value = readValue(elementType, null, null); - ((Map)object).put(key, value); - } else { - // Set field on object. + while (true) { + if (parser.peekNextEvent().type == MAPPING_END) { + parser.getNextEvent(); + break; + } + Object key = readValue(null, null, null); + // Explicit key/value pairs (using "? key\n: value\n") will come back as a map. + boolean isExplicitKey = key instanceof Map; + Object value = null; + if (isExplicitKey) { + Entry nameValuePair = (Entry) ((Map) key).entrySet().iterator().next(); + key = nameValuePair.getKey(); + value = nameValuePair.getValue(); + } + if (object instanceof Map) { + // Add to map. + if (!isExplicitKey) { + value = readValue(elementType, null, null); + } + ((Map) object).put(key, value); + } else { + // Set field on object. + try { + Property property = Beans.getProperty(type, (String) key, config.beanProperties, config.privateFields, config); + if (property == null) { + throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); + } + Class propertyElementType = config.propertyToElementType.get(property); + if (propertyElementType == null) { + propertyElementType = getElementTypeFromGenerics(property.getGenericType()); + } + Class propertyDefaultType = config.propertyToDefaultType.get(property); + if (!isExplicitKey) { + value = readValue(property.getType(), propertyElementType, propertyDefaultType); + } + property.set(object, value); + } catch (Exception ex) { + if (ex instanceof YamlReaderException) { + throw (YamlReaderException) ex; + } + throw new YamlReaderException("Error setting property '" + key + "' on class: " + type.getName(), ex); + } + } + } + if (object instanceof DeferredConstruction) { try { - Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); - if (property == null) - throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); - Class propertyElementType = config.propertyToElementType.get(property); - Class propertyDefaultType = config.propertyToDefaultType.get(property); - if (!isExplicitKey) value = readValue(property.getType(), propertyElementType, propertyDefaultType); - property.set(object, value); - } catch (Exception ex) { - if (ex instanceof YamlReaderException) throw (YamlReaderException)ex; - throw new YamlReaderException("Error setting property '" + key + "' on class: " + type.getName(), ex); + object = ((DeferredConstruction) object).construct(); + if (anchor != null) { + anchors.put(anchor, object); // Update anchor with real object. + } + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); } } + return object; } - if (object instanceof DeferredConstruction) { - try { - object = ((DeferredConstruction)object).construct(); - if (anchor != null) anchors.put(anchor, object); // Update anchor with real object. - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); + case SEQUENCE_START: { + // Must be a collection or an array. + event = parser.getNextEvent(); + Collection collection; + if (Collection.class.isAssignableFrom(type)) { + try { + collection = (Collection) Beans.createObject(type, config.privateConstructors); + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); + } + } else if (type.isArray()) { + collection = new ArrayList(); + elementType = type.getComponentType(); + } else { + throw new YamlReaderException("A sequence is not a valid value for the type: " + type.getName()); } - } - return object; - } - case SEQUENCE_START: { - // Must be a collection or an array. - event = parser.getNextEvent(); - Collection collection; - if (Collection.class.isAssignableFrom(type)) { - try { - collection = (Collection)Beans.createObject(type, config.privateConstructors); - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); + if (!type.isArray() && anchor != null) { + anchors.put(anchor, collection); } - } else if (type.isArray()) { - collection = new ArrayList(); - elementType = type.getComponentType(); - } else - throw new YamlReaderException("A sequence is not a valid value for the type: " + type.getName()); - if (!type.isArray() && anchor != null) anchors.put(anchor, collection); - while (true) { - event = parser.peekNextEvent(); - if (event.type == SEQUENCE_END) { - parser.getNextEvent(); - break; + while (true) { + event = parser.peekNextEvent(); + if (event.type == SEQUENCE_END) { + parser.getNextEvent(); + break; + } + collection.add(readValue(elementType, null, null)); + } + if (!type.isArray()) { + return collection; + } + Object array = Array.newInstance(elementType, collection.size()); + int i = 0; + for (Object object : collection) { + Array.set(array, i++, object); } - collection.add(readValue(elementType, null, null)); + if (anchor != null) { + anchors.put(anchor, array); + } + return array; } - if (!type.isArray()) return collection; - Object array = Array.newInstance(elementType, collection.size()); - int i = 0; - for (Object object : collection) - Array.set(array, i++, object); - if (anchor != null) anchors.put(anchor, array); - return array; + case SCALAR: + // Interpret an empty scalar as null. + System.out.println(((ScalarEvent) event).value); + if (((ScalarEvent) event).value.length() == 0) { + event = parser.getNextEvent(); + return null; + } + // Fall through. + default: + throw new YamlReaderException("Expected data for a " + type.getName() + " field but found: " + event.type); } - case SCALAR: - // Interpret an empty scalar as null. - System.out.println(((ScalarEvent)event).value); - if (((ScalarEvent)event).value.length() == 0) { - event = parser.getNextEvent(); - return null; + } + + private Class getElementTypeFromGenerics(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type rawType = parameterizedType.getRawType(); + + if (isCollection(rawType) || isMap(rawType)) { + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + if (actualTypeArguments.length > 0) { + return (Class) actualTypeArguments[actualTypeArguments.length - 1]; + } } - // Fall through. - default: - throw new YamlReaderException("Expected data for a " + type.getName() + " field but found: " + event.type); } + return null; + } + + private boolean isMap(Type type) { + return Map.class.isAssignableFrom((Class) type); + } + + private boolean isCollection(Type type) { + return Collection.class.isAssignableFrom((Class) type); } /** Returns a new object of the requested type. */ - protected Object createObject (Class type) throws InvocationTargetException { + protected Object createObject(Class type) throws InvocationTargetException { // Use deferred construction if a non-zero-arg constructor is available. DeferredConstruction deferredConstruction = Beans.getDeferredConstruction(type, config); - if (deferredConstruction != null) return deferredConstruction; + if (deferredConstruction != null) { + return deferredConstruction; + } return Beans.createObject(type, config.privateConstructors); } public class YamlReaderException extends YamlException { - public YamlReaderException (String message, Throwable cause) { + public YamlReaderException(String message, Throwable cause) { super("Line " + parser.getLineNumber() + ", column " + parser.getColumn() + ": " + message, cause); } - public YamlReaderException (String message) { + public YamlReaderException(String message) { this(message, null); } } - public static void main (String[] args) throws Exception { + public static void main(String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 6e62de8..9424f33 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -59,20 +59,20 @@ public class YamlWriter { private int nextAnchor = 1; private boolean isRoot; - public YamlWriter (Writer writer) { + public YamlWriter(Writer writer) { this(writer, new YamlConfig()); } - public YamlWriter (Writer writer, YamlConfig config) { + public YamlWriter(Writer writer, YamlConfig config) { this.config = config; emitter = new Emitter(writer, config.writeConfig.emitterConfig); } - public void setAlias (Object object, String alias) { + public void setAlias(Object object, String alias) { anchoredObjects.put(object, alias); } - public void write (Object object) throws YamlException { + public void write(Object object) throws YamlException { if (config.writeConfig.autoAnchor) { countObjectReferences(object); queuedObjects.add(object); @@ -81,11 +81,11 @@ public void write (Object object) throws YamlException { writeInternal(object); } - public YamlConfig getConfig () { + public YamlConfig getConfig() { return config; } - private void writeInternal (Object object) throws YamlException { + private void writeInternal(Object object) throws YamlException { try { if (!started) { emitter.emit(Event.STREAM_START); @@ -103,15 +103,16 @@ private void writeInternal (Object object) throws YamlException { } /** Returns the YAML emitter, which allows the YAML output to be configured. */ - public Emitter getEmitter () { + public Emitter getEmitter() { return emitter; } /** Writes any buffered objects, then resets the list of anchored objects. * @see WriteConfig#setAutoAnchor(boolean) */ - public void clearAnchors () throws YamlException { - for (Object object : queuedObjects) + public void clearAnchors() throws YamlException { + for (Object object : queuedObjects) { writeInternal(object); + } queuedObjects.clear(); referenceCount.clear(); nextAnchor = 1; @@ -119,7 +120,7 @@ public void clearAnchors () throws YamlException { /** Finishes writing any buffered output and releases all resources. * @throws YamlException If the buffered output could not be written or the writer could not be closed. */ - public void close () throws YamlException { + public void close() throws YamlException { clearAnchors(); defaultValuePrototypes.clear(); try { @@ -132,22 +133,24 @@ public void close () throws YamlException { } } - private void writeValue (Object object, Class fieldClass, Class elementType, Class defaultType) throws EmitterException, - IOException, YamlException { + private void writeValue(Object object, Class fieldClass, Class elementType, Class defaultType) throws EmitterException, + IOException, YamlException { boolean isRoot = this.isRoot; this.isRoot = false; if (object == null) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, null, (char) 0)); return; } Class valueClass = object.getClass(); boolean unknownType = fieldClass == null; - if (unknownType) fieldClass = valueClass; + if (unknownType) { + fieldClass = valueClass; + } if (object instanceof Enum) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, ((Enum) object).name(), (char) 0)); return; } @@ -172,35 +175,47 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla String tag = null; boolean showTag = false; - if (unknownType || valueClass != fieldClass || config.writeConfig.alwaysWriteClassName) { + if ((unknownType || valueClass != fieldClass || config.writeConfig.writeClassName == WriteClassName.ALWAYS) && config.writeConfig.writeClassName != WriteClassName.NEVER) { showTag = true; - if ((unknownType || fieldClass == List.class) && valueClass == ArrayList.class) showTag = false; - if ((unknownType || fieldClass == Map.class) && valueClass == HashMap.class) showTag = false; - if (fieldClass == Set.class && valueClass == HashSet.class) showTag = false; - if (valueClass == defaultType) showTag = false; + if ((unknownType || fieldClass == List.class) && valueClass == ArrayList.class) { + showTag = false; + } + if ((unknownType || fieldClass == Map.class) && valueClass == HashMap.class) { + showTag = false; + } + if (fieldClass == Set.class && valueClass == HashSet.class) { + showTag = false; + } + if (valueClass == defaultType) { + showTag = false; + } if (showTag) { tag = config.classNameToTag.get(valueClass.getName()); - if (tag == null) tag = valueClass.getName(); + if (tag == null) { + tag = valueClass.getName(); + } } } for (Entry entry : config.scalarSerializers.entrySet()) { if (entry.getKey().isAssignableFrom(valueClass)) { ScalarSerializer serializer = entry.getValue(); - emitter.emit(new ScalarEvent(null, tag, new boolean[] {tag == null, tag == null}, serializer.write(object), (char)0)); + emitter.emit(new ScalarEvent(null, tag, new boolean[]{tag == null, tag == null}, serializer.write(object), (char) 0)); return; } } if (Beans.isScalar(valueClass)) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, String.valueOf(object), (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, String.valueOf(object), (char) 0)); return; } if (object instanceof Collection) { emitter.emit(new SequenceStartEvent(anchor, tag, !showTag, false)); - for (Object item : (Collection)object) { - if (isRoot && !config.writeConfig.writeRootElementTags) elementType = item.getClass(); + for (Object item : (Collection) object) { + if (isRoot && !config.writeConfig.writeRootElementTags) { + elementType = item.getClass(); + } writeValue(item, elementType, null, null); } emitter.emit(Event.SEQUENCE_END); @@ -209,10 +224,12 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (object instanceof Map) { emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); - for (Object item : ((Map)object).entrySet()) { - Entry entry = (Entry)item; + for (Object item : ((Map) object).entrySet()) { + Entry entry = (Entry) item; writeValue(entry.getKey(), null, null, null); - if (isRoot && !config.writeConfig.writeRootElementTags) elementType = entry.getValue().getClass(); + if (isRoot && !config.writeConfig.writeRootElementTags) { + elementType = entry.getValue().getClass(); + } writeValue(entry.getValue(), elementType, null, null); } emitter.emit(Event.MAPPING_END); @@ -222,8 +239,9 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (fieldClass.isArray()) { elementType = fieldClass.getComponentType(); emitter.emit(new SequenceStartEvent(anchor, null, true, false)); - for (int i = 0, n = Array.getLength(object); i < n; i++) + for (int i = 0, n = Array.getLength(object); i < n; i++) { writeValue(Array.get(object, i), elementType, null, null); + } emitter.emit(Event.SEQUENCE_END); return; } @@ -256,10 +274,14 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (prototype != null) { // Don't output properties that have the default value for the prototype. Object prototypeValue = property.get(prototype); - if (propertyValue == null && prototypeValue == null) continue; - if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) continue; + if (propertyValue == null && prototypeValue == null) { + continue; + } + if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) { + continue; + } } - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, property.getName(), (char) 0)); Class propertyElementType = config.propertyToElementType.get(property); Class propertyDefaultType = config.propertyToDefaultType.get(property); writeValue(propertyValue, property.getType(), propertyElementType, propertyDefaultType); @@ -270,8 +292,10 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla emitter.emit(Event.MAPPING_END); } - private void countObjectReferences (Object object) throws YamlException { - if (object == null || Beans.isScalar(object.getClass())) return; + private void countObjectReferences(Object object) throws YamlException { + if (object == null || Beans.isScalar(object.getClass())) { + return; + } // Count every reference to the object, but follow its own references the first time it is encountered. Integer count = referenceCount.get(object); @@ -282,20 +306,23 @@ private void countObjectReferences (Object object) throws YamlException { referenceCount.put(object, 1); if (object instanceof Collection) { - for (Object item : (Collection)object) + for (Object item : (Collection) object) { countObjectReferences(item); + } return; } if (object instanceof Map) { - for (Object value : ((Map)object).values()) + for (Object value : ((Map) object).values()) { countObjectReferences(value); + } return; } if (object.getClass().isArray()) { - for (int i = 0, n = Array.getLength(object); i < n; i++) + for (int i = 0, n = Array.getLength(object); i < n; i++) { countObjectReferences(Array.get(object, i)); + } return; } @@ -308,7 +335,9 @@ private void countObjectReferences (Object object) throws YamlException { throw new YamlException("Error inspecting class: " + object.getClass().getName(), ex); } for (Property property : properties) { - if (Beans.isScalar(property.getType())) continue; + if (Beans.isScalar(property.getType())) { + continue; + } Object propertyValue; try { propertyValue = property.get(object); From 7a59bca489223e87998da8e2946de40b16fba8f2 Mon Sep 17 00:00:00 2001 From: Olga Stepanova Date: Tue, 13 May 2014 13:36:57 +0600 Subject: [PATCH 002/114] formatting fixed --- src/com/esotericsoftware/yamlbeans/Beans.java | 187 ++++----- .../yamlbeans/YamlConfig.java | 6 +- .../yamlbeans/YamlReader.java | 372 ++++++++---------- .../yamlbeans/YamlWriter.java | 103 ++--- 4 files changed, 267 insertions(+), 401 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 8e99413..7e20110 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -40,27 +40,23 @@ /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ class Beans { - private Beans() { + private Beans () { } - static public boolean isScalar(Class c) { + static public boolean isScalar (Class c) { return c.isPrimitive() || c == String.class || c == Integer.class || c == Boolean.class || c == Float.class - || c == Long.class || c == Double.class || c == Short.class || c == Byte.class || c == Character.class; + || c == Long.class || c == Double.class || c == Short.class || c == Byte.class || c == Character.class; } - static public DeferredConstruction getDeferredConstruction(Class type, YamlConfig config) { + static public DeferredConstruction getDeferredConstruction (Class type, YamlConfig config) { ConstructorParameters parameters = config.readConfig.constructorParameters.get(type); - if (parameters != null) { - return new DeferredConstruction(parameters.constructor, parameters.parameterNames); - } + if (parameters != null) return new DeferredConstruction(parameters.constructor, parameters.parameterNames); try { Class constructorProperties = Class.forName("java.beans.ConstructorProperties"); for (Constructor typeConstructor : type.getConstructors()) { Annotation annotation = typeConstructor.getAnnotation(constructorProperties); - if (annotation == null) { - continue; - } - String[] parameterNames = (String[]) constructorProperties.getMethod("value").invoke(annotation, (Object[]) null); + if (annotation == null) continue; + String[] parameterNames = (String[])constructorProperties.getMethod("value").invoke(annotation, (Object[])null); return new DeferredConstruction(typeConstructor, parameterNames); } } catch (Exception ignored) { @@ -68,21 +64,17 @@ static public DeferredConstruction getDeferredConstruction(Class type, YamlConfi return null; } - static private boolean canInitializeProperty(Class type, PropertyDescriptor property, YamlConfig config) { - if (property.getWriteMethod() != null) { - return true; - } + static private boolean canInitializeProperty (Class type, PropertyDescriptor property, YamlConfig config) { + if (property.getWriteMethod() != null) return true; // Check if the property can be initialized through the constructor. DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) { - return true; - } + if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) return true; return false; } - static public Object createObject(Class type, boolean privateConstructors) throws InvocationTargetException { + static public Object createObject (Class type, boolean privateConstructors) throws InvocationTargetException { // Use no-arg constructor. Constructor constructor = null; for (Constructor typeConstructor : type.getConstructors()) { @@ -117,9 +109,8 @@ static public Object createObject(Class type, boolean privateConstructors) throw } } - if (constructor == null) { + if (constructor == null) throw new InvocationTargetException(null, "Unable to find a no-arg constructor for class: " + type.getName()); - } try { return constructor.newInstance(); @@ -128,28 +119,20 @@ static public Object createObject(Class type, boolean privateConstructors) throw } } - static public Set getProperties(Class type, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { - if (type == null) { - throw new IllegalArgumentException("type cannot be null."); - } + static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) + throws IntrospectionException { + if (type == null) throw new IllegalArgumentException("type cannot be null."); Set properties = new TreeSet(); if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) { + for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) + if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) properties.add(new MethodProperty(type, property)); - } - } } for (Field field : getAllFields(type)) { int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { - continue; - } + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { - if (!privateFields) { - continue; - } + if (!privateFields) continue; field.setAccessible(true); } properties.add(new FieldProperty(field)); @@ -157,46 +140,32 @@ static public Set getProperties(Class type, boolean beanProperties, bo return properties; } - static public Property getProperty(Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { - if (type == null) { - throw new IllegalArgumentException("type cannot be null."); - } - if (name == null || name.length() == 0) { - throw new IllegalArgumentException("name cannot be null or empty."); - } + static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) + throws IntrospectionException { + if (type == null) throw new IllegalArgumentException("type cannot be null."); + if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); if (beanProperties) { for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { if (property.getName().equals(name)) { - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) { - Type genericType = property.getReadMethod().getGenericReturnType(); - + if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) return new MethodProperty(type, property); - } break; } } } for (Field field : getAllFields(type)) { int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) { - continue; - } + if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { - if (!privateFields) { - continue; - } + if (!privateFields) continue; field.setAccessible(true); } - if (field.getName().equals(name)) { - return new FieldProperty(field); - } - Type genericType = field.getGenericType(); + if (field.getName().equals(name)) return new FieldProperty(field); } return null; } - static private ArrayList getAllFields(Class type) { + static private ArrayList getAllFields (Class type) { ArrayList allFields = new ArrayList(); Class nextClass = type; while (nextClass != Object.class) { @@ -209,20 +178,20 @@ static private ArrayList getAllFields(Class type) { static public class MethodProperty extends Property { private final PropertyDescriptor property; - public MethodProperty(Class declaringClass, PropertyDescriptor property) throws IntrospectionException { + public MethodProperty (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { super(declaringClass, property); this.property = property; } - public void set(Object object, Object value) throws Exception { + public void set (Object object, Object value) throws Exception { if (object instanceof DeferredConstruction) { - ((DeferredConstruction) object).storeProperty(this, value); + ((DeferredConstruction)object).storeProperty(this, value); return; } property.getWriteMethod().invoke(object, value); } - public Object get(Object object) throws Exception { + public Object get (Object object) throws Exception { return property.getReadMethod().invoke(object); } } @@ -230,20 +199,20 @@ public Object get(Object object) throws Exception { static public class FieldProperty extends Property { private final Field field; - public FieldProperty(Field field) { + public FieldProperty (Field field) { super(field.getDeclaringClass(), field.getName(), field.getType(), field.getGenericType()); this.field = field; } - public void set(Object object, Object value) throws Exception { + public void set (Object object, Object value) throws Exception { if (object instanceof DeferredConstruction) { - ((DeferredConstruction) object).storeProperty(this, value); + ((DeferredConstruction)object).storeProperty(this, value); return; } field.set(object, value); } - public Object get(Object object) throws Exception { + public Object get (Object object) throws Exception { return field.get(object); } } @@ -254,21 +223,21 @@ static public abstract class Property implements Comparable { private final Class type; private final Type genericType; - Property(Class declaringClass, String name, Class type, Type genericType) { + Property (Class declaringClass, String name, Class type, Type genericType) { this.declaringClass = declaringClass; this.name = name; this.type = type; this.genericType = genericType; } - Property(Class declaringClass, PropertyDescriptor property) throws IntrospectionException { + Property (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { this.declaringClass = declaringClass; this.name = property.getName(); try { // The PropertyDescriptor returns the wrong type if the getter is an implementation of a interface method with a // generic return value. Method readMethod = property.getReadMethod().getDeclaringClass() - .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]); + .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]); type = readMethod.getReturnType(); genericType = readMethod.getGenericReturnType(); } catch (Exception ex) { @@ -278,92 +247,70 @@ static public abstract class Property implements Comparable { } } - public int hashCode() { + public int hashCode () { final int prime = 31; int result = 1; result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); result = prime * result + ((genericType == null) ? 0 : genericType.hashCode()); return result; } - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Property other = (Property) obj; + public boolean equals (Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Property other = (Property)obj; if (declaringClass == null) { - if (other.declaringClass != null) { - return false; - } - } else if (!declaringClass.equals(other.declaringClass)) { - return false; - } + if (other.declaringClass != null) return false; + } else if (!declaringClass.equals(other.declaringClass)) return false; if (name == null) { - if (other.name != null) { - return false; - } - } else if (!name.equals(other.name)) { - return false; - } + if (other.name != null) return false; + } else if (!name.equals(other.name)) return false; + if (type == null) { + if (other.type != null) return false; + } else if (!type.equals(other.type)) return false; if (genericType == null) { - if (other.genericType != null) { - return false; - } - } else if (!genericType.equals(other.genericType)) { - return false; - } + if (other.genericType != null) return false; + } else if (!genericType.equals(other.genericType)) return false; return true; } - public Class getDeclaringClass() { + public Class getDeclaringClass () { return declaringClass; } - public Type getGenericType() { + public Type getGenericType () { return genericType; } - public Class getType() { + public Class getType () { return type; } - public String getName() { + public String getName () { return name; } - public String toString() { + public String toString () { return name; } - public int compareTo(Property o) { + public int compareTo (Property o) { int comparison = name.compareTo(o.name); if (comparison != 0) { // Sort id and name above all other fields. - if (name.equals("id")) { - return -1; - } - if (o.name.equals("id")) { - return 1; - } - if (name.equals("name")) { - return -1; - } - if (o.name.equals("name")) { - return 1; - } + if (name.equals("id")) return -1; + if (o.name.equals("id")) return 1; + if (name.equals("name")) return -1; + if (o.name.equals("name")) return 1; } return comparison; } - abstract public void set(Object object, Object value) throws Exception; + abstract public void set (Object object, Object value) throws Exception; - abstract public Object get(Object object) throws Exception; + abstract public Object get (Object object) throws Exception; } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 935a546..feda306 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -91,9 +91,9 @@ public void setPropertyElementType (Class type, String propertyName, Class eleme throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, cause); } - if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom((Class) property.getGenericType())) + if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) throw new IllegalArgumentException("The '" + propertyName + "' property on the " + type.getName() - + " class must be a Collection or Map: " + property.getGenericType()); + + " class must be a Collection or Map: " + property.getType()); propertyToElementType.put(property, elementType); } @@ -140,8 +140,6 @@ static public class WriteConfig { boolean writeRootElementTags = true; boolean autoAnchor = true; WriteClassName writeClassName = WriteClassName.AUTO; - - //boolean alwaysWriteClassName = false; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index d3fa10a..fd529dc 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -16,6 +16,8 @@ package com.esotericsoftware.yamlbeans; +import static com.esotericsoftware.yamlbeans.parser.EventType.*; + import com.esotericsoftware.yamlbeans.Beans.Property; import com.esotericsoftware.yamlbeans.parser.AliasEvent; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; @@ -40,12 +42,6 @@ import java.util.Map; import java.util.Map.Entry; -import static com.esotericsoftware.yamlbeans.parser.EventType.DOCUMENT_START; -import static com.esotericsoftware.yamlbeans.parser.EventType.MAPPING_END; -import static com.esotericsoftware.yamlbeans.parser.EventType.SCALAR; -import static com.esotericsoftware.yamlbeans.parser.EventType.SEQUENCE_END; -import static com.esotericsoftware.yamlbeans.parser.EventType.STREAM_END; - /** Deserializes Java objects from YAML. * @author Nathan Sweet */ public class YamlReader { @@ -53,66 +49,60 @@ public class YamlReader { Parser parser; private final Map anchors = new HashMap(); - public YamlReader(Reader reader) { + public YamlReader (Reader reader) { this(reader, new YamlConfig()); } - public YamlReader(Reader reader, YamlConfig config) { + public YamlReader (Reader reader, YamlConfig config) { this.config = config; parser = new Parser(reader, config.readConfig.defaultVersion); } - public YamlReader(String yaml) { + public YamlReader (String yaml) { this(new StringReader(yaml)); } - public YamlReader(String yaml, YamlConfig config) { + public YamlReader (String yaml, YamlConfig config) { this(new StringReader(yaml), config); } - public YamlConfig getConfig() { + public YamlConfig getConfig () { return config; } /** Return the object with the given alias, or null. This is only valid after objects have been read and before {@link #close()} */ - public Object get(String alias) { + public Object get (String alias) { return anchors.get(alias); } - public void close() throws IOException { + public void close () throws IOException { parser.close(); anchors.clear(); } /** Reads the next YAML document and deserializes it into an object. The type of object is defined by the YAML tag. If there is * no YAML tag, the object will be an {@link ArrayList}, {@link HashMap}, or String. */ - public Object read() throws YamlException { + public Object read () throws YamlException { return read(null); } /** Reads an object of the specified type from YAML. * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ - public T read(Class type) throws YamlException { + public T read (Class type) throws YamlException { return read(type, null); } /** Reads an array, Map, List, or Collection object of the specified type from YAML, using the specified element type. * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ - public T read(Class type, Class elementType) throws YamlException { + public T read (Class type, Class elementType) throws YamlException { try { while (true) { Event event = parser.getNextEvent(); - if (event == null) { - return null; - } - if (event.type == STREAM_END) { - return null; - } - if (event.type == DOCUMENT_START) { - break; - } + if (event == null) return null; + if (event.type == STREAM_END) return null; + if (event.type == DOCUMENT_START) break; } - return (T) readValue(type, elementType, null); + return (T)readValue(type, elementType, null); } catch (ParserException ex) { throw new YamlException("Error parsing YAML.", ex); } catch (TokenizerException ex) { @@ -121,41 +111,38 @@ public T read(Class type, Class elementType) throws YamlException { } /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. */ - protected Object readValue(Class type, Class elementType, Class defaultType) throws YamlException, ParserException, - TokenizerException { + protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, + TokenizerException { String tag = null, anchor = null; Event event = parser.peekNextEvent(); switch (event.type) { - case ALIAS: - parser.getNextEvent(); - anchor = ((AliasEvent) event).anchor; - Object value = anchors.get(anchor); - if (value == null) { - throw new YamlReaderException("Unknown anchor: " + anchor); - } - return value; - case MAPPING_START: - case SEQUENCE_START: - tag = ((CollectionStartEvent) event).tag; - anchor = ((CollectionStartEvent) event).anchor; - break; - case SCALAR: - tag = ((ScalarEvent) event).tag; - anchor = ((ScalarEvent) event).anchor; - break; - default: + case ALIAS: + parser.getNextEvent(); + anchor = ((AliasEvent)event).anchor; + Object value = anchors.get(anchor); + if (value == null) throw new YamlReaderException("Unknown anchor: " + anchor); + return value; + case MAPPING_START: + case SEQUENCE_START: + tag = ((CollectionStartEvent)event).tag; + anchor = ((CollectionStartEvent)event).anchor; + break; + case SCALAR: + tag = ((ScalarEvent)event).tag; + anchor = ((ScalarEvent)event).anchor; + break; + default: } if (tag != null) { type = config.tagToClass.get(tag); if (type == null) { try { - if (config.readConfig.classLoader != null) { + if (config.readConfig.classLoader != null) type = Class.forName(tag, true, config.readConfig.classLoader); - } else { + else type = Class.forName(tag); - } } catch (ClassNotFoundException ex) { throw new YamlReaderException("Unable to find class specified by tag: " + tag); } @@ -167,43 +154,38 @@ protected Object readValue(Class type, Class elementType, Class defaultType) thr return readValueInternal(type, elementType, anchor); } - private Object readValueInternal(Class type, Class elementType, String anchor) throws YamlException, ParserException, - TokenizerException { + private Object readValueInternal (Class type, Class elementType, String anchor) throws YamlException, ParserException, + TokenizerException { if (type == null || type == Object.class) { Event event = parser.peekNextEvent(); switch (event.type) { - case MAPPING_START: - type = HashMap.class; - break; - case SCALAR: - type = String.class; - break; - case SEQUENCE_START: - type = ArrayList.class; - break; - default: - throw new YamlReaderException("Expected scalar, sequence, or mapping but found: " + event.type); + case MAPPING_START: + type = HashMap.class; + break; + case SCALAR: + type = String.class; + break; + case SEQUENCE_START: + type = ArrayList.class; + break; + default: + throw new YamlReaderException("Expected scalar, sequence, or mapping but found: " + event.type); } } if (type == String.class) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) { - throw new YamlReaderException("Expected scalar for String type but found: " + event.type); - } - String value = ((ScalarEvent) event).value; - if (anchor != null) { - anchors.put(anchor, value); - } + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for String type but found: " + event.type); + String value = ((ScalarEvent)event).value; + if (anchor != null) anchors.put(anchor, value); return value; } if (Beans.isScalar(type)) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) { + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); - } - String value = ((ScalarEvent) event).value; + String value = ((ScalarEvent)event).value; try { Object convertedValue; if (type == String.class) { @@ -240,12 +222,9 @@ private Object readValueInternal(Class type, Class elementType, String anchor) t convertedValue = value.length() == 0 ? 0 : Byte.decode(value); } else if (type == Byte.class) { convertedValue = value.length() == 0 ? null : Byte.decode(value); - } else { + } else throw new YamlException("Unknown field type."); - } - if (anchor != null) { - anchors.put(anchor, convertedValue); - } + if (anchor != null) anchors.put(anchor, convertedValue); return convertedValue; } catch (Exception ex) { throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); @@ -254,13 +233,9 @@ private Object readValueInternal(Class type, Class elementType, String anchor) t if (Enum.class.isAssignableFrom(type)) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) { - throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); - } - String enumValueName = ((ScalarEvent) event).value; - if (enumValueName.length() == 0) { - return null; - } + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); + String enumValueName = ((ScalarEvent)event).value; + if (enumValueName.length() == 0) return null; try { return Enum.valueOf(type, enumValueName); } catch (Exception ex) { @@ -272,185 +247,160 @@ private Object readValueInternal(Class type, Class elementType, String anchor) t if (entry.getKey().isAssignableFrom(type)) { ScalarSerializer serializer = entry.getValue(); Event event = parser.getNextEvent(); - if (event.type != SCALAR) { + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for type '" + type + "' to be deserialized by scalar serializer '" - + serializer.getClass().getName() + "' but found: " + event.type); - } - Object value = serializer.read(((ScalarEvent) event).value); - if (anchor != null) { - anchors.put(anchor, value); - } + + serializer.getClass().getName() + "' but found: " + event.type); + Object value = serializer.read(((ScalarEvent)event).value); + if (anchor != null) anchors.put(anchor, value); return value; } } Event event = parser.peekNextEvent(); switch (event.type) { - case MAPPING_START: { - // Must be a map or an object. - event = parser.getNextEvent(); - Object object; - try { - object = createObject(type); - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); + case MAPPING_START: { + // Must be a map or an object. + event = parser.getNextEvent(); + Object object; + try { + object = createObject(type); + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); + } + if (anchor != null) anchors.put(anchor, object); + while (true) { + if (parser.peekNextEvent().type == MAPPING_END) { + parser.getNextEvent(); + break; } - if (anchor != null) { - anchors.put(anchor, object); + Object key = readValue(null, null, null); + // Explicit key/value pairs (using "? key\n: value\n") will come back as a map. + boolean isExplicitKey = key instanceof Map; + Object value = null; + if (isExplicitKey) { + Entry nameValuePair = (Entry)((Map)key).entrySet().iterator().next(); + key = nameValuePair.getKey(); + value = nameValuePair.getValue(); } - while (true) { - if (parser.peekNextEvent().type == MAPPING_END) { - parser.getNextEvent(); - break; - } - Object key = readValue(null, null, null); - // Explicit key/value pairs (using "? key\n: value\n") will come back as a map. - boolean isExplicitKey = key instanceof Map; - Object value = null; - if (isExplicitKey) { - Entry nameValuePair = (Entry) ((Map) key).entrySet().iterator().next(); - key = nameValuePair.getKey(); - value = nameValuePair.getValue(); - } - if (object instanceof Map) { - // Add to map. - if (!isExplicitKey) { - value = readValue(elementType, null, null); - } - ((Map) object).put(key, value); - } else { - // Set field on object. - try { - Property property = Beans.getProperty(type, (String) key, config.beanProperties, config.privateFields, config); - if (property == null) { - throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); - } - Class propertyElementType = config.propertyToElementType.get(property); - if (propertyElementType == null) { - propertyElementType = getElementTypeFromGenerics(property.getGenericType()); - } - Class propertyDefaultType = config.propertyToDefaultType.get(property); - if (!isExplicitKey) { - value = readValue(property.getType(), propertyElementType, propertyDefaultType); - } - property.set(object, value); - } catch (Exception ex) { - if (ex instanceof YamlReaderException) { - throw (YamlReaderException) ex; - } - throw new YamlReaderException("Error setting property '" + key + "' on class: " + type.getName(), ex); - } - } - } - if (object instanceof DeferredConstruction) { + if (object instanceof Map) { + // Add to map. + if (!isExplicitKey) value = readValue(elementType, null, null); + ((Map)object).put(key, value); + } else { + // Set field on object. try { - object = ((DeferredConstruction) object).construct(); - if (anchor != null) { - anchors.put(anchor, object); // Update anchor with real object. - } - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); + Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); + if (property == null) + throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); + Class propertyElementType = config.propertyToElementType.get(property); + if (propertyElementType == null) + propertyElementType = getElementTypeFromGenerics(property.getGenericType()); + Class propertyDefaultType = config.propertyToDefaultType.get(property); + if (!isExplicitKey) value = readValue(property.getType(), propertyElementType, propertyDefaultType); + property.set(object, value); + } catch (Exception ex) { + if (ex instanceof YamlReaderException) throw (YamlReaderException)ex; + throw new YamlReaderException("Error setting property '" + key + "' on class: " + type.getName(), ex); } } - return object; } - case SEQUENCE_START: { - // Must be a collection or an array. - event = parser.getNextEvent(); - Collection collection; - if (Collection.class.isAssignableFrom(type)) { - try { - collection = (Collection) Beans.createObject(type, config.privateConstructors); - } catch (InvocationTargetException ex) { - throw new YamlReaderException("Error creating object.", ex); - } - } else if (type.isArray()) { - collection = new ArrayList(); - elementType = type.getComponentType(); - } else { - throw new YamlReaderException("A sequence is not a valid value for the type: " + type.getName()); - } - if (!type.isArray() && anchor != null) { - anchors.put(anchor, collection); - } - while (true) { - event = parser.peekNextEvent(); - if (event.type == SEQUENCE_END) { - parser.getNextEvent(); - break; - } - collection.add(readValue(elementType, null, null)); - } - if (!type.isArray()) { - return collection; + if (object instanceof DeferredConstruction) { + try { + object = ((DeferredConstruction)object).construct(); + if (anchor != null) anchors.put(anchor, object); // Update anchor with real object. + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); } - Object array = Array.newInstance(elementType, collection.size()); - int i = 0; - for (Object object : collection) { - Array.set(array, i++, object); + } + return object; + } + case SEQUENCE_START: { + // Must be a collection or an array. + event = parser.getNextEvent(); + Collection collection; + if (Collection.class.isAssignableFrom(type)) { + try { + collection = (Collection)Beans.createObject(type, config.privateConstructors); + } catch (InvocationTargetException ex) { + throw new YamlReaderException("Error creating object.", ex); } - if (anchor != null) { - anchors.put(anchor, array); + } else if (type.isArray()) { + collection = new ArrayList(); + elementType = type.getComponentType(); + } else + throw new YamlReaderException("A sequence is not a valid value for the type: " + type.getName()); + if (!type.isArray() && anchor != null) anchors.put(anchor, collection); + while (true) { + event = parser.peekNextEvent(); + if (event.type == SEQUENCE_END) { + parser.getNextEvent(); + break; } - return array; + collection.add(readValue(elementType, null, null)); } - case SCALAR: - // Interpret an empty scalar as null. - System.out.println(((ScalarEvent) event).value); - if (((ScalarEvent) event).value.length() == 0) { - event = parser.getNextEvent(); - return null; - } - // Fall through. - default: - throw new YamlReaderException("Expected data for a " + type.getName() + " field but found: " + event.type); + if (!type.isArray()) return collection; + Object array = Array.newInstance(elementType, collection.size()); + int i = 0; + for (Object object : collection) + Array.set(array, i++, object); + if (anchor != null) anchors.put(anchor, array); + return array; + } + case SCALAR: + // Interpret an empty scalar as null. + System.out.println(((ScalarEvent)event).value); + if (((ScalarEvent)event).value.length() == 0) { + event = parser.getNextEvent(); + return null; + } + // Fall through. + default: + throw new YamlReaderException("Expected data for a " + type.getName() + " field but found: " + event.type); } } - private Class getElementTypeFromGenerics(Type type) { + private Class getElementTypeFromGenerics (Type type) { if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; + ParameterizedType parameterizedType = (ParameterizedType)type; Type rawType = parameterizedType.getRawType(); if (isCollection(rawType) || isMap(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments.length > 0) { - return (Class) actualTypeArguments[actualTypeArguments.length - 1]; + return (Class)actualTypeArguments[actualTypeArguments.length - 1]; } } } return null; } - private boolean isMap(Type type) { + private boolean isMap (Type type) { return Map.class.isAssignableFrom((Class) type); } - private boolean isCollection(Type type) { + private boolean isCollection (Type type) { return Collection.class.isAssignableFrom((Class) type); } /** Returns a new object of the requested type. */ - protected Object createObject(Class type) throws InvocationTargetException { + protected Object createObject (Class type) throws InvocationTargetException { // Use deferred construction if a non-zero-arg constructor is available. DeferredConstruction deferredConstruction = Beans.getDeferredConstruction(type, config); - if (deferredConstruction != null) { - return deferredConstruction; - } + if (deferredConstruction != null) return deferredConstruction; return Beans.createObject(type, config.privateConstructors); } public class YamlReaderException extends YamlException { - public YamlReaderException(String message, Throwable cause) { + public YamlReaderException (String message, Throwable cause) { super("Line " + parser.getLineNumber() + ", column " + parser.getColumn() + ": " + message, cause); } - public YamlReaderException(String message) { + public YamlReaderException (String message) { this(message, null); } } - public static void main(String[] args) throws Exception { + public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 9424f33..d63f8ee 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -59,20 +59,20 @@ public class YamlWriter { private int nextAnchor = 1; private boolean isRoot; - public YamlWriter(Writer writer) { + public YamlWriter (Writer writer) { this(writer, new YamlConfig()); } - public YamlWriter(Writer writer, YamlConfig config) { + public YamlWriter (Writer writer, YamlConfig config) { this.config = config; emitter = new Emitter(writer, config.writeConfig.emitterConfig); } - public void setAlias(Object object, String alias) { + public void setAlias (Object object, String alias) { anchoredObjects.put(object, alias); } - public void write(Object object) throws YamlException { + public void write (Object object) throws YamlException { if (config.writeConfig.autoAnchor) { countObjectReferences(object); queuedObjects.add(object); @@ -81,11 +81,11 @@ public void write(Object object) throws YamlException { writeInternal(object); } - public YamlConfig getConfig() { + public YamlConfig getConfig () { return config; } - private void writeInternal(Object object) throws YamlException { + private void writeInternal (Object object) throws YamlException { try { if (!started) { emitter.emit(Event.STREAM_START); @@ -103,16 +103,15 @@ private void writeInternal(Object object) throws YamlException { } /** Returns the YAML emitter, which allows the YAML output to be configured. */ - public Emitter getEmitter() { + public Emitter getEmitter () { return emitter; } /** Writes any buffered objects, then resets the list of anchored objects. * @see WriteConfig#setAutoAnchor(boolean) */ - public void clearAnchors() throws YamlException { - for (Object object : queuedObjects) { + public void clearAnchors () throws YamlException { + for (Object object : queuedObjects) writeInternal(object); - } queuedObjects.clear(); referenceCount.clear(); nextAnchor = 1; @@ -120,7 +119,7 @@ public void clearAnchors() throws YamlException { /** Finishes writing any buffered output and releases all resources. * @throws YamlException If the buffered output could not be written or the writer could not be closed. */ - public void close() throws YamlException { + public void close () throws YamlException { clearAnchors(); defaultValuePrototypes.clear(); try { @@ -133,24 +132,22 @@ public void close() throws YamlException { } } - private void writeValue(Object object, Class fieldClass, Class elementType, Class defaultType) throws EmitterException, - IOException, YamlException { + private void writeValue (Object object, Class fieldClass, Class elementType, Class defaultType) throws EmitterException, + IOException, YamlException { boolean isRoot = this.isRoot; this.isRoot = false; if (object == null) { - emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, null, (char) 0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); return; } Class valueClass = object.getClass(); boolean unknownType = fieldClass == null; - if (unknownType) { - fieldClass = valueClass; - } + if (unknownType) fieldClass = valueClass; if (object instanceof Enum) { - emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, ((Enum) object).name(), (char) 0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), (char)0)); return; } @@ -177,45 +174,33 @@ private void writeValue(Object object, Class fieldClass, Class elementType, Clas boolean showTag = false; if ((unknownType || valueClass != fieldClass || config.writeConfig.writeClassName == WriteClassName.ALWAYS) && config.writeConfig.writeClassName != WriteClassName.NEVER) { showTag = true; - if ((unknownType || fieldClass == List.class) && valueClass == ArrayList.class) { - showTag = false; - } - if ((unknownType || fieldClass == Map.class) && valueClass == HashMap.class) { - showTag = false; - } - if (fieldClass == Set.class && valueClass == HashSet.class) { - showTag = false; - } - if (valueClass == defaultType) { - showTag = false; - } + if ((unknownType || fieldClass == List.class) && valueClass == ArrayList.class) showTag = false; + if ((unknownType || fieldClass == Map.class) && valueClass == HashMap.class) showTag = false; + if (fieldClass == Set.class && valueClass == HashSet.class) showTag = false; + if (valueClass == defaultType) showTag = false; if (showTag) { tag = config.classNameToTag.get(valueClass.getName()); - if (tag == null) { - tag = valueClass.getName(); - } + if (tag == null) tag = valueClass.getName(); } } for (Entry entry : config.scalarSerializers.entrySet()) { if (entry.getKey().isAssignableFrom(valueClass)) { ScalarSerializer serializer = entry.getValue(); - emitter.emit(new ScalarEvent(null, tag, new boolean[]{tag == null, tag == null}, serializer.write(object), (char) 0)); + emitter.emit(new ScalarEvent(null, tag, new boolean[] {tag == null, tag == null}, serializer.write(object), (char)0)); return; } } if (Beans.isScalar(valueClass)) { - emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, String.valueOf(object), (char) 0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, String.valueOf(object), (char)0)); return; } if (object instanceof Collection) { emitter.emit(new SequenceStartEvent(anchor, tag, !showTag, false)); - for (Object item : (Collection) object) { - if (isRoot && !config.writeConfig.writeRootElementTags) { - elementType = item.getClass(); - } + for (Object item : (Collection)object) { + if (isRoot && !config.writeConfig.writeRootElementTags) elementType = item.getClass(); writeValue(item, elementType, null, null); } emitter.emit(Event.SEQUENCE_END); @@ -224,12 +209,10 @@ private void writeValue(Object object, Class fieldClass, Class elementType, Clas if (object instanceof Map) { emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); - for (Object item : ((Map) object).entrySet()) { - Entry entry = (Entry) item; + for (Object item : ((Map)object).entrySet()) { + Entry entry = (Entry)item; writeValue(entry.getKey(), null, null, null); - if (isRoot && !config.writeConfig.writeRootElementTags) { - elementType = entry.getValue().getClass(); - } + if (isRoot && !config.writeConfig.writeRootElementTags) elementType = entry.getValue().getClass(); writeValue(entry.getValue(), elementType, null, null); } emitter.emit(Event.MAPPING_END); @@ -239,9 +222,8 @@ private void writeValue(Object object, Class fieldClass, Class elementType, Clas if (fieldClass.isArray()) { elementType = fieldClass.getComponentType(); emitter.emit(new SequenceStartEvent(anchor, null, true, false)); - for (int i = 0, n = Array.getLength(object); i < n; i++) { + for (int i = 0, n = Array.getLength(object); i < n; i++) writeValue(Array.get(object, i), elementType, null, null); - } emitter.emit(Event.SEQUENCE_END); return; } @@ -274,14 +256,10 @@ private void writeValue(Object object, Class fieldClass, Class elementType, Clas if (prototype != null) { // Don't output properties that have the default value for the prototype. Object prototypeValue = property.get(prototype); - if (propertyValue == null && prototypeValue == null) { - continue; - } - if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) { - continue; - } + if (propertyValue == null && prototypeValue == null) continue; + if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) continue; } - emitter.emit(new ScalarEvent(null, null, new boolean[]{true, true}, property.getName(), (char) 0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), (char)0)); Class propertyElementType = config.propertyToElementType.get(property); Class propertyDefaultType = config.propertyToDefaultType.get(property); writeValue(propertyValue, property.getType(), propertyElementType, propertyDefaultType); @@ -292,10 +270,8 @@ private void writeValue(Object object, Class fieldClass, Class elementType, Clas emitter.emit(Event.MAPPING_END); } - private void countObjectReferences(Object object) throws YamlException { - if (object == null || Beans.isScalar(object.getClass())) { - return; - } + private void countObjectReferences (Object object) throws YamlException { + if (object == null || Beans.isScalar(object.getClass())) return; // Count every reference to the object, but follow its own references the first time it is encountered. Integer count = referenceCount.get(object); @@ -306,23 +282,20 @@ private void countObjectReferences(Object object) throws YamlException { referenceCount.put(object, 1); if (object instanceof Collection) { - for (Object item : (Collection) object) { + for (Object item : (Collection)object) countObjectReferences(item); - } return; } if (object instanceof Map) { - for (Object value : ((Map) object).values()) { + for (Object value : ((Map)object).values()) countObjectReferences(value); - } return; } if (object.getClass().isArray()) { - for (int i = 0, n = Array.getLength(object); i < n; i++) { + for (int i = 0, n = Array.getLength(object); i < n; i++) countObjectReferences(Array.get(object, i)); - } return; } @@ -335,9 +308,7 @@ private void countObjectReferences(Object object) throws YamlException { throw new YamlException("Error inspecting class: " + object.getClass().getName(), ex); } for (Property property : properties) { - if (Beans.isScalar(property.getType())) { - continue; - } + if (Beans.isScalar(property.getType())) continue; Object propertyValue; try { propertyValue = property.get(object); From 5a1db5b3f061c4483e63054c2a73b1d46a156e9e Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 27 Jan 2015 18:36:08 +0100 Subject: [PATCH 003/114] Got rid of Introspector, bean properties changed slightly. Bean property getter and setter methods now need to be named the same as the field they get/set. This is usually the case and makes it so we can enumerate the fields and look for a getter and setter method that matches the field name. closes #25 --- src/com/esotericsoftware/yamlbeans/Beans.java | 112 +++++++++--------- .../yamlbeans/YamlConfig.java | 45 +++---- .../yamlbeans/YamlWriter.java | 43 +++---- 3 files changed, 89 insertions(+), 111 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 7aa2a59..454be78 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -16,15 +16,11 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; - -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; @@ -35,6 +31,8 @@ import java.util.Set; import java.util.TreeSet; +import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; + /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ class Beans { @@ -62,16 +60,6 @@ static public DeferredConstruction getDeferredConstruction (Class type, YamlConf return null; } - static private boolean canInitializeProperty (Class type, PropertyDescriptor property, YamlConfig config) { - if (property.getWriteMethod() != null) return true; - - // Check if the property can be initialized through the constructor. - DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - if (deferredConstruction != null && deferredConstruction.hasParameter(property.getName())) return true; - - return false; - } - static public Object createObject (Class type, boolean privateConstructors) throws InvocationTargetException { // Use no-arg constructor. Constructor constructor = null; @@ -117,16 +105,34 @@ static public Object createObject (Class type, boolean privateConstructors) thro } } - static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { + static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); + Class[] noArgs = new Class[0], oneArg = new Class[1]; Set properties = new TreeSet(); - if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) - properties.add(new MethodProperty(type, property)); - } for (Field field : getAllFields(type)) { + String name = field.getName(); + + if (beanProperties) { + DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); + boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); + + String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null, setMethod = null; + try { + oneArg[0] = field.getType(); + setMethod = type.getMethod("set" + nameUpper, oneArg); + } catch (Exception ignored) { + } + try { + getMethod = type.getMethod("get" + nameUpper, noArgs); + } catch (Exception ignored) { + } + if (getMethod != null && (setMethod != null || constructorProperty)) { + properties.add(new MethodProperty(name, setMethod, getMethod)); + continue; + } + } + int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { @@ -138,27 +144,39 @@ static public Set getProperties (Class type, boolean beanProperties, b return properties; } - static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) - throws IntrospectionException { + static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); - if (beanProperties) { - for (PropertyDescriptor property : Introspector.getBeanInfo(type).getPropertyDescriptors()) { - if (property.getName().equals(name)) { - if (property.getReadMethod() != null && canInitializeProperty(type, property, config)) - return new MethodProperty(type, property); - break; + Class[] noArgs = new Class[0], oneArg = new Class[1]; + for (Field field : getAllFields(type)) { + if (!field.getName().equals(name)) continue; + + if (beanProperties) { + DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); + boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); + + String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null, setMethod = null; + try { + oneArg[0] = field.getType(); + setMethod = type.getMethod("set" + nameUpper, oneArg); + } catch (Exception ignored) { + } + try { + getMethod = type.getMethod("get" + nameUpper, noArgs); + } catch (Exception ignored) { } + if (getMethod != null && (setMethod != null || constructorProperty)) + return new MethodProperty(name, setMethod, getMethod); } - } - for (Field field : getAllFields(type)) { + int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers)) { if (!privateFields) continue; field.setAccessible(true); } - if (field.getName().equals(name)) return new FieldProperty(field); + return new FieldProperty(field); } return null; } @@ -174,11 +192,12 @@ static private ArrayList getAllFields (Class type) { } static public class MethodProperty extends Property { - private final PropertyDescriptor property; + private final Method setMethod, getMethod; - public MethodProperty (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { - super(declaringClass, property); - this.property = property; + public MethodProperty (String name, Method setMethod, Method getMethod) { + super(getMethod.getDeclaringClass(), name, getMethod.getReturnType()); + this.setMethod = setMethod; + this.getMethod = getMethod; } public void set (Object object, Object value) throws Exception { @@ -186,11 +205,11 @@ public void set (Object object, Object value) throws Exception { ((DeferredConstruction)object).storeProperty(this, value); return; } - property.getWriteMethod().invoke(object, value); + setMethod.invoke(object, value); } public Object get (Object object) throws Exception { - return property.getReadMethod().invoke(object); + return getMethod.invoke(object); } } @@ -226,21 +245,6 @@ static public abstract class Property implements Comparable { this.type = type; } - Property (Class declaringClass, PropertyDescriptor property) throws IntrospectionException { - this.declaringClass = declaringClass; - this.name = property.getName(); - try { - // The PropertyDescriptor returns the wrong type if the getter is an implementation of a interface method with a - // generic return value. - type = property.getReadMethod().getDeclaringClass() - .getDeclaredMethod(property.getReadMethod().getName(), new Class[0]).getReturnType(); - } catch (Exception ex) { - IntrospectionException introspectionEx = new IntrospectionException("Error getting "); - introspectionEx.initCause(ex); - throw introspectionEx; - } - } - public int hashCode () { final int prime = 31; int result = 1; diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 3a12ef0..1dbffcb 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -16,12 +16,6 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.Beans.Property; -import com.esotericsoftware.yamlbeans.emitter.EmitterConfig; -import com.esotericsoftware.yamlbeans.scalar.DateSerializer; -import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; - -import java.beans.IntrospectionException; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; @@ -31,6 +25,11 @@ import java.util.IdentityHashMap; import java.util.Map; +import com.esotericsoftware.yamlbeans.Beans.Property; +import com.esotericsoftware.yamlbeans.emitter.EmitterConfig; +import com.esotericsoftware.yamlbeans.scalar.DateSerializer; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + /** Stores configuration for reading and writing YAML. * @author Nathan Sweet */ public class YamlConfig { @@ -80,20 +79,13 @@ public void setPropertyElementType (Class type, String propertyName, Class eleme if (type == null) throw new IllegalArgumentException("type cannot be null."); if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null."); if (elementType == null) throw new IllegalArgumentException("propertyType cannot be null."); - Property property = null; - Exception cause = null; - try { - property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); - } catch (IntrospectionException ex) { - cause = ex; - } - if (property == null) { - throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, - cause); - } - if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) + Property property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); + if (property == null) + throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName); + if (!Collection.class.isAssignableFrom(property.getType()) && !Map.class.isAssignableFrom(property.getType())) { throw new IllegalArgumentException("The '" + propertyName + "' property on the " + type.getName() + " class must be a Collection or Map: " + property.getType()); + } propertyToElementType.put(property, elementType); } @@ -103,21 +95,14 @@ public void setPropertyDefaultType (Class type, String propertyName, Class defau if (type == null) throw new IllegalArgumentException("type cannot be null."); if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null."); if (defaultType == null) throw new IllegalArgumentException("defaultType cannot be null."); - Property property = null; - Exception cause = null; - try { - property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); - } catch (IntrospectionException ex) { - cause = ex; - } - if (property == null) { - throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName, - cause); - } + Property property = Beans.getProperty(type, propertyName, beanProperties, privateFields, this); + if (property == null) + throw new IllegalArgumentException("The class " + type.getName() + " does not have a property named: " + propertyName); propertyToDefaultType.put(property, defaultType); } - /** If true, bean properties with both a getter and setter will be used. Default is true. */ + /** If true, bean properties with both a getter and setter will be used. Note the getter and setter methods must be named the + * same as the field they get or set. Default is true. */ public void setBeanProperties (boolean beanProperties) { this.beanProperties = beanProperties; } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 6e62de8..665c5a2 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -16,20 +16,6 @@ package com.esotericsoftware.yamlbeans; -import com.esotericsoftware.yamlbeans.Beans.Property; -import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; -import com.esotericsoftware.yamlbeans.emitter.Emitter; -import com.esotericsoftware.yamlbeans.emitter.EmitterException; -import com.esotericsoftware.yamlbeans.parser.AliasEvent; -import com.esotericsoftware.yamlbeans.parser.DocumentEndEvent; -import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; -import com.esotericsoftware.yamlbeans.parser.Event; -import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; -import com.esotericsoftware.yamlbeans.parser.ScalarEvent; -import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; -import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; - -import java.beans.IntrospectionException; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Array; @@ -44,6 +30,19 @@ import java.util.Map.Entry; import java.util.Set; +import com.esotericsoftware.yamlbeans.Beans.Property; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.AliasEvent; +import com.esotericsoftware.yamlbeans.parser.DocumentEndEvent; +import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; +import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + /** Serializes Java objects as YAML. * @author Nathan Sweet */ public class YamlWriter { @@ -243,12 +242,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } } - Set properties; - try { - properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config); - } catch (IntrospectionException ex) { - throw new YamlException("Error inspecting class: " + valueClass.getName(), ex); - } + Set properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config); emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); for (Property property : properties) { try { @@ -299,14 +293,9 @@ private void countObjectReferences (Object object) throws YamlException { return; } - // Value must be a bean. + // Value must be an object. - Set properties; - try { - properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config); - } catch (IntrospectionException ex) { - throw new YamlException("Error inspecting class: " + object.getClass().getName(), ex); - } + Set properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config); for (Property property : properties) { if (Beans.isScalar(property.getType())) continue; Object propertyValue; From 9e07674b7d07ebef6f622ad23223768c44431914 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 7 Feb 2015 18:48:49 +0100 Subject: [PATCH 004/114] 1.09 --- pom.xml | 2 +- project.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2c701a..dfb7071 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.08 + 1.09 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans diff --git a/project.yaml b/project.yaml index 96b523a..ee560ea 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.08 +version: 1.09 From b1265377e3c06bb0160846e0a15291efab528a24 Mon Sep 17 00:00:00 2001 From: The Alchemist Date: Wed, 1 Apr 2015 18:28:18 -0400 Subject: [PATCH 005/114] adding null check for nextClass under some circumstances, nextClass can be null --- src/com/esotericsoftware/yamlbeans/Beans.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 454be78..060a88d 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -184,7 +184,7 @@ static public Property getProperty (Class type, String name, boolean beanPropert static private ArrayList getAllFields (Class type) { ArrayList allFields = new ArrayList(); Class nextClass = type; - while (nextClass != Object.class) { + while (nextClass != null && nextClass != Object.class) { Collections.addAll(allFields, nextClass.getDeclaredFields()); nextClass = nextClass.getSuperclass(); } From 7f3d20fcecc933885aa3c72ead4938e936dcbf38 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Thu, 30 Apr 2015 13:48:39 +0200 Subject: [PATCH 006/114] Use single quotes for strings that look like numbers. closes #29 --- src/com/esotericsoftware/yamlbeans/YamlWriter.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 665c5a2..96d6c05 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -192,7 +192,16 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } if (Beans.isScalar(valueClass)) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, String.valueOf(object), (char)0)); + char style = 0; + String string = String.valueOf(object); + if (valueClass == String.class) { + try { + Float.parseFloat(string); + style = '\''; + } catch (NumberFormatException ignored) { + } + } + emitter.emit(new ScalarEvent(null, tag, new boolean[] {true, true}, string, style)); return; } From c312f30d18c821b4b3e55208e88013997908afb8 Mon Sep 17 00:00:00 2001 From: Simon Koelsch Date: Thu, 2 Jul 2015 23:05:05 +0200 Subject: [PATCH 007/114] removed System.out.println during parsing --- src/com/esotericsoftware/yamlbeans/YamlReader.java | 1 - src/com/esotericsoftware/yamlbeans/parser/Parser.java | 1 - src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java | 1 - 3 files changed, 3 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index a302b9a..6736633 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -344,7 +344,6 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } case SCALAR: // Interpret an empty scalar as null. - System.out.println(((ScalarEvent)event).value); if (((ScalarEvent)event).value.length() == 0) { event = parser.getNextEvent(); return null; diff --git a/src/com/esotericsoftware/yamlbeans/parser/Parser.java b/src/com/esotericsoftware/yamlbeans/parser/Parser.java index d387ea2..0ac84f2 100644 --- a/src/com/esotericsoftware/yamlbeans/parser/Parser.java +++ b/src/com/esotericsoftware/yamlbeans/parser/Parser.java @@ -79,7 +79,6 @@ public Event getNextEvent () throws ParserException, TokenizerException { while (!parseStack.isEmpty()) { Event event = parseStack.remove(0).produce(); if (event != null) { - // System.out.println("Parser: " + event); return event; } } diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index 9281cff..997c347 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -133,7 +133,6 @@ public Token getNextToken () throws TokenizerException { if (!tokens.isEmpty()) { tokensTaken++; Token token = tokens.remove(0); - // System.out.println("Tokenizer: " + token); return token; } return null; From e92650b58fd57eeffd9d6acae4ef5a126753d795 Mon Sep 17 00:00:00 2001 From: Daniel Boitnott Date: Mon, 27 Jul 2015 06:42:55 -0500 Subject: [PATCH 008/114] Added support for boolean properties with is pattern per JavaBeans spec, section 8.3.2 --- src/com/esotericsoftware/yamlbeans/Beans.java | 13 ++++ .../yamlbeans/BooleanTest.java | 78 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 test/com/esotericsoftware/yamlbeans/BooleanTest.java diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 1da9d77..424808f 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -34,6 +34,7 @@ import java.util.TreeSet; import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; +import com.sun.org.apache.xpath.internal.operations.Bool; /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ @@ -129,6 +130,12 @@ static public Set getProperties (Class type, boolean beanProperties, b getMethod = type.getMethod("get" + nameUpper, noArgs); } catch (Exception ignored) { } + if (getMethod == null && (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class))) { + try { + getMethod = type.getMethod("is" + nameUpper, noArgs); + } catch (Exception ignored) { + } + } if (getMethod != null && (setMethod != null || constructorProperty)) { properties.add(new MethodProperty(name, setMethod, getMethod)); continue; @@ -168,6 +175,12 @@ static public Property getProperty (Class type, String name, boolean beanPropert getMethod = type.getMethod("get" + nameUpper, noArgs); } catch (Exception ignored) { } + if (getMethod == null && (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class))) { + try { + getMethod = type.getMethod("is" + nameUpper, noArgs); + } catch (Exception ignored) { + } + } if (getMethod != null && (setMethod != null || constructorProperty)) return new MethodProperty(name, setMethod, getMethod); } diff --git a/test/com/esotericsoftware/yamlbeans/BooleanTest.java b/test/com/esotericsoftware/yamlbeans/BooleanTest.java new file mode 100644 index 0000000..238299e --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/BooleanTest.java @@ -0,0 +1,78 @@ +package com.esotericsoftware.yamlbeans; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class BooleanTest extends TestCase { + public void testBooleanBean() throws Exception { + // Create a bean with a value differing from it's default + BeanWithBoolean val = new BeanWithBoolean(); + val.setBool(true); + val.setBoolObj(true); + + // Store the bean + ByteArrayOutputStream out = new ByteArrayOutputStream(); + YamlWriter yamlWriter = new YamlWriter(new OutputStreamWriter(out)); + yamlWriter.write(val); + yamlWriter.close(); + + // Load the bean + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + YamlReader yamlReader = new YamlReader(new InputStreamReader(in)); + BeanWithBoolean got = yamlReader.read(BeanWithBoolean.class); + + assertEquals(val, got); + } + + public static class BeanWithBoolean { + private boolean bool = false; + private Boolean boolObj = false; + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public Boolean getBoolObj() { + return boolObj; + } + + public void setBoolObj(Boolean boolObj) { + this.boolObj = boolObj; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BeanWithBoolean that = (BeanWithBoolean) o; + + if (bool != that.bool) return false; + return !(boolObj != null ? !boolObj.equals(that.boolObj) : that.boolObj != null); + + } + + @Override + public int hashCode() { + int result = (bool ? 1 : 0); + result = 31 * result + (boolObj != null ? boolObj.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "BeanWithBoolean{" + + "bool=" + bool + + ", boolObj=" + boolObj + + '}'; + } + } +} From f2f4e142ab3c0d3de465b15abdca50de4f61cbea Mon Sep 17 00:00:00 2001 From: Daniel Boitnott Date: Mon, 27 Jul 2015 08:19:16 -0500 Subject: [PATCH 009/114] Removed unintended import --- src/com/esotericsoftware/yamlbeans/Beans.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 424808f..f240e52 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -34,7 +34,6 @@ import java.util.TreeSet; import com.esotericsoftware.yamlbeans.YamlConfig.ConstructorParameters; -import com.sun.org.apache.xpath.internal.operations.Bool; /** Utility for dealing with beans and public fields. * @author Nathan Sweet */ From 24afd0a3917171f1a9b622f63439a070874394b2 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 8 Aug 2015 01:52:47 +0200 Subject: [PATCH 010/114] Remove spaces in names. --- src/com/esotericsoftware/yamlbeans/Beans.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 060a88d..1e633df 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -147,6 +147,7 @@ static public Set getProperties (Class type, boolean beanProperties, b static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); + name = name.replace(" ", ""); Class[] noArgs = new Class[0], oneArg = new Class[1]; for (Field field : getAllFields(type)) { if (!field.getName().equals(name)) continue; From 1517822506fd66a5de34e5c7ef4fe5e41cac5dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Guimar=C3=A3es?= Date: Fri, 14 Aug 2015 07:23:56 -0300 Subject: [PATCH 011/114] Must Use LinkedHashMap and LinkedList to ensure comparision with YAML constant on test classes. --- .../yamlbeans/GenericTest.java | 287 ++++++++++-------- 1 file changed, 155 insertions(+), 132 deletions(-) diff --git a/test/com/esotericsoftware/yamlbeans/GenericTest.java b/test/com/esotericsoftware/yamlbeans/GenericTest.java index 2204650..0b5d341 100644 --- a/test/com/esotericsoftware/yamlbeans/GenericTest.java +++ b/test/com/esotericsoftware/yamlbeans/GenericTest.java @@ -5,140 +5,163 @@ import junit.framework.TestCase; import java.io.StringWriter; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; public class GenericTest extends TestCase { - private static final String YAML = - "integerList: \n"+ - "- 1\n"+ - "- 100500\n"+ - "- 10\n"+ - "stringMap: \n"+ - " a: av\n"+ - " b: bv\n"+ - "structList: \n"+ - "- i: 10\n"+ - " str: aaa\n"+ - "- i: 20\n"+ - " str: bbb\n"+ - "structMap: \n"+ - " a: \n"+ - " i: 1\n"+ - " str: aa\n"+ - " b: \n"+ - " i: 2\n"+ - " str: ab\n"; - - public void testRead () throws YamlException { - Test test = createTest(); - - Test read = new YamlReader(YAML).read(Test.class); - Assert.assertEquals(test, read); - } - - public void testWrite () throws YamlException { - Test test = createTest(); - StringWriter stringWriter = new StringWriter(); - - YamlWriter yamlWriter = new YamlWriter(stringWriter); - yamlWriter.getConfig().writeConfig.setWriteClassname(WriteClassName.NEVER); - yamlWriter.write(test); - yamlWriter.close(); - - Assert.assertEquals(YAML, stringWriter.getBuffer().toString()); - } - - private Test createTest () { - Map stringMap = new HashMap(); - stringMap.put("a", "av"); - stringMap.put("b", "bv"); - - Map structMap = new HashMap(); - structMap.put("a", new Struct(1, "aa")); - structMap.put("b", new Struct(2, "ab")); - - List integerList = new ArrayList(); - integerList.add(1); - integerList.add(100500); - integerList.add(10); - - List structList = new ArrayList(); - structList.add(new Struct(10, "aaa")); - structList.add(new Struct(20, "bbb")); - - Test test = new Test(); - test.stringMap = stringMap; - test.structMap = structMap; - test.integerList = integerList; - test.structList = structList; - - return test; - } - - static class Struct { - public int i; - public String str; - - Struct (int i, String str) { - this.i = i; - this.str = str; - } - - Struct () { - } - - @Override - public boolean equals (Object o) { - if (this == o) { return true; } - if (!(o instanceof Struct)) { return false; } - - Struct struct = (Struct) o; - - if (i != struct.i) { return false; } - if (str != null ? !str.equals(struct.str) : struct.str != null) { return false; } - - return true; - } - - @Override - public int hashCode () { - int result = i; - result = 31 * result + (str != null ? str.hashCode() : 0); - return result; - } - } - - static class Test { - public Map stringMap; - public Map structMap; - public List integerList; - public List structList; - - @Override - public boolean equals (Object o) { - if (this == o) { return true; } - if (!(o instanceof Test)) { return false; } - - Test test = (Test) o; - - if (integerList != null ? !integerList.equals(test.integerList) : test.integerList != null) { return false; } - if (stringMap != null ? !stringMap.equals(test.stringMap) : test.stringMap != null) { return false; } - if (structList != null ? !structList.equals(test.structList) : test.structList != null) { return false; } - if (structMap != null ? !structMap.equals(test.structMap) : test.structMap != null) { return false; } - - return true; - } - - @Override - public int hashCode () { - int result = stringMap != null ? stringMap.hashCode() : 0; - result = 31 * result + (structMap != null ? structMap.hashCode() : 0); - result = 31 * result + (integerList != null ? integerList.hashCode() : 0); - result = 31 * result + (structList != null ? structList.hashCode() : 0); - return result; - } - } + + private static final String YAML + = "integerList: \n" + + "- 1\n" + + "- 100500\n" + + "- 10\n" + + "stringMap: \n" + + " a: av\n" + + " b: bv\n" + + "structList: \n" + + "- i: 10\n" + + " str: aaa\n" + + "- i: 20\n" + + " str: bbb\n" + + "structMap: \n" + + " a: \n" + + " i: 1\n" + + " str: aa\n" + + " b: \n" + + " i: 2\n" + + " str: ab\n"; + + public void testRead() throws YamlException { + Test test = createTest(); + + Test read = new YamlReader(YAML).read(Test.class); + Assert.assertEquals(test, read); + } + + public void testWrite() throws YamlException { + Test test = createTest(); + StringWriter stringWriter = new StringWriter(); + + YamlWriter yamlWriter = new YamlWriter(stringWriter); + yamlWriter.getConfig().writeConfig.setWriteClassname(WriteClassName.NEVER); + yamlWriter.write(test); + yamlWriter.close(); + + Assert.assertEquals(YAML, stringWriter.getBuffer().toString()); + } + + private Test createTest() { + Map stringMap = new LinkedHashMap(); + stringMap.put("a", "av"); + stringMap.put("b", "bv"); + + Map structMap = new LinkedHashMap(); + structMap.put("a", new Struct(1, "aa")); + structMap.put("b", new Struct(2, "ab")); + + List integerList = new LinkedList(); + integerList.add(1); + integerList.add(100500); + integerList.add(10); + + List structList = new LinkedList(); + structList.add(new Struct(10, "aaa")); + structList.add(new Struct(20, "bbb")); + + Test test = new Test(); + test.stringMap = stringMap; + test.structMap = structMap; + test.integerList = integerList; + test.structList = structList; + + return test; + } + + static class Struct { + + public int i; + public String str; + + Struct(int i, String str) { + this.i = i; + this.str = str; + } + + Struct() { + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Struct)) { + return false; + } + + Struct struct = (Struct) o; + + if (i != struct.i) { + return false; + } + if (str != null ? !str.equals(struct.str) : struct.str != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = i; + result = 31 * result + (str != null ? str.hashCode() : 0); + return result; + } + } + + static class Test { + + public Map stringMap; + public Map structMap; + public List integerList; + public List structList; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Test)) { + return false; + } + + Test test = (Test) o; + + if (integerList != null ? !integerList.equals(test.integerList) : test.integerList != null) { + return false; + } + if (stringMap != null ? !stringMap.equals(test.stringMap) : test.stringMap != null) { + return false; + } + if (structList != null ? !structList.equals(test.structList) : test.structList != null) { + return false; + } + if (structMap != null ? !structMap.equals(test.structMap) : test.structMap != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = stringMap != null ? stringMap.hashCode() : 0; + result = 31 * result + (structMap != null ? structMap.hashCode() : 0); + result = 31 * result + (integerList != null ? integerList.hashCode() : 0); + result = 31 * result + (structList != null ? structList.hashCode() : 0); + return result; + } + } } From 007ba784a12a8baae93f73c635ecc7074b34934e Mon Sep 17 00:00:00 2001 From: Guillaume Perrot Date: Mon, 28 Sep 2015 03:03:52 +0100 Subject: [PATCH 012/114] Add an option to allow unknown properties while deserializing an object. --- .../yamlbeans/YamlConfig.java | 6 +++++ .../yamlbeans/YamlReader.java | 5 +++- .../yamlbeans/YamlReaderTest.java | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index a4032ef..b2c6701 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -210,6 +210,7 @@ static public class ReadConfig { Version defaultVersion = new Version(1, 1); ClassLoader classLoader; final Map constructorParameters = new IdentityHashMap(); + boolean allowUnknownProperties; ReadConfig () { } @@ -241,6 +242,11 @@ public void setConstructorParameters (Class type, Class[] parameterTypes, String parameters.parameterNames = parameterNames; constructorParameters.put(type, parameters); } + + /** If true, unknown properties will be ignored instead of raising an exception while parsing. Default is false. */ + public void setAllowUnknownProperties(boolean allowUnknownProperties) { + this.allowUnknownProperties = allowUnknownProperties; + } } static class ConstructorParameters { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index b6e2708..8a0f040 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -289,7 +289,10 @@ private Object readValueInternal (Class type, Class elementType, String anchor) try { Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); if (property == null) - throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); + if (config.readConfig.allowUnknownProperties) + continue; + else + throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); Class propertyElementType = config.propertyToElementType.get(property); if (propertyElementType == null) propertyElementType = property.getElementType(); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 6b94992..e7a610b 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -292,4 +292,30 @@ public void setZ (int z) { this.z = z; } } + + static class ACD { + public int a, c, d = 4; + } + + public void testAllowUnknownProperties() { + String input = "a: 1\nb: 2\nc: 3"; + YamlConfig config = new YamlConfig(); + try { + new YamlReader(input, config).read(ACD.class); + fail("Unknown properties were not supposed to be allowed."); + } + catch (YamlException e) { + } + + config.readConfig.setAllowUnknownProperties(true); + try { + ACD pojo = new YamlReader(input, config).read(ACD.class); + assertEquals(1, pojo.a); + assertEquals(3, pojo.c); + assertEquals(4, pojo.d); + } + catch (YamlException e) { + fail("Unknown properties were supposed to be allowed."); + } + } } From d6d806428b6aa4c85aa0adcae8f1ecdb70572c46 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 5 Oct 2015 11:58:16 -0700 Subject: [PATCH 013/114] Renamed to setIgnoreUnknownProperties. --- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 8 ++++---- src/com/esotericsoftware/yamlbeans/YamlReader.java | 12 +++++------- .../esotericsoftware/yamlbeans/YamlReaderTest.java | 10 ++++------ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index b2c6701..c155ce6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -210,7 +210,7 @@ static public class ReadConfig { Version defaultVersion = new Version(1, 1); ClassLoader classLoader; final Map constructorParameters = new IdentityHashMap(); - boolean allowUnknownProperties; + boolean ignoreUnknownProperties; ReadConfig () { } @@ -243,9 +243,9 @@ public void setConstructorParameters (Class type, Class[] parameterTypes, String constructorParameters.put(type, parameters); } - /** If true, unknown properties will be ignored instead of raising an exception while parsing. Default is false. */ - public void setAllowUnknownProperties(boolean allowUnknownProperties) { - this.allowUnknownProperties = allowUnknownProperties; + /** When true, fields in the YAML that are not found on the class will not throw a {@link YamlException}. Default is false. */ + public void setIgnoreUnknownProperties (boolean allowUnknownProperties) { + this.ignoreUnknownProperties = allowUnknownProperties; } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 8a0f040..f1ff6a5 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -288,14 +288,12 @@ private Object readValueInternal (Class type, Class elementType, String anchor) // Set field on object. try { Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); - if (property == null) - if (config.readConfig.allowUnknownProperties) - continue; - else - throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); + if (property == null) { + if (config.readConfig.ignoreUnknownProperties) continue; + throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); + } Class propertyElementType = config.propertyToElementType.get(property); - if (propertyElementType == null) - propertyElementType = property.getElementType(); + if (propertyElementType == null) propertyElementType = property.getElementType(); Class propertyDefaultType = config.propertyToDefaultType.get(property); if (!isExplicitKey) value = readValue(property.getType(), propertyElementType, propertyDefaultType); property.set(object, value); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index e7a610b..b74c08e 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -297,24 +297,22 @@ static class ACD { public int a, c, d = 4; } - public void testAllowUnknownProperties() { + public void testIgnoreUnknownProperties () { String input = "a: 1\nb: 2\nc: 3"; YamlConfig config = new YamlConfig(); try { new YamlReader(input, config).read(ACD.class); fail("Unknown properties were not supposed to be allowed."); - } - catch (YamlException e) { + } catch (YamlException e) { } - config.readConfig.setAllowUnknownProperties(true); + config.readConfig.setIgnoreUnknownProperties(true); try { ACD pojo = new YamlReader(input, config).read(ACD.class); assertEquals(1, pojo.a); assertEquals(3, pojo.c); assertEquals(4, pojo.d); - } - catch (YamlException e) { + } catch (YamlException e) { fail("Unknown properties were supposed to be allowed."); } } From 3d237382486482b88e41d767ae8f611aa47848c7 Mon Sep 17 00:00:00 2001 From: Sam Lindsay-Levine Date: Mon, 16 May 2016 15:27:16 -0500 Subject: [PATCH 014/114] Added protected findTagClass() method on YamlReader as an extension point for different behavior in dealing with type tags while reading. --- .../yamlbeans/YamlReader.java | 68 ++++++++++++++---- .../yamlbeans/YamlReaderTest.java | 71 ++++++++++++++++++- 2 files changed, 125 insertions(+), 14 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index f1ff6a5..42d7c0f 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -133,23 +133,65 @@ protected Object readValue (Class type, Class elementType, Class defaultType) th default: } + return readValueInternal(this.chooseType(tag, defaultType, type), elementType, anchor); + } + + private Class chooseType(String tag, Class defaultType, Class providedType) throws YamlReaderException { if (tag != null) { - type = config.tagToClass.get(tag); - if (type == null) { - try { - if (config.readConfig.classLoader != null) - type = Class.forName(tag, true, config.readConfig.classLoader); - else - type = Class.forName(tag); - } catch (ClassNotFoundException ex) { - throw new YamlReaderException("Unable to find class specified by tag: " + tag); + Class userConfiguredByTag = config.tagToClass.get(tag); + if (userConfiguredByTag != null) { + return userConfiguredByTag; + } + + ClassLoader classLoader = (config.readConfig.classLoader == null ? this.getClass().getClassLoader() + : config.readConfig.classLoader); + + try { + Class loadedFromTag = findTagClass(tag, classLoader); + if (loadedFromTag != null) { + return loadedFromTag; } + } catch (ClassNotFoundException e) { + throw new YamlReaderException("Unable to find class specified by tag: " + tag); } - } else if (defaultType != null) { - type = defaultType; } - - return readValueInternal(type, elementType, anchor); + + if (defaultType != null) { + return defaultType; + } + + // This may be null. + return providedType; + } + + /** + * Used during reading when a tag is present, and + * {@link YamlConfig#setClassTag(String, Class)} was not used for that tag. + * Attempts to load the class corresponding to that tag. + * + * If this returns a non-null Class, that will be used as the + * deserialization type regardless of whether a type was explicitly asked + * for or if a default type exists. + * + * If this returns null, no guidance will be provided by the tag and we will + * fall back to the default type or a requested target type, if any exist. + * + * If this throws a ClassNotFoundException, parsing will fail. + * + * The default implementation is simply + * + *
+	 *  {@code Class.forName(tag, true, classLoader);}
+	 * 
+ * + * and never returns null. + * + * You can override this to handle cases where you do not want to respect + * the type tags found in a document - e.g., if they were output by another + * program using classes that do not exist on your classpath. + */ + protected Class findTagClass(String tag, ClassLoader classLoader) throws ClassNotFoundException { + return Class.forName(tag, true, classLoader); } private Object readValueInternal (Class type, Class elementType, String anchor) throws YamlException, ParserException, diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index b74c08e..dd5a983 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -159,7 +159,8 @@ private Test read (String yaml) throws Exception { if (true) { System.out.println(yaml); System.out.println("==="); - System.out.println(new YamlReader(yaml).read(null)); + Object obj = new YamlReader(yaml).read(null); + System.out.println(obj); System.out.println(); System.out.println(); } @@ -316,4 +317,72 @@ public void testIgnoreUnknownProperties () { fail("Unknown properties were supposed to be allowed."); } } + + private static class TypeTagIgnoringReader extends YamlReader { + + public TypeTagIgnoringReader(String yaml) { + super(yaml); + } + + @Override + protected Class findTagClass(String tag, ClassLoader classLoader) { + // Ignore all tags. + return null; + } + + } + + public void testIgnoreTypeTagsTopLevel () throws YamlException { + // We are parsing this document that was output by another program using YamlBeans, that includes + // a type tag for a class that we don't have on our classpath. + String input = "!com.example.not.on.classpath.Fish\n" + // + "species: Walleye\n" + // + "weight: 24"; + + try { + new YamlReader(input).read(Map.class); + fail("A type tag with an unknown class fails to parse, even if we ask for it as a simple Map."); + } catch (YamlException e) { + // Expected behavior. + } + + // If we ignore type tags, the type tag in the document does not override our request for a Map. + Map map = new TypeTagIgnoringReader(input).read(Map.class); + assertEquals("Walleye", map.get("species")); + } + + static class Lake { + public String name; + public List fish; + } + + static class Fish { + public String species; + public int weight; + } + + public void testIgnoreTypeTagsEmbedded () throws YamlException { + // We are parsing this document that was output by another program using YamlBeans, that includes + // type tags for multiple classes that we don't have on our classpath. One of those type tags is + // embedded within the document, not at the top level. + String input = "!com.example.not.on.classpath.Lake\n" + // + "name: Superior\n" + // + "fish:\n" + + " - !com.example.not.on.classpath.Fish\n" + // + " species: Walleye\n" + // + " weight: 24"; + + try { + new YamlReader(input).read(Lake.class); + fail("A type tag with an unknown class fails to parse, even if we ask for it as a known class."); + } catch (YamlException e) { + // Expected behavior. + } + + // If we ignore type tags, we can specify the return type & embedded types instead of failing to parse. + Lake lake = new TypeTagIgnoringReader(input).read(Lake.class); + assertEquals("Superior", lake.name); + assertEquals(1, lake.fish.size()); + assertEquals("Walleye", lake.fish.get(0).species); + } } From 500d1a190879074dd727cb30dc0168017c973481 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 3 Jun 2016 13:27:20 +0200 Subject: [PATCH 015/114] Always call setAccessible. --- src/com/esotericsoftware/yamlbeans/Beans.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 3644fa5..57182b7 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -15,6 +15,7 @@ */ package com.esotericsoftware.yamlbeans; + import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -143,16 +144,18 @@ static public Set getProperties (Class type, boolean beanProperties, b int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - if (!Modifier.isPublic(modifiers)) { - if (!privateFields) continue; + if (!Modifier.isPublic(modifiers) && !privateFields) continue; + try { field.setAccessible(true); + } catch (Exception ignored) { } properties.add(new FieldProperty(field)); } return properties; } - static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) { + static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, + YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); name = name.replace(" ", ""); @@ -187,9 +190,10 @@ static public Property getProperty (Class type, String name, boolean beanPropert int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - if (!Modifier.isPublic(modifiers)) { - if (!privateFields) continue; + if (!Modifier.isPublic(modifiers) && !privateFields) continue; + try { field.setAccessible(true); + } catch (Exception ignored) { } return new FieldProperty(field); } @@ -262,7 +266,6 @@ static public abstract class Property implements Comparable { this.elementType = getElementTypeFromGenerics(genericType); } - private Class getElementTypeFromGenerics (Type type) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; @@ -279,11 +282,11 @@ private Class getElementTypeFromGenerics (Type type) { } private boolean isMap (Type type) { - return Map.class.isAssignableFrom((Class) type); + return Map.class.isAssignableFrom((Class)type); } private boolean isCollection (Type type) { - return Collection.class.isAssignableFrom((Class) type); + return Collection.class.isAssignableFrom((Class)type); } public int hashCode () { @@ -320,7 +323,7 @@ public Class getDeclaringClass () { return declaringClass; } - public Class getElementType() { + public Class getElementType () { return elementType; } From 75263ceca0659ac51a9eba4f0fbd86bd8a57ef85 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 3 Jun 2016 20:41:42 +0200 Subject: [PATCH 016/114] Better bean property detection. --- src/com/esotericsoftware/yamlbeans/Beans.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 57182b7..0066cfb 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -159,35 +159,36 @@ static public Property getProperty (Class type, String name, boolean beanPropert if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); name = name.replace(" ", ""); - Class[] noArgs = new Class[0], oneArg = new Class[1]; - for (Field field : getAllFields(type)) { - if (!field.getName().equals(name)) continue; - if (beanProperties) { - DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); + if (beanProperties) { + DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); + boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); - String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); - Method getMethod = null, setMethod = null; + String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + Method getMethod = null; + try { + getMethod = type.getMethod("get" + nameUpper); + } catch (Exception ignored) { + } + if (getMethod == null) { try { - oneArg[0] = field.getType(); - setMethod = type.getMethod("set" + nameUpper, oneArg); + getMethod = type.getMethod("is" + nameUpper); } catch (Exception ignored) { } + } + if (getMethod != null) { + Method setMethod = null; try { - getMethod = type.getMethod("get" + nameUpper, noArgs); + setMethod = type.getMethod("set" + nameUpper, getMethod.getReturnType()); } catch (Exception ignored) { } - if (getMethod == null && (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class))) { - try { - getMethod = type.getMethod("is" + nameUpper, noArgs); - } catch (Exception ignored) { - } - } if (getMethod != null && (setMethod != null || constructorProperty)) return new MethodProperty(name, setMethod, getMethod); } + } + for (Field field : getAllFields(type)) { + if (!field.getName().equals(name)) continue; int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; if (!Modifier.isPublic(modifiers) && !privateFields) continue; From ca8a256d58be3089d505901d9618e570aa6cb77a Mon Sep 17 00:00:00 2001 From: DevCharly Date: Fri, 7 Oct 2016 10:59:24 +0200 Subject: [PATCH 017/114] WriteConfig option to keep bean field/properties order --- src/com/esotericsoftware/yamlbeans/Beans.java | 11 ++++++++--- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 7 +++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 0066cfb..7fb00cc 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -111,7 +112,7 @@ static public Object createObject (Class type, boolean privateConstructors) thro static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); Class[] noArgs = new Class[0], oneArg = new Class[1]; - Set properties = new TreeSet(); + Set properties = config.writeConfig.keepBeanPropertyOrder ? new LinkedHashSet() : new TreeSet(); for (Field field : getAllFields(type)) { String name = field.getName(); @@ -202,12 +203,16 @@ static public Property getProperty (Class type, String name, boolean beanPropert } static private ArrayList getAllFields (Class type) { - ArrayList allFields = new ArrayList(); + ArrayList classes = new ArrayList(); Class nextClass = type; while (nextClass != null && nextClass != Object.class) { - Collections.addAll(allFields, nextClass.getDeclaredFields()); + classes.add(nextClass); nextClass = nextClass.getSuperclass(); } + ArrayList allFields = new ArrayList(); + for(int i = classes.size() - 1; i >= 0; i--) { + Collections.addAll(allFields, classes.get(i).getDeclaredFields()); + } return allFields; } diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index c155ce6..876ffa4 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -124,6 +124,7 @@ static public class WriteConfig { boolean writeRootTags = true; boolean writeRootElementTags = true; boolean autoAnchor = true; + boolean keepBeanPropertyOrder = false; WriteClassName writeClassName = WriteClassName.AUTO; EmitterConfig emitterConfig = new EmitterConfig(); @@ -170,6 +171,12 @@ public void setAutoAnchor (boolean autoAnchor) { this.autoAnchor = autoAnchor; } + /** If true, bean fields/properties are written in the same order as the fields are defined in the bean class. + * If false, they are sorted alphabetically. Default is false. */ + public void setKeepBeanPropertyOrder(boolean keepBeanPropertyOrder) { + this.keepBeanPropertyOrder = keepBeanPropertyOrder; + } + /** Sets the YAML version to output. Default is 1.1. */ public void setVersion (Version version) { emitterConfig.setVersion(version); From ad36665b2c2b053477043adb563de2449a4d7dbb Mon Sep 17 00:00:00 2001 From: Sanjeev Kumar Mishra Date: Sat, 5 Nov 2016 15:48:40 +0530 Subject: [PATCH 018/114] provision to choose to raise error if there are duplicate keys in YAML - issue #57 (#60) * fix for #57 * as per review by @NathanSweet on fix for #57 * removing the whitespace in the eof * trying to remove the spaces in files so that git does not complain about * adding gitattributes file to fix the line endings to clrf (windows style) * reverting my changes to see if I can redo them to fix the line ending now.. also added a .gitattributes file to keep the line endings to windows style * Saving files before refreshing line endings * redone my changes * correcting the .gitattributes file * removing .gitattributes * fix line endings to windows * fixing line endings to windows format --- .gitignore | 2 + .../yamlbeans/YamlConfig.java | 8 +++- .../yamlbeans/YamlReader.java | 11 ++++- .../yamlbeans/YamlReaderTest.java | 45 ++++++++++++++++++- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d5b2315..fabc91a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ bin/ target/ +.idea +*.iml diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 876ffa4..4cd8177 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -47,6 +47,7 @@ public class YamlConfig { boolean beanProperties = true; boolean privateFields; boolean privateConstructors = true; + boolean allowDuplicates = true; public YamlConfig () { scalarSerializers.put(Date.class, new DateSerializer()); @@ -58,6 +59,11 @@ public YamlConfig () { tagToClass.put("tag:yaml.org,2002:float", Float.class); } + /** Allows duplicate keys in YAML document. Default is true. */ + public void setAllowDuplicates (boolean allowDuplicates) { + this.allowDuplicates = allowDuplicates; + } + /** Allows the specified tag to be used in YAML instead of the full class name. */ public void setClassTag (String tag, Class type) { if (tag == null) throw new IllegalArgumentException("tag cannot be null."); @@ -264,4 +270,4 @@ static class ConstructorParameters { public static enum WriteClassName { ALWAYS, NEVER, AUTO } -} +} \ No newline at end of file diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 42d7c0f..52aba27 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -308,6 +308,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) throw new YamlReaderException("Error creating object.", ex); } if (anchor != null) anchors.put(anchor, object); + ArrayList keys = new ArrayList(); while (true) { if (parser.peekNextEvent().type == MAPPING_END) { parser.getNextEvent(); @@ -325,10 +326,18 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (object instanceof Map) { // Add to map. if (!isExplicitKey) value = readValue(elementType, null, null); + if (!config.allowDuplicates && ((Map) object).containsKey(key)) { + throw new YamlReaderException("Duplicate key found '" + key + "'"); + } ((Map)object).put(key, value); } else { // Set field on object. try { + if (!config.allowDuplicates && keys.contains(key)) { + throw new YamlReaderException("Duplicate key found '" + key + "'"); + } + keys.add(key); + Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); if (property == null) { if (config.readConfig.ignoreUnknownProperties) continue; @@ -421,4 +430,4 @@ public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); } -} +} \ No newline at end of file diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index dd5a983..207ee6c 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -16,6 +16,7 @@ package com.esotericsoftware.yamlbeans; +import java.io.StringReader; import java.util.List; import java.util.Map; @@ -318,6 +319,48 @@ public void testIgnoreUnknownProperties () { } } + public void testDuplicateKeysAreNotAllowedIfAllowDuplicateIsSetSo () { + String inputWithDuplicates = "a: 1\na: 2\nc: 3"; + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.setAllowDuplicates(false); + YamlReader yamlReader = new YamlReader(inputWithDuplicates, yamlConfig); + try { + yamlReader.read(); + fail("Duplicates should not have been allowed."); + } catch (YamlException e) { + } + + String inputWithoutDuplicates = "a: 1\nb: 2\nc: 3"; + YamlReader yamlReader1 = new YamlReader(inputWithoutDuplicates, yamlConfig); + try { + yamlReader1.read(); + } catch (YamlException e) { + fail("Duplicates should have been allowed."); + } + } + + public void testDuplicateKeysAreAllowedIfAllowDuplicateIsSetSo () { + String inputWithDuplicates = "a: 1\na: 2\nc: 3"; + YamlReader yamlReader = new YamlReader(inputWithDuplicates); + try { + yamlReader.read(); + } catch (YamlException e) { + e.printStackTrace(); + fail("Duplicates should have been allowed."); + } + + String inputWithoutDuplicates = "a: 1\nb: 2\nc: 3"; + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.setAllowDuplicates(false); + YamlReader yamlReader1 = new YamlReader(inputWithoutDuplicates, yamlConfig); + try { + yamlReader1.read(); + } catch (YamlException e) { + e.printStackTrace(); + fail("Duplicates should have been allowed."); + } + } + private static class TypeTagIgnoringReader extends YamlReader { public TypeTagIgnoringReader(String yaml) { @@ -385,4 +428,4 @@ public void testIgnoreTypeTagsEmbedded () throws YamlException { assertEquals(1, lake.fish.size()); assertEquals("Walleye", lake.fish.get(0).species); } -} +} \ No newline at end of file From 36b0e5c73b15761d15f88301c91530bfc748c3df Mon Sep 17 00:00:00 2001 From: Sanjeev Kumar Mishra Date: Sat, 5 Nov 2016 19:54:35 +0530 Subject: [PATCH 019/114] README documentation for the allow duplicates features (#61) * fix for #57 * as per review by @NathanSweet on fix for #57 * removing the whitespace in the eof * trying to remove the spaces in files so that git does not complain about * adding gitattributes file to fix the line endings to clrf (windows style) * reverting my changes to see if I can redo them to fix the line ending now.. also added a .gitattributes file to keep the line endings to windows style * Saving files before refreshing line endings * redone my changes * correcting the .gitattributes file * removing .gitattributes * fix line endings to windows * fixing line endings to windows format * adding documentation for issue #57 * correcting documentation typo * correcting documentation typo * correcting documentation typo * added more descriptive example * added more descriptive example --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index cab3b95..317cba0 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,38 @@ In this map, the "oldest friend" and "best friend" keys reference the same objec map.put("best friend", contact); ``` +## Duplicate key validation + +By default, the behaviour of this YAML parser is to ignore duplicate keys if you have. e.g if you have the following + +```yaml + name: Nathan Sweet + age: 28 + address: + line1: 485 Madison Ave S + line1: 711 3rd Ave S + line2: NYC +``` + +The above YAML will give you an `address` object with attribute `line1` set to `711 3rd Ave S`. This is because the key `line1` in the above YAML is duplicated and thus the last value of `line1` will be retained. YAML parser will not complain about it. However, if your business logic requires you to validate YAML for such duplicates, then you can still do using `allowDuplicates` option of the `YamlConfig` object. Following is how its done: + +```java + try { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.setAllowDuplicates(false); // default value is true + YamlReader reader = new YamlReader(new FileReader("contact.yml"), yamlConfig); + Object object = reader.read(); + System.out.println(object); + Map map = (Map)object; + System.out.println(map.get("address")); + } catch (YamlException ex) { + ex.printStackTrace(); + // or handle duplicate key case here according to your business logic + } +``` + +The above code will not print anything, but throw `YamlReaderException` at line 5 saying, `Duplicate key found 'line1'`. + ## Architecture The YAML tokenizer, parser, and emitter are based on those from the JvYAML project. They have been heavily refactored, bugs fixed, etc. The rest of the JvYAML project was not used because of its complexity. YamlBeans strives for the simplest possible thing that works, with the goal being to make it easy to use the YAML data format with Java. From fdb4ee14637d443a82c08a5b6be294e2fce9fa89 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 5 Nov 2016 15:32:49 +0100 Subject: [PATCH 020/114] Updated version to 1.11. --- pom.xml | 2 +- project.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e97489..24189f5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.10 + 1.11 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans diff --git a/project.yaml b/project.yaml index ee560ea..8b9be3d 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.09 +version: 1.11 From c902cddf67d5666ff282abd7bbe625bbf22d0f4f Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 8 Mar 2017 10:57:48 +0100 Subject: [PATCH 021/114] Empty scalar is null, use double quotes for empty string. --- src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java | 3 ++- src/com/esotericsoftware/yamlbeans/parser/Parser.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java b/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java index f1b3384..8a94f07 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java @@ -48,7 +48,8 @@ private ScalarAnalysis (String scalar, boolean empty, boolean multiline, boolean } static public ScalarAnalysis analyze (String scalar, boolean escapeUnicode) { - if (scalar == null || "".equals(scalar)) return new ScalarAnalysis(scalar, true, false, false, true, true, true, false); + if (scalar == null) return new ScalarAnalysis(scalar, true, false, false, true, true, true, false); + if ("".equals(scalar)) return new ScalarAnalysis(scalar, false, false, false, false, false, true, false); boolean blockIndicators = false; boolean flowIndicators = false; boolean lineBreaks = false; diff --git a/src/com/esotericsoftware/yamlbeans/parser/Parser.java b/src/com/esotericsoftware/yamlbeans/parser/Parser.java index 0ac84f2..5c3e65d 100644 --- a/src/com/esotericsoftware/yamlbeans/parser/Parser.java +++ b/src/com/esotericsoftware/yamlbeans/parser/Parser.java @@ -579,7 +579,7 @@ public Event produce () { }; table[P_EMPTY_SCALAR] = new Production() { public Event produce () { - return new ScalarEvent(null, null, new boolean[] {true, false}, "", (char)0); + return new ScalarEvent(null, null, new boolean[] {true, false}, null, (char)0); } }; } From ecb417e1158b2788d4dcc09e7026bbaf339c9d59 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sun, 12 Mar 2017 21:25:18 +0100 Subject: [PATCH 022/114] Remove invalid characters from field names in YAML data. --- src/com/esotericsoftware/yamlbeans/Beans.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 7fb00cc..d7d1b06 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -155,11 +155,20 @@ static public Set getProperties (Class type, boolean beanProperties, b return properties; } + static private String toJavaIdentifier (String name) { + StringBuilder buffer = new StringBuilder(); + for (int i = 0, n = name.length(); i < n; i++) { + char c = name.charAt(i); + if (Character.isJavaIdentifierPart(c)) buffer.append(c); + } + return buffer.toString(); + } + static public Property getProperty (Class type, String name, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); - name = name.replace(" ", ""); + name = toJavaIdentifier(name); if (beanProperties) { DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); @@ -210,7 +219,7 @@ static private ArrayList getAllFields (Class type) { nextClass = nextClass.getSuperclass(); } ArrayList allFields = new ArrayList(); - for(int i = classes.size() - 1; i >= 0; i--) { + for (int i = classes.size() - 1; i >= 0; i--) { Collections.addAll(allFields, classes.get(i).getDeclaredFields()); } return allFields; From 75f8e44e175645c6a9d5b192e3e54fa7f8dc395f Mon Sep 17 00:00:00 2001 From: gihwan <0x1306e6d@gmail.com> Date: Mon, 13 Mar 2017 22:28:31 +0900 Subject: [PATCH 023/114] Update outdated maven plugins (#64) --- pom.xml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 24189f5..5f6c35b 100644 --- a/pom.xml +++ b/pom.xml @@ -65,37 +65,44 @@ + org.apache.maven.plugins maven-resources-plugin - 2.6 + 3.0.2 + org.apache.maven.plugins maven-surefire-plugin - 2.12.4 + 2.19.1 + org.apache.maven.plugins maven-compiler-plugin - 2.5.1 + 3.6.1 + org.apache.maven.plugins maven-source-plugin - 2.2.1 + 3.0.1 + org.apache.maven.plugins maven-javadoc-plugin - 2.9 + 2.10.4 + org.apache.maven.plugins maven-gpg-plugin - 1.4 + 1.6 + org.apache.maven.plugins maven-deploy-plugin - 2.7 + 2.8.2 org.nuiton helper-maven-plugin - 2.0 + 2.3.2 From c6df1de7cb9c4809f981a2706dfbd3158e24a41a Mon Sep 17 00:00:00 2001 From: gihwan <0x1306e6d@gmail.com> Date: Thu, 4 May 2017 21:54:40 -0700 Subject: [PATCH 024/114] Add test (#71) * Add Beans test. Add BeansTest. Test Coverage : 91.7% * Add Tokenizer test. Add TokenizerTest. Add test1.yml file. Test Coverage : 97.4% --- .../esotericsoftware/yamlbeans/BeansTest.java | 729 ++++++++++++++++++ .../yamlbeans/tokenizer/TokenizerTest.java | 198 +++++ test/test1.yml | 1 + 3 files changed, 928 insertions(+) create mode 100644 test/com/esotericsoftware/yamlbeans/BeansTest.java create mode 100644 test/com/esotericsoftware/yamlbeans/tokenizer/TokenizerTest.java create mode 100644 test/test1.yml diff --git a/test/com/esotericsoftware/yamlbeans/BeansTest.java b/test/com/esotericsoftware/yamlbeans/BeansTest.java new file mode 100644 index 0000000..e90d009 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/BeansTest.java @@ -0,0 +1,729 @@ +package com.esotericsoftware.yamlbeans; + +import org.junit.Before; +import org.junit.Test; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +import static org.junit.Assert.*; + +public class BeansTest { + + private static final double DELTA = 0.0001; + + private YamlConfig yamlConfig; + + @Before + public void setup() throws Exception { + yamlConfig = new YamlConfig(); + } + + @Test + public void isScalar() throws Exception { + // Scalar Type + assertTrue(Beans.isScalar(String.class)); + assertTrue(Beans.isScalar(Integer.class)); + assertTrue(Beans.isScalar(Boolean.class)); + assertTrue(Beans.isScalar(Float.class)); + assertTrue(Beans.isScalar(Long.class)); + assertTrue(Beans.isScalar(Double.class)); + assertTrue(Beans.isScalar(Short.class)); + assertTrue(Beans.isScalar(Byte.class)); + assertTrue(Beans.isScalar(Character.class)); + // Other Type + assertFalse(Beans.isScalar(Void.class)); + assertFalse(Beans.isScalar(Number.class)); + assertFalse(Beans.isScalar(Beans.class)); + assertFalse(Beans.isScalar(YamlReader.class)); + assertFalse(Beans.isScalar(YamlWriter.class)); + } + + @Test + public void getDeferredConstruction() throws Exception { + DeferredConstruction construction = Beans.getDeferredConstruction(MockClass.class, yamlConfig); + assertEquals(null, construction); + } + + @Test + public void getDeferredConstructionWithPrivateConstructor() throws Exception { + DeferredConstruction construction = Beans.getDeferredConstruction(MockClassWithPrivateConstructor.class, yamlConfig); + assertEquals(null, construction); + } + + @Test + public void createObject() throws Exception { + MockClass mockClass = (MockClass) Beans.createObject(MockClass.class, false); + MockClassWithPrivateConstructor mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor) Beans.createObject(MockClassWithPrivateConstructor.class, true); + List listCase = (List) Beans.createObject(List.class, false); + Set setCase = (Set) Beans.createObject(Set.class, false); + Map mapCase = (Map) Beans.createObject(Map.class, false); + + assertNotNull(mockClass); + assertNotNull(mockClassWithPrivateConstructor); + assertNotNull(listCase); + assertNotNull(setCase); + assertNotNull(mapCase); + + if (!(listCase instanceof ArrayList)) { + fail(); + } + if (!(setCase instanceof HashSet)) { + fail(); + } + if (!(mapCase instanceof HashMap)) { + fail(); + } + + try { + MockClassWithoutNoArgConstructor mockClassWithoutNoArgConstructor = (MockClassWithoutNoArgConstructor) Beans.createObject(MockClassWithoutNoArgConstructor.class, false); + fail(); + } catch (InvocationTargetException e) { + + } + try { + mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor) Beans.createObject(MockClassWithPrivateConstructor.class, false); + fail(); + } catch (InvocationTargetException e) { + + } + } + + @Test + public void getPropertiesWithNullType() throws Exception { + try { + Beans.getProperties(null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertiesWithBeanProperties() throws Exception { + final Set properties = Beans.getProperties(MockClass.class, true, false, yamlConfig); + assertEquals(9, properties.size()); + } + + @Test + public void getPropertiesWithPrivateFields() throws Exception { + final Set properties = Beans.getProperties(MockClass.class, false, true, yamlConfig); + assertEquals(9, properties.size()); + } + + @Test + public void getPropertyWithNullType() throws Exception { + try { + Beans.getProperty(null, null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertyWithNullName() throws Exception { + try { + Beans.getProperty(MockClass.class, null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertyWithEmptyName() throws Exception { + try { + Beans.getProperty(MockClass.class, "", false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + + } + + @Test + public void getPropertyBooleanTypeWithBeanProperties() throws Exception { + final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", true, false, yamlConfig); + + assertTrue(booleanTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); + assertEquals("booleanType", booleanTypeProperty.getName()); + assertEquals(boolean.class, booleanTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(false, mockClass.booleanType); + + booleanTypeProperty.set(mockClass, true); + assertEquals(true, booleanTypeProperty.get(mockClass)); + + booleanTypeProperty.set(mockClass, false); + assertEquals(false, booleanTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyCharTypeWithBeanProperties() throws Exception { + final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", true, false, yamlConfig); + + assertTrue(charTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); + assertEquals("charType", charTypeProperty.getName()); + assertEquals(char.class, charTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.charType); + + charTypeProperty.set(mockClass, 'a'); + assertEquals('a', charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MAX_VALUE); + assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MIN_VALUE); + assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyByteTypeWithBeanProperties() throws Exception { + final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", true, false, yamlConfig); + + assertTrue(byteTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); + assertEquals("byteType", byteTypeProperty.getName()); + assertEquals(byte.class, byteTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.byteType); + + byteTypeProperty.set(mockClass, (byte) 1); + assertEquals((byte) 1, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MAX_VALUE); + assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MIN_VALUE); + assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyShortTypeWithBeanProperties() throws Exception { + final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", true, false, yamlConfig); + + assertTrue(shortTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); + assertEquals("shortType", shortTypeProperty.getName()); + assertEquals(short.class, shortTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.shortType); + + shortTypeProperty.set(mockClass, (short) 1); + assertEquals((short) 1, shortTypeProperty.get(mockClass)); + + shortTypeProperty.set(mockClass, Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); + + shortTypeProperty.set(mockClass, Short.MIN_VALUE); + assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyIntTypeWithBeanProperties() throws Exception { + final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", true, false, yamlConfig); + + assertTrue(intTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); + assertEquals("intType", intTypeProperty.getName()); + assertEquals(int.class, intTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.intType); + + intTypeProperty.set(mockClass, 1); + assertEquals(1, intTypeProperty.get(mockClass)); + + intTypeProperty.set(mockClass, Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); + + intTypeProperty.set(mockClass, Integer.MIN_VALUE); + assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyLongTypeWithBeanProperties() throws Exception { + final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", true, false, yamlConfig); + + assertTrue(longTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); + assertEquals("longType", longTypeProperty.getName()); + assertEquals(long.class, longTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.longType); + + longTypeProperty.set(mockClass, 1l); + assertEquals(1l, longTypeProperty.get(mockClass)); + + longTypeProperty.set(mockClass, Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); + + longTypeProperty.set(mockClass, Long.MIN_VALUE); + assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyFloatTypeWithBeanProperties() throws Exception { + final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", true, false, yamlConfig); + + assertTrue(floatTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); + assertEquals("floatType", floatTypeProperty.getName()); + assertEquals(float.class, floatTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals((float) 0, mockClass.floatType, DELTA); + + floatTypeProperty.set(mockClass, (float) 1.0); + assertEquals((float) 1.0, floatTypeProperty.get(mockClass)); + + floatTypeProperty.set(mockClass, Float.MAX_VALUE); + assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); + + floatTypeProperty.set(mockClass, Float.MIN_VALUE); + assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyDoubleTypeWithBeanProperties() throws Exception { + final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", true, false, yamlConfig); + + assertTrue(doubleTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); + assertEquals("doubleType", doubleTypeProperty.getName()); + assertEquals(double.class, doubleTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals((double) 0, mockClass.doubleType, DELTA); + + doubleTypeProperty.set(mockClass, (double) 1.0); + assertEquals((double) 1.0, doubleTypeProperty.get(mockClass)); + + doubleTypeProperty.set(mockClass, Double.MAX_VALUE); + assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); + + doubleTypeProperty.set(mockClass, Double.MIN_VALUE); + assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyStringTypeWithBeanProperties() throws Exception { + final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", true, false, yamlConfig); + + assertTrue(stringTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); + assertEquals("stringType", stringTypeProperty.getName()); + assertEquals(String.class, stringTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(null, mockClass.stringType); + + stringTypeProperty.set(mockClass, "4cho"); + assertEquals("4cho", stringTypeProperty.get(mockClass)); + + stringTypeProperty.set(mockClass, "ghkim"); + assertEquals("ghkim", stringTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyNullWithBeanProperties() throws Exception { + final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", true, false, yamlConfig); + + assertNull(nullProperty); + } + + @Test + public void getPropertyBooleanTypeWithPrivateFields() throws Exception { + final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", false, true, null); + + assertTrue(booleanTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); + assertEquals("booleanType", booleanTypeProperty.getName()); + assertEquals(boolean.class, booleanTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(false, mockClass.booleanType); + + booleanTypeProperty.set(mockClass, true); + assertEquals(true, booleanTypeProperty.get(mockClass)); + + booleanTypeProperty.set(mockClass, false); + assertEquals(false, booleanTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyCharTypeWithPrivateFields() throws Exception { + final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", false, true, yamlConfig); + + assertTrue(charTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); + assertEquals("charType", charTypeProperty.getName()); + assertEquals(char.class, charTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.charType); + + charTypeProperty.set(mockClass, 'a'); + assertEquals('a', charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MAX_VALUE); + assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MIN_VALUE); + assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyByteTypeWithPrivateFields() throws Exception { + final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", false, true, yamlConfig); + + assertTrue(byteTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); + assertEquals("byteType", byteTypeProperty.getName()); + assertEquals(byte.class, byteTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.byteType); + + byteTypeProperty.set(mockClass, (byte) 1); + assertEquals((byte) 1, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MAX_VALUE); + assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MIN_VALUE); + assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyShortTypeWithPrivateFields() throws Exception { + final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", false, true, yamlConfig); + + assertTrue(shortTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); + assertEquals("shortType", shortTypeProperty.getName()); + assertEquals(short.class, shortTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.shortType); + + shortTypeProperty.set(mockClass, (short) 1); + assertEquals((short) 1, shortTypeProperty.get(mockClass)); + + shortTypeProperty.set(mockClass, Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); + + shortTypeProperty.set(mockClass, Short.MIN_VALUE); + assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyIntTypeWithPrivateFields() throws Exception { + final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", false, true, yamlConfig); + + assertTrue(intTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); + assertEquals("intType", intTypeProperty.getName()); + assertEquals(int.class, intTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.intType); + + intTypeProperty.set(mockClass, 1); + assertEquals(1, intTypeProperty.get(mockClass)); + + intTypeProperty.set(mockClass, Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); + + intTypeProperty.set(mockClass, Integer.MIN_VALUE); + assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyLongTypeWithPrivateFields() throws Exception { + final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", false, true, yamlConfig); + + assertTrue(longTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); + assertEquals("longType", longTypeProperty.getName()); + assertEquals(long.class, longTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.longType); + + longTypeProperty.set(mockClass, 1l); + assertEquals(1l, longTypeProperty.get(mockClass)); + + longTypeProperty.set(mockClass, Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); + + longTypeProperty.set(mockClass, Long.MIN_VALUE); + assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyFloatTypeWithPrivateFields() throws Exception { + final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", false, true, yamlConfig); + + assertTrue(floatTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); + assertEquals("floatType", floatTypeProperty.getName()); + assertEquals(float.class, floatTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals((float) 0, mockClass.floatType, DELTA); + + floatTypeProperty.set(mockClass, (float) 1.0); + assertEquals((float) 1.0, floatTypeProperty.get(mockClass)); + + floatTypeProperty.set(mockClass, Float.MAX_VALUE); + assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); + + floatTypeProperty.set(mockClass, Float.MIN_VALUE); + assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyDoubleTypeWithPrivateFields() throws Exception { + final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", false, true, yamlConfig); + + assertTrue(doubleTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); + assertEquals("doubleType", doubleTypeProperty.getName()); + assertEquals(double.class, doubleTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals((double) 0, mockClass.doubleType, DELTA); + + doubleTypeProperty.set(mockClass, (double) 1.0); + assertEquals((double) 1.0, doubleTypeProperty.get(mockClass)); + + doubleTypeProperty.set(mockClass, Double.MAX_VALUE); + assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); + + doubleTypeProperty.set(mockClass, Double.MIN_VALUE); + assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyStringTypeWithPrivateFields() throws Exception { + final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", false, true, yamlConfig); + + assertTrue(stringTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); + assertEquals("stringType", stringTypeProperty.getName()); + assertEquals(String.class, stringTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(null, mockClass.stringType); + + stringTypeProperty.set(mockClass, "4cho"); + assertEquals("4cho", stringTypeProperty.get(mockClass)); + + stringTypeProperty.set(mockClass, "ghkim"); + assertEquals("ghkim", stringTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyNullWithPrivateFields() throws Exception { + final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", false, true, yamlConfig); + + assertNull(nullProperty); + } + + private static class MockClass { + + private boolean booleanType; + private char charType; + private byte byteType; + private short shortType; + private int intType; + private long longType; + private float floatType; + private double doubleType; + private String stringType; + + public MockClass() { + + } + + public boolean isBooleanType() { + return booleanType; + } + + public void setBooleanType(boolean booleanType) { + this.booleanType = booleanType; + } + + public char getCharType() { + return charType; + } + + public void setCharType(char charType) { + this.charType = charType; + } + + public byte getByteType() { + return byteType; + } + + public void setByteType(byte byteType) { + this.byteType = byteType; + } + + public short getShortType() { + return shortType; + } + + public void setShortType(short shortType) { + this.shortType = shortType; + } + + public int getIntType() { + return intType; + } + + public void setIntType(int intType) { + this.intType = intType; + } + + public long getLongType() { + return longType; + } + + public void setLongType(long longType) { + this.longType = longType; + } + + public float getFloatType() { + return floatType; + } + + public void setFloatType(float floatType) { + this.floatType = floatType; + } + + public double getDoubleType() { + return doubleType; + } + + public void setDoubleType(double doubleType) { + this.doubleType = doubleType; + } + + public String getStringType() { + return stringType; + } + + public void setStringType(String stringType) { + this.stringType = stringType; + } + } + + private static class MockClassWithPrivateConstructor { + + private boolean booleanType; + private char charType; + private byte byteType; + private short shortType; + private int intType; + private long longType; + private float floatType; + private double doubleType; + private String stringType; + + private MockClassWithPrivateConstructor() { + + } + + public boolean isBooleanType() { + return booleanType; + } + + public void setBooleanType(boolean booleanType) { + this.booleanType = booleanType; + } + + public char getCharType() { + return charType; + } + + public void setCharType(char charType) { + this.charType = charType; + } + + public byte getByteType() { + return byteType; + } + + public void setByteType(byte byteType) { + this.byteType = byteType; + } + + public short getShortType() { + return shortType; + } + + public void setShortType(short shortType) { + this.shortType = shortType; + } + + public int getIntType() { + return intType; + } + + public void setIntType(int intType) { + this.intType = intType; + } + + public long getLongType() { + return longType; + } + + public void setLongType(long longType) { + this.longType = longType; + } + + public float getFloatType() { + return floatType; + } + + public void setFloatType(float floatType) { + this.floatType = floatType; + } + + public double getDoubleType() { + return doubleType; + } + + public void setDoubleType(double doubleType) { + this.doubleType = doubleType; + } + + public String getStringType() { + return stringType; + } + + public void setStringType(String stringType) { + this.stringType = stringType; + } + } + + private static class MockClassWithoutNoArgConstructor { + + private Object anyType; + + public MockClassWithoutNoArgConstructor(Object anyType) { + this.anyType = anyType; + } + } +} \ No newline at end of file diff --git a/test/com/esotericsoftware/yamlbeans/tokenizer/TokenizerTest.java b/test/com/esotericsoftware/yamlbeans/tokenizer/TokenizerTest.java new file mode 100644 index 0000000..6a328bf --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/tokenizer/TokenizerTest.java @@ -0,0 +1,198 @@ +package com.esotericsoftware.yamlbeans.tokenizer; + +import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Iterator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class TokenizerTest { + + private Token STREAM_START = new Token(TokenType.STREAM_START); + private Token STREAM_END = new Token(TokenType.STREAM_END); + private Token VALUE = new Token(TokenType.VALUE); + private Token KEY = new Token(TokenType.KEY); + private Token BLOCK_MAPPING_START = new Token(TokenType.BLOCK_MAPPING_START); + private Token BLOCK_END = new Token(TokenType.BLOCK_END); + + /** + * Purpose: Getting the next token correctly + * Input: getNextToken() gets the next token and moves to the next token + * Expected: + * return Token.STREAM_START + * return Token.BLOCK_MAPPING_START + * return Token.KEY + * ... + * return Token.STREAM.END + * return null + */ + @Test + public void testGetNextToken() throws FileNotFoundException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + assertEquals(tokenizer.getNextToken() + "", STREAM_START + ""); + assertEquals(tokenizer.getNextToken() + "", BLOCK_MAPPING_START + ""); + assertEquals(tokenizer.getNextToken() + "", KEY + ""); + assertEquals(tokenizer.getNextToken() + "", ""); + assertEquals(tokenizer.getNextToken() + "", VALUE + ""); + assertEquals(tokenizer.getNextToken() + "", ""); + assertEquals(tokenizer.getNextToken() + "", BLOCK_END + ""); + assertEquals(tokenizer.getNextToken() + "", STREAM_END + ""); + assertNull(tokenizer.getNextToken()); + } + + /** + * Purpose: Ensuring that the constructor Tokenizer(Reader) is working properly + * Input: Tokenizer(Reader) FileReader("test/test1.yml") -> Tokenizer(FileReader("test/test1.yml")) + * Expected: + * peekNextToken() = Token.STREAM_START + * Tokenizer(BufferedReader(FileReader("test/test1.yml"))) = Tokenizer(FileReader("test/test1.yml")) + * Tokenizer(null) throws IllegalArgumentException.class + */ + @Test(expected = IllegalArgumentException.class) + public void testTokenizerReader() throws FileNotFoundException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + assertEquals(tokenizer.peekNextToken() + "", STREAM_START + ""); + Tokenizer tokenizer_nbuffered = new Tokenizer((new BufferedReader(new FileReader("test/test1.yml")))); + + Iterator tokenizer_iter = tokenizer.iterator(); + Iterator tokenizer_nbuffered_iter = tokenizer_nbuffered.iterator(); + + while (tokenizer_iter.hasNext() || tokenizer_nbuffered_iter.hasNext()) { + assertEquals(tokenizer_iter.next() + "", tokenizer_nbuffered_iter.next() + ""); + } + + Tokenizer tokenizer_null = new Tokenizer((FileReader) null); + } + + /** + * Purpose: Ensuring that the constructor Tokenizer(String) is working properly + * Input: Tokenizer(String) FileReader("12: 13") = Tokenizer(FileReader("test/test1.yml")), The content of "test/test1.yml" is "12: 13" + * Expected: + * Tokenizer(FileReader("12: 13")) = Tokenizer(FileReader("test/test1.yml")), The content of "test/test1.yml" is "12: 13" + */ + @Test + public void testTokenizerString() throws FileNotFoundException { + Iterator tokenizer_iter = new Tokenizer(new FileReader("test/test1.yml")).iterator(); + Iterator tokenizer_string_iter = new Tokenizer("12: 13").iterator(); + + while (tokenizer_iter.hasNext() || tokenizer_string_iter.hasNext()) { + assertEquals(tokenizer_iter.next() + "", tokenizer_string_iter.next() + ""); + } + } + + /** + * Purpose: peeking the next token correctly + * Input: peekNextToken() peeks the next token and does not move to the next token + * Expected: + * return Token.STREAM_START + * (moves by another function) + * return Token.BLOCK_MAPPING_START + * (moves by another function) + * return Token.KEY + * (moves by another function) + * ... + * return Token.STREAM.END + * (moves by another function) + * return null + */ + @Test + public void testPeekNextToken() throws FileNotFoundException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + assertEquals(tokenizer.peekNextToken() + "", STREAM_START + ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", BLOCK_MAPPING_START + ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", KEY + ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", VALUE + ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", BLOCK_END + ""); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextToken() + "", STREAM_END + ""); + tokenizer.getNextToken(); + assertNull(tokenizer.peekNextToken()); + } + + /** + * Purpose: peeking the type of the next token correctly + * Input: peekNextToken() peeks the type of the next token and does not move to the next token + * Expected: + * return TokenType.STREAM_START + * (moves by another function) + * return TokenType.BLOCK_MAPPING_START + * (moves by another function) + * return TokenType.KEY + * (moves by another function) + * ... + * return TokenType.STREAM.END + * (moves by another function) + * return null + */ + @Test + public void testPeekNextTokenType() throws FileNotFoundException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + assertEquals(tokenizer.peekNextTokenType(), TokenType.STREAM_START); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.BLOCK_MAPPING_START); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.KEY); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.SCALAR); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.VALUE); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.SCALAR); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.BLOCK_END); + tokenizer.getNextToken(); + assertEquals(tokenizer.peekNextTokenType(), TokenType.STREAM_END); + tokenizer.getNextToken(); + assertNull(tokenizer.peekNextTokenType()); + } + + + /** + * Purpose: Ensuring that the iterator's functions are working properly + * Input: iterator().hasNext() returns the same value as (Tokenizer.peekNextToken != null) + * iterator().next() returns the same value as Tokenizer.getNextToken() + * Expected: + * Tokenizer.iterator().hasNext() = (Tokenizer.peekNextToken != null) + * Tokenizer.iterator().next() = Tokenizer.getNextToken() + * Tokenizer.iterator().remove() throws UnsupportedOperationException.class + */ + @Test(expected = UnsupportedOperationException.class) + public void testIterator() throws FileNotFoundException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + Iterator iter = new Tokenizer(new FileReader("test/test1.yml")).iterator(); + assertEquals(iter.hasNext(), tokenizer.peekNextToken() != null); + assertEquals(iter.next(), tokenizer.getNextToken()); + iter.remove(); + } + + /** + * Purpose: Getting the next token in a closed reader + * Input: close() Tokenizer.reader -> Tokenizer.reader.close() + * Expected: + * Tokenizer.getNextToken() = Token.STREAM_START + * Tokenizer.getNextToken() throws TokenizerException.class + */ + @Test(expected = TokenizerException.class) + public void testClose() throws IOException { + Tokenizer tokenizer = new Tokenizer(new FileReader("test/test1.yml")); + tokenizer.close(); + + assertEquals(tokenizer.getNextToken() + "", STREAM_START + ""); + tokenizer.getNextToken(); + } + +} \ No newline at end of file diff --git a/test/test1.yml b/test/test1.yml new file mode 100644 index 0000000..8068c9c --- /dev/null +++ b/test/test1.yml @@ -0,0 +1 @@ +12: 13 \ No newline at end of file From 4a6759c71f9b61ab2781b55c815ccef2ca969b6a Mon Sep 17 00:00:00 2001 From: cal Date: Sun, 7 May 2017 08:19:05 +0200 Subject: [PATCH 025/114] Avoid class cast exception when analyzing generic type. (#72) --- src/com/esotericsoftware/yamlbeans/Beans.java | 14 +++++++++++++- .../esotericsoftware/yamlbeans/GenericTest.java | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index d7d1b06..0d195e7 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -24,6 +24,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -289,7 +290,18 @@ private Class getElementTypeFromGenerics (Type type) { if (isCollection(rawType) || isMap(rawType)) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); if (actualTypeArguments.length > 0) { - return (Class)actualTypeArguments[actualTypeArguments.length - 1]; + final Type cType = actualTypeArguments[actualTypeArguments.length - 1]; + if (cType instanceof Class) { + return (Class) cType; + } else if (cType instanceof WildcardType) { + WildcardType t = (WildcardType) cType; + final Type bound = t.getUpperBounds()[0]; + return bound instanceof Class ? (Class) bound : null; + } else if (cType instanceof ParameterizedType) { + ParameterizedType t = (ParameterizedType) cType; + final Type rt = t.getRawType(); + return rt instanceof Class ? (Class) rt : null; + } } } } diff --git a/test/com/esotericsoftware/yamlbeans/GenericTest.java b/test/com/esotericsoftware/yamlbeans/GenericTest.java index 0b5d341..15a2a99 100644 --- a/test/com/esotericsoftware/yamlbeans/GenericTest.java +++ b/test/com/esotericsoftware/yamlbeans/GenericTest.java @@ -127,6 +127,8 @@ static class Test { public Map structMap; public List integerList; public List structList; + public Map> multiMap; + public Map wildMap; @Override public boolean equals(Object o) { @@ -151,6 +153,12 @@ public boolean equals(Object o) { if (structMap != null ? !structMap.equals(test.structMap) : test.structMap != null) { return false; } + if (multiMap != null ? !multiMap.equals(test.multiMap) : test.multiMap != null) { + return false; + } + if (wildMap != null ? !wildMap.equals(test.wildMap) : test.wildMap != null) { + return false; + } return true; } @@ -161,6 +169,8 @@ public int hashCode() { result = 31 * result + (structMap != null ? structMap.hashCode() : 0); result = 31 * result + (integerList != null ? integerList.hashCode() : 0); result = 31 * result + (structList != null ? structList.hashCode() : 0); + result = 31 * result + (multiMap != null ? multiMap.hashCode() : 0); + result = 31 * result + (wildMap != null ? wildMap.hashCode() : 0); return result; } } From 3b01522e15deb1dc00330cf962c796c79944d2d5 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 30 May 2017 14:17:47 +0200 Subject: [PATCH 026/114] Uses system line separator. closes #73 --- src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java b/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java index 7cdebc0..fd018c2 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java @@ -360,7 +360,7 @@ public void writePlain (String text, boolean split, int indent, int wrapColumn) } public void writeLineBreak (String data) throws IOException { - if (data == null) data = "\n"; + if (data == null) data = System.getProperty("line.separator"); whitespace = true; indentation = true; column = 0; From e8dbdb9376a174c8a5f9d823097fd5f833acd214 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 30 May 2017 16:07:27 +0200 Subject: [PATCH 027/114] Added @SuppressWarnings("synthetic-access") --- .../esotericsoftware/yamlbeans/BeansTest.java | 1165 +++++++++-------- 1 file changed, 585 insertions(+), 580 deletions(-) diff --git a/test/com/esotericsoftware/yamlbeans/BeansTest.java b/test/com/esotericsoftware/yamlbeans/BeansTest.java index e90d009..6f9cde0 100644 --- a/test/com/esotericsoftware/yamlbeans/BeansTest.java +++ b/test/com/esotericsoftware/yamlbeans/BeansTest.java @@ -1,3 +1,4 @@ + package com.esotericsoftware.yamlbeans; import org.junit.Before; @@ -8,722 +9,726 @@ import static org.junit.Assert.*; +@SuppressWarnings("synthetic-access") public class BeansTest { - private static final double DELTA = 0.0001; - - private YamlConfig yamlConfig; - - @Before - public void setup() throws Exception { - yamlConfig = new YamlConfig(); - } - - @Test - public void isScalar() throws Exception { - // Scalar Type - assertTrue(Beans.isScalar(String.class)); - assertTrue(Beans.isScalar(Integer.class)); - assertTrue(Beans.isScalar(Boolean.class)); - assertTrue(Beans.isScalar(Float.class)); - assertTrue(Beans.isScalar(Long.class)); - assertTrue(Beans.isScalar(Double.class)); - assertTrue(Beans.isScalar(Short.class)); - assertTrue(Beans.isScalar(Byte.class)); - assertTrue(Beans.isScalar(Character.class)); - // Other Type - assertFalse(Beans.isScalar(Void.class)); - assertFalse(Beans.isScalar(Number.class)); - assertFalse(Beans.isScalar(Beans.class)); - assertFalse(Beans.isScalar(YamlReader.class)); - assertFalse(Beans.isScalar(YamlWriter.class)); - } - - @Test - public void getDeferredConstruction() throws Exception { - DeferredConstruction construction = Beans.getDeferredConstruction(MockClass.class, yamlConfig); - assertEquals(null, construction); - } - - @Test - public void getDeferredConstructionWithPrivateConstructor() throws Exception { - DeferredConstruction construction = Beans.getDeferredConstruction(MockClassWithPrivateConstructor.class, yamlConfig); - assertEquals(null, construction); - } - - @Test - public void createObject() throws Exception { - MockClass mockClass = (MockClass) Beans.createObject(MockClass.class, false); - MockClassWithPrivateConstructor mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor) Beans.createObject(MockClassWithPrivateConstructor.class, true); - List listCase = (List) Beans.createObject(List.class, false); - Set setCase = (Set) Beans.createObject(Set.class, false); - Map mapCase = (Map) Beans.createObject(Map.class, false); - - assertNotNull(mockClass); - assertNotNull(mockClassWithPrivateConstructor); - assertNotNull(listCase); - assertNotNull(setCase); - assertNotNull(mapCase); - - if (!(listCase instanceof ArrayList)) { - fail(); - } - if (!(setCase instanceof HashSet)) { - fail(); - } - if (!(mapCase instanceof HashMap)) { - fail(); - } - - try { - MockClassWithoutNoArgConstructor mockClassWithoutNoArgConstructor = (MockClassWithoutNoArgConstructor) Beans.createObject(MockClassWithoutNoArgConstructor.class, false); - fail(); - } catch (InvocationTargetException e) { - - } - try { - mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor) Beans.createObject(MockClassWithPrivateConstructor.class, false); - fail(); - } catch (InvocationTargetException e) { - - } - } - - @Test - public void getPropertiesWithNullType() throws Exception { - try { - Beans.getProperties(null, false, false, yamlConfig); - fail(); - } catch (IllegalArgumentException e) { - // nothing - } - } - - @Test - public void getPropertiesWithBeanProperties() throws Exception { - final Set properties = Beans.getProperties(MockClass.class, true, false, yamlConfig); - assertEquals(9, properties.size()); - } - - @Test - public void getPropertiesWithPrivateFields() throws Exception { - final Set properties = Beans.getProperties(MockClass.class, false, true, yamlConfig); - assertEquals(9, properties.size()); - } - - @Test - public void getPropertyWithNullType() throws Exception { - try { - Beans.getProperty(null, null, false, false, yamlConfig); - fail(); - } catch (IllegalArgumentException e) { - // nothing - } - } - - @Test - public void getPropertyWithNullName() throws Exception { - try { - Beans.getProperty(MockClass.class, null, false, false, yamlConfig); - fail(); - } catch (IllegalArgumentException e) { - // nothing - } - } - - @Test - public void getPropertyWithEmptyName() throws Exception { - try { - Beans.getProperty(MockClass.class, "", false, false, yamlConfig); - fail(); - } catch (IllegalArgumentException e) { - // nothing - } - - } - - @Test - public void getPropertyBooleanTypeWithBeanProperties() throws Exception { - final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", true, false, yamlConfig); - - assertTrue(booleanTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); - assertEquals("booleanType", booleanTypeProperty.getName()); - assertEquals(boolean.class, booleanTypeProperty.getType()); - - MockClass mockClass = new MockClass(); - assertEquals(false, mockClass.booleanType); - - booleanTypeProperty.set(mockClass, true); - assertEquals(true, booleanTypeProperty.get(mockClass)); - - booleanTypeProperty.set(mockClass, false); - assertEquals(false, booleanTypeProperty.get(mockClass)); - } - - @Test - public void getPropertyCharTypeWithBeanProperties() throws Exception { - final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", true, false, yamlConfig); - - assertTrue(charTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); - assertEquals("charType", charTypeProperty.getName()); - assertEquals(char.class, charTypeProperty.getType()); - - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.charType); - - charTypeProperty.set(mockClass, 'a'); - assertEquals('a', charTypeProperty.get(mockClass)); - - charTypeProperty.set(mockClass, Character.MAX_VALUE); - assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); - - charTypeProperty.set(mockClass, Character.MIN_VALUE); - assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); - } - - @Test - public void getPropertyByteTypeWithBeanProperties() throws Exception { - final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", true, false, yamlConfig); - - assertTrue(byteTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); - assertEquals("byteType", byteTypeProperty.getName()); - assertEquals(byte.class, byteTypeProperty.getType()); - - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.byteType); - - byteTypeProperty.set(mockClass, (byte) 1); - assertEquals((byte) 1, byteTypeProperty.get(mockClass)); - - byteTypeProperty.set(mockClass, Byte.MAX_VALUE); - assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); - - byteTypeProperty.set(mockClass, Byte.MIN_VALUE); - assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); - } + private static final double DELTA = 0.0001; + + private YamlConfig yamlConfig; + + @Before + public void setup () throws Exception { + yamlConfig = new YamlConfig(); + } + + @Test + public void isScalar () throws Exception { + // Scalar Type + assertTrue(Beans.isScalar(String.class)); + assertTrue(Beans.isScalar(Integer.class)); + assertTrue(Beans.isScalar(Boolean.class)); + assertTrue(Beans.isScalar(Float.class)); + assertTrue(Beans.isScalar(Long.class)); + assertTrue(Beans.isScalar(Double.class)); + assertTrue(Beans.isScalar(Short.class)); + assertTrue(Beans.isScalar(Byte.class)); + assertTrue(Beans.isScalar(Character.class)); + // Other Type + assertFalse(Beans.isScalar(Void.class)); + assertFalse(Beans.isScalar(Number.class)); + assertFalse(Beans.isScalar(Beans.class)); + assertFalse(Beans.isScalar(YamlReader.class)); + assertFalse(Beans.isScalar(YamlWriter.class)); + } + + @Test + public void getDeferredConstruction () throws Exception { + DeferredConstruction construction = Beans.getDeferredConstruction(MockClass.class, yamlConfig); + assertEquals(null, construction); + } + + @Test + public void getDeferredConstructionWithPrivateConstructor () throws Exception { + DeferredConstruction construction = Beans.getDeferredConstruction(MockClassWithPrivateConstructor.class, yamlConfig); + assertEquals(null, construction); + } + + @Test + public void createObject () throws Exception { + MockClass mockClass = (MockClass)Beans.createObject(MockClass.class, false); + MockClassWithPrivateConstructor mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor)Beans + .createObject(MockClassWithPrivateConstructor.class, true); + List listCase = (List)Beans.createObject(List.class, false); + Set setCase = (Set)Beans.createObject(Set.class, false); + Map mapCase = (Map)Beans.createObject(Map.class, false); + + assertNotNull(mockClass); + assertNotNull(mockClassWithPrivateConstructor); + assertNotNull(listCase); + assertNotNull(setCase); + assertNotNull(mapCase); + + if (!(listCase instanceof ArrayList)) { + fail(); + } + if (!(setCase instanceof HashSet)) { + fail(); + } + if (!(mapCase instanceof HashMap)) { + fail(); + } + + try { + MockClassWithoutNoArgConstructor mockClassWithoutNoArgConstructor = (MockClassWithoutNoArgConstructor)Beans + .createObject(MockClassWithoutNoArgConstructor.class, false); + fail(); + } catch (InvocationTargetException e) { + + } + try { + mockClassWithPrivateConstructor = (MockClassWithPrivateConstructor)Beans + .createObject(MockClassWithPrivateConstructor.class, false); + fail(); + } catch (InvocationTargetException e) { + + } + } + + @Test + public void getPropertiesWithNullType () throws Exception { + try { + Beans.getProperties(null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertiesWithBeanProperties () throws Exception { + final Set properties = Beans.getProperties(MockClass.class, true, false, yamlConfig); + assertEquals(9, properties.size()); + } + + @Test + public void getPropertiesWithPrivateFields () throws Exception { + final Set properties = Beans.getProperties(MockClass.class, false, true, yamlConfig); + assertEquals(9, properties.size()); + } + + @Test + public void getPropertyWithNullType () throws Exception { + try { + Beans.getProperty(null, null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertyWithNullName () throws Exception { + try { + Beans.getProperty(MockClass.class, null, false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + } + + @Test + public void getPropertyWithEmptyName () throws Exception { + try { + Beans.getProperty(MockClass.class, "", false, false, yamlConfig); + fail(); + } catch (IllegalArgumentException e) { + // nothing + } + + } + + @Test + public void getPropertyBooleanTypeWithBeanProperties () throws Exception { + final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", true, false, yamlConfig); + + assertTrue(booleanTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); + assertEquals("booleanType", booleanTypeProperty.getName()); + assertEquals(boolean.class, booleanTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(false, mockClass.booleanType); + + booleanTypeProperty.set(mockClass, true); + assertEquals(true, booleanTypeProperty.get(mockClass)); + + booleanTypeProperty.set(mockClass, false); + assertEquals(false, booleanTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyCharTypeWithBeanProperties () throws Exception { + final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", true, false, yamlConfig); + + assertTrue(charTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); + assertEquals("charType", charTypeProperty.getName()); + assertEquals(char.class, charTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.charType); + + charTypeProperty.set(mockClass, 'a'); + assertEquals('a', charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MAX_VALUE); + assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); + + charTypeProperty.set(mockClass, Character.MIN_VALUE); + assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); + } + + @Test + public void getPropertyByteTypeWithBeanProperties () throws Exception { + final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", true, false, yamlConfig); + + assertTrue(byteTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); + assertEquals("byteType", byteTypeProperty.getName()); + assertEquals(byte.class, byteTypeProperty.getType()); + + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.byteType); + + byteTypeProperty.set(mockClass, (byte)1); + assertEquals((byte)1, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MAX_VALUE); + assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); + + byteTypeProperty.set(mockClass, Byte.MIN_VALUE); + assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); + } - @Test - public void getPropertyShortTypeWithBeanProperties() throws Exception { - final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", true, false, yamlConfig); + @Test + public void getPropertyShortTypeWithBeanProperties () throws Exception { + final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", true, false, yamlConfig); - assertTrue(shortTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); - assertEquals("shortType", shortTypeProperty.getName()); - assertEquals(short.class, shortTypeProperty.getType()); + assertTrue(shortTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); + assertEquals("shortType", shortTypeProperty.getName()); + assertEquals(short.class, shortTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.shortType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.shortType); - shortTypeProperty.set(mockClass, (short) 1); - assertEquals((short) 1, shortTypeProperty.get(mockClass)); + shortTypeProperty.set(mockClass, (short)1); + assertEquals((short)1, shortTypeProperty.get(mockClass)); - shortTypeProperty.set(mockClass, Short.MAX_VALUE); - assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); + shortTypeProperty.set(mockClass, Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); - shortTypeProperty.set(mockClass, Short.MIN_VALUE); - assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); - } + shortTypeProperty.set(mockClass, Short.MIN_VALUE); + assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); + } - @Test - public void getPropertyIntTypeWithBeanProperties() throws Exception { - final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", true, false, yamlConfig); + @Test + public void getPropertyIntTypeWithBeanProperties () throws Exception { + final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", true, false, yamlConfig); - assertTrue(intTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); - assertEquals("intType", intTypeProperty.getName()); - assertEquals(int.class, intTypeProperty.getType()); + assertTrue(intTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); + assertEquals("intType", intTypeProperty.getName()); + assertEquals(int.class, intTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.intType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.intType); - intTypeProperty.set(mockClass, 1); - assertEquals(1, intTypeProperty.get(mockClass)); + intTypeProperty.set(mockClass, 1); + assertEquals(1, intTypeProperty.get(mockClass)); - intTypeProperty.set(mockClass, Integer.MAX_VALUE); - assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); + intTypeProperty.set(mockClass, Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); - intTypeProperty.set(mockClass, Integer.MIN_VALUE); - assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); - } + intTypeProperty.set(mockClass, Integer.MIN_VALUE); + assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); + } - @Test - public void getPropertyLongTypeWithBeanProperties() throws Exception { - final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", true, false, yamlConfig); + @Test + public void getPropertyLongTypeWithBeanProperties () throws Exception { + final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", true, false, yamlConfig); - assertTrue(longTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); - assertEquals("longType", longTypeProperty.getName()); - assertEquals(long.class, longTypeProperty.getType()); + assertTrue(longTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); + assertEquals("longType", longTypeProperty.getName()); + assertEquals(long.class, longTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.longType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.longType); - longTypeProperty.set(mockClass, 1l); - assertEquals(1l, longTypeProperty.get(mockClass)); + longTypeProperty.set(mockClass, 1l); + assertEquals(1l, longTypeProperty.get(mockClass)); - longTypeProperty.set(mockClass, Long.MAX_VALUE); - assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); + longTypeProperty.set(mockClass, Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); - longTypeProperty.set(mockClass, Long.MIN_VALUE); - assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); - } + longTypeProperty.set(mockClass, Long.MIN_VALUE); + assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); + } - @Test - public void getPropertyFloatTypeWithBeanProperties() throws Exception { - final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", true, false, yamlConfig); + @Test + public void getPropertyFloatTypeWithBeanProperties () throws Exception { + final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", true, false, yamlConfig); - assertTrue(floatTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); - assertEquals("floatType", floatTypeProperty.getName()); - assertEquals(float.class, floatTypeProperty.getType()); + assertTrue(floatTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); + assertEquals("floatType", floatTypeProperty.getName()); + assertEquals(float.class, floatTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals((float) 0, mockClass.floatType, DELTA); + MockClass mockClass = new MockClass(); + assertEquals((float)0, mockClass.floatType, DELTA); - floatTypeProperty.set(mockClass, (float) 1.0); - assertEquals((float) 1.0, floatTypeProperty.get(mockClass)); + floatTypeProperty.set(mockClass, (float)1.0); + assertEquals((float)1.0, floatTypeProperty.get(mockClass)); - floatTypeProperty.set(mockClass, Float.MAX_VALUE); - assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); + floatTypeProperty.set(mockClass, Float.MAX_VALUE); + assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); - floatTypeProperty.set(mockClass, Float.MIN_VALUE); - assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); - } + floatTypeProperty.set(mockClass, Float.MIN_VALUE); + assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); + } - @Test - public void getPropertyDoubleTypeWithBeanProperties() throws Exception { - final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", true, false, yamlConfig); + @Test + public void getPropertyDoubleTypeWithBeanProperties () throws Exception { + final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", true, false, yamlConfig); - assertTrue(doubleTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); - assertEquals("doubleType", doubleTypeProperty.getName()); - assertEquals(double.class, doubleTypeProperty.getType()); + assertTrue(doubleTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); + assertEquals("doubleType", doubleTypeProperty.getName()); + assertEquals(double.class, doubleTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals((double) 0, mockClass.doubleType, DELTA); + MockClass mockClass = new MockClass(); + assertEquals((double)0, mockClass.doubleType, DELTA); - doubleTypeProperty.set(mockClass, (double) 1.0); - assertEquals((double) 1.0, doubleTypeProperty.get(mockClass)); + doubleTypeProperty.set(mockClass, (double)1.0); + assertEquals((double)1.0, doubleTypeProperty.get(mockClass)); - doubleTypeProperty.set(mockClass, Double.MAX_VALUE); - assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); + doubleTypeProperty.set(mockClass, Double.MAX_VALUE); + assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); - doubleTypeProperty.set(mockClass, Double.MIN_VALUE); - assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); - } + doubleTypeProperty.set(mockClass, Double.MIN_VALUE); + assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); + } - @Test - public void getPropertyStringTypeWithBeanProperties() throws Exception { - final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", true, false, yamlConfig); + @Test + public void getPropertyStringTypeWithBeanProperties () throws Exception { + final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", true, false, yamlConfig); - assertTrue(stringTypeProperty instanceof Beans.MethodProperty); - assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); - assertEquals("stringType", stringTypeProperty.getName()); - assertEquals(String.class, stringTypeProperty.getType()); + assertTrue(stringTypeProperty instanceof Beans.MethodProperty); + assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); + assertEquals("stringType", stringTypeProperty.getName()); + assertEquals(String.class, stringTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(null, mockClass.stringType); + MockClass mockClass = new MockClass(); + assertEquals(null, mockClass.stringType); - stringTypeProperty.set(mockClass, "4cho"); - assertEquals("4cho", stringTypeProperty.get(mockClass)); + stringTypeProperty.set(mockClass, "4cho"); + assertEquals("4cho", stringTypeProperty.get(mockClass)); - stringTypeProperty.set(mockClass, "ghkim"); - assertEquals("ghkim", stringTypeProperty.get(mockClass)); - } + stringTypeProperty.set(mockClass, "ghkim"); + assertEquals("ghkim", stringTypeProperty.get(mockClass)); + } - @Test - public void getPropertyNullWithBeanProperties() throws Exception { - final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", true, false, yamlConfig); + @Test + public void getPropertyNullWithBeanProperties () throws Exception { + final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", true, false, yamlConfig); - assertNull(nullProperty); - } + assertNull(nullProperty); + } - @Test - public void getPropertyBooleanTypeWithPrivateFields() throws Exception { - final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", false, true, null); + @Test + public void getPropertyBooleanTypeWithPrivateFields () throws Exception { + final Beans.Property booleanTypeProperty = Beans.getProperty(MockClass.class, "booleanType", false, true, null); - assertTrue(booleanTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); - assertEquals("booleanType", booleanTypeProperty.getName()); - assertEquals(boolean.class, booleanTypeProperty.getType()); + assertTrue(booleanTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, booleanTypeProperty.getDeclaringClass()); + assertEquals("booleanType", booleanTypeProperty.getName()); + assertEquals(boolean.class, booleanTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(false, mockClass.booleanType); + MockClass mockClass = new MockClass(); + assertEquals(false, mockClass.booleanType); - booleanTypeProperty.set(mockClass, true); - assertEquals(true, booleanTypeProperty.get(mockClass)); + booleanTypeProperty.set(mockClass, true); + assertEquals(true, booleanTypeProperty.get(mockClass)); - booleanTypeProperty.set(mockClass, false); - assertEquals(false, booleanTypeProperty.get(mockClass)); - } + booleanTypeProperty.set(mockClass, false); + assertEquals(false, booleanTypeProperty.get(mockClass)); + } - @Test - public void getPropertyCharTypeWithPrivateFields() throws Exception { - final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", false, true, yamlConfig); + @Test + public void getPropertyCharTypeWithPrivateFields () throws Exception { + final Beans.Property charTypeProperty = Beans.getProperty(MockClass.class, "charType", false, true, yamlConfig); - assertTrue(charTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); - assertEquals("charType", charTypeProperty.getName()); - assertEquals(char.class, charTypeProperty.getType()); + assertTrue(charTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, charTypeProperty.getDeclaringClass()); + assertEquals("charType", charTypeProperty.getName()); + assertEquals(char.class, charTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.charType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.charType); - charTypeProperty.set(mockClass, 'a'); - assertEquals('a', charTypeProperty.get(mockClass)); + charTypeProperty.set(mockClass, 'a'); + assertEquals('a', charTypeProperty.get(mockClass)); - charTypeProperty.set(mockClass, Character.MAX_VALUE); - assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); + charTypeProperty.set(mockClass, Character.MAX_VALUE); + assertEquals(Character.MAX_VALUE, charTypeProperty.get(mockClass)); - charTypeProperty.set(mockClass, Character.MIN_VALUE); - assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); - } + charTypeProperty.set(mockClass, Character.MIN_VALUE); + assertEquals(Character.MIN_VALUE, charTypeProperty.get(mockClass)); + } - @Test - public void getPropertyByteTypeWithPrivateFields() throws Exception { - final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", false, true, yamlConfig); + @Test + public void getPropertyByteTypeWithPrivateFields () throws Exception { + final Beans.Property byteTypeProperty = Beans.getProperty(MockClass.class, "byteType", false, true, yamlConfig); - assertTrue(byteTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); - assertEquals("byteType", byteTypeProperty.getName()); - assertEquals(byte.class, byteTypeProperty.getType()); + assertTrue(byteTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, byteTypeProperty.getDeclaringClass()); + assertEquals("byteType", byteTypeProperty.getName()); + assertEquals(byte.class, byteTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.byteType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.byteType); - byteTypeProperty.set(mockClass, (byte) 1); - assertEquals((byte) 1, byteTypeProperty.get(mockClass)); + byteTypeProperty.set(mockClass, (byte)1); + assertEquals((byte)1, byteTypeProperty.get(mockClass)); - byteTypeProperty.set(mockClass, Byte.MAX_VALUE); - assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); + byteTypeProperty.set(mockClass, Byte.MAX_VALUE); + assertEquals(Byte.MAX_VALUE, byteTypeProperty.get(mockClass)); - byteTypeProperty.set(mockClass, Byte.MIN_VALUE); - assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); - } + byteTypeProperty.set(mockClass, Byte.MIN_VALUE); + assertEquals(Byte.MIN_VALUE, byteTypeProperty.get(mockClass)); + } - @Test - public void getPropertyShortTypeWithPrivateFields() throws Exception { - final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", false, true, yamlConfig); + @Test + public void getPropertyShortTypeWithPrivateFields () throws Exception { + final Beans.Property shortTypeProperty = Beans.getProperty(MockClass.class, "shortType", false, true, yamlConfig); - assertTrue(shortTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); - assertEquals("shortType", shortTypeProperty.getName()); - assertEquals(short.class, shortTypeProperty.getType()); + assertTrue(shortTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, shortTypeProperty.getDeclaringClass()); + assertEquals("shortType", shortTypeProperty.getName()); + assertEquals(short.class, shortTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.shortType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.shortType); - shortTypeProperty.set(mockClass, (short) 1); - assertEquals((short) 1, shortTypeProperty.get(mockClass)); + shortTypeProperty.set(mockClass, (short)1); + assertEquals((short)1, shortTypeProperty.get(mockClass)); - shortTypeProperty.set(mockClass, Short.MAX_VALUE); - assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); + shortTypeProperty.set(mockClass, Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, shortTypeProperty.get(mockClass)); - shortTypeProperty.set(mockClass, Short.MIN_VALUE); - assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); - } + shortTypeProperty.set(mockClass, Short.MIN_VALUE); + assertEquals(Short.MIN_VALUE, shortTypeProperty.get(mockClass)); + } - @Test - public void getPropertyIntTypeWithPrivateFields() throws Exception { - final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", false, true, yamlConfig); + @Test + public void getPropertyIntTypeWithPrivateFields () throws Exception { + final Beans.Property intTypeProperty = Beans.getProperty(MockClass.class, "intType", false, true, yamlConfig); - assertTrue(intTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); - assertEquals("intType", intTypeProperty.getName()); - assertEquals(int.class, intTypeProperty.getType()); + assertTrue(intTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, intTypeProperty.getDeclaringClass()); + assertEquals("intType", intTypeProperty.getName()); + assertEquals(int.class, intTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.intType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.intType); - intTypeProperty.set(mockClass, 1); - assertEquals(1, intTypeProperty.get(mockClass)); + intTypeProperty.set(mockClass, 1); + assertEquals(1, intTypeProperty.get(mockClass)); - intTypeProperty.set(mockClass, Integer.MAX_VALUE); - assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); + intTypeProperty.set(mockClass, Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, intTypeProperty.get(mockClass)); - intTypeProperty.set(mockClass, Integer.MIN_VALUE); - assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); - } + intTypeProperty.set(mockClass, Integer.MIN_VALUE); + assertEquals(Integer.MIN_VALUE, intTypeProperty.get(mockClass)); + } - @Test - public void getPropertyLongTypeWithPrivateFields() throws Exception { - final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", false, true, yamlConfig); + @Test + public void getPropertyLongTypeWithPrivateFields () throws Exception { + final Beans.Property longTypeProperty = Beans.getProperty(MockClass.class, "longType", false, true, yamlConfig); - assertTrue(longTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); - assertEquals("longType", longTypeProperty.getName()); - assertEquals(long.class, longTypeProperty.getType()); + assertTrue(longTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, longTypeProperty.getDeclaringClass()); + assertEquals("longType", longTypeProperty.getName()); + assertEquals(long.class, longTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(0, mockClass.longType); + MockClass mockClass = new MockClass(); + assertEquals(0, mockClass.longType); - longTypeProperty.set(mockClass, 1l); - assertEquals(1l, longTypeProperty.get(mockClass)); + longTypeProperty.set(mockClass, 1l); + assertEquals(1l, longTypeProperty.get(mockClass)); - longTypeProperty.set(mockClass, Long.MAX_VALUE); - assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); + longTypeProperty.set(mockClass, Long.MAX_VALUE); + assertEquals(Long.MAX_VALUE, longTypeProperty.get(mockClass)); - longTypeProperty.set(mockClass, Long.MIN_VALUE); - assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); - } + longTypeProperty.set(mockClass, Long.MIN_VALUE); + assertEquals(Long.MIN_VALUE, longTypeProperty.get(mockClass)); + } - @Test - public void getPropertyFloatTypeWithPrivateFields() throws Exception { - final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", false, true, yamlConfig); + @Test + public void getPropertyFloatTypeWithPrivateFields () throws Exception { + final Beans.Property floatTypeProperty = Beans.getProperty(MockClass.class, "floatType", false, true, yamlConfig); - assertTrue(floatTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); - assertEquals("floatType", floatTypeProperty.getName()); - assertEquals(float.class, floatTypeProperty.getType()); + assertTrue(floatTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, floatTypeProperty.getDeclaringClass()); + assertEquals("floatType", floatTypeProperty.getName()); + assertEquals(float.class, floatTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals((float) 0, mockClass.floatType, DELTA); + MockClass mockClass = new MockClass(); + assertEquals((float)0, mockClass.floatType, DELTA); - floatTypeProperty.set(mockClass, (float) 1.0); - assertEquals((float) 1.0, floatTypeProperty.get(mockClass)); + floatTypeProperty.set(mockClass, (float)1.0); + assertEquals((float)1.0, floatTypeProperty.get(mockClass)); - floatTypeProperty.set(mockClass, Float.MAX_VALUE); - assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); + floatTypeProperty.set(mockClass, Float.MAX_VALUE); + assertEquals(Float.MAX_VALUE, floatTypeProperty.get(mockClass)); - floatTypeProperty.set(mockClass, Float.MIN_VALUE); - assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); - } + floatTypeProperty.set(mockClass, Float.MIN_VALUE); + assertEquals(Float.MIN_VALUE, floatTypeProperty.get(mockClass)); + } - @Test - public void getPropertyDoubleTypeWithPrivateFields() throws Exception { - final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", false, true, yamlConfig); + @Test + public void getPropertyDoubleTypeWithPrivateFields () throws Exception { + final Beans.Property doubleTypeProperty = Beans.getProperty(MockClass.class, "doubleType", false, true, yamlConfig); - assertTrue(doubleTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); - assertEquals("doubleType", doubleTypeProperty.getName()); - assertEquals(double.class, doubleTypeProperty.getType()); + assertTrue(doubleTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, doubleTypeProperty.getDeclaringClass()); + assertEquals("doubleType", doubleTypeProperty.getName()); + assertEquals(double.class, doubleTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals((double) 0, mockClass.doubleType, DELTA); + MockClass mockClass = new MockClass(); + assertEquals((double)0, mockClass.doubleType, DELTA); - doubleTypeProperty.set(mockClass, (double) 1.0); - assertEquals((double) 1.0, doubleTypeProperty.get(mockClass)); + doubleTypeProperty.set(mockClass, (double)1.0); + assertEquals((double)1.0, doubleTypeProperty.get(mockClass)); - doubleTypeProperty.set(mockClass, Double.MAX_VALUE); - assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); + doubleTypeProperty.set(mockClass, Double.MAX_VALUE); + assertEquals(Double.MAX_VALUE, doubleTypeProperty.get(mockClass)); - doubleTypeProperty.set(mockClass, Double.MIN_VALUE); - assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); - } + doubleTypeProperty.set(mockClass, Double.MIN_VALUE); + assertEquals(Double.MIN_VALUE, doubleTypeProperty.get(mockClass)); + } - @Test - public void getPropertyStringTypeWithPrivateFields() throws Exception { - final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", false, true, yamlConfig); + @Test + public void getPropertyStringTypeWithPrivateFields () throws Exception { + final Beans.Property stringTypeProperty = Beans.getProperty(MockClass.class, "stringType", false, true, yamlConfig); - assertTrue(stringTypeProperty instanceof Beans.FieldProperty); - assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); - assertEquals("stringType", stringTypeProperty.getName()); - assertEquals(String.class, stringTypeProperty.getType()); + assertTrue(stringTypeProperty instanceof Beans.FieldProperty); + assertEquals(MockClass.class, stringTypeProperty.getDeclaringClass()); + assertEquals("stringType", stringTypeProperty.getName()); + assertEquals(String.class, stringTypeProperty.getType()); - MockClass mockClass = new MockClass(); - assertEquals(null, mockClass.stringType); + MockClass mockClass = new MockClass(); + assertEquals(null, mockClass.stringType); - stringTypeProperty.set(mockClass, "4cho"); - assertEquals("4cho", stringTypeProperty.get(mockClass)); + stringTypeProperty.set(mockClass, "4cho"); + assertEquals("4cho", stringTypeProperty.get(mockClass)); - stringTypeProperty.set(mockClass, "ghkim"); - assertEquals("ghkim", stringTypeProperty.get(mockClass)); - } + stringTypeProperty.set(mockClass, "ghkim"); + assertEquals("ghkim", stringTypeProperty.get(mockClass)); + } - @Test - public void getPropertyNullWithPrivateFields() throws Exception { - final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", false, true, yamlConfig); + @Test + public void getPropertyNullWithPrivateFields () throws Exception { + final Beans.Property nullProperty = Beans.getProperty(MockClass.class, "nullType", false, true, yamlConfig); - assertNull(nullProperty); - } + assertNull(nullProperty); + } - private static class MockClass { + private static class MockClass { - private boolean booleanType; - private char charType; - private byte byteType; - private short shortType; - private int intType; - private long longType; - private float floatType; - private double doubleType; - private String stringType; + private boolean booleanType; + private char charType; + private byte byteType; + private short shortType; + private int intType; + private long longType; + private float floatType; + private double doubleType; + private String stringType; - public MockClass() { + public MockClass () { - } + } - public boolean isBooleanType() { - return booleanType; - } + public boolean isBooleanType () { + return booleanType; + } - public void setBooleanType(boolean booleanType) { - this.booleanType = booleanType; - } + public void setBooleanType (boolean booleanType) { + this.booleanType = booleanType; + } - public char getCharType() { - return charType; - } + public char getCharType () { + return charType; + } - public void setCharType(char charType) { - this.charType = charType; - } + public void setCharType (char charType) { + this.charType = charType; + } - public byte getByteType() { - return byteType; - } + public byte getByteType () { + return byteType; + } - public void setByteType(byte byteType) { - this.byteType = byteType; - } + public void setByteType (byte byteType) { + this.byteType = byteType; + } - public short getShortType() { - return shortType; - } + public short getShortType () { + return shortType; + } - public void setShortType(short shortType) { - this.shortType = shortType; - } + public void setShortType (short shortType) { + this.shortType = shortType; + } - public int getIntType() { - return intType; - } + public int getIntType () { + return intType; + } - public void setIntType(int intType) { - this.intType = intType; - } + public void setIntType (int intType) { + this.intType = intType; + } - public long getLongType() { - return longType; - } + public long getLongType () { + return longType; + } - public void setLongType(long longType) { - this.longType = longType; - } + public void setLongType (long longType) { + this.longType = longType; + } - public float getFloatType() { - return floatType; - } + public float getFloatType () { + return floatType; + } - public void setFloatType(float floatType) { - this.floatType = floatType; - } + public void setFloatType (float floatType) { + this.floatType = floatType; + } - public double getDoubleType() { - return doubleType; - } + public double getDoubleType () { + return doubleType; + } - public void setDoubleType(double doubleType) { - this.doubleType = doubleType; - } + public void setDoubleType (double doubleType) { + this.doubleType = doubleType; + } - public String getStringType() { - return stringType; - } + public String getStringType () { + return stringType; + } - public void setStringType(String stringType) { - this.stringType = stringType; - } - } + public void setStringType (String stringType) { + this.stringType = stringType; + } + } - private static class MockClassWithPrivateConstructor { + private static class MockClassWithPrivateConstructor { - private boolean booleanType; - private char charType; - private byte byteType; - private short shortType; - private int intType; - private long longType; - private float floatType; - private double doubleType; - private String stringType; + private boolean booleanType; + private char charType; + private byte byteType; + private short shortType; + private int intType; + private long longType; + private float floatType; + private double doubleType; + private String stringType; - private MockClassWithPrivateConstructor() { - - } + private MockClassWithPrivateConstructor () { + + } - public boolean isBooleanType() { - return booleanType; - } + public boolean isBooleanType () { + return booleanType; + } - public void setBooleanType(boolean booleanType) { - this.booleanType = booleanType; - } + public void setBooleanType (boolean booleanType) { + this.booleanType = booleanType; + } - public char getCharType() { - return charType; - } + public char getCharType () { + return charType; + } - public void setCharType(char charType) { - this.charType = charType; - } + public void setCharType (char charType) { + this.charType = charType; + } - public byte getByteType() { - return byteType; - } + public byte getByteType () { + return byteType; + } - public void setByteType(byte byteType) { - this.byteType = byteType; - } + public void setByteType (byte byteType) { + this.byteType = byteType; + } - public short getShortType() { - return shortType; - } + public short getShortType () { + return shortType; + } - public void setShortType(short shortType) { - this.shortType = shortType; - } + public void setShortType (short shortType) { + this.shortType = shortType; + } - public int getIntType() { - return intType; - } + public int getIntType () { + return intType; + } - public void setIntType(int intType) { - this.intType = intType; - } + public void setIntType (int intType) { + this.intType = intType; + } - public long getLongType() { - return longType; - } + public long getLongType () { + return longType; + } - public void setLongType(long longType) { - this.longType = longType; - } + public void setLongType (long longType) { + this.longType = longType; + } - public float getFloatType() { - return floatType; - } + public float getFloatType () { + return floatType; + } - public void setFloatType(float floatType) { - this.floatType = floatType; - } + public void setFloatType (float floatType) { + this.floatType = floatType; + } - public double getDoubleType() { - return doubleType; - } + public double getDoubleType () { + return doubleType; + } - public void setDoubleType(double doubleType) { - this.doubleType = doubleType; - } + public void setDoubleType (double doubleType) { + this.doubleType = doubleType; + } - public String getStringType() { - return stringType; - } + public String getStringType () { + return stringType; + } - public void setStringType(String stringType) { - this.stringType = stringType; - } - } + public void setStringType (String stringType) { + this.stringType = stringType; + } + } - private static class MockClassWithoutNoArgConstructor { + private static class MockClassWithoutNoArgConstructor { - private Object anyType; + private Object anyType; - public MockClassWithoutNoArgConstructor(Object anyType) { - this.anyType = anyType; - } - } -} \ No newline at end of file + public MockClassWithoutNoArgConstructor (Object anyType) { + this.anyType = anyType; + } + } +} From a9633b224205c20ab8a77feb49fe80352682c9f9 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 31 May 2017 09:55:27 +0200 Subject: [PATCH 028/114] 1.12 --- pom.xml | 2 +- project.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5f6c35b..3f0a51b 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.11 + 1.12 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans diff --git a/project.yaml b/project.yaml index 8b9be3d..da4e6d9 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.11 +version: 1.12 From c52d46d339ae544f2b84210d1d3a77306a655b01 Mon Sep 17 00:00:00 2001 From: Tony CHEMIT Date: Wed, 31 May 2017 10:44:37 +0200 Subject: [PATCH 029/114] Update pom (#74) * Change my coordinates * Add badges on readme --- README.md | 3 +++ pom.xml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 317cba0..cc886c0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ ## YamlBeans +![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg) +![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.esotericsoftware.yamlbeans/yamlbeans/badge.svg) + Please use the [YamlBeans discussion group](http://groups.google.com/group/yamlbeans-users) for support. ## Overview diff --git a/pom.xml b/pom.xml index 3f0a51b..d7383aa 100644 --- a/pom.xml +++ b/pom.xml @@ -125,9 +125,9 @@ Tony Chemit - chemit at codelutin dot com - CodeLutin - http://codelutin.com + dev@tchemit.fr + Ultreia + http://ultreia.io Europe/Paris Maven packager From 73a439e7c624d46920e9692c658995ccbd573366 Mon Sep 17 00:00:00 2001 From: Gihwan Kim <0x1306e6d@gmail.com> Date: Wed, 31 May 2017 19:31:41 +0900 Subject: [PATCH 030/114] Apply system line separator to test cases (#75) --- .../yamlbeans/GenericTest.java | 40 ++++++++++--------- .../yamlbeans/YamlWriterTest.java | 19 +++++---- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/test/com/esotericsoftware/yamlbeans/GenericTest.java b/test/com/esotericsoftware/yamlbeans/GenericTest.java index 15a2a99..a04d3a8 100644 --- a/test/com/esotericsoftware/yamlbeans/GenericTest.java +++ b/test/com/esotericsoftware/yamlbeans/GenericTest.java @@ -12,26 +12,28 @@ public class GenericTest extends TestCase { + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + private static final String YAML - = "integerList: \n" - + "- 1\n" - + "- 100500\n" - + "- 10\n" - + "stringMap: \n" - + " a: av\n" - + " b: bv\n" - + "structList: \n" - + "- i: 10\n" - + " str: aaa\n" - + "- i: 20\n" - + " str: bbb\n" - + "structMap: \n" - + " a: \n" - + " i: 1\n" - + " str: aa\n" - + " b: \n" - + " i: 2\n" - + " str: ab\n"; + = "integerList: " + LINE_SEPARATOR + + "- 1" + LINE_SEPARATOR + + "- 100500" + LINE_SEPARATOR + + "- 10" + LINE_SEPARATOR + + "stringMap: " + LINE_SEPARATOR + + " a: av" + LINE_SEPARATOR + + " b: bv" + LINE_SEPARATOR + + "structList: " + LINE_SEPARATOR + + "- i: 10" + LINE_SEPARATOR + + " str: aaa" + LINE_SEPARATOR + + "- i: 20" + LINE_SEPARATOR + + " str: bbb" + LINE_SEPARATOR + + "structMap: " + LINE_SEPARATOR + + " a: " + LINE_SEPARATOR + + " i: 1" + LINE_SEPARATOR + + " str: aa" + LINE_SEPARATOR + + " b: " + LINE_SEPARATOR + + " i: 2" + LINE_SEPARATOR + + " str: ab" + LINE_SEPARATOR; public void testRead() throws YamlException { Test test = createTest(); diff --git a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java index fdabc9e..741597d 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java @@ -32,6 +32,9 @@ /** @author Nathan Sweet */ public class YamlWriterTest extends TestCase { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + public void testPrivateFields () throws Exception { ArrayList list = new ArrayList(); list.add("abc"); @@ -212,14 +215,14 @@ public void testExplicitDocumentEnd () throws Exception { YamlWriter writer = new YamlWriter(buffer, config); writer.write("test"); writer.close(); - assertEquals("test\n...\n", buffer.toString()); + assertEquals("test" + LINE_SEPARATOR + "..." + LINE_SEPARATOR, buffer.toString()); buffer = new StringWriter(); writer = new YamlWriter(buffer, config); writer.write("test"); writer.write("test"); writer.close(); - assertEquals("test\n...\n--- test\n...\n", buffer.toString()); + assertEquals("test" + LINE_SEPARATOR + "..." + LINE_SEPARATOR + "--- test" + LINE_SEPARATOR + "..." + LINE_SEPARATOR, buffer.toString()); } void checkWriterOutput (String example, String expected) throws Exception { @@ -231,14 +234,14 @@ void checkWriterOutput (String example, String expected) throws Exception { } public void testSingleQuotesDoubling () throws Exception { - checkWriterOutput("abc", "abc\n"); - checkWriterOutput("abc def", "abc def\n"); + checkWriterOutput("abc", "abc" + LINE_SEPARATOR); + checkWriterOutput("abc def", "abc def" + LINE_SEPARATOR); // single quotes around output as soon as we have a colon in the input - checkWriterOutput("abc: def", "'abc: def'\n"); - checkWriterOutput("abc: def'ghi", "'abc: def''ghi'\n"); - checkWriterOutput("abc: def 'ghi", "'abc: def ''ghi'\n"); + checkWriterOutput("abc: def", "'abc: def'" + LINE_SEPARATOR); + checkWriterOutput("abc: def'ghi", "'abc: def''ghi'" + LINE_SEPARATOR); + checkWriterOutput("abc: def 'ghi", "'abc: def ''ghi'" + LINE_SEPARATOR); // That was the initial bug: - checkWriterOutput("A: 'X'", "'A: ''X'''\n"); + checkWriterOutput("A: 'X'", "'A: ''X'''" + LINE_SEPARATOR); } public void testObjectField () throws Exception { From d9b874aa455ac6771ddbe1a4f23d218eca939265 Mon Sep 17 00:00:00 2001 From: arrayadd Date: Thu, 1 Jun 2017 01:15:54 +0800 Subject: [PATCH 031/114] Add Chinese document support (#77) * Create CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update CN_README.md * Update README.md * Update CN_README.md * add chinese support link * Update CN_README.md --- CN_README.md | 314 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 316 insertions(+) create mode 100644 CN_README.md diff --git a/CN_README.md b/CN_README.md new file mode 100644 index 0000000..f8872ae --- /dev/null +++ b/CN_README.md @@ -0,0 +1,314 @@ +## YamlBeans + +若需更多支持,请移步[YamlBeans讨论组](http://groups.google.com/group/yamlbeans-users) + +## 概述 + +**YAML**是一种人性化的数据格式,使用YAML来替代XML和properties文件,可以获得更多的表现力(支持lists,maps,anchors等结构),以及更容易的手工编辑。 +而**YamlBeans**则可以让Java对象和YAML格式之间的转换(序列化和反序列化)变得更容易。 + +Maven 仓库: +http://repo1.maven.org/maven2/com/esotericsoftware/yamlbeans/yamlbeans/ + +## 基本的反序列化 + +**YamlReader**这个类用于将YAML格式数据反序列化为Java对象。下面定义了一个包含4个实体的Map,其中最后一个实体项`phone numbers`又是一个包含了2个item的List集合,而每一个item又是一个Map结构。 + +```yaml + name: Nathan Sweet + age: 28 + address: 4011 16th Ave S + phone numbers: + - name: Home + number: 206-555-5138 + - name: Work + number: 425-555-2306 +``` + +“**read()**”方法可用来读取`contact.yml`文件中描述的YAML对象,并将其反序列化为对应的HashMaps,ArrayLists和Strings。因为我们已经知道上面示例的YAML文件中定义的对象是一个Map,所以下面示例中我们可以直接转换为java对象并使用它。 + +```java + YamlReader reader = new YamlReader(new FileReader("contact.yml")); + Object object = reader.read(); + System.out.println(object); + Map map = (Map)object; + System.out.println(map.get("address")); +``` + +## 多个对象 + +一个YAML格式的文件中可以包含多个YAML对象,多个YAML对象之间用`---`隔开(开头第一个可以省略) + +```yaml + name: Nathan Sweet + age: 28 + --- + name: Some One + age: 25 +``` +下面示例中,当while循环中每次调用YamlReader类中read()方法时,就会将contact.yml文件中对应顺序的YAML对象反序列化成一个与之对应结构的Java对象。 +下面代码会**先后输出**字符串`"28"`,`"25"`: + +```java + YamlReader reader = new YamlReader(new FileReader("contact.yml")); + while (true) { + Map contact = reader.read(); + if (contact == null) break; + System.out.println(contact.get("age")); + } +``` + +## 反序列化其他类 + +有两种方法来反序列化除HashMaps,ArrayLists和Strings之外的其他自定义数据格式,例如下面这个YAML文件和Java类: + +```yaml + name: Nathan Sweet + age: 28 +``` +```java + public class Contact { + public String name; + public int age; + } +``` + +在“**read()**”方法的入参中传递一个类,这样YamlReader就可以直接反序列化为指定的类: + +```java + YamlReader reader = new YamlReader(new FileReader("contact.yml")); + Contact contact = reader.read(Contact.class); + System.out.println(contact.age); +``` + + +上面YamlReader创建了一个Contact.class的实例对象,并给“name”和“age”字段赋值,且YamlReader会把YAML中“age”的值转换为int。如果age不是int类型,则反序列化将失败。 + + +除了上面这种反序列化方法外,还可以在YAML中使用添加`!全限定类名`方式来直接指定类型: + +```yaml + !com.example.Contact + name: Nathan Sweet + age: 28 +``` + +## 序列化对象 + + **YamlWriter**这个类用来把Java对象序列化为YAML格式。且“**write()**”方法会自动识别处理public字段和getter方法(一般private属性会生成getter方法)。 + +```java + Contact contact = new Contact(); + contact.name = "Nathan Sweet"; + contact.age = 28; + YamlWriter writer = new YamlWriter(new FileWriter("output.yml")); + writer.write(contact); + writer.close(); +``` + +输出: + +```yaml + !com.example.Contact + name: Nathan Sweet + age: 28 +``` + +上面`!com.example.Contact`部分会根据需要自动输出,以便YamlReader类能够反序列化时重建对应的Java对象。但序列化ArrayList时则不会输出任何类似`!com.example.Contact`的格式内容,因为YamlReader默认就会用ArrayList。 + +```java + List list = new ArrayList(); + list.add("moo"); + list.add("cow"); +``` +```yaml + - moo + - cow +``` + +但是如果List的接口实现是LinkedList,而不是ArrayList(默认),那么YamlWriter类就会输出,例如下面: + +```java + List list = new LinkedList(); + list.add("moo"); + list.add("cow"); +``` +```yaml + !java.util.LinkedList + - moo + - cow +``` + +Note that it is not advisable to subclass Collection or Map. YamlBeans will only serialize the collection or map and its elements, not any additional fields. + +***注意***,在yaml中把集合或Map设置为子类节点是不可取的。YamlBeans只会序列化集合或Map及其中的元素,而不会对其他字段进行序列化。 + +## 复杂结构 + +**YamlBeans**可序列化任何对象。 + +```java + public class Contact { + public String name; + public int age; + public List phoneNumbers; + } + + public class Phone { + public String name; + public String number; + } +``` +```yaml + friends: + - !com.example.Contact + name: Bob + age: 29 + phoneNumbers: + - !com.example.Phone + name: Home + number: 206-555-1234 + - !com.example.Phone + name: Work + number: 206-555-5678 + - !com.example.Contact + name: Mike + age: 31 + phoneNumbers: + - !com.example.Phone + number: 206-555-4321 + enemies: + - !com.example.Contact + name: Bill + phoneNumbers: + - !com.example.Phone + name: Cell + number: 206-555-1234 +``` + +上面是一个由Contact类的List集合构成的复杂Map结构,而且Contact类中还包含phoneNumbers这个List属性。另外,public类型声明的字段也可以是java bean的属性(而不仅仅是getter方法对应的private类型字段)。 + +## 标签截取 + +`!com.example.Contact`这种形式的YAML标签有时可能会很长,会让YAML格式显得混乱不堪,不利于阅读。这时可以给类指定一个替代标签来代替,而不是用类的完整类名。 + +```java + YamlWriter writer = new YamlWriter(new FileWriter("output.yml")); + writer.getConfig().setClassTag("contact", Contact.class); + writer.write(contact); + writer.close(); +``` + +下面输出不再包含Contact类的完整类名。 + +```yaml + !contact + name: Nathan Sweet + age: 28 +``` + +## List 和 Map + +在读取或写入一个List或Map时,YamlBeans有时压根不知道List或Map中应该是什么类型的对象,因此它会输出一个类似`!com.example.Contact`的标签。 + +```yaml + !com.example.Contact + name: Bill + phoneNumbers: + - !com.example.Phone + number: 206-555-1234 + - !com.example.Phone + number: 206-555-5678 + - !com.example.Phone + number: 206-555-7654 +``` + +这样会导致YAML的可读性变的更差。为了提高可读性,你可以在List或Map对象的字段中,指定该字段所属的类型,像下面这样: + +```java + YamlWriter writer = new YamlWriter(new FileWriter("output.yml")); + writer.getConfig().setPropertyElementType(Contact.class, "phoneNumbers", Phone.class); + writer.write(contact); + writer.close(); +``` + + +现在,YamlBeans知道“phoneNumbers”字段的类型是什么了,因此不会额外输出多余的标签。 + +```yaml + !com.example.Contact + name: Bill + phoneNumbers: + - number: 206-555-1234 + - number: 206-555-5678 + - number: 206-555-7654 +``` + +Map中value值的类型,可以根据期望的情况来指定,但Map中的键总是字符串类型。 + +## 锚点 + +当一个对象的结构中包含对其他同一对象的多个引用时,可以设置一个锚点,这样这个被引用的对象只需要在YAML中定义一次。 + +```yaml + oldest friend: + &1 !contact + name: Bob + age: 29 + best friend: *1 +``` + +在上面map中,"oldest friend" 和 "best friend" 字段引用了同一个对象。在反序列化构建对象时,YamlReader会自动处理YAML中的锚点。同时,在默认情况下,YamlWriter在序列化对象时也会自动输出锚点。 + +```java + Contact contact = new Contact(); + contact.name = "Bob"; + contact.age = 29; + Map map = new HashMap(); + map.put("oldest friend", contact); + map.put("best friend", contact); +``` + +## 让重复字段生效 + +默认情况下,YAML在解析时是忽略重复字段的。例如,以下情况: + +```yaml + name: Nathan Sweet + age: 28 + address: + line1: 485 Madison Ave S + line1: 711 3rd Ave S + line2: NYC +``` + + +上面的YAML将会为`address`的`line1`字段赋值为`711 3rd Ave S`而不是`485 Madison Ave S`。这是因为上面YAML中的字段line1是重复的,因此line1的最后一个值将会被保留。但是,如果你的业务逻辑要求重复字段都生效,那么你可以在YamlConfig类进行设置。以下是设置方法: + +```java + try { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.setAllowDuplicates(false); // default value is true + YamlReader reader = new YamlReader(new FileReader("contact.yml"), yamlConfig); + Object object = reader.read(); + System.out.println(object); + Map map = (Map)object; + System.out.println(map.get("address")); + } catch (YamlException ex) { + ex.printStackTrace(); + // or handle duplicate key case here according to your business logic + } +``` + +上面的代码不会打印任何东西,但会在第5行抛出`YamlReaderException`异常说`Duplicate key found 'line1'` + +## 体系结构 + +YAML的tokenizer,parser,emitter组件是基于JvYAML项目中的。这些功能已被重构,修复bug等。由于JvYAML的复杂性,剩下部分未被使用。 YamlBeans努力于实现简单可行的事情---让使用Java操作YAML数据格式变得更加容易。 + +YamlBeans 支持 YAML 1.0 和 1.1 版本 。 + +## 更多信息 + +有关YamlConfig类的其他更多功能,请参阅javadocs + diff --git a/README.md b/README.md index cc886c0..6c94e08 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Please use the [YamlBeans discussion group](http://groups.google.com/group/yamlbeans-users) for support. +Chinese document [中文翻译文档](CN_README.md) . + ## Overview YamlBeans makes it easy to serialize and deserialize Java object graphs to and from YAML, a human-friendly data format. Replace XML and properties files with YAML for more expressive power (lists, maps, anchors, etc) and easier hand-editing. From 8f587080bf518956eac4a921f9f958c2ed3de0ee Mon Sep 17 00:00:00 2001 From: Gihwan Kim <0x1306e6d@gmail.com> Date: Thu, 1 Jun 2017 23:38:51 +0900 Subject: [PATCH 032/114] Extract literal unicodes to CONSTANT (#78) --- .../yamlbeans/constants/Unicode.java | 34 ++++++++++++++ .../yamlbeans/emitter/EmitterWriter.java | 46 ++++++++++--------- .../yamlbeans/emitter/ScalarAnalysis.java | 8 ++-- 3 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 src/com/esotericsoftware/yamlbeans/constants/Unicode.java diff --git a/src/com/esotericsoftware/yamlbeans/constants/Unicode.java b/src/com/esotericsoftware/yamlbeans/constants/Unicode.java new file mode 100644 index 0000000..3581d7f --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/constants/Unicode.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008 Nathan Sweet, Copyright (c) 2006 Ola Bini + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.esotericsoftware.yamlbeans.constants; + +public final class Unicode { + + static public final char BELL = '\u0007'; + static public final char BACKSPACE = '\u0008'; + static public final char HORIZONTAL_TABULATION = '\u0009'; + static public final char VERTICAL_TABULATION = '\u000b'; + static public final char FORM_FEED = '\u000c'; + static public final char ESCAPE = '\u001b'; + static public final char SPACE = '\u0020'; + static public final char TILDE = '\u007e'; + static public final char NEXT_LINE = '\u0085'; + static public final char NO_BREAK_SPACE = '\u00a0'; + + private Unicode() { + } +} diff --git a/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java b/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java index fd018c2..fd9efa9 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/EmitterWriter.java @@ -21,24 +21,26 @@ import java.util.HashMap; import java.util.Map; +import com.esotericsoftware.yamlbeans.constants.Unicode; + /** @author Nathan Sweet * @author Ola Bini */ class EmitterWriter { private static final Map ESCAPE_REPLACEMENTS = new HashMap(); static { ESCAPE_REPLACEMENTS.put((int)'\0', "0"); - ESCAPE_REPLACEMENTS.put((int)'\u0007', "a"); - ESCAPE_REPLACEMENTS.put((int)'\u0008', "b"); - ESCAPE_REPLACEMENTS.put((int)'\u0009', "t"); + ESCAPE_REPLACEMENTS.put((int)Unicode.BELL, "a"); + ESCAPE_REPLACEMENTS.put((int)Unicode.BACKSPACE, "b"); + ESCAPE_REPLACEMENTS.put((int)Unicode.HORIZONTAL_TABULATION, "t"); ESCAPE_REPLACEMENTS.put((int)'\n', "n"); - ESCAPE_REPLACEMENTS.put((int)'\u000b', "v"); - ESCAPE_REPLACEMENTS.put((int)'\u000c', "f"); + ESCAPE_REPLACEMENTS.put((int)Unicode.VERTICAL_TABULATION, "v"); + ESCAPE_REPLACEMENTS.put((int)Unicode.FORM_FEED, "f"); ESCAPE_REPLACEMENTS.put((int)'\r', "r"); - ESCAPE_REPLACEMENTS.put((int)'\u001b', "e"); + ESCAPE_REPLACEMENTS.put((int)Unicode.ESCAPE, "e"); ESCAPE_REPLACEMENTS.put((int)'"', "\""); ESCAPE_REPLACEMENTS.put((int)'\\', "\\"); - ESCAPE_REPLACEMENTS.put((int)'\u0085', "N"); - ESCAPE_REPLACEMENTS.put((int)'\u00a0', "_"); + ESCAPE_REPLACEMENTS.put((int)Unicode.NEXT_LINE, "N"); + ESCAPE_REPLACEMENTS.put((int)Unicode.NO_BREAK_SPACE, "_"); } private final Writer writer; @@ -105,7 +107,7 @@ public void writeDoubleQuoted (String text, boolean split, int indent, int wrapC while (ending <= text.length()) { int ch = 0; if (ending < text.length()) ch = text.codePointAt(ending); - if (ch == 0 || "\"\\\u0085".indexOf(ch) != -1 || !('\u0020' <= ch && ch <= '\u007E')) { + if (ch == 0 || "\"\\\u0085".indexOf(ch) != -1 || !(Unicode.SPACE <= ch && ch <= Unicode.TILDE)) { if (start < ending) { data = text.substring(start, ending); column += data.length(); @@ -180,7 +182,7 @@ public void writeSingleQuoted (String text, boolean split, int indent, int wrapC start = ending; } } else if (breaks) { - if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) { + if (ceh == 0 || !('\n' == ceh || Unicode.NEXT_LINE == ceh)) { data = text.substring(start, ending); for (int i = 0, j = data.length(); i < j; i++) { char cha = data.charAt(i); @@ -192,7 +194,7 @@ public void writeSingleQuoted (String text, boolean split, int indent, int wrapC writeIndent(indent); start = ending; } - } else if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) { + } else if (ceh == 0 || !('\n' == ceh || Unicode.NEXT_LINE == ceh)) { if (start < ending) { data = text.substring(start, ending); column += data.length(); @@ -208,7 +210,7 @@ public void writeSingleQuoted (String text, boolean split, int indent, int wrapC } if (ceh != 0) { spaces = ceh == ' '; - breaks = ceh == '\n' || ceh == '\u0085'; + breaks = ceh == '\n' || ceh == Unicode.NEXT_LINE; } ending++; } @@ -228,7 +230,7 @@ public void writeFolded (String text, int indent, int wrapColumn) throws IOExcep char ceh = 0; if (ending < text.length()) ceh = text.charAt(ending); if (breaks) { - if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) { + if (ceh == 0 || !('\n' == ceh || Unicode.NEXT_LINE == ceh)) { if (!leadingSpace && ceh != 0 && ceh != ' ' && text.charAt(start) == '\n') writeLineBreak(null); leadingSpace = ceh == ' '; data = text.substring(start, ending); @@ -253,14 +255,14 @@ public void writeFolded (String text, int indent, int wrapColumn) throws IOExcep } start = ending; } - } else if (ceh == 0 || ' ' == ceh || '\n' == ceh || '\u0085' == ceh) { + } else if (ceh == 0 || ' ' == ceh || '\n' == ceh || Unicode.NEXT_LINE == ceh) { data = text.substring(start, ending); writer.write(data); if (ceh == 0) writeLineBreak(null); start = ending; } if (ceh != 0) { - breaks = '\n' == ceh || '\u0085' == ceh; + breaks = '\n' == ceh || Unicode.NEXT_LINE == ceh; spaces = ceh == ' '; } ending++; @@ -278,7 +280,7 @@ public void writeLiteral (String text, int indent) throws IOException { char ceh = 0; if (ending < text.length()) ceh = text.charAt(ending); if (breaks) { - if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) { + if (ceh == 0 || !('\n' == ceh || Unicode.NEXT_LINE == ceh)) { data = text.substring(start, ending); for (int i = 0, j = data.length(); i < j; i++) { char cha = data.charAt(i); @@ -290,13 +292,13 @@ public void writeLiteral (String text, int indent) throws IOException { if (ceh != 0) writeIndent(indent); start = ending; } - } else if (ceh == 0 || '\n' == ceh || '\u0085' == ceh) { + } else if (ceh == 0 || '\n' == ceh || Unicode.NEXT_LINE == ceh) { data = text.substring(start, ending); writer.write(data); if (ceh == 0) writeLineBreak(null); start = ending; } - if (ceh != 0) breaks = '\n' == ceh || '\u0085' == ceh; + if (ceh != 0) breaks = '\n' == ceh || Unicode.NEXT_LINE == ceh; ending++; } } @@ -330,7 +332,7 @@ public void writePlain (String text, boolean split, int indent, int wrapColumn) start = ending; } } else if (breaks) { - if (ceh != '\n' && ceh != '\u0085') { + if (ceh != '\n' && ceh != Unicode.NEXT_LINE) { if (text.charAt(start) == '\n') writeLineBreak(null); data = text.substring(start, ending); for (int i = 0, j = data.length(); i < j; i++) { @@ -345,7 +347,7 @@ public void writePlain (String text, boolean split, int indent, int wrapColumn) indentation = false; start = ending; } - } else if (ceh == 0 || ' ' == ceh || '\n' == ceh || '\u0085' == ceh) { + } else if (ceh == 0 || ' ' == ceh || '\n' == ceh || Unicode.NEXT_LINE == ceh) { data = text.substring(start, ending); column += data.length(); writer.write(data); @@ -353,7 +355,7 @@ public void writePlain (String text, boolean split, int indent, int wrapColumn) } if (ceh != 0) { spaces = ceh == ' '; - breaks = ceh == '\n' || ceh == '\u0085'; + breaks = ceh == '\n' || ceh == Unicode.NEXT_LINE; } ending++; } @@ -377,7 +379,7 @@ private String determineChomp (String text) { tail = " " + tail; char ceh = tail.charAt(tail.length() - 1); char ceh2 = tail.charAt(tail.length() - 2); - return ceh == '\n' || ceh == '\u0085' ? ceh2 == '\n' || ceh2 == '\u0085' ? "+" : "" : "-"; + return ceh == '\n' || ceh == Unicode.NEXT_LINE ? ceh2 == '\n' || ceh2 == Unicode.NEXT_LINE ? "+" : "" : "-"; } public void close () throws IOException { diff --git a/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java b/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java index 8a94f07..02b78f4 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/ScalarAnalysis.java @@ -18,6 +18,8 @@ import java.util.regex.Pattern; +import com.esotericsoftware.yamlbeans.constants.Unicode; + /** @author Nathan Sweet * @author Ola Bini */ class ScalarAnalysis { @@ -106,11 +108,11 @@ static public ScalarAnalysis analyze (String scalar, boolean escapeUnicode) { blockIndicators = true; } } - if (ceh == '\n' || '\u0085' == ceh) lineBreaks = true; + if (ceh == '\n' || Unicode.NEXT_LINE == ceh) lineBreaks = true; if (escapeUnicode) { - if (ceh != '\n' && ceh != '\t' && !('\u0020' <= ceh && ceh <= '\u007E')) specialCharacters = true; + if (ceh != '\n' && ceh != '\t' && !(Unicode.SPACE <= ceh && ceh <= Unicode.TILDE)) specialCharacters = true; } - if (' ' == ceh || '\n' == ceh || '\u0085' == ceh) { + if (' ' == ceh || '\n' == ceh || Unicode.NEXT_LINE == ceh) { if (spaces && breaks) { if (ceh != ' ') mixed = true; } else if (spaces) { From cf4ff1d36bb6156d113390e99cc120defd6ba0cc Mon Sep 17 00:00:00 2001 From: zebrafive Date: Tue, 27 Jun 2017 09:59:17 +1000 Subject: [PATCH 033/114] YamlReader maintains entry order when reading a map from yaml (#83) * Added test to verify YamlReader maintains entry order when reading a Map. * YamlReader now uses LinkedHashMap rather than HashMap in order to maintain map entry order. --- .../yamlbeans/YamlReader.java | 9 +++----- .../yamlbeans/YamlReaderTest.java | 21 ++++++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 52aba27..79b052e 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -34,10 +34,7 @@ import java.io.StringReader; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; /** Deserializes Java objects from YAML. @@ -200,7 +197,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) Event event = parser.peekNextEvent(); switch (event.type) { case MAPPING_START: - type = HashMap.class; + type = LinkedHashMap.class; break; case SCALAR: type = String.class; @@ -430,4 +427,4 @@ public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); } -} \ No newline at end of file +} diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 207ee6c..896a86f 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -17,6 +17,7 @@ package com.esotericsoftware.yamlbeans; import java.io.StringReader; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -156,6 +157,24 @@ public void testCyclicReferences () throws Exception { assertTrue(root.right.right == null); } + public void testReaderMaintainsOrderWhenReadingMap() throws Exception { + YamlReader reader = new YamlReader("a: b\nkey1: value1\nc: d\n"); + Map map = (Map)reader.read(); + Iterator entrySetIterator = map.entrySet().iterator(); + + Map.Entry mapEntry = (Map.Entry)entrySetIterator.next(); + assertEquals(mapEntry.getKey(), "a"); + assertEquals(mapEntry.getValue(), "b"); + + mapEntry = (Map.Entry)entrySetIterator.next(); + assertEquals(mapEntry.getKey(), "key1"); + assertEquals(mapEntry.getValue(), "value1"); + + mapEntry = (Map.Entry)entrySetIterator.next(); + assertEquals(mapEntry.getKey(), "c"); + assertEquals(mapEntry.getValue(), "d"); + } + private Test read (String yaml) throws Exception { if (true) { System.out.println(yaml); @@ -428,4 +447,4 @@ public void testIgnoreTypeTagsEmbedded () throws YamlException { assertEquals(1, lake.fish.size()); assertEquals("Walleye", lake.fish.get(0).species); } -} \ No newline at end of file +} From 9b878d7c87a67cff106ed9ea7db0cae2275c3ccb Mon Sep 17 00:00:00 2001 From: Czytx <973383143@qq.com> Date: Sun, 24 Sep 2017 20:15:23 +0800 Subject: [PATCH 034/114] modify 1024 (#86) --- src/com/esotericsoftware/yamlbeans/emitter/Emitter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 30436d0..07c8c7e 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -478,7 +478,7 @@ boolean checkSimpleKey () { length += analysis.scalar.length(); } - return length < 128 + return length < 1024 && (event.type == ALIAS || event.type == SCALAR && !analysis.empty && !analysis.multiline || checkEmptySequence() || checkEmptyMapping()); } From 76466f56ce7be2aecacfc54acf34195b567db98d Mon Sep 17 00:00:00 2001 From: ericvergnaud Date: Mon, 2 Oct 2017 16:52:27 +0800 Subject: [PATCH 035/114] Support for direct modification of a yaml document (#88) * clean start * get junit dependency from pom * Ensure constant Version is used * Support for YamlElement * Yaml elements which represent parsed Yaml events * rename package * add toString for debug * add Yaml document manipulation methods * more setters * Revert "clean start" This reverts commit c9cc01be998786d6c936befeb393a2ac74e59b30. * Revert "get junit dependency from pom" This reverts commit 87add49866daa221dddb502b7b18b9582691cf4b. * bump SNAPSHOT version * Update Version.java --- pom.xml | 2 +- .../esotericsoftware/yamlbeans/Version.java | 3 + .../yamlbeans/YamlWriter.java | 10 +- .../yamlbeans/document/YamlAlias.java | 21 +++ .../yamlbeans/document/YamlDocument.java | 27 +++ .../document/YamlDocumentReader.java | 175 ++++++++++++++++++ .../yamlbeans/document/YamlElement.java | 31 ++++ .../yamlbeans/document/YamlEntry.java | 65 +++++++ .../yamlbeans/document/YamlMapping.java | 144 ++++++++++++++ .../yamlbeans/document/YamlScalar.java | 50 +++++ .../yamlbeans/document/YamlSequence.java | 124 +++++++++++++ .../yamlbeans/document/YamlDocumentTest.java | 157 ++++++++++++++++ 12 files changed, 806 insertions(+), 3 deletions(-) create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlAlias.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlDocument.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlElement.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlEntry.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlMapping.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlScalar.java create mode 100644 src/com/esotericsoftware/yamlbeans/document/YamlSequence.java create mode 100644 test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java diff --git a/pom.xml b/pom.xml index d7383aa..26f7da5 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.12 + 1.13-SNAPSHOT Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans diff --git a/src/com/esotericsoftware/yamlbeans/Version.java b/src/com/esotericsoftware/yamlbeans/Version.java index 116261c..b1bf27a 100644 --- a/src/com/esotericsoftware/yamlbeans/Version.java +++ b/src/com/esotericsoftware/yamlbeans/Version.java @@ -19,6 +19,9 @@ /** Represents the version of a YAML document. * @author Nathan Sweet */ public class Version { + + public static final Version DEFAULT_VERSION = new Version(1, 1); + public final int major; public final int minor; diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index fd9db01..9ca11b6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -33,6 +34,8 @@ import com.esotericsoftware.yamlbeans.Beans.Property; import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.document.YamlDocument; +import com.esotericsoftware.yamlbeans.document.YamlElement; import com.esotericsoftware.yamlbeans.emitter.Emitter; import com.esotericsoftware.yamlbeans.emitter.EmitterException; import com.esotericsoftware.yamlbeans.parser.AliasEvent; @@ -137,11 +140,14 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla boolean isRoot = this.isRoot; this.isRoot = false; - if (object == null) { + if(object instanceof YamlElement) { + ((YamlElement)object).emitEvent(emitter, config.writeConfig); + return; + } else if (object == null) { emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); return; } - + Class valueClass = object.getClass(); boolean unknownType = fieldClass == null; if (unknownType) fieldClass = valueClass; diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlAlias.java b/src/com/esotericsoftware/yamlbeans/document/YamlAlias.java new file mode 100644 index 0000000..0078fb8 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlAlias.java @@ -0,0 +1,21 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; + +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.AliasEvent; + +public class YamlAlias extends YamlElement { + + @Override + public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { + emitter.emit(new AliasEvent(anchor)); + } + + @Override + public String toString() { + return "*" + anchor; + } +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java b/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java new file mode 100644 index 0000000..8b8be9b --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java @@ -0,0 +1,27 @@ +package com.esotericsoftware.yamlbeans.document; + +import com.esotericsoftware.yamlbeans.YamlException; + +public interface YamlDocument { + + String getTag(); + int size(); + YamlEntry getEntry(String key) throws YamlException; + YamlEntry getEntry(int index) throws YamlException; + boolean deleteEntry(String key) throws YamlException; + void setEntry(String key, boolean value) throws YamlException; + void setEntry(String key, Number value) throws YamlException; + void setEntry(String key, String value) throws YamlException; + void setEntry(String key, YamlElement value) throws YamlException; + YamlElement getElement(int item) throws YamlException; + void deleteElement(int element) throws YamlException; + void setElement(int item, boolean value) throws YamlException; + void setElement(int item, Number value) throws YamlException; + void setElement(int item, String value) throws YamlException; + void setElement(int item, YamlElement element) throws YamlException; + void addElement(boolean value) throws YamlException; + void addElement(Number value) throws YamlException; + void addElement(String value) throws YamlException; + void addElement(YamlElement element) throws YamlException; + +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java new file mode 100644 index 0000000..b2ebd99 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java @@ -0,0 +1,175 @@ +package com.esotericsoftware.yamlbeans.document; + +import static com.esotericsoftware.yamlbeans.parser.EventType.*; + +import java.io.Reader; +import java.io.StringReader; + +import com.esotericsoftware.yamlbeans.Version; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.parser.AliasEvent; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; +import com.esotericsoftware.yamlbeans.parser.Parser; +import com.esotericsoftware.yamlbeans.parser.Parser.ParserException; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; +import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; +import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; + +public class YamlDocumentReader { + + Parser parser; + + public YamlDocumentReader(String yaml) { + this(new StringReader(yaml)); + } + + public YamlDocumentReader(String yaml, Version version) { + this(new StringReader(yaml), version); + } + + public YamlDocumentReader(Reader reader) { + this(reader, null); + } + + public YamlDocumentReader(Reader reader, Version version) { + if(version==null) + version = Version.DEFAULT_VERSION; + parser = new Parser(reader, version); + } + + public YamlDocument read() throws YamlException { + try { + while (true) { + Event event = parser.peekNextEvent(); + if (event == null) + return null; + switch (event.type) { + case STREAM_START: + parser.getNextEvent(); // consume it + break; + case STREAM_END: + parser.getNextEvent(); // consume it + return null; + case DOCUMENT_START: + parser.getNextEvent(); // consume it + return readDocument(); + default: + throw new IllegalStateException(); + } + } + } catch (ParserException ex) { + throw new YamlException("Error parsing YAML.", ex); + } catch (TokenizerException ex) { + throw new YamlException("Error tokenizing YAML.", ex); + } + + } + + private YamlDocument readDocument() { + Event event = parser.peekNextEvent(); + switch (event.type) { + case MAPPING_START: + return readMapping(); + case SEQUENCE_START: + return readSequence(); + default: + throw new IllegalStateException(); + } + } + + private YamlMapping readMapping() { + Event event = parser.getNextEvent(); + if(event.type!=MAPPING_START) + throw new IllegalStateException(); + YamlMapping element = new YamlMapping(); + MappingStartEvent mapping = (MappingStartEvent)event; + element.setTag(mapping.tag); + element.setAnchor(mapping.anchor); + readMappingElements(element); + return element; + } + + private void readMappingElements(YamlMapping mapping) { + while(true) { + Event event = parser.peekNextEvent(); + if(event.type == MAPPING_END) { + parser.getNextEvent(); // consume it + return; + } else { + YamlEntry entry = readEntry(); + mapping.addEntry(entry); + } + } + } + + private YamlEntry readEntry() { + YamlScalar scalar = readScalar(); + YamlElement value = readValue(); + return new YamlEntry(scalar, value); + } + + private YamlElement readValue() { + Event event = parser.peekNextEvent(); + switch(event.type) { + case SCALAR: + return readScalar(); + case ALIAS: + return readAlias(); + case MAPPING_START: + return readMapping(); + case SEQUENCE_START: + return readSequence(); + default: + throw new IllegalStateException(); + } + } + + private YamlAlias readAlias() { + Event event = parser.getNextEvent(); + if(event.type!=ALIAS) + throw new IllegalStateException(); + YamlAlias element = new YamlAlias(); + AliasEvent alias = (AliasEvent)event; + element.setAnchor(alias.anchor); + return element; + } + + private YamlSequence readSequence() { + Event event = parser.getNextEvent(); + if(event.type!=SEQUENCE_START) + throw new IllegalStateException(); + YamlSequence element = new YamlSequence(); + SequenceStartEvent sequence = (SequenceStartEvent)event; + element.setTag(sequence.tag); + element.setAnchor(sequence.anchor); + readSequenceElements(element); + return element; + } + + private void readSequenceElements(YamlSequence sequence) { + while(true) { + Event event = parser.peekNextEvent(); + if(event.type==SEQUENCE_END) { + parser.getNextEvent(); // consume it + return; + } else { + YamlElement element = readValue(); + sequence.addElement(element); + } + } + } + + private YamlScalar readScalar() { + Event event = parser.getNextEvent(); + if(event.type!= SCALAR) + throw new IllegalStateException(); + ScalarEvent scalar = (ScalarEvent)event; + YamlScalar element = new YamlScalar(); + element.setTag(scalar.tag); + element.setAnchor(scalar.anchor); + element.setValue(scalar.value); + return element; + } + +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlElement.java b/src/com/esotericsoftware/yamlbeans/document/YamlElement.java new file mode 100644 index 0000000..b350aa5 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlElement.java @@ -0,0 +1,31 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; + +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; + +public abstract class YamlElement { + + String tag; + String anchor; + + public void setTag(String tag) { + this.tag = tag; + } + + public void setAnchor(String anchor) { + this.anchor = anchor; + } + + public String getTag() { + return tag; + } + + public String getAnchor() { + return anchor; + } + + public abstract void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException; +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java b/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java new file mode 100644 index 0000000..e290e6d --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java @@ -0,0 +1,65 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; + +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; + +public class YamlEntry { + + YamlScalar key; + YamlElement value; + + public YamlEntry(YamlScalar key, YamlElement value) { + this.key = key; + this.value = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(key.toString()); + sb.append(':'); + sb.append(value.toString()); + return sb.toString(); + } + + public YamlScalar getKey() { + return key; + } + + public YamlElement getValue() { + return value; + } + + public void setKey(YamlScalar key) { + this.key = key; + } + + public void setValue(YamlElement value) { + this.value = value; + } + + public void setValue(boolean value) { + this.value = new YamlScalar(value); + } + + public void setValue(Number value) { + this.value = new YamlScalar(value); + } + + public void setValue(String value) { + this.value = new YamlScalar(value); + } + + public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { + key.emitEvent(emitter, config); + if(value==null) + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); + else + value.emitEvent(emitter, config); + } + +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java new file mode 100644 index 0000000..166c6ee --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java @@ -0,0 +1,144 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; + +public class YamlMapping extends YamlElement implements YamlDocument { + + // use a list to keep the sequence + List entries = new LinkedList(); + + public int size() { + return entries.size(); + } + + public void addEntry(YamlEntry entry) { + entries.add(entry); + } + + public boolean deleteEntry(String key) { + for(int index = 0; index < entries.size(); index++) { + if(key.equals(entries.get(index).getKey().getValue())) { + entries.remove(index); + return true; + } + } + return false; + } + + public YamlEntry getEntry(String key) throws YamlException { + for(YamlEntry entry : entries) { + if(key.equals(entry.getKey().getValue())) + return entry; + } + return null; + } + + public YamlEntry getEntry(int index) throws YamlException { + return entries.get(index); + } + + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + if(anchor!=null) { + sb.append('&'); + sb.append(anchor); + sb.append(' '); + } + if(tag!=null) { + sb.append(" !"); + sb.append(tag); + } + if(!entries.isEmpty()) { + sb.append('{'); + for(YamlEntry entry : entries) { + sb.append(entry.toString()); + sb.append(','); + } + sb.setLength(sb.length() - 1); + sb.append('}'); + } + return sb.toString(); + } + + @Override + public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { + emitter.emit(new MappingStartEvent(anchor, tag, tag==null, false)); + for(YamlEntry entry : entries) + entry.emitEvent(emitter, config); + emitter.emit(Event.MAPPING_END); + } + + public void setEntry(String key, boolean value) throws YamlException { + setEntry(key, new YamlScalar(value)); + } + + public void setEntry(String key, Number value) throws YamlException { + setEntry(key, new YamlScalar(value)); + } + + public void setEntry(String key, String value) throws YamlException { + setEntry(key, new YamlScalar(value)); + } + + public void setEntry(String key, YamlElement value) throws YamlException { + YamlEntry entry = getEntry(key); + if(entry!=null) + entry.setValue(value); + else { + entry = new YamlEntry(new YamlScalar(key), value); + addEntry(entry); + } + + } + + public YamlElement getElement(int item) throws YamlException { + throw new YamlException("Can only get element on sequence!"); + } + + public void deleteElement(int element) throws YamlException { + throw new YamlException("Can only delete element on sequence!"); + } + + public void setElement(int item, boolean element) throws YamlException { + throw new YamlException("Can only set element on sequence!"); + } + + public void setElement(int item, Number element) throws YamlException { + throw new YamlException("Can only set element on sequence!"); + } + + public void setElement(int item, String element) throws YamlException { + throw new YamlException("Can only set element on sequence!"); + } + + public void setElement(int item, YamlElement element) throws YamlException { + throw new YamlException("Can only set element on sequence!"); + } + + public void addElement(boolean element) throws YamlException { + throw new YamlException("Can only add element on sequence!"); + } + + public void addElement(Number element) throws YamlException { + throw new YamlException("Can only add element on sequence!"); + } + + public void addElement(String element) throws YamlException { + throw new YamlException("Can only add element on sequence!"); + } + + public void addElement(YamlElement element) throws YamlException { + throw new YamlException("Can only add element on sequence!"); + } +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java b/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java new file mode 100644 index 0000000..84be98a --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java @@ -0,0 +1,50 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; + +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; + +public class YamlScalar extends YamlElement { + + String value; + + public YamlScalar() { + } + + public YamlScalar(Object value) { + this.value = String.valueOf(value); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if(anchor!=null) { + sb.append('&'); + sb.append(anchor); + sb.append(' '); + } + sb.append(value); + if(tag!=null) { + sb.append(" !"); + sb.append(tag); + } + return sb.toString(); + } + + @Override + public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { + char style = 0; // TODO determine style + emitter.emit(new ScalarEvent(anchor, tag, new boolean[] {true, true}, value, style)); + } +} diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java new file mode 100644 index 0000000..2f94862 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java @@ -0,0 +1,124 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.emitter.Emitter; +import com.esotericsoftware.yamlbeans.emitter.EmitterException; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; + +public class YamlSequence extends YamlElement implements YamlDocument { + + List elements = new LinkedList(); + + public int size() { + return elements.size(); + } + + public void addElement(YamlElement element) { + elements.add(element); + } + + public void deleteElement(int item) throws YamlException { + elements.remove(item); + } + + public YamlElement getElement(int item) throws YamlException { + return elements.get(item); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + if(anchor!=null) { + sb.append('&'); + sb.append(anchor); + sb.append(' '); + } + if(tag!=null) { + sb.append(" !"); + sb.append(tag); + } + if(!elements.isEmpty()) { + sb.append('['); + for(YamlElement element : elements) { + sb.append(element.toString()); + sb.append(','); + } + sb.setLength(sb.length() - 1); + sb.append(']'); + } + return sb.toString(); + } + + + @Override + public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { + emitter.emit(new SequenceStartEvent(anchor, tag, tag==null, false)); + for (YamlElement element : elements) + element.emitEvent(emitter, config); + emitter.emit(Event.SEQUENCE_END); + } + + public YamlEntry getEntry(String key) throws YamlException { + throw new YamlException("Can only get entry on mapping!"); + } + + public YamlEntry getEntry(int index) throws YamlException { + throw new YamlException("Can only get entry on mapping!"); + } + + public boolean deleteEntry(String key) throws YamlException { + throw new YamlException("Can only delete entry on mapping!"); + } + + public void setEntry(String key, boolean value) throws YamlException { + throw new YamlException("Can only set entry on mapping!"); + } + + public void setEntry(String key, Number value) throws YamlException { + throw new YamlException("Can only set entry on mapping!"); + } + + public void setEntry(String key, String value) throws YamlException { + throw new YamlException("Can only set entry on mapping!"); + } + + public void setEntry(String key, YamlElement value) throws YamlException { + throw new YamlException("Can only set entry on mapping!"); + } + + public void setElement(int item, boolean value) throws YamlException { + elements.set(item, new YamlScalar(value)); + } + + public void setElement(int item, Number value) throws YamlException { + elements.set(item, new YamlScalar(value)); + } + + public void setElement(int item, String value) throws YamlException { + elements.set(item, new YamlScalar(value)); + } + + public void setElement(int item, YamlElement element) throws YamlException { + elements.set(item, element); + } + + public void addElement(boolean value) throws YamlException { + elements.add(new YamlScalar(value)); + } + + public void addElement(Number value) throws YamlException { + elements.add(new YamlScalar(value)); + } + + public void addElement(String value) throws YamlException { + elements.add(new YamlScalar(value)); + } + + +} diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java new file mode 100644 index 0000000..7a5aba2 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -0,0 +1,157 @@ +package com.esotericsoftware.yamlbeans.document; + +import java.io.StringWriter; + +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlWriter; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; +import com.esotericsoftware.yamlbeans.document.YamlDocument; +import com.esotericsoftware.yamlbeans.document.YamlDocumentReader; + +import static org.junit.Assert.*; + +public class YamlDocumentTest { + + @Test + public void testThatTaggedDocumentIsCopied() throws Exception { + testEquals("--- !someTag\nscalar: value\n"); + } + + @Test + public void testThatScalarValueIsCopied() throws Exception { + testEquals("scalar: value\n"); + } + + @Test + public void testThatTaggedScalarValueIsCopied() throws Exception { + testEquals("scalar: value !someTag\n"); + } + + @Test + public void testThatAnchoredScalarValueIsCopied() throws Exception { + testEquals("scalar: &anchor value\n"); + } + + @Test + public void testThatAliasedScalarValueIsCopied() throws Exception { + testEquals("scalar: *alias\n"); + } + + @Test + public void testThatSequenceValueIsCopied() throws Exception { + testEquals("- scalar1: value\n- scalar2: value\n"); + } + + @Test + public void testThatTaggedSequenceValueIsCopied() throws Exception { + testEquals("- scalar1: value !someTag1\n- scalar2: value !someTag2\n"); + } + + @Test + public void testThatAnchoredSequenceValueIsCopied() throws Exception { + testEquals("sequence: &anchor\n- scalar1: value\n- scalar2: value\n"); + } + + @Test + public void testThatAliasedSequenceValueIsCopied() throws Exception { + testEquals("sequence: *alias\n"); + } + + @Test + public void testThatMappingValueIsCopied() throws Exception { + testEquals("mapping: \n scalar1: value\n scalar2: value\n"); + } + + @Test + public void testThatTaggedMappingValueIsCopied() throws Exception { + testEquals("mapping: !someTag1\n scalar1: value\n scalar2: value !someTag2\n"); + } + + + @Test + public void testThatAnchoredMappingValueIsCopied() throws Exception { + testEquals("mapping: &anchor\n scalar1: value\n scalar2: value\n"); + } + + @Test + public void testThatAliasedMappingValueIsCopied() throws Exception { + testEquals("mapping: *alias\n"); + } + + @Test + public void testThatMappingEntryIsChanged() throws YamlException { + YamlDocument yaml = readDocument("scalar: value\n"); + yaml.setEntry("scalar", 123); + String actual = writeDocument(yaml); + assertEquals("scalar: 123\n", actual); + } + + @Test + public void testThatMappingEntryIsAdded() throws YamlException { + YamlDocument yaml = readDocument("scalar: value\n"); + yaml.setEntry("scalar2", 123); + String actual = writeDocument(yaml); + assertEquals("scalar: value\nscalar2: 123\n", actual); + } + + @Test + public void testThatMappingEntryIsRemoved() throws YamlException { + YamlDocument yaml = readDocument("scalar: value\nscalar2: 123\n"); + yaml.deleteEntry("scalar2"); + String actual = writeDocument(yaml); + assertEquals("scalar: value\n", actual); + } + + @Test + public void testThatSequenceItemIsChanged() throws YamlException { + YamlDocument yaml = readDocument("- value\n"); + yaml.setElement(0, 123); + String actual = writeDocument(yaml); + assertEquals("- 123\n", actual); + } + + @Test + public void testThatSequenceItemIsAdded() throws YamlException { + YamlDocument yaml = readDocument("- value\n"); + yaml.addElement(123); + String actual = writeDocument(yaml); + assertEquals("- value\n- 123\n", actual); + } + + @Test + public void testThatSequenceItemIsRemoved() throws YamlException { + YamlDocument yaml = readDocument("- value\n- 123\n"); + yaml.deleteElement(0); + String actual = writeDocument(yaml); + assertEquals("- 123\n", actual); + } + + + private YamlDocument readDocument(String yaml) throws YamlException { + YamlDocumentReader reader = new YamlDocumentReader(yaml); + return reader.read(); + } + + private String writeDocument(YamlDocument yaml) throws YamlException { + StringWriter writer = new StringWriter(); + YamlConfig config = new YamlConfig(); + config.writeConfig.setExplicitFirstDocument(yaml.getTag()!=null); + config.writeConfig.setWriteClassname(WriteClassName.NEVER); + config.writeConfig.setAutoAnchor(false); + YamlWriter yamlWriter = new YamlWriter(writer, config); + yamlWriter.write(yaml); + yamlWriter.close(); + return writer.toString(); + } + + private void testEquals(String yaml) throws Exception { + YamlDocument document = readDocument(yaml); + String actual = writeDocument(document); + assertEquals(yaml, actual); + } + + +} From 095d83d58a835e838a0340f8abb8c8460cf64a71 Mon Sep 17 00:00:00 2001 From: ericvergnaud Date: Mon, 2 Oct 2017 16:53:37 +0800 Subject: [PATCH 036/114] Support for merge (#87) * use maven for dependencies * support for auto merge * Revert "use maven for dependencies" This reverts commit a1593b6b9ce3ca713fb31ce3e07936b7111c0657. * back to original setup --- .classpath | 35 ++++++++++++++----- .project | 6 ++++ .../yamlbeans/YamlConfig.java | 1 + .../yamlbeans/YamlReader.java | 23 +++++++++++- .../esotericsoftware/yamlbeans/MergeTest.java | 25 +++++++++++++ test/test-merge.yml | 6 ++++ 6 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/MergeTest.java create mode 100644 test/test-merge.yml diff --git a/.classpath b/.classpath index b569539..d18ea09 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,27 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project index c04efa1..70b24cd 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 4cd8177..870af33 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -224,6 +224,7 @@ static public class ReadConfig { ClassLoader classLoader; final Map constructorParameters = new IdentityHashMap(); boolean ignoreUnknownProperties; + boolean autoMerge = true; ReadConfig () { } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 79b052e..88ff5f0 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -326,7 +326,10 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (!config.allowDuplicates && ((Map) object).containsKey(key)) { throw new YamlReaderException("Duplicate key found '" + key + "'"); } - ((Map)object).put(key, value); + if(config.readConfig.autoMerge && "<<".equals(key) && value!=null) + mergeMap((Map)object, value); + else + ((Map)object).put(key, value); } else { // Set field on object. try { @@ -405,6 +408,24 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } } + /** see http://yaml.org/type/merge.html */ + @SuppressWarnings("unchecked") + private void mergeMap(Map dest, Object source) throws YamlReaderException { + if(source instanceof Collection) { + for(Object item : ((Collection)source)) + mergeMap(dest, item); + } else if(source instanceof Map) { + Map map = (Map)source; + for(Map.Entry entry : map.entrySet()) { + if(!dest.containsKey(entry.getKey())) + dest.put(entry.getKey(), entry.getValue()); + + } + } else + throw new YamlReaderException("Expected a mapping or a sequence of mappings for a '<<' merge field but found: " + source.getClass().getSimpleName()); + + } + /** Returns a new object of the requested type. */ protected Object createObject (Class type) throws InvocationTargetException { // Use deferred construction if a non-zero-arg constructor is available. diff --git a/test/com/esotericsoftware/yamlbeans/MergeTest.java b/test/com/esotericsoftware/yamlbeans/MergeTest.java new file mode 100644 index 0000000..7fb6145 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/MergeTest.java @@ -0,0 +1,25 @@ +package com.esotericsoftware.yamlbeans; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Map; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class MergeTest { + + @Test + public void testMerge() throws YamlException { + InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("test-merge.yml"); + Reader reader = new InputStreamReader(input); + Map data = new YamlReader(reader).read(Map.class); + Map stuff = (Map)data.get("merged"); + assertEquals("v1", stuff.get("v1")); + assertEquals("v2", stuff.get("v2")); + assertEquals("v3", stuff.get("v3")); + assertNull(stuff.get("<<")); + } + +} diff --git a/test/test-merge.yml b/test/test-merge.yml new file mode 100644 index 0000000..c32529c --- /dev/null +++ b/test/test-merge.yml @@ -0,0 +1,6 @@ +stuff: &anchor + v1: v1 + v2: v2 +merged: + <<: *anchor + v3: v3 \ No newline at end of file From 3f079472f63ea1b3bc6ea533f33b5f662fcfe3ae Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Mon, 2 Oct 2017 10:55:13 +0200 Subject: [PATCH 037/114] Reverted Eclipse project file changes. --- .classpath | 35 ++++++++--------------------------- .project | 6 ------ 2 files changed, 8 insertions(+), 33 deletions(-) diff --git a/.classpath b/.classpath index d18ea09..b569539 100644 --- a/.classpath +++ b/.classpath @@ -1,27 +1,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/.project b/.project index 70b24cd..c04efa1 100644 --- a/.project +++ b/.project @@ -10,14 +10,8 @@ - - org.eclipse.m2e.core.maven2Builder - - - - org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature From edfb84058edc90d52c68162cc2d871136b982a82 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 24 Oct 2017 18:57:39 +0200 Subject: [PATCH 038/114] Added setClassTags and setTagSuffix. --- .../yamlbeans/YamlConfig.java | 27 +++-- .../yamlbeans/YamlReader.java | 98 +++++++++++-------- 2 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 870af33..690beb4 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -177,9 +177,9 @@ public void setAutoAnchor (boolean autoAnchor) { this.autoAnchor = autoAnchor; } - /** If true, bean fields/properties are written in the same order as the fields are defined in the bean class. - * If false, they are sorted alphabetically. Default is false. */ - public void setKeepBeanPropertyOrder(boolean keepBeanPropertyOrder) { + /** If true, bean fields/properties are written in the same order as the fields are defined in the bean class. If false, + * they are sorted alphabetically. Default is false. */ + public void setKeepBeanPropertyOrder (boolean keepBeanPropertyOrder) { this.keepBeanPropertyOrder = keepBeanPropertyOrder; } @@ -225,6 +225,8 @@ static public class ReadConfig { final Map constructorParameters = new IdentityHashMap(); boolean ignoreUnknownProperties; boolean autoMerge = true; + boolean classTags = true; + String tagSuffix; ReadConfig () { } @@ -250,17 +252,28 @@ public void setConstructorParameters (Class type, Class[] parameterTypes, String try { parameters.constructor = type.getConstructor(parameterTypes); } catch (Exception ex) { - throw new IllegalArgumentException("Unable to find constructor: " + type.getName() + "(" - + Arrays.toString(parameterTypes) + ")", ex); + throw new IllegalArgumentException( + "Unable to find constructor: " + type.getName() + "(" + Arrays.toString(parameterTypes) + ")", ex); } parameters.parameterNames = parameterNames; constructorParameters.put(type, parameters); } - /** When true, fields in the YAML that are not found on the class will not throw a {@link YamlException}. Default is false. */ + /** When true, fields in the YAML that are not found on the class will not throw a {@link YamlException}. Default is + * false. */ public void setIgnoreUnknownProperties (boolean allowUnknownProperties) { this.ignoreUnknownProperties = allowUnknownProperties; } + + /** When false, tags are not used to look up classes. Default is true. */ + public void setClassTags (boolean classTags) { + this.classTags = classTags; + } + + /** When not null, YAML read into a {@link Map} stores any tags using key + tagSuffix. Default is null. */ + public void setTagSuffix (String tagSuffix) { + this.tagSuffix = tagSuffix; + } } static class ConstructorParameters { @@ -271,4 +284,4 @@ static class ConstructorParameters { public static enum WriteClassName { ALWAYS, NEVER, AUTO } -} \ No newline at end of file +} diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 88ff5f0..6df3619 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -18,6 +18,15 @@ import static com.esotericsoftware.yamlbeans.parser.EventType.*; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.Map.Entry; + import com.esotericsoftware.yamlbeans.Beans.Property; import com.esotericsoftware.yamlbeans.parser.AliasEvent; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; @@ -28,15 +37,6 @@ import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.util.*; -import java.util.Map.Entry; - /** Deserializes Java objects from YAML. * @author Nathan Sweet */ public class YamlReader { @@ -65,7 +65,8 @@ public YamlConfig getConfig () { return config; } - /** Return the object with the given alias, or null. This is only valid after objects have been read and before {@link #close()} */ + /** Return the object with the given alias, or null. This is only valid after objects have been read and before + * {@link #close()} */ public Object get (String alias) { return anchors.get(alias); } @@ -106,8 +107,8 @@ public T read (Class type, Class elementType) throws YamlException { } /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. */ - protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, - TokenizerException { + protected Object readValue (Class type, Class elementType, Class defaultType) + throws YamlException, ParserException, TokenizerException { String tag = null, anchor = null; Event event = parser.peekNextEvent(); @@ -130,19 +131,19 @@ protected Object readValue (Class type, Class elementType, Class defaultType) th default: } - return readValueInternal(this.chooseType(tag, defaultType, type), elementType, anchor); + return readValueInternal(this.chooseType(tag, defaultType, type), elementType, anchor); } - - private Class chooseType(String tag, Class defaultType, Class providedType) throws YamlReaderException { - if (tag != null) { + + private Class chooseType (String tag, Class defaultType, Class providedType) throws YamlReaderException { + if (tag != null && config.readConfig.classTags) { Class userConfiguredByTag = config.tagToClass.get(tag); if (userConfiguredByTag != null) { return userConfiguredByTag; } - + ClassLoader classLoader = (config.readConfig.classLoader == null ? this.getClass().getClassLoader() - : config.readConfig.classLoader); - + : config.readConfig.classLoader); + try { Class loadedFromTag = findTagClass(tag, classLoader); if (loadedFromTag != null) { @@ -152,26 +153,23 @@ private Class chooseType(String tag, Class defaultType, Class providedT throw new YamlReaderException("Unable to find class specified by tag: " + tag); } } - + if (defaultType != null) { return defaultType; } - + // This may be null. return providedType; } - - /** - * Used during reading when a tag is present, and - * {@link YamlConfig#setClassTag(String, Class)} was not used for that tag. + + /** Used during reading when a tag is present, and {@link YamlConfig#setClassTag(String, Class)} was not used for that tag. * Attempts to load the class corresponding to that tag. * - * If this returns a non-null Class, that will be used as the - * deserialization type regardless of whether a type was explicitly asked - * for or if a default type exists. + * If this returns a non-null Class, that will be used as the deserialization type regardless of whether a type was explicitly + * asked for or if a default type exists. * - * If this returns null, no guidance will be provided by the tag and we will - * fall back to the default type or a requested target type, if any exist. + * If this returns null, no guidance will be provided by the tag and we will fall back to the default type or a requested + * target type, if any exist. * * If this throws a ClassNotFoundException, parsing will fail. * @@ -183,16 +181,14 @@ private Class chooseType(String tag, Class defaultType, Class providedT * * and never returns null. * - * You can override this to handle cases where you do not want to respect - * the type tags found in a document - e.g., if they were output by another - * program using classes that do not exist on your classpath. - */ - protected Class findTagClass(String tag, ClassLoader classLoader) throws ClassNotFoundException { + * You can override this to handle cases where you do not want to respect the type tags found in a document - e.g., if they + * were output by another program using classes that do not exist on your classpath. */ + protected Class findTagClass (String tag, ClassLoader classLoader) throws ClassNotFoundException { return Class.forName(tag, true, classLoader); } - private Object readValueInternal (Class type, Class elementType, String anchor) throws YamlException, ParserException, - TokenizerException { + private Object readValueInternal (Class type, Class elementType, String anchor) + throws YamlException, ParserException, TokenizerException { if (type == null || type == Object.class) { Event event = parser.peekNextEvent(); switch (event.type) { @@ -220,8 +216,8 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (Beans.isScalar(type)) { Event event = parser.getNextEvent(); - if (event.type != SCALAR) - throw new YamlReaderException("Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); + if (event.type != SCALAR) throw new YamlReaderException( + "Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); String value = ((ScalarEvent)event).value; try { Object convertedValue; @@ -284,9 +280,8 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (entry.getKey().isAssignableFrom(type)) { ScalarSerializer serializer = entry.getValue(); Event event = parser.getNextEvent(); - if (event.type != SCALAR) - throw new YamlReaderException("Expected scalar for type '" + type + "' to be deserialized by scalar serializer '" - + serializer.getClass().getName() + "' but found: " + event.type); + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for type '" + type + + "' to be deserialized by scalar serializer '" + serializer.getClass().getName() + "' but found: " + event.type); Object value = serializer.read(((ScalarEvent)event).value); if (anchor != null) anchors.put(anchor, value); return value; @@ -322,8 +317,20 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } if (object instanceof Map) { // Add to map. + if (config.readConfig.tagSuffix != null) { + Event nextEvent = parser.peekNextEvent(); + switch (nextEvent.type) { + case MAPPING_START: + case SEQUENCE_START: + ((Map)object).put(key + config.readConfig.tagSuffix, ((CollectionStartEvent)nextEvent).tag); + break; + case SCALAR: + ((Map)object).put(key + config.readConfig.tagSuffix, ((ScalarEvent)nextEvent).tag); + break; + } + } if (!isExplicitKey) value = readValue(elementType, null, null); - if (!config.allowDuplicates && ((Map) object).containsKey(key)) { + if (!config.allowDuplicates && ((Map)object).containsKey(key)) { throw new YamlReaderException("Duplicate key found '" + key + "'"); } if(config.readConfig.autoMerge && "<<".equals(key) && value!=null) @@ -444,6 +451,11 @@ public YamlReaderException (String message) { } } + static public class Meow { + public String tag; + public String value; + } + public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); From d32e8a8cdecb093c976492ce088684d136a19d5a Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Thu, 26 Oct 2017 09:28:42 +0200 Subject: [PATCH 039/114] Support writing Map value tags read with tagSuffix. --- .../yamlbeans/YamlConfig.java | 13 +++--- .../yamlbeans/YamlReader.java | 39 ++++++++-------- .../yamlbeans/YamlWriter.java | 44 ++++++++++++++----- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 690beb4..387281f 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -48,6 +48,7 @@ public class YamlConfig { boolean privateFields; boolean privateConstructors = true; boolean allowDuplicates = true; + String tagSuffix; public YamlConfig () { scalarSerializers.put(Date.class, new DateSerializer()); @@ -123,6 +124,12 @@ public void setPrivateConstructors (boolean privateConstructors) { this.privateConstructors = privateConstructors; } + /** When not null, YAML read into a {@link Map} stores any value tags using key + tagSuffix, and when writing YAML the value + * tags are output. Key tags are not stored in the map. Default is null. */ + public void setTagSuffix (String tagSuffix) { + this.tagSuffix = tagSuffix; + } + static public class WriteConfig { boolean explicitFirstDocument = false; boolean explicitEndDocument = false; @@ -226,7 +233,6 @@ static public class ReadConfig { boolean ignoreUnknownProperties; boolean autoMerge = true; boolean classTags = true; - String tagSuffix; ReadConfig () { } @@ -269,11 +275,6 @@ public void setIgnoreUnknownProperties (boolean allowUnknownProperties) { public void setClassTags (boolean classTags) { this.classTags = classTags; } - - /** When not null, YAML read into a {@link Map} stores any tags using key + tagSuffix. Default is null. */ - public void setTagSuffix (String tagSuffix) { - this.tagSuffix = tagSuffix; - } } static class ConstructorParameters { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 6df3619..c47505b 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -24,7 +24,11 @@ import java.io.StringReader; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Map.Entry; import com.esotericsoftware.yamlbeans.Beans.Property; @@ -317,15 +321,15 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } if (object instanceof Map) { // Add to map. - if (config.readConfig.tagSuffix != null) { + if (config.tagSuffix != null) { Event nextEvent = parser.peekNextEvent(); switch (nextEvent.type) { case MAPPING_START: case SEQUENCE_START: - ((Map)object).put(key + config.readConfig.tagSuffix, ((CollectionStartEvent)nextEvent).tag); + ((Map)object).put(key + config.tagSuffix, ((CollectionStartEvent)nextEvent).tag); break; case SCALAR: - ((Map)object).put(key + config.readConfig.tagSuffix, ((ScalarEvent)nextEvent).tag); + ((Map)object).put(key + config.tagSuffix, ((ScalarEvent)nextEvent).tag); break; } } @@ -333,7 +337,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (!config.allowDuplicates && ((Map)object).containsKey(key)) { throw new YamlReaderException("Duplicate key found '" + key + "'"); } - if(config.readConfig.autoMerge && "<<".equals(key) && value!=null) + if (config.readConfig.autoMerge && "<<".equals(key) && value != null) mergeMap((Map)object, value); else ((Map)object).put(key, value); @@ -417,20 +421,20 @@ private Object readValueInternal (Class type, Class elementType, String anchor) /** see http://yaml.org/type/merge.html */ @SuppressWarnings("unchecked") - private void mergeMap(Map dest, Object source) throws YamlReaderException { - if(source instanceof Collection) { - for(Object item : ((Collection)source)) + private void mergeMap (Map dest, Object source) throws YamlReaderException { + if (source instanceof Collection) { + for (Object item : ((Collection)source)) mergeMap(dest, item); - } else if(source instanceof Map) { + } else if (source instanceof Map) { Map map = (Map)source; - for(Map.Entry entry : map.entrySet()) { - if(!dest.containsKey(entry.getKey())) - dest.put(entry.getKey(), entry.getValue()); - + for (Map.Entry entry : map.entrySet()) { + if (!dest.containsKey(entry.getKey())) dest.put(entry.getKey(), entry.getValue()); + } } else - throw new YamlReaderException("Expected a mapping or a sequence of mappings for a '<<' merge field but found: " + source.getClass().getSimpleName()); - + throw new YamlReaderException("Expected a mapping or a sequence of mappings for a '<<' merge field but found: " + + source.getClass().getSimpleName()); + } /** Returns a new object of the requested type. */ @@ -451,11 +455,6 @@ public YamlReaderException (String message) { } } - static public class Meow { - public String tag; - public String value; - } - public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 9ca11b6..47dc2b2 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -22,7 +22,6 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -34,7 +33,6 @@ import com.esotericsoftware.yamlbeans.Beans.Property; import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig; -import com.esotericsoftware.yamlbeans.document.YamlDocument; import com.esotericsoftware.yamlbeans.document.YamlElement; import com.esotericsoftware.yamlbeans.emitter.Emitter; import com.esotericsoftware.yamlbeans.emitter.EmitterException; @@ -135,19 +133,19 @@ public void close () throws YamlException { } } - private void writeValue (Object object, Class fieldClass, Class elementType, Class defaultType) throws EmitterException, - IOException, YamlException { + private void writeValue (Object object, Class fieldClass, Class elementType, Class defaultType) + throws EmitterException, IOException, YamlException { boolean isRoot = this.isRoot; this.isRoot = false; - if(object instanceof YamlElement) { + if (object instanceof YamlElement) { ((YamlElement)object).emitEvent(emitter, config.writeConfig); return; } else if (object == null) { emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); return; } - + Class valueClass = object.getClass(); boolean unknownType = fieldClass == null; if (unknownType) fieldClass = valueClass; @@ -178,7 +176,8 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla String tag = null; boolean showTag = false; - if ((unknownType || valueClass != fieldClass || config.writeConfig.writeClassName == WriteClassName.ALWAYS) && config.writeConfig.writeClassName != WriteClassName.NEVER) { + if ((unknownType || valueClass != fieldClass || config.writeConfig.writeClassName == WriteClassName.ALWAYS) + && config.writeConfig.writeClassName != WriteClassName.NEVER) { showTag = true; if ((unknownType || fieldClass == List.class) && valueClass == ArrayList.class) showTag = false; if ((unknownType || fieldClass == Map.class) && valueClass == HashMap.class) showTag = false; @@ -224,11 +223,34 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (object instanceof Map) { emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); - for (Object item : ((Map)object).entrySet()) { + Map map = (Map)object; + for (Object item : map.entrySet()) { Entry entry = (Entry)item; - writeValue(entry.getKey(), null, null, null); - if (isRoot && !config.writeConfig.writeRootElementTags) elementType = entry.getValue().getClass(); - writeValue(entry.getValue(), elementType, null, null); + Object key = entry.getKey(), value = entry.getValue(); + if (isRoot && !config.writeConfig.writeRootElementTags) elementType = value.getClass(); + if (config.tagSuffix != null && key instanceof String) { + // Skip tag keys. + if (((String)key).endsWith(config.tagSuffix)) continue; + + // Write value with tag, if found. + if (value instanceof String) { + Object valueTag = map.get(key + config.tagSuffix); + if (valueTag instanceof String) { + String string = (String)value; + char style = 0; + try { + Float.parseFloat(string); + style = '\''; + } catch (NumberFormatException ignored) { + } + writeValue(key, null, null, null); + emitter.emit(new ScalarEvent(null, (String)valueTag, new boolean[] {false, false}, string, style)); + continue; + } + } + } + writeValue(key, null, null, null); + writeValue(value, elementType, null, null); } emitter.emit(Event.MAPPING_END); return; From 05a0b0295a285a33e9b29a32ffa597d4a409e3da Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 8 Nov 2017 13:03:59 +0100 Subject: [PATCH 040/114] Guess at number types. Silly YAML. closes #65 --- .../esotericsoftware/yamlbeans/YamlConfig.java | 7 +++++++ .../esotericsoftware/yamlbeans/YamlReader.java | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 387281f..a8ca8b1 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -233,6 +233,7 @@ static public class ReadConfig { boolean ignoreUnknownProperties; boolean autoMerge = true; boolean classTags = true; + boolean guessNumberTypes = true; ReadConfig () { } @@ -275,6 +276,12 @@ public void setIgnoreUnknownProperties (boolean allowUnknownProperties) { public void setClassTags (boolean classTags) { this.classTags = classTags; } + + /** When true, if the type for a scalar value is unknown and it looks like a number, it is read as an integer or float. When + * false, if the type for a scalar value is unknown it is always read a string. Default is true. */ + public void setGuessNumberTypes (boolean guessNumberTypes) { + this.guessNumberTypes = guessNumberTypes; + } } static class ConstructorParameters { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index c47505b..3ac32bf 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -200,6 +200,21 @@ private Object readValueInternal (Class type, Class elementType, String anchor) type = LinkedHashMap.class; break; case SCALAR: + if (config.readConfig.guessNumberTypes) { + String value = ((ScalarEvent)event).value; + try { + Integer convertedValue = Integer.decode(value); + if (anchor != null) anchors.put(anchor, convertedValue); + return convertedValue; + } catch (NumberFormatException ex) { + } + try { + Float convertedValue = Float.valueOf(value); + if (anchor != null) anchors.put(anchor, convertedValue); + return convertedValue; + } catch (NumberFormatException ex) { + } + } type = String.class; break; case SEQUENCE_START: From 541d4128b36d10131a073650a8f35b2fd5046fca Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 15 Nov 2017 21:17:29 +0100 Subject: [PATCH 041/114] Fixed guessing numeric types for null values. --- .../yamlbeans/YamlReader.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 3ac32bf..2934e0c 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -202,17 +202,19 @@ private Object readValueInternal (Class type, Class elementType, String anchor) case SCALAR: if (config.readConfig.guessNumberTypes) { String value = ((ScalarEvent)event).value; - try { - Integer convertedValue = Integer.decode(value); - if (anchor != null) anchors.put(anchor, convertedValue); - return convertedValue; - } catch (NumberFormatException ex) { - } - try { - Float convertedValue = Float.valueOf(value); - if (anchor != null) anchors.put(anchor, convertedValue); - return convertedValue; - } catch (NumberFormatException ex) { + if (value != null) { + try { + Integer convertedValue = Integer.decode(value); + if (anchor != null) anchors.put(anchor, convertedValue); + return convertedValue; + } catch (NumberFormatException ex) { + } + try { + Float convertedValue = Float.valueOf(value); + if (anchor != null) anchors.put(anchor, convertedValue); + return convertedValue; + } catch (NumberFormatException ex) { + } } } type = String.class; From 3627070a46a03dbeddc60d8efa9b300a936ad7ff Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 6 Dec 2017 17:41:05 -0800 Subject: [PATCH 042/114] Fixed tests on Windows (line endings). --- .../yamlbeans/document/YamlDocumentTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java index 7a5aba2..50d68ff 100644 --- a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -5,16 +5,17 @@ import org.junit.Test; import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; import com.esotericsoftware.yamlbeans.YamlException; import com.esotericsoftware.yamlbeans.YamlWriter; -import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; -import com.esotericsoftware.yamlbeans.document.YamlDocument; -import com.esotericsoftware.yamlbeans.document.YamlDocumentReader; -import static org.junit.Assert.*; +import junit.framework.TestCase; + +public class YamlDocumentTest extends TestCase { + protected void setUp () throws Exception { + System.setProperty("line.separator", "\n"); + } -public class YamlDocumentTest { - @Test public void testThatTaggedDocumentIsCopied() throws Exception { testEquals("--- !someTag\nscalar: value\n"); From d0b1b60d2a2da50d2441883bb34392e7cbea0801 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 6 Dec 2017 17:41:33 -0800 Subject: [PATCH 043/114] Fixed guessNumberTypes and default to false. --- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 2 +- src/com/esotericsoftware/yamlbeans/YamlReader.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index a8ca8b1..8e4e43f 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -233,7 +233,7 @@ static public class ReadConfig { boolean ignoreUnknownProperties; boolean autoMerge = true; boolean classTags = true; - boolean guessNumberTypes = true; + boolean guessNumberTypes; ReadConfig () { } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 2934e0c..16814a5 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -206,12 +206,14 @@ private Object readValueInternal (Class type, Class elementType, String anchor) try { Integer convertedValue = Integer.decode(value); if (anchor != null) anchors.put(anchor, convertedValue); + parser.getNextEvent(); return convertedValue; } catch (NumberFormatException ex) { } try { Float convertedValue = Float.valueOf(value); if (anchor != null) anchors.put(anchor, convertedValue); + parser.getNextEvent(); return convertedValue; } catch (NumberFormatException ex) { } From 31e33bd87a240dc89abdbfafa62c38378805905c Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 6 Dec 2017 17:49:35 -0800 Subject: [PATCH 044/114] 1.13 release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26f7da5..9ef4341 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.13-SNAPSHOT + 1.13 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 984061a5864471f94083de8de1bc91bb5a0f180b Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 6 Dec 2017 17:50:21 -0800 Subject: [PATCH 045/114] 1.13 --- project.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.yaml b/project.yaml index da4e6d9..a3e607f 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.12 +version: 1.13 From be5dd73ccc54f85efb26447fbe37d5970e855f43 Mon Sep 17 00:00:00 2001 From: Tony CHEMIT Date: Thu, 7 Dec 2017 02:54:24 +0100 Subject: [PATCH 046/114] Release 1.13 (#89) * update pom for release 1.13 * add travis file --- .travis.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ae4ad97 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: java +jdk: + - openjdk7 + - oraclejdk8 +# We don't need an install before +# we run the unit and integration tests. +install: true +script: "mvn --show-version --errors --batch-mode clean verify" +cache: + directories: + - $HOME/.m2 +branches: + except: + - gh-pages +notifications: + email: + - dev@tchemit.fr From 0cfe937d9393b9abccc10fec66d5e44aa0641010 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 22 Jan 2018 23:40:28 +0100 Subject: [PATCH 047/114] Updated tag URI chars. Left minus in there, despite not being in the spec: http://www.yaml.org/spec/1.2/spec.html#ns-uri-char YAML is silly. closes #68 --- src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index 997c347..a2010c3 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -42,7 +42,7 @@ public class Tokenizer { private final static String BLANK_OR_LINEBR = " \r\n\u0085"; private final static String S4 = "\0 \t\r\n\u0028[]{}"; private final static String ALPHA = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; - private final static String STRANGE_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789][-';/?:@&=+$,.!~*()%"; + private final static String STRANGE_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-#;/?:@&=+$,_.!~*'()[]"; private final static String RN = "\r\n"; private final static String BLANK_T = " \t"; private final static String SPACES_AND_STUFF = "'\"\\\0 \t\r\n\u0085"; From 290f58c2d7148c441fae458c5dcb77c4988602a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thorsten=20Sch=C3=B6ning?= <6223655+ams-tschoening@users.noreply.github.com> Date: Tue, 17 Jul 2018 20:23:22 +0200 Subject: [PATCH 048/114] During test execution under Windows the file "test-merge.yml" was expected to be in "target\test-classes" at least one time, which it isn't, so that test failed. All other tests seem to rely on the current working directory instead of the classloader and changing MergeTest to follow the same approach makes all tests succeed. (#94) --- test/com/esotericsoftware/yamlbeans/MergeTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/com/esotericsoftware/yamlbeans/MergeTest.java b/test/com/esotericsoftware/yamlbeans/MergeTest.java index 7fb6145..e8faba6 100644 --- a/test/com/esotericsoftware/yamlbeans/MergeTest.java +++ b/test/com/esotericsoftware/yamlbeans/MergeTest.java @@ -1,5 +1,7 @@ package com.esotericsoftware.yamlbeans; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; @@ -11,8 +13,8 @@ public class MergeTest { @Test - public void testMerge() throws YamlException { - InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("test-merge.yml"); + public void testMerge() throws FileNotFoundException, YamlException { + InputStream input = new FileInputStream("test/test-merge.yml"); Reader reader = new InputStreamReader(input); Map data = new YamlReader(reader).read(Map.class); Map stuff = (Map)data.get("merged"); From 3bbaa1ae6907a3cc951742fb3a28cde61a2018ee Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 24 Aug 2018 17:49:49 +0200 Subject: [PATCH 049/114] Use Java 7 JDK. --- .classpath | 2 +- .settings/org.eclipse.jdt.core.prefs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .settings/org.eclipse.jdt.core.prefs diff --git a/.classpath b/.classpath index b569539..b4490fe 100644 --- a/.classpath +++ b/.classpath @@ -2,7 +2,7 @@ - + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..f42de36 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 From d6a88d178f2651d473de2f343a71ab52f177e7ba Mon Sep 17 00:00:00 2001 From: Gaibhne Date: Sun, 9 Sep 2018 01:46:06 +0200 Subject: [PATCH 050/114] If there are missing 'fields', attempt to use the no-args constructor (#82) Only in the instance that one or all of the required constructor parameters are null (meaning there is no row defining them in the yaml) attempt to use the no-args-constructor of the class and use setters to set the values. --- .../yamlbeans/DeferredConstruction.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/DeferredConstruction.java b/src/com/esotericsoftware/yamlbeans/DeferredConstruction.java index 7f36051..eb8dd36 100644 --- a/src/com/esotericsoftware/yamlbeans/DeferredConstruction.java +++ b/src/com/esotericsoftware/yamlbeans/DeferredConstruction.java @@ -42,14 +42,25 @@ public Object construct () throws InvocationTargetException { try { Object[] parameters = new Object[parameterValues.length]; int i = 0; + boolean missingParameter = false; for (ParameterValue parameter : parameterValues) { if (parameter == null) + missingParameter = true; + else + parameters[i++] = parameter.value; + } + Object object; + if (missingParameter) { + try { + object = constructor.getDeclaringClass().getConstructor().newInstance(); + } catch (Exception ex) { throw new InvocationTargetException(new YamlException("Missing constructor property: " + parameterNames[i])); - parameters[i++] = parameter.value; + } + } else object = constructor.newInstance(parameters); + for (PropertyValue propertyValue : propertyValues) { + if (propertyValue.value != null) + propertyValue.property.set(object, propertyValue.value); } - Object object = constructor.newInstance(parameters); - for (PropertyValue propertyValue : propertyValues) - propertyValue.property.set(object, propertyValue.value); return object; } catch (Exception ex) { throw new InvocationTargetException(ex, "Error constructing instance of class: " From 54c76af24bff7d4b81bd14dd92365f8483a714ee Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 13 Sep 2018 19:16:34 +0200 Subject: [PATCH 051/114] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6c94e08..4ecb378 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ## YamlBeans -![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg) ![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.esotericsoftware.yamlbeans/yamlbeans/badge.svg) Please use the [YamlBeans discussion group](http://groups.google.com/group/yamlbeans-users) for support. From f6c74534e5923dfeb9702b2deb4563c0bcac0177 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 6 Feb 2019 19:47:10 +0100 Subject: [PATCH 052/114] Added comment to test. --- test/com/esotericsoftware/yamlbeans/YamlReaderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 896a86f..0b76d9c 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -30,7 +30,7 @@ public void testSimpleFields () throws Exception { "stringValue: moo\ufec9moo\n" + // "intValue: !!int 123\n" + // "floatValue: 0.3\n" + // - "doubleValue: 0.0002\n" + // + "doubleValue: 0.0002 # comment\n" + // "longValue: 999999\n" + // "shortValue: 125\n" + // "charValue: j\n" + // From b2ccca46b52432643639fd8c02d059a93fc8091f Mon Sep 17 00:00:00 2001 From: Sarma Seetamraju Date: Mon, 25 Mar 2019 04:28:20 -0400 Subject: [PATCH 053/114] feature enhancement to allow single,double,No quotes (#108) --- .../yamlbeans/YamlConfig.java | 25 +++++++++++++++++-- .../yamlbeans/YamlWriter.java | 8 +++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 8e4e43f..ff3a3bc 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -139,6 +139,7 @@ static public class WriteConfig { boolean autoAnchor = true; boolean keepBeanPropertyOrder = false; WriteClassName writeClassName = WriteClassName.AUTO; + QuoteCharEnum quoteChar = QuoteCharEnum.SINGLEQUOTE; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { @@ -220,11 +221,26 @@ public void setEscapeUnicode (boolean escapeUnicode) { emitterConfig.setEscapeUnicode(escapeUnicode); } - /** If true, class name tags will always be output. */ + /** based on Ennumeration parameter, class name tags will be output. */ public void setWriteClassname (WriteClassName write) { writeClassName = write; } - } + + /** define the quote character you'd like to use, when writing YAML output. */ + public void setQuoteChar (QuoteCharEnum _qc) { + this.quoteChar = _qc; + } + + /** Utility function: primarily for use within YamlWriter, to get the quote-character */ + public char getQuoteChar() { + switch (this.quoteChar ) { + case NONE: return ' '; + case SINGLEQUOTE: return '\''; + case DOUBLEQUOTE: return '\"'; + default: return '\''; + } + } + } static public class ReadConfig { Version defaultVersion = new Version(1, 1); @@ -292,4 +308,9 @@ static class ConstructorParameters { public static enum WriteClassName { ALWAYS, NEVER, AUTO } + + public static enum QuoteCharEnum { + NONE, SINGLEQUOTE, DOUBLEQUOTE + } + } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 47dc2b2..6026b3c 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -151,7 +151,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (unknownType) fieldClass = valueClass; if (object instanceof Enum) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), this.config.writeConfig.getQuoteChar() )); return; } @@ -203,7 +203,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (valueClass == String.class) { try { Float.parseFloat(string); - style = '\''; + style = this.config.writeConfig.getQuoteChar(); // = '\''; } catch (NumberFormatException ignored) { } } @@ -240,7 +240,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla char style = 0; try { Float.parseFloat(string); - style = '\''; + style = this.config.writeConfig.getQuoteChar(); // = '\''; } catch (NumberFormatException ignored) { } writeValue(key, null, null, null); @@ -291,7 +291,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (propertyValue == null && prototypeValue == null) continue; if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) continue; } - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), this.config.writeConfig.getQuoteChar() )); Class propertyElementType = config.propertyToElementType.get(property); Class propertyDefaultType = config.propertyToDefaultType.get(property); writeValue(propertyValue, property.getType(), propertyElementType, propertyDefaultType); From 772d3ed2f1e38c39565f5cbf18c2a7807218b50b Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 25 Mar 2019 09:35:33 +0100 Subject: [PATCH 054/114] Clean up #108 (quote writing). Defaults should not change. Formatting. No quoutes must be 0, not ' '. --- .../yamlbeans/YamlConfig.java | 35 ++++++++----------- .../yamlbeans/YamlWriter.java | 10 +++--- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index ff3a3bc..42c612e 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -139,7 +139,7 @@ static public class WriteConfig { boolean autoAnchor = true; boolean keepBeanPropertyOrder = false; WriteClassName writeClassName = WriteClassName.AUTO; - QuoteCharEnum quoteChar = QuoteCharEnum.SINGLEQUOTE; + Quote quote = Quote.NONE; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { @@ -226,21 +226,11 @@ public void setWriteClassname (WriteClassName write) { writeClassName = write; } - /** define the quote character you'd like to use, when writing YAML output. */ - public void setQuoteChar (QuoteCharEnum _qc) { - this.quoteChar = _qc; - } - - /** Utility function: primarily for use within YamlWriter, to get the quote-character */ - public char getQuoteChar() { - switch (this.quoteChar ) { - case NONE: return ' '; - case SINGLEQUOTE: return '\''; - case DOUBLEQUOTE: return '\"'; - default: return '\''; - } - } - } + /** The type of quotes to use when writing YAML output. */ + public void setQuoteChar (Quote quote) { + this.quote = quote; + } + } static public class ReadConfig { Version defaultVersion = new Version(1, 1); @@ -309,8 +299,13 @@ public static enum WriteClassName { ALWAYS, NEVER, AUTO } - public static enum QuoteCharEnum { - NONE, SINGLEQUOTE, DOUBLEQUOTE - } - + public static enum Quote { + NONE('\0'), SINGLE('\''), DOUBLE('"'); + + char c; + + Quote (char c) { + this.c = c; + } + } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 6026b3c..3643fe7 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -151,7 +151,8 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (unknownType) fieldClass = valueClass; if (object instanceof Enum) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), this.config.writeConfig.getQuoteChar() )); + emitter.emit( + new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), this.config.writeConfig.quote.c)); return; } @@ -203,7 +204,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (valueClass == String.class) { try { Float.parseFloat(string); - style = this.config.writeConfig.getQuoteChar(); // = '\''; + style = this.config.writeConfig.quote.c; } catch (NumberFormatException ignored) { } } @@ -240,7 +241,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla char style = 0; try { Float.parseFloat(string); - style = this.config.writeConfig.getQuoteChar(); // = '\''; + style = this.config.writeConfig.quote.c; } catch (NumberFormatException ignored) { } writeValue(key, null, null, null); @@ -291,7 +292,8 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (propertyValue == null && prototypeValue == null) continue; if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) continue; } - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), this.config.writeConfig.getQuoteChar() )); + emitter.emit( + new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), this.config.writeConfig.quote.c)); Class propertyElementType = config.propertyToElementType.get(property); Class propertyDefaultType = config.propertyToDefaultType.get(property); writeValue(propertyValue, property.getType(), propertyElementType, propertyDefaultType); From 1a7fc5b567570e45b4762dd0c67c75cf5791c4a1 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Sat, 20 Apr 2019 16:07:34 +0200 Subject: [PATCH 055/114] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ef4341..67435c0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.13 + 1.14 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From f5610997edbc5534fc7e9f0a91654d14742345ca Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Sat, 20 Apr 2019 16:07:51 +0200 Subject: [PATCH 056/114] Update project.yaml --- project.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.yaml b/project.yaml index a3e607f..1ee82c0 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.13 +version: 1.14 From 8400e141811c5bbc1b02b2ccce910627468554b9 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 2 Jul 2019 23:16:57 +0200 Subject: [PATCH 057/114] Added parens to dubious looking logic. --- project.yaml | 2 +- src/com/esotericsoftware/yamlbeans/emitter/Emitter.java | 4 ++-- test/com/esotericsoftware/yamlbeans/YamlWriterTest.java | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/project.yaml b/project.yaml index a3e607f..1ee82c0 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.13 +version: 1.14 diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 07c8c7e..240a90f 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -527,9 +527,9 @@ private char chooseScalarStyle () { if (analysis == null) analysis = ScalarAnalysis.analyze(ev.value, config.escapeUnicode); if (ev.style == '"' || config.canonical) return '"'; if (ev.style == 0 && !(simpleKeyContext && (analysis.empty || analysis.multiline)) - && (flowLevel != 0 && analysis.allowFlowPlain || flowLevel == 0 && analysis.allowBlockPlain)) return 0; + && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) return 0; if (ev.style == 0 && ev.implicit[0] && !(simpleKeyContext && (analysis.empty || analysis.multiline)) - && (flowLevel != 0 && analysis.allowFlowPlain || flowLevel == 0 && analysis.allowBlockPlain)) return 0; + && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) return 0; if ((ev.style == '|' || ev.style == '>') && flowLevel == 0 && analysis.allowBlock) return '\''; if ((ev.style == 0 || ev.style == '\'') && analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline)) return '\''; diff --git a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java index 741597d..35fddf4 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java @@ -36,6 +36,8 @@ public class YamlWriterTest extends TestCase { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); public void testPrivateFields () throws Exception { + YamlWriter yamlWriter = new com.esotericsoftware.yamlbeans.YamlWriter(new java.io.OutputStreamWriter(System.out)); + ArrayList list = new ArrayList(); list.add("abc"); list.add("123"); From 70043e6d4ae370abd8621f119ed852797ebd867c Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 2 Jul 2019 23:18:12 +0200 Subject: [PATCH 058/114] Merge remote-tracking branch 'origin/master' --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ef4341..67435c0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.13 + 1.14 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 773263adb021ba0a4f8244ea5dd40e95e9f3529f Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 31 Aug 2019 11:18:53 +0200 Subject: [PATCH 059/114] Fix null scalars. Caused by c902cddf67d5666ff282abd7bbe625bbf22d0f4f. closes #98 related #66 --- .../yamlbeans/YamlReader.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 16814a5..2740c9b 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -247,37 +247,37 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (type == String.class) { convertedValue = value; } else if (type == Integer.TYPE) { - convertedValue = value.length() == 0 ? 0 : Integer.decode(value); + convertedValue = value == null ? null : Integer.decode(value); } else if (type == Integer.class) { - convertedValue = value.length() == 0 ? null : Integer.decode(value); + convertedValue = value == null ? null : Integer.decode(value); } else if (type == Boolean.TYPE) { - convertedValue = value.length() == 0 ? false : Boolean.valueOf(value); + convertedValue = value == null ? null : Boolean.valueOf(value); } else if (type == Boolean.class) { - convertedValue = value.length() == 0 ? null : Boolean.valueOf(value); + convertedValue = value == null ? null : Boolean.valueOf(value); } else if (type == Float.TYPE) { - convertedValue = value.length() == 0 ? 0 : Float.valueOf(value); + convertedValue = value == null ? null : Float.valueOf(value); } else if (type == Float.class) { - convertedValue = value.length() == 0 ? null : Float.valueOf(value); + convertedValue = value == null ? null : Float.valueOf(value); } else if (type == Double.TYPE) { - convertedValue = value.length() == 0 ? 0 : Double.valueOf(value); + convertedValue = value == null ? null : Double.valueOf(value); } else if (type == Double.class) { - convertedValue = value.length() == 0 ? null : Double.valueOf(value); + convertedValue = value == null ? null : Double.valueOf(value); } else if (type == Long.TYPE) { - convertedValue = value.length() == 0 ? 0 : Long.decode(value); + convertedValue = value == null ? null : Long.decode(value); } else if (type == Long.class) { - convertedValue = value.length() == 0 ? null : Long.decode(value); + convertedValue = value == null ? null : Long.decode(value); } else if (type == Short.TYPE) { - convertedValue = value.length() == 0 ? 0 : Short.decode(value); + convertedValue = value == null ? null : Short.decode(value); } else if (type == Short.class) { - convertedValue = value.length() == 0 ? null : Short.decode(value); + convertedValue = value == null ? null : Short.decode(value); } else if (type == Character.TYPE) { - convertedValue = value.length() == 0 ? 0 : value.charAt(0); + convertedValue = value == null ? null : value.charAt(0); } else if (type == Character.class) { - convertedValue = value.length() == 0 ? null : value.charAt(0); + convertedValue = value == null ? null : value.charAt(0); } else if (type == Byte.TYPE) { - convertedValue = value.length() == 0 ? 0 : Byte.decode(value); + convertedValue = value == null ? null : Byte.decode(value); } else if (type == Byte.class) { - convertedValue = value.length() == 0 ? null : Byte.decode(value); + convertedValue = value == null ? null : Byte.decode(value); } else throw new YamlException("Unknown field type."); if (anchor != null) anchors.put(anchor, convertedValue); @@ -291,7 +291,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) Event event = parser.getNextEvent(); if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); String enumValueName = ((ScalarEvent)event).value; - if (enumValueName.length() == 0) return null; + if (enumValueName == null) return null; try { return Enum.valueOf(type, enumValueName); } catch (Exception ex) { @@ -427,8 +427,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) return array; } case SCALAR: - // Interpret an empty scalar as null. - if (((ScalarEvent)event).value.length() == 0) { + if (((ScalarEvent)event).value == null) { event = parser.getNextEvent(); return null; } From d265820333c691551552377dc05170bc1e6bff85 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 31 Aug 2019 11:21:00 +0200 Subject: [PATCH 060/114] If property is not found, try prepending `_`. closes #98 --- src/com/esotericsoftware/yamlbeans/Beans.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 0d195e7..5be5e52 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -209,6 +209,8 @@ static public Property getProperty (Class type, String name, boolean beanPropert } return new FieldProperty(field); } + + if (!name.startsWith("_")) return getProperty(type, "_" + name, beanProperties, privateFields, config); return null; } @@ -292,15 +294,15 @@ private Class getElementTypeFromGenerics (Type type) { if (actualTypeArguments.length > 0) { final Type cType = actualTypeArguments[actualTypeArguments.length - 1]; if (cType instanceof Class) { - return (Class) cType; + return (Class)cType; } else if (cType instanceof WildcardType) { - WildcardType t = (WildcardType) cType; + WildcardType t = (WildcardType)cType; final Type bound = t.getUpperBounds()[0]; - return bound instanceof Class ? (Class) bound : null; + return bound instanceof Class ? (Class)bound : null; } else if (cType instanceof ParameterizedType) { - ParameterizedType t = (ParameterizedType) cType; + ParameterizedType t = (ParameterizedType)cType; final Type rt = t.getRawType(); - return rt instanceof Class ? (Class) rt : null; + return rt instanceof Class ? (Class)rt : null; } } } From 2ec3f7569af891db0d364649bd2139cd8667d7f9 Mon Sep 17 00:00:00 2001 From: Seii Date: Sun, 26 Jan 2020 08:15:15 -0700 Subject: [PATCH 061/114] Synchronize Maven and local lib versions of JUnit to 4.8.2 (#121) --- .classpath | 2 +- lib/junit-4.4.jar | Bin 161477 -> 0 bytes lib/junit-4.8.2.jar | Bin 0 -> 237344 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 lib/junit-4.4.jar create mode 100644 lib/junit-4.8.2.jar diff --git a/.classpath b/.classpath index b4490fe..0055a91 100644 --- a/.classpath +++ b/.classpath @@ -3,6 +3,6 @@ - + diff --git a/lib/junit-4.4.jar b/lib/junit-4.4.jar deleted file mode 100644 index 649b0b327f07b8daf6606a7afd7d90afe915cc4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161477 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 Q!hiZ<;j&-_j8?e*2Q-yc;s5{u diff --git a/lib/junit-4.8.2.jar b/lib/junit-4.8.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..5b4bb849af9583fec1ea0a0ccc0d571ef49aa8ba GIT binary patch literal 237344 zcmbTd19WBEvOk=T*|C$3ZQHhO+jhscZQJhHw$rigbkfO}zUSWa-nsvC``z*FvDeyb zj9oR?tThY2Syf9`0tgrq;MZr*G=%dX7ymp#0009>2`TbXiAf97yo~|?$o`WQ9ANi@ zH1^BNmcj?A&c}iDar{Xt#V0K$ETo`FEhT&_H9jUOK}9_SBSA$zIX+b{Pdm@BwQoNz zfM7={Dmf{t1Q7O#XyP8)tu-TD9#KMG!7-DZ??mbzdM7f%5k_hpnGp*L(q0~!^EU3D z(XG|B_0LHF{9_XToa#S*KtImrPS$3QH2<3D|4Re^CykM-qmi|PnT@r>Z^+^PBsaF# zvodnAvA6gQHO!yX_Dp;dk;Q{5z?EjlI!thDP~!dNXTBBYSH-%ir+)(xzY2`M=fz^)GZr zuC_+@W>!Ylj=voo|35Ps=s6mh*w~vH{cc4u|1-app6hR6dr>A49iyviz;({c^qkoBIFokYE0e+)B^d%+|?L&+)@=|HfAT zme<je(*%c(m6X2N>pdDG2v7zFG-evJ79q}Ah4xnx zGM@N!O@T66Mk}X5p(USsDTNk}mef?D6C;Y)CA2k1&wT5cyleEzv5Ah3_SxF?;6J=| zyI#FJH!X789!zG{A0h}WM%+vLhsMq%7I4lP$sAk^NH^hN z+y%p4UzP!AzNKiT^K75P66riW>uHJ<5iLQ7=}fI{zO`g`$J}T?w@uMWN5=sr9m?le z+YVG`aa?4P$}MHN)#(yX8^*R)S6|v*F-zOlv(A~>WtzI!NVb-Pwif1xFn7))hyj2T zdDIkm6a#-+H8AjCpgv;YP`x<&zN}Zy(TLW2NuKi^3E&%)>EWt^O;Bs=#J-H+g)Dlh zjWNUO0qajmmT|&{jZj!1>PQ%=Z>)@+*XlQ?=Df#K{NStlJUMrO+k+6AICqw>(!BKq z42~^$*D5hsQkRC!{NYn=;E-6#qO-ow2#}f_SD$|BPAtSvdwpc0088-w^ zHA&?c#4l6!0&bO+W$<^=sU8a)q|F*PBx)y__LXAoN<$LSttF@i>K2iyztILx(&ToJ zdSPATXpAK0iN`HlLVaTFmQXhDQR2^&3};8&XUD^pRWl>+7dGXbQ$kSnu?o*O7s8~T z!|Ga*N8-N(+7&;Vjj!w;G#x8IG1H z8@9 za;7P1fdKV#HAh(07sUhC1xs5K?v=yKJiB9DL_x{X-Rt3Ol|Y+^OX57DPYl4HNgBr3 zF6a&r-eb9`4uIrDsTLkDyQARlGo{F%+u$s;i`G>rVCxaXcyzV-pTY7vEa#$p8$Vb5 z%*Z*l@6cE$jEL_KQ+*)pxH>hU{EDz-1W{V(hNVS6^y3}sU-tJ0^h`jM4SxIt0I>RD zO{5=|^oOnfFGR;@;ArM-r1;^i6`af*jR@(e4J`E>95NN8Z0303yfEAC(pU+p0@1gz#|^_Z^p@J5 zW-pl5{Svy=hg?=UMRc%KW`0m}K7Cx=k}lP?_Fgt8es^z35VRH*wD#6B!BwGjYp66? zG|>A1-U0WjsU3~GtHUR;-T^-cC&++I7Sq+&GgCWb)buFp$9%=--q_RZme}ISR(X|u zDBf;QbYOb+yA9T)c~z*>`X3!JD21QQRE(%`g3TD?M6w{)pP)K@E~K@R?(xx{_0B*{ zstxHOhCthhopsYoc7j5U5~LJ$(@JuUa!mBG+le2h_5D#%31wBxu)f1gW^5D2U8v@C z4$E|JQ3b6?a~{9xEMeZB0jA&fX*pET-oq#3XARS>YOW(RtCcS)Q2^LG6YQSBQh|6g z&9hY_i-U2V?!mOP_}93YkS=>Lm_DE$C8N71Hi$A#_ese!#AAA<%PQ!IP(<$|R_3{! zQ3$vCJs?Yf$iijIy#}1%qI&R9GiWk}Pa6gtWJ_A8;yan1GV~Fs|5)zbwMgY~GlprV zuU=cmU3w1QEM##tkp^11#r&)C=nj5G1W?aZLI zQ-cT>r}1HtV5&_ud5<(yo<@2~xb5|d3Y!kCWCHiRZDrbK!ru_!I!<-2JY}dL38Chq zLF5w;nvg_QxOA$7rkV_Ej;)zo>D8?k*9Q$)uI?N>Q{C!olU^XcF%Ps*i&U znYV@a$kb5KC=?&p53NhLf3z2h_rdhl5o5_ZnPmYBPqKpVNzZ*-<;l%Dch*h`RXPK{ zV0g|$e^s2lLuN_X7s7|Jc5VMwp7*uK!K5Jrr!vr(!+=?F>$&}abDx2C>2SQVJF=eb z5w!GGgs)RBj&Bc2lj<(Qld!KOH$ulp*V1?F&<#?xe+~@rwv%oQm98)WV(LEX0Jv-} zajgPucH>}R7-~u3`^%_Bg)Mhq+jpQ+Bs`F=7}Yc;DIGhBR(2`(A zCAr_4t|I-*jGs^}P!_Q{=fB1u9&T@OoF{gLDC>5pnC-UXK%#b~zE{_#USvVs1&+cj zPw{QsK0)>!hn&yzD@M4+V;}&R0nEU!KhN>_8QM5 z64to}-}x#G$X3PdLW?^P(`?2zB<%{e3mRcn7!2wRR_t~HsB?VbxF)2vOF{D{K)F&IIm=mSzOSlYbe6^aX@w5(Sz`~?{f)gm- z3%I$$A?>Htl>!aGSyJ6qLxp}x=(hwpcJ^Bi(5#dQ&?Rf^Uh90=bjBjO05|+~=HY?e z6cl?<7P~RRI65o{_eVVxqQQgvc8vNF)z2k+;X>XV*NnL(m-Va@s{ z5Q_*ceRK3vsBZP$!s|rqoyva6XiUM;&~D5qj03Hlxte*vdS#CR8KuS5FxR#&-pEJU zx3ozd2loZ`5T5#rZk~FpD<3;%g1<9tc_Uk+4;*ax zD=z$D)m{o(GKhRgTysfH^M!>V>grIGA*(34xKC=l@Jx7So&fxQHWC~O1LxND9ObV7 z?*XAQ->!E2iNz7c?ly3CrerTIlcTQ^r7ku-4kud=*HfQ=J`TwMFt}m}&_Kc?<&zG; zbqlf}v=GF{(@Ex1b{>Np>mLnjp^uB(P=|Tk@L=}*1ph)yx_0Uw&r#knz|_;NjMjVG zRvIo8q?y}x8mF<@)JW`OvSKrOx(=V1xC1I+x>Dv4B&fvlOwrsraPDL^mLpkazQAZ8 zQ168PG7_^qgB=8q6LSe+&E96jzAtT}{TO3}_Y-{<)rHu;X^K(1UQ=QCtnR?J_cKbE zAh&5d@qKH`Gr5bVfFhw+k%ub1@vx541RU~WT8&eE&$JGyvP)yL`pWdUaBF;F*mBzb zD$|>MvU+?mk<^Geh?nayt#h5j0IjP4L(*B$+Fp@st;#MH->oiMTzeoj&_a9(vzADS zhNhOLY%$R+0f<-u60{zx-w-5uiV7`YBA!FiDuW8k$$C;d3R1*wk)IGJ-Zzl7L)C&P zl`M$9t5&B}pGk|PN{BJzVQqVvy$mqO!$YFm)-e+4yA13xrW1=^bGO4EtMv^O6^-ia z6%3Nvz#i25W7WC|o5D0)n8L7u#v*f}M>xL&+Xj5DBPK7vztfa#^Wn7KNI zuP%;Wg#!SyhI2>wm?Y^rF7pz&V$cYNL3QR}0XvjiZfu&xD6?Y4x6q&Ec!BFoJb(26#W8vO>+V<)%OgMT0SbKjw z4DgDI;mgqPClS;95h|6WAL>?s%lj-BT^SMGP=eM6b27Habn`;<3`rk+v}x(VUxm#p zb|1sW`#Ofswp+Ys7`4BCQ)c}R_Saap{90=b`=RpCA1Y7ycPjr&)dh_{l015jHunG2 z`M3@14-gbMbui?N9iJ4F1k9ff7})=b63#^^QC60y0KA~-q|>0O2d}ZP5rz{96_-!0 zb?cKWY5$q~egJ71Wu(g>!}lz%O&%YgpP*Mr(N?O0*G9Q zUWn8J_h$hlWPOXKX+nA@nGo+8K4Al3GK-Js5vX_zuxP8D3sgT9f<|e*htL;A^!mRW z9vVN6KM6)hp-(&@T;$|KOHHYfBsADdrUKWfe!dLWb4fF0Nfp&J@pJdWffG z9mNmITp*8fjhnEi3X-)5npDb!zQ>)+Q%nl-?iG`1UGkBV8@8%o%VqHYpG*}@UwNlQ->G=wV=FJJb|6KwSkUjcI`R=jLiAiT~*gnVc3N%)H;sO0&IzN zk&Hty^h>w1k6)`l4yjjuMSxS7Qv#Xw^qvzwqbZ2x{)VF22mbIciTJ{vYy6q1{=i$r z%r?{>!l9&+yYXm48(hvM<@sq=W{0!2X7MLVwme;1{UA z1$|c$C#UyMe^saWB=;=G<2R48Vbrt)w-*lrD`_V<~bi$Womq3YweiJ>st zl}XDE4VUzZf(@wk5ONI9HJ2$owv4dbnT0emERy`Rj>?R~zD^-FvjnZNA=WK6p&W%q zzP3`UYkF6JfOAv-Z5pg2JFa+P3?b`_9o5ETWc|)C-B&!<;)LlnG8W;OVlH0+llY?_ z9Lb?*>ZsQ>y<0F&#zOorq%PR7K0ISi_?zNOMc(P{+*^kkN9QGk6A_9emw{XpQCxzb zlxtCNyEg#1FMvx3ps#vMolryIXFrqF>YKKm*o`iTz1H%OE|0&rR&T&h&Ais{2CTWE z_4>HvUOP(13|zc>_b0$V#9TFUv(T?Nh%r&Lc*kpzQqB;S&nymSL-g7unNex4bBfMU@uwAahD{CG^#6_L~Yt-UsZD(jia_gxhg)bsuCLKd^+~a zarFlJtB%-SIS-zE=m^Y*)4~0%j{M<|HN7G ztO~oOOVCZ+!;W0% z&_qNYCoDgJE8Gh?)DYLv^0IJ_DSIbkn)G=uFxCvEoRIEb-jZwKZIyk_M zAaGOVkdlH`wl!)meEC5GyZL@k(GwoebG$y|c*Eurju%{~sC3>VbeKMMr9y-|4Nu(^ z@no(Z;uO*Ugiw3zu*H-|F^px41|(dWP=7w$d%Gb8s09-FKu zo3eob0I2&&^#97m{^3jhr;x(u;9z9`Pau|=Z04$Hf;{w|OzC3J00B3w&p!$+HZo4g z$S=_t0~#k;G@%Ux61O;;zDJzuNY65kz-#MUX0@S?S|g<1xUjfbI3E~M7`_ULlDt^y z^pfX2D?^=q=O2C8@tDcMkjw$gdEjRM{k-Gwb<%CBeZ26U`yJtH7sAql=HQ02TMx z28mL}&fsCoiym^cscE;FwWoF;I_GHPkfJA_0K>Q8NSabtO@CM!+ubqpZ+W>OGWNS< z$T|x(=rRtw=x?4<14~Q+w(I^jw$D-r9!VA^Zcl7)p5Z*-52jq6Zna=1-D-CD2K_H< z_in#rJZ^z}@tE;X=5$pXcvO1j<#SVE{37KO8}+{3I_{^2DKc+z0`ZylCP{k`b9{7cxPN9uuyfoN5D} zE6FfPjSE9T_Eg_LurtZ_y@xTBqGd#gvf9MbyeCDv^{$3I+RiofWKa^XDj_H4WR9#Z zFNeyuk-Yk=LPPLjlh%S}lBI1dbZ{SMK>%{X1X^u^^y{aUV`7|8!2r*IeZ%}sA!}RN z5SX~tuSzcQX-H6NC^h0Np_|&d#mR*gZRv_S*)b;uWaSscOx)B3gKDE??P4&{;H96E z>Q0oDFCx-PXB@{<4cy9At;2pGCllI)t>YwVub>rcCD_cHd$4v@nh#W@s?ICm15vcD zp_Ii{TBapy#6g0-fA@4*i(YL>m6pVeCC&bnxTxI~wcl~XSB=`dt);yl_K9YYSI^Rk z2iufGBxEt=ScY&MSf)^or=LO_tFHz1am~u2-cd3qW4W%Db%~9(%=ES{xQ)-i?hN4V?yWI@$~#lEoN&cN zBuu};UdL^5Ohv9hD4(iZzx*>F3YKBiP(DoBbN& z?;14T!QVYA!PN+s*zdFPFQco2sQ*XetDD*3@JY$=j8 z-X8nNqf)oFpLeB%I+WYG%jEsFf?!GaK&nyqjzb>DKfr$AK* z8dc&-i;S;#Im<9_76N28-r~vJ=2}x`0!C`4Afpb7YJs#~OyqpkW%8~wGECJ(0pQlT zb$(OOr8(K+NtLGTFiZ(OWf^hi3DYjvWNNUmTy3gynM4tM&=6^m&;|jDHKTH+H=uEP z4*TK;phVIo#J$TgU`o`CHAQromVP^0Z*`Rgn$V61WWzxr$cCt6G{UCXNa!X+(YhQ1&%y}B zyQ*R7hS4fHtv~TOu~8w~egbx!V;|;l^GC{hh=68g5JU=oa-5OhkTh>I>v5LTbyleH z08XBgHwG(3_poipH6PS2t3()l6x(eVO{-NTn$O}3WYBJosnxjEuZA>&YwYBwqFA%> zkSC6Kp}7?_keST4_v=veWkH=`PXkm3gr&lzF`yc#jY`5l4E_GlFEbb%K;li$O~{pM zdtWemJi>ir*wq)0w-%3gJW`{p;fE@0IIT_%I*sbNd)zW`$d%M4Fll`&vY-zlNLW0X&uXBiUc9&W*zo1>PEtw#a3wW?VOT4*}-yv6=Nr%Qo?Vfku);FU-LagR(g$yS*X~$f1!qucu^seMOUW!qrQ9Ou zH0J3psAGReNUj7Cb)$IY*c*60b)iv_0{h*%&RJ1&7S@FbMi)xfU(q&jdz4XFF6B-clY;G5gvvI%w)^ZLV? zuk#3_+7Wb46<+}1yN+Pn~WKBmI&E#Mc8V@Vr($f-XAqE>l^|B)%!_amB~E zoYYA%;W;s9iKy{YiOuOUyvuE3^ytq@dn?v9)Z7+XjiMY2-9OWXYf!}}S(^P@UXWaa!p&X`O0 zGqPK^r31oyy5jpGBy36_^R*RW-yjq5fyvE%)G+xSg)kmKAt9_%y&Mzf#|ajdud~Ou zOi}j0Oof+Aze_%O-_NRig&V8wt9$AfJn`@}Oe5>zZ_XDU!3!!^H>fNv@_`72X*tUI zVru@CqpO@jRB zx#SiQl(6L%@@341JyZhDzXpGNjk+a{(8mirnQ-dGf{n<{@gdqF&(vmY@`N^Q7sAE6 zx(g5D=|Q5iQHw=A~@WtrsxJdpr+Ky($dZn>CMqhS^eIKES5s$6}_z4U+o zH$Yzsq%4$gfe&o+oR3fD&D5cKH zLTLR7-5CRF0`yD4#vbKJFg0BCoSVH>WcXo!e)U##G|>v<{(_O>U~-Y+F{!t?siXY~ zOK&YGu*|m-%baZb^0g~%G6#sUhz_u{G|2{tdgPMv~qC5)M=u1HoYu5%B#^P}m`5f;|e{=AX~={mrETvr5KNnS>rr#@yl zX{DT&6&R*)&hPOdvM=^8goHBR>$Ibd?Xm2Y&u}NcPv``L(hu8DTvG)iZyEy99<(}) zC$<=?LrptKlF_be!l+A9-jH<>>cJGM&JVidPa73&8*6|;6?l^)?V{()YSkftIFK6< zFn^CQA;N?=(V}KZCg5!890};G7M`nfHo!Vj z@vT)Yh~WHKUihxi_L#~L!23m$@?oX9{;`?HWb5+%?Fp(Iq@tWP=vW$h3nhL{lAb)4 zgZvwBQY$)fz2pIZib}3NdqlhErom-K7>MV{I=+KW;5F0Q#cd#%l8?K z8)+j{`RHO}8*CsIK0Q5`5$SiE|TKNU>YJoA z7)=9pil7bjz#!){zVcYvRYx}* zACneO(*~4dNbg1j)wAYqW5tUV2+%Si$<1D&b4b#*Mo2*Zc)(A$RNeP8oAcwEuK|?k zbIX9xQ|d!ZU?OC71W~HvnU(E>fyrhN+ZG15z{u)dlm%;C;$dem1Hizja1T0!l=kVi zmfcN{IY?5G%JR9~3@>%xXc&Q`Ts#Zo8_#6MjN8C0i7Yxd^&4DGTlS5GUFBG*yGA+U z@K8y@ly=x7(XgRhDlBD*9ch@raW>z)7oFoY2%$v|Py61ZLZ(UErN2y8#uGCPUASU? zWx?X+bj0*Ux}o&xll08eDVi5?-_ej=##g_1!e`rnJ%#o?v=RSy!$@zlW?-39qDhAv zPMUiNyP)GqILEv`!bCBgoO%%-D4}SZ-eMA#?IjUrcvmWGqR@vte^XR9;h9itLDF}; zALw|M7n?(*g8)5Vf^JAXTDZX6JzkO-*$t?6V#aTwhTl>bzjwcX*>D8j6d=OF{!YL? zuCClEaK$nkc*Igse?!G?k^%w8z>$Ha7mS@V1dm_~n;*UEkf1&-1F)Om{8f9gR_p5= z$JeKzmuR0m3O}^ONZw zd1P}i$`&-t56GXIMBGvvX(UxEYwZYCVHz#OTMyH2nXDI7UPeT=@#2RQIFF!j!j8+2 z4864km`5Sl`*?`BT_$850$ppX)-G2QwO~Re7JxwqL5Z}UuTsfmI0e(fqwH92zpcsNAZwowZUV|8hbk~WjA-RI5)SX)O=YmC|6m~5)+P>>nCW=z z41B8g5YO6Fsw2)PYa#0>di%FzddecxtEl`K`nK6$hQ{js8e)Mao;*Abcc1 zQ9f5`R`ZkQ+&3nwZ<~5FeUBInz+J!y!wDq6?DdTf7J5O zwo-=E7=?L%5WXk@+B8+8=zu+S81?<&CvQKQRTSPXgxRgPhUk=YCdTIwZ{EPh#m>BVarE6yMXCTx^A9r<@*I zHed_DDX_t(cDeGXLd0amxT~~JA>OwTVki<1@6+(djhe#^-&--xr(p_+9?ojYdLw=~ zr$~x?S!1tLU5xFcPyBgVMlg1xg&rq~{a91B3-64!__jvg_k*$MWG2PS0~kKDEuqSA zcq&za)nDH1y=+&$QZ5%7^EPfGs7AP&MaV{?N6J(c*k4*3T4GN}z#z%zr216k3hgU=D-!Ml8L~AUml&2IbCCvkwH($bo zro}Mc8Sk$w-+1(zyC~PPEcY_Ee94vJxjPPi`}&sAblJ%e5ChE5Gp;qsb;xzfZ%xfE9F6qyhuc~QxbN}?u2eT#5B zrBKj}2z%T9E#YlEF4$sQb!ZdC4CFlreOK850ex@QFp(=$r08u=r0Cs_FcL-1(j78n znQ~X*fqKY;=m*T=VCbb&>65DRoTAGXZ}DgnCkskyF%Bff&uIop7lqgYJ-x5DQK}?r z^E18;M(9y~gWPNA;W#oHMW`->R2Mk0<@ya8G7*S{rBfzoPInNy4d)plmt6f`?X(3J zf--JF1Z;tl!6Wg~=GNr2mCrSPvdvul{UBaNMt8*|K`s0oAc{qvFP$Ob+p)i4&AnMVlrVWtoD_vfozBoxu7$I*vJltST4g z_02$Xxj15zZp=9X9wbw$ULs*0M>-;13@{LySeh@jmDyw4u-iRVVWVEiN;%D1`SL{G zJ(`Xoi|dH3h>4h5-g?-xtRusC7Llc;6?{FwIl2^i+05K~@JAqgVZi04jJ4Kg3=4*D+W!1W36i)i>jThL@*hU^n8;oWx z*3Fshry{u;F<5;F(au$-$-74;K+kRXhRm`Irr+3Xq`eiYs0wbDOf_9zA{nwOaGiHz zh*;*@Ykc4bq7#lUvP9h&S@NPI9A7T^MnI=ZCW>9OP=Lx`Sn@hmC#2-=C@nn2!+xOd zLCoOGaZ-YUSRBrNM)ToH6rc4Kk_!9`DIwAQw1HXvJk<_~H_4(4vzUj~m`5bg)OApCT>*pXigIWjgJ6_FVL-(r&>B@&zMfz}FjT^+8Uc>+od0?LC1N=u z9iY`bIjjjdhr5uh+`U|}flAgf%7N{WsP>dJx=~wzbkQD0FSiLF=pF-KA?+0Ype4eZ zisS%Nab`rgjjTd-Ki{xY6+snGnPsl04$C7+vi=aIs>uLML~qJ?(vJk;1tJ!joJQhH zZYTfjlBgB8G~x6hkJ38E@Z&5yd^85$6wN%s7;aimN4Xj<&(~8qDS{$E)p({((9C7B zi53_wzLX|=6mq6dVuDou&nh>P787(*P}82MHYAmp?SK`oTly)g+nzB}Y`EKIxb0%Q z2G~z40%5f6qtOaVIu-luD4HV<_D}{5OhY4O0CfCT9%vMAg`p-q-LL-bkdq$3%J(28 zJb4G=5}=JQZ1YAnHC^YZl=8NpPFqV>OoUGP6Vdz#fq(y5X(1jl|Af-n>1(C#SJb*1 z?&HI{*Vl6j^=tuFB*HF=nv%K!nf063HcsAJe+JDnJS^g6_$q;n%N-Y_A;7*HUzI%| zSOiy7(5-g6=(|&GGKcRze~n*jFRkEHACPJ9BZg7`4P^R%;+KHlNALZ=O2?6k77BB` zNL*`4p!^NUNPD?{D5g*daDusn#wPY0e4^lnFzJZ$lx_=TgOmgctpTK>{VUr%w9jI_lI`I?YfRVdT(9)y3m74bm%+#kpym%z86d;7PXghy8$W`0=cD(F9 z!2qtp))5fd7?M|J*oE33%IFSxKzhfUMSq+a&_lb<>m%R_vII@*Ygue(rR(a=ljGd2 zmEHagN!BH^P6Lij!Pqv(PMbLTiL993@Z4|TwMgrg@-@zY)U|k<>wt+30md~>1N>7b7$i!VbM-nl}heN0$R$N9c#fxMtMCyE`q==Al@3 z`SQc`?W;^_Zvmu!NrC5x#G7E*je!2-`IlfLP{E>>)8>rJ^-FKB_qWSOo=;eP(ZmmW zCD0SDGlERo^lWVf>3SOxM+bGja-A*%-DAX(5$G*thf9*Gu0qk;OfvyXqu1=4(~*p{ zRmkz`y1ifN<-+lR;)$Jm7UR(omtyH@%aMaew{fj>rB7wgqeZgr6XAJVTb0+Oe741uw{X~SVo%fL~!4$qdgBd+AbF2)#_!a7AQ z{L^d=k+wMDdnK_E@jg_)L1px|mb<&smSF|Yxik^K5X&y>sFWiG&GD)Oci-i3bLqWl zuBX0`xbD<}GC0#Q`EmKQnVtQrv=lhf3Kby7i^(2L>hO~@`HV!!tW3Ki=>vV!lq@C` zAlhO&1_(NuSPxO5X=gmwbA~;@kY@3Zii!O=TcA2rbt>m|l7E1>^uj_rD z5tZ@hlo?DWruml3S@wh*vgf@9{OkReW+wEBowA+f4b-AG?!1#u))yuQ^#``>yTl7_ z8X_yIPwdz;7nBY7*4uFUD4MP)(pzFs-1I2cTSb8yDJEs8HwN2OF&9aZ4EYl7CmoWb z5QUu`RI**$i8WWBHy7kD)m(9KRG;8*(4UrjNigP$pCU%u(2-Hb=Xn@t5Gu$P= zRw`vB8Tc_FDpxZvCB|hHb4ET$Kz@wv>4L;|j*pW*f{9__Osy zYIjtJ;FeKC=XOn9&6Tzhh*%M}&T5p4+;xC?U1~<$q@up5r!dja&s4~%V^`vtKA@I1)I^71Vi32KO{+&Wm#dSCLv34i=A10?9fH{<6$S20_uHx?F?m?4yqs7r>m%=NxoTt2DM!?xKZL4rje11mP~ zPp+`CijB#db}0)9G|%~2R2t^Gjuda1;BJ(#e*zU6z;V~t`JhLbK?yVTv)T zBuOvMod*%W2i?1)8xC(Z{ElM^v_EEQ4DI0~G_&DkrXi$~Yt9pS>J2w{CUtQEsdMq~ z6_Hn!l2hIfT;uhT3i`W>$p7FP0X+vJVLbyq!+&Li6tq6FK}cNno|Kj<>alqP>L``S zrbd-?*L-uyPv-qLWee_%SXK{K<(eHcy#H++g7yT`Cm+zn_iu9(Q3O1{pMwYO2c3tQywy+{#y5i{j4*44 z!>Vhp@?jC}Zc=!R*DR#iIuqf9HCK6BaeMJycNU>}ym(TctnXm+$tSVbk1Aw&9Eq(R z)9sBW3TJn1AAIHDa)=}H9JKcudJz!F)ZZUPR2SnEsP!zf`pOAs;IkFjM`+YyqRIm+ zX64@dGN$0a_u8j)K7C(EbmF|#x2kr#=;!NbC_9(C>lX!sq?E#b(=sm0ivIZ3xFLAw znnObH=OxRCN*@8c!P&D_U!0#uoob9E}+p_Mn@0QV7vAgO~&&??z?6qAccCFH#Q28s5*+BS<9M{pZ@^}De| z_$uCm*An#eNTbK=abwaSBTDx7+k#lg{ghU$?&ld-IxrzmG&B07l0V8Nk9G1A)IVl< zgLV?l7S!uUwrEFnCJsxsLr;JldSXI)OMqk&Kv={d0jKl>=CAmgemr_3}%+8?H!k`a+d9QKvdkDgy#xN7K!l2Yk zVSDp_apSKY`p%%%xIC;~4Ww)fR174Ev3$4yd8~9~BuX?CwUn{3eC*|nH1&c!m85hm zjhtPi8aQGQvOuD--qQNoZQp$4QI;|E0%S#n<-FNM@h}O12!IfQz-j9Mw0gS%_v!)N zaMNHyR(vgqK|lXnr~VSRGF#$@I4vIs#@~qbKMnd;QXG)Iyhs_S2Kx9Rft^;fEvg!j zez@S|59wD4{Vl#fk2WYdzXl_?coe7jKGSAjp2Fyc+J#XHNvF(rDm?TLRu$?Apdsxq z);ovM(l}QtL?WY7UA~^(O9dSGMxLIPaU$LrTslsq7a8!Q^a@en#L>R65K0P=vzJ!s z>ojVv1@MNz!415j(rr0~^m7C-KB4{mH(@xt#%|d@_CIVNui^iC@$}#8biai8}EvT9|H+BgfL7|v+t?eV?mD67aLjLtsqB1GA1j!)e>Oj!`~wYQcdA1fV;Vr zi^BkHUg!VAj?3Wq`AxF;Fy;9z5>P39yyMskr9_hH3^Al}xf3ZfjpLW6e&Bky@;N^29TzdHzO*tIVgdsrwulF@K2HxjYoX>#y5Vh6P^Nsj5VIO1)HrEhcz51#f)7|68&`I7 zr(tyLI4Y$?pIOGt70RZuHx4bcyu0uEi&3hm=_+uR{~2f(>6 zX)6m1qHT`tcuoKtY~6~_MH5={GFxbmAXGyt z4OWdN(FY9{OZM$jD<*PtTJD7|XjTzMX+ScBgw=9yG#FlybQ`xOWUY1@XPdFs;U>pI zyP$*5ZD1EEP%bRm#zdu#C*s++a5Hf`2ag6gydo&nPc>9Dc@`HR2DM>viL|gq_qVoa zE>3@sYlv950g>R#@zn{e0P{2_aTvepc4jk4Z=qf;k95Js7?3GxnNS0vw=BL|0C~U* zvQ{+UQMN4tnf0tusY@0u=P$sXFAIs8HH!I+cnlsJ^+U8C=on5cN_mcF6QZJL6|R0H z8r1?U0Thuk&tA@szP3nRW9od7vyq+Smju#SxX=&~O%XyyDf1!)wa&mKB9m zr(#X)Q9e&MN*H@MCgLg)g~w9b^)EL z;A{dyRGwd)U>wx&P`B{k7pdV7A!|iKypiwMEq1g)lS5qsmk9*BM!U88P@ny?_{T_aS&rB0a|y{|v*>Q8;97?D7q_u|r@9y~3W{^00S=I=uGBnOTs`Cz`h& zaL#KttX$|CH$fXx#vf7afE%L{RaS6Xj2k#lg)z(k< zqVNAX#1H;_5xivS;`)3lfHAzM5a(M=glXu6B(dMXja$&b_^NDJ%uNo{j(;bqny?eM zEWh6smarRpb5SBLjGR-z4~o2HbMnf-2ZB}}Mf1Y$@%C>mJGA3=^l~5XXt09)Juvtc zbpBeURMJqGlZAVe)<|O(9&OC~ijR5@Qj(Jgh_BtP(L$;O`kkL`K-VgT5!vKbg8L{U z0X@}jP{k)<*BXH!Y52*0^7x|p`$cnm01i)=HxR9_6Eatd_ztx7m7PKl6*8S9S&B0J z5E2dp#Q2Su#sJpEl|>ev%X5l~t&C6@&_%iF5Ky8j3R=tXK``VOGS`Z&(C*TkJU!(- zWw}Kx--d4r)oIeXmY6pCSw>xCHD1Cwm71Pb8XgOQk3_AfDauMY8sF(Q(F4QeHmF;q z4eIeBFtV9K^`&(=5hqbFlD){_+HI`Fd=spd_w9e47d3>RAs$Uv#>u&vEfc>#MA98L zsv#c1O7-TQFem3HMITA9O_00OulSg8_f^S;LJX-pSk|{uP2Zyq*V`j9pYGQ+gubUd zW}V_mh8tiKA>=J+zO-4fW5qt;OO6eBI3KLv!f$-iPXv$#FpckWq8d@3SSG$GP`lN@ zgPSnak>gpcXw!-dCw{Cs7p)#lRTSXKGG`5Uz;K@tSqMm$*}P9nrNCH{kcoGo z-V}@)TP<-p;kWP@^O9oW3k)1n%l^jQztVSnFoMJ*EyEoAF3QsyI4)SECsfwpm#VQ z@>2e#T+D-3&)S1QO5I!YtYZ|tlwQ=U$xymP;N+AaS6KNlns)pcbc^Jr;eBdIuRHTy z^VFj_!tFm^tA{61(a zV1x<60$*BZEJ8l93Plp%#&!r&8&Qfyy|HrQ_=PBbQ2=Q%e7F#eqC;lEL9W|;sT zh-emk!Kqt#p|I3SvRlp;!z4W_aUP`9$$^H-yyD3?=pk=x4WEl= zWk6?VG$q53nkd}c*=eg0P>L5oe*Bj9`ZQ~Q?D5&thkpp2#ScjVOn<#1i) zyXA%i5Od(j+@^&~Vp0$S$jGfQB7#)XC?Xd1FTiZ3b6ea;zr+S1t`#G=#UY^*Y2DO{ zGuOB0>YosRRypJyv-V>WPP_V08ih1;(`@ar>+wbVZw>y4+OEA6**s*c<+90J-O;$s zcgICn(-I-$UI_pL6p5H=jN{^f; z2xo}b+2oiHHAS(bqv;=WkzW)xD<(WDu$)A;u+y1Ukf=Mo=~(nIi==A@~L* z+y5ta0nEF<4}Kqq@PAmz{%!yGpT8)jeP}+aXy+}d-DI< z0$v-~fYdWwp>bxr&1i275QULOr$XmrLap=Ah!jO-PQl3)0eAeXb63})_Uc_L>5(gL zlubm?9$ayoY4|EyZ;V80X!Gv&qifrAtqR^5H^=Hv3X!_HdC;IOs1h#8cG|q`4m6Y} zK!FcIz7u}>1!)?~64t<>PmMTmijj{as{i_w~>AV&9%#}2($>ZLZPQ3Ow&E@9P$VX*e=q% z#6k2rFw=niNo4@7NtCEEjX)Yzxqo`$=^pDk6fY>_O>6P&>j5 zr_^wcG4JE^h`(^Wf7oje^Uy`8=Hb1A$FaNb&T;ST1^?&WaX%2|*cGzGFO|kD0Z=m> zzQCl2rU+@`Jaog#1{8~6KpZq3d54pznEIw8++4(SR#bhvF?%1;zUKN{S;Vo)D=ijp z{y_T;UZ zIw;X+p-nZQUF7u_=&v-Rm0Oq{N4GH#{vFwWNxF2<7i=^1cKQ@5RdHzolh$XgOR0@s zCmnM0>4>DCMa#VK&ij~ED2Vd zEV`4YgeZ&8QBBCEJezEL?9*sMqoOv4wUXpFdnmQ)!&#nf?pjUZ?9X^qgBsaMpYjW9 zs4inatRru(yNm);It)Me+J@<2@#SJMO0|#p8ikHPc&?TLb7MyWEO9>~C=Yvenk%f7 z+jKpelc&0XU59mhV1~C4_QcESDY$4)L-`S|8qj8|A=jvrR2^pCV?Fzsi>Ep-*PDGr zi3zWcyGB3n$jq^0h*kY(O6|=OqHm_e$QRCz(_w>(TWt0wnOcsu&nZ^eA23esVNsr`21Aj9 zDppy~mk!74zcrtWHeu#ns>67v_W+ov`C{8#pdE{rm!AEJ(!34QU3cD{Q)=rlS}R3c>FIy@$l8#d&jAn4AD4oEB2PnMZ?0HTD$LNX?XH_{m*@=+Sxu{9!z zNT%9Z`i5^S4Drg?JA-g&S5_TRc3+tMhF9x!Uo0{PN<&aNOV`T4FIe9I&GQ@T@ujJ} z>kQFfOT&TXSOy(xaxm2a!7kcF7BH91X0?Xy)U)t_>5gQD=|?~O;i~V0 zsCwrNrk2AjL(}nZ>8E2`}q+Ga+|>>Th9y{z-$UQ6s)u_L}`wm!_1bm z<`~ejWv^nLTeV>>D1$ppyjHr~)!TFrpgq-Aj&m6!wHMURRP^Q_qOU!hWiQBa?(?`wJy>rGxCiGo|N3M)E0Dq`zC#3s{aKlsW{=v%} z#0D-NIh6oC!#ZjWFNi~Mc)}LxDp_oXS8)0z!u$O&OCIJP+$$pIGXO^sqicxO2iq}g zQ&ze9+S})u!_9C|_Id1GL#*AX4UzEAClSmB zfAyLBj%g;6c&5rD9Xd*Okt&=T3OvRWaOr1Kkh^5RhOkoMHyZdm%HPrw8}^YW@Ff$S9Uk&lK6Tylh4`4NytjqTkt!8m?H%!5Hn{I<^+;SaS@q01HE6!$ zsH^hk%^(Ew#tNWUlq8q*$vl#1^0PA=e^QkO>|et-g%WKU6Crvd&t)$q; zR}4XaeHeW*Bt2cPt|O+*lezcBiSD05PK^79Q*e&f3b2T^PBmM>{`1-&o}=;i!r*JzOTAEaa{55Viy$2;S@k zVrz-eZC$Rl;MC&d(FVcP^-PvT>G^|ncAo#sM4%YoG#&9hgw=eL8ASifJNQ2$vV0Y# z<$o|DenOA`YNFy(K;2EMun?isWAP%)$|%5yFo#j2q1!I$rv9Wwi5(5*A0RNzsS`mc z$hP6m^^4Cu9XsCS4uD<(NEpnHLLS*hL8GJ7&@h=?c4}Wh?;hI@GH9M(d+WJ<%Y@DY z)2e(25_nRiz{O#4vV&KHGi#;8M3%eBI&ZR6_K7dpLhY{0aIidRFZG;gXFBs`K-(A1 zfr6dOQs!0D-}(xsjcQSnTwlcQ>P|dg>$VX66c9`aqtL2o9vj9CP8Bj)gna-Wo38Xr zw2oA<_iG4-tEf_>xfN;WFDfUUkaoS+Fw*d8jr)X9Q-aXd@GA|I3eRI%MEnZPG{ZiWR`tO8x*=cd*EfjKGQ^h7nl+ok)ZuV_e4AEZ=5ODkX1FANEhZh?t37 zh*`?9*U_hqJB7^riIK2RbBh_TA-3yN`CnFQ!h}b(QFtJrxo>RoKUt#xza8^GuJK=! zlm?WC&a&%gy;%uXd|n7kk`1liXmv@Vi6IV7Lg>Ia8zhNgLPJ8XEss~*XpC8kCIg8r z2xTY>T+2E&fQHm^xrL>ba@=hzLqN+y@&~CU+ainZR@p~ZiDfdIW%B69oUeV}&NRzQe=H#!J*fTY+Z$4% z*J{MRn0nA_RZoA~fckKSXmyZhF+_j7VfN5N`=DnvAb*_U-O%}veuO=OpbpFde(-B~ zU>|LcPiLSnuto9gHoVAj9+5!+K{_r{Zp{<%qFk=LMHO_^ zC;cY(KMfxa5I=FgP=@7yJX=C;UMIB_FtFqs}sQ!8f@L&Iw^AYX3(F5_o(sxMjVMu;&;sA z)6*Uo5=!UEabrmk#+e~Lo5@lA4|JEsr;R>7Uc=L}<>imtIPB)mkuFjlM=ch1*;{Uh z9LxI~;H!rURN2v`PLUZG&deu{NT$=pl6Iswyr`0pqsE!WX0x*T!z`IB@gqsz`;N@n zt|(y6fHJlOA>Ygy3KuEvCa%0PmCT-J0mIyI!r*Aot+u8$g{K+xm}LzwF@Ki=5fy|u z^aX)TWz$dAXI2@I35WUG=|`su7d^H91Y@C%zu5AfbVI{Rln%scl8Rq&5W@0{76Udg{?X2{` zzQ4*WoG9QE2-_v*^9!&+p!?!xgOMQtP+7Gz%H54T?2huq;o2SUsqM9$iq%=c}mFfHp}f(!mi%cnM0oU1hjKe=Tosi@~fNXxca$J-fW^ZczmNAT#P(&3ZU z5j!OSyR6b?1193k*&P&o?$j3Z1^{bz_ZqEppLU@~E8RK#i|^&BUC^$xKjmRkAD;Au zkPd1yOx@z*QB9;moxV8V(0(3a{CoMR^`h%9tB}}iWo-FfrH&y}b+GTa#5SULMqB`b4>gV-H%-(DYGqT9zsnubM_LE-3;D|C9xHI>W{vIcO6Y zV)e+0M~gQETBwpq5v$3bb_0Fs^U0iL;8s+Zm813a$+?KYnqNJ(m93^;1qQQ}KUmw` zYu{Csc%+lGEf;TLEm(^kj_2lvnP+IxHhzmd3Vcd1A-PNUm}L%eAt3cf)@1Xrq|KV| zAAN?jV70^ps;W1JVuc*GB z2-vW!&^@#I#h!#H^T$R34IYs5Xu{O^M)C{RHMVO-!Plxi@c3F&_@Vk7mNh`0DNhWr z;j})kNR~tC7*3c0@yUi?9-Gb*Tv(^hj%gK7*iFim%0|mL?d?pqPP6})=sm5_P9->O zqxunS$xnE@#ExwZXWM8GK{Qz#j&&L0tDE#yYO<)jwc=i z_MKCY&?CgtL=1wYRlXG?1!Igl=oWH63|sjitV>xJLjeMgHL#`dQYkoQYVCps>=13D zJNgmcifo#7>hZ*;KJprbu@R-0dNrelPDQU*)^(D{FjRMo#+Km1ThII@#g~%u+m0wY zGQXX&n$#dMjHd^;W=hZS7eDOwk_nqDr*6mYm3E=r2DfI&CDJDECe4?0V6cSQ-M1r$@uoYy zWH^h_oTghpH7l9pCRu+t_E^b$f_7wS;Y9r$X(zTD^fT-z)3IA|n@QOzeQFr(^Woe^ z6|U4h-}N&ppPDVw4>w@`@L^Cll#$uG3SkFU_20LeZL~GY#jqyZ8F)$Ay`L0z-MA`k zblad4%jR6U@^Z0%7Ml;n;6%2FSKj5;k2Uh*Bp^ z$&ouxN$rTjY52Ddk8U$lKrI6c%eXS+u0g+^4+DK=P>-_j8cop87Z8G9`q0qcjnToM z&Ge48vhO)YThWqSIP#1vJs(K>U0EDU4Vi6ehKoSH|*00 zm3Lt%`2HqYaeL!~GX`cY=AA2YgSX|)7qngdQI{dE+nz6-+*ik_nKw4%`}t$e>-H2m z%#cs(-F$(NAU1jnLsA_*na*I@X^A%7Sms7ceo$VP8mZ(GuLCArv3wVyP_jQ?h}nlh zvYFt)=aTquYe`=gdUqJ=M+fq%qZttUp4V0s?-sKNU2w~tp3{1`yTh?^2NX;P*6uG_ z4k^0&gG)4xaN`WJCu=0JA7h+eyj+~_?5182U7S)*Ed|GBEjbv8LfG@gno7;RWOM94 zg-@@HGFv-LI{T8C(S7C4$yGdOHSzny%bR6MXg_4*QnR{9rN~=lU$Gmq?6jP%IHdgB zCL$n!TRVbG(ip8?z(+Zl<$`4`N)N}m`a~%U?HfbU9AVAHhCAc=`OTsCdqVdcXb)o| znw9H;mya9Wn0n~Ez!R0TWZ>%-rS0b-v}CGpqOk!)r+;kF$wp9sH)oA`8ygItdD!IK zatF&2DH^*nE7qFEnQCOcbG~;VbaG1ZSTlS^jRPMZ0at|B*KY4j2o=YQ2>a?C+LF~a z1f8pCTr!@U#m5VbniyQcv*k#<-$5*ze?cjk51s0zBN^~Ms{@Xn<)~p#W%}Kws+i&w zs}@OZeCfb0+t>L7z;-STjOve%+j5-NHD1{-Kyb^T6KL zod!p_vml7<0tl*Wa%l+6S8A4SuF9&)q9PD?j_$9t^42HZKt3r1bK}z}J)-x`PU#jqqVGlYg_e<3&lefyyv-W< zi6LLyCAfrG2}TpY*j&ILd~XUDs~{e{3^3ugh^oGZ6n<00K3UBq+Z{rE(JMOig)MzL z*nS2&dj5pu`!?3cxtAn+K*e(c>&3@%p;Jjrab5l*rv0_^8oo|xZvzi)VW$;!zW zp-|SCd)8~+O)V$Fhd>;S$33dtcL3f2CYOE_zNNwmei@=u%c08PjNMjdq;&-rJ8-@g z8P+$NqiOGjL-ZAs=+jXhQH9$`b<=1jC{pQo?{2{=$2M?|*kBRTs@dE$)$Q+oCxOiR@(%cAe zWc@itv`(b^-1T@f>XkBG@udUkR-vksc?6!;K#&FjfbXLNBg5_*6yUt{x0&ZwJ{>68 z6v3Xs7qkBW_q`kU=F%egRBU^a;g9sR@-Z(;^fT0HDh8H+zH=p}(=04wy zF8Lt#ZqA@boENGF+DscO&hR$-J4^ijr$(D7qzgB!e!h6t*HqfU_7;gnIr_r61ci2d z&^>V{Z$D|x(rL}WEaj0Bi(V{^){)E9{hI{~D7H&gAB(C5RJp*S*mb<`hIpigPidEb zHdR3MHv7Y?-Z2#M;_kBepJqq*X;mhE)z>uc8p8Uf-(2(Bj6C7RZe&E$#s;nJTP&Sp z_F>33^CH7}Jhg>(jewgrLUf z#cZM8Lm|yk>)66{+42CMTRWj&_m{3Dam9b5HSiB}p5xNMZh2Wso~WHm#?vp)hz!TT zIx8tqBPz%;tU$*&@Pa6tV>HwV`iNNiftWmib$aLC{yb;c67+nVpBCf1DS1EfHh|m4 zN&4kheHbr+-V+>;&;64P{Q)RzSp$r$h}Ve`lOFns!WX&~T-A#9Y?&)W6|~lhm#qSh zqkwJStraVL30#m#;jYsRjI6Q70rvp6d)emr+6@UWwj`)TMwhcS?=_O zFxZDPm|T}46uSvYN6pJ6XS-}n$Z$EABTyAULQ)h}7*NwA!F8zu)XEp2_xTJQ-Az}~wp}qE ze^Z@D)twV}1vPx#ZD&tRAF46*>t7zc&ARU#`_Jau_rE-pM_54lPZXfsi-B!R^rx4z z!}0Wns*}a87XbAZI%m%Q@i57s^}BEuFqXaS5>(Y8k`J3r6*8QZO0AJenlcGa(xH`C zK15#X7%kw+tGZzcHiJ6X+Vbq0Wj@pman2oOcFi+Y0O_r~vxz;%Yzd%W@Te0cs$=)S zXUFuR)Gxd9iNzRy6N(kaWZ%@#*EUo%bw0X_JJvTk(tz zY}RJA19)Z&a(i@-{sk8u5eB|QERyp)Tl5GYyh3IN^Qa!==B#pTA6>d03 zaMn2v<|05ILE%D}j4TmquTIeawA(y4kv-o zDstn=!`B#8Z6rvUU1%)sph=t1k~RB_Z=AI+sx_@nGAwhMH@WdFH2uuQZd!EPn%vWA z@Gto3Rx>ndVg#bror>Flz~G33`G{Z>M{Yxn*J|7w-q02b~sK$bokE)rH(ll#C|0@$I{v4hOj1%W3y2(hlvf> ziZ$H2w+Du&Wr{X`p!VOBBFYJM^!tJQBWrtt(7AqQP*V85a*VnAC;N56Qj=n1fU5)&Dnu)z?Ui;ZFb!YxXnv#U{PkC^9->hqKyUO zUK7ACd*mQJBNgL8U{y@>JiZ8dld%3!TWqY^;4&*W?sW?k#_%AI4o}#*5$=LJN7QJ9 z*HYcHrEw%DCFIG9#|FH&BQcFsIeLUZ9$Iv zNct^>auc4ah6mJ3q!E+y!}WR^Q>$waVzv*2veA|4y1pKgW~zqtg$c-0TitLpO|qY2 zO$Ob_Vp)LEX2ol5eI+7KEp7O+CrGwj)mE+)!dO)G@cdjJ(rfhYqXnBO$1g%fw4rec z^HvP5o*^6Sp@kc+zx3`eW||1hiXaBZnNrMV7YOJh$Ka=pe?Q`M2BBH+2GVsS9vC%@ zPsDNzV0?;6+z~5DdQ>V6>*P)dwZ+}dDgD2Q; zW&*;%lRv}q0-|}iz-82Gn_BgL(5Xf#Q7?=fYCrd42K8vAQ`s;+`C{){-M+nqjJ9X` zaPI~>%G!oupF^OntRk2(yM6do}3wE6HbiTmCGVY*+d;1rQzp9GPA4$joKm6Z1kqLcEq`gR#l_ ziJ(vaG67?uJZ3}qbEkK9IM}%5Gy#pM+{SOV=-Y8!{C?UfLN%%rO8|IgCgYzi&p7IX zGm2Z*hsr~9paWu@sGG}r=fr1wf%@zj)n_;rzt55EFT0X8I4p>@fYo2|GEQU=0n0-d z@sP#U*40u`+MN`C0kugBumu`6$hz6surzI21U~CwE0)uhrQdcoTRG}!nN}R19nBsy zl(A9JZY*}22%DN2rBQ4ucIwicwsfm5S4&OV#MTTJMkHpJn9MQ`+(@H zkJ>s^p$6F0bJ@n5+eqim|6R%uE)U~*)8TB2Ijx!(A<5`icodyCI82Q*5(X+9D+?B#?-d*T93Tlo~jf!T5uYch1 z-VRZ$N!Sc2^t7@d{-|rLJ9GraB!n(`-T!bjNFliqk#bib&5&6dvCyxO#da8&z%8rh zCH@0^kXM)#Y5zk)o_;d=m;5Mp{GZ0Gv>N&#bQa5NTV;OBaSCSVn7^P>nUSyC%m5)A z-ex8PyxCFY%%nh*={DYx2uIv?z6cvh)4x#08h(d0HyUDBcuDOdN_`lIIIPXwbdlrUga!JQ6 zd)O4f!n|56wt=f)fA>{ju^@Mpn&sqkXF^v`2*)wcpKc*_3fTR|tnOPVJoa)=%RL1q zd$DX=@h+Eua;HG+>+;dyI4DPV_qog#XJ3Re@y~~%SM>hd`Hw1htdNMcSCuv15u%PwXBL zwIDNPfHh2AbY)iB0GPBTaEn#RpZO_cf%TEMXcOL49!zAJ(Bun+Q5nkcIfEiAROk!Uvv+&hknB(y~EqKb)nl@uR>|TOz za>U~o%XQs03z|Sh5Z;JoIkVIzZE-AHbRDbRPj0td(zAJZ#qxn4c)W7>xC85VlkLf$ z-FiX#@{5@Wrp7RQrf%U%E}vnQupG1cPR6ODgQ7vtT|G)4i9E=~0iGe;!_v%LVU^9! zig9LbuV+4UN`#ANF!}|W5GtGIy%m(xdk5l)9EYyC{^6v8iQo+?4Y}; z{1Pxutu;;=L5JWL7}PUMF8SD>1@Q4ohp4Jy!LqY~YP-Z~Ao3_|+4*JY>WN&t;|#Vo z567jnNVe~sT3N+YFeF|IH1uoeHwK|z*)o8Hc-ON&EKjQiL{kUkF_KB!T=)&_BbR~XJ$3F8WNGt?_JK73``en=>lS2BQ-_Cd5ya1Fv7IDq9bd~Y) zrSp3po`RyyM@)QD7T8fy7{LczCZQd{ZcDbi(N*xy2}?+ zD{*ysljhC>CfDAe&+4e?`=atn${VhV?H2)b-2?|S;odpilj?JkJP(+Eqo*~oZDtmt zk|l6aA14hrh^4ESk`(>o)DQqpnwrIG=puPp2b)hF1<;og8Lbj`lg%x39em0vVWsLbSaG{Lr=8fKKKwrzAb)`- z<)&e%m%UH5Bl0{*^4MAA{H6TCL8b)Wj;}jB5O+K3CaQH0p+n3)@0*yfuhxG&f!*qW z+|xLTY{2)0>*{UORn10#vL9IhB&*^?g0h`kM%wAcwZ(}KR^*hp@*tjLgYH(LNv6l8 zGX!_lUK9oWQ_~yd4LKF>G_9z;Lxd_CwRrgC(mPTy>Bg+%+Mrx)+@l=5J5(+KEMR_C zLHeWJM$8|rd);&7{IwN%H+Ww7OYW6=LsIPiCMg@z&F3#a{(N};`LJ_}Z_Vd=)potS zx_;b#XFyW)i2PzEmFd?n!0MLqt(d;*gkbMd9-l7Ylfqj}N%T(srC->TT=Eh&q3+ie za=?%7kaOz9a#&8Aet^B&tQ?eEywv*fyuq?LQN!X*d7p&Ol{ z19H!!mbw=!DdH2){wr|fgE8hA>ckIzGF|XRc^dl*>{Z=i-#kOWFY(GqI^~96r8o}? z^R*EA$mo^))qL4kf0}$%X|=Mecr$NN;!8dd#?>7+;f}uZ*c1~nqFT84^dzIT>s0f0 zj&qwuY8CkBdu<Ba?kJpr$h|eLH=M^8PR6sg!aF>ReZZM(mR!HOP&a6OGi|XHB;Sm1F?Tv)W+{e zp|S!BJ-|O`ist#EJVK6hA5Fle#5Kao*zXI?jU_PtTXJg@hL>5e4|aAn*uy*T83=z* z!zJ&yz$6agt@SZCzr^)Lfyec*LZ4j%LLRp3g@w6^4H-XBHi$HCQB<3ehOOgW7Z4m- zO*x=!pbV#ar&4ubxirwaG|YS|l_zTL!Djo%YhyND0b;I{&JFyvcD@;=Zu6)$Y@OHGZPFyAjShcf&f(dJ!WqZbzIg-Y&O@?-7KM!(21nh;hEF8xtd zbIa>@EAE{K@vTqC3#&=A^ShyYg?0T(eu;^^Cj;8w8?f*q-p{??7V?A{agP>*GA-f= z8w+`70u0gB=sd`_^!lvYT3am+CT!Tt3*i%2+`igauZwfg^FvVnKWUGqPO0?7&OJG8 zhws?>nDmXL1D6-whhyAK2@40zex{dRo!t9-8d$i>*5>}I&nspl*f77wMD$LHJ9v=U znJt*S?M%>>2d^)%>_O)4SJvwN{Q6H}Zg!^OC(O4dWaRt%Z>{pbHYxwwqWq^;*;a8z zYDfSx$JV&$w0=Vklz>nVHjfugk`S4K#InArO!L*q}2J^+>7KO%J;6Uvt=@mE*Z!2Q1GL zx0WiA(ju+VsB|eCOf$KR8~4j29lLFf)XFVBuqSHXa#t@WVZI?xqK6E6H||AmlJ^!_ z`4+{CARXIeS@ZmIPNXrYPjWf;HAlsC>)+>uLnJSK;u&gE5{i5ZA;0OWO{=z=(zKkv zJTOPc_D8hIRwkEVM8egAwOtG?+*TNrozF4&!!1wD2f$e#=j|Ql_YsT1$7uETqxgVV zW!;?qOX7N{PD$+feT**O@|u4e8UJ;ZYTqTJ-=V4h+(y|dPRV^68F@iMg=&L(eD>Z_ z_k%{qf8|9)9rguH<)(#caYTiDXyd+!U0(cPOEFcY7R6U`-X+O=hcQ?a?&7)gr* zi=u6Exiv_~m93jn$bsv+h8JPH2W`X`3vk@a=}Cm1t)<>iTQ{L5Fb?7|spb7(u6R;3 z1H@D@+G^islS32-a9i6kmcU-Sp^0!Tx#xLBhKz(XUrw(l9Cnlp+szGGED z&m2Qdq$|mxY3$}k3*?49Q;?0KNq(w$3%=q_QZxCFp&5UgYU`p;{NReSMA{e$%-*EAu=h7v~#A)X++Is@4dS7^g?id zym56mdm)%1`goUVzI))=Kljmd75(LX#0j)|u*j`qKn90i?_G;)Y)JI?p!t_O$>7^kd{=`FOOw!O> zXH4AHM`moy)u#wcelI0~y!g2U_a!Ak9_A$`ZfWzhB=8ZDPW)p0~zD!(b;_c4g$l}e3-qcd$#qV!o z7b(B#ZZ_+{*%kwibj>E+l-j%9W^tZ$rgE?l<80;H7?J_sFwp|H#ks9ra%jPfYBID71vRY`bSF#i zur3Sk$~NSN-4@F2%*;PGuO$Vn)em@SoRn4~N!--Y!gc2zEr!dcuE?M0Bm6cS>pxNf z1{E)E){~PSIdyP3TGtmgRut+PjXHql+^vC#{ybLaaOL}!T<)4Zmh26Aj#;&_-=}fd z;ioMz%82EnJ>BN2xO9Smc|S*oaZFGPV~z`hirPR+knD&x7CVC3O#E_eF>8%SDGF9t z`?}lgusVnSbOkXLrf0jwhTEndY;<+O^rGGhE84$VSk-2qsSZ?9@Z51yTcm0$IWyYH zNtW8TeY7=PM~WIOS@7C=l}y}8)z(X8c5-r6hkyn;o%}$m>wBct%;bz$)iX!!U-(`z zoqtljfAs*cjZ$zls(zN}Q7&80Rnt=4XtXjS&sgee4CPkNV~ zzxXsdmhYL>7-~P{M|VwfHO>qmE`qlib_dd~SsBnjVy(rmVsQ|fh>g=+#_V&0_eHma ztqe96-tRPSXCvn&k8^O&&7#q)dj}?F5lqyAaaS`{DE`{XXSn9|*%g3~3nbVJS9~8I zh<6_?4%Jdm#T{^jn}A4&0xNRKE;Sp3ex9yrd>lSDx}*b z7@>!Iho;ARX7wvJ95GE=9zcc1`EH!9VMWE-V`}1ZwKngeuAW{o{^1ie)0R)wRwGFw<*RTs zlk?`=!jCkv3;T}u#c(bE9<3n!9`;fTXy|#Q9b~HQ@ShuQv4YDrbc^5coSUpHf~})>-&GlEUb0L;TBHD-HCf-1`9klNIb04!m&cMx86 zR8c7l!_!y{CM@dwe5wW|mq|$6^}01uM&-|9^EF3v89Nr`9f>$zO7fz#bR8$C@*bzM zAZFW%zeKk$kXysy)nPLg2gPw8QoNUIJrWqszK}yD?M`jsxWAeko_JbzUY#q>|8j@F zGV4?g!7p+Umq<%s6hqZ&1hB<8ly38f<#xyh(hpONT=yB{tl)@;SfnB+6M_Oy8f{eE{0&K8ux2bv!4&60?1*(<*WS_& z(Gy92A2AkXQ4gV22-uF;x1#yQDi!gY06IjZq#YsvMMdh_y9y{j)NzkTYab@+exr&Q z{iZ)+ghoo*k`;NpJC&Iq*F`aBH_5{q$rf!dV?$1Hz?Jpa z-WxgbsuHFJcYoaTBH}y_WY;H}faigOynudLD016DZv0QDGG-^}^QzqD7`am#BTpYN z2I>=TSt`=@ zyrg}J68l2IssRZ2wHWNL(HV}(FXI|WMmt_kc*dcf0jq>V9zC`@j+fmMrQaMo)7xCE zT%gIwWRsS}I#_XJsc$rK9@OA@_&>`*cTWBmi>gQW*JtLVYA=lQ*pm5?_Qy8CgU2Zt z%f|ZP5j*_`e@+JWb5H$BIj<`nqo^s5P^TRGj?-}u;Wac*9lmV(=SO7c^3N9K1B6sb z$TG$}&X4}+(Z6+x+l7lrfiS>e8uM%y zuS7NzP3u?&syu@y6+VaR<3Od;!!;*{hk^9Gx)$Bus47D1K3RFhXZhqlwkl6q)t=U* z!*^c!{9z~7K|S)fg)%1Gdg#@KEs~x~|crISA20Zs~)&8mW9ujSMV4 zG&E;eH$(ZxmEsm&6c1()kN9D+dcZq+=(}1Et-wx!gT+{;=O}*lZKfJH;0FVoJ^i6&sYND3otBp%=le*$k zW~mi|nygj87@9&tE4!!~lAf7v^37dlcmi5F3W6d6#)~LKA4wyEYy%~UXqqS@x;w6- z_WPo7pnZ;KvnN=5WnU7x-8qlD&H11HbgJX~!462(*5IliWD%O}Y2gvT{R*}=y=f#= zJLqzn<*PeD5d?MUwYdT~2EXf(50RU8D2~YrsvNo7rLEHn67O(=uT^tjh#8oNnp{6+#F?uPbvXS7ZQ-(- ztFMfRp@9)kenU-b=zTVK-DJzIsg~D`ZKq{o(gdZ=d82CZ$v9imyE$`Q3ttPPMBCj<+AZ$5Tk1~fx;Y)Q+!ZGA zhxcZ%#L^sKqURaH0C0{jA!qGSYN2!8p2aCq#T`sN`8_G4T%O9$0?NtBi?wMqDyiq+ zGDQ(VpIqw9GR;7~E$P;2o{J@~!3ZY{hj;l-YW4I@j1?PA^^9RvxTVR>UlgsDe?P9B z#ba!MHXS8xD-kg<9?pt_A41DB)}$=)1r9npc`;C8swu#x+R5mEhKApm=no@G*(61B zd{MD&1XDkYCxVIjm(&nJmEG+ptD>% z@HU@BYLU3}q@qBF%yOWYST7vf3&i}rX6^~M6%T}ni3o2caJz|Q>Z8L4mLdf0-RGga zJ#@DmvqZRKnQYdDDY?isQZj>7los%Rk=M{2cg&1@iC^MQ;rtn<4XNd`F=aX;G=(&y z>DKTJ=B>2hvZ{@i-cgqW|8VM*8C!+(0l|UG3ABT|i_=RB*|))(Ww};ss9WiVTKPFa zs?rPY)gU~SQ>>x@LV1y3SmIHg9R(hi$Z{>qW51{U+!l8QdFc-4gRslqfPsPe94K)w z{4OyT5BCnrU%INhKRWQ{m>8J<`xx9W@LN%-l=6wDw{&9-(hr)v$>-a~tb) zi-w6=H=>KH5wffTavIvCe~-NH-fUU(~D($6oT>RhD9zQ006X zF)PifH$ja`>68-(Q285)DOJ)m%jOMlBJsjxv)*ytta4voMv^u@Pmvjr94nSN0kR&l zVOsd0>Hag^)o@|*%l_6Y?`WS@#!Y}ZxxnyC+10bQCD3Qsi z%AGs5wQpRZ?6IdRw{D~(0rVh#7mcEm7fVMmIN=@=w(`?cul-147$I=kC%{{gOgE_6 zD<=w|p(USg&bi{Wt=8m+v-+3x+_mB;M>fnD!ab{VY%)gRnXtt%1y4ccoD=*7h9)$Q zVRnCd98id&dnO(DF+FIZz6iuTgmP^Y9W%rYzpGM!2C7jRaCSQkpD@j4@Usx~)&{}} z&oYDx6F;0E*AwxTL=TKd%7I?35Vt(V06aQF(5A={^UcPrmj`8AnnRGWWi5C!Q?C+FC}O^L6tDr@-uNTJcRO7L~OKQq5vTPmU2fNLE1p4tRX zaTxay9!>oI4@ruCWrwg}$X&Q-wpD`);e-q*opLiUELAr=(d9s1&Ag7m9BpC^svEf* zL8$IlINFwymMbtwU z&=$kMULi(cqy|{f&0RP{2m*73uZUJ^o9&e>jaU^FvCFbBT8g*&lGI;t1h*gAL|^d_ty&k4@R7XCsTuCZUDzN4I=Ma0&_Nxe<)WCB(tm;_8b&l{Qxb3ejn{u@y6ijU7E#``yajbyiqfYay0BFd^qwu^$expLfOW1ryz!8A z82I}^mgef-(5cy`ci0%EZToIo(hc09`|nj%v)9nke>V(l^a6X3QBCT=#4==j+Fow< z--xbDi$?|7`S694dQ>2WW9_i zR4WdO<-7p2xE|=}$*4Jy+@vr&0^tygqS)z};#?g{vSNK9HR*T@p{Tms*>A0SPWYA* zL}v{I*c19GSlp$4(9mppUeybktGH@!&tu3*sQl8e=IeX*R;H*Wg_9po0F{K0nV?nK z9gz;Yj;oP;prvaCDW`pj(4qEgc8MreLq5Aom&Id3XG3P)SJ7~G@AgOt@rtO4FOj0m zQ%D-uXFaHRO&%=n8WPA}oxan#!5DqAhD_-WI{@4_LzT!m6}taJf5yXpleL#6sYVen z+g}T)qy1GwwsSP2GdHj?as-Gv(EY{66E<-&an<_Jkd$Z@Nd!oHd9@@r=0;D|8^?f@!GRA%n7 zojb}L$$TU|ctbg|8xe1b%Iapo01jYhfrRz2+V_K2Rd1B3$Q@7=I;hd5px~Ow9W2bQ zvK=kVuBx4kh^x})>WC`}b6lDg5<6)DB~$zod|uu3Cget$B2xL&#aX1} z4voolSqX3OffhCDRw!3#<*YvYnT|9!>LskME$1q5YPPGS!%oFP-a>qf$tkVJ^CXu+ zCHkavb~|0x(6M42^ql*w1RZu}_yzdn7%xTIP6g>+R6VEYo^TFOa3%$xL{*PTv-H-o zrU8=%?IOE1taAD3Q?1FDb%YYx@vPssx<*VV(eE%`(Uai`QYBTKy`FqYCvXsCJD$Zt zUNqG#cAUj(4(bR&eZcWI`YFHXXUg=o8fj4v`&ZL)GIo8(jLY;hbr%TAxH(;}L~)Gp z7DZ0&j_1}xm9>R94mD!zOX~7sJQ1PVPUFNNJ)^wT>@3J_&5v1!N=}nUyI4@i-Xi3b z38UnKyt($2hUJBp1(t5%c6lBQvuu`$g7Bs``l2?+HHFNQ5`byg*SRA`)2EBf)op)E z;Q@Y4&G>jHU)H=5-7zV~HbQdZX|8uS)rNPjxuwS-L$jXh=tI?QI-;BunY*L53_pkj ztWew}qf#NW!=VXhU97ug#X?MRJ|B#WVM<%P@p<=uy|8Sp%=O`c!HG4>sB((t)inmY zz1Iu-HFtyPOP)4a_p$_mMioL&35YUo&ieD z=`)INmA;-2$;|p6s!G*^gqTFZ13Tw;RJyZg99^a9Is7+HQG5B}s~d0}zky%JgDboY z%ZBy?{C-|oJlupY*IZ$NT(-*c<8PIA>lx+OuT$$S_rerS`{^Uww?}i~CIzFBirjp! zXS`si9lDKy2hpiAYU|bMnm;7Ck^k8Dl%!~<(^@=?x2@_mC2P6pI5vT9_E$XyV z53Q@vHd(L%kiHidH;I9b8g{$s%nS|`;2mUhI7B#LAFnZ(ggOeU13sA29=Jw`nMi0! zzjEb&!njuOj$-m6;>Cs761zkPAR}qb7NjUM%$!0kg|GNv$ERtC12GW; zHlfkjqhTJb71&UXu&Gd3LM*rH?PMojh~BNOk)=q~ve%jV1Zj(pLE1+AO!=Tfj9j)h z-V!kcose?>iR^-~7X8>|VjfscTL4fb{#6@fWo)h^{CkF%5@R!n)?3Cd0ZQl6wz^@B z)D7>z)+>7>X`QDhw-b2E{pBQ}Bqh?vdf92^9s42fH2N zC`*`*^I5M1jcYoVEB!bd=9*Tt01nQ84Yyav9odV1LI>Jy517%O!51EHjocyH z4*Wgv*7AJXL!C8^q;Hp`y{5cjxCT-ZoOd0fJSL`L#q zaU-rD`Yk|Ftaw9O$12CFOP=_lR{o8_{HucaHUx3a@Zwk#zLdbi@}z?JvDd>=gx?fR zNT4n#fc9~$`8H7WdSg)5TX9F@G0Dr|B`Jdvpz;a@$tf^-5dsLwfBnew4rxqe3;zR# z54TyLCLO|G{LFzm_C{~*&?%>Zs8+MViJ4>JZ2QWs0GEQj4!z_4X{JR|;p{vhO{AZ(DV z_(GMUhBE97uNv~~N?Lb+l9?MfB*EWq$b@>Gtm@R@+ZcPL)`bo~t)$i8Z~860T4yBm zXyj}S*9JSGZE*pg$XbTwC8|zJDsK=R+BzaSX{&RSJ#gb{b|I=?K4mU>zMWfk`S^Lk ziu{fm!yX3Cbd4v6Iwv?Lzg8baAml6`C zr?8U3)^&L8`2lJi?c8l0(H?!j;W%Sc$mAH$I1A#Khrxp|FJzvqV-J5f;1sUm=mG6M zeruw-iDV4}T^UTK0V0R+GRN{kg|<7@I}p{uajSTB{sr{k^`*Z&vQ`DCbu6G2$pVmD z`b&oNKRmLGfiqzD!rvQ&q7}zvffkls-;YYHQ6A&1J)|)S`sq zKKO|%Hgv-yT`78MUPP?`sYBiqFcGzl@TD$;;xp-co=F=0`y#DqW)GmrF z2R&_vJ?9`=hRWdZ*iO6K%U_j_ox0`l4Q#8SZddt4f zDEIKW2$RByYGyw?qMw!^`Z3n$iW#|Vu>5T0m@kVf#q{nzsS z9nIDxip|L@^f%FR>t(I7gJ_(F{UWmkL#uC1yF}0G^%!UvMn_!=)vv8Kw|RH?`8^X5 z0n96!BjyEhFZBv#!e__Vr-MM6C% zb51ZHi~UVkP^N074sV4<;}vL~G9{~rI|NDsv4nqLDF)T_m^?_Ea*OXt3V&*~HSwm0 z53L_?LuJs~IHPx#F96R)>_}57bTnbOY#23YhAvwygL|8M4lc7e^;#%aIlRN3n49NF zhIW!n|B8z8CKa@aVyoS5ZIp6$l(Wl7PO!T0NHGfC>Ibl2cJ{NH)(^zhxCX~rq8l$rc>dw7JEbVX> z)cXE1>lx#ikCIic#dxLl9&l{&E-b?+A;t%WxV&_O8L|>pn=ZcXmCZZWZ!L=GUTKuE z27<%pehZFswN0Ao49s_$win9b`z$exqx;ej)1GG5kV`bo+kHH8CytB6iQ4*mq+iaN z+CW1e|5l08UN$DZT*%xbJf*}@`&zkQO4}Y>nF(4u2(-HN&=Gb}Jz!#Sw864i735WA z8h8OTfM7xSv4xrZXKuT+NxR4C=FQ-c%Kgk2`KM0Mb|T3eB~MW)%DijL2k1>8rwuys z+5w!aepschmj76bpbeWIxaQ#GO4}X5YK{_M)zgdg$O~n)!kef0KCI=|{crEF;TZDT z3&v)?AQ?mDK2iqN;h48&NYG}XAzf&L8Y9I%-4baWgDukYBu2<=MptH2#Ns50bbbCCRSCdoS?dEf+Zk#6r5%Kx0w`s?b7TKT3<0q{Z~ zfa~i&ywHE-g#WEpuHXu0;0lHy3RXQ37&zaN6gM9M5G?0`{Z%pkE3BWhrMKYxA~(I<6&cRzppFFA8=D^W0mFk51QFJ@6NDOhEm-ZBmgcoK>4 zGW#KE=qYJR84-v0Uxk$bDfIW%m-xmvV^oljq=-SefiU4jb0#x;Q%_w(2@8bj6%JMc z)wI=%5f=$@2;Yji5oUCWfd=9xa)2^AO1DU~@&g*`nT@}0Q~wp1d0?=xEfz}E;!vhJ zWGm(W(NYIT{{R+u^u*6Z#~`*p0WBAWlV=qL69pp$gVzV_3eY#zH_-n!TM1*WpOGB^ zBF@HW&u9;{-~gh+z;OL-kORYg$D0@b7hX8sH)F^^4Z;6GV)^TkE8%R(&j5bG8*tEn zv5x-X9{|e;0Goh4{^c5ifB^EO3qP*uzHcfk5MLoLFdv6Z>qzJX&(HA9idCqn zf(s>a^A)-^q}eapN)(N)fFGD};aSlNnre1C!9SQfaAwCs4lf?;%Alt2%|>p;1nrO- zb*VcZLLODsW$qFI=OMX5PdL|pIJF-K`(er>x8MxXzAjsBQ-v|~d}97H8k$Ee;MoHl zjn@BmME{6{eB^oL04uDsz(;_0#qyH)`SrHNx6|+?QSJkIcD(6z%iWTZM-TrvcNyb| z_M`PRjZ-IQt43GO;%O7fcN; zY|Z{eibM82 zRC5|U_GMEZ~Z$QXp7rYPR6K|K_rY0$94)M#42k^xWTHixcw zT52tPUw)J=HRO^MWDUA^hz$d=Zqi0CK?uuOu9k?sxkx5C@8hL_V63e5eyiC?sw3zR-0A?~Yp-*1Zz zGAZ?zgyoRF8to*L=wWnfwCkEQgEJKd7|2tMkw4J&(Ys$*^WCG_5K{`!MP#Xs5LNSA z;1Joj9Rx$-sSe?qZ7smpQKV5}+f^k_LXtTdSRzX(ZOhqJLGLm9%|pvkiY9e3!dg8* zXt#!F``>NurV%c%TQ{i|unv5xXQ{%xCh+?k-)19K2G{q+SeH~b8SHVUNY5C^Xs1*c zm}OT%c%_xLVS~Kxecz5J=HA1`h@=QJX_FSeU!ZOiL5G>r^|)dlTLK2S-e_TFDCd3| zhD|4S@2ShuhA`~v&q_DiFH$J&kFJ2dudK6uua@@=0}1Pm)eVw}=9gq{&=T)MJVhV0 z(A7-4OCowUM1x3Rrtk`x#^}>q?GYg3K21RFXTO}~i&DlSmGeV@HtrVc(&gTv{2~8a zxb7F2Wxk5HFjpRL`bPx+C3ZZm_j}ZGJGArF{gX5+{F}=uve;cY@Q^7b`&Ap^|XaR{YD@$?hbm~9C8p>5Grob(K( zXEV)fy6Tx=;AX)CboFKMsuIJAG0)4tER10_uS`rG1#`)BF+F<)K8GIW|a+ieJw4~;ak>-rjy%7uz z(-E?3$mm0B+|VrI_MR07UZnvAe)w-`WaMM~#Sb<9fYtLef*jf z3lww?I88Gl2tYc;Jj8Z+(5SIc#yR*;t=J49_k9q^4nx~rWmnIfZ>cE^^aC;Zgg3> z*w962zdiGBD|giMkH%RbmF{1OgerwaI&u#A-{;!}s@pDNriv-mV)yIoqfYFVZZ`aW zZMM2dUsIhjEK;*>Kf`1k*c8uRj6#)i;4c_FmW_cPi^}`pO$EEwQnhUb>t?UkPq=A9H6UFeoP(GaZbVC;lKN2|YZ`aOY(>vfsow?9BlCJp zLFT-4Y?HRtn&T#GJ^RR-C??zI#D>I%J)T$BZMS;Swovf$vBPQC8e?*${Iw^c9T`mK zxXWIQGR*K}x!0oLD3N^_1H*?3_CT~;#UTVQ1kzV84PV1`bg4y3ACaP31F5>iW42IQ zzI>1mKcZ+W{yDjwVf>my7IKx;BD3g#+D)m8Rd&avN9$$tNYL%Bcf;7*G=@;S? zSq?G9*x*(i`#@UH>n=e8FrR)*WKxe(AscPdk38aQB1I!Ozt5wZ{eJ&7r zFnB5|crUb}e#hPwd169Gy^hOQ-+xmgYnc6}RB(SqJo~io8=y-(OKG>SQ~V6EO4M;H zwas^teZ;fX4}%iuze8_vlAg7gf< z1D*6N`4Qr<6JFZTzAfm9=cTv%pV3g{##ioNfIt0z1K!r-zX7k~w5ANtv-CIMc_kHB zXBGVlS!se|SHI5|2LvW24HY&Xh`7!RXA6&tGYa_v^988sx}SiI_e}S|^fP@0@&RX2 zaYU#{nE(La>2`IQ)6nil@^!OB*A3JX5lTsVEsL<{{yXT*JY~Ai>$a|N`vdXzIoJOH z*cREoAqZt%u`U8mCBv_9yEzGu>RC9q*6z7eXYS!U=~`?wUGz|n!4uanNWA|6_5( z#C6GPicR&?Q+x4Rycw|P+6ji2R*2P~5B~HAH=5h*XpkrDC2Q)nP^{Q64wNS@ZuUo> z%)@!8XDDjY`1e$76DUb_@mo$FY=eTg<@1+`(VruIu*)PtMD9av=nr{UT3!S|Tk&Rn z@dxPP)bvl>j=-nQClA^cR<7v!UD#~8kdr^W$LfAkORkYY1u&fq0dokY$mA;cre(>J z(2SiRgU?FwhC8;-ooeIJ^eRIA4A5eX-SeH=yjbgMva{958I+}4Yu>|R6tOFms}PN? zVlSK%yp@ra8j}Vf-gec=Nke)l{B50{`woN8W$vV%$7vw}DIaW4KGlFZOH`{ravG)5 zLVY+SkPDM{PzN^i__~}FfOx3Mo30d&vLkFYS}G{sx!PbaDBdM|JUC|RZP?AABm)3O zfSD`?K!-+e+fEBW_o*f@VPLV;tBK(fdCL#{FEKaUUXt;Ik9tfm^qD-ciFsX_EE;+d zF~Px$J2#rS+xReT?~1Q9A7$_OEN5-iz46Ip9W`Bb_Ttdk?c52%rLf~?QfK9D3N?_9 zUUbs7kAguHbV|I#+hIuK_cC~PJTo$AO+pl_K4BE9K7z*K)}swVrW_=v>7;51eN0<3 zp{g9f=sfFk5ef9$bD%v& z%H1Z+&D@V;q=k2cKX<%^V2dt{jOdu9GZ)Q;rBG*dXH%kczGs)ap^ILbil=Y>PbF*5 zJb6D;_&rOjo&j=rV;_=-YAJ8gb71wKjPA*9w7e~r z(R}O$pivr03%z%gy{DBorKvt`oInaN8Qc&GuqvJq4<@ig40E&_Rs5~b|FL}ZuMF`n zj-RU)kb;K*2!}-ftB3zP$Tu+-_`8Z&q^hNc1z1c)3Mnlfk`iK%Ce_r``i-J0mj+!b zPam0_graFslPq!0*qkwOQaaA_1*q$OLru^7rnkIMWXbWRoNwq??j;jb5IkvR^}$$r z!_-Qu-POlM;`_r?O*cqdA7CfFVI8!>`u>c4N-r0b43xx4G$?R|Jt+gs~M^+y!SG%PtxY6;h5 zs>V_VVWPP*lvoxdp~Z(|em)MJATV@M;MeA&K|GzHs2Eu_N#ZoZ+QG*cVG-*wah3|T`smPA4v3&nWITx8sTPj zm#Q~B8B$_|nxrss`|-}O8ERp7E_PuzLTVL`J9^3@WFSRpW-f<>L)OX!X}8-JL;xd& z-fMNwEHNS!Z;xt32!%*WPqBl&3E$f?M1&v$Nm7c~e4{R`AqbiQp(BVQM~qG{qK;^} z9DmK>O2R#81gUpj1T5)(KY*}fKZ!&~JJ3ukqwNzHCH0J`2Z?T-TIBzusM0D)lNt4C z*K@vt2^eg(gAcfxxxpZh7)#@^aELj0Vn|N^mccJtJV2~mwKh|nXvKijm@M41tR&er zsa0bv^RkOC<dCJn2_}xxfJrKg|`y9_TqWQ!$TpLd`Fz_Emd+&sai8l4{sJQ?|vG{RaB|k53ry`}DU33vi4k0e0DbSa^d%`|wK- z{y&S*?LeTfcup%WUu*(L;Rk+#afHvO$C?i%!)Fjak?F*I7$gMRp^dS&c$YEYr|b`i zC1$|y^$m3P9(X+O9WhKNS6?4a^A3nFKM;o7VswXZYm*X#zR$jFL+Gm2dX8D+k-I_o~d^u^r^N zIemdG(Mz#$KhfNt9J#!}&Ul6IKSSStEGw>Ba6P9Lh|bn(X@;7354WFkDH?B%fq+f! z@GPlibO}gawP`&&HM35mocM(Y{6g@ybvvYYZWIXfSg-Ad{`WQ*Z7OCc2O!kp0fajL zQ5*ZO+(zQ>3okh7zqXHuY{3RkGv~|i69&4`h?JJ*&PJ@G(9lbQijqaeM?-P&fW9HrYuHG8~Q2BExnCk zx=&O=Sfa3B7ox!(U%>%;r!HSNl6&biwT$vZNN>|sGF_+3j=k@J(MrWHg`mqKh58b} z+@(iOPohGuZ%A5+XQG+QG(ndZVFQSDhS999&7W9rHV$~2bdzTR)1~~GOZ*M1ySW=f zu?1o_zQ(dqyaTg_kgmdZ27VgJi~CN69Z~trb|QZ7zfhaSI2}BFr1Zk$JHljQo6Lwc z$YbEXGV>e8W}V_sUS=T7YO_7;Fm*)W1U1i_i}3 zJRxf$!hYvSSo!OF0kO06NR!L9s(6Oj%O65+N}xh^%c)XhmrgPpMcsqK2zN@6Wyjep z&Fb6L5H`|ljZ`rNs?r{F3o34ckJB^JBsgZ%MTQ!~@U#j3#a)-$$7prSl-Bv|4cFvBeOK z$xUxU)6V;IV&6&WES@JiW4&JqHzlCaSv`=dl$5w|eA)jMmnmFu1NoArLSA(Gl2 zm*Jiu##b{y)t7a}w?|K<9gcYb%&N|bIdb ze~n*B;}Q6Nr;$!CFK{+YYF3ep&vA}sToi7_5s(m;Kg1;8OM_4tiZ4^RWR_3r;R z>U=``XKvvibI3UXsKPx1s&N0o1p6Cs0=6E`=Kr^z7_F>fH^&eExd_RjJvW=U34S21 zXii?FT}Anj8>$>3T*dFP)>1LAVkp-9^Vn}UPdpbY?iuKve4uiV*mbqB<2=Ufh|87x z@mDvWABcKSBrLXqwF(T4xLj) z65loU93>4Cv8wdgy()%bx^)0SmjNDRNDj{&;#-rX0y`&NO7QnJFgmmrqtPRLjHeix zR0GE;SIHM6T?XtOPmg|AVc#X42HBHkG>nKqxeD086{>iuwyqAz>)vsZqgm_vKAt`G z78?}1NV2}`lmXYRU9Zv@lKbM&~OK|%FNTMst6f;%fUT2xpjSQ0&+*Dlc?rwv9R5tE44ifpx z`bn4D=eD0UUq$BZ(F$Vd^#<#r>Fu^=2Q6Ymz0+Pm1T~8(R;A~}CLlCoO zY`%PiTdoGa&X}O8AueK54x{LzV-o5buC%L{aSqw-Nj*@`tbwA49aBpwvxpD{RvFyW z&YJob@(i~wf8EtdrR2PH5*M;KA9qKOIx`I%%{;^)K&@!BDh5=hCyt#%6dD%DDyG>kTQXs~@ zhu;RdpGe{I2gad>i&5UNJ>&JH-+20z(cp5YI!$_SahP>!ywv#R07maIyXF~USYQj( zmn>R%0%1F-RIkDfl!kGv!xx&-U7gQ&eHgs)vAsAicZE_ z-+IvRVyWeNohKcrR)|!oTG#IyNQMtqcAFJVN4yHN0uJr=j;I>=|4PNsF3 zOFYHQHkN3+s^C*@-CjC>zLa$7dHDydO*!&(zIE)pJ`n5dMT-t^Jx=Dz ztIb6T4UgG+tNbSLK*PB`v-#p#x^XlS>X^HAOtNbacP=ma(|TjaEp6Cq^wg_ zKIU(rZGMe)mnSe`l?qqbcbC-y!G$uT6s3KZZ)zPs1%xZTt0sF0d(&=U5JV%>FB&CG za13Dk^bDPNiQ{297&XqC%lL?VPi&-`#m>`U^ZRLN>MOEuwki@|=#XS8ZyZI)Qm>t> zQ;!i%q5XaT6ftkMq=5Ky@SCmi_gAI?Lic4_snjlHQSv(0Py|PGcO*L(2XvrN+Iw7k zZ!G)D3_rn%4*ovx0ECQ%Wu<|9vO|oJ9DPcn5{AUX9kM(+C#VNuRuYPrQ1dLIEK4ax z6pC`AbVl=|<_J&^7ekZLSC^Z~K~CxW zO~>EMzUaR>mVq!L2$BkpR1g%(sNplS;C1p4$@>j7ayW_lAc2H$d%=jvbw!fPeSL5( znx^A}0xv;Y1Xu5HHk}k3XOUDwL`RNDUcSV6@$TzFq5PI^7-6-|>0=+H>Qt$e7(Wai zKKT;5tD@esF|=qVT18&JLiZ7_F*z^69kg}(W}a01E$yphgGz`~5({46vTppih;#kDl8yNy$19$X&T<3(To7eeT=WRsao*~8#C3H3 z{_=Lm<}X6cQ5)ZEK(L1&xsGo;=zs;iI-=FGCvQB{rxPNef>5D?8v;`h?DH9aRIyHV z6m1pWyP0AVTOo>%KIFAGwt^ywp`MbXOoc4>1Z3=rVOZfCUM>bTinFgO324SSZ5=xp$6*k@F<3JJ)0?n~0y}a2zV9JdsdS{u65lY8B zrKUXxJ(0r|u-IDhzU?1@+6F@BK|Xo8YyB48gL0;*XVG|<1Y2 z`u+}ZA2JHg{_zDLJM1EG6jwzqxW@rhZxjcS4P=lIIV8LsmZ$ z3S|^|m^N~n+E!L-!uDg}Kdcj+8-SF&%L>9b0BUYi@>x5CCd_%PTJnF^)z85vMREyZ z>Gkh_huUZl8yAIbdxN{yM`sUEA41FxnT$qYo~seFV7;MLJ~gKG(p_XTTbRNqJLzV+ z;*fF79AGe}kTS$pRJBpj(3;z=|K!Yy*K5b)?WPNeYN$5Aow~fYUo{zx% zUzUAkF=pGCM(Q)=VK?QJbJy(m`S}j352=ONCmsPG2ek*3A_&lfA>uZzF#)1N6v+{H zk5FmWM2b7apGT?iF3rFmx((5g!J&MpFJrBmry7={FchdbjaXn*`Jt2}k#5M4T{PYs zRcxi2BE)>SVeJqrWItCbUy|2RT)|c4DZvscStgpZ)1jbh=@n5{*Len`8LF9x`S?hM zRh;3uvd&Q1DAUn?S?Hk4b*AP#>9&|zjAW{<;}X_mPVHBSO;whWucp*qMAT$mgtp}f zL2jC?&<($juQG4CGB0U4vrRt-p4?yrE5+oDMWKDd&My5*hE!;)j-B9(_@=WbTioIr zZ~CYRN!xjGe3oKs;o>4^Wm)*tCfk8LROexk)|^~#l18SC{4_fgd)rf|Xf-1O1`*#M zF$F~K&M{+h)wh`eTU7SAAOTunlCoeE4V!2zC!5d|?*NN|!B`F9PHKdx#9ZS1%mJi< zF?Az2#L3DY^%jL!(M_wbB>DrI(bgcRKL`(l(Xk z6AGDHX|Cd<{6SV@pzPA|64ze+0xLC8lgnOF^6LWqA;IX(N2+Wlw3tzg**@`8+Tb;F zhHdaWsaLWqcL+Vd)ok=)|2{?h!5_&RyzSlatL^StT7QVnngmc2Pzy4{AYA?NKSShI z`ZZaXdpvdFuGXc|*hMHhbrTTFZVp5@cH2m15ZBKT1kaGYJx8no0+L`&m*2!zdl00J zhCov;I>Ml(zlhfD4K}m;i9>#xp>OwYPtuR<$sA=RWHX9@&hg4S6_%hSjfBLs(U;QH zQFp^C9yEyF-=Xk*owG z3Pv%XiW=k$vPyP<2fKAbt~XqK{F^86+kl%c1b`(3kZ1h|v+8e8AnRiNcVwBZyawPo z!1H9H!^CLO9{5sBTLC8}0e8X&NeYOIQ&dGHm;0=-tI|83kGPiKiujy>_yjg9>QOxu zluvSou*;4M*;ttIcpZ+Xrzd~Cf80R#P_m-j8`Z%{VNmbIN1=csq7EjgU87eRJ_!$D z3SFgGWPY*=ab{96h_}lQ>Z6lqG&?MpCc~=jnM88;i-;YmhPM|#|Hw}8v-X5h5*e;K zjP;OlgPoc>i?P~=>*xT?R5%Q(8>UmE9F~k%1MeLkZYp^m4j+?G2U4WXoY&o9?l5;9qaB!A~2V%V1%&P6G z{)9>!Ib6-F0coxF$4OVoe1lEZx6)cQEg857+8$da+hVcas>C3fV=B|(0t9#3Vb_5v zqC`)vmt2<=pk?1Vts%!n+MBH$rl^Q1Q@k@^`sKxf#L@0_EV;!?E4e;jJ=xlvnua@e za}XkxVzNRiu-=MW6w?nsG!VEck&R1f8R#+2C|R$QOjDeXl%7o6Niq)Cc+of=dv>(@9X0gL?0>E*}SDnK*R#` z%p*x5ek$8yU8OEBvAcX1S7_1(2Z~BRZH;dq&%J3ZTlT@_*sCv>mT8ul(aZ`}lt*V+h@Y3!E>65j^uq+9Ctxw2q*O^N> zc!!xsA6$9LS@>zr);5no612s(!q@i>P6U+#-Lv{NM2dQ|0f;Ott zW2cIj`e%j=WsCJZne2hv$ugSl=K69tW3cR3;_zr$!q*Q}w>`zauVPN6$odWWFbEd2 ztp}Ke>uBkc;$<5MEi%aW6a(RSgiy4GBt*P{;cn*z&{#9ox`Yf$q$VA&%(w^U{7_5y zt)A8BeH!)j)@K1=>iHaE{h43`o^s_Z(2~}p#mmd=;DRT*z6q7;s}tqR5alEG`E&#! zhOK8z3lQXt7JcQoZ4@^ZKRx)Re=rJX+!dcZ=-~-XERZ3{!mOQ(rXE)Rrwt-VQ`v_X zMGJk%G-NzgTx$TaGhr#p13LNE&6rm}g;(g0FNWh0xtuEZ0W}NCr0W$ z$MNYHjELmcbA}`s;u(fw#cN(?JRA=@c~}a&kALgC1%skH=>qy~ zo`C0nH)4AOBP#ktRl6Aob2^oGD7j2F)i)WDghlU0S%ceFPwRSxN*86G!5dfh6> zx!v1Pp9H;->+v#eN0d!$o0rQsG$e7Az@Sw5G00mwwYjw zi*&~LVGtf^EO+#NBBYHpW_qLTx&Sg59c{_a00NEGop$ak$EnF0>&vkE45QjnSc>41 zh|QE`BYlyOVYi%vC%02u+lPt@mS~KG zRkAqy)&HcrcJ)=5P@Ga;xl4-2w!Z|@R*OSddrN>~py=wMdPNXq{jr^PrBlB*txrwH zOU^&Nioj2!jI7~o9##=bFllD$valc@p7@$z-@<4T*LGU48>x4{_&(ewzn8SBp9j~E zs(9RVjXVXz{hL0LAS9kid&VVrVk58-OGWW{L04lwY0rze?WltJX|{LJd_F7KN*puo zcP(I9Io)-F!8^>uVg;KeiymcNlj58S{2{ho%Q#Q8bws>tQt9H{+H6e@sR8;2+zUo^ zbA>?36>>CzDRk5REvJt4Y2U5>j1R^F!N_P$kZ5=rCg zGrK{gKwl9tJNRD%5#r4_MO8l--@^|{*c#&1*#YfyM*j?(HAkcjVdoh^riM`%MUeFT z$^9WiJh<^akWL4fhkrf4#dKFB!hredu!~5zdl_N(m%cDB5#py~1_kB-F-ZABl$`>G z?VfC86TctYYU*s(Iwuet;ngv@7=3qh-s@DJVo518#iH;;9PX+aZh;q%1y24^VKlva zjD9l&0cRVom;+nd@aMk~Mft@}AO-+L;0Zuqi0=Ps`~RfAsv20k{OxO^6=mcAv6Rml zF^j`G|BEG55x*8bF=B`n*1>G7(mbwV(NR-FkccS`#}7n4Xvm=L9sp06!1RJS9!t?- zIeR1h_tnJsWdGgQ^N2kAQLj8bb+haXhZ}IU=cL`0 z^7D;ah@sXaa%?C9kYlw}jv8#4@w%~knJnM(75#p9u#Vu=??S}x5VM>x?Mu*b{^Psc zexkA~A~Rmzc#sfJswmG%NWMEFhA^BmLj($3b{hn2KQs`&gdFA0n8_qZ^$A9u`>%SO zuB>8#1!fl>70%<1DqARc+mTv@3C5z>w>lUVU|JWlA0bUE`XP9*Ph-v{8FSUt609b> zb*nq{w}WzJ&{f<@GXbWJ3Vmc)q=(Yp&2>L@LO#&gf(>@I8qpWO647KtRGtbLD}d%H zH?N=MKCQuP-zT9Wgb~Pxa+|*DqKNSEA;wX3blTi ztd1E2@qD6Qkc`jSu!tA|+4=7$)zg_8O6{ewsOlEaK^ds$uVI|5KCGfdY3;+jr;}?* zRA!mWDqqxP zW(#O!gnYUDAdRs|V71YNXgokKkymo~X^vb5tO6YD7*CF9-vlYOJ6adNCwwid?f+fb ze*olKK1^d6)&X1PBHGN=^eHpluk&9iDI6TLc}ubY*zbms-I$2>Syg6Y1M!-21zWK} z9+W4QbH>8qb=+_Qp*n~0;dShn6dT^V;9;MoHM0@ss+|6hcrtWWRO_-P%{Q0T)M|~G zgCz{fWD)#=i8$}wE)vOV1CCvD=3|JOK`vI26fE1+HJeKfM;x$3->v*+TvqKGv&Wa* zv+z7S%^wIjwwW>p-CmR-I#Z|WvNbfJX(ygElJC!-Nw3GdEpw1#kzkp`2hNti;9>{B ze-S7!=qi25)-G3~&m_!9Dssrdtk*$IB9w@O2XeByc^-~U)*Zh#p2<`_u;bPY3q@vd zH-K6>|yqA2Ga!- z@bPhch~3O!s8TsZN3k}a;F#;*6Qd(@K&_B?3PCxF$Nv%wcsGe~9-%aDI)zksIl@Y! zYn0Rv?vgU55UQb);>HottdemxO6LB)8KfJrzgW_`-gpb9?3$3S1@@a|SO>PUmwWu334ik>)*qtYF7+pykNoScj7oJRRCP|$|%2=rB zkGv@AamuJRlev}$nXhC3=7|+MHOUr1QyYT-Dm=7=tX*Lk@p2|gOGwh#d`Na}Ew_80 zq{~LV?N@X)UbB?me*8MbZPc4XMLZDlyac~5oK+goO7xZ?F)^VTd%|O1{P^y4moT5t1SJi{%M@p z?yqwq*zW-jpR*QC%ssQmv9pxoPjDS%&Z)yo37oIuuP9kl>P8A@6UMIml8~iWDU&K- z;);4r_u&WaCT5ji(G=|yOxzZ*`w3Kn>8$Mw^?rzB*j+AYj(Lz|HIv!-;vqufp3$-_ z^*wpI&L#Q~NkFv@tcv1$0X(G`pT6DvO?v$YFcxJHSAUG|`2B4z~Pbl740oXBuRQ;+1zW}>XYLyIJRZk6P5*Tclk*3q-niAp{O8OK_f^B1ep5yu|rZ=IkSBln*ahdQIu+FRD@Q# zSvBqaX2P`PVl9Qvbv4_o65S@XPOD#PUei8q+g%>_;CHM$sc!%p%J0+Zy8C34o4)J! zGx8UPSTIx#paE!_&|0NiR;rx1J;|>;#ou9aN}g6s-Ni!8Q5ANv7x@5$rQ#JQRZi(X z7*&ttP;1#U8|qH}ekYT+y#KcwBGjGI9DIdaVN5+G1ElgCku6_$O@H-Wai;e)Ec%4W zO6Ye`U#aF?kKL1p4=~hP#ak}aA4U7>D(@K)JEJ!tz|BFLF!r9itq(pEAV2PVH&lE^ zKz{t{#g}-9g?1kUrGFm-@hi#s;l8W;wd=(Hhv%-n$?gv7GsOD>$nSzVKjqt8q-Xjr z`T+>&rhW$~kYOH~g4%(BtOpm5w9MlkQfAFzK?ioRgE?np5b-=KRuFUP0&!!7ZMIwvXz1Wh2UIk|sL@}Ey1^r)gRSS$; zLyRen=CEUiF`}C11p6!Kx}MKrBbeKI#U>XUySxI?BsU%$$|Pi9Mp8{Fy_%*#IEo$* z8_|(xTlXZek_!x2l2!T18Hh}+#c9Hu#Lm#z$P@QyHF;rdCNHV7>=s1BwyR9TSk}eB z(dAmf- zFZZKy86MoL6*X~XMV`;8TX!Pdr#$LV9m@9Zlsp8kCD~Td*rusC~19aYe z9HPF9ltz0b1VM=r)aYr4A7*M5t;sSxZW+8NiVwc__C#y=a`)Fr=WJ$45o*h4cDf0I z8^5s)FNq8^QRSVp|mLXA=EXZU?CN5#asgKMI>M4*0~Ex8jH z_93{gZsZizM z8FSSmj$u{oIgNT68aR_dI-0T8SyC9Sru<$|+DDLr9%BO(siiqzSWGl_`aP$W>=-7* z(Kv}HL5o9FNjkEQ*2K~5Q_7quG0LJ^(NQ}*C;kFbJXqSW7#bg}$_Hb)tPXzJplP&x z)--;7--OA(5|riOJM;zNcwWC0m^yLlvo_KPNSlR)40R5S4p7!O3MyVe*tk?$g%JDl zt{GKL00t^06`N9haD=VOrGY{s$dp*?Yq2}mw}X?UCXzx96w*YE^6_!!Rb7?MQ34B) z`ZmZf)aIy6*u_lv*xBYL6!Cr0;$8NMYZ%GVrUzx;&;zCEq%D2#n~CChi&=d?+?Ss{ zzSrKBB7Z`zl)rcxL&2ur_N@DZCftp@JW`3qwU#-sZx;3oQsI#(Ul;HDYLyBj^if#3 zd=$vAEEkfhvgOG38eFSul4gdn`_p{oA2#!gz+Z7Jj9rI|W-&*)jwJhC;t+9&g@u?e z87$vlIy&0u0AgCo@~p}MD_%FUlP$Rr1pdRLE&jkHwC*U%xJ-4;giX{;S@oD)sVZKL zw7h@dCT+3=V2h}XDKoY)(4kt=NZ5!;l{sdX58P#dVY6z(7TWRtuoAjiPu*Lg5FTJ{ z-W_sGd*T>RU19SCK9d8+{T!7#RKUx_X-UMmN=ELR4tJKS>2#hlWQWL!VNYP%KIT5Z zVoU(*R{F4(-!=CHiYu8ua&^?5h6QYU3@JbGuEfQVH^i8^w7!)J(=jME5T4^q(BXvZ5R5YmXz*)fsiF-_WI=#ULtU59J^ri?vbr?TLF0!2w4tEhG zIOCWc3@}CFtZoG0Ah1ed2DDE1ZMq!5yg>;e@1%e{;K$>**fTjm;i=1=;)gxRlyP^8;x+;qj;!=D=fVMEiWpAyF#d?ctY=-N zXenzs!ngCpJhc^A|C2AQIp9Bm(sT#-m7NB;LIgFHwLehYt#5vFB=KMtaav73t(Acj z1Xgw;T4x&2*of)d`KQ;Rp6_94K(6d-UG6T=@|A@u&IT^e_GlQndqG*uPS9!r`)DN2 zB6PVx-oY8sZuOlDQuqw+m+O{1IyvF&HuJ?u$gL7M&|-?(NuheFA) z^_qad{-om&Gmu7%ii&F&AFnnDMZAQBxW6z8sb+c*&SFj_^k;Nukjpwr zS8{<@)4(k&YYn*8s&%la&Kj%NEUVWjHf~TYx@En=J9mZ{ttICiQR2&i41Lg^q)GKI zVj?2n0$n)!^=C9wiQgY}OgX(_U5O{WKiMHaZHel<;`|ICsj53_geHv0zIwEQ?=}a` z9_?X~#CR1l96^haZbCDeF836)*aSMZY?(+08%l#&tq4U?au{73{JTHRR?#^a*7kO~7Df1r6$Y`DRTB9k1ZrVJzzV)%2u#xhU zFwKtjndJIPImO$pa&-d`PaLbZz&7ox8^wL)!VDl|GZZrUm!=A9)MWT6SXt7X90#nx zZVorG2kg0LHk*UY)pfOm3oEoTZrC!G7u(a?Zr^<=+mOEx?2zu#5A*F`KTVBy=A56} zACR8oB3rf!uG?~~)8g3Ho*?&&rxM_^Kjvd1X9+WGRGjiL&WLl%+?t^m)_YAah+8V( zuQDU7Tr)Rtnaxq{jbKIBoN6?6sr|Opty2^=f1WYm4(hRAHV*V+Q78$6ScbVdPoz+s zQh?pEvRc>u&a$a}NQK#SrxPx0s0v#F zy-8k11->LKTDGiF4o|RDd0)0|o?kI5uLx{YsB!ng=jL@o5``b46;O8ad*HJbX-gyxVM-lAkyjU8EZGgwT8FJ`LKMMpOI$kfjtcA@?~c#C5h z+|dWZK+De62)arz z9|7RzaLF}N=EX3iUj?$)MBxq#q@pxn%9bR2E1b`0K{>3@x)CC!$d@w8jteqwP80{) z&Lq%CCLiYY%Hr%o(&uRrlAii>&eSa8>_B8Y60x_6unjS~XBa$l_g1_6>jj-JN?%Z| z>3S;p16k2Kjv0CyoXx+6DXWa+)}rGI)_(2p7qrkLJ6%Co5`8tR+pD4<^~E5?WuTS7 z^;W|P<$A{1i6}H+^2W3F^o$BeN%T`}?pZD>bRUn2zTvdUHBJyxM8=5SBebYL_W%bO zYV`QJe_1(8s~7Bg`gMSKxLN)y+Vdy*`z)g`w9|9#-Wr3s(N}%4-Ddh;bi~`auKL2^ zRYsgQB=a}Znp?*FTh+nRh31BmS5TT12Gk}vVd|QdgqZbRs5Xs&32Pr|(c)v!rrQ{4 z5r}Ea6Woah_*Y%9R!fMFf`kJ+k*79v!EYSBCl380p)h6~n+jKPdpMyGSh#u`mkPT| zp;lRL_~6sEy#)Hz$ej_!;VQ&lq>0_s?cHnj#;c_p+(AC%?pL>eS8M!Hy2fMT z-F*T`Ga*0&`v0oST>jOV^HKQ^RXwk?w6JhqR|Jpu_lTqdxE_K0yzgK`N-A8FVXuai z@gb7PWU>mr0e*dcd*l_YeDy3yF#(MpE&UO<9!5HpL;Yu}=^bjv5SM7YU8UM&hV(N$btawLy+Qt^{T&669=(m`Xu zT3lLsgnPq;8!OQbr7h6)>&`5Rfmo|utTbnCgyL&52t!p0Brn(tuVEr`^*|d?zf!^I z9+&wL;TAruECAhFgCY9}|D@A8V_`y+#S8QSY>^#jxbc0853|~pz_XEjScPGQKUzF; zW{J?TEAmKXK8r^D%+Qwy6IM|P6o2PB04;Th>?8R=|H3wQgj{ER;W0A+)&+6;*>w6WBg^Eiwu_T zg;m8{Dpys~EC=tEzw=;R-{ z%Rz?z#e?Pi!Goonm`$()c(5!J_C)G_hCg|*&kuPy>fa_>j(AYw)$x_30Fp=C6HeBp z?vuImbc_$7C}_jLrXW@jL|-6^*2eE|Lg_!C3DF$HxCB7s4}b>yA0*G8(EK+>1VD?uMcu6>iwYbs_|Fa4bO&J5eCK?j{;$eLGo62FlY`~7tT zr=hmmTbfC}{*LE!w{8f=P`Pu?-HtKtH|!)`GMyGoBERys@gPe-+m9Aq#2z`DI*h10 znc$(aG8cL%EmF}7ktqQ)_jg##=(OP@LEvhjwkQc)%H0r^|MVvy-NZ7+^|4{O0UKN$ zT>e|n_79LGT>wH4K#Rfw24L&=50HfIZ5<39EuHM`#0)KMTpUeF{_C6nc>2Qw=+E#< zR@-nwR>k;jdkrT|Z)q89P(4a2TN@TI8<3(vl9rOP=oo63DBD50mNZqOqM%?h{G%5_ z-&w%?HSa5vu=XM*zQ@hn?azAOowY>m%x}B-J1^HKzjU99{65~^a=*YIC?eP!5rx^w zupzqnkD(-5vgyDQg<~G`nKDfrCg7(AuKJICsk!k;?yd#-y1oPob-;E^c5qwgUq=rD5lvkFs=3W5}k)x^4j9CM?vq5?wBdM<7Li+9CL$#Yee?!R8@DLKzO2;nYXE={F< zNL)z8r#UOJdc(s99Fs$((&HnbSpJ}}~C8ijSe zLW7dYjizUPQejaw=70=xiFY53+bZs!T)PXit~p(6cROrDl>%;=X?^xjnl(?K`4|b| zvd8Ho6xxhpZn~jc?gE&gvx^057)h!2jueF9Gv93!TfM&C3lXbmOCad>1ga|#k?x;?aANfXLjHspQS%Q!T^8Zb{*)=lk@Jg~;Un1F_;oNxH^&toLdcePA?3Xj`AO5MTC-J* z6+6h`zlK*UZa0eVx91z{sOR&Hw2p3mM}hD@Ah|7c;`icpi6#WJN;(>{kG)wPx+d(D z$GgqrSLf|o!VGyx8R9)?A* z4n;pld6!l49BEO4pY1wn5Z9zgjwkGnjqR_)lL^fS zenz@!ZqFy2VDH8!beV6t;T-J!M*^LIpRA&q9RWvQRBGB-&nJ*;*_W%3+*8uaep9Oz zRjVOZVE1f-t-RVBGq_z1Fb7!tFflyAN>Cmt&GGNC>R*0~$vP~ge}Hh;*a*N~>4-BQ z*qmvLQ)k;+&TMiC6~Q$lYl@G6YX2Cq!xUt_Bog*pT2*6hNWtMMuK%I6i1!>ey)dZ+ z${Fo6`wa-QKvPj67Nk;10gYTl&_`u7j3P{$i*n*wEeUL}@S3&BK^*r%k4rBPa6wOK zZh=00o+++!X`y2Rb2;`{mW&Pn!u38-810Kk4D?VR&a26`%)yE!3|LxANv?Q@BoLhvgEu_9JCGd6i@Y09sF zNt~lQq=jnH=ONfXXN5qjDSP7KYO`3dR^cSYlJI)2W~cV-swoJ2aO7E{_lls-c<`J3 z8DmPX3sp9|UlGsENx{H%E&}A0UNC!8XsFEhO;hPtuzopf;pyE<<6q$_UqNA)B&iMC zp3hR%`SNX#W3ZMa@9-S2Im`FU)qp(CaR;WSHVTRl<@l?I^dJ~0=zK+te|MNQg@CR_ z%TpB=>kC8uqL9>)!i0iz`AM%W^;uG4vNX1gl{cN6$SD_AljzoD4>GPRF@Gj>+Ji3X zdMXWGckSohm)eIOMVKEPi;g|l z6EQBUq@LCs1#s7Gx|Ax>;zW?{pVZuBN<|!Dx)CFT)?g-C`FrhnYqv@w=hxY9ALb zH0HcjrBcO6LT6j%)f1r$;#9Xr)t0j;SF0_SdcF=Fvp1C<`e1VY%i=^pPttUvz3A4);C=25|3~^um#BM5a zFC$OPG&x48HhHdCY8(#tdx!EP z{ROOPT?Byx?<3ztYKlvX!@Gm0lEQ1jUfG9>)U=W_7`w-Xw1FU<(JbRdohwX6|M!o4)A4WhR|c$%ZG{rtBC&Zd|~ zzX!0Y(E&iB{9|=1x!C+G{gW{4BXYq#SnAu z47&O5N^2hbZu+%Ol2@jegT_jZBnoXPmv)4YFV|W&VDeWekPD6;;2*azb+ZZu4&Pe0 zUR@+dH_MY$>((dOzV6+c)$^+8xcA@xsA-K;2G}nGT4A_6?@xHI-%4)jh`^&j2mY;f z8-b;FhY9$vQGn0CWDI|{ZvVL8j6}}f?yo;>tI#P2@TJU>&C+0-4h*_EkE8ew3_~ag zQ~)Ie-7s&ksozi8KDsIC#^#NViUfw=D^9o!4~L|Fc{w>X&D+z%&j*xcNH2n{&DY{{ ziSR0?MS3TrRT|}+wTX0^GmD9R1)U!$b69L+*c~?8qo#$*9&|kSP3&g)SQ5!IeBM!u zZ>C6gh{oJBE{$^p8s7Yq+H**WGo*ZOvLJkUKE`P;%fuxKqccpMbY!I5dC2gngDB{0 zNt2UDX+==pI;U81oDlzvpFi;Yt9(?nl?KF*FI%&+*Ua|iR2YKj>1iXP2qtNXp}PKS)0zqS zNzUe#=4UhC1IVl5T$KnK6fF%m^9=LMzBqx;mAGK! zv`#Fma>;7X9Kt9Yv1x+c)~spr9DRFvHonUty8*joA`=?A%tJAHy~!v3dGgQpYX29* z`;u!N9W~IF;ZhDJi$t^IYxk~w#Z=a8!I>zlyN3NNb~!LJzgQ&$-=wX!pEq`(j>cf( zwD;G;G$i!yzR2FjJY(S;t=~!BD4&dIix^G&)65LO8)dM8RVNw!3?XTW0KTr;(y;W% zYIPB;#@m7j4H_dP$n!Yi_p$_!Q2#u?FUhJ4z5w3ND?nPv_>UEC>h56bXlZL|=M2!* zDi}H%+Sr)d{85{^00b~3j3kVIm1oKucF2qfymCTpslh=_#hcJzz=u5#h>DN?6cQ49 zL;{&J!a#Ig2M|hBb*yYfAv1+AkqKta_7!U6Z8nVNowr#qfwb(oB7} zi#ISaOuGM>qVAMSxDrbikk6E))g_B&Y{VEoqnlJ2k zg~V%6mLnf&c=02cfrXx?x9%zQvndlcm?JA(a(|Q!`*#P>T1aEFY7#-Bh0OxY=tA*h zT)CPq{+En$$_FVX7OFBN&uq0B%UqWnwKvGBB~GD|N7S{Mvpy_MaS-Qvv1R!c)GA|; zYuU|SvTUvrLrOI}AHid6$EG)DDMcw(uOv~2kP>epr$n(Bp4QQAIpgbHJ1+Y@y@=#* z`0AFb#QK=m)CxXlzp(!qPPcQcO$&ffx&lnZ{Y$C(=LY)!5l(*vld7%Lh6sZ1G@GIk z4EL%BkkmYUTlr;^s?sY~sDL#RB!^|#LQv95qxULw#3837=~3Dh>$fF z7Vo$|9KOk2;!TxWZA_Khde13qjZuO~c^2=CVFEOOb>p5B9BarYzFIIom;Vfd#>W1y zHcapq<7FHEG6vYitCWdA1?Z4-!bh8#)jJ*PB&9b0b_A+>jWdT<(``E&DC)AwIr*VV z7>}72NQZrk?=QH9!T#jN+;?Z0&AQxa#u8Y?E+HyQ)(KuUW}QZyn|6@x!`Sdsht@em z*)DXptTE)uf*U16{(}0>x`dVRzkCFl6Itb3CadG}Wa}3Y=eJ94HW}614r7S3Dq6Ro zKnNojsO)#FBF)MW#G`YDszYijfmKz^G%Ibnr#yQ>^6M}{Vj5IL^T1w$MtUI~+D5E= zN0736pLCd{q3oJ5hhwN&(n&A(?`>tjx-MyWYoM8-yg`nc<8>o(G zyJxzd3}z~4sm;2;S#w<*=njO$(ASqA&;bnjn`7*6s$+iM@D7w4FINRpC-x(1=NINH z(Pwjz?$7T5Gi7bKrRH;m_q5AGtcAme#63QHkAb9&fE$fE?^C{+uP021T{%QybSZh3 zhZw$VU?C|(*F2YxtNL2oQo1`0-Q$1{P-G|dwnnM%u1}1r@<=jRq4}<^wv=x>4bPpa zQtslN{=T8|s@qX~+mkiAth*)RmU0VUbd}*Y+VY=&m}UIEPZPL&{MoPyRq!b$K*^2; z%L{S_C&ET6IIbhUZYPjE{Q6DSQF<3v7Zl9OQP`RP5H(iiHK9ukBHE6 zSVF6a@liA?#jS5#K5lhW^o~*KXyeHXlQGaTsm+ZW>n9m+l~ zrKliwp&3dif6bbOUpV6eO$_oN0pDod{*nk<1eCP=Eb%%0_Z3?ww710aRpgy2nAx%P=b?5oP4gOp7=Llva z-1Kh`OZZ3buGn?!(8-~fkK8*i)0~}|`20V-!5Ct^u;BL{VWDfnjw>Rpu^v}cWhzE3 z6uxN+gK7eGQo7oO&)K3bz|Ns_Zd9pxB>U5bp9x=Ab5gTJ%V?TTOfca{(9ss7VI*ZW z_G9jYRZil+6q{a8XHKwHJELb6SkkA}kRJCH>{n|JCQxx)TX}AgSFrf$?$xO7o6a;X z$jhuW+1j0CDGmK<%QRGo+)!GaZ;9|XAr8Dqo!FFRUv*Xi#=D^9%$8sa%pIp&2q0U{ zSGa(I4c8T@=d^CN@$8ZIy_TPnM_ZmsHCtz!s5~2)xac}HZFsxK(>>bGviBI|-kDU8 zO{KVU{gV4c>lIv;!|t96wz(A8p~G&BVU?I0v3?m4Bh*}Tf0~Qxr@U748c$Y9Nh}?r z_309?&ND%&u6l@(bH%OD*Q#B`IDWj4m*sTj`BG~$%%k^65|v*mn&7D=sm3@^SEtYG z1y()9*`s~vMxz<{qjovXU|}w}D@Hi%;#4FnMko_q8N-!1%R)^~f@w|8wL-S1@c`Cf z);w7A=J~WOuoJLnG{qd8t}q5Dc0~W~dWIeNEpxUacT>HHC4?LE{Ab~~!ay547|1XHAuUy1^muU(WPwnfyrC{7_Jh0`2r*&-3f z@c69|+ze+&Wb|}L)u?&pIcAjG8FL3=Eucd~ z_yO5ycvy}jwCJ+&;eu1#(8QF&a|;E8L-0c~9Jh$If-4`aTfUpvL(+C}%C%uJlKHUfx+az+2y!j_{*WUoADq%hVqPy|7`ht6 zzsxXW4#+D{&QE8qGS}oBJ}|80t6!BHK8d3>TDfZ~jve|aIhVwg(ZfMPT~g$P5TdF& zf8~_Ww`b8bsdW-oVvgjLLX`<+Nl1Nm*Hx5JkRZWXVKOE`(rv0;59COawwQMGil~`9 zT=ilpnTt@0S|B3UlvE}Y(J}c%r|8g3Cr4vuxuHTM%%X1BHK_>xCh1dsmUw2tANq91 zkpy9u(3{@K6nxc-yl(42Pmz6sAgM(`>Y_TR!w7$LmMvLdS)g&PFvWf{pY>)8oXM02SEu~Ad9IA;l$mR|Enb+Y^WHqs06 zf>Y8;<#?#f6rrN*xP+Nw3dTeKX+mK(HP1)fBw5U+eD;i~~((hs>b~4&DHVHKe zRWD_o6y4l%ar9Ks<(_Ead(A{vur&+@*yXo!DEPB=9%ZHNv{4aZDA2&*Wrv4mars&d zYCotd#Dy~;(Y-p-b7L}A(R`4X5(0%AGLnHNS!FUb!*TSmk|er0)mUwmz5=8c^T(gj z5vta>nh$Q_IZ|BENkX<#D01-`=l2ChoKy8(C5GHS1y^~sLsTLfKzUnoUMqrc7IS<- zTFyjr7j^}(gc3Xi-sqcS7_Cti+D$M9=+Uff<|t5DVB*l7lIU+_kIGyYu1!Blu~3QB zTSc9|AHS|L0oT`TKo0M>Dt=U&69^6aAF#+JC@X8kBDd75onSoedGzXa8weXzqFbt( za?gr14qV*0S{l)iDz-@CSPub-Q%hVWbbs;NF8{cd;VE=;()7dIoG>~hc#lf#VKNwkNx`t zx$SIiU26GDyq!tLej1U1eTL<$j?TsnmsU%^xWkH{h4Hf56arI+ zp5Jx++Fs*#eL5^M?!+qGwX-wnPnUQnYRhO2coI>@&7_jf+!sVUicq&t}ubMT<%eNA@Jec(U@IMN&-@jgVf(s?t%kT6t7pHrTwH)(A$C=X}nnFElpcfwY zOsCYqSxgze@ixT_;}f7fBg?j)R^%QjC|vh;1aLvi^7_Eo465Q+q0J~nT9?7YIhTv` zBG}LVPV2@JPt|4Pc$HO9TKxRm^!y)`?bJ{1d;$Pd^aA(@{cjH>QFmh-7ZcNeolAe! zlJPTAAdCpZKNgo=teclAb|t?msSNI}5lNIQ+~8Mx%C8uXAtA3#r93V1Jb&R!;?OQa zr3worWZrpCdkOLO?&${M7;ydW`F-k(tpB(~dKPF#sQ5}a7NuoUwCPA04L3R@1Xs)W z2_-Ujq{!QIK>m5}k)??GI)my@3i*0zR?|{2+PaOI>J|EjB4%C|XUppQacgsC{||iR zr7FU=!!a~WdHWClP42=%s6fHrw@>+Q3vhL$v#ypjiJMmRmhV!OKU?)MF{d`i+UEkV zIhbCUeW;Opj0DRctBRdHS?h0yHy5`U@<(AVKhB+eYkJ50WST)irEsL*%?oH0#DXhL zYos7GbkofUE8c6qnI{*@lzpr3Q$ij3?!uzA_cs^BKk!HVwMzK^8-Gc={|0{mMLPVW zx}33o-AeVQ2!fJoe;C=1_XX@C{n2?R(^mxhU;-AKJ!i; zey;t`FLHHfg=fZF;@Ww9N+l%zPoI5D3eia{<0TE|DvDXqpg!4JN8?%H)gA?2?!yYJ zGvhq@(za<8Uyypya(l8=F~i@3ddI|y+9){`V-nM zg_B1pFp@PzNCv#BUKO5RmZDF(&q1Q*GQ3-{FScB_9Nl@jaH%!xGBu$v*!WboZYZ z&0jY$pUz5mn*KG#Y5sb@l3M!(oRPE;K9v-9JZv`tqh`FUH!iM55+lVyR}9WhqK)zZ zO^I+6Bi?~Ks4h*MbQ4f$TEow1JS`X5acr6?l*lA*SPD-ZxyKMP)wkQUQG5T!uq>HG zk~I)vcQH>QM9tP)U}IUDos2qfmyp$V71MRH)Nj_^<196+Mzt$z+_u_Wibl07HG+Xv zwOl9oz0fgczIln%deTf9$O5;p++apy(L%QjMp|gUdZIgJRnl{fl~Jf&!eu;lpOu(- zq$$fD4yliRjuPys=0e1-U^2tL(q3)eer6HUk#$mXpOwLfFjn(gPcWqL^I(jC_NblV zAv=LZctX)k`{L~~Lkc8nQ2eaRTOdFH^yDF`oU=yuMBIm+9(KI#eHhjaI$W1?(x#O^ zzuvfPBEwQE#_a^OxJvqpuAJ-+jXx!l-tJQj^jat!ZI2FFSTY($%qhj1-pLADF+Nlr z0u&N#$|B#2o_ctWPkA=9ET9M@gPMj3}qtB9gG5ec`~h;@9dy>&ARcIz;QJ*Gzcy)8yK~XVrl4 zfTH$fHGBjC)Tw%RqIMXx9pND(p87~BMwP1qPY8e8AoC}M#?>E@yRGsjcmfd9!bIq{ zChKz6yO_~4lD{Kd_41JE5r?d^^0PU>PDSFTUE;uk`!%!f^9E0C$X}3^&;_e{MH7+( zj(OHbb8qK-w%Ggzi@l)PE|2HTs zNKR2f-$peFXMktN8N*a`w_wZh^;pSj?Q!4N*{`H>sMVnXp(B$F z{M$TcPq$C-yPzA4Y{oW2rwAFyw$@~qIC#9esGbGMOLXcWm6hj7vsmeyX9}qSmvn-! z-OTyx#QLe2`WVz2Otbz@`f{-+G=}umx(qY#7F5hF-q%@QxiEfG_4ncPe{?EPLb%?` zb#?wGaNEwXp)$zg+uYRG`wGRYe_TxZ4cDC(vOK8AWhbBW?91-kp(= z4I@Jtc`8^a-q#Z&4;8Uwp z6>E$_%TJN06xmE=QxxOh)ImB;QGkn<%bKmB-&aYg;}N-`OH!*5UgZoc*;fRupFPN9 zuRoTAHCp4lM2^VElSN3?K~}vE zI@jR|A3JC`*54lGjpC-p!+up1k#rhQ9W3cZEFEWrRg5bNmBUMXgg)=2tF(V^W{yg} z#tBqwg06<`?)QztfZ4R_j5${BKVO3ofrrsJ;1y&5juo2!_iKT{c@zdt@^eyXrMVOk(eBQ2NnP8^(F;+S-w_}}`~Tq?F`Nk| zNL8e3d3BP0o5_Cga(lnw_Qi+CHZLMn7Svs3#1%Id)9PA?G}{-K8`c0awkgFGC6Xi_ zYCzBAGD*Kgi<7Rgy~&EK8JRIRJPJS3#-!@<0YZdX1;uIfrbGK$%9)Z65)X#=$fKn{ zjtli(g4C|gZyX_NvfV@@JSoL~el5M}=b)xc%F?Keh&NT$-AO;jvl7NQ)TpX@Ln`bQ zwNh8%%&b<*VBM|#cI^F5`%0>0NVjkVzeWMZXYo`?&(gbX51hBNQG)X?GWJpL7 zFmgW>hnW{Vp$w`rFLioerUBHe)A7cy8T~I!Hx|HPlbw5ZK#66bZRiI1-+8Vf%D~G| z8Dc!Yi+v{>IOLP3B2$)1OUg(2dFO*?sz#nVr7zC@!nRK#su-!ZVLOH4!ke0~20wG-xg@42k1{tbkG>?ZznYMGW9)#(f-C@7MNrVqGY13fEqKAb^<>kQ~wv_&B z_2F=yB6v(PiIDWhN7SE8@me7-eqIJ?*A~fIi7qxY&l~zJQ|IY=Tqk62HXSKCOU>&O zh8am2Bzyo%=(iTN511*C3vRI=ZnV-mn;~AAy%Obzc9?nTHJgMFsSfe$?J||*O*3;k zRqQdn=TpQ{i?Q)f4Wnm(b3 zM=-df9B0ki>{fHEnjwSjFSS*1pDCI|w$4$ONhh@D5PAa#1B5QnQ% zqu2D%^Hb^6XEp`x6mGIhx7tK9CGltFkD1Q5?M(Ljncrr|(K+t7eW5^A2+`1XN`yCR zsC-?reH$+y@Tl2snnT>THDBO%s`q)@g3-4tcYUbN-KRo@X&k7(+8^v)(LDO%`3m$w zcpSr{wJ8tjJ|lensdi(C3gh5}d)>FkwHX*dxG z_16Iqs%F$1V_#q)G@c5>7&l*`5hk9}TtKMiJ0e&tmAi~6DEr&3rmtTNe(tXWnD-ue zQSLVnrk+s+{9gykRX5U9JyGN+-l9Y5x2(%=E-5eI!XG90J4!dUcz&THp{U;K2H$3m zuWM|5c(b*^XFBWLE_!#z`~0Ur{rh6Br#?OcPky<$+IDN?)qBPI7UP9qgzriGuuTu& za{+GTN(gVNCcnJ$Wz+pvd!HbPLe2TFX zRXaUMpe^nurY%QKzE6q?Y9NB+`*W2Iv8^;?fI>qeR7U^fr%KGv_M!d@VQfwPADmgU zS5cxw@kkUj%1~43v`1*QqX@$hZzvOpNE#g7!2U2)W%Rn7KZUR)?5NY9L=!JBB$J4m|HMooiiP6J?0i)l|;3poEMptao#ST>yE`Z zH>zb8uh||5zv|Hi-K|px++lh;ix?=HdI75*BhlLwomaOQo+aVmeK~@^t8y`8e?64; zoYjSO5v!%{P-^8i9yX5g^>3WOQO<90tiZ9z>PVte2@`|!BEfX3nC~(ixSl&u!4;2Q z72x}+Hfd4HLJxMvUG3h(Zs|tK9IkQFQxRncW#Ioyb5r^6FIZm z;x3G!fRnTjH+6yGo=g6ok+=N77rLUlw3ilxTio~Oo&bCccUIprL+WYe8)7*AVR@D= zaOZDySv^JdgLNUf`{r1_G32b5eMeZn{-y4Fmi`L9mFamb@^L<`Vx`0%C|ZjUZSi?~ z1T}Lv((h7zwXE;pKbGP|B7_*^SIs8oe=%D_`09osr9`s ztq9N4unaHp7kw!do^T%_6$du;XxJ_-0v)p2J(6+)?VK=r_JVNJjYYobBBkJ5pdJK- zKa&cjNNIX0Dz@0buk=)sGnP4Dvy;K!XR7|OP2xQpJb!#rD%DBtCAWRASlr{hYf8e_ z3_ovWc+}Z+JYXt}-+CN!Dvx)aVSLa2E-h2jdj_~byxaQee=145M7Y&OxE@_sHnC;& z&R)i5+}YTa7)Pu+Nyu~-D{L@#8GeUe3ItcTuYNO<*sV`Zs`*~EYL{lKPdB;=C3mE5 z4oSYLc3G<@6e#Dnxe>f;!q&vU!j4T-WyXx0wq{lDjHG1r*2nXfbQ1lp_+Cz|VBNk2 zrxHj34cneRJg?E-Dthl+Q#X^0+?>!$e4uYDwI_1{LB{sW8CQhiton9)iH0dK&6;mQ z$KgBI>7Zww$#u!@#Xd*47%V-HECefh4kEgXtVnxZdoC)|%zh zCkWOgPt~Updx@_~2?^on3X1I};nm1nXJ&cAi|TA!-fKcOJq-<|3`WqX)j*5iDN74@@V*U_xM&x8Nm6>O@G2P;b8$41Z{;To^vs(?%G9yS z5BuprDw)s)J3`5aB9-)q$bzV-hthV2YX&$Sp1?CZ&Is=`Amw6|<1C%z&1u{he+FA8 z9*mx4FDI4fA$0?0xTsbK)kcxva%9SB`oZ=7@Tr#O>jK}LnPaokIkiNP?jO;#3RkHn zm&fXL)h1pkn{q<8E>b`bD4J@@TZSP=?ENvQq$u}{)qz+cF@{}c-Q57nRVQVbL9#T@ zBgeB%(l~6{(q;)>$ru|n-dYZ4Z$l}MexnR&Pw~m(tsZPLMX;o13q?^GJFIP$4V4zw$^HBxB8P?Z7nSTS>O2c8U9Q082{)CJecOS|EYl%5amtC&)D z{Znb)F##G8DZeu_5*}PwWankB8Q00WZ7=R1gJiR#F((s@2jikd2N{bjas6+E1a*Ei z)`!NgLxO(W%?0cVyGR&wBNAXS`v*&Cx@Ck7b8;i%1tVVFe&D#gq@+gmOj7K2Q*XRzQ}Wb_e!xT7^HY5V%# zB1W!3ToP*s9lC!5<@0TDU@6*WL0epj2ZsvFJSXNMglZNO(L^CJpAqd$mhBFAog6qX zB~tb}KCL|c$z3+&Hgm_w7LJ3Lf-^I1cnPr_^(&MCEAE%@`!mIw5BSukxVYWVeGAqq zJ(19g1HU7BmiAQKeZ<+7rGqddmv5Jb7A(Z5!ToLQh;|gCLQ4x}?Q>@*oyqTro}3AT zFaWD@5mWKTap_Hd!WM>1vP}noYIxK}XyFFjeEt~I;sN&0goYIv2{<#-J&{E4ea|n0 za1i;)+hz6`8TIamg!Pkyw#ju(jWl1V%v$RN4HVyyrqlRCGDy@HFs6N)IV77af3du5 z-fNEP*?wON?4M$>IoQ}s$>!bnXbGMF+8R*Wo^a9uXXU`A!~GzS&Ml3$Mb)(>j%joN zgD;P8$*E%cB}94Lh#-h<3;?KMTTn}SHKZ+vJF}5=RJe4!#dz)!_`Vg&;Gym8A!Xx^ z^{-RbcM~PscG9dTDj|FH52h z(5Fm^KIw1S=#EuHrX*1`{u)o@2uk2_gvT^pPRcKjg7r^_<8UEGEyOTe3&l66*9Q@5 zW(nR@l)4^cisaSKE20>>l2<^+EdH@aA6<*ly$~%3Oe|qQbuM@S6yJvUtd~A~`qw`# zRBath3LGoFVL(9m{@Z5$pMP4>#Ky$N5J)t&HIoKD2NT{Kw7hBSUNH*NXLOA7S02D@;e4UPmn6ArL`hmc@~8U#mF>t9YLt~H=kS>dh8 zIt(w5A=+VG$C~_S6Tlv3y0gaAIu%{%oG%H^gQ}^zF8uE!vb*qC(b?2cU8!3Vr_?qT zZIjLG^jCh56hn||I-9uYdAe$}`yCojs3xs0k!&DRZs#ec)lo20#Y!}lC2XUGPOCK+ zg^#!%TB>iyZwEIW#xlj$pwXBu{IqtdVJaA>@#}NZNS0i7mGbbH2p;t2QOuj-O1_qY zJzT#tya1yXUBDCPn@vmE0Ao{1DUlbM@7gkk*6z_3Ah@m7OnlzQ$!#x#r)3O!udZJu z)XC2JUiEuDLuA6$*@NkeJ3tl8YlK!4mR>7m(H4vAqgF`{DquF(H4Hr8=jQ{c_^MDZRQo*XI9;v1*H9h zIIXt(F7Q-mQs8pZ-a#d#x_zneJ2SvfMVIJbl{1ew-eBAeh7m2kSBm znH*%z#*QU*m1nHA>L1PyGuy?1T7kTJyFN03rLAFt+epN-eqJ&cZ?h2dkd|k-=eD$wcN=oWWobb>LW$@|)>5!$J@z{cbhHWiUYNXa{3VWmCXGkb_bpcD zw?Mgwqm7juM!;r7=6wV;SRyf~tsaPdo_mTAA8S$!7{KN*&_@`bjhzoJ&uppQ64EQOg9q#Auw!V$>WsqQ|1hG(K+1WS_k$dQ@v>jwGM=${NtsZkf z^o*`#z}60A7_nsQScU}g831EA&vnp7T3*wD?pt{s6k`t&uSa;b5^;7x2Y5_IC=AUl z{2RGdV_v!_o{R!TVOTmL-l0=`4;v1~cVlJ-UNIQnk}B>HtiGHR4$&J57M>^vdgwh`oG@dAo#;M{E zg9vEI#2KFzv<2kDps{yBhWY|jgscI(#=+^3d*(?}r(=f)QyE`AYZjS%)OKnd#?*WB zq`_9C{-ob}d+vJgZkevx+35x!HB$5o_s&dSfJI?1&<8Oa+Twsbb`S{tGUO-`8o(GF zcvT;1fc1^BntrhJC3s_DW>&2+tG;ZjAO zSOL)i_%!7#yLl)0IfWNmGjm#OjGUiTcbjQT$1?2}6ze2gnG1MAqkAUGtwXhD$sOCJ zIvR_3S+SUlFcbUJrDYwoWN&THxF>7yLJXrt{6xiZQ3CAUzALbsG#tbEW~ z0mjEz(HFL4m9k1_xL41`BfTeCibvb2wyE9r1t0jfg%NPi@zrVVD$C)`Dx`y>;NAVP zo7E>$Hmw=idb=cc_AfHK&yGFhy{uWY2PWDkAOq4}Ce;m?Pr3N>E?P{3W|8$pUIO{U z9~c?eSjEk{2YFj}9MZMpk(XQ~yr!YiR=UUBBsatX5>b*)swT3_^;?x* z^KK&cR(sZeu_9a_4HNs?pP1SJbJv!;(cf03zY#*i@(u~I8@KQhHyos%pC*Z5?~rWW zKLM$?PEH%cZxi~HUj|K*iLbE;@8k<;q6=123ac@-(V34LTWAPbx8Bhl zOh1?-ULMXWlWti?y&%`c3zafJM(r5KVl> zErJ(5k*>n_^a?42zO*6q0vKPBVFUD#KVo0xjqG4%^@yU?2H(>U*g&n+e%VAdA&h`8 zvp;#D@x;cfjZJLFo?~dhy$F-OfJ;VqvoKd(rEXK&=YPuVg}H}(hJc(G>&?wK5!w~R z6qY_nywt9xG^`B9{5EKu8-r{}%g>J0$qh-f`a&^Abmh&x$88C@!F^FSk>Fc@4m;FhosXiI_ArJb> z+vgKcN&9K0pAK*FKyz%PaD};X)itfRGtY*fM`b=LS!Bl_S>Vl_xG(cVWz*N9&Pd!bSwP=9aqk3v zxq-v$VV(`rlQyve-ir(c##Rttu#g?VPZm*l{kRn=RN?Xu&MQi7kb zL%Fr_qR9s{vZ0euau!K%VwTY-Ra-ydDW_x1x^tZzW1*9(ptR-;?D_^t_hW6l(J52n zBGj;^9rmj0$$%DkmOA!<2?7JWMRdOip<^CRdB2ZGxd zCd{0!{Q17xhFC;Wp>M~om1A~QRaMdpj@_nt@@X)K#|@z9Ea9TA*v~ek_L@RcMa~P6 z%&pc7$Gb-=_f;arsu{6s9h8h%Gf+kLE}dgO95w#qBrCHS|(@unuo@%JJvs>bx<)8Uarv20Go=^ik)f^A>R}* zi{grBAx5h!K-vcj_K>!~$PHd>;~ zegN(Z%@L@wRJ-x7FE5v3rL7$+LNi&UG7Y5LJ?N@}`{P$tf40i?GcW`@;PtTH;&hrA zg1lizzw@b>?b}3VJko5>+v_VKr(^aNY%-iD;!rtkF(#GuFx`5+_6>4j9<$acrtIHW zGdH#*7V?=rrFiJ4vxggzJ{Nj`kg+&d>q%;_{v?=?@&CvUd${$B=1YG>eXj)2k-mrb zICxdk5TWAlUz76okC5{Am0*8eO_aW+WQ(Z%-h3;V1ggBPvLWSw?zm*XV);E`FUW*X zgFO(@HhOpT3=B!%y|WdhlV!pLAW@pkK7c4kN3j8UB#c*ezeF)386hNUqLNz}8&0P6 z1Vin$9xK#^>6ZUmN*P&ic($OiLKgs2rIf<+kbZ#P@0^IaoSAND)qb?*r+H{DwJD4I zwHpVWb|$HpMtd+xL3D-5E{cVXU1g77QPw4Gqj)@B*z45WcRhvPZ+$fI0!H9JA4hx3LCsKtgbtN)ig_X zSova3^=2*i^%;zd7cu3+xip_UUVkLV4ve&@ML75Vj$pkj%Ec3`m(>kRyDeH6wqVm&DsVfBqd9*HZRpE|R!jk1f~dgd zcG$IFB)@%v-kMs~!?&URXz3$Pky;}hzfBf_Y-&Q-4n1ga?eheTd<%~wKXX-VM9=Ro z`E)H<^MdKp^ic`zQ%rsTn@GqjZVyUj$$UdRO3FtxX{_k+pY?{j^6Y3T)S;kvrmhV%_g}X|R$Kmcb`CD# zAN>HnmgNWDj{k;w;%MUJV(sii_m^kR9|QBB*JG6Onj)}W<;j8|g&80sCD=##LO|Sy zEMkoM1wuru5@kYhCwMiucv!-6KJP~96-=*5GH;Rc*U*Ub32}tT96yQfLx018KVH}0^Tt8ch7OMSG`!y$=m4IAAT7**6%s}P zzVOKuB7X+&P}N%lBcvU0QT~l0Vbc z{o^FG-I+WMW%NDdLSU%jJj8>cTf6enqb=7z-GycGw{~uyFp8Qqu3!uQwce`C1AIJr3BQ64jqnxg(BLURL|G>Q;dC+FIY~RE zEH_HALbgmeWCje}4U%!{0j;7|%I$}V%uyO_NkI0==*#@v{5Hj}QGu@87(=&}*(8X@j449HwP=iAQ`dagepJj`;{*jp2~FI>M5@ z(%hhZ_$zwnN1-smYUoELly|8NmEyGZj8Sh_PBJZ@AnqjGUNAVjJKukOdkN0f_?|Q} zE1J)LVze0Y1J04;53eU9)$uylwJtJT5QqgYyL76o?9>QZoP7ZRS6VNgjwjIU5*Sn<)OH zy8h}nRbG^ERM6gOCDx2Ja*Gl%p6mC&A&whXs7ecOteRUS9A=OkqUNnqJ5YjZ@UCAI+ZR^c<1x78nEf#8;vPg+PeY$@> zRo^c~J2x%V19tu1X4Q!8nvL0v+xL0mz!F#v$D#J7i|<#~Xz5k%nlqP!0aVGTCYslm z^T(5frhaJx+A_8(50mWD4v&padYXLvrQvq|KC9I6SNVRX7wS^expZ zDy2Qhg?F6D8a#&DdRGg4N5g!VR7U4oxgul3F=E_-aP16(3RF{%sSJ z1bWe^kq^(qTiErO{p*@`%Nw|k*)klDmR^3G1*2!bV2k7z7I)H|S?aF?g!Ida?dx4` zN>G5+A9tjmSe%EzeI?g}Po7i7|M2SB@>v9#PH4m?j$^Rm2azlNK4Eh4&EMF$yv)Krr~{ z7mx8tF?d;MKRLA52cw?#pZ_IzS zmirf>`9F*N&)SwTa5geGadi48)SmPo(O|0Uv=ddE2c+ooUU9V0x^U!Zv)J-RIFZl# z&6l&ZYgeotT3|cFYxyUTxk{YR;1i6_ z)E2=%uFU&DTZx`NKxH% zj?p7K!Js#35~GW0xII`#n_^q+nVFpm_tJab8U$CDTMbQ@b!hV$tB1bLw0%hy-9a;0 zm~ehY2rB339H(EYrXA3?wvyXYL)T#$AHVDWO2-=39l#N{?BwMv!BJ&hD!Y}B)?>vW zZfuKXTzPgVGlsw18ag3$pxCip720_`*{*Vsk={D@zVIahWm|#68dGGek)@z>Dl1$A z)=*-YQPuc9UrrPP5i3{@Y>dOWxKnlI9;Joc1$`-c+&xf*p;EznPDf>df?G@U)4724 z=<1*mmK;AxnkC&Qtb|ZTavKa>q57yn#>wU(vyIn zrA^pulExKHhUsI{BQs&0ukp{V{Cb^uj+4cq1_Z9}1X-=#b9Z@X=A22#P>T$TKU^;G z?c{8y{Bg`M6wbtW7N^ziYRK_5Ay4oH_?qHCHEH_A`1mCweL$sf`yRr&y)g^x0;WNf zF!L2$S5Sf(D9tekTE*O@c;u0a6L$7~_yqDFD1zgK&FCXQd?VCkLwbiD3sEC8A5&=) zypj0AwRqp|lJ``4JO+7`}S$JIKn`9UGFFSV{XpqsIULmBjexeVL?uuJ{jWwy>gLpkq>mp~9kWZd}9)5hWxVWL8~h z!AG*Q#XupLI;xIz7`xI zPIbaBDu4liC5=_GhtuS;HBuZ-1j9;WFAn%lYCgQ-0Oe+~nc$Eku(o%Udd?B7VtS&x zw$G)_)CL40^Jr#fIjz)#*+ynlIJ+`+&0=9)CFq^2F_Ya7)z|_XhS{W%d)GLDdH^Gc zyHRov^Z^r)fOcz*eu~sh7f20KHS3Z}o2LV5)Q*jQhVuTOtz{hjzhq{XwgcZNAMV5L zH|r?&N!13@Hu$f84JXfbc|U`xzY#ZYS&txEMT;TZ{4uEDD*=6%4{oE^+AVDvZH z%+yxVkN=*p^D{WPn1Z(vDX?~!aUW&*jH$~p`{@UYhPkI- zf;CV+4~PeKjDf77jmg-iq6sOsP#`FSt8$bX!psx79)MX{S^d*HU5_q@!R12BDj(Ph5Qa9c0RIuec%U z+``*%9mPs1l77(vw8EgpxOMcazqJag#%yzK6m8 zCdMl&o~+QHY)`}OglA;%1Dvfa<)ls4fMC#oSnnp{<8NeI!}}7bYGCCA{I4p9=|3t* z_8(#QiMAT5WxoCd>gY{h3`A};saJ`@BovHbL_cI&j5>@vv>pWA$v;xf!_xmytKCkz zg4+Zk)vvW>IsM^>H<{DJ_XAvA)B~cRwL(xxR0M+q-D6X!w$uh-jqmN#94e+Af(z-QUv8UljSZBIktY_Ss`J|V*WfGc_)vk|Ok=pEz z*J#LnH@Bc27$Me6X#+J_Z}dB{TQQyHTbzhuco!}A0E?C3 zh(YQ^;Cn`=Nvx3G4!nsCFbtV~eD3)cbYSvy9R8b1gMkyDJ;kPEk71Y>QMmqPrTdOz zuuoc}?QT$hp|%Uy3X)c_^r7$%(PZOL<&czS1CFmMW?GLjCDr;W147*M_Ml%_BKPY2 z$*{Bc)BOE|S-8zov8k;o=&Rc8CFT!wpHbOp!bxq|g+tr>YztY?>Z8Z2fA`Rl%ZCxmmV=x&HWY1RK9fdIySMwsN9W866Fyp(GqN+!XNxkMFB@nXX z6cQk*6W$G2!CKIIvdc>BthG5znMOpdHvO^;)>>{r)AG50{9c z+Bx>3pOg#p36v%HVFY(czC-x;;aB`SG2Wxo4%tz~&7{R9Oh%&i1t z!W_etDUfoB$Q4OwaT;7$9jyiJU;#6 z^2N5qpuSKa1;mAoV8AO&E7KU?4h~jBDsAwfzQDHy-86)HyW`~p9LcN(E~ zTT0j8(s?~MZAoU|x&p{se0OI;MJP0^4Ll!|-()C(O;OtmxjwZ0^pPGd)~$rdz) zAU|zmr;O080_P8^4g#km>D1YSs`vFAk{?%&k z_Vc6h3=CM75rtoWX-A%2Z5^(iJ%QB;H30O((^C|fNETPc%s3W=!SVIRFmC%_xA(H9 zc}-`%us;A3+j6o&FAN6;xiO>*Yt8k?dKm{svy~Z38I{AEq`pttq)?6U+P7V+S!TOl zOvGN<;8Bt^+PQT#+g~p5ZXgzIV6-b*(ieU{@h8+ukjxC+DxNj#JdHOiKWb{-*wqw$ z-AaSyoHrx)u_Cbb5}c@(2X*7?zyro@KjrHfi0g^OV>&V+Z(GsC;wf#yHbiT~gX!!p7C ztz#|!$5g;Sk{LbC=>);)V8$jUUf!Qx!R&+M2D%6Q24d;m#cg!Pn?4an(yqBDGp}0= zNs&fIu%>s9H?VRdIwBh}vdYvE;_kTO;zK%jw zySICFrs*QfDt6td;4F1^_DE#O2m^L6MHH*%ACpx2_cY(W>q?}FiGA>DzW^P8yCP|v z=LUZ3T^W(B7-V-u>hyj6)bp=<-y;Igb^-WwIDk**-vGw{_tW{0aK;W<;N!uC>K7!4 zO@=K;L#r$;iv@uR6LaSFJT`{RN!D>bj@Vrx2lg7Ubz*%i;&sf;CNU$wR~|;ld{Hkr zt+_7PBVh_&7MF{(Es^@SIdEp;MXjQ#bQukL%nMEw`uAK$Zv6oL zcX|U~3{g5R66v)a%^Plf6I5c`AuD> zYT^BZ%3ywCXNrm<%Kqkh_}!1fkA75L$PB#ls9vYss7jTDfdm_h?jj!SYMH($QNH`;|M0T4zx74eySJ$^w0h|07}Sq|ns zTxP`y6NRj`-lJ_74Kv$2@6)_5((jKiDH9;ByBqZN^ukV*kx0b@smKdhnTiI%$b*&P zVndR$(ntNRpBtt!k3^K9jlRYN8RU_RlFU&vXW50_=eQ52Cy&Px+^bd2a~ zlBc!Z*P5J~4Nn<{eR};CM2*}Oo#A2h(Z4A%kiIB)L>?CPh0*h>)HcoV3Oc}PKHdKp zm6syDPZ4|YFx;}Urz@dd^8E0r#{lDTq_@OOkrb0?A7hl^bFS^_y^T6;LLk$jTQw585+Ate4h_A&ccd_FOtoS<5XB*dtx0<#4`V`*1 zBTKAKOWZ!f1h3!ZnBwj+PA=49B6#Tx635dO0=c`YFaG+;M6 z3GcakEybHE)0U)^PfO2b&C3>ojgHcV<+2jBj+53skPnB;sUu0wl>IY!`twwb(3XvC zBY#%f1jb=?sV^5eAl7dOM_&J|pBohwP`gM>o7pzOyh~ztUFPupL~GpGsXtPp!Y%8Q zPr00*nVI+)5<&_Er3Q5orFeC1P_e6S_{;cCm}Oa99S5gOBse?GphRog)Rp7gO!b&C z>gdnOwnQ8HVXByZQ-jH3LdB$^E3<&fWrl5yt?#-0A#iZ2!-~=>h1}j0 zq@l$UGnmS*pVg~=qvzY2;E|viLwotJLUjm}MUC>aC;}L4%`SCwroQFsCD-p27oJr5l4BOc_Q9Q=O7p6dS{9 zAYLNk2~zTpR?k@7aE@aBHuBX?`C?ZIp5pl_T{bKvHd`mF>skgAGkU#shZVI;swU^8 zg==;-@fY@04}{dTT}E$Inl}w{@8P?*5%Of-Ui)JcI|f2$YJt&Y0H3bg?g(>T!N*mN z9LM*Z9z~O#b0qza>`m)lY;7r4R{3q3I${fz*NOG8j53-d@K(s!0-e^RR>4L5LEHUR zpnD{TrqIP@(*}71!}VSal3`#*CQB4B+{AkP?2OU6U@AZ{Gf25n&Z4Ej0l}0?>R&TnNgmws+wF~ zRaaqD*3xpVdvI=rWCyb%U8jf)C4O6_AZZ1j@gerqB>U>1LpZlXICFZZz?@VXY9QJ7 z{^B@&jD$5?dF}Qu$%BHOV z#EU<1#1_#uLl#7z1}nS~m%$EeDyZHtdcE~)*rG(fGB17=e#HeF584*^k@E4Azn2Zk z8nGFwd%!%Uhi8?ow77kdq>+w@2r48?CZn^!OS< z%{fUCu4ufUFU&F7i~y-7n-Wc*_o-2LBB+s+tyUJ+N}RK)Rn>%(hy@bjZ3Z{Kf8 z^FoVj(p(Bxp0QuJeVZDc{0nlSM@WtWJ5|DQVZzf7P6#zn#?i83(P;F3g;016@}!CU z-lu(NxT)$1M1Un#OtG)(4~oJaM5kYT>td7q&OA4Sb+JrtNtEP4=WxCvm_!zfA4jCpkBWj*QH|w z5Yk9f{{zu*xT0sp2p*&{HG5Clm80?T_XdXai&U?|1B`L^^y6*P!OS&%s90I1!_J~xdd)I1lCA|&(RTYt+c#s# z><>uJC!BaL)ka1&k-k($p6s90mzQ^@?b@rSH6q%HKAh0ANSK$MqJFw8B?=mpb^vq| zT6Uo*zE*CQP?Sm51M#0UkG7dTf}P5zq#Ly;LKk(H)^M|eIM5KksSb0?d5mkcJi6%q zwYQVAg1 z2c}s~_wD5-qEa+62emHFzt9GV7Q=oMau&Y-fznmr=MqF-pdhd-6(k!q2T>;CE_#mU z$&e@;&5%1`sA3UR?IqehH7S|`Xqq?fb$rhQTjv|dcE%<3!c)N*-V=TYy z{^>>PeDsOG@W9v<{m}WHKUlTGTarKy-}d^2O$}c5!)I>38Gbz?w?iS!L%;UH>i2%& zXYfxf^RREQQkcZ@CALZJ*!J`5YD>PPT{P*H2`GUG=01NL9>7!e)oH^p$PGs&~g>yPC)(hXZyr$@sP`aY=>^WyU$V@BTYnbXJmi5Gw9UItIR zNA9_ZY(C|gVnEdcgBPx}5mV7+>07(D(DS}M_gUyJ&$W9T_|l$7g=>erS0wsuuC$S3Gb+YY$QF%1j); zf^4%6J2k5(Cpf+A=h4M9Dft@ZNdi^PU*uS2;TxbU4n4Ov z$jFT?vYiXdZ_g0P9<)WVYLUeNiy|!6cQgGTihYF+mp`q)m~v@o7>kx>jMZZ;dT)Y9 zaf#6HEBkhFyl>orrPlS!c*e5w%nI|`t;5geaLUA^Rhd-IRvw=;hCsyMJ>^_6p# zh%5IHJ`M55mH33g`$dq?UI`!LhC8#x9HK0bj?cC_GqpSaqFO!1T;+h0mFqoMAcjw| z(CJ%}V16lNQw=ht{DpdoC?T}+8ePP0vWIJ8fIY)PlpaNf3{~^SlucYM_8X@adCa=} zkTNTrxIv#cqte>?+uh&Xw=OywJcNPet_<9f>i^5#_)jYpu|G64K(kI&18bMR`>9kZ z@BpV`B%cgzM`BWJRKENm0`=0M5OqrbOnhNMq^MK2u~_TYi{K5Ov7LI9e;BGbTp1b= zV(eqsxo*Q>PtjlF*VutX*Qtnpwc=&=zHK#!iS-d10DO;_zPd}UkCEFOG1d{`Y+{YT zUpp97VV^d3K6H$g@jvwu-}+XoaBAK`g%yN($D=38|DHo@{rI4Oz%CO*lHh^ut|R+V z+fVE0t&ZO_d~cH5`e-akPIF)@Fn!PvZ!BuQXElbcsw6;LVfD&7d%d> zgS3gG`UtG#-%M8KV7WlXfGf8K1fe+ocPsbjpqZtlWAz7xE5W*cOmhRfV!6h!$uNIc zF@*>F7Z{5Z=GH7#T1;S!?`nF*^RVfJ$kg-w z#|Ml)4&pavd$vK*K8`iDHUFnvFdEaLSS?jqo)38)zKh9 zf`@Dy|6SYs_$BOOM^_v_J$(pqEu(V!@9@*q3qM>l+*5cNqK3G}@tg|%-Dor?ZXE24 z;ROFMLrxyjisHhoNkcc`Vajp9!?1(!*o8=Th4PEDUhc28RmJSyk}G>6Ipa);tl^hw z%B-wzIq)VYT%(tfV0}T2a{)6t;B1G*gM%2g-LHEz%;Q;V=9T1mKP_6KGqA>l8s_r&M z=xUVyI->5@_ux%2a;Zh{Cd2d873(AeznIWCTQ$c+YlE$j{B|c1hO5-(({bCvw2r@N zdvqLh24K|SZL@HoGCEZqSib!Y#;VfjR;B^&jS`?F<=?2{{^K%N{tM3{{>Kjw0b3(; zyZ@B70JOlCLsdX}|15$rZrBFBk5_1^MQa18T*+1)jzR2q&^pDRF$?BslB-ZeAt%KAuX2 z1e;ur43Fu$+&n$bPQCFWxfh_mMgD;-=&h3LkRAyZS9v%(q-da1j&8I?DrL6Jg5b3z zk14dFBn&Y(Lv&~DR=xSPiwu|(}FKV(Dg163& zJOC#nC+A#20UjP_bb;Im$5-3kUUO2Xc65-M{5x$`p}@teki9G2XKH)p=sh4#g$B7mg>nw_i|M(lY)Y zT;7kd)V$w&E@pme^-7{{AJ=1SRfnGT6b(}dPvFDksx3kE-FqDZ1_0g9?GOq6hzb7f zdgbU#+{CSQ!V|3Y%MT!dopG`Zl?_0P^2`;p$2Jqs zE^3LRrn#U0r2zTL&$?|$dW(!;!W@r;?@$<7tfM`xY8GI^d_9kE{zEfv@9Z80)(;i-v0Lr`uwj!bsly3Ar{^EKRfZl$auHN~rhAniTruddue@hJ1wE+E%T~4}S zuqnrzJwA<(2xiVL`W5W5x0zNuzbThTs8B7tK!qc6qctq~Zzr{QKfEV9P@h@|45Q@v zADq-k?T8QDn?W^LhE*eK*~9ebvSGGV|j{*QZ#> zYZ3h7!<(}3?~|i@OprMhC&QKD0-FjJG&TFQM+bn;aIZ%}5zq&8@3P#{Twqc5J6CA2 zK5z^~_ z9FmFM?Uos+s&hyD0rqh%2s41M15-*Ql6%B+qu*GHY1WwsZn&}H3R(@UaoTOzO?Ym| zzFfAJsRX-k`-p;aX*J?@t5;MzDKIYE)Wc)wEYzwY7>pgyVy5gFfDBZbSCiGF`L5aq z?`cmEVg|=p0pEn2d}awKSzroOJ9d@nKGdoPP^cTW8n-u3$@K8+Y6RQ7nU6Mc5NZqP z=EyB;SazG7i%i@FfZX5Md*pf~8FY>@Z|#bf5pCj~>d1P|cL*x5yrvVFElhDo8Y#OUuPeLmsz#N~dcnUT1(nV74I1C*L))J;zMrhw6^>I#tQe%zoXL zmJYf7jEPz8OuV3$LczXqMOvcivHrw?ZP?J*EkIW@kTB$P^~5Nn^;=0d)gQGUjUYI_ zOf}NK2D*74m`Zg_lV<1x_2jAJUq*CwZ1I}~0t5N{S`9&LR=9A!pV zUUbw~<8S-vK#WIOi#q?B`m0tOOLR0QBw24Pgy*G{PJvtQI>P2o z1!|2PU3q}H?UmB5fGe@MJ5B zF>;pDDLd}u&usROvVPeJBZcEwa@qWjf=pbhJ9fO>qNh^jIo<7s;NbO9pw1=PqRF=H z!49Adok9&N?IWkWvDxmR$=$!f>h2GuoZkeRX(MM>h4essYg#ccA|$A3_ZX2Tw}-rp zaf;e7b_-1+sjnBffkXplQhOHV4l``L(^YMiY=txBtqFHI8 z65Y~@I=>1!g}wLeocZF#PM>&sC23f8Khl_Upgap)yaybr_c-f*At{QHLd#&_2fq%* z_VB%ttwJEwjj72;ubz`V75TM&#J2GnCpW~uKBLjWMu!U+CIG`79h-hRCh0F|j{Z#YOe%|u;_va2Z z?4<<2=70>yRQ}%|Nq7Ua=BbXZkWMRZ~PL;nwH?-X5mw5|JA1y!+a+qP}n zb~0nzw(X>1+qP}nPAa+CXRWh#TkGC-+CJ?*%$NVuY-6<1M<2a^{rCAQHr#E@CU{I=o^p5J^Zb2$ zAC~>muU0OM_SXOtZasG;kX`L1xR^+bCurQf$umn>Z^DIhv0haJKRmB7joM}tj}0tk#o&k8g_k1pV${}`;^ zbJd2HvWmh_EgXk>B;Pz3f88Kyo4g@L*o`GKjt(q(aI9YuRWfaT=lg@vS|fOIT9S%bAXfn-Rt&u(JOlqVb<$${Q1 zPh0Raia}$Ra354hLTtn}s4$Qn{a!7$X5{ATD?_H}l2IROb2(|HT+$sRGH@AczY9q& zo>n1qYHi88b^!;8RG^MYH`8fzvZ^${xD33MiARL8+S4b>sZqCT(G>?w9>2U85Hd75 z1J~cMtq-@Zhjwqs>SPn#Ef~8ILU1pB4MGr@Pfk%V-6)$ zZICRFNLk{xAezJ|L>WcehUcxvSChx@h|8CVF;lP*fK#+oh^gG50f;ff%sL8HL9~u} zM()}B3#v5u8v<-#-5(TWe_PILZ?s_^Ctn`Sv(IbI_$d-o@Y>^{uEboa}G0g$ZR`+uk&=f^oo zUB8dr*|)Fx|D^u&|CtT^BNv!def3y3!T#!)!WUJkE);ERD3h$8s!iaq=4fKkR$rqb zB$j5}Yceu4F0UN0Kpqvdk54wVij4BuHwpT=Pf-IzO(oMF>D)78@%+@IA~W#4DB`Q zw=O0&frnyG&^7WJ2lYlD{trFo5aP!xd>7YfN{Nss67GH=?r~tw&N%7oc?rP_Q+D(Y zx@RzL4)P7V_lMh0UAS9fPjw;E?89+kn;BIQPvrp^uHn`$lAZI7P}|Uzuwzh9#ewFV zOR%lILTHcht6AA9?a~P>H=AVt3dA~&(6dy2RWHm&inX7Y>XHfV&!(d)k zNbpFORT!im|9*5_qC0g;9d|Lh>)1e+tDHDQQp{3u%-CpPPbpU$O>91e7~A!h@d&f3 zm28bGM&gVuUpE{#WlycB5hxo|q(a9q*if8}DI?x_hdK{^PP8y3e@UdVA?WW~32*wH zaCF-mXEl!ehk)$1@)6wV80E^Z&TLMBBD_&&%zgR5mxKo)tg^(IuCQ9)pnnpxY2k4{ zre9cot%8oeCQWRb+JvkA2;{{oy%iUx)^n${#AM?31;f=Kq$ly&f?z7IpB%GrD^6>mk%?-0;^c&sDT5&CIIfjc?%67^DHxl#VrNa&dNpk{mitij^% zm}gPqj!?HQBqFMvbl-CXoxc3Qlvc@MJEcDGt`8_ykd4F}2ydDlR0Qu)-tO?_Qt9*5 zGz9OUQq#WlcpR*BuR37MhUmuoX{<>h_;utGjGkqC zr|t`GZk*;%bY{a;;#-4&%u%HV@morLYCn4p2?Mk%b&Mbtmt}^Q2Cr6hVc@7ls9!rr zTWZR60Yypz#iIzQzdv9C4^{c6j=V7cFhj0H<2(SF355catp%~k*=GkPWs>HR&d~Ci z`k<=Je%f3VIPqq6ph7>uT1C+2;&0K#480Jflc3CS!QyA9in47n#w7oNx&KkwoR_vF zui(n7LFfO-7Cnh3NQ%?*^9{jia2IC-lC7PeA%wkM&`2)y6TI2&EaBl`JAIEf|1i9| zaFtz(9f&xk2XTZ=37=xBBA(*2l2>M97f9$1-yT}w=bw|@TY@Gk1LoGcVkhgL=Opg02$gwzN*usDPa-{b4s;16 zg1tS2?4GLucE6!yH#9{xY*}iL!v=O&sR&gSu6NNFm7n@L55vF}h+)cApF?`<2C3nB zqb0O>U{d%ZQ>+EQt-$nWZ3c2#I_Vn_@`ONc$(k4RWg-6M%dCuX5u9n6k-!IP(($nQ zbc{?j@7jzu-v_c|43UngV!yFHb+27(zMSq5$40I}N0_vd0x>Kn_uq4~K%=t2)1_f` z3lY_2o@#W$Tkx~aAyKn=G` z;D8SttY4j-ot5hkuM3Q#6BFNt6 zy3+@TMwUYD2Kh50qaCvkWoUqsxYC~n!M4Ey&??3b^utqR-}Uf&1E>kf4c8(}mewQd zxiSsG6s=r?i>B{yA|p*Z)gq3KxM=r5!N=^RQkSGs;KR~Ts6C(s)i&GG#XdT8ZhRR4Y>}iLIMa8v^GtY#MIVG&<*g=aS@klIEoQVil-w>-$E_SJErKg zouvqiD!q!Cani-&k7C(aiK(b*pfnc+Sp;W=2$(YQ`w%t~tfczt5Z)0mAfhIujG-5i zA+OChMenu*j|mLMmcDT^`@-N!f@>{#YvK{nOv!8`rYpn9@QTaA16RZy>E)Kk%1d@5 zby5*_izZXV*3z(#=HTn>`|xR(XM5V?e$?!{#r@BAk(fQ1)E7s5k|iCQeD<&g6}%yc*INmPZMeuiQ&>p;8kQl5$fQsfwi4 z^M^r4@rGk77qflaaPlxCk`=ils2N#nfRkZM`37gJWREUIVX7ccDQh2UHQ6K*SIoj) zNe$`**qxC*%A0iX1A=Ej^qZ(mJa-gBirC~M^@4a%g(80gPIKEUYwTIP(8Cl!Ccn$3si`1{juiFWo=KPP=!yg+~9~;C!R) zgJ5Fng8;u~r5`k3Jtwn+!#-1OJ=4H>Vpq~kbL6jEIi^zpK>wccTxNOa(Jv^0mQ-YP z*ZXO3UPLx9`8fmByt8L= zjdeCj%CNjceqO8MZUg0C6Qu&9!+2dBFoiHHn#S~`IJrFH;LMAna*Pw`Y)2Ttx?D+d zApZ!*U0mU|*v~|NQ$aW*e>%R`@$YVJGzf(iqqJmcjw~y>u7cVWSOP{AN*HXkbS$a9 z$VO)6bwDr=qZty)7X-Cpb(uIS(%1)w&KR+%frSBIW_?y@EON>&6-4J)E*`QvlxTus z8wxsoBiVR%W;HQu@)%62HVSB=GYzy5(=yHyO)AtYb@3bmz(K?Dl!JNY!+A67(`*My zpO*_sQq;`uHpv^_mYU5U=`k%YvR}xf2%yLZ$p+c)Je{sTPeJxTpE) z0E)MdtG=Hc?NEd#L;MgL*ui$8WUTwsuejqc*Zt-XGhuZdVgum>yM6twVh7re5Z`Eq zO&Jvql$Mh{R}YB_tSR+Y2@P!~7ljT?n|yfzpLc1hl+yiQVP^3+r0KsoENTgYl3a2f z6A{yum*r03V71!cpRwwG4c}9%2{*ASSXdV)u1Tw)8&j&Q3!k>)b`o6@SA^IN@NcCF zZmIis)Pt}$enH(uFgZL`nr983@yMY5F6{1(vTL3Jz3&J<#uNtQG>0lkXQCy|@*d+6 zIgzy-&jlc^dHS&D9=?^Ca6 z-vw*pT<)WBKGrr+ra@9pl;~{G4b~U^WFyAcz3Z_zz$dgbu0P%lBOd_-?TN z9~>+k|F;JGA3d{w+-N~^|7dk5NODO>k~phqfLd!+R02_ec>_cu95WmuJ-+{v4Qpz* z$?ku(Q5_(32-TqOgITX94S9IBw|-(7k`6kD^M~W-UFSD0vQ4|ezr@92Vab5^*E;Z= z=yh-&l44tn35f?@WyKgz9S((Zd zK8cmSVZKo^U|B^?@=~H-6cS&G#7oYC-04EXz-6>`G|FU91)sg^1ZBWjOHf>Sh zWd{!!dNWb6!vXW~(Xs4{0SioCcm`ezWZ___98HvpgJ`6w34(SkdJZ}hx324{{0}XW zU3Xzq$M*{2ey^b5{~#;)*3TQ;+k6|o^IKU-|1U7-{}vT}HzEJAutiba;oripAl+Bi zD>5^aHow}a+qzx?O0-Zv(vvjmA)K|dymnEp48Pwif??Fb!Ak|@u7d?#U+aeudSQTI zDnXf%=c-5I>L}yBi{UkDT*xQ=ZI&qJ3)Ds)^S%RBiF5xri}Bl?Pliaa?J3oAn?a$$ z09V zu0I)p2ks{&oBeo`F$->EGyT#bF8?mHvMW9v@c%Bq@_g5URQ`W1jkROJ6l zeDROFa$yAJ1`roV|4UB2)m0(D90u zeNWH-Y`#hItA^f~+Rp%j^0eHM!Eu46LTgd&0iCd%00xGIrUFN+&K@hhWCbmU<~392 ztSup+4CR*nmA81CG1sU9+)k+b-fIJ)4L7o>7|0FRqQhRV>?EnDm!=8S4}CcL*iug& zaQxIHIce7KV&yUplc@8Y2rRD|1hiik0IkUxX;fp}PVW+7`PN{(3?nGMP|rhP-%Sa; z7^h>@uCkEqooi%99y5f)ELrugbk^TL*|P4G1T{vA-OCn|;cR64((pkl`I;Q*4l+c! zI27G!s#$gUJ*&~=QiwP!V)tjGx%Q~7&bVEYpK>V@=bEz)lYE=d@eL$(e}wMhxAcQw zb~Er-qYTVrkRV(EE9d`lE881Hm49I+=%MHDz zv(%H;+yClH0&4ygENhl zy;MjkMQ@u+@1JXE%=9JwJ&qK*Qhqy2AN!QF59@nNv!Y{7TTti~E zn7jjS4VZjwZ~g-hvlim7G4g%lXORDo*2n+4RQ?Yk@t+4=HOvQb82NK4 zxkG?T&l4WlTF8no`LdLR4`ZE;jYtlD9YF5{Nn?#_X-!+r3rPBtK3;hLdh`C|B z65aqo8)@I7?`EAbS6&PIaSMyV(*uvksM(QY0?vNa8 z?{`H=@PENUzJ@MN4Y`H)RGJ_G{Td#)*f(G!Ch`)P;XxlN zp+g=jxkX3Lfi^6tu_oj}?K>WPhs5`k@8~9Cn+mPeRk=k*)`i}+)E@w-3@!`41c%#} zza)p-Qu+c>>#qm5kI&7U6ISLHFW(Gh8g*Zt?FXB-1hgg5BPm6S!GJ<@M{{EE#@9;I zUYQImIeKo0W2ks`$2}1#{$AyjW^g25LFxqmEanIG(kJM~0J(BLuYMX$A`0HUIcvkBh-+}XEMlv{?y+!kcA z83hncG{P8@^<+Z)hn}@ggSj&iSacpoH_kLwtG8nGPJcM1HLYtmW`&W*A(F{lFOdBV zPn#A_MvZKT?k2oLem*iL%9lq^35yuT3j|pH1(j?T-G3j}Iu7Iu@ux^=E0M%E*A4eC z21RsizBvip$9Yt5?VPYGq_0mTIjOl~CrEjkVm2weg21&nz40}V(GaN}_DDtwRUoU( z4wCy^sG`VC_+4T`qiQNOuz#!C7pd86ihjFljgj0##6~~@ab~)DhC!H2e=$fL!zSK# z&fsVnV)M<+U~x+f^iM+CzHECFS4S@0zdEUi0?rPA4UFJI`OYzPGV>WpwR2950N>w7og|8 z`i1Ee9;QW0H*reTK-Yg1n=%IdP48GoTC|@0pztn<&C5gFo}GcQDb4N`4VLpQ+=bgJ z+>J6g=@*1Bi12gv$&)F4sS3W{%?!WC=!(HDb=Bx+9I;3p0tL;mt#dbP36-ScbJ(4; zL+4$H4UA*$Y%#0l+ia+$$X>JqRb~DFTSj`=?mJvjD~E=o`%>$dy-|eb?N>&gHh0D0 zRlS9Trri-n?q0Z|{LtvPwUh3*z2$^P8DjpxSyuYs@5jB34^LtK0QWJWIyQOoLZ6qw zs0!}hspeu8G`M4xfiZvKq%nW_{Yf6|Lk~carOkdsFZMz|`f8!KyuFH~#zTLKH1ZHy ziWvz#j}%S)teX&ykB)0%^?*0@KScywgeF9&kbYL;)v4{>jpbDO8T_?C20$kZtc!^_ zavH3)Z5T65#A(5ww*;k>K27nj%b~~%`irx941Z6 z$$zrPv9;M%^E4y3C3UseO6xEmb{MXnB>GCMiv$m}) zQ`H<;o;t;oz~*EmgU69kV7WbU#fY^x`0z3S^1=1Z7i(-H=5y{2oc0Mj3`iFLTGZ&p zONQ&{MNMDiA5ucpdDXg5?^cye63TqqZp`>Y3j+*jib}}99hG<(#v1jayRoB@!L1M19|r0n;+xi?16e zLteg_mwOb~VjD5PTS$y}`n(LjM-a()>7w{-D_V^)_}Ntk*h;C7TeN7B7Vt*^qyk7S z@qHF#rP(In#_+mQ{Hq*u&vMFOi>}xRO3f)sm3(foi8$uow75;`>;YA|0v4y?+);HF zfpu!m@cN*g)Tp&uy+|w$`Q#VsDi4Di^=hjd<%zrz%IZoohL7qX>%gYf-JaR*7+^=1 z;6=9Y8e^c1EOOf{4-Q2!c8`b~%aZTS3YMJPe?MSiEuo+EeLW~=!~ePl1lIl8(N1{~ zCx1Cm#5tIFHjjmUAI_Aem2aBKzVRxbdS6&hzc-h(+$wqEb3kFf?0w_&qKjA~Uue)0 z+4Ot!Wr$)%1z-+lcYf}@3W#|&|C$wZqNmsh2adet$6zGdJlk`R>|GIdkJZ9#Vd48t z3GQnr2|_JzM;Qm1o;nF4eGo@$C>Bg2kJQC_&z|U_3{MvP0cmd zAbAz{Xd0h2u9ul11=xbp6 zH~SE>WnD^<_$)6P^C|fzE}O%3;6}eCNHea0OR3-}uHuYTqKrTJSQfzQOv;07xPYE}oeLrw!?}nocG`i70rZ}aT~N@K{P2QsfZQPi#FN2r-l zs?mtmh+6F8v^dEs!)AZR(wAT}Hk9p;#Kg}bpQ=*;iel2@>(TV*pfJWOGS%|v`N5R~ z1P{Fb{y4HR+>#*qPLm|Q)1?2&6v}_6N&gy<_~)!~rRupZmMXfp2`$xyjIX^VSjv$< za^7bPs{ zngKo1om{y7S2UUd_z-x~X<>dX`2Hd^9_kU=h(XhVn^iQOuw8pBYFFmq>A~5f>{oxZ zF0^4CPw|PWp&JdXELZMeT1a+VqM4}bfm>xy>fma~Xxq@;44`98h_fS6YfW9c#!UaA zQHQkqG{@fAsE0xuPQi`rU7aCvwRHd6y35_sj>cFXRXQiqN^PwrDlc)ytjIWLl*W!C zOXoMEEqvi=v?{1tZYF_+fscBVAyki#PXMEeiMRvP__9pj(nJh;pmGhZuO70LV*SN~ z0#6D0sbc!27)dWrMS^}H#6vP>mwqR+&US_W2wE5f28okm1`XAu`BK?LCcnhI5l;ra z31c$N5)SFoqO(!C@+T;)C^&o& zjq0)@g;j;DNV_ZWOi-Zud2SI{PoL87vlEZPfjE*QB-V!SmmJkY)6w#wvmA2MO9yYV z(7OKU836Bn%~+j|ruE&``)0B<4`TKR>9Hx6H$WCMd6sQf^q-6fP0TUR20{!;}lh3HC9Z7i%H{iRPLFo$=y0=soI@< zGD_)v2n^ERQX+Q`L1U<0c^}CkBj@h1zEt{~mb0z(>!G!tb}&}m-s$}s6FG3P1Krf5 zFGbv2i&Kp<`GeL^k378(irn3Qt=ticiUeiN3l&UX6h>G`};$F zaQLPK&8Ey%k?nNHE2T8t4gLY#Q)w{gR)wD1XaUH3MLS=H?wGM}*1!}IrGEwmE!0Y- z{_+RpcrdLmx8~NF&1)uIR1zQbonCEGReK3ARmF8cQZ^!gGK5#x9{TkgY~A}|ALHY> zyz5^hd-h+c+W0S2nB$m0QA@C4CHwuMM*Y3=A;?cm? zv^?LF$co;Hv^j+HCeJ6RK5fm&2yerg8zF5>B1mPqJMEyxjHuiR~0p}oL7UT7=Q zzEJwX!lI!;JGFvlLP%zZ{yOn4TODOCI}ECf-~LcleT}+VkM95`PHT&jtNUW<8~xi1 z@20qibFdOjHcVshthgmOup(XNVHdhk=z$Ys@FTXqU!f`$+8!mo9#-BNEk?W|c6jmF z;W6Rp>{Wjcw>P$Ab376AsJX-kdDcaaF-f8;UwtXKo>b&l+3L_SWNhJ5G_U>WK%8+kAY20EHEbef;otY6(rOD0nwQe zZ%fz^j15tRkJ9QV_EOxyXC_C;4kJEVK7RQs3J<*@=pMOYP_2U(v@D0)$IY}sA&3r@ zyYNmXyVnwO_vSrTgi%7 znNw<)un>Dci3rLye9xmM;*U57dGA4bUxU;kV7nBMi6rM%sKsJnx4TFxOU?- zqjNq%s~-_}n6$%+_AEElC5Y4`P8ZPB-i^Nb$MpoKtwiA(zkEZYQoq?>`Jy$X^AMv$ z%*E8~6bm>J+HB_1a^})JRxipszT*&M;x_Y7uRH-nH*vhabD0h!{BiBReocj+E^ zJ-B_*nD6{07*jfN>vXcAK3>+tPqU+w^(2B z+e}L0|E4tiuL`WWyRp&ttfsNOqq(h(u)V#l{r@hfezf9P?d5q|iEk60zDV(O2(Lpp_pK=3wEEM6ykT#$G@K>da0VOm zXsIK?J%hTVQq2RH=Qrodp*t}vzbP@37{$9=@TrnUlxtndRTd`Xo$5`MwvC?3Iqb$0 ztxfvLCeE^HOZUiL(^+XN2w*g7pxU{#mZ@(V1fju)`m0Gn9)7< zOLdZ`5&=kaZ~R#cmTjjNiw&Af)ax@@4f<_u)^l}vs~XbBJufn}Mp)lgiM_<@O&93F zPR#9v?UKJe+_l$0e|gQrhawbOAuoZEv}Q@h#4i|=)wS;>P;9>K2dfl$(CrosMDz8G zsp&U3^I>-6zFkAczf=hkuHCM-|l9#4cTciMxh43oPEWm1!G|*c5 zgG0YoA4Yl>s3rakneN?q3 zBxElzvasnliHu4~8buvfZPOWkziTt^B%`}hh4h95eHC(0>ED`9fhOns;DvY(2Fd*} zlrfk{({sp)!v7xd|0A|v>`$xg8I(bx)4xo?bf7FnP^2d-{If0d9;Uh1=lvi*-#3yD>@OYKE5{cVxRSrAi!6`D&(KLk2i;dQoc(0L_rbnMQxVg-d9hwCd0PyTi zdB9`JDQpeHx~sxk=l@l$0#>mzHs<)%Dw(%5TA%)=XbHe(=Z^ATKj#!Fg<83Iz2 z262#fl;sgTDQmvN$hmt0ehWkH$NZ3JTH2HDINX#kKR2sE-RbyINphDBT#d*-h-5SK6>w3&vZjSe9iM4MuqHIUi(VS1 zC&NEt8CQ$RQMBX>{VVrv+$zhOw6Y&5etZIOB^wMZlW8={6o zx@mqymNe-&VshdUW7MDd#lwR)$rJw>TkyliB|f3vgze8^Mv?7Q!XAuEw5gzGwQ`rH zJ2SmqY7{iKI|V4ZXHDfgF%4+*pA7pDu_Lu^xXZLB6j849x`8Wqew6>DC(H>@bHIEz z+m^n6{}b+z!#|-34z&MeLuG6vYx9pVl_bwCE5HwzDR@I3o*xvJq#}e#tFdBM5Jo~o zcHqaNGHe*|Y`+oL?jiJ*9`wzyGTt~wjRLP-*(5hHC26<4rmg;-(#_IiL$(ql&FdrejwPLDic*adwBo-U@n&GH93X^?ORTVy(3+2jx|mD zE0ZRVVLYtx_g4Zr<{AQhQKRpigNUhdAyH>Lc7tsdh|Fh>crJVMDU8Ede$Q6U)ID$$ z2ndCilZOdGDDk=^+kvXD*GQ6`{ROflct`S+vE<7v2hdi#e~Uqci(+w`n4q5cUd z=H?BC{w{%Nh%^Q_3(bCbl+MFyOSolJOSE_N&uISOhg^t690ReoEJmm$u@*e^^m~ff zgu^0sbZ#UE9`Uui-Hsbmp~49VX|q#P3KO)65cyF}ssuvWF16%1jd(wlu9BQlIt7NO zy}P@*Y9k~MSmTPrJ|($Q@n2+W;%Df)DaQCzqWAGw(QWPSnJ95;@fsqa$`FAxqV~Cr z-DKI2)fGwChsqVB%!&`VB|^eTSq5TA(AVQVH{sDn6%tUYXJIB-@^Hb)m->utDu>%I zThURsl|a&b7weI4j~;vAge}x4!*&;adOJV0F3vzA zDHIKy&r2OM=wQ-k`)0au?AgKx#yyLWxxb>BhoOH9ww&%D*#Mw8TcY5jIbp605)CJM zxg#AmTyB22MT7rNNHsXA_Sr+H`O7_G$^(q_RiQ)h6!IX)Unb(i4Wi?(ejH;-LC+&9bj*!n=wGx>dDRYV#?Mwrg=41eKu=!BVd8eRi_>BylB zdLf)l_A~hJ_aKu`LW7^#EhIucZd@kYHq1G&!nVG&&PyQEWNT9Wn_}xibC+XXjHw&T z-(Q=pO35zI#%&eVJN2WP%4+;az!-ID$Sag)A0VWDqSfpk@9(C|oc1{tg^nPZQzk}a z&(7b;`JB!kgpesYbHW*-L`e!0ES{*l=xj_tVM_ky#UOrs5dejoO)B$NxgyD^g|JY1 zlF)V-><@@rdk7+%crB-0#0BR`fNR??5#@6z^Q_7rNAVT)eOd4Sfmq8p2uZv8K8AeX z$MD|?eg9nxvbWcF`-iulxr43EKYdI75#gegtQ9qt;k;o2H{fi<#pCYgP&730!HK_f znBY4FGG=C9i3|~9$()~HQk>ZLe$al6YQxm1;7rQXhYhBD3NNBiW?CHu#(&@F|b$%`=N$Y zp}*-p&1fLG)rP|1|G@Atbm0!)gx#t|?CL|sKzYxe#6WAz_Ljh8gJnV|RvTBAe()`K zioOc}yye@O5ZHtiI1Lh3w=AV;@D{*R8TByMR`@9hsd<++!6KkNj^YS+1k$$bxR{{d zuL`vMX_#$u%6zI^??$Vne0XeP{|lfF>Q}))l9M) zl6}4Gwa;A1jXednk;(>R@2Fg_rQ)KH>=v5q+l4gr? z%G^GUbmq<6zNoiUVZzx(YPssRow7}}(CmVSu+T|dW_jR$$i80fenHXLb~>k{&P}@R z_ER-^Ji=Ym&E(evo&I>Zs&b30^q0^Xkv)M#G7BijgK`@d0oSy@ky#pm1*1(xrK@L# zKCqoSAx%KdR-67>qjNW9A)aM3I&)(#k>5V9&lKnwW=_MD(F!#MFgVEEv1@>ZNV7G> zNt3}ovdB1 zw58Nv=mC439u-5g@U^V>Z+eK+T$15Ud&}V-j173<=Q9!LFIi1uzbSA==HoOsLlsIO zV61&`PTCodhe17}W81&5t<^SGWeTZ`vGT3;1zhf0y!y>C)q{C?XHOVtHw&ZZaa`Wx zBrL`?`Pm^&W3!?O_WULYun)GQ(j$m4fh4c0LI`~Y9InnogQH@t^6fF`OjA7 z1>NR1Wui%-+e{`-BSJQ+LUP81E@4tz-`NMq&<9CJE;qj^ZK1g|ZO0-~7kFu1tG?G+ ze`y66LJ9W1+NI9d1;gxeOcQ*Y7!Kag7u({ z5*w?q2iZ`^=#R)1oHkBCESt@w>dg3*RK8#GyLWyd=%Gz|kyXH_Fs@?sGXj8RNfFO= z`Pz>0Xf^PiErH7rhvXQ?9W4EWxxxn*lIw$SG zx6{P6A?*HXV_*mNomn<7#2$;DB^&QNpwKiiX>zV@fCl1$EI!vsp+NnM63A5i+#@+3 z>Dp%BYyHpi6>~6zeQHzZ*6KC0rIVe7G!%9sTw_Wzb;G*7SwGl>TJ@^We^3!**qaB^ zzeC6Hcj)+cirEZ?>O zNce84HX~s4COwlwSKhiq~F^zRW z=4|$n7?tcSXi}(i#AKkMG)8C@vx`4;O=Xchml=0qT+8N zI+T_{J&X~?#A&>mds8>;fr25^ZQF&xt%sBPT--LhFhD9r6csdCu&bwvO0~AhA))2m zZbkKRBSm8Nwm@Pst`jCcRewsf%-Q8X^YOn$YtFldyGWdM`#M7jF9d2K^7mLKJ_l?( z^rVJ^jddv^2*>C%EbESjZ$S!|C-x`N^w_=a=6?ut_*P#a>O8=c9?VHzGHC zUYjmbA*xrTvqga;;uL8ADs8zTmh>Bgq-jnbsMUh;t(+wN`|A zQNVdOnjoBq_!vSv6;!9l*NgPQ;LS`%5=JeTn#|&r7>EA4-(qyR(qK zVW`4ATC8*+AP>`RdF*DHoIs8|;4WCGxL*^E2EZA&v|Ka1&riT+2HytQC`D=3fAhrU zFGjosKfFm`F++B59N$~f9x&M+Ud&uOerr`TXuYxqh0uvOl;?!SrtE(3@&AUJ`sjLz z_7B)kn1jU6EOK#2dhifRBzXrJ8sQb6HV4^n5UiK+)`;TVMUTqikMvCUx|@c-gRGR- zV01f2Ua1ySr|r$g8_Fh(Z_dU~&v6T0Q49~=Bb-DbYAyB=AuNBOj)Sfg|V-B4aKa;)H;a z+UrJ;=jXdgQvb*bH{H#*_Y9*f5P+CYQbD#=ocEmmi60!|W2ssf_XS+LoZ2`ygcA?H z(F_)Pr7zZcs)QPGsh_$*I-=pz>v@AEIw$G^kP^N&FhU*fpQ$Q@LWVvI9 zl_3;|K8$JG*@sz$)mg*3WjD{jKWIyJvuz~g`v|^mI`0z@xKNMa^&!dl-j^~xgenPJ@!YoKcMf$7(bRh9*JiD~ug%x=I zeBpKWTN(-4ipOs!9kKKL++#J)&Na@~&_Ui~(OL;Mf6$epL4C+!%ca8HsxMU!A}iO7 zx1X}n6g5a-c9DO|<)bbfHeJ#gk|f{M_VAHg?;(0x7+{$?ZLr*;^RV8MOr^Ra4}*&6 zw&6>usj&cHi2eRfTS9?W8015g6%>CgfY)laInIoqR#%#=S#w55BO|h?sMphla6glD z{ZLE+Z*@?gW+B-Y^qO;nq1} zbSqd_%R2aTYvzV8smE5YZMT;;_F8u|qg^}S7uRO>Xq&b=>s?@?v3Q1@ak*rK{xW1_ zqD+|OeycglIO7-2KRgeEnQHw3b>b^7+wP&(OE|K-R-d#xLnFVi!ot13GKr}@cv{ZM z;3b+|;6%pw{()pE`s$Ax_$JK$JM(u{h#Q4HYIw$Gpevuj57Cw(pd*;V*7Up|(R*kM zZ~z1{VPinb28bK)5oEW3N9;S~&`uAB2o8Q-3pjPN--%x2_^*ffC+6cjp19XF|g2pRV@iwbj+37Xe;vUa8h$yQ;JVE%sQ7! z!%`$45C{6AvT^APw+}C=im&H;U;`}yQ0Bd70P41Z3uzN3+MavrN(|i;QvrK0eP)lQ zD|7-|m?c_~NpzdA#c{1a5SPS@Yk>3A%$&LBu>M~^j%PcT5&3{t4SgD!8IjE9cCjoX z=CJdT864h1GD69*oed$~E4LOIR)i{eelaML-5!l^!^x_m3}4uv`wv0q7~xPp9$(HCi##z&BI1Cr`a* zG!m8hg#v7*H=&)ckFS4^eCZq$XHwsFAlYxK+rLA){}uU!UG)wBX+Hj652XJHfAare z)B_g{YJl{*Xr1(hidktHNi9dj*V(*H@~4A6C$ zN@Hx^=FQRlK?Bh1=j$IrX`;SRTQR1qE{q>#7OZt!GNo(Vm7`PQE*tRTKo30$g9S=o z=$tv`&kN7itRz$Ebk`O9%-FY(EkZs#Fl1OAz+7z4fJHQV96=Mib z+{q^uD6zLCIR+d&U(9+`y#d{V(Fz+-dy_DAC*(mDv6^T>zfhG7_dwLfme7FP&vZ9f zxu664Mdz4@jFqUTvEon@10i_+b*;~){!=ID549cZDiB(xC!#wreYAYxc0o5#DvQu; z{#4NfS&GKBp6x0iEuS6NASyp$nPrI_b3xVq4m&Rb!)xo4PrtW`Jb+?y8A8-SsVesm zv=XB-B6E2|(PX6de>%4aVw=64e(#LHcMi(%Zxy6}?TomM@&9RWk`%_jeJhZ-5laAj z=`4lclq6s>K!RKZv^ElcUQ1joff|xAuXyoboFOW&T(jp7+#b{#5r2O*yp=ooP{wFs z6lzEohi%g-uaCFCmz=&Ku6lXFjTq%E#(Pbmz~?9|HRcB60-!EZ8_WRNT>FDEyds$-nyG%MiWi8q<`h%ma zF;P%J&m81v$5!g2-p$hOtS)i~*(diq@#4#(SU7;PzS2T^oKzw0l4J0KR6MV-D$~kKVEN zaiVQJ>@%5hhKUv1Vk|4CP_hp`Vbn&foTNF3Vhr6}BWjAKA9c2e&}B!e4F?UjQ2Z6cPe|Is` z(RJ{BUy+4OSt)SwxVcXYuWFmI&;>}$e1plg#Ll-nL*LVbh_*$;@MubK4j3DLz1(<# ztLdlf#S@bYd9e%7t{s^6O*^9=sCU=gNz?4j&*0&OZ>zUh3Tm`YrgSnQqM39gu;#gG zLAH!_%c^ncmbIf*8Vzoqh`co!-E^#(X_oaD9WV_*^?`N4#PNKCMJ6J_6cqD_zWPIj zQWv&kMiKN?NQ3g#r~m!o6pZz){^zrK%Sp?EY&0^11oKn`(B)7<*)P8jHV|QAYOiCB_8qB=55R}&Z!f~E^T=#j@B4U3`aWaKB}oTYeJ^iP1}LEC~~R4CI)2YQO!U&#V)Q~_s{gC6zDZ-1Qi zwq27LAArt!&p>CrYG)bbn*9bes|Z7$s~ivL!*XqVzL^k zi#1oN(BvhG32OU?LC4SEI@FK%4HyScj;9Z7rV|oYSxd(>YBUu~H>H?6Gp=fQzReOn zyWZBAy?C3LeAMf$CE9e4Ieg+ev?-3h_< zDcl*V_a+a^*)Fl(n~Da$8{K(9(34#<2MJv@T0Rw5Omllry3G%(%bW`=)xW84u|^dE z;jIgjt)C3xTTF`yTuEH3$Bm;=i!Jer*5}2)n9_T6w>gr|$@EfXF=Mze)>^mkhG1dKi}mE-!M7Nt!+d&g z%8J=NyOy5H`Mj0@n9^i9#BgDSIZC!(otQ|~Y5N(HZo{B@v?eE&r!8md-G2vN*8{hU zGDp+8SZ~Ye0=CAdyCt{W&?ac4H(M)Voboir#;C{Lt=tG7O!bp=QplHT+OwysEHlqU?ct5wa;8CR-2j$HG>Sd<05KQ#pj^tuVhR>hykgB+R|x* zjl5~JzGUx;#Id==9c~5RDz${oRbmprPFek9j_PuEx-nKtg;a-Y)Z~$Lv0mi1M?njG z0ei;nw9!Z{mV;EZF?}G9hRa00vjCUDhC8c!LNpp#+Zrv-ST-kp8uzatK`Z6r#xxs; z#m^nkjYEgYe*AY{~)fox1!XA>uUx z>0X-a9-_hgVYa@5@E>LH_hKiH+*Gw9OM>pm(A$b(j^}^wyxxwa6*RoPz3q~I>|&vd zp8gTN_=-(|*bHN#e1GrATSB zVL(K9nla^+5_f#xuaR?#jqyI*v9V%ade@Ty5yTu=4$a*=XX3fQo6}~}MKQ$}nWTB% zwBpQj>tL!S3dqteqOx*_y{zdrkC2u~Q(nyAjFdILo-$Jvr@KoTb?8ky@}k8HCY*?1 z*UWvyUzvIG+-j9b7XvGfCIVi+^sxI%POQsDR$1Ylm)4=|xQ}2Zc9oyEXH339dYx~9+2MYK`ZmOV$*={m3!U1L=Cz7r7lKMf0D!Ja zy}Do)Fs(Lmz8~d%g+fbckty^_?ob#7$K+JcQ}pVDe9OadD&(R$8a=PlqshUrsvvGk z274eDct883u(9Ho-T+DafPXgd6B=t((baKbu%Uw1{Q#`JyUXM3lX2NuO9Hy$fUuwN|e{+L?me#}3imm2Ni-gO5or zx)#b%uucy;FIzq2tBb^|v^;T%+pb;<6`H9n=4n(VBYx??6RnJY2JpvJ#J zB64CE|1M+saM09~42H5jXpOPn-R$Ik-;1v9LTkrC0I`_Kjoe!8pC%9=3K^t<^*e;- z^PRE5mOV5jZfnA-sAg0X4y^YoebR`KW3syBSzEB1h1~ii+q`oliX|0%UO31x#F;pg zs1lPFxwtRrtI<*8x4fzT3WX z7got+WU0l3NRF=O>o4Pu7g=oy{O|8is$ff(O@R!_dv1^qLg-}iM3*~2zRJrTJ|ez& zxq%Zf2{(psA&;paE!Pz>>b{Wp8KdB##%tL~i0z=Eg|fPS>%e2<}o8u_PQ@3ic%A+ie4LD&DLwdZS@BTeUWVFfMY@t z|7vARz@9cm^>yg19!xS_G07fNzg>XX2D6lLkpBWtM$Hf2jXQ{Xptcr{fhZhE{gMP7 zaj9S-ZtvLp`H|{Jf0o?SI>gG>`M2Q*9LR^HbKbI(7~cM7EtDJ-$A#qvmYVZv7YWt$ zC!a1R?@?9Vk8FCWqeh;DAuo8=2hzV+mTq#apD0Uk&TT+@+Nb;0YFcLb>5B>c`A z^`&|s4EIxcNeb5&pRDtZm^aE^YWm5`TsF#1paW9g6fQKGCnCu9c=%H6k!+bF@0uba zq1WT$hCG=zhw>vi$H&~TT77$e5asUeE;%>lhp`XDBZB>NJ-kwsmOlH`rpP@-_4w?DGQz zt?RjHV0Vc+tnFOtCGQ84={4*jWPLv!;1=%1HtM#oHieQ07-KMv(G=rv(ip0dvt@eN zl>W9u`0>vI&;NOg-= zbc}QQ=_)iA7jW_Zlt6&&wM^;m2LUmu=Wm8iRf}q_lwT)aVVplMPLG_0YJe>YgHkU+ z8i5~|NiqRN@y5`SL1JwPKZiaIRV|qJ8~JUP13t56f@R4>+%>Ba!rW%NIHy&UrQi#4 zp=&Tpq|q<@x5&PZ@CiRdmIBTX8KL(d}L&TEG z21>ZWi&w#RrNJz$H=u&UzNg9jwxmSO4aR<~fqE}!BSQ8F_5(&Dk014tOvX#KY~zNU z!6@HQ5VUDp*A{@&_G@o>9P9oQ{Hen#(qjSTBW)1u{U0EP|ISA?HnvWGt+@V?fs$0z zRo6t&-v<%k>z4s!eu1RvsGZ336vwEz8yRGlYvVzTKvQ8w03126>rRJ(9JkQ2F%8(at*7 z$!VqO8j5|nIC^O}IazF0xyJ8b&DK8}T}eJYPHw5r;48UP8DZyn|IR)xea;|7yEfU} zyr&sl!r5(XKeS+RPr=?~P+QKeP#mouka>X3ROT8fUc)@#V5E1X&PJR4V2QV`KF2Qg zrM-!!y{(!%SZyhZvBshmphyP|7~6|3%2*XiU067v8@qRkv1RjATsO>Z@2|$RrZheI zLY^trWQTTSGHXqIvZ2^66Vsu?+gL*vFR<8;30O10=3|{v?5~JF+~1?pJtH6?;7D^} zW2R)Hf0f(uB(0;-590cavzC1RgcM|EO)NC-&VdSUx)ZyztCrKOSFC0rBBTEp=0qtjXZEN-6uBQS_fG(6B~Kx=Qi(f^+-Je-vJ5l> zvU#)=Ni+OOeqw0#D&u}{aM2V1uYm6sk?*CGnn~lI3H3)clk&_s=^I33 zc{?groxEr*Eb#jFvrtZbM?>$MkJO*V@CdFy2**x&#o=D5wVECu#({Y-5KGvgbb(Mt^ys zTPr&z?C(kN3%7|@{*Y?$jyryOZqD@2|3)D|pXseQsRIP)*nwcq{{TS$ zI|qrI+Soe$k%6KVv?cLCo)k+1R@zICajC1aO;x#d9Ozz(P#A%NM?gA1KACuZ0(H}| zUsid)N4u5G@?t>Ahq|i)F9Elx^`qm(I_Kedo9q2U&ILbsXRk;oSPTRY(iM~}BoBi8 zj~lQG@I}}rZ|g>GVkNeg_P`T+F1f&`Xton=pBcsbfM4WV>L-QXX--88KK!&Q8)=iU zHQZ%UKinq;1@THMT=H5-6!c@hUFz}w&i;i7&QXJWZz4l z1$e>UIiPH(9RD#&fSbT|>|_Dk;mW(%N-8SHXmXXB?mFGBCK}}{^IO{QHPa2zost7} z==dJv$kLu?%(99qIMgjl0_;HUaCoxrJC*EtK_5EtMNL~&kj)Vc^70w{B5BpbA`R`RhOTvbDlQt@><4pP z-4EDy>CVk*S*%yOD?$}l|HAvSahk=7%zuq#zC2_(-v6rE;`f1A7FrU8cI8R5Un()( z7=k%}YK{4T!Be@762QNSR<;u6p3X%3fb=Lz_(MyY^w6Vs`)*&zKEb?q`jNdG!$wiU zU6R_o`SYd*nArt$qDwrA@{*@Q*v7B^uSdLmp(Bn>DFFA*sisDkbK10rcn|kQ$y+k6 zVOY-iQP?Usd_*Rkm_V5VcC;ijMhARb=Ayvns`Q=$7FnP#7muH=_R$QI{u=Fx6r+=- zZ}3cbwa{5DmLe&uJ{XN_sbu2|PJbK5}V;EBk&?q~|RaESoF zWq*7FsQUG4iDbU2!YZ0yvt4iVr;53flq3%hD7bJz!Sx?_fd3Aze}Uu7|7dff6xRN7 z0O!ppth1C7#1$LMf(X)r$MpYDsFFlX`jM)f8kf3vjY4~tE?WC+6`e20cRR0B4vviP zTa4iNs*OYfEXrq3lOe~+%&ZOFo}S-Om#`H`R_jiBJIO)DAKcfQ?S^`_E7e*}cldzz z+AvJ3dc#Y2+<+jLRvPS4ZJf{vw|T7cp%*@5%=VpcFWg$<4O8(!X0jH+D(p%?TYd@> zS(rFE@P{rR+BfaI#)oUxeIkC@7E1s*#s@#SEV(A{>acE&i}Rb0>o@KUxAE&5d-OjV z*FBTKt`0xt6+~*J88FRHa=kg(?cVPiU3oYLoMd4Ueb^U8f%3Q^902Vx_u&o{GXj!xyBW?D2=o9pf9gtV?W+1N{+#^rROAgP@ z$}Sl?VsZBJ*wwPdn6zS_(-sdjLYL%sV8?EYty6Ug#{of$j*U04I6GQ5*uQ9?Ia?bs z6AA-v8r#jmC8*HiIjG4DhBFyN)vTMlNgi;{KmOcmjF#sx5EZBVq)ZA0w}P%L)+e^8 zph01t5B)p`K6EB^fGW%yju053CNZvEoMe+gYN(emO|_yt9v8HmV!vci?65jP=-N=y zzM|gJIK&!WO*s?w$3+m_zURVUZr>k3we0^X%2@u0vK5Jb(7tYNad6PP;^m&Wks8V0 zHvD5apP0Pe=SPkzAUCUwQGDxb5+5AA&o?6SO{rZC6tq;&^DIZ1^YxLj(>>;I%HwTE z-&+IVuyCZfQXLp3Vz=Zpf-(Rr3T>mYYxh%RKwV+;HX`l$LJ-^@Lmhz8^QE(#fqHVz z;EPrHH~q5R3>aHsqk$x(qM5$ECNX!@StzfIz!OVN@%G7SOVX;MmAi>%N{D#+6lywq z@>WWxfVEYRrwBvA!w|OEZsbv7sUk>giQwG_8C&|T4uW^_O|^Bh?hZ%Al$5+{=WV^< z*BB*W1OpSrEih{!Q*iIFdz#<%Lh@Fm1rLxX)v@4x!kq! zWjeh@I7KIynEvO4wxf3-O;NY91re$`bT`i$R&>pKk8|fBZ(q*~;plZ&zMY7J?Bu zyRUCFyz|G>NB~M`-4$SmIL?h2KhTa|5|X?~vb}@vJtXQOW^K?9ox_LsbJJN!6C;q zdld&o)U-n(|^UOgvfDca;MNw?bEot}7*v#+f= zC}K~->I?)*xK?hs|5K^jmLNE?XEpxuV8(!{2eS`|o(=)t>87h^$<HmkuLeUw-?fP3M?H?~MNm1KzRt}jL3&QUkagqNPbW>7{gof_16F&ES*u4PPS*a{| zHCYzR@j97LdtM?$fg#~}ww23N_D09k-OHLR*r{8&K>sI1 zbJ_~E8RVfobPMVe+<3E6vvM`%t3Z-Aea_^b5#*YM2b@GY*Y7yj1Y;yalES zrg?k!h@M->$zDX97+Z}z!A;RL!O!|%h1&NYwK#&S9w5dD?RJhgzF$8fJxIG=@?1CJ zIFQ=me`(gPGsYvduUzB`8RQ1GAexqjbh~)_v%xRv1_xhb*xX77_er4wkIQFchOjKK zN&?t%Gtg_g9;Xk7Kn7OX;h4a7OGJYHk5ENN%;iRtTY>vh{TWB8<&);WSRp^%pA4jE zx8U7QP?^()KF;zzyv{_9-Yg(zEILEZ9Ihq*YT)6%N**604Y0y=wU5y^o{SQ%(rRN! z%V#FzInHlNrOx?*yNHwgJ_7?{TVMw2yL{+y*GR!gqyvdP) z(UV3;^RXq&c+lHi@J7dnf^lL-^#W`n&C6!lB=k&Cm4$)LSRZANsgzL4$m$MOVtTtbgz zJ%%};&Ki}PS&$V9@qRO77RoGeaKA<`{f4fTm&y~jIW!yDI2zd~K}CnEM1D&w^zru} z$3q+pqP#i8{J(bs=SKvkfaF`es67Y}>u^M>kW=@-8G zbN>Ktn1mYBb>tIVd(13mL?1lO0cJAg8QbRSor9nJ#M6}cayE_*RJ!z1^K&;`3O5Zbt>%By)Lr8V64h z?mK!^d8G?VtMD9$bo#GUFIOAqI0I`~2P|3rYREUU9>ic?$pqJVR%!Nd-*k+gQD(+4 z;paFd*x?L+nhK)U(jL=Ky(2ARJpswIDT3=aL>JE(KnV z;W$&$VZiKTHExMd)Z5gq=-6ffkw@j=kHGI7FeE9;uZ){ ztvl+y@VvB;*v=uQ+L88TSc&>k>d+nlyyH}&jr*2`Up{3|z|M1AK3`HA;VXJXd<(e~ z@RiyX89lJACKTWBr{R>4BY~BD4&5dt=){;|lYB?I-sX+m*9q+RtnZCvwlkjVI`h>Z zwkKMA#B)ZV=uZdbYpVZC^b6}7n*ATq&mcu>S2Nx_gKsbZ)uO0SHmVR@?-ejzE+j zli)EM(Xi||2aRP)**wXc-(CQ7nhGOs8NFp%zZ~nDJqBVo;dsK0*$YuGV7iiFGdlMZ zOuA88*ZFtz<`46j2waT@z+fzOKck#K@`OW~kAl=&zs=eeh6|UrUffRZ3QZEaa_>8j zuJb6y0qbUut_{y0wVJXXs;cewCQc~BiOYNl-g-2HD=A6Y6X|9E*vUf@8`9^`bpR!h zxH7jY1FbZh$uRzC^X}Exgzl@F9X!G?5^c>-pXCz-Cx%rT+S;V)_g%07f~uWK>BW&> z+@v$m8azku0>sMi{X)6@On2ji1Fn_@F*y;Ebxf`X1{4txS_c`(hue${`umu9T51FF z05mCcW3?Qgor7nM-v>ad!K;BN81u~d z?6e#|BfdtE-SN}vXPIyHkPVLo|2hvbm zMjlel(J<)xCC)O&Ih!9S%I?-JKEvEE1Xmc1Evoy8E^0#5`f)F|gyH5%)fbv7<@$6u z`}IMG8-?)8fu5_ri5{IwgK5dXe4dR~-#aa^W4Yc3>%nZm?ze{3i*BV%-3J%TkH=ee z1)|F>YoSL|DGfMUEjqWfwopw#)f}g83Ao=ob=6Y z%xz5nYyYbXx;-#P_hH*`N|;c{J^tW_8J=6XGEEEtL6@ZQHNw225ffa1eWZD#eI;=M zrff@>{T<3S%f59-&{U~(oMDr5ujjI_ha|h9V}op>_-hF>=SBBn=al>8kf+PbgRd_S z%q3HI(oBTwZmd1IAK3wF>QKjPL+p3uPOSbWsOmkK-<|Y3#Ztj7Mqb}Ox)+I44Ic?I z`k^}EM5yha8NUD_ zZ_8bQ;eW}47TL(f zC0SBo*PF6}v>lJR{_1AugUK;KK&ap>qd7mp3jTKd8tx)GmX_X>m_ceQW#IJ46=cfc zkp`?eHASHLnf59a*$?5tBR#<=by29elRy09o1WfWwl&(EbkHh2n5EQe70wY(8RqW4qOGDKkFDG9cz6NilM9u+kF^sTW zfVgl-M8{4XOtvn6$#_n!)=vo$&Hp>r6yk-2{R?iFRb{LUhMv#3PH5s6i%n*FK3DQlKT%`lk%{@A1_q+j>LMpegVYSzpJ z*B9g-OWKD#Q^>+LXwN>6@UO3`IOJU$LM7K06&3Bu>8@9zxZKv0bx+bfoeQVL$5}SZ znc9Wyx5a6EB^eRf;yY!bWfa#+;fpseTenX-mZ~N@yOZqupX~EfP8hx}H zh<*tPV}^pkknn^NS8#e1Ijy7A>3cE4Pw)U~uyH=)^SlO({R}`OOBkE>;dj|3$0v~t zGblRwc^HPadjw@$cQ2>62qOorm>67*>&2KE;q^TiF3vtjM?FOZK z+LoZ5K$7oKN@6?mO(dSLJLZK2Jw7`+YseiF8NQ=kEZ+G_G`2=Q2$lZ>5|{Q56s&c~ z(2k<7kVfltV6RjlZxr8i<*fdiY5OwrY4&Cmvxhkd0`j55&}j~6k7x+qEp)0S zgHO}!EwE1Ejb{-1C&JzpV#No1C>mfwH01Sp&1nJ=K0;ORfFv;Ljh6`dSFsr>WQLJcI?j1qr;*(y_hC}g&%~n`2o3y zwld}qtdIpK0u-p##^u*}USkYf8r)&(G1icARqK3k>hL(%@|j^;A+kfLQe$!WRMioY zLavjUt?oRvz}kJRpjihp52h5SsF*~FJ2kNWUHNIY80Yk^5;A^?2wKdw+4Yz88SXCI zQK-~lEIpWpFE-GvQQV0}!ZFY}-eN4XzhQ_X2TzPrv(QJkC7M2MW^r{=sulIj2D#%i z%<*`8*K?IlBh0U!zUb+isrGU{KNmgl7%!kq+t}j^XM2d3;hYWFO@E!MC)89i?0UmU zvMu%bcIp+V_~sN^B~L}QLtk$nYNa?dw8g(YYTLI?E;|ACwF z-+|<8;P|gt`UeC6T84q`!Vzwt>FT$&g;4!>%0_`1=|^*o1gj)# z)(suxmpNXH@&G>UoHzMF+h$3cp!is6lf#azn~j61t(+dPO8pK0C{YT9VGSqRDAE)` z!Z8Uqo^T9u4)IEAce*f2aUNra02y*v@8boW*&uJ9?*u8H&L!B#n+(`^vk5yC{Z99t z?q%b-wk;O^`uW7XS9x=luss7b)pB-K)UIReDH7319?P-d{StZmOIdG70^G9*K^OK* z=GV2W$UfWXvW^~G3v#%I+JPF#EmO3@k5A7O(`4 zIwP#kN6SjDwKr-O6*S9Phh;R`XZt2xRvJ|7ss4yoQcNZMbr~LB$LMFu zHppOONXSn0A^X_FINr?$B3Xu37Ua{$a2zN{vWnPi@bq_nM}K<<HNu{6h$G^u(Z?SJvCR;BhCJ)n=vHo)o>Kpi&#VeMQ;=t( zOpaNdXk(~mD`N4fMG4sR96We}=FhaSu&u!XlP|yV4j3zYaYfz(*gU5xFhi4dP~xb5 zzqld45xxHrx>t-Pt$(?g+k>Q88UHV1k&?Tev7)}oKf$Xcr8NgUVPsyzs>XW{?-cyC zFw`H6xe?aTKq&(RaC>}%*vxN%BycSY7FIQq8fhySL0eC7H$w8lPvjrvpk?68$MKY| zpgXTW1!ivu9~qQT({nR#@RrEB=B)7le%gonKE}W*6gP)Fc#2CAOO!i>N4+_JiD@G- zmL9CBmNK+Py?H)FfQZXhc$MWNW$A_m?M^isDh-mr3iqVmG@O1?eOY&MZ}O_0!Wo43 z983{uHd*FgX-ePWr~p8cTMn_U7Zdw7uH{G`el{w#X+8MjDly1bL%=W)Z*A{N zdiRYao;Kx8da|C5eF|BkxEVXKNhE1n6h3)^J>w1@%eX+{)0kVHK5i2oQ0*EY8^d+U z0UHw@u`Xf55>;-oUi*txLGoP~EG5lpoKtwWpO}n`K^*^gO%K9cd}U%u!j0x%Cmy0V z2l2ccl3BA;Vp$y>QPllX8H$%@xB|uz*)jR(z);TMn~ym-N_%Y3b%?=yk~UJfOU-8U zU0Qz;A{}&i3tveFX-k&*1`WP4XY*wEXByuo*0&gSbTmL^Wt@}myZhuvzlZ$tf9E0fO5%$5E=WCeItM6qyGGect;Y96Oa7L-atV$MHHUpj@ zvQ!^AeArpAm{Vtt5%rFwk^HQC>7Joi{;ncHj=spyA#pNBTgD&&c)-~>#^9_Hf&J{- ziZ^UR>Yi>R+AhU{-|i5R!3w-Ar-v0CCBKuJkjdIF==r#_!5k=`5*5ErP*#o9`g<54 zZ&ube6?Ck4{J58>rN6@6ol9kt0LGiU85_Ow%;u5%8c{6&Xu|ewsP6qrlBk~x!&a~v$Mv#f z^OgD`&d}=*YLD)smq{hLdz!_sAG^zvZAVYTejO^8Cui$SPYF&>saxz%KVCA5AlXOX zzfq31U>8puvCAcWqRJE9v{n}#=!mmA{Z%=HwOObG_pElMCTH6vZY+~F1M!1ifxVt^ zwvdQmTZr#dm+|80$0P^ zHbqD_^O1)vf9hNNhkZbQ_Md03-L0zl(`~RgrfG%}R6+k@IR4XZ@ZVFmgR#9cNYV_1 zqW_CY^7n6l&({CF@@HhUnbS&#(#YLE;V8nak)sxn%Yn$3E)&{f8==@Ru)2f}>wySE z{jY6*HClQ+>DBhiO4?OhO z0PJNHppWJ=)26;!-C=A@dI+G+g@Jh8K=gT6__~?04|4OSRN)-T`<&ofuKcAiH6n(* z%!x~Y!$B6+-=&BsV)`%}FZ=-xCrsh??Y8*m5cB=>4U^?z7+-frJnI1&O@oHB^JWzF z61^AD8U7Q>1!XtAV6burX954bGP`xWRs$ZHzV0sx@;sJPV@Z8YwP3^E0b>M7s&dP) zogL_xBl20iPzHw_w@qfopJ7-%fPAt0LQ2EW`6cGMB#pFIyLQ68MMYa*Q)9_SN3Q_mDl;7B$Opa-} z3>iwC!y_A9!hf0as^zJPMDLRRAcy#(pe2$-eR^!ECV)7tu02;)DIg1@O4UM<#6==i zq_qH{LJ4sXO9?F5X>>6BIT?O6keMg{YJt<5opRX$eGSMTD`+1EPSK(LeY7l z=62>K;1bD0kXFOMcWf`5w)6>6Iz>>0!U@2BJOBKLZ;O`tQfntDx?-D?bzC!Vh zLY>Wlq3<~0q9R1!LmYTAV5k%luclTu8V)lryfWwy4{L1s!Nv`=1>n=5o?|g6D5Kq> znTp9lAp9qRgv+mkm4P9BZ(;)M2k1P5o&HeSRI!&zeg1)_00-weuQe9l9R0?l>-6R& zCtBS-=^@58%uPBbu?6R`d+ABL2Lm;2*GiGOMei}^Miu;`ffU+sbrUM1FEw6G1}oOV zX1yDXIPt<`jO^)m;Ug`#;nLYkGptUYnup08A(N#&mhOP3=I3N!5&p)a8^RhwMNXU& zqUJ;A+=nn#;AmL-u`SF7W2A9C!7^0D_IQ77Tn`lEiS0Yl0Q)-K%xR)NWH3m2|)H-^?eUcazRG}em~5(FKwKoUfs`%FuNXi;+HQ_Elwgvp z)YP_0Q1=vA*gkFXPsd$>ExG%R#drKTG}X=%2hxe!*tXQyVn`Fp&|ajr{5GqO9^#+f z%LmJ=`{$llkn*(B>3r$5U$NBV(asbE<^4itI*<|w@)Xl1Q8W_TKGkZygK4)NLkjVI z@GB6|Nc;u6BA?E0c+`tnPizch?$izk)u9R1@4sOUfyz+0iF|;DehpU9Mt{U5)F@_2 z<{PT!)8hsxTmSfk9p)er<}K>VNow;kFL`uE<+W*6^%kGRJ#h043>X^`LV!df;*uG> z59MJWVnY#jJ#U`Eq%Rwr!-5cXUhEHFp?}DN23h^!^G6GqEncpP4EhiRK)LrnusZ+! zAt>5{^lxoU|M4aKJ-eX47h`$o7fr7U_99SObW~YIhS4MIFz~Ua%aLE%w<9^9xVU7U zr(f2vpopr$tciy>YfY8YDT$qBOXTtiI{@p79j>y&n`%)Z>Eb!mo zyU3FLTyaY31y%1cV$kTYv&U&yJHoG(BE9Ge)PgNsbAoAqiI1K$^Vp1+uw)3_gpxQS zcauCSh*Dqi;M@oLjCg_zk7i9D_M$A!u2c3(y{bfGulA6LV<1$yZ67l(4xa{dC71ht z7M0O0Rkp`afkcY<0+S(EHEs{mf#TXe>DEdz3#aptg=O*3F_14yJD2C7+78VkWLB{d0{dBeJ zi2VXs(8))*J0i7>x!QN@s3oF^{b;rG^45|aG%ws<(?=eEdc9C%d0`D<5g>YTcx76LCo81FOX z*0cvw&=VsVdwy}lIQI(qPLj#12$(;H&|mRAA;CU4y7@koKg~7d60;l|9oYhB>nGa! zPV$c@u~38(WLAgiu&c=@-;IYgo3ZT1B$$%&G!YSCpje-elrkBtb%0Lc5jDUtJ4n$!)W?wtFm07AW`Coyes2G$x^L`a ze)Qao6XfA}A3$E2kp{^qn8mq>rp zebfdPeYfaRIp?~zQyxd*C=n%KU-MZ+S%cf}C zOFGGCXL)0*=A3G}>Pz7v*T?Knmx$qlLujwLdfO8xf&z+3n=@BmCQlwwpeJVoCctG9 zy7oeq)t^m_-9ra{+d{l#&d!$wNa-!o8tM?j&!|mp zs#I^e_s^I<0lx)x0&@*GgdR2>3~2G5IB()8DSv85n`jcIOG;3~i@aW{(MG)l@}jQ2 zc-dYkE0fFjWo+^4YPw=qX4dL6VXozLxklz7eT4;|iCc#jQj{3&CW{JRB&&jcLDkmk z014kQ>!x;ClGTN9VE=0C?~szqPx9WdV|ZOWM2C4`J@?nv#KTnw!Nr3IbUk!#bnoA_nBGg{sK#3 z=LJqgGQyTbu)O9brBkdaG1r@(?FdOO1jfe%ZgsQ?JSx61fT-ulegJ0K!(ncTbCiR99@#w}?d{*f5|Q?ex_ua4-)mOk;Le$P zEV)w?ZhpcuK~a*Mersd%;ADUd zwaY4&WcF$sZ$WP}IUdD;kcqpYCZQOK+da($3}a_l$1ac%hdgS~_ytpKiPQZcj(QXz zg(dPL=)cYbEV*D0Occ_tkJW3Km@B_(--QUEKK^kQLP>2ZhYCvZw+LWhtp7WP$D z!GkzlY5yN(@7P^=yrv0Pg%xwhHY>Jm+qRulRIzQ_wr$(CZ9nOr)3f@VnKQkf7yAS3 z|N5;P*L`6-?~3Na<{I@`X{j{(!aV6?b!(>KmB#$3BZZUpr=tBTBMpZFJp(dYKOIo8KS4(j^L9Jnt zx(=BYlwl9qj{$PEb~4lglz2;-_Hq3z<+f9|h4Rp2^T}E*4D@ZBs-phDtqm<7N;yWm z?QA|j7_wt(%otj4iG8nGnzl@rb47Qaars#Zw5GmE&@lsjsNqPQ1=Y%1yImoA*p;o! zcHtJg1}>lm?+Sk2aqv!Md;zg9B@%PF(*hvbwcpyP!#MTUd8AXb#K~j3oRaT<~;_WT{3U{ODYN(q$27$N_*Q8M(wqL&$u_h{j>8 z(bDc4>`*UWU%nz#Xa<@oTHsl-d30@!lNV2sEY5t|qP2ZLu$!3kqNEzkAv89=`$)Ooai@K#&gz(AV|_sa zvDBy|Hmi0bwGszlHIy@VMkAj zPX@`6-S{RkvTEmyHOxh|KQ-2d2K8K1SKD!^gMQg<%w%D5YjPzP3|k0<(T%$&*DY=+ zVL~*<0Fd4`t6E6Vow#hwkv>kMOtI*+-fMNZ&3NYq?xN(8x2I7JBjR%S=6it5dJ5vb zb@GhEbNnoqoI=A!g$&IIQraFuB1HUjHP4TQ%eK`B#Gzpi!QpIq;r&>E zBn<>69?6mgA0kX%722+pUS|VNA9j|`Sh;?$oGz-EgsQTF?ugKom^gb7XIngSTntWL zBy=zx*Fg%)!J7wV-6T=i6pUM)VJ8ps~T@^Zfb+9u zbcYz!c}~A0&~Ww$A;eVm(v9m-Xsd3zxR7%KFgF4jG@;{|i{Dk2vdJjZ+0fYKpYJt6 z!t?P%1IW-~+WgiX))k*brbi#-!tFi75#i)M|Kb=Rg~t5x57v{kBzXN*6ND^6H)sM> zbG_1Mld4_$8lAtdyMnw&`=dpCL{^{4M=UUTR~?R+9IUFFw&hQNj%p~#4b(^QFVb)w z1h?=+=&zy7Y^9#>7$8}|dLKNh9jMF;WKIXzK(dfIXUqHXs*fTX zc4QPkafcQgyTq53_jC-e-^2*CUp1kFuH9!28V5l|Ks7VVjI>_5=(9J|eL7+uj9oPo zeBEVJ!2Oh*on@N1apS=12@DZV-#gn1KlM9^TqYu0ub4hmTu>{Vy}OxE@&nC+X&|e6 zMJG~uz)^j%M!tdNWhjl5$TX27ry``3_z!Mg6-t@X;=5~xOvR}}-`cE+;JQ{$l~cWF zZ4k_C$%K@ob@cugT{Ye@_PXv1`HIF~bJnYFjk;Q;^DVPzKKQUkfJKZI@un@BOgTs& zf;!BouL!x6MAp&gzZ@$g;xdGpzK@l&-@kvuXaBDv`yaO0-^x29<8J}pKL9eh7HMEQ zc+W|4e*WLTM1;U|x^rRqef>qV0iZ*Vlg7UynVt6^2)x1|Lww=3^Wf_wh{){dhVIf> z_HK@r-oXDNozKhmD+4+x8bQ?|*eZ6THFGQ6nlT`PEqn&2Q~N3R1OP>fgHs4V2?|#A zJV<0Xj{-ik)6!zu=%xZJC;I%C+TWCMcAp4Ux_%If_rM}ld%^0X1RQGR($naN_+ z)_F7>{>X$#vp;-Mi!;30&L#?|GpZ&J7hOq14iDwYw7CU&qHd{>=bMFtXP^P>GwV0& zXlDsWL3T^p(STvql$7@h^!b{=)b1W05ya!qQO!=l`tkjmC?J~z7JsgIS!11@^1l=! z!|c^MN20Y;(YG_0{T9+Cbrt)%*b(c8AePx9ox#eJ=6P}6d@ci)WsJks2%#f zC8K>ex_={i`>*dW_HEc@re|sPNAF*iY!3&;R4w-zsE4 zcy~^AkUoDrc0~)Y^)3=?YR>rjAL;^zxmA90xXZrDmhq>IyNr~VkE1u5A5gBl1Oc(Y z;6|t}woAkozhP)1+~U+|KRXyN8N$K%B{s_;utjw`R#gpjr_cgbJOTci0Zg0Wf`}JU zbopDA78hrKqZ7D3Bi-f5(zJ!Zb_o0w5?}`UM4)h?GCDcQH#ui8Dps*7$8O$OIa9mr5L@KjO9^NP4xzCMHxoHm)BD z2p;nx1C-lOPDIIPPpo9)8ONeHi6)ez%mf_$4`gm}aNhTRKRmf4lFU6C?o4!-j~sv( zp$$R}Auh;DEL8th)V0ccQOq3Ngx{fv0^*x6ff|@QlIH|)zA>(s;-0j+Qbg>A{LD*b z5M6B|fvqpTlRb}KPKtp%=qw)LMVtwh_cBFrPVbQC8rKIiaE=!y%`MhzRttZ~y}mIY<@g>?BB%w1}t$n(PX zfD}Pkf%+Pi;S&`5;75v?8iQSOuuot6J}jv`!pZGr|X}0^cG|+ zzw+{x>z}e^Gi9!ucS3Y2k&wOqtZ7S{=@MD*|^1JIKg_vt9qH z3P?df5LE;@?EK`6PhKpbG)YGPy#8|Uz?JzyjtDjgkF*9RV0kZ$aWPn=XZR8&fN}X=bLhm3uLFtwI8p8*UIB~yBRCcN%#eF{Y9+NpHzif zs=gU2x0D60xyJg1Sf_jsne34@`i&gBpA|sH>X|#Lf9Sf~R%aOyw^|ug=CxduM$y=< zVjYFIg=(cOv2kE+ehD6(r{hm&(oq|<)VG-Ri7nd4`9LqsVR0K39k3WOmMs`}X z*JfZx(7eNQmCR~r`9YrlY2AC_q~l6TbHPd*9x|6TZhS~;Ri3Z#ZR|W)u3}}Fjl}bi zS8E)!#r&?Gat`IiOX5G*5# zi1SFMco(qZ@s*wh6_kogrG)~UW+Yv0Xkm$_uzVU`Np+7$*aI)cvNq$Uh-IK#;`R^53nK?*UO z9Ru_W-Q#iHZAt$lf(lDl@l2n=`=5wLw#&%B=#F8+vA!F@aN^Xog~91tPH1p6!!x!xt;G z;Q1)AZUd1R>Viu%)PU2b1a=UCw5}W&)fG^ zvlsXHg^n*ex`XP|a%70~|Md_%A1q8}_RR&^NBXyNrvDmq{?A(Y57buWMF9EeJD*q2 zC2^$$+*j_pJG7OGvW#R^up&fAitaCrYy5IOx-&SMmh;JEd8%oe08)gMRb*iTW5;aY z<^uE2@CEa#`jZ#%`=w7ztE{Iqy`O3zUdt<2?nfV+du~-W-0xR!O+PFLyByPJZ|Z$a zY*+sVGJwfH`}#;d=ay$K+@$*yce!aR(+rzo)i;{z2BrZ7w%h?$&q~Mz+Cx zCh08LTcfk=cJV)G1KaF*GJNpF<_X3R!Bv)90ATaX-WXu>%yhdLYy37rRj*4tTdfQP zfY+w@HJ*|8UWPO6v$cFW4?S4goBetkk${m{ivl=ACY@5M`aO<#?6`^_b1(`zv*%yU z$|BkD7-v+=t5Ggx$3yw$j+s4I^&)0V1D z+qk=1vS`@`?pFr}y3RSi#Id>>?_@izXWE;tNwQ9nQ0x~l%_nfJo3}8tQGipAOeocf z&0>A73KB4scb4~G=I)Wlu*nm*kk%HiQNkN`h8dB(72FhQAOi7u31j{tRw5>6aAw!3 zZs$zF1VTDj@Lw%pR9UGuNjD(qA1x=hEgbDTS=&;aJhx@n8K*08e2ZsdC2~SKai|?V zEu?>;H$6zt(O;ZO@(h(Ta?+PB_i55oIoBh9PCX9~d~i@G=vj8Bu%fil!sam&UH9lo z^Q+h}gtE+lvd}f_R~j;IqJfve(ptMqE8XPjR7auFk=m7Z=~NY6bCg+~xj$SYz&GGX zr7Ml+R)9HvP6^4{LnYx31V5+|!Rcb$(TB4zIV91~#l|M7+)agm6TMy!(4tb*0*GD#BuS|2rWs$u1UFi5jpjCl|nGkhHWw1+Po$^d= zo!0EPRl~7aHZSS8`{V3p6F}nYZLqeqC3&5uA^T7o+LC<7{-V8+fmnZSF{%o| zx+Q7UPYaB=3&ljTOAWnYDS0vKHim&)Ut6DS|9j}X;+Gglxxg$kpyiiRpx?4Fuvy<_ zM&*D@?!Z6Js-cPE1bxK5w6YVO3pnsHo+-;`%c7S{aA&0 z&j0hT#O?uJEb~Iipt@B1xqos=wlD9(YuzwSw%GmR2}0^`&qX?KTJDeOnMXB@zH|Mu z{NEZMBSTm__ffx$nn+o?8qwRF;zQ=yw+$I6!Y)e$s<$+Mi<#6|`lJr+QLksr+IV~! z@gG6?p;&pEqBy>TDS2JfWM((erM(s~-XkeDn&KYs!3gBovVcb|B+8L@GEWv%A-KBW z>5LnIL#>HfS>XZhG|{kwKvg8PMu?dtA}s6jyIkvK%bPj0 z`&ExBzx(y^@Juy_Pn0sKCOAJK*C63JCL6{;m1{=^wFe|Kw*h5;u^QT}To^2(I)%tn zGS>^t`EMs1YpB6K1|IR)23{a*cG&P9w2H}%nxjBnK`?ae4h0c(6As zfl937qW&d%G~5w53E>*oAa4Bgi-MMLbk}dyi3_&=C35*zSp!GIES0S>i28U!3n%yIyQ`#1yKSNvX`*N&2}Pmv#Ep3CM*n(~ zGE0}NU)%$^MqYDHz5e+*hMxVv8idk5>_UTZd{>?W1wAze@5On{ z4%(h?kGcfp;@@E)0N;t?j2vVd+*!f7CXj`z7k1|-yd?q3ENB>slts0a@Q%A5#nOgn ziY^XsdFH_x+gJQ}BT7-uEPaJsHB+|K;^n8^p%xlxa}iB(*&6#@Sd91@G@9mboI$ap zHZHm9B}Jd!PK{ofIZNaO#cK)pil9bFh43f=jG?x>TJ`H2#G9G(#JxVr@Y2OcV(V2{ zka`ROx^nS9C3b!)e&PP}5(|s+BfI8%fpz(2^ZyS3&3{fVjQ_VCRzc(2pbCvEuD{&6 zNNS!$$-T}g0c}7kTfKccX_{Z6G`m8kQ*x0`dU>)}`(1}WcU!>wFHh<7gfYGk_eJmI zfyWWYmXR^e7uP%J^xzzyRHR$K*5SZI7iVi@h^AcMh38 zQzW#T1{yWOqmu1*jnjn8$yZ`T_Jo2uDvpN>{S%QZD>}e>8{$jTfApN8+VE8GN5ete zj5M$Wzj{lo&w14J%p=9){_j`sc$J?7lm!+O zD;9I6-MD_FU*2R5Y>;{;4Pjtu$nGou(?;AT?;koQJs|YWW{5R6fAT2dNy3^C#^1EP z6dPmo4Qu>4>P!G2Muiqf7pQv&PSP_d^^3VqQHO#SCLjyzC|T3ZJl8ZtN=6KHUDSp7 ziE_&1A&KO<@|?^OEcmr$R|ioF0pv^BRMTa%X)eewH}N17cA<9h6baSB7D2cZCKLr4 zVWa#lqIXjD*l%;+5NtK)fa@a^RIf2jXkKGKVtY# z$?4J%rT6yuI;xh<7P3O3DHC~T-SY$ z$Aw|nGz-uQd@gLVVaT`WZP*g7;9C3Ye!{AioNotnac-g6cUeM9s-XSbvmR4VQ6!Z( zD*AAcSj^V3(-7G#Q%?D^iqCVH*tb!5u$G10hM;b9e%-xR1^bBXjK*jaqxZ))JUqK) z;)%qy$wd@l!7Gg5T|TDb=_Nl%p7<>H z%w#&02mVc(1O};|lnkD=$7xlnpolI6Gx{n1aJ^tfNOQ(8tVqxnQrVpFw^52Arb=hV z-dpLjF-P+6tocGaiI=)&snkR_zSRut$R%XzJ%xc;8=}$r)#)jutb6#PPoyGSREE(k zf-PwL-R7d`ryJf8ES%WzIbg0R@>%Fm90k?)f1zYW^&&bLe;W@IVEtP`$vt#3ZaR-g#HD0PEQMMPevxE&-iQ1zFa*{S&{Sl7fJ}AA5mcq0qlDP8#ymG{evs^ zC%?gJme4G1^I?f$^)wfsq#M8kX-nU&Yhvtb__@+o$}%|!*>dm+*#nm+`bGeq zC-%kw9Zo`41pGd^L8NIgkB|;RsY9gRK%LAQeMhzmp^jOU?2KiQAQiyk1Yu=1gn8kl z8WtWP>B(3Qr-7fg82FoC3FPLo8inv%M=R$*S00T}h9o;0O0Y?1Xnny?^VN``M^R|l=0jy+@m=s|2OSXiIli$KpV zP#=1zl^SiX0y$_NM4eu1Hl`+J2%%u8zM3vqjY!|_?~9^za37yLhk31q1DdG}1*05Q zAt6e?pAm2nf!r7xQy`Nvi{Rlol0_}~9d!gNB}By?FEJ8guthwg#vh2JiHb&lg1niB zU|K~uvxDF%=grEYKq-M{pjJ}h+BX*qe@CQ0Gs*_p z2iYRF6I@Uez6jVUhUar6tmUzvDAyl}W`cvuBz=X_+y|~=K`oEh2(_R+is&&0GkLF; zl48vyv`q_#C(=@Vw8hDe7n9m@{X^qU|#6Vjen)3>D55(2jIMmtg z5yufl7M$4&_61CQa7|`Q1&NxHzz7^7f zh%4x!Au5}YqqtF&(Fort{#E?Ia&A;4rlXspN%9j&s&}AdWii8xX89eMkArhrKo&!gD$p9l4U+y8I5!0L8>FR2wU|RXJD|x=m!j znp0`OiM3dt)VahJY!4i&%O`nMM(I5q;yv9i&3=0oQltSvK*bJ3gk0!w4w|tt=7&8$ z98G`c?|N7NAf4i8V%U-AgixJ5IrJ{M8x79FU6<@)ReF`<;^4jHP)y!XK|1oiCyLMS zp{5zVsrVTQN9D%kU1(58=^ZAH!n*H*;xp1)mP@88i2k!=SI3oKC|s>^G7qh+Solke-<19RJ@mh=mry-Uw$`&LkVOZyX<}f>XK5?{?xBiZ+ea}`;)r_qhvSH0j zyxd_tpPRnV-iFuUS46pPTZvU#M-J0jWK%J+ULb6z8TN28L_M>vBjO@2NR@aH8|%8f zVc?yC;0XKzm@9NeiD#Ky$h246j^x*l*)n*r8V^|TidSmceS-^_ne*R6 z?pN*&1nXes`RmzmA$18-Xe|s*C>@}JB2Z`t8iY4$-!!S3`5oiAmds<>w)1Ipq7=2< zR`hrnx(YM0jtYo)+-yFsJ^2cJs_YCh=Wn}#dHn{FEcPHjNYJVNI{EyfB6`@l;q}M7 z@kMVifI!7~Bvq3xSb_jwt?2?|DN=EYQR%O3C&Ts!2Q(WrLcvw=4I?@s$eQ8_!H#_( z6!GOb=bvFj>ptwM^Wv^QoIHD+-<4BW8 zs?X;Of;41`0_K?MtyxmMI9AE*mlW170gX3v>IguuWYfyOeyYX5R7&vwGVlc{BXJ@? zz7VpmUg5xc;Pw(`?bSK(S8l;phk?+{#L;Twno+u>y}fbh$EU;0fS{7u4t0F*q?$R| z0}n<@I6E0LNnrjA-3?Ps=}tH?d~_RDWH*fULCucA5sT!FLKKGZYyug%l6VWYG$^`4 zPIP+pv$}Zr1W7HN@fcLQiQiFY|6uq9yYP1UVRbQ=ozck9-{sG$EH8f7X6(^S!8&Q`!X$?oL^g*;{E6`*h7Wq4iFF4bh(ra8bofE&U7`nE%KXwq0r@)HO z**O4HL*3gIn`~EI1D(+v)2=R=e0FWg8B`C0ka4u@=C9FjLoaeiTfv5#Sl>uIy~*wu ziLXo&-)+9B7MHne0Ky~f031f19Vf>7T*)Yr{`UxdOqR%4!8K=?^Up0?dK0@+oj#bw zg<;lU+^iPsj@UV5l2AZibrh}%Y|F#F5gE^mo)&uwCa2;1Z}cvSz|8^J6daLKb&h(B zVIW=wN0YTV+Cs7;`)h(-ajp#(_FdVs69kjGb!3kk zlLi)l3c3O+$27PaOP$q}_n2HOXkV%E?O*#Y=lIzQ>r@ z#~jCR+A%+b;u{~gjNk75MNzZ3fV#H(y@79iZ{Yt%OZs0^>;JOtlmAbyHxb?cJ|Qbu zeY@?zds&ZRAj&T~?Pc`*?M+Q&Qrtxfzs;dQl>Zqb^>$gPd_=)IEC$a5mH!hF_xa~L zSwCCKU_a5k+U?8x`i8?%-N)HeRMwBzV>i_vD_o}an{ByDg!y0bqV=9!0gX zt6lt`{@V$Qv(QK}M)ZV^LnSFfcl>3X^6W{%VMO!+4-@3V3@b>i`ngo#xI7t$o5l1N|Ypb^MvsI)xPu zvt3LBi2+V&jXbsYzZ~AXRCPmzz7Oy6-?t`8Ki?=ke|oPe0Zz?vv*8vNu86Yn znhY!0j`q)OuIjFi-ru%#zJ8zuZ3TEqhU!s-qSeYt?m>Yv$kp$SS#x#^&~cvf!`A61 zpbF$*>gY-LwEG4~b1G*G)dcG!OFXlO;&>npcv4Z4k*mf`cT}u}beE=CqlreN5IKS< zjmfOXM9*qg#g+s_0t~50X}Lt27_^UB3y(}tCbCa4#wx831*4%zN6LF%u?PeA*D_j`0C7zuZYr9{(XICzPAt?d8xa1kM(YF;6ymH!2Vxa0;HIRC3G}n#PH6_|OJ4gc=FDGctLRnUe?HJs?RxW>O zYFrnCaXXoVI4}xA`9X4n6;Di*M4{sFOLeTmPl#$>yq+MrU^fWUDQt0^Xo|c{LD5q~ zBEyRb8rmf&rqkIxlhdwW?R2-ja3blprC`_cWZsv%Tftd!Yo(nswJ^;x^#ChhbbFw>(+Vb@#n5QiVm~a3k}O9l*@3O_tMAKZ2-tYbr9b zLfE1M$th9oXxXZ*RRkL+cHwFMcygnlFeGQ$PkPP#+GdRgx>1W+sLckgU)sts(Og9^ zDZxDGen7vfPd8)tW73kRJ$UUIW|vUD%8n`Im*Hdw;Ym`o@Gi{^^DRaUD$KT1BsK_; zLUWItRk=I)CrN)pLbt3!`27#9_5q&@t>IP7`YHv{A^q1!oOovBIrn43G|U$yEAMOz zI63sEeXkc6L9vs>YUb62Wfh}xuA%;T$^3gTt~1Y$+pXXV5s;(bzZ0Ejo-mTMcl?fy z&2lS8-=BP2VR!M3?Q-aro@s*_>$7qic@s&b;~s;pM-XC$e(3vGJIaRV`Ky6em=44(j7VN-xtdNS)NJFvMM}?^Z%T`Tx-H}gNk#DZ(8KbVQ7FGp_>A-UrITPukBVz&Jydqv9CJ5 zsMkGk*LnRb_0!zU(JLzq4wIP^`%hPI;N1|9bF|gQdSGWL%GG-MbNz`SR)H(Uqm}VIl_%c}*=1|a)i_Tn?t(O92 zG^+?&uFwl1Ozqb_@u|=xJunEdvc~Z+XNC*eXjIpZW z97)xZWqdrl6zR!Ab;9reiky8IGs*p4#qI<_39+}-xPX*!{|+24nPq|m7O zpe|z%tfGpSrh9LORlUkaRGqLAOB6|bM`q?BZ}m6jr%c-k4>fr% z2cvqZhRemUl@T8Fp-76OZOzz83(W9F0c1>z&pAzTm@x?p8J1lozW6BPO&4Y9r?I7$ zfg1sHvRmj8Fw8iT?9RD#iH=y5i^s(S(J9q=(tDfvaR`Nlbk$>_LHduux~%Dv#3hs-8?f!Exku^stquVQDqQ% z?s1A2h7D`G6TO<^Rj2^S^IBnjWl@4z**-$l!N*m*v~Rk}e)CDILPp(C zxOANl^EutMbv;Hi!Wy*Y5G*i9jl@tGSfQ=Z7OD`%C7sIuxc~=+Crh7~hA13Tj z3{Re(#uH>LeSoBe|NZ9MGzjsLV^NRtP-fWdMQX^#sQ*2rSjU>w+aCzQ?w$#_YEJ}g zv)e86VH|8fAApq5s7fVnPnD7p!V&9CL{(p6j){%GP<}nmtnmTn6`qCmOujjP!vM@P zL<9XUpUS*)E-iY|;!DMJP5G|g_ev>&1KibP!&Kh9P;=Ofc5ia1K0K%F?M=VKqd%wt z{SIq$J8A(?@y>EVRJ+A|2Hu>#5drq@&2oAl=+?2@>|T4;!0~@!8$Q!@rbascvfjpZ zmFPezGi!V zvOBLgK~cCQI&=m?uu&P-@a(q=N^V zX9dxAgTouaeiBkBthL_88_{~Al!LLs-^HnEV&v4}mNA9%2o6M( zO%||AN3~rX%_9T(+CRRWhC;T|n7hC1Kn7Eu>~Dkh2XDYmc^GY|_cDDb@C4$u8gXKD z4fb^D{@k$PifMY-ZSd>8`CF9W!`M*xYKnt)HYoOX`DNza8~W zF&}*W7;67xAfhy*S*-uShdllvr0Rg2D!FH&D7wW*r^3CKnLJ~lX1$?ul0P`kha$SsXkamB z;XSz6uQ&{RARF>5;LvehkfxbY`O}9TXMhSVlvFgb`?p8cbR$&6qQDhVE<-QEC#1Y# zQ|?&XAUvM-*lS^RyA7g>N~LH|mbQ9IF9mnL?F5l>QMEDF$mA>Xq7$eN-vt^cQe^-V zk*aKk%P4e^lfiOk4QjGgRV?#6GGuEM%n4WPiI9_yfGy%#LPhN456~sa;$!vXW(y_n z(0+-%CF%MfX=FG=he7*Y!ejtF3K)hI>|A)G(1>fzE22_5@c}zTF;1Bl1y3yu-YPG=X; zsT{FBv_yw$zP+~h!Amf3fgW^iq7oibZoBi#J^k96<9_EGYCWHWBT7`S=weS(JwBzp!F zuW307Md9gnrdB_zs(wP#|E76i7W8-XD?0`a!em<$p<*&mFhV$SLSWlJR8nW?ovCj9 z`|`=_F3qg;HRegA!#-!aL5o>2fyxiXq`}o59Fj;NR`OPF{GPGoPhJ)*f zSjZ(^mAO3{m(C+yQsjCnQ%8$cv`?qqHYNMWn&+ylH#-VtPFUp_vf8(x4=4+IwHPU5 zl&1bI9o`qQHAd+vh|lcjKQ5@qeY@5j(G@onrk$WUps+kkx@7l3)L1yp`8LSzBeZ`w zPOO?R@r^2b!=+HOL)2<~!hY)!UkJ}}zoCBK8i7xW*^Pfq_o)_CbQ1^v@xu!4-@8rx z)6TE_t)wy3bF}&2S>z`=$Vb>;uvFujt0FCs=a8Z zHUz-$pcrFP<0fIjUAhTDTfGt6`A|g8Ldl#Oo-o0@Ve`;VM0N7O55#`fht}y$%6k?^ z)>*h|Ky$`N8dx&=3S9SQ2(=NqvGVC6ymm2o*6zOlAoIfeLGDGy!eid;v>v=W1oTc6 znsw?bHZ+ED|3WwA(2*WUjg&NQ#E|lIVQJqx`67UUQFtD1@ai@-JFR8sN%Xg_FiuRG z#`V(3;rDk61hI;x*Xrm?o*IKli94w#OY1DE) zTWLka5z-Rw7`Z|U}6DTYK!cSkRIIv67ulfU; z<`Z-Viz2D52clnIBWXcNeoz+cBQJ$6@R0S^~c+XCYdOB~h{X)op8%5J^z_xIM5rdK5U9e2y3Nz_v+HM+_} zmHFkwTh7?#S7fGHjw0>jWOkiNGIZJ)jIYa$e8w>mh)pQVL}5ZItACiI!CSa!dddxg zJrl-eAL6f8qyuva5>wQtt1mWZ=41`77N=W#w(Lka#?8kau~hg^*)i0a(X;W*k9?!& z3hpp6i;37~yM8JlCSsA}D8Xl~@2>;XmHy~2TyIaRm0p%-sQ@Ij$oNMeHaG06k%NV@ zof=+xy3eH6I55|##uh`?<~Q^+7;ckE(=!y{MYv|VDN*!wzg_415W4bkl+V)>-i7D- zm~jrJ$noRW9crZ@NfE~zstrv+zhiYx-$(#=^-lPf-mc;~&q|5Hi9Znc{zhE$vf4)R z>LVzbM|L)}2+)JH+Ezd z_PJDQ1hd{Nj06CqR*#mTnHppYG>nrC$r=e@jjgTJGj`yJilH_8F#+4?Yef`-fD#>+ z3rZ{1`*i7Ue9uBELmFV4%AtzeqD%6&ww2qNK3xz8TQKjyG}Xw=@KkNg)SslE1JYDb z;d>;Bl2e8R)NUHDI3ByT#p0V>3@#x#Gu3Z=+8`)$gSUOTO|t_pwH9|CCG_ZNqjc%C zY;UOrxh&drdA;~V)an+6bATqF34snQjMfZH4b9ss3UdFZN5xYBx>S9urN1*E&{56P z5-+F4Qwh41eMCrL9u^#Th<63JS`TlK#o(mdHzlpUQj`&^p&>B^rM0vt8>(odp3A1p z(WXp(V~3jfAzXs$bfSQGOz#@3EWjCwqTKd$w!|VfLCH+=#w_^N+SZsZ?y-5G6HqNn zWTY8jx25SL57&*Z^o4{k0%=5(&t|lLmv)~M z*5w!UoTuj3EU9M7TAq#Xh1JY?){i{^-VBTM2ezU8=jy9+&O=3>u`{5d3E||dE24D? z`RvycD}iQ{!v!w`4j<*$V`&O^ifw;9FyEr!#>4$+VfJ z_ycDCrrCE{@CyTjX`PqDF2)J1?j}firatfxBOS@YJb{Vh806R-IZ7YuOOB&D=u8_r zJ1z3Tvf7fh-V)WyOFY0JJ-Uaho3p}_OR3%|9)t0cy535HsJcezQJySS6zE>z zg^_SKGQ}LN&s;u=4`~7rLbESxSH-*fK#K~cep}YrPe{-3URY%WjC{M;W)m_-crS|j zMg@?{94UgO_Q*#yAC;SHjRa*}-}Vk{XhAVPlj3dYu3vN)@0rwKh6qj+(OR+j0LS#U zfXEPid-gY#nu=&_1Qa2{0WgZS5lCf$cwY=PdoYK%Z7F;Xu(D&SLS!8kQ^5$%FCP~s zt9_HqhOpe$tOYu@=D?@wY{Cpj=eEGJRR}h&+?#?GyXMNc$BTBq{>LOc`!^@SJ*_5L zRPTFR&z^OsbA6|)$SbuTZT!naoGXNEZRrk82~i)0qmcNI3mOG0Pe0 zxLr4LhYNi@#p`?gcD#|J6QJMpCorqw=CkSx`K3O9g&;A&I@d&7HiBg4;XHXjJq8y_ z*B|*w;D0qkmNd<_viUyTAbj^`s{gkL?!N^u{R1~q(o(@xLf%M%mZZlnD2V^@06gFL z@?%iFp@OeGfZq(MSW3NMAqZcymp55&LZr-lfM>g`)Bksyx##0X*Pi{Bm^eQ}vn7uG z6_-nv!z1_6{$Z83*B5ZN(Yx+8)^AL-K2o#s<7R!{z9KO0z9X=2qI~2Tdnau)pV-ED z*+!BXRq+EQu-W?bO@r^&{5P#`q+4x$wx--bNY`}JF2j~jmr-T2A!}KA&KYx2e-74H z{xj!qP&ra>$XoIP@!Yh+|o!FS@O4-^(P~%mx!%%SmhcsR^rMp+(Y6RMf{krUH zcTLDkx(GVrm~o>txN$T>s*qS?zwAd7OZvwx+gZLjw;?}^MipFj(_A4 z7`ru;VX1UqL*H87}Xs(wR6n(7mb0 zZqJsD_=jnAr!3jOy9#w5fzVOMGe?TkX;$<-Z5p{~rK5KIpDBh`q&*R(s4q?}=f(1w zmvUT1xyjAJ65rf8qa__Ei(S~uQ?j&TU8$kg5clUmzZBJ z%~e`)uJP{A<-g~eRJ2K1t5LqT<_glF_+`p0Hx<2xH`r#*eib&CwVWi(*THeo`EuFM zU=y@AXj@R4@7{!C(qG`$lfbD}nqgs1JdN08(pE!HnT;Z@1WD^SOfl%Q?g7uko^||v%*HBH9*VzWp@RcZ zt;+p>jD2HtrtOk-I_x;<*d5z;I<{@AW81bnwr$(CZQIWGoH_5zn)9ytX3o#7=U3K! z)vl^tReK}wqIQJjRVLC+{BeOfWd!P_i$W%GDZSz5M@n`Grw&u#G8?h$r9KOZC z9df-KT;*%`ci!W}Xb*nEun2RH|GmaAu>rCETa&k6#I_f@eaE^k{?>ZaOmj&{O%aN) zWFFd-8YU^68iw2dld&+1u;T_hB~k!A5LLQCH>!Q03U!mG|9UJpd*}pp@;x+mly^?ofqfzB&NqQ>-2gne1CW58o(e32gi&=!rIQ4{@A2e$QIIDt^lyz4o3f0{cFO zFQ|d9IA5Ui0Nbb5xNcHX-x9=QK? zr~1#Z%}~*BMpi=oq}a$fwW5F|BAV^-;Wv!Cgri23_XO!B;W zyggoMWmI*2os;-zJH88-I zdaaL*(@ca+106`I|8U2q-Ak+ZF2CJbzKeqIp+rVActhDvrK|#WjpBR#`{p^)g{t(q zB5aG?G#z8)NA2qYi_7wdyB}-lTe~h7AlDmP!^pNpSHjo2hysXa8L$ zrq5N)o+xau!3nm?wpZ*jaMMf<39@5VE@ty3>*5Ypub!lQH8Fl;C7? z(r>n;%rQ2wF@|&xmTpk zTEUjajRwctB|tcT2l%7-MA64H)>;krEz{m2lAixE7<7W+A8f|R%=yx63T-f2sl3FT z^PRbHtW`b}(tM&xZ7kUS48t;VHD*%{gAU5fxQie=ZtoZxZ@y&PfMUtgQXn@`Rg{k+ zGo@>hJ(Bnw3c%0$g++s`@mKOyxM_BeXlmTPeMw zuD>3cfj1I_8ppzGMk~1rVY$?Bbk;O72Zj5>yRoy-oRXD1S9E~}yVzQF^axim+0cxQ z`e1-I^`^;g_w=*`qovF1AyV!}x?89pN3`O%hn`gSvvnICO{$Hrv%Wf%1PVHcP=QEe zoH`N&>;eTBjx)+@rAV4;I7apqh%3&g{3v+_|7c(!IrBai)L2aJ$>4vhayJi;JUWd3*ucPE| zWi}Q)wDfX%^Dw$ss8<*EwmZe{VXvheHL6yVe6s5%0Mi&N+Itktxo0k({{ApL zrORBgVp+)BN@-f>mfy4}K2pHW&1p7WNH+7gU}CEEe%QXbs_jf;fM=Wbpt!A z`Xxd7y*1}sQKfL=fyNfq)~hu`gWn=?@rlfI(&x`&ZQc)XimqTjXQ%p9X`lGV5h z8w6bGHJlgtHRdkq$7r=xm-6ZD&edPyowa19(1|Ltl2q4Q-<1p8T2oTS_1K&2W zb{WO*vv%b&g-y@sE1P1Oaa^K``r?GbzZsy~$}*6IQKI+Y8_U`p@tC`^wzIspVzHpkc)?1MkAyfK61GR!91F+5SLMD@sIFr?&ySdvsZkLH zgP`*E@Or^|lwJLx?{((T(%P()cjW}{9Z@7`_j6A|fZS5;e6S3}t6=B^IWNM0+{wGw zQ+=&ZOHMQUK=*`UY+CbQF96}4Lq94ZP&rsP^UY5$A}tsiawXvU#Ip@pm~lW%6D^Yx zBC5py(c0sAG?C!aPTH80LqpyI_i}kEGg3586PNHkOWViQyztn|`Gc}~ z`xT{y=o3oK3o_nQ6z$e)rQcIVIg=@|_&nQ9YG?zZ%#L*Zoj<7= zwe$y4LLjiQoBD?DVhLYaT9Njk%-uaq=ApJr$5IwmDmrKm?X;rc(&>p^qkgMtC$GtY z8AxVN9nYmbm$x>9*0ymo(kr5G8|L*3RJZ6t^DOz^6?_)l2al+t4|0tWvvlL+GPIt$ zIYx4!Q#Agfu3oeI%i6rI!otytTk^F19Md`N!mJ+%b3#8hQB%gKA zgs?z<8qxB`1=KKbsH9YRYVwAPCgSvf0=+*g8}py2-acM6jE;qod5j!hrHr3sLr3Q{ zBm6&%80;r6-CZ-EQa|3ZwmyMu5QTsEt4is9|9#t>qt6`*3lL;C#@k&^7@|hWA}Lvq zYjlo#hRG^xPU~k8r?@Oxj>Uaa^2p(6&&K^=xAX-(kGOZ81)6;fI#R2^KjnnA6NuwERa=&+d90XFhCaNaF+Q*gj) zn19QVp_5@2E|=n6Lbc^zEs|kSs2g=O=B)*Jzz5JfZ|PRweFblg(|e&S66`eENuf^~ zFVtK7a%IYJ9hO%N)L?)MRC4B?p$>DHtJmCMrc&t94UyeNQge*HKv9oI(ET_;l=%6$ zn3XgTd&QbQ84%{6+1uYJ$L!c-#h9nrHeg3XM1HZh=*;j^fZ}EzWIu)cE!ywKK|}(; z$<*hfUziS*dk~Q#S5x(u7FV;B&rRD}I4%_*ga`e3(oeH#dBYw~Mhc)(TqYtIguM%W zNH&WOBJ8Sj=fm^HHcT_TTdR+#z$nVw^$iQAz?EyYT)3!RrrY;`sZZ@9LoDvZkFt~ND>uSl!4 z0c)Q;b4|^F9S~o-LOqtScdoNqn66p%)H@R+EbJc@5vB!ebe^$&pO(jOTAV!^p+QkK z%*3O`0sE(>YP&bZNx0`F>C3o+q|~iKSLH0Y*FnIZVZ%z12AY&hyydZE+3;yhyGNq+ z!cuZBRbuEd+BuB~?87dCpVF0lqfu=~%G}br^I!n~)Y4Lh7SZ23z*j-GJa-2fTjY`) z!lC!!Yhw1cT@7w(_-we19i+yFx!^0P~V5s?e? zNL27^1)$#HA~wY}5mnwsU1#zNy+eWIUl^6eK7ZpLwg0+!DfwY67GvN9ov(F&Jmk?# z@*&8V^}uD%g?23+^DyD#LA1ulu*9bWvihV&@vQybGk+x;vG$Prqiv$mqgzA#mDs!~ zNE4DdzYEeimdQ(t2BPD_Kr6TJ*!t(gJ92*mf}jr2j>!wpl*C3hI0s+FJ46D%59POc zaCb`Rn7EBmA@3nwkg5&q3CG#FN`|}!*%tYUcX_JGV8Qm$BW-=Hz8^^|!)Lg?P&~Kg zJaD@d*PRzRw|ydFh@swp6S(TtZ{(>35CAs-^#|7f9jh#@4UH^l|KaEPkB&mx+QH1& z%;+zEzCg)R1xpCoi$rXtRt-c7q(E6+w!5A0PlCE-iK*qUnHcjPvIV!vILTp~q>W6^ zk`Kxcu#ab5{cRXWWlcWj28Q=yLI=Y%@y9y*8^4{qxWpW;3o)M zTs%=*g+A(x&TvetGxY9Yj->0@C}bY#Jvbh*J(^3gnsPlMOfL#po_Kwwu&IJ)egUCA z%s$<^Q}A5!<{{bA=FP?yBn|?)j%nLw6OPkrE3jV2aflpy-S%N!rYqa7%g~|Qh@D%x zqhtb>Do&B&OftUSr$+anbkeo9p zAX$I7LEt-xURoKb5m==pOUqimA?gdPDl0}N~6&;fU)?Ar_VX7>2)DGeOl;)y(P$(5;c6V6gRrsoE12Zs(~t)s9T=Wc0v zns0csu`XyHIE`+^&Y?F=vM)ya7@g<4gP?X?Q|V5pFYQgDFNEq+wchscc_t5oZ4C=d zmyoMtxL2lR-=>==->U)>jA$4tQCpm7^wA<*m{Qw6yPu12RS+<&uQ0A2b~>B{f!-f1 z{WRr_?gR0gNyGAT1!l;3+}w~W9{4QZQFb8axl{Tu-GBv|@L}?TvGwEQxl4_v)su7% zWIz_Y;LU(>8b)w@`b$L+3?Nb*)7rR2VB$#?8)f^5I$qgdorz)+BgM+f&!t|?b>~^F z_8A2v?OrPBG3R2(g7%FJ%+q%f zjb3Wiwm{Al=Mh742BX8+&Wm7@v|fUIk{hNoV6vL!?T1;A33^Uo&oz{_Vz|BgFxS`H z6pe|nkyD@x$#;AC8x%;hbi3hr63C2e==`(DoB*|5C!j$!uXWR+nf&F~oO5-5-$!K7k$^h=>}LMdBu!8}VW{Mk2m?8z4GbhX};vvB(l zwsZ^HNv@rQx83$4D(k;5P`Q~fI~f4B-vR(-3-7-J15%C_4rVqMM*mP9bhI-PaxpLh zXkb}e{lzf=jIHJYHVMYG^B1V7XsYcyS|_LlyVUd&F26+S%6NPPJt@zcVaP8EGfJOj zJnl$EbLmY9c;50NblT30zq!`Hn@pyRIkRW{!-*Xp$iCS+H=v9dy;*tCQL?BsdKmo+Ft?36BsIY&B;dib0y3z)WYRQ^ zO{^)Y`dfg$lA}O&ed(%iQM@RWB%XTa)nX@F{YJwCna7#sb8Stxg1{YxtlKZ$aUK{o z9W*kkpsElRvr;}Du|`(I`XpSGVwOJCMECirR#vs?w6I;w2|DvO4M<-t6jKba%qw9r zgz2I-DZVYw^k;QPkCt=ttgt_u4Td6h5G4dFcdDGCHFGZ#d)P^C=^gXGTBseP1Cy27 zyz1>A2;+yI%FYMg&D_k|@|;1(J8ksS%JhCT?AMAjOXD2bun0BM1*t)!L}1!bulJ@0 zt~DF%&|KcAy!&fz_jt218)5MRt?B$2UNie64EK7Zq+mNm$lAVN6}jM^fWWgbC@-Qov)Bi^58sC0HYNS;2q87$U`{6S?OZs1(Fdc-+o0d?eVK~v@+GxHL$ zUSuI4&Xgdf%-}~OS|c(I*24E1o{&Dp=V1J;I)i&3j3>K#ti0RGvp*z$OMpH&3w`wq zZVyZ=WBCfJ6yI~vKoF~F9C8q|h7`i(VjK?qd^rWDx<{ArSr$A(YqFN|?>prfhPjYo zfDO(Wz#C@&ciykOk@0_6wmKOJ=vi3&!<_xEmn-*AOSU;HXIl-8u;Bs9c3Vj4kQ>N7 z=$QaR5J;AGnKRWwHRBNnjc2uIKS=oN%V>s7j5LY|F_x7px6V7Q!=b4(4WKqv2}A>S zAy`UCWd+3>s-8PgTF~96!PHxSK1YK)!Jn(Nou;G?bb;aKc1!gGsAui4v@}%56DZgnBg54@;U);*5)L6jPoLxEenQtD9?5 zVCPjwG*>#gPT@ify(aX1D+YD3kpYVY(6K!5+(oEMWpt<_)5<4RdJyPXfZz_BxiN_b;tAOOqNj6kXZL5 zF-EqGb}&lMxX*B8aR+^sB6!h`irf=5WLH{x20shu`6V#G^?;xL0tb39F!v=<7kt?z zbVE6PU1X+ES$Sm6Z3Bw>UL{?eTHhUqc__99mmo1dfIkcO{2JhaKX123c570^9C_`H zIjHgaH@?#ki%w1#P^WZ&0|9aV?;qISLC?X+(#Q&68YJXm1MubjJ2)w7DXb|Ya4)e8 zlZi`?o1`+?rq(w^_DqnP*{lNxHijCkVjfSk>d{1nP`MIeh~7!8@=hO(BQq)($31;! zbUM3LMnw9b)c2V>JY8OHaJxTN-XCpww}aV0@SdU%b>K(rbgJW$J>&$b;Y44zL=7d+ z$xb5K|p_8lw0v0oZrD^wVxT`jz#^d2Z> zH=e%F=R2(LqFXRf^W151KP;p^3X8y^QU#@f8ZZ13!SU=)ar9j#kbxu;Pa+(fcrCnF zR&w(-9H~fsY&}sE=Vcl6Lraor#$_6V%s)zYLR-87dVOIzEBGk208Kip-c3eoq^4>6 zK=;NwQp)phu)~%9TBRCbrg4^P8uO+1I;{`S1r#rI3M>IS8_aq z)hN~6CH>YR5qB{|X*E|iY9uj(&b<(TkvP>IJdB>)o$pKe+|7rH;Ty}C&BVog4MPk& z1)ESqKxf-J8(3&$Ehe))m^ydzTiof|t|Y2Lcbn70dK*Gf)quWX1xDVzQ0J$AuCJ2O zMM<_sb*ZD2?rlHw)!VE>B{5)gf@VN_eux}p5S5p0DgXQW`&1hmIuE&-k$vz@dr~S) z@2{VK9JY%3-6hf)=)(`AY6;?%^M)Aw((zWwx6Jc9&85d#0$y4w#`jD234-T zDit=f4~UHt7-G2thNW?S#!cY-d<`ljlh<>JDC>D32(k&4@%x!g3)3gk3>MI~7J#&K zX#gyIErz~SL$>V%nKe6@!%MSGYpndeDlhQ-Dx@AE9L1||?np7W1r=Ol+G}7PaThBX zUHp_cLx9msgWUK4u~b%&BYz=^AE8D#Bnh zhl6iKVYrAHevM1TpEXV>mlKUX4-KH0l0`AkLV zSm{6!iHgXYvN%h`B_>31Bwqx11&K0)9YIlMho2k408=2nxqQYGc_2`#pTWjPupZ6G zn8cm{9)fe~Fq<%UjXYXc$o;eOuNf76^GG;)##0eqTNK;ifAZz4Qv7lf+?1Ok7l+s)OUoCKVjmDFkDRL1?`p@O0|h~sGSKi zyDdNdgh254MDvfUSBe*iiUM45>*^RyCS6QE-lnE>fWVJcsw1SJQ7qJ&!wbP6urN0m zZFQ!ok>Dk{49Q3pFq&RrraI_Sn@LOJLMfF92nIk{#S?$Xoex6F^h*{KIXyMQ@Ra7H zhtf%DBlTd3;ag*9H$u}pz+pse*ifM>bC>i7J2+A0;`71{+x0h~zjImv_3A5F|tLn z>U2_51mF*BDvOv7G0wDNgYcMm;)+>@aD4%~@!Pa5}% zj>kow*(~pmS56-=kE~vzO}RcP=Ji?nK<9<>@}0&g(9O9U9v<1gV9caA)y3*u0~EZI zXLM)BV^xLVtYS5xK5p1E;G4%*K&qxu(zGsWUUDqYK6bueN$7UZ*7dEs^$9o6k%H07a-YB{$;9^`)5NG%rAEX8Ld zvTgnd23&b?3S2{c2^FD2B~t0kxi(uy&fq%|w#IY;NYdsTYY|{=NwhY5>`wp8(!3Gg z*xj_1sYx9>_i?$M$waS~98BC|*B>E)lch-lSdxbX$td ztL@sQ@H)D#nJsKAS<+^oj>HS%_y{@UZr7nZI)}YZh}QYdMn}mlDd9mw`b9mymnA!N$>Dz>Z+k2kHULf&MnOwy{FQPpc?){_f_o z^(2eAr-CXr-i7NF-zgMl6lUKN{3~@`ni#JbxrzDF7%GEC3>N#A#1qv<%a_^{4r-^f zen=MscNrECSRM4@JqcJHjAcOqp>>99=1%%+>Q1C)9)1eoy00}NkkI=v8XNomMu7aa z%9&OM+c#W;fk(}7r7)wua2}^!Roe`lO@7CRec`m+V>t{i8hAPtkSX~X!LbUc_?qsg9RVIhAkeu5L-Bm zU&OL@(RhPG%ylP8l-_2A6;*rn$L5B<2P_k+-$2BTa=Al!+TRu|Thp=Rjb1>`E|8+T zN8GsI^-E*#j=$G+u5x{;WH%x!KrhNa?(=#UY)*kGi6n$*jZ5AB@iiv0&EPL=+VC#v zU-0Eh_P!oFhRBspU7PDCxOB6~8v;f2qt6kBFm=Zjl$HOh$u?N|NSc6aXzO25;b^Jr zY6Vab1OkwO?cc8C#jKpHe;fVxQAwiwKRPwQ9k5ngHPt=vu2u;e*+TQovp&6J;qp`9P^7$_6{4Bj@NSzA#yEhkAHNgo@3nRK2^dAYeea|V*B#w6&D z#6Y&03)T2$72NE90JH$)jA8tgM$lj#cyY=d)DXmy(fo(p%pjheSQ7K;Ayaf$s_$)A zG|gWRV8f%^H6kUV8YG@K1i>ZUeA<7d!9COHpyfhNKE~1KcF`+FdRK1A_*_MvoU#Dcr)J4H)Zt9Pwx$w1#c;rGgd#no%FgD=2ug;$_407*al+z(a60%!6Lh#anUeS zzvE>Z-~OnibMYs78Sv>iK%uZEujj7pr_Qt=icZEi33Q6Is(t}0d%J{{yyhvmWZiZX zj6iwB*%KX+OT`+7qE>SSBp0Oii_Xuhp)8%`dr9lQtn9r#Yq_;iTocb_5Ur2$U9p7 zG6tA;+W#|w6ew;0#%jo48$^ZzsySeOgNxDP4fx*($SCmqWcf-VE2KcnJk@%^l4#n- zjRkrj@HppgdeBU;IXmyxG0cP@9#$slaJJHI2O&N<=eIUIhD8)h;oYZNj@~cXk3J_? z-ukX|yrHwHM z>>xc%AJf`AdB#R8ThC0{$_*G``=cdY1%OM5HsZA;EBcN@Dsjq;I~bWtu{A8z>7?7B zHmXqTiE>OV;Z=t7gZqO(B{j0Pp+8)i$ZSeZf4JtvO~!NWSPsXH)~>}Xk&OSfv_voN}k?Rt>d8C3yYF&?>m)t0o*2tkT2Sz zw3x5RjD5RYwDKN3-HxSb$vqimUPbw~kqcNe5SeHwKD)n#k z#;6<;Y1DGrZ^X?7ncbL`O2zWU%Tb9hrKxK?sTyeP9&^qz2itr2tv$0rkh9Cqt1qy< z#MOcMl9oW&1Z8cna|^vNQRB%4k*&o);m4KWfW~cbekXBvLTEi3au50qf#;+j5|IS_%4yn4rl*bhDbkSenvUwH~_nSO33<*QAAW%hM} z#iZF5Z9~!kE%oz-82AYx=lf-@+|RX+@Z#hcA{)@PYC- z(=^gxbX^}q(QD5{W!>g)UA^Nl%=44*fqOgR0onzL0x)K_7p5_y6u2eeE77bTe_o-A zuJ{Fb2kCrKYcU0)?4X-$+G@K5aP68e$GEw=c`lr<1G{LS21%lIMF`K$7^&%Rw`4kp zhL1rpvWVK$PF04jqj1LR6qZ6)2o6y%nnGWalVwW0g~xGTACKL;KX^RhPu@abY0vMg zL|?eSVzkl?qvO_tlP_ReXZDung0=HkhPck;Hh~OOY&DqIus}Ll z%Z;-cR1n`I&_38JCuLV=MJlqA5R9O^{=B=oy~zA4@RX=(vrhv$qbq={B+I{@rvSk7 zPYa&EkS9YCkfr1hKBb*c8jV^|1m((pl3BJ;{Ve{T>xUo(MZ!-vQ)J0*32)&%q7Bu4 zd_@+RU2FD(1bfmC+3v*q;Ooiv3xZ9w9S?D z^@af>f7<1f)+pT;x4nv#rCF67HJ*=3<($I(TCq8mwUcR@q&C?4cjgh;Rw<{-83c}v z7vri#)K->rrqE%Iynv0>Y4g1OZHZ*Ovbue@mKJmn!occ;P7?nRYdz(H z^FVSxty2{GDe?n0*jP|~JxlhdndVRI{9v0T?!Eg&3if&_Ub~Ie!@1;ypoM z71^_jsV;*;1Ev2p9rS&j&5UQ;N<8yt?W;{1fq~*9+$)GI(gI0byX@S27$lqyq z6%qGV1V9}p15m9b`Ii{6)N?Q}1>|4a|Bzgj`s=TMX5)$^Z-pPf(1*GxhrjJa*E0!7 zR3OuL)NX|(#HI!@NA64R=|EW7z^yYo2QzjCY({=4DKK{UXe-Xu4gFfT&8F)azr5JI z^LTtazdP@};^p=F)*RhIMx)0Z#+)x$N0VA_nkjMvj3w7DDBN2{*1McgO{|xOpCHe? zAkGN-0~!LRC&H^ABdE4?a7&re)oMS%BgI&_*6kNL9$65@;H2R+MF~ltg)(c{t!*No zolp%Fu~qWzB?RUrdmb}*Nq#aXY84$3(u%=>VC-0!a>CX@v}=#LMl~XFfjnKa*+^8D z&7P6=PAV~Sy$gLri&2JGr3Cc8-91Nb6)N*UDJE0A)Pb@F;t43Ayw5bRB{>KJDjMdH z(g`{U=-O|Lh2e;0V{CNt9uxCs4}V;RX_xw$(e4&XcWxNL(4Ne8PGF)(DsPAVaFf@{ zUSC(YL=w}7t3VJxdTGb7KM4tmYwfR_eCs_YI#%Q~^M2JX_zcM^{o926^6E;IVwaeJs921B2`iasbOV4S?^IYoEAfgF@5Epd^I(VF z?6vMu&2%@384{`|EB^if4~H|-B}=tLK!L!H?g2@;!{NwNE3|v`r^f<5C8(=UFq~n0 z|M#?EnE+n-9l`e3#xy=e`Ljh*>MqO05c6cJ8L@uYm0x3udMCCJ6!jGN2PqO*_mPXU{ugg|%!@j}^J4uY4f3lZq7Ee#!Ix?6gG-8^9V zuw*LA4tiRiu5#Jv?&bK4m@b>HUR&0qQ;2YtC{Tj-@q712yTn{Opts{h2qt&wCNKHW zPxXnC1r>BE_Wj)5lMSVUck+6HRG!RZE3>9A$Luc_-Q$uKBB;$SN>J2FArd5mb>b{s zfa^;eq`Gz$nge5kM=!7B%BL#*U_)seaQQjUgGWR{i*dW}Q>Hp4lb(b+4g~(>mTRt(ZXsrN+ibw*}AZ)Kbk-ix$+dgHGV$8BwGVFdsci@^sW@RldKB%y7`y+q> z7P>$%6+$a4yTB@IYkG$M(J+VqhGKDq>XX`5;VN7FewHp)EgjBT5O+iX3yX#YvANl= zjE2HSbG*+{ZXhYLF?{IzF_1~Y)b>YNh~7&tw&>1E(%ddxxh8fvg-8)r=UOydpegg` z5laH*s+-9-?b#cOT-#<0Q|a>Aw(1ioP2rI{hhIRaB_xGK6f**;dCGwz#lM+Or`K~k z51UAIKVL!ZKLD}^fOtpxkuhMuB4hx#cKO4Qjbw9brC3YTLt6ytK8*!BiGYq{x4p@|N6LO8KDo2 z0D$cUI2|DR7r_3X9@jtNI#JP5VU7>MtCCuZJeJ^lCnbeAnW&(mqPb^@kLe7&gU$6C zqBN!?(qUZoJ7tF4jnN>W&9+;YFsEi#7=#z2X`14BJKwzgcz?Kn@WE&w&cmz2wAzpD zwV;jKHpXqDTMAxl&fBrV#k@Q)Yg-R~bIIPxd3DXt$=#tBP!>HA%hJnkop5f!0Bprr;Bh-mbmHa+n}yf{z~c)Dj_56no_}PU}fW>NyF$u;Wn9>CItcFZ8o1b*TtJcfmHY z$(JGW{0#dI4k68gO@ciyO0)?{s`ZKMIsViv71ta=HW8BEd)x_eS0&%3<+^=>z;MD< zXRif^7&7ILoUGStFu>`K$0{ie2X;DXJy7;jq6kQs6&=gBU$R0VuoQ+c(v8@bxPh6y zNOh&8!YW=bCw1a4!1-I(@grt;(_m8-hBP5Fx;Jh637T*)-xn?D@5yTzR87x8nH&X# zk34f;i@&yU8>lA;C)9djq}ugejImjcz8Kw2+MK{Ps7skKqA(yke4ri39|ba`#Br!R zqi1!X{Eu^(9lv!1n-xVcRrvVs@~GZ$%AdW7dh;I&1mNa0wFdi24C}%SnH3DVq<4$G z8ry_5%OfsP*OF;eJ|m{k9-r*f9iL{J?hP}uON($h5Zud3FeP%6;2jiDHjY#c7hc!K z&k$w(M%)vPuw*D*U{8;}kC3O<_oS&!s2F|Vtbf?upXag5n!&0mTvI(N)_)>coI&5x z$LSU-6-@s^r$KrZr);c^Y_b5q7(mI&=1vVP1kdUWtK-8sz}T!pF5(d|xgs2*LMg5y zH;NGlP8EWcP%I{ONFSpXV_(h4y;of3m(cvCLNCRoq7J6aX?%8EHN%KhQJIHKETQ7E zANn_rtbp|${R)r+gaFfS^8YDJ|6{IgWcXi`?f*&x|7bS=Rt9y7NsSU?T7olnbJ{Ur zSHnUr$Ogba=D?A*8rO2PYBwyMnl!cNLA|4BN&I>s_Ud>?pC_CGfr+>KCda+(I{`x& z55}&q_h(35sH)x&eNCn(LRq>QaON;E$g@B^d>qnCL6sHi+7W8r{mJrJGO+%b}=|~(+g+WDH3ao?@h3` zhuKM&t7WP>jQKSN%Y&XYgd@SYuzjt?+@*5BuV({PCq+CCoo44WG4m3JFL}jjau6__ zCorboYStE{q`s%ULNn@8sS92QwRV08Kfeu*nDPAoEbx3wGF0C zq1v76M@%&~OrhFCC*X`^31zhhr*w=mnTmghCrt;VH7Zt_!0cQ>lc7i8`b$p7p+`L~ zxA2vgf*^OPRLKhE&SUBBLD2+uX{ip&?<@^w&P4IBxkW7)nZ_t0q1m~8 zBh&D-{U;Rb8kIk`CIrzjvHbBv90!LWH_*I-tt^qC4r%K#h4Y*#<`4Da=psf4-IE&- z(UU9F7Yg|q0ZYnnzMl8;WzTzTuQnjN)acz4K8Msf#Ha3zS~-N8@z*r*pWr9oE10{$go=J2YBfi~k@L=kL3 z7AQw2$KSDS!&xO>+i2w+_$17tw+kh@Eez!ybt$YkPzDZ#=d`EU0(gVek>0&cJ>dZr z-ZY}~hEa+ZqYnjyi5)`~B1@K;3NZx)+j-~j(r1dEm4qQ=lDkxHg9+R% zQqV#N#U`8`++V~QKyFa7!##O)Y#X;!6b0hD_;Tt83ySN&JM3)E6WJk?)qcIgWQg<|1o6 z^&H3G>~GDkK974^r66RoGn#Ygq*yj&B*S(yK?Du2p{e(DFnEMydcd5|A$D5cSS8qn z5&P<8jtt^DF$8f>UI*Z~+cXyx1HJbV@YyGSn?PgsO|+gMr|CF!Sbw2YsvKekcQ+(>UbR*ROB1xr;Z0j^;_=Md#cs6*+LS&;t z33_xbArMLkyp~0Y2I{`Rq!CnjX+5{xLTu`AhmI6C!O|_WBs2N-U{m{`qhU6xZ;c@dQ<`mEDmI7ooMA z6374j*|Nr>khoUuah4h;N{=EuJ<_IFgQX73=!BJMQfNYRK6TRzqY+6Vdd+fJKQ=&WYHA9aD&Ji-HkQHQ z4ODM`BUMFljD>YYRpbHtOmqik5=t_2II-8Yojit{-j>4+%_yP*lt*R<+Jf|&ERgo) zT?tBOFAdpPw-7MWmDs`5*#PApo*(g00qq`^*Uk4#oGsjhlhs*=H@6rSrzLr+?y%9( zvM3Cc*lbe9o3&)49RdmSUlXv9NTrqB1z*EY&a3yH9s7=oIzC)b$ z66r!`Wy%;j2Ez|pipjq^LoXU;57W#?_Xf`*Z1fE11ZhQe@s9#=0I!^7dvHz8DBz63 zJVe|re8$;Ti5z_qIL_OH?YGT9x7r16hIV8j2?@3(y-^Oi_qpBWdc^)%Gd}zd0s))P zaTko)Q*ILY-Ge#s1mWSw=He-AyFXJ6v^E{G-aFoL<`BvF*3Zvgaz+7t@1Ek3r>U4> zY<;?fts*fI<{5?`_u@>F)?#SYrC<>YdYHf@F*8ouR$RCu##rD}W*Ew3gfb?TGN~yh zqPV@LKQP_4dB8`)N#tsOCG>N3OgH?n-QNJ8QEF`sdgAA`ZupVGAy~>z@0e>z;D-Fm z0p|({rmv&gnz4S0&eclV8Ih)>a@uc+L+Ap|c3)WF8R9;#g37Q}q;u#rk}e-h?>W0i z(K;aO6HUU&Fh<>!zJ6$S3gZ6mzR7vjLZa$%k5CGsZ|Kzpy6W^WWJXjnE0~G<^KV<8 zbByM~5P%H+9RNmT|056qW_nh77V<{+){b@tM*ms%M#^hh|ByrAMkAA0D$s2ZU!VX} zgev%rClyHx8%a_eYWeE*XR~(~07DWQSs(N~tToIxzqUT)2JO*yO<*kSOb#d0GuWMt zJ3YVN!Ma)0!|7~f!fQjZ7p2N`aw|$3&A~6RTz zh;0SW^ro~1$@T%Mq(p%SIvF$bnfqko-UoLkeR5h z`zuO#lhgO}4UmSkqv+>6jrBBQL9v{UDW{k$y#Nqy7^veBh;)LSM6 zsw2`jb01Hz&*~+GVTrcj|CQ@FD1jB<1}qvN{r`wGmw$as z|4eECF<&bH+rfRtMxV?lA<=@2V5v|_lvw>5;)fu=0F@6UuP(G}n_-0nv4&N#RL^xM zaIFYQ^dDPnR#Y@Ja=XDPH?QL>j6-*OM@v^gkJWp5VGy{uuQt^?dUJhA!QcWHY6-%x z>;eTVr;N>Sw}Nz3j(LjhBk{7u2DpO`wX=yJxKAx%WZ(J+RkB|h^f`cjdKe_Ijp3u8xy^TPxhPcKA zjot7VEymA`RB6ggRB4^1f-et*&rb2)Z$wAxP72#CCbwe2K84Dzta=NSNFdCrv_|*8 z4V2xZ$`*>rS?c(B=Jfi?)EL8KH4E49fXQ}BzmQnzmY7~5rbw)OfgfqPKLICB5eMOn zO=sppfhz!Z0P7Z@luP;)o2rAL@U7$7hHkHbQ60sW=eh!F{2rz0*R;Z(yrx*=cL%)5 zu*=t6@S@8Jj1y<>{|v`3tUx4?S>8r$WDP z6>>RQ)p<)vD3=ud{g;$U>IqDZ6D2f+4Ovdj&mW;pF0SFUTWuS(DnJSj2L1FL0iw!@ z9Eg*7wTn4KL!dM9qyxL=2u7oiysCYx!~WW#z5=J=Cs8 ziT8N#1GKH$wQO4zB#*lcp{rbGpTGCX1n;W1L-uaJ)k*yXHRZ?839MEN$;8S2_}e-1 zpWGf94*=(P2_O@Q|A+rk(a6%q+D^~TRoL3X@IMdwB9$~0)`Zcy&(bV})?ut91pLML z&9VILDD&|r3Lud&_awg$zuVVfV`E?ZWb!XSY z9{E9=F$>RMnjYATP5oI`gO!?7#Rn^=o%LzgPv^-!%7YIslVsMwX*z3hG>$@-e9k=M zZ#Ep;D%@*zJ025%Qgr4Il(t+lXyBxPCCYco#}u9AG`i+%sB>8k99439Rs_{+3|U+! z#kx${T+t)`a1~-_mM{9qABNc_uRzu?LN^u!;6WpinzzsDx@zm{wb%i3Hxl8VP|0xr zwDqKUOY2x#!KCQG#(^ZSe8nRhznfw@${^S3i<7|%Rq;!m6j59dg?kXQSt}pEUNl7f zI7A8cQ;5W5F9;?aO2s0l{RX0|H`oJNcWSiIlk%g(KJzEZ7Mr|63CRS8x{!+AEuRcy!;g@6^`Jyuvw=dcAVc3Q@5WL znxpsRZ^-AFh3M}6kjkPs8!L$LGG!9$!mMsd6uSkeKABl`W!?86zP z|2aQs_>3H6l>o`5ez=Yv97^8Ny+@t;u z_%@BKK@NlO{Qd5tgPZ!9qy^8eLlY81ngH=%1=m-P8$loWH3KwRx*rbDb`7TLE0+8@ zXA6RFZMP)l8v=*|IzFLHipP&McyVd(G2Gak=i=WvLlZ#YOJl$E+O)E~d2&Lh$s9+u zb|Lmn97o5Ol!|UpFuUZ!@xMH{3aBsRvY@_o8AN3imMV+^V8AGp81q|I`5tIPQuk^` z67gE7TdN=rNfzNV8BvlJzA3&Xk^EA=-kz1iGvY}<2z zm!di|9m2rUYPe=`si?zo7nmtvbr^ll%eeWCP%g{9SwHAHr$1fAqqw^=dS zgbvS>g20sXvnEq7hZ7v8Q`V11)mMDq5^G~c(6h~Ml@kQv!ATuP`sYha6(kZynTwg5 zXH#VhsAJbsY`XID+qy~U6&?a`Et^j`;!v4+;NQ3VH7eSE#Qq6p z!KC!Nv7B5a!u%b4ONdpN--dix0w($?@2X;ET}tmkN7TLe6Bj z%3ezmtCcUG+kWbHYF)3O)EivUns~3`t=X2rXm|gql{yav#owd8z?-6l85|z}Ct2ZW zT$-Wz{g0|=+#WzyNxoP&$`6VJh8q$J3SQpspsP`J?@|%vv5kK$)p*dh1#8u~dyL>g z$CW(`9_h1Eug&MwkyXDxm&$MrTMzE>$8;Ob2K!hv#?L1b!!0`84+5)m#ta_S?~}@g z^&08!T>ESSbm&vl+;2x!7L@Z~Gy6?q+c7VysgVyDU81x6f-(cw*3ybepEw|N0fk%} ze`c%sgw~|L*qW-b=(&z@4dkW(Lz{ z_u1Zs7@%8$e34t?7vG=U!SqtY$Mj?|arE7U?iiNeADU(w!X2-p~|k(=A_BOo7G0ABPYbZ zbLXTpAUHH7NFRxly>Uu6PyVeW;76o204tgK$T6YV?$0L78AY&~q@Ei>ohEDckQ0c6 z5z)!l=SDrg$8d>w;INf5LpfV2mkUP2+C!FkU!$cA;U7F>Sb`eN;@nmtK11%%0Q96_XrV~u%SWQ zIEJE3d$=5~{W2Z;N6!Il8?uoxe7UYhTNJ(>?UDLi(``iFV>c+7=zh{OblJ>FoB4h&1EN7P~Lufyo+{;st3xW2k&Q*(!0g@--#S zgY4?N>@~yv#~;yC&SHnvS-}jHeXB_r!C(?<@;b{U2NW`)ILAukmE>XC_PZyC)sbv3 zaYM@b`hG+DtIX$k48D1NIBw6p4~@t zhcTk!i+hvj<8I;q)C9NJLwHb751%PFu1NiWR<#dcygwyfj3v9`v?+q%W@Xk=eeBa) z63h6indrF>ve}ajcN6`3R|!=EuVq=q5whB@S~302c=}<*Q;F$uIW6jy89t&vH^A2e zzfyOTx~2QyktOhG(K`e<$#ek4DDK}y*1z;b|G9^0TLS3aJPB6yE>Y&z3mRu?l?HXF z5H}3F5eTJ)XjV}7_VLjt11l?xT$(pO-}#_vz`y{u+>#yjKZ!(v-BV+@94|RcFV}Or zyFR{M!^RNK+j%0wv>@-=1X5lGwcr~t4S)n+Z&b}{7{gkH?Ndz_ldKZaBR}JVA@wF&-eWqEnD>jIe>IX$Up>32-_8Co4 z6cDx{GirTIau>CeXw>&|64MxEken~vh5n0Y*raK6$S!HOP9k4)uNw@G9m{R3D)c9~ zSq-Xrtd=n6Tfs&Oc2=Lih|Pd>y`q-@0gwy`fFFMo0IJ3g|C}lEpQABKQ324+MEGo? z;bDi@5GXQGk(8q-x!I4?iwx#BP<#NIeY&V^(rj*?+|-UU{S_Ccw;RCc3-ILTp&tmD z)2U+u3|XD>db`m2e7!;Lf%gYfzL`lEqs6gOpL6&#P#n+%lB4f4mb7&grf25bh#6Qw zoosGa$Bsq{HT)`EdQE=t8zX{!!?3hiDD9aJWARH;jS$MSZzM($BjZ64=`(!CxrE(1 z*k_F=c#PK%wy`R5@Xcx+{V??jm4Hec*M4D7boGNpz4jfWXS=hJ|sie9=YS{n7ayVvYl$D}tCL86#c?@`-#7 zyW6fl4DNSYG@c2=-hhzc5Ejd(KR-UgPv{#yso?s{tZ^yvzW43r{V7p-TY&l@m;W;` zIfUeP9OWXOE}OWYQ1q^o0R?$quG@bSqDVV4s%8!}rYJ z)-1&oY)ih4KM1G2XI-ZEpks@))>7&GtKJUt>DKYACC5;PyN6PQ_M?*U>#spEYI6HA z3J8iaKv4W$efyuF_@8M(3j3dFq0TvKq3oet(XugC_;lA%7*qkl0EW_!S9X^<&55t? zyrOCPlfWB^@)_h^VK+-clBF#+D2vPWaNTh_Gwm&;+x1)1^`HknqF$fxVV>8f3bW?2j6n zmjkAa9w|AFeA^0ZUz_LQ$yF+%xnzSf*|FD+aXo`GtcD|pc#9$;>E%r`L$&Bw=1=SF zLN0D^-J1C&#yBLji{yM^N()F3v(KD2)7>&c3jPsn`wq#YXPU5jw*rSSgeRn}4A#52q$azUMLh@ZB)?-eG;BM7~V5ySxR>j~j zCaj>Z&bva)C**_DCU<$l9O=b&9g=_bKlV%M>*hcjf~>l~;*Oy~wwm2YJ)4E4S@UlZ z{rAxzsn`FG1}Hkp09YNtUowM@jj;orkgc_yGXSsqPija~lCcHAblw?-_IvCyW>UEg zK+qI2nexqI^JNhE%7V~1cJnY~WIs7HIb8N*fWM#%(TL&SzxkjTEXq>oVHJgJq>3E?>BJS!aq=LBjDl zXkS7M4Pe_3yJ1)BkPZk1d$FSlc<1OdIS(AWC77d-HV_G555sN@;cWUVWR$?R!dufP zdiEXI=8yWmd0TEH)UDnHCp%ia2v(Wu$(oFQ1HVX8K4n)$uoJXkGgact?O3=BA;<~t za|8jC2?luL?vfzEUWi^za5lpZJ6ID9!DpCH691Z|3gm_~6}u{wWmNu}`rNxO^MCj? zD)w4C&lGf_p}&tjC|*R!aaX4xLw6hUE<6HRRNnVg2(4|sgv+V(L+g#@`|zf?UsYaE zQ!ubH9CR`Z%8gb(9<0YF$J0nn{Y*b&g*N&O{@dnT;bRnbBWAKSUV0p4*rHtX;@8{J zMrAhjTI`2vE<4nK9xc`}n*DX^r*ZbDd9h-gD@<%rL^5O)A#)wctxoJ@Jk4396n&?6 zSmY<_$~1$0&>^=oM@ljaM~jJJ}+YI zhfo#TxC-*Fw5-Qdt-#xNpOE+wQun32rriOH!bkc?JcS|$TWq-+$!y7rB6Ya#zW`sC zNfRG#fS_^$0AKk3ub}!@YAFKfmsbJEFLn=jiq?;2QfBMbLqXr6q9<1J?;>F+P>qTL z%B6C_>H~4Lade&DDsPH~gbyIE3M!`75aND`#K|3JSy@b`hvyZ)zFuBn`0=WV2V&7` ziS)y#F%f@4A)ZS@3MF47xFg131p1qpP4NnmcPbe}qE8wo={FT(o?K zm2;r9#N*vI9H12yk6(2xSgOs%Kt1F+m3b)}X)~ldD5zO^+Jc6BCjhU#?8j&#i;Kw~VfW zM`Zm0CgiS|EM<=EsJwQwj)6oJsDY7G7fJ@ZP=0vVonKUUq5=0Hzjwsoy=dOfEs29< zLthbjAr^eFQ|Nyw9VKGDkJ*F3x4W3X#h!Uefu9}bhBpk;VU&8NcH87r#)=bs&JGtn zBK#!wR(4&?QA3>&pK%3InyG$B)_nvYoOG^R10{0!%dd~63cr_)447%+xjm#9^&6qj+shJR za>oa8vVB5}n#YR$IOR`80l(&&!v%*7Ro4!2?f}Z4Zqfu9Z+^$!6`CLwf-x$`wa~=7 zosJdj4!?YziS#xKUB};6hd|OvRwLa0T>pjsJFTWr5ef*_2Eg;z>(@Usp|G)|p@X@> z{~B4t^$HOQ@ei@zg)Du=l_WA;a=8j7Ig65t^47oFge@X+q|q0oXz0fdEnx^OTwJ z&(&g*llb^TskVVa^9&~f>pT{T8aNceSrQtF1Sh%^A6YrM6#Q3w=FX={GyGV&>Cx{V zcZZ$rUQbx{n4x*`;|0)z{Z5c-ck(o1HyN?l;cquHJq&Z+5>D_h&R)uZ)nZ znv(?XOoKQjtL^p<`!NXou3;P5+J197**(lnw`AQM$358Y2{(oa4!o(`Gr-I9+MAAc zoACRzr`>1(`to@Ml2$+-?hXUg(sxUb2FqWhsBCTb(v(G~+L{Ya!ZC7K_B0Dbd9>W@ zSewXLILn)SjLcZ^^7Xbq(B!0#EhGi+u6+)CXOkFq!>W9=)!^ZV=fEizB8Lhn21ky| z0%cqnJC|J6#8~&eHTO|3YnNU}#<C_wMLAAb5WAaaOBD$e1i4gByz?}M3;N0W6FhwHiX76 z|30?&X-3PUK&+Ibh%8?T#8I6SMKowcxZxL+DDm8kN6?B3ba<`_3^KkX&l*&~NuLm% zRi<3W-ZqR%(m#A?27T=DnpOHU*5nRcWoG8hkr~d(0-n_xn`Bo&6B|xi=DF9fM3(pV zwf7`TScDjX{B28EUtm#0rJz(W;~m$9mHTa^|C9?C66EA!yqndQr>gbf_?9}$%ua~_PR$t&HLjz`#6 zZ+|LCp4zG7NdK~RyE_bjd>e}pJa(Nib7(jYR@WLx0HTS@Maf|Itg2hk#01$A-lxc{ zUKq;P7W!w$(&G>u}4-@Nq^|q-^2jw1%k2@}3N}it9 zkZds0-p|Yf+W}Hc2z$U}tL+qi8Qd?&Bd;fY(jQ*4}qU z#fne+b3(kAC2&rcx54F|A$;R^s9(u9-#!IHxbhnrlIGrT#HO1HvBD^RyKz$Nky=qo znlzEAWQZxvZq~68AsPul>Fm z4%3o1&dP4ecS+{{E{G|Gfcun3j@gx>TdK=K47!R{ahcqd?dl4YP0X<%nJkQ-x$O3U z4-9sOXrqWS5nQ`~?=8k})GF$8j0)0g$!|+e+Yi94oAgYJ!VMQV$!ByCYuGO|gS4!Q zF42m!vZ1y(We~wHT2qn{QO2&cQtESfHQdYlvZrluc$uk4I+2_PuaZLOG}8&1yq6`R zT>LG0(u(oj0A^DH>Epf$Mm8Qe88E^ESM0)FIf{kdWXJ21Tg%XIo4TwdC{NR@A|PPM zrKJ`crWOn2zG*~xSam1RG+cg4>ayaAesPX-v(=DPSGB$;#W%8C$dc`QsBaRz7+NY7V)*8J_+VdeW%6mhj zA^ypZE84-cd5TKZg`G$WxV{u7bJ)N3=X_3YAOAHh7I2p7j2ow`RzxUnk4;}g#HYbl z6zRGU^@>vL5(y;C)b%+$Rkc(_hva$@O||y@K9AZq2lM`m9(Vy7=w2PHq#NB3^?|1N zs`LkJx<8ZIW>Uy8Dwl+$K^ghSPj#Jj2__fW=7^+yL3$XeUJO>f3GPi9%P0AJ(o&mo zaT-4jCmfq2D0W;jsYqN3dew{wE8D3N@bS1mY5sa=iSjNk&0&o6G)I~ds*t7CSj8=FL-Vaw^9yyXb5z3Egbg}5(f>hcL>U+ zLTaNc>Yu*3tBbTF-d^$(p^k!PsVDK<65cA1TnQ-;AdTx&MP9UFQd31;D6B0Xb2i_9 zPheKE(!K_3iS9pO$1KDUw5n86@9=@1#IY)Yd=CCy$7F`k>K<7mr(iVv`;A+Nf5Vw0 zx}yIr_D8j|x`6a5acM?f9<4i#@pwYh`~oz5%a~*zlUYXZEnqZ~bw;4Xy5)&2?J0yZ z*S#gR#qZ**lYF1p#sVU1g-=+Xm(=#3eDm(L)AkAVD}aHC6_-{nHp}N0&RK)dP|8}i z8G_a|3oHRiDcc!7Jrr4Pcj1Y)d?r$tH~2N8d8dPMKTi8 zU3kSU@%y}6xkQk-%?cg0S#Z3dXw5mnancP?V;GNmkEXmN&d% zX&Y1@kvGN~@kPdj&YCsgw1+C2e6bPW6s>%4SZvs^G^w5r;`S%L_n>$QjCo&!u7j2{ z;b*KIRmJwQLBDEQPRSs9NBSrm9U8JuWDTazGJi}4x2n81T2h42Sjm7bTlmZNvC!AFcm~xh{XSKjyoXU&n&+2VcO#P}o$;CTW9;PN@ zgZb9uaARDPU2Q@cWBP}5lWl*KEgKCrPVN9QYYVsGHrDR>`s*)s+prapY9^p=dj`~P zKmP90^iM&jkgdbN8;+Ig?p`PhxL;Ey2`kd1P;j7H0_yPg^#s(p!t1_h{uE-N%wYmG z8X+4Jj4dls_4PojtE`J?CjypowRqy+8=BC{X~TX)T3-lQUbQziK6$V1b+2A!q-~s& zCV;-3;IFvfZC-hIcRz7$?XUmJ=7!T_`K;Wg#EG{L0mg?{A@uEM+$_t*2|?-VkQrJV zobz!H@n>OI407Go-hGh0xwE->#lyT(p@#_pcFS?=((Q$?OToZZv18*U-@^mD107*p z*9>~I>y9)8;{LJM;{Sc8pOH6T50RI0FVKI`P5v7n@t&dJy*}nr*i%eIik$KeqL<=( zCQ2K3*|w6KbOkT1s9dOm><|T5IXyr!JA#A6Cl@{!Me)p>jZ`paaNmHDg{V0d{zqu{ zs3LNjeEA3L?N6wrPF(ZxV-_bRK|{~g^N7pwVpu8*6D5j`$l~+C78A@9oY zQgvJTYe^{MonSnu&qXJ}6!|rq!u#m+9-WQz#Ya*OrKRj>%Tc^Z=7Dyuljhf`$Uv|J z%x3NJl*NYM34MIIm=sWs{Y%YPbHNQ}ic$|E?bLJP4~@&2=__K1D|q8b7LY9gZ*0X# zYWVcjP?I}^tVG5qCX!N|UfK{$$-Fa+U@{^@I>`v>P`-7=*`uVRXqjFOvYugr&}143 z8l(kEZ!|lj@s%fjz0@~S%yq5}%+dkaq3v(P6FK0@{y5Y1OEM<8y;9mPcJxcGo0N<=PE%pturV4Cumq2k4pt8;g(q&!Et#sXXlU;f z*5C#5>P+Dhnb2mCxPF%=eWdhmTPVoDT%&R9gVSN9APNm^#QU@Udn` zd%g78#{Brj)K`8FaeT2-nd45Z7fqO;v;3Nnc#$Hh6G_v_mQ9mT4`roeG^!v76)VB= zz~DO@LrR_KsQvqqS6OW-PKjy*2+rf--@QKrYA)53H1Fj2dE z=v3aJyOVFQQQNOI3F>tRudY2A+LEIB0~Y?U?6F_YISO*=0F1ho8Qi+@&wULE|Fx@2X@h(UQ4*=8I(bu|UGJKMX9}%$wZy2s;-)oika&{rP4ovG;z3!kL9zOfMMTSC zFEc&>WvpoWhiZ=`*X~O$b&>H0Hj1sp*pla;$54|LRxb563>hZ!*6HG@JtBpBR`y=}jKgg}~!Tc#% zHJY(P$4#I@Je^zg${xw}&%SmnGOrqv<_qU8?Cv@}r1OTsN=`@f{Wt`eHha=La*JCx zCk#fKFinQALaxaRhKMN+h$UJXeXO!a4H$yz}AvQyYNnST)&X9 zPS6@y>^18Z>&nwychIl6Mh4^gJcB>ez}G~EE{-eE44<#QZ%j1HkncgJqH=&yFoJJg zDv^5ve~NJ&^)I|v=7EOp4I^D@hqHG5p2F(@)_mrUtXw|a9c+=R_-#l|rYvFvddr^t zj02u7ACx4*PT5qjF{HocYVR>h=_X_diBQ5gn;K9@R7V(6RuDTb+jE5vy_iD z`F`HLKJ=XfqXnD`$dfpA8G;_WTA0igPZYiF1tn8E5$#n4-Ci|kUpP%hUZ1Lc6g>M3 za#!Pixo5#DY{}l+bw}R7Lpg^A_pZkt578P_4xk1x_) zYh&D(x38OBtvu8bmBd3q;y&wGh)>BDE}s5)!~xt0|8d8pjaBr7Sd3AjoFgN5ox>s- zF!}75nXPg_9JIc&W6(`OFn<@b>b>@J!^CP@zD88nylA=>_=zgO@r z61C9=v*u_?&l3~1ESlmdRJW1B^NV&?@ZjW3)UGU>w(_By3?S4pWRQi}H4|wpcC=0l ztrVo{4n#A^a*{pQ6?28L)ZFSj^2k?`fFle{ON?SbbE-q-YG1s9&Jzc$7aK>xry#vA z-gVQ2obvTA$vKsc{i={4>}FVx5OnqgtqXU~1$)QZBm-Y9zPE5mQA*-fVO0;FX|-r- zw=gAA968=$EYe@mZU`7^Eh4((d=}#@hLf?_Ozqr?GEC`bxyc`fLoOMT$>ZLw736-M zppu2D#Hgr?bvQ?kI)habHo&KX0g-C5To%vsG+cB-GEu4SU`U;VB=QbPx~(sA_X#PM z_#bB6cz6W5;#RmuSg{o73YV68yq}U|klC7w-6V&3@9N*+uTogiYzNxgWFXmm2)7Im zbUT#2O{eTwBTCogvK-5FIu73#LDs-@aHVU1HKq+3?eV28LlT^AM?`t~> zN^byP4?fA%5F}x7`}x%QT3WNMDKGEVCeXdTyeOV0IP>{g=}9RQ2GwO#!1A4Nk39aX zpCO%roaiH)Neq(XkUVz^s4jg2XPUwWLjdcwsec}0Vs=YZ3NqsQU6_=(^RZkj*P);C z5z2giD=cP@o(@&G-*~?~!tCo2;tfIsy~UG+%nITka+Qs9r=?@o%XjwFaH`+gOiIyv zV7m55?k6(Ze2LkPMER|JOn><2gn>kmIW=ITtPo)vE2JnFJmwB9U7bxOSOz|WsfTm3S94j7d=HTDZ&`c=oDeM^#WX&Zs6)r2f<-9DFS3v zH&8vM^tL;OGcp8o`T z|GgalYEERSov6*LqI`*fGa`rhqsXswHmQc_W7?Oa4vgg+Ks5NOH#AKe;UP>Arw#^P zR%)Mj`J8clpr51fBs#8MeH3_jY)R=g(SvwIyC=L|er$YfUSIwC^#Pary_p$Vxp{hbOpq)OM4t0*oirwEl1}8n%KlMXXN4$zwwZ|_n6?%?TB8H8F)83 zk?$cVgI&gvgU6~od_qSk-P&X`_2jA=P__i??EbT_3i=0Bc(sY(B#a@IF9}XXAfA4- z$(dA@P1?YW&gpJ{ku#g-_yb%aco-jR(R4=H*~$Q;@(#|?O7Snave?8KCCsui|6##K zHPn?#`zCBout~=f?hWHTfovoD{*lO9Ba*(FB;8vrlc%lK>@bJLxhR}^5?GYl6H>^LKghj$v=!>iLlVHcrZ*t>QRauHRjJB!Qiu?AEAata(D#T^{^Bys5V(uGrxlREZEBco|newj<+uaeDlMb#@)MrXh+-*^L|j)q%$@ zdnme*7t(OaGj+7#al{^0F$u9yaNY$Br_Ub6P-0mbT&Ki!O=TIT$OhfS%%|*2kIc}HuKui* zy$(%JB4a(9t6tfaNo*SrM=E(1m;_!|*uyrU5@(wg>rrF|Lu|=P>)n z*#)(<)C=mPr;3%AC|x2;R27y!PynphGGuya247HY@kAjKFBcpU#pT&R*+R%JfgTq` z*Qa2?sRbd^YI;f;9Oi3~!GgekcLU4P9FlQtXiD#K&j{)mu+ugvaD>>p&Ix7G$Pk+r@uIF~pLk<1^p07FfM@dF1-v?5i)+OJ?Sl{u z3U)ga(%t_V0;*e&p(L|Jw`;H(DuNCaEoNraJ-^(n`2LbIX3$B2Iv==|9ZkWn122N0 z&LI_n2-rPb*5>CPmWGLvHkDiUa4zg;ldtU)=BRVG1s27(6iKaZSMKAK!Om(MST*3x zC6O_6DF~Kd`SLuK9UiOMi3>b{V+d9R84(J(1-fg`j0TK9!0)><)kR;(#)0^_2t2s} z#Eovkem8MVF-V2uK!2-2N6I6&h#1Fw0b%JfxH9b0Z(H_xSQ}7@c2#=IYI;jucdf7G zfeJPTO(Zqh)>Z|q=HoBT-6iXFsaV(M(8t=knt|Abdp zHJdLM35ekc)XRb>*+SkzxV%4NBnPrnt4YWqJbicTZDtNUAfeO_4e9%Y25h z8-n`ZP(Kofb=J&xI&TBZY#ug12`oACJ?DwJI;wB_)IaV#)_QtL&_YMT*%3J|XWqr=ZE$L8U(+Y$~xCC+$pZ6L6-tqSn#`dCUeLIN!G{3Rw( z0d86>5R+h%slKG0kHvFE_XCt913f57YEt9&d%oWeKmt8{_B$M`A`H`@6e$*Uyj|Lv zi`9^-R&L&X0{~_aJg|FJW5j;u>WIEQ4?9V?8bRrb$?lp5d8Bq8pz-kb{V^b#RqR@C zURV7G+GlLzJa-B(+y&3Ip-QXqE~FcwUG-YproemAyX9lV0R<`RMT6H4&e7d zY#tICc5c0alBRJmfW*a3HC|&Z9RI0}z|PgdTP~g8UtUms@~uf#QyLxSyoIr^wx}+g zXqd%%OX`-Pa^#A$>8F~~T!fL>#@I`3uTusmW0~hgu_;8I6umR!r1A=?sw$=&CVC8h z(AOnG!NMUG{gR|qeN#P^_3frC5shj&`5#YCk*jiYv{sVQbm3sGMA}gX4Vqa>p?aOF z4x!?AAmm>&lmXRd6NuoYd+brcbh!)Rj!zq34$Iil&GL&^#1+!z+o(CZ z$GXP>-9FQ^zfS^^2P41k@f^Axl;hj+QHTT^eEer-p=}IJr}V66l!MBR{K?bP@Hi0T zA2MW~ym`C1XWl$BgkZ~fDm7lOQ6>CRjjHK{eq$AuCTZn`mxaV5VAG> zL8kD8oWB6icIu1TyMXN+0|1}@$`1Z_J^YV6^{-u=qaA5PgmMm`%!%kPe&j@=Cf`kIy(sR5cRD6Q>S(d~C;0!vx73 z>?-4>Yv70{`V|(hF+4VDiSz;4rk1W{T2TcB36ut8ptrfS%8~xN-`_EHjVb36w}Y&u zKXTnK_?O_O&p=hxhe4p{w1w`XK2O!i&&KQJ$vW-Eh%5*W6dT*d?1~s&RJl6NCSp`* zExgq$n^;E#F30yc*hT1El7?-%i64_}Y+@e3O|^NkEjxl0VeYl3mI*t?D$w#UlsrhQ1<3cZq#iv6A*mX^!b02#hp#hl_xw zzghT*S{KEefG(to>b}4^=vB~7HX&wF^p^6YK9&Tor7DxKnmfeKF1!z_&O0eigIMJq zCV^~(d=Pp66Y?clTt_(D2$Q3%1m%!71X`yby6KwLy)YE_Y8&g>wH=saszZ|O2{n-~ z&KvqvCwxrA4e3yuVc~cOeyX&kP-`=W04v2lQW7hCMUMVATqR{&ku!Ta>}PVIr>6{= z??%jS$jzd@A+*D0*B5bVE0{g|ThIr??D$<@u${eWu?@mxGN*qjVmJu$zQ_RB1rz|g z_$x{HU&+DQM$O#G>|fa-C{95NSO8)8%W}2o=TEeGAq*aI#D;q-P(*P5q+=CfxpKH8 zyd$0S3n-sPyw`8urDjthux-csshFSLFIV($H!qLy+pt`CP2BT5RxpXhl#&v;K2Ga! z3(BM7Y5Y1OloA@i2Y&F84!?~BKS`(L+G^p9Qn4-yWugeE-tOkxwok@>$XkFq04ajofW(aR@4|APr#(Fy#)rD7PeEsNTpDn-)(E0jY zyjpJnd%P3#G6r#w#ydQ_2qir~nbrx*%m13U{A$%7jvTcRqm#Zo)F$Qf<+o<=VuLl` z2gPdji700V1BJ$sdJo7SiO3z;U~~m@!=bt>i-bM`S1PYSgHox`=b_3WfW$7tP$UFI-)wrK{%fwom_npb+<@D7Oo39cM=ZOBWUr=y>Hna8l z%5w7`gArXwihB~FrVUI6^^fB?CS`EJTm5$=yN7KErLuW&g=0CfE+kT26q*le6WB?C z!l6xKQUbX_MTdh861ypRJV)$bV4dM8m(v6=*bPT0rQYyhZo=IdxG}*$(*!gJ; z(hgWgE_4>Wb06f%oc50W+v{Sr&@YYj`yluKESr`H|sKAm}Vs49ttEdZp=T zwx{E@uGrFU;;)q~X#V&5i`r!Hm4>eX5zobmBemdakU<<{Of2GITaF&S(@0NVs$95p zB6%BypIty(+D)t;YKF4u(752a6@lvUR9qU-+o#->kwkUu=^uA4{e{1FV3mEc>l^_K zkqB4_>c6XN0pdY(J7+8XfA}rZiJDveZ?xzr0A>fmfZ%;nWo=y!pvM>dfELjZK&R6| zp;Hl1=TSfD)m#l`C7Db!kUx8P1|LSwL`6>#86-D;u!0CAhbqZt z;cDP%_XpvX{vh6L3?1sN@#okdWmK~uL(&&cxcsn{4?g@Ay6h+P-H0y)&AFhRMt=X# zD0STz@T0T!*!fQ7(D(Ln*^Rw$4}<54628|44KZQUR3qP+kXt_h%?`~O}% z#(!T;{-+X@)=I6k$X7r(7PttE5A-XYQoEN%9WOB>zB_bv>pc4gPE|T80-h)0)EaIU14jNADp2V8L@yN=QiOYC% zVT!ZYIFjt+%nOQ;k;d!_tdQENmDv}kklPe3bnsASgYf3oCc+ff7QH!Hc0J6x)={+b z*{R7gl|%{<>`*)-o2kPRYJ+2ViM7or?$-X8ii<*ewp~1taP=n5O4m=XbIl{Y=R_}P z9dd^G5n>_2=0R8Y#iznFZNh-{dR#_K8a!EfZR17(FXZ2!WB8=ato7yDM3pjqXXQ;- zD4=nK(hYsq;ydJsFsi)`+I=)I)kf%W()z1CA_H*DJ57F=>kM}2_28HKvi%)?VAK{Z zw%dN3Kl_A&AM5=YXQHVRgTp_j@Wyn`@rRS5XcVqtxdoepGyRPBF+HYJw1W5DedjgT z)~OOHL8I>%cfU^Us~DAng(w!Z5KpS0mSB>VM?>9q920h}We=joa1uG&C^}l>nIocq zFjoEwM^E%zw13u-ZNvL%%W0f(N?VRA0EGAQAYU>dcgbi*a(Or?#faGCe`2<#48_Qd z(CL11nd(J1-8>lArs5G{;AO*=s2vLqTr z*~5ZS&+bzQQFD^VBiNq|sJxfB&6H)ePg$qt2(V*PgaZQM8Cqj1$QCx ziAB3#3nGtPV5EO6`y6xd9Jx=qPqj98e|dcY>4DKX9jnXV2#bNkp+<<1t1I3pi{Zgw zF;kb*D{~u;bTP^VgUx;C6s3Ce!LuFwVEz7B(IgdDcLKP}+ky%D#Q00jqZ0I6ZH1rPB#3EmxlamlA%Ls| z>X*AH3}NVXW~&;n?Bqc{cP|`O(N5(CY4f>cWu^8ceMz`a%iG2CXOL!8Elg%V^fGFV zpJB8s{OEvJnfMvt_ZFsK%$mGm^VEV{IsR~PUS)^dS_xwg{y~pv+R`Rk_J>53am%a0 zJ)cRrR#1@5%GJs0wF)1Cm5d#g2L9)GojcT$T^6liI_ts_Kv>k7$ThbmTdCI5eMuvsxBj zLbL%)&h>@h^m+MVz#7a`tmYmm%%1Gj^omrSZ>dtEwA(<&Or`S5c_T#O6FIa7*^IXo z**|{S!9Gwyi-on-n-@bW$_R6dX8jM!-ZCh&Ey)@#io&6AcXxMpcXxMphXM+BcXxMp zhr-?6-6>qY=l1QMd8fXPnCS=(5d^=^-s|klT$y_daaVG0my_0 zL}{A;BSQasUOzI5AHcA|34bbCH$r+uxER8%w=O1_me+BCm;n5N6CBzZq`UWHJ=QlvVtyh(P}{4!r4Z`T)A*?7iMyn z8$V0ek>}MKJNN3X$wF!}O0Vx4c(h{Q9-lTY=FM^WE+AGmMBy}F%@pJ)iS9j9N13zA zx^pL9>E%^b4DZ{uu(be`^%3XfGkRN8#`ozzyH=+j5?8Q**Ukex9RKU9%h>*c|=9G8ZVEQUcr0~twfIv&v-J6b(H{W-nCD+aN-%tIl-J0l`}YEZPUH0dA{6gq)Mi9Y#3 zdS6DWjUa<~c4#2aDAuxyb|8cwsk7Kx@q}n#P+V3q%Bd`7U8Td&Xtm2I;ACr=Txz+| znoVG<=r2b@QL^x>carb+Xi_4_cTJ$*1_q&@f8^H7ru~$ zj+U0o@Z2D4G5swctG=OvJa!W&1f=|8y|JU=@(o5vM`R1v-{wBN^X>$~sWKEc(A7kY*-tJv07)NI8Rbw>wd9W)?$;`T-xvTkr2vxW|MpJ*ecdloLE07( zkk@Wn)m^aU;wM}wd<}?089=Big(UMW5!->AZ?A7u5XI!Y^?>IMgF=mA!<&2~8(0p7 zvyhng=iUQM5x%$s^tb>L8BR6W`4MaIY4if?p?0i&o#50#qghI`my8LkoTAYBvbaz| zzq^!nwdv6z@BCmH%a~p`4p!hVp!bB?NFJT8lMw35Yhthz;Z z^@b0X1i8A<^QdvfliNk(-chHNX|7c%Ur=B6-ag<}Sx8NPS&Kwd7#kJ6Y3k7w?XkijZt*K!ZW#ezEUpmU2m zA*JK3W0(vLnK#d1$A0PrI)`an1wZ)R3aByvJ4mT^R(WTk&64h#_Y-0-p@wcP-ujbh z%Y$l55M|5h4MvqbO3c<-mOC)3_xb>J7=DvkHy>ru<+P*f#~-YpXH$Ig93a@60y^RU zd$9it=>H<}6B9HQ=i~rIJ}Gs9w4x2lHxQwN*`h!>rG(s{A$uVzUVP~8_9XKm`U11! zh@w1UTfL}5Q2yg=FQ>r{>(Yh?3bj1<8Qh*t-Jh3P@c^;5$R5SijT>pc_aElStsQ*CcRbv%51ikO*%5~y6k*O zzH_P-ROgY{We6rhON*isP^%6yK59B$^r72>jg2s$L-^Nz(_#Dhh#sMMgmk2d>E<~u zIClB6<(ZwKy7g!FBD9LXM6c)dYnr5W}35&c%&?ggA} z<4bXFpm|*)sbt!4=U{U?rPKHi0V=o6b(f>3(;NJN>g0 z2w3z`-T#>fcc?nKX#>tmMSxcW<-ZHHHnvUxxdg+1@&Eu!Ugv)eWk)JoDkA73{m_9! zU6e+$T^^*PgqIubP*O=NQr{`Wn~0{QX%T53uX6(2@|r-J+?V3b!Ci2gtV8%E^L()3 zI>kP9mw`D}y~X?EYfB_Iy8L)BT|m3ndb~X@6avmKa6O$qL3D&e<6ExXH3K+F1{}jw zT@|)N2wX}872|t4J!z3|vgkFFepAbDT76Iu=s<_;hjfl?E>*7RN5D zWf(ywh3RilU%3O^!aIvCN~n9GGWv)DYHLRSK3yJdK-eCc$i}*(OhvW&>$yZV<8iF((p8CoLgJ zJw71~y=tNz^3tkHjl>phrin5Z72MBmEIFabnGm+O+US(d@9!LA(-zZ~Iegz6iDo8b zrRci>9uw*)SgtfrP&=w#Z*kDboXJ_$(wSk9^OJH}&JH3O8G0`-KpI37jnw$cs`S() zSSL@0vL)0WWX+9yyB#Wh2;|Vsnl!#_p`B$CRN;S61QfK;a z3P(ovk9oA4XjPo5Rrp$mEXLN%pNraQX6ud+Y-~cvnKGAo!?e8%ePrHddH48tGpfN4 zzBceWkRB3nOyTex)w63j!cQmYGFXB{3R^ppMBV+L3w}dbYG9f29KrAec{X|I#F7=? z`1$f%#zP2*V-5crB-N1ZcaC9*}abmxg^q0;XYRUMqk!X3X4uYZi zpkUd41;fzOV1})^qIyaqWn1{Qp2Oj-)eG^}bNG0iNEUB~IHd$%$sg+^&p*^s?pUQi z5TngCo;l!?!kM!(t#I^^F4SGrzGm{20+f71T#7xk2y-qtkr_H>a{JIId>Jnhhzj8V!Q&*`1Z|Az0$G$vzSuHEC-2raUX3==9m6$5KVUi| zB)ftUXOB>u;@%2T*DcC+F*NP9;w7f;rCW)S@RFuY z7s$Rfk%~%VS_J$6|Z4<(x~RS*cKsJ+z^SsxV&94@sGGX%jt zr;}3CrwWvzT2g61D@%{Q$qo!{c-z_Ltg51KKk^<+R-60Zgtp|8wTTGr_9Bf(8;-1fpqfKsqRxCJq&pN0^C zzV!-oLfF#@1S~!nHVN^vaXaYQWdK$(O@VVC^!t` z*morhsOBlZTvH>E`yJp&1wYAD8Sdh>$YY;qi(a)s88Js1eh2iv&M6G0V*QG-RQzNkb%T_t{_L7C&xqifph%HjK3I-v9MAV>82Mn_P9K7b$n-HqV4$5#vu zE?~3_ZRuw08z$rcs|3`A)dIWo%*R|Zr2-Tj#(AhSSU)L7{z>Re1pO?HXkd6Qti;jN zRLqg%L3f0(WCgILCt;(b5L`!zu_(xVwnU3338VZLTm1=s%qo8jD=?HQwv<3q{f%Bw zEshTab3HeW&9-5%T_^q(-fpdFi}#T7T%RP>AQy9&whKS0fi5vd0Z(*Uh8a_}_c81@ z9bVgyYAjkZgo%$UxFTf5smYOHNN)A`EW5&)X;hGDvF0Lpi+Ve=4vBi2vU}jWD%Y=v zRoOYa9_LVHxKMK(ze$j}Y696$GYxp6K~c;)L?-a|Iev$?(C2!|!&6!lFpK%|+cDNSI$~zRU@=Z5~XQjwI+y8WN80e*+0Mk9SOn>8AH@ zn`G)r)?JoMlgSLn!wlO=Yk)TPEixY_rn1yd6$vE@MT_YU4^E0%irX)}%$pqeP&OEZ zpg|e5-<@+xBLm?Ig+lw-MxyD^iAH#PL!kbz#j%ixEAc$CMkO8oN*AVeAq{Jc;z3jB zE~=#0?MzXJ^!~NTE+B%Hgo(?tTNxq8A=%#wQqKZ-V58iL8o87(IBY=amaCa&ReJW+ zVoW;SxFSgtLL1*FQEh+ATkX>KOPRcP5$*}aFbbCQ1)(mw6a0Mj7XZO-l}8KK$9Ogn zJ>5U$1zF)ZFwGdPt9-+YC_KFiHG*@Iu2?netCbHl?jJzLoKs`0waEtw2JA32v~0L0 z?rl~JT**s!*d2!k?RME!}O`Lo1VqU)r{Lgs{^xYSoJdydyC-r%H*;i%_Q1dE8?Vd>deW=yn#L7$0A+w}thGdkS z%Y|+ZErIKH;Ki-!YD7KscIc9+kJMDAZEG$5?7Kb${d`?tI4&?h~lf#?ipb? z{iC*^b~m?ZcIe+ft~fqwH)VL{eUjgv|LS2C87A z+>8-f=SuL>S#pxWWyGA-efgG0SAl4p+kzkY28Z^N1Mr?Liv!>FIDiB^x6}UMu z>a14YMmqiDoRZ%^Z~Owl)aQSUDW<CzM{*-Zy$WQ%tOcCHK0%eAqJSB2?cM2&Z-50Ld z+*^*;9Uwts^wE~usqINS_vErYDU?_jPx6s#ZcOKQFGBp z1RMuPK;|lKxl{g(IO5dN2|*&gUqD|Yqdd{B*-#2rD6_Qp*mvrpcwTqoH^Zc@7^zGa zb=w$ZzOgelKyG7e9`Sv93=SOiybv+Z2r%CdkDA{;RTOWcc_J>Z!y@MeH!KCU6=s0; zByq7~+F35?tJB{Nio|e{K3_5G&D}d91MUysT_o+G9r~*LscWxndIq8e({a|uzyu{q zn&XWgst8Y-d1J;|k3pA8fQZu{{K=Lx*R!VXYHFAoAw!)yL@z43x|}1ao14v7sCT1B(1A$i~RGxV=R$`cVO#K2q zyI$lB<0^u$i?|T1qyVT9W0#}IG;oWKD+p#Lm?V*VGLGyz8x(uWCoa#eG zFu_eeTRb=VwbW2-x9*o_^l>Y^xgivP)}aHs=3z zUyn5>0Uw{Hh}}YN1J}CNlPk+uvVnfsEavl40xJz`Fq2$G<9Ew7N zcVB+1TP7w~JuOqel@!sQYSxNW`p!{0r%1>Nece72Ju)67d#B$gV?G_(?5rtT)`Cef ze1#X{M3a+0epRjT(^vd6P@=Qy1!h`iLKWr3!tR)1ox~hd@&2o0l`GJpjn-e;6lYPF z*)en_3BMn{iMewFS1<>P8LJ&wcz}WsX9MMF+0hT-1I!3`ze-q#usl3<3g2WR35`B& z0-ePK=tr_Vb4?$eb6>xQC=oxEZZU%bG}Hyz<@0cA zTq&-pMCh^)$Lf{=-CUQ{DJJx0bryw$u_~xm`wJ~YWAYinqJA_HI}gC^E(-q9l`c{c z)R6NOoT7j_ad}J!JM_sHH_#~6Toecv)}5-6hY17YABlOU4d=c%!LK?UEG6&w(OTEd zZv>*TYHq*iQa!t^$B2-T#;2*|4pSd<;ClUkWJFBH7^BLDB1=wP- z_`0SLobKN;S)>sEh)m*7IukmwGfcUKu=f@lbmYywM;{_f%(U$w<-)ynas(dM=b+vg zpq5~h&>Hf>@y2v@SktD`3o3A5P3_rc9C_&>>%}bL{iE6oA7BUj3s$H9aqwgP7px}! zG93W~KkMDJzk;9NEI@hAX7BK8FaeR02E@ocU%3vOb*6M(qB`NH(ly#Oi5L>mcdjq6 zfhmo1K_T5j%d5|8sVlZ7F5Q2p>FLZ;J4qye*L#tiC>SW04C7hXB{T4VdT;5U^&a;p z+rR2P+2B`AXp_ZcbHn)F*9FDF)br68OVv-|ss~7IB98M|qTyjcz4wodipvO4??vSZ z{G;AG6(a(BBO@5|f(z#2*bkk5=`l0AhSsyoI&oTqmsR9XioD@x#!+B@m`s;LB2$2R zL^pxFYW}O>>xHhQALY8Efr95ck6u`~qx)Hldn8WgQb<$vf%t2Yo_A+H2^(3&omNJGw}Kt<850yOSq~)t+lTuYcmoM@f6y3{dc8`E05N9WJkw zK2%8!_=(kPx0#shI-#lfF#64`8TOjGJ^MV~M1wSkmwvPd?IhaQ!M`C!{+6 z0*hCqtUlEwQ@K$f zyUAwgMy*m#@T#EX*>Fq2^IVUF6xn*viYtRdoPCN#npD_eCk;->OJqBA_Q;aL_s;8N zR;;xV?r}+GrjQ4walf{_}Jxm)}M!ev2*!(bwfCirQfSokRTSea+!LWHD}sp+V$b}3C{;Php{WRI2$ut17^yUG1$tHA!>_F zvz>=VQ+kZ3@=gI7z*AySf|6`6>>s7SW2ZbA5+)y{MrZqqEgmE^^cyD~ig@_DRP{dF zIwLk~&C&T!EO=@=aQpf7-bxGG)h>L5hlM$-N+eNEb z6AVV1UJ4oYEb4nZ1^8>U`wZq`*8Z3@l-CF(>aC*&5BnoHj!|HF9QnH9T17m=hON!< zpTFlB!jtZnu{0^Jh2i;@)dvX#?}jYMYr;)9wzH612*k#+e`V9!qQT3~(=mVwS^ZyC^Io3c|>Fz0W$R3cJ2w@0$z*lQNqJ zw(Ine-R@QG5OurSZA(}?hr=%@r+AJcyXf4U(b zzdoMeh|P|`s4UDH6Vp5)A-^6$_tS7zcv~b+R*iDcw|FPZx*lh#7f+m z#4xxFWbWUPUMf;#2(AH~n!C4GZiOIpe^4d+u+g(Pc(bKXO4Bu1Pc@JG5j?8s6|Cn$ zOaIt(%nrRw&YY$*_2im*$E$z?-P7FUrQw77&2-ryvH?l2(`Ft2Zq`Th?iqK0G=G_;451GI=cigBeV3u>9 zPAcO^Fj_xU3aB_mzv;P$|LVbST=m&AGE9b#FW!g5F0R#iA21k6OWnkaNx8qt0rmfA zF|yE1lns%&Cye?j$@FKzn+UO-|Dj(zQx?G(O&#(gD7O=YN2?5>vW@Q1m^MeN0y*(cnoYcaG3BIrsG+zCvh)S6mr@XmASse`JOK=hs@8 zyrqgMj5ORGAMlMOHvktzLJ(O9iXXz7yi!2^M}kCT12G@S&2XI5KRnGazd!B9SMxmY z3VP4Fau0(hI^J*aj_zgZ*F)gP>?b%f@Lq1dwz`^JW-`9LZt3~}w*>m5KyR1+-57~B{ncjy2vF>}31i28%*9KPx(OyP@ zk)DP07$^{xVt{-J(8x})19>-?ji%lq#T1|k$b@4G5|fy?HJ+QP!)P{ZGeTQWTT)&- zWbu!awrJ`X7m7D?Q010GbQhgN6yO@1j+=D|I^Cz9yw2IZbDC{%2;Jz&Wrv`ot$`4H z5ibXbkcK99$Op(yz)@y+mpLvRk`J$&>ZL>Xnny;MqP_JKoN3T-jysF0Fu75-;VejYP#W7o5Avh9p|az&Hh=v|E(mTa@ihoD8I#T_=~=yCqscWO zUkzE5a((hF>K?vfKV@e%(85VdxDK`J_L^>@(6IgdF0br_!DqM8Wo;n=KcG&yKHXV^ z{vJhpa@FOBEGPj_*mQ?kl0%R=g!p{;)EcsJHx+Mw0(p3vF=rv8;=EJZ+QanI6PvOi zcIzJT=C9|`TWka&@lWb?6oFxw@yRFsgSC*C&50DBO;xx5?DZ zVMMNqig*k&z+7O`R0{|7?u;r`#~ofiI0T7$IYg3otV#-iP9U79usKSD5}wE-}_Xnl%) zsn=&r8&fE2HsAw>`HqN>o0aSq_U3}EKR{IcsCv`FbUz6L2aMIAOtOW?Z2X&Kv;)7fLxZ?O7lB0ii zp}p@5%gcoR#~VXiL3}xglPZm1q47SA+R#`00S1(Ed{3gfW?#!hvGAB+=9uWROx6oaJI6W@Aq9Pe}+UbDoQ48|p zh1tEj@yBF_E!on~Hr&LFZ1OvwR;s)Fi6?OJ-q@UCbyqwfS z9w0Ugete&B^AEEo+s^ml+HPZiw{(4_X}+?5x?MOQ_%4@SQ{!jdU<@M~GCJE55^@7O$H5h?HDjhz95xnTAcnG|D$WJCv<=W{{lU z3t}>=Fl3zJGG0e>5JC$A2Et+i17YiE2LZJ@yvJ%DI06x!(u&NSKB?Ds$y2owP_F^g z7;Ec;rxK_&ncarLP|rkbW_iMFAF{Z8IeZ~PX{{||kxP=lUqiK(+`Ky=q2Y3dTr0C!?J^D6vSdohn2BO27t3Ri@J8#^KOus0BvDHSpQ7>#wKJS7$atTZg3*ueWY z8KEOBzm?TLM(+O3`kdv*WWLfPaR>Uv!VBU2#9+0FFxVYn4j6#bjvV^d*Tn}sSS!eZ zjhlTiIuMAvEsAbC3?^@oexK!edD7Z>vi3Sg>8p6|_RC;QxL-IPe=AlZjYjaDF6EB% zGFVIcUFX+ci#L;zoyUw2T>^0FfU*N8f=7=iKK%k7#+}`-#)fCqla$m5^oO5fvhVH! zlU*_i2pkVneRg;Th|TB@tczK>b+fU#H){{}H`dypl$Q-{1&@=sLk9Xr1qP^V(1BeU z6gCLbs%eyjE1WJA)wskSR5S(7G~T=y17+cMF^0?UK3{|UjpZo_8+Lc1%d*7?X#!J) z5rbCiKJ|+7JvkvCVsmk8!e@$X5)R1QNq7Y5w>W-)dJ-fAN){*A!OSw(CgmvwepQai zb4D5|653-f>=^e`urm&cVbdXT?Fhv3?MJ3`?otFU}bA)`45pi zCP6EGM@K-*7jXIbU)QVuvX=x5vT@7ITD57Ddlkez|DV#Jy>r=I=oikK@Q zS@nK|hZ%aT$Vpt7gv~()>!uVFlJAc&g3V6uO+i zQ1$PI?V*cXf&eY9bB54((yp=<~ za9$QUo7!Zyqj1hbW|Y_eA!rdxx+T(lPCrHRZuHhLs4{%hCvL9jpp=c_L2ow9<*s{N zdpJ`p(B)ofFbFrEuJ&h5)-Nm-b30SBAoa%6b}=146_9!(!P~_V47tqBQzrN%Q zMo~}nA0B32zoI*2!z4@Mv~!dRjA9>%=c5d{NeAjFDy#Lj5FL6Mx8J zloqO-@u7<8dD5vx5C!yr`VZ=V11Cfe7{@25@fXb(v7k?rKl~zub##qA%;s`F5F@?2 zA>C(U$hl2~AoSfOPWx!xsNU6u)zjo^V)GIH$Y+6@n>%iue=z3fnv z_jwkXK%?K(x$YQ@BXZ`-@*a!>JD)dU61MP}F!SgA@a+RB$2<9Zs?(=dKJLgJEz^sn z`$r;^54GcmNO5;zcYJdL2kbGKYS1u-fIrX_VX$WU|;PqO*=U)_SQc3@wQtd7P~0)MqCr-9%#0 zB7%?MqtMPgkz{(X;YpVdS#wJ)yH*bPJbpy)%hDd4SXy)-<+h9$L$>+tlHY>=4|;ao^0n zp?O{|x6dAOWk|HCBTzAbQuNk)bh{R+VZ@4vaIt{}+q;RZ^q1e~m}% zH#z6CV~dDg-I$T1<3fv2pLGTG4yC7@oNwa9z`Vec{pnyzg&vwx>vq&+$K=#;g%`9) zar!#{BvKT&H*2gB)1O^RA3rj06ejQeurF<`NvC_oj?6%csKSJ>f@d@+rH?s zZXrrTNE4egVoWAqpJ!MLGX$PPbPFY4JiDg6qyJhz+@!6F6Aqy7H7~8 z^SaFj+tbtdy=4ZO*dlZyjhG6+Fl&C$`q$(D$#ku~nQ z)(B};`G#ED#fQK|ScKI>1&76O=5pC=XcWY-*Nvrdm+wqJAZor&#|u(Kpr~BbJD43E zV9XsOC4Jo+z^wmW!ybykNpw3$qS`*x$eitj*;gqsuXB!}UtpHBYD~#DwiFpp-iX?j zFi*jfHi8rU?WqY*_=hfoRlT0ZNn`;1T?(2&u{70IS29+8GV?@hn7L;!5!C7>wB~{! z{TzBc)50l!jjJ101d$xcfMqV~O|qrk6c>wvKA1&fb;`?DSbed{WiWaQqKQz<3xY({ zs+U5-up(k=60%U`Ea)00J3Kdy-^ZpU_ze3ONJPCGBPN;$?gd8+gz}sQr&(iD?;iU^!?H7$|7MABoZL>E_L8lc(_HAmK*n;Ls6! z_e?mBcpvJ?jW?Bu<>})lJ*1X&sVNr)`x?iNFls@aXD-jnxAh7?`) zuxo8vKYu}R(6yn&n?h2VTIPH~%Ke+T83iblKF@yPD=bnnSYK6b>| zGQQ?X z35N0bi6bg8xi0B1$Yn5%?lGHVTJT)LlpS-ZZk`o?NR?@*K|1%2KNG~ugCZRy`exXR zShU533{Bjp_+d71FG~{Kuks7`Rs|hr+T7hNLpm}Voomz(RFkXf#@@f5%g;)aEqjj4 zi36@K4gtMWi#cdCk1MoZTSE&u82Sg;FayhE)DKW^&^U*T!R^=lAM|MQ)jkA?rQBih z+eUT>VS9*P3VVv6vNP=dG&82D8jzIM&^!}YOvmx}w($m5n|~`Qz?A<}Xs;qjg|?mkHVKoR!}*(Yofbxds!`x4!$P-E0!#dI_ze zukP|IhjtnjCannF-=wgWU_!O<899xgjyxhuTV;6_u1yy0Ui$^-yrbs81^iL%zWIbx z;4p%}TV`vxw85_QK_o+s67W&tBDrd6FME_Mi*MCEkrz$=Tv~Bo*)Dz-1m*3Y!nh~n z9%?vDbIh^(sOiPs@!aN2-7$9OT}LJk=YsYK%nX9CM#>zL&y`~oJk8|ArE8oT z%on^o48^XXoi03=e1DQmSOJ zyTO!nFe5Yw-yI}(%N~lf7H}qA4Y$KJi=__+I-A1QwI#G264WP=K@<&#-jj|_^H^Mw zhO27zI&`rwNBNdor`0l4cS_qs10B7-(%+1cw^EnY?%Dy$&>+;S2w~D&-4NU)!qxo( z^xoYlFO&qveI#5=Ym)~U;X~{o&l|aT0@ollLc)_f*;3zNBA@<7)vy?BE)=EOA&-(+PTzV^57i#<6k^>@C%s6!-3@j#^vk|2qGY8#Y7F|nn0o)N=R_S` zA6|OSZw7=vr^zP@rTZBL48rjJEf}&O`+x!i$b42l;U`(8)kPjbFL>KQCO;L`d~GEG zO!A-2BAzJ1<_hxUg|2eY3A=8r*A1cwZj!qj!AB4rq0IywfjAQ65#Zg5IamF2TZ>%| zL18azXC-cFUe$`in#0hE8O66vE*%LvN|vNZM+gmT;`!JOz8)o^m%h-Qkjm-OW5Nf; zuCv~dk=dKVkN!V{p0A1?eJx`%La>o|MPy5=blIgfG+&t#r4`vXf8Wi|cGnrELATEp za45SBAK#8h#$PRj!!3f(8*(SEJ{!256VFNfqKu zo47(XZw!=IDw-|Nc1W?Q?wf*Yaii7Mqy87p-IXhZWV29$KsqwzHbINzSqgdYHKqlr zxvzlm_;E@j{|=JuQ2NthUa)bqP~(d4_sr{wy%%Cx=J8k{7iulu~CH843Ch=kJV#@n59D=eO1yP(hvv@dlVn$YRMe1$gbKecqPsAG( zh2pneP6tDUS4&@JT(aGN4yVVR64YdsmZZxLWyGot zv*IXs&aNSZTZnv5kGettTjv{272{786IdZ;lZUm|y+L0CI{Z*vdJuNyKI?TfqWUI~ zRLXrt-v|k2Q3CHPQ?5n<}B1#!n+Y8juuu5yTI)F%{$m#Ey8P~#+{cB=dl^gVf^5n!(uyLV z8X%%qC(Me>!^`y|wtj>*O*R*BT8g_Ur3n9U1EGI|v@TSQ4eH)aBamxVh@2}v>4-DY z8d2Skwf*b&uSd!08Pj!dwr#cO@A>9RBJr6+-9X+y<_v+`#uTS!3bOX1x}~z6*)|T* zyc_p|2X%|A=Nh;<>wat*UM_quS5n>*foi!c7J4620s4ooxQHcA-wh8 zL+pZXrdm4lh{5dgyqf04*lW#=+Y~O_ra7`hK0$=w45m6b|*D% z)9>X}7#u5VnkE!f&G7Kb!Cn<$PS3zDF0dv_nkL4mLyhy4rLeZVv8DZXDM5tmhNwFw zX35}Sjx?|3aza#w_ZZKdX3nax3uV-`v8c^7c){$iQ3=Vbgu1D~7pg}B?F6)|?br>E zM)};|8U$@2FP0ppe^`HS5Oy%VTz)yC7woTZ2Oa|Np^R08t1CXapr~|8WJ+WUJpgrA zOf}Y4HCHr`uf|S2EQxOOI@)^DkEgXpD1+S2R~mnurNAko#!~ot@ikxB?&9b~704LA zM=}l`zg_!ZKiK%;J5AM;J;Jw=8H7C>GH7M71>UqjW$ncJM(mnGqvXjov+g7#$Lfa; z)b}!UC29}T#q<@-eK7qdzYlmA)|AD0JocPDQ)0=u9jmlDm1DID-=+r$RxjnMu6#IC zko&;fNsI9O3J7N;l;xK!1`yKPb%`u1Y1m|qKK+g;^7u8C@5~%Qdb}R=hy<0XgmNDw z4~yRux`{Es7DP{)H~QmXnzZBxi%gD6m~ng$Xl5LO&1&;!wsNe81ufY9su5#h^aL?cRUHLoZG0)XKZ3 zQ1X^Ozv~H4kHwa&wLBJ(VV(NTkCgCrH>oZjNYK1+937+e4y|IVNH!hU@UGfz88db> zg{y6o1Vtb2JDEw-UBvpr@t|I$dRU#M*}Rc>;jqL#3*l3y@1_fGc_dc*5v z9(F@nMOL;|#y#kVPFqp){e@%Rx5q(7)kxU_Xku?kzaPRdBJpiG6;dn6Hy~jLI-khr zfpYL1l_H~Q1(y2ZF^Q#exsVDZnVOOe=tgCElf&i zfGs!yJpZ0`l+$<6w>Ab`jx_f$HX{13za0PGl)#u?34kn2&}T!FEVmn- z+XGRXLGP-9Ds9#*M?!*?O5KWpHxn$6FD&t-jz*vWu(-)n_cLkyRrVC^m*i#G8tfcw zJykrfQApN2PHVB84ON-K{gD$`kM@+3QUo?Mq{XuY9jB_`psLD=j$4lU)}9f;5wZMp zwX#+`Ff0ABV~HFVCCLqX!x2 zg?4*~t#+6iQoF3y+6*KW&l5agp37OC>w8WzUbIX|)){N<2P*}9y zW4p~MYBSsja3$AOd0NU9H|qQ_&RwwVkZPKwOmGJI(7O2G(pCn+esIRmC>rv6f2=aK z-d%+-VBoM3(6kl!x7_Z3t?NHl`7fgV|DkwAY#sjV|NmU~LKSmQL}jEOwwx0I`o{KAB~}GQr6;99_jmnNysfAqU=z$caJ%(T-qFw@ z&(zg8a7jtcb0ydbL(E9gLz{fgOVFuwlSx5Ap4uZ2ThiumeXjc^%i+W|NEg43ys>Sw z+V~T1Zj_a?ttUtNb!?!C_Q%-Spr7Wj&h%Y>^2q48Vtd9jka1@plY`O3x-&f6Rp54{|pzfp-6EbWuNlklhlVU^?D>a``R{frUZ{`Lea z*OYySj8SP_ML_7uJ1J;GV1)}oI_a#3$-Y)esz4w-hTBVmTQZ^*gRGn` z3W3IRut={DqH=W2-z0`s-$X*6YGT}-g>Dx`T}iF?KhA@P@=-{=`9hKW7$6QB`K%X1 zi@T^U=V|eZR5Hcb%olT|a86SC?tG`M+!tP_da&GBNwQ)=GGUwv?+qWv=_-dUQi#5Z zEcDJ_beGk>!X(!(JxA*%uR^@>1%Gci3sP_S>L!wkTQf`Anl0{pnhP6M!}jhQgA<}) z>rx%|4()}4I0o2QlRKa85Cou)>iM*Ok+o{7u1@F~z|!dMEU9pY;w|4q_EtagfZZl3 za+dB=(%)WJ4%*bHPP@!Edpq4K(EHQQy~w$7sF zqphPK+dDajluut9e7t8+L5dbf&S>mfHRofXQ zl|_I)z>@H#nNH3vVwn)@P15~z_{+nR+kKg@ZOkDF@=nq&pK~Ah9R_ga^+1D#x8b+`hs1Sm6G+rNK(YD*tr~Y7X64eNPWC`vrMW^ zx$1B5%^^;wsvT{ul0v(vO9W)`*oC8}_CYxf{wjY^&Dgd|@FHbfnfX%53D4g)onksB zcBLqNts(%Wqq>dd-u~BLv+k%gly6}3g>k6)?5%9U|HIg6?`>4 zd3AB~d?HN4v%31>bv5U}@=P%MW%ls{_?_0`=$yn)3vRUqia8mZn!*bK7^MnwK43z6Q z-(+-)Af&m|u+buSqAK&SnS4y(I(%7*B!d)a1zOWphd`deL5AKM)#(ZbHX7Lxzm`G~ z;LITWsc*`27KUUvO2bSmVVIDDmOPmcIW`d>Lwji!AXt__U?Q4e)Q=72M8|q1*X-f{ zA7k$rU0Jto;a0`AZQD-8cCzB6f)(4Yif!ArQ?XI8?TT$DH~ag}*|(kV?t9w#vF86Z z=i9~@?>l<$PktQ-MomuxI|6G<+bxr(9=*RDyO)mQdhO&hb!Zyk?NUhe1#*GZFFdRg7P(Lx$h$5+1Io7&n zeG+sd4w(1$NYc@lzbidlhf3NI&^~J3Pog2;Ik#iJwF6fq_R^*txp{@{jtszlZ)YNW9PpG~L z7~Yb==_xUi8)KLTUs+iR=h10woE7=Otqnd~>9r*E4dSi^S=mGN1<#ageF8LL${!R& zFYyxRUdn6`o5nnAqF2n6{BeqE8*f5pDVhPgdKX0Gg{6mmt(qLhO{6#7Eqt|$YTLS9 zgi_EmU&;;hLMeQ>0?Pjf_av+jS$nh$ia>WTh(O#Wvp}pJb~cNZwgG@#r>Ej)IZD;G z-_H{ifj)TX>|Jph1CA{x%3OxJZu|x~HQZIg;=x=jxbZg+T{;0AH4*0`a&1wJw9lR! z^u(!>q2Ccexee<@y-n*yzl|%vwyPZ!IT|2;?#K?YTUKb?jUR^fEL9u~U@&f$i590m zYbTt!Qlv0ih={)oS_>y_gT7)oy1r7%n_z0NYiN2*+UN;yi(e&sZGvyrIZoEye&iM< zip$dZ=^u{`n%hSuvdUz!v(;uu~-G`!HwokKuBcFiOz zzr!C5+3DU_0t6Gn?g%{73@H#wBtM@Ogk@if`s`e0O2&wd~gOU$_ZPfLv9$nTh4Xy9z30Q_ZEqL#Ya*;pC2H-cwx^v_>JSPiera= zY_C7QO!D<3Yo~mZay-K;03+b?*ch1{#YOSl~G%N%?kYAw^M^PUU)j$2soHyf~?m z*nQkaHO}ES!f{A+SG0SDvOgr171@yP<8($I`!2?UsPwB8Zv*W2Gu7GNZGibNf0!Um zt70kgK3&x_`)WtZ8VcXWHW*r1TcdS4fPJeEMwaurHs{3~bM$en`pCa_f&*ea}TlEg8n2I3kS=bCBQWdJ!Vl%rJ=0$C~>SfCcJheuLUPTfL@ z)F4AyERr~F8mtw7d|NC-S;!rXiC)p#00rvCg8fdA$7fBzJG30S-bPhRfG6c~^PP#b zcAku|PF*0dQgqa8@FT>U_?W!a+VV>9f9jKjn{d>oKD(yePrb%}3W;}Q{5-P*ZJhq6 zN+U4tFZXrOra5LZ11&~?p%QYgbYMWDs8K{Wd~AP#&w`!cdej-ms%||Y*()7m#5C-` z%tMb8HCoc}0tqr^d`^~|iNI6`1UrS9vN!)1g z1#SD88whtaW4$pOj*?gZ>BcPh;_ur2gUF8-1Lvrp+B9`Tobz^0Z65sPc`{* z(RHXZ^-E#i*jdA5Q!l;d=ENXKcKYW1N>sC|)Ah`I6@vtY^MkRb*&l|*z>DM4<++P; zo2p&)v`SyTL_MnftYQ(i!35Te2mhMY3FD$5Sxk**eKLb=HyLVoki8;#rxp9G$78Ok z*TC97_Me=$EZ2Qp@Oc}SiFdnIk&p~lW344QHoLju^m9+bv*$uNJj%CCtcn6^HgYEL zPYk)PS@R4u3SbLW`#0+pC&0)Epn|3qgA0^sV_9MS*>5~_uGHPs zvLJMd=*xS-SNb{FSKnnv9l-OepE7fdb148_)vQaV z_;iy&9scFU=c`{ZtIV~J&$K-lOO3zLymP1zb&QG|5uRE(3(Nr5n;u{J`#B*|<4)9Inicg>`xj&l zmn`t^x#o;}lY$vlRLxAo@+Ut{x+obRz9(|tvwK!oPx1PdU%!VR7BZcGa8AT4cCpLv zJY#)G!lTD~81+%fNAi1B*ZZuM@8pW2TB6inl9#2#ru4=2lS(!jXhDF`#wUCo#RPA| z_J$uu*XYy_X4;#Tb7?W;? zS{G6nYZu&mF54tu>KQfz{<}-U-Y6 z%|9YMzHm1U9e>sX&8J}d|FA~iA7c8T(U(ZyM>3jV;CX57!-_f zMPZ_ki=$>d{6W^5OiwmB8H^9oozV|f`};I4qa303T@#ywJ%jz!%-#4;BN(5!BLVpu zyWx6>Cj;J`&RUN*BqAQ4lcFgVVQ2}3*pjJ+1!8|J6iKdU&&kBMpk);Zn^&HRIbL$q zAaP!Kj~=2$8rV1;%T%!92}NU?g&vwrJ& z25#Ef=zO{df~t-7!{3{Cs*Fn-5|i4VUZP|-LgdNd!R@K?L}IBN32?1v!9hbJk|EmA zL)j9IHM5n>&Y6giY7CrdY?`Jmc8Z3Iwj$e!Dnn07sx?7Nu!czwOc|Arntn<63<9OTKfV6X4Z z;haKcL1j*5xn!6E`@%w{ighhFd7Y<*jZXt+**6kw37 zJQ}6aiThTRxz~~3&c2EpH9TBn&H?#K-;8&DU!k7qThrGeB~#lIb6Hjv7s}tLVkFtL zT2Q22cW}+5JZBy+drNh2_CqXG_(~W(;N$4fDqdPpz)DsVq$e=N_ z-XJew1t@z)1qo{;?h!JC6X2zTLf4cw#X<>9VYv1wcs6lKA)p^Zl^MAXrX_f}HUq$o zA~yL3_O(zTfvZ%}A;D_5at5ESG&7{nKg@@Bixi!XpUrR%>6b5L|9=hfzfU-S_rrx6 z&~7?v=cReP0dp{#%x!K^R+r2CL zN4FsQYqBg*w*OYL7wnq@e`o@+sj(DBO5((O zx1@4^9A7){%QMde3B-J=EN@49h0O}#oWznF+0v{foH$76G}cvSW}zTz4YV}GU4IZU zDIN)NfvZvcFMBqFfVXvVzUiL+)Nm=RLSlY`2<$ee5uxviDr4YnE+;*r5=~vNxa5ib zQnENWq1qd|0}U2Z+?2ryR@a}hLpUrU)`VdX{_D)OKCNow)Y*bm%!r^qGD}!wEZ-XY zz_ljoPc7f@idtl9kP%L^jd#^B$4Q-RK2O6jZkX7Ey`&_oC#$U9aDAReHsEHmWMa&oD3L{JF^^+~P)! zk2a|-1_el&8}tZ62A#}lGhbcwn#7k2RAxn&$u)1T41{z+(L|c?Tz~4DD-$%#EG7+a z91;N9$Wm#I<+YjVQ61H`x({$-5`kba)V4@W(rNr?d^#Fwc&i?$%%m)mxNIw1R7Em4 zlz=5o`O%QE*!z-&KSX1a5n@8Dv`GOO0kGGx=rc6<+{(EFweuk{a-_aBU9<#*_(`6w zSW0C>9>WauqF&+z;rA4Iq@*5ZZ@#d*9%E8mtOvh{JnefO2F$#H>vswe77>uiSEIm#GhQ6QxwpWfH*+k229pq!9~G~iU$Y5 z`haLes+Co$HJ(+=$&HL?wyis(!N$P?Y^TLw|r6yk>wVq z3J>P*!PnVRSSXf5iJ9GyWSxec_x%T~AfV_^g)uj`ihf!KUNN`psAo-mHUH|dDKK84 zlJqNR+K_T{p<*~ygF^5ZJ)Z3C*#vodW5uUD zQWW(k)*0(&en|i`q)Ua=s?jq4C%VosdXB2?(Wx6-8z5FVdaWp`%m9lVTb;k4pC$V= zS$o)}d7dWSB2x&lTm_QPaY=DzExS%|bbSg2y!uWK3BbY-o!4RD_BfV8E4A$G2eni} zog*5VTtnzQj_?#a-e9awdvQ#6eEY0peUQNv<)&zC;;u`NPq-OZz~}2oB-nw&o3?|D zqA`pNO7GW_%LrXXpO!ZhlknMWQliN6f?a>2kYodiR2gS;>dDqBL)PF+tdMMMJ?K5R z>15iMs|V0Gc{uSK>-*q1yduSTv(w7xZ)=A+d*`-!Y9Tx7Sklz1m#W~w^n2dEK`#?4 zmO6?3k?~kHb4{+NY-wW_n&3?{?B|@WeIg!StrlTYmq=A?iGStSeLLmo-KsI$)TI^070q9tJx*1hdAg|XLvycF1!-lIe7Xm z*wy|j=n5uGT#l}tkk9UHV*=`=a1a%ry&f?Cxqge#El_Jn0B^JfzV|!H`)WgwDlJ!) zf&Fmoh8iWz-e=oCeb!VYKFj}oW(BinOILJ~R_CKKvaZQeoYy@p`S@)^HSYBnOSO_~ zD~18=gv3|9V4D`jkxYnh7jyf=v8{yM%3Z>{Ix8aZ&@lGM9l8>>;A{*J1QNOGc!8sy zU}6%)?Ynjlvw03^18fhsFHs`=VJKQN2&h8D8{a0u6%XAJm^Hs5YAO1vm55>D)Dny@ z{q%X}8Abb<*@6{Q&$)wQQOY3qa!>KH3cRKN=7<@2QX?ab*s7zmXExwPnBz)wbl)a) z(N;U;gNVwW$e30e=SfFi-mXUF{1lZNL9yo$r(75zj--NETN$wZQx7OoEsRELK2>gY z_H;y;3gqK+hLx>?`LACkh(v)pzmTDD7S1e{2N0qS_9YNb${oQb3#Q zl5YJAI33L{H+)g9IhH*vZ=lD3ptPOL2~kFRUDcX+64o#gG~@Q9(wF9tArQEt{%CTX z3S&Jl1%9Pp%xb=+Y;K5KGT4+k_EArMHSL5p6{5({4%w@8-_~J=&M2qjQeW*{Ho*sT z#)Ez7aLPMTR$iYelEAmGUO`bCg{59CPs#x41Zb{x3G*$LpCyoDj2xQi0<9L=N2kt- zS<#~NJ2%@h0ys0ecR(UCeNxmFzK4D@so0cTJ|-(n^t7jtmomo7bfg{>6s_%xZBZTM z$+vEvZsMJ)qu779EI?m9OUdQ+3-lz7tWaU*J;F*dS@{F0TvzH^OTIVLfWFV=e_@6( z#1L+T>^Nrp5aX|_gwaM4p1CCxJYW-XxQu5>I3_v;GoZj8un4X*1gV>aU-Rk&xHT{-USuSBaKF{0hmP~eUl zB2Vy?g$Kl@rnpjPkyH!FZR+K%PktxJ8W2uf+~wv0okSggMZ;z64NgA8(-d=i{DU@Q zgNgs3FkRux_A55ZoA`_&Z`rg!VBCd{UAtCQdQC8VvT!fkMEhiN7Zy-N7*B~{cK6(p zM9_!*3;z$fWyLRos=hxt_7!J>QLG9pHfiMX545*liT#~HKrVQ~Prif&aq0%&mpuga z#+4jx=qZli^0Bv~axMC)5tz25ciUdfGcYA0_g~nZlK2v-aRIPmdBd?pYfYk@ouYC< ztU@hkVD|N&*xIhR7G~Xbt`(`>D4VDt`RuV^&TqIwJm)MEJnmfUD8eGORAQx6eRN7u zfqA{Yxu1z>ve1n6T#K4mW46h~cj(}+H-3R9K%C{Bb&e{!F8A3tj^LsCC;O;qkmi#- zX0*n(L3zQ$eAZv@@>V$_l*8(0WagEHqcGCHk<z4Do`Em zIU-L6yfPRuB=J|F7MHhHoM5-BhP*~E5);tkeJ+~lYhslzE0e%m%u-(Dx?ESAwse~d zd8nU@+ggF4GISrFRHIN16N6(&C6tfw2sfUmNKEy?v#$hg!Az`T%6}FQKafJ%c&CVE zjKM>l)+OL6;K|%o#7!xZgpo%T8F27tXswRWHUD}h-Kq{3+u;o&%iPtVXEahoNfU4h zP-AeY)Vf6WsJUr-&|nHcCTZcyR62c5^(!L_0X$^pq1Ce=_^OU_VCf;8g!q$O6MG9U z?Ge9;U!#6+kCfp_Ii^=cPp{aorJm?un({RIPdFcd9Xx~S;y7fSgc+^{+J}33hP~LM z;U!(G+)bnpKH+j4*g6=J3%II;ay~uc16A;Tk5Fim$#DcG)je!!JG^qtTPQBIg$%xG zn?>F&xK(k#{zGc=t5%9i=yT*G@i|ENPqF8Yj4ICNPC&B1o_}dffd4J!iQW8*ZPxV{ z+e~v`1dCYWZfd$7SriNE4XMNL(!6n7i~C{6M&VNbBv&v;kS47SGLq}az~!LrW;r!; zx%whn;R|Mo_EKYwZ7zjkinJhw@A1gdmJMC;%vIri2Clxil1$E$dR7tz0(L2oDA`n9 zHt|^qFX|BRln|nU&7<(m*Mo6ET<%a)%;IpialtoM9GnM*>p4+&0#rU4?x=sa|6Wj% z^K#y7FlXM=!;bgC+PCkNwhE4)MV7#HXjMRttC-i5GU)nTrn%!b5dTe__~CJ-&W>mb zPIwY~NDG@XqOpj{VFsCPmK`6}zHLviEeyupDvv+4r0RdNQ(ay#QHB z=qZR5za6SjKRQS$dCk^u4|iSN+}6UQxtEm(w-R@^{%iHRfP9KN9&1 z{$`O?b+Z)ewZ_J;F+TOnBnc-qf-HNjrP^jacMKLhmu?x#kyUk;z-3llU9fuQGfhj| z`@u1(Q|185t{r}JoTpX7)2c*u+{D@)lQv~4H(#oSBT`l6S));F0(+yT;tG@0P1T|Z zD#E6`@EdkQS7^MZzSvdHG^k$^n3gdkwmvslwnshQS|N{ZY|aRfF0)r_bb3F-#(sMi zHgn09B@*ApOLdx`x+sm(V^+`v1hQJC&1aw%Y1*qwq~8~BEbmSXD~>Pw)gavJA*N!* zHM{xe(&s4vbTvqf4#Q%|Nop8VKyVs?cm3xK6|zQnrqqbIcZ zUBQ21yZ?Gy*&N8+AOZBb!4bmuu?ED_ketJql4|7|R zf(IRJIl!Ms6cyHEn0`~H;=tUiT?ODyAK6mM)J&&Y6C#(%o|eh8vae>oI!YYA{pc8VR9?@H^W zK4(kDi+tPHFZfZbhw~92gy3EURPA+znb`7tw@=$PlS{GTNduON_wWjz_Cohz&6~`X zZAXM39YTnS*LPg^BKvGb6QUc}Nc1Q?YW6TbvXfxzA)rU>f`W_bd|&E#=gDa$)X_tY zDoptN~{rqBxx1EG%CFokfks;j!nK~Z7|K2)wFV~B(kEHSMcN&1&HfJ zufQX>jEK#bv^FRW>6(f(X6AO~_R;Fdqw+7hq4ARBiSI|EC3Xh^heg+M)NPNrG|iXA z=Q^FsVDG2a-7we3N_#K&L@t^SX@fcxV|oRg;tIp*)DJNx4m)nBm&iPQHD0j|M?(a8 zKF~%+?u&qulYt^Qm49$VI2$=h|kqA>@JhH_6ha`0j<2UUQo@0B-lt8mR3SB-Eir-WNDs9 z?xVi1?d0V}`{ZnNP;GrFPy9{sd*v#b!j z{HY_t&V48$8gHrkloNc-8#qce!|zeLu9|L!^1#R=M7s4Sc$0gM?`KY=8?Ajs`Aixu z&H5lQh=hDZ&(=q-kZp)EFhdt?hKn!E`rv9Tk@S28+CCqhsyN9~B-L^!M?>|H0pGN~ z39lr_+>68vO6mKhXEtDhG&4$%g&?sWhuTwNK_f?aF>GSu!$tZrlO`DJZVrF9lVhIV zN!$4<7dF)}!zt^1mnIJ_SN!&6p#3gZdoaoB9nq>%%AvUzWTaE>x+1wMmbC%X|Et9n zX#dN-IM^ZGzpi~O?3uxjw>L&kFP5Gqx$LmzdRq=JL>(>zjf;GS5-tpqF^3{gbrm=T>GVVZT6Y_y=Z?)08MMqyF-LXO>;-lvSWI)23DeSOo z7nFoMcJc=UyE4W*x)Fpoo?7I$o4%Bt=KpR9Lloj*qxNkL;QWNC z&@-JSAf{b^+ttIX-fngKA)6SDIt?G^)7GZ^@sE2Y;p@lbg3k>e`g4EzPrw^TMr&JR zpcSL|zofgawhor!=0Gdszqgu5Rn1Sj3EI0&YXP)DtyNvgPbj%?FuUXdp`i&bm8)C? z#7DFh8K|PrqR_1$*FvTZ6OUVur_m04oRNiSb}XqB2U#zf7e1!H-rf#|6uy+Z^2kaqlrBgn|`U7rj+2N63X^5>yBPN`Sw}*B3oCY zn)SJyeGfNiwWF5a1WF3t9A_BR7`Ay)TAez6H2%7Xq13;!D5|%7ZqgWvo}k!O0Ha{jf+hSR@`1>Cf{G@AVJq9C!KQ|a8j)6;a~;fuYLC4+YZ{w9~qQeSVH z(MzRQpFS^Ae~y3iJR1#H*e`M73Fob6H~f~YQ!o%i3T?kv1oQmEBAJA!PuO)^?~D-H zP*?CGPZ_d^Q0x!miduH|++!P+IramP@4JR{jHtF#LZDM!Zw@7bfnr(HUYgLM%21iq zE=L@Oiwv3Cop9ISXcJc0cKuJF5*YlOTS+crT7(3SICGH!iV6;ak?vqnkeroy${>50 zW1S;=#4b&rVXqU`zI^&suYC)UXSEZZqXpNArFcu*IjT(C{gp_oWhux9Hi%^UBOY$#4#Vxhhu#4xKQ02_t> zr`9V1fX>_hHV}Xkbjbe`tphT)DAAAh_KT4c{Wj}(eFwmsgo>l7jxvmBvN?nn(4akFD| zwpDVfS!7XGF)HMCY*=McrqDP~Xc%3dgeU~?MSK5yQ<4nPp)q)Cl`Kvpx#EUG{o%%( z+oED!Vcux%21WzoUdEZLo$?Rx|EaXVQJZ@c|EzY8&wDZY|9B7ak7}0#S{wcw=A$C# z_<23%4GNpyjS~}twTmg9pewAApCubsR+30l4n>gTFg5!{g$b%{p$_Xt#o>O79FtM& z>bv`2?8~e@k#C4CYP{<=tp|4}S?d>qU0&cd;S$V;EvOP0uDdP48CtUV06647&Q`3V z4~3*}C?ZP@QP>T|HY!YHzh{sWg{m3aajIRRr4rX6E(jbI%HE*nUYkx?HGqqj%b8!1 zmp8iY0~VV-`)3;w`8QB0s~(;usjB#c?liMzIgP4z^2lBqSM)+t09w6*c6f^iolf|1 zvFw%Vdk8;9&lp^_II{as1C{7kHa6sOFm;uxVlh%4dPW5)3y?D(fIIo$~r){>A?u=SRJ3p`)9 zNYI)e2wVk;+}k8<4s6z0w1HF_jrPM*@sHt z;pS8>L8P7q5*y4l!Z1r+?5k-_^q&CaVs@OLN>~jzePBAhFw6aLad3|er05#QsU((Y zum<=RKLYvG_W3o{mR35yg>0AD`7-rkbBs)5brLI&TzCoJ!Xcl%_Ei&Z--ITp+A;ac z(KLw_Is{N@y7zL1difg%mNvsx%?K2PMHJ9n;8ewvk@JXX_Pyf%fv=B?uSiGTZa&2K zz+OW*U9kP11qt62I>`H3kolhl`Jc!f|DWYo4(McNYy9t|78PsTnNOG`0};MPAbg%< zr*#+tjCSx1xwx+3a8gW;IQh#?v5YGuFgd9qaQsK%$W;GdG~KH;KhUqJE4LY|j#)Q8 z=YK(TsCG3$aL#GtRQ?E|$hf6+LvX|o=jZfHDH==O{~7~dvkd!bP`I$~Z6kfoHtrhP zRO5$9Dw^!szRPMXFz(;)pLBNMf(66AXqk4FWdvEY=!m!dWHQ8$GQ;;dnLqZRv=yCl zh)$T6N&oH%cX0!&Jr0o_udp3M3?TLlQXO~cU)3unZs)O;%$0VcXF&XATK*BsRCI0n z#a~Ztn>OyHlSs;bddvYhU;4%>;{>|u>;+E_uMIb9d?$n_^Lz9>940xAIGu5fqS7E8 z&&$a(iyXl!QPqW}zo{-a?jB#JweyTE*bu%b3O~X56Q6_+P3HV5{2&dK;BmRq=tIR< zP%1j2(}C+d$Fk)0y~JZEj(DfHTpHeKVX!UXD3{OXsY+8uY(CACfIBgAQj?zkbKMAp z-pu}KJQ(9K>ZlNGz=25@@NBE{h+6w&L5=?e_9=DS-KUm4t@&uWeTCudvHGKN#cBW( zxAR#LpZ!C+`JEu6X|N%MM#d%q*W2YA$0S+ZB-K3^^Kk5@+CRiV_Zyp%pw5 zdnf5VyJ$61#O2<-EjJHLJbtIhPP&5sMLdQDGVL9jJ`po-H05t2nNr21|46N>a=D z^~9STB$&W!2HQ9^sDz|TsF);xJgz)Kmpy{u4di9=m(7kgEp1wEDsHXyOx~MuPRE0Z z;g8qJmxr%-{hKIUUwEUu?F{WuL$>nVbfmXfs>rC`{WLnT`Y??il|i7FXsW_47|Y$H zync}Ta&@T9afp02a$)Rs5Us*pJ?M zWcJD1DelGheurD^KOO+9+SP*N40yH_zDjd@)oFhhuJEBI;0!#jHhgUKTk4$+(yj}G zd8SCZMdw#vpbh6<)u6%!=R7#WSE_OEuSl`c>#6FAoM$mb6OGmu6X{8Ut~ZojNIHP` z6whWF4#t{sJf!sfi*~+03>< zm95*TQCbEyHo|B5^^eGa=O3p50taq*6n;v^M=19+6;T!Gu2#^^+J?v!s8c8I;h|#JR#T@=?*AlhZ0S5(k2?p+f0Of9kRUOR-3G@j}T~Sm!`dFlFK9G z-RTN=>!OxrN17O58cE%S`-{g1!p;}0wBQ?&R)*y-Wg8)izQ>Cki#uBQtC~er6kt|{ zi(g?)+PT&2_Kq+ihmA~f>1x=F)QB1Cl^g*nOgz)IkRXsPeL|1uj^syZPlmq--D}M^ zg*EzZhj@jmXqbApSMWx$MYTFK7X#xl43;GU#&yl$lEdHtL`!37)x_;bw>X9=2x{o-Wlm2vBaz(OA7!{-++w` zL597Z_Vfe?aD_LXrV3aJGi)i}rkfbJBv>^FPF=X{ggWHgXIty5$AP0W(-4*FGnaJ@ zA}uRA0`u#np6wodl0ERQt|yilgR<`>rApD{<;1CyZP%;BL1(x}Y;rkWaNk#lV^&+d z7R`NMWg;&&zM&(Q>X1}OOL+xM>oCDHFFdcr+_)|WLJr}=-v%gAoq%X3g;j)cygAB9 zk_{at=u76IXK`?yykaEjsN5H+JDIo~lTli8Wl+Q>0|50$5$YP_i6FVHa;9_k{ZxBG z(aQ@g_m>hYUqixI=|7QSQdNjMNZEwjt5~g%gp$VUxu0>eVvE{dC%wDMti7g;c}pYu zY0+EIooAN~S4w=)!}CKSzhgs+?UshdMqIzE+~Yf~LvL}&Y}-KRyTb^K$#D50TR`tZ zO9!t0;TJWj7wIbxS*WFsh>N8%S70TI?^pV>=Yv8xL}|wS+c_c5=(UFT2@LTGOiidJ zYTV$Ka=~8J+XP2&MDs1KuaA;A&%1y? zVE4U+BvQpf+HYG)D=s(#Wqt5%3ToRFe;$X#Jzr+p(*!VF!olvbJu{6Pdt*#)!+rE} zhcDV8e00;jgX?*WjpPrr_k+xj26b1S?Su82$D9FiSQx;~;d5TxA*FKDo2 zP$q9CPL>7?+Ht|f75|X4YEXeEEeR`r_q?Ps!SYT%g@{zfHDbt0SPYmQHT7D!K!4M& zjV>U(S$fBRNwH%*^0#Db-?#TQXJEx1JxP=Ju~m>QH6cTp;e2*sVgf(imhQ6J_QcJ1 zvN`Ua{paV{7Q7YVb$MabsdIl`Gg9Ol!td_B_PVI`Z{0RuvQ2Rkon2)mvkrdq%4ZV3 zPh7nPxs`8cIW)rCA{I+xQ+mBT2KRDRH$-L&B6txLzdr8vW)-t}QKFrepZG;W_6!;X z`Ju-Z5-q{5=U1%PDO{UviQ@ghf9WfptM`ous*Xg{- zxEMRl?h^C?)fh5Dg^-paQl+kC+ErQ0)f{c7QEl*LB7qT(Ae;;!k>aye0T{ ze3w+`Uqo!dWpiglvxgHHjB(SCpNSVBKGA*mT{YOcJ#F2E#DyCPnzeUFlJ2QtLEgYy z;y-se&E!Aw{!^K?v_z6nd(__%Tdhe*5T7g@k z3#ooPu#P^9TiVrNuzLZGC6Ieff!LpJeGRS*jG*8WDOz=-5S6-7!P-7&Sk+9E9u_W< zRR>F?p}-`LA8;!w?IXDZe@<%;H!!ox>|3p+J6-g^#*MX>BiNHujNhY{!i5-=&@?kl zZEmbT=Yqh@6OY0(8GitOhP)zIj%cFRN$emBKy%k(oI>awfJ!7A1J+&pTcO#BeMnQx ztDG(#X5@KqjYJ;nF3w|^sm*7RR$Xb*-os9>n-yN+*9z=N@Nia7BkPij%txI7CGP4p z`cZ1B7xsuPBY9Lc%}*#J)};wU1t#Q_8I(O`j1rEJfoVamDk>YjXWJT9)}Zz$v?TiC zszELo?Bn08y(C!`waGT|y|teHVSCfgY{mK)3ulQf97W0Tpv|!l)=jjVlWjA@q$6M1}N1F*At4Ox;rk5T+DrI&H2OGjh$h z?hPe;O}v=z5I=r)3433RA)%l=&x!4Ca6e=`v>)tdjbwkk-lKHC)To)Is9vUqVNpbH z(aV;4tpS)Sx1wOcU03Z8Sz)9rQ`9bhg>jFSKZ3y0sIFlKhxhLIy78e!OTZA69}X6{ zJt?1>Rmv!GI4{RX-LbGGhBE^~T2ivN-V>U?;m>%#I4EPO_=quhwoo zh%}Z6&u@^5NNGBXmNpxK8=ClmIk2SX)dp< zjmo;RTJ+B*CtpB-CrmBU-HKy|j!O!9O18-1n^)Ct^ReH1IdfhOSPW$cKwzpvVnCA zc0uvbn^mjJOZtd0EsWOBX8N@!{P{?Vr{BR@co zyK7=se!_5A(>*;EK6`raJ*M5-Jcyi^3u$Y4DAE;BMX?x&>9ARC{t{$)0&pf+v-?cc zWahwMEW}C$45Tl@CaUOjIU~&q6i?eH_+fgX|R58%hAGu}yGPW+1 z^;~v}th2R-FZdmGc2aa!D7(p8k(h8^c&l1!_$=;}AAKQVZ)%o7|QvU8sIB{ z3CZ+9z`?s`EZSicT0VXuJO}12-aKG8I!IQ(WZIl!lNv`$fJI`j!ruheSsD&d1 zDdM$EUU`2>esG(=EWU$hPGxB%&HvLzCsE6QnHv#rI$+P~#M2gM-k@Q%^9$RiF0>Mg z3K@aw2~N-!M=I2ul=k|Gfjr-A8-Xip-8W%f%*p_gUQon`z^wQY zZBuUx&$z%oh?jnD$de0pIL=JRA|qdRUy9^9eqR7J<1=9Usv^)*g5v??>TI=55yQcs z*K8f3dxuR%QK-cd5v}^N<6GH4H!zGPP!u8te%L8s!e1A`zaOg%1vPHlqQzWb*`NWF zjYbst{^(R8#<6Qh5>9Yyj$tlvg5$~643@sYwc!?a#ntXq%_hC9v7l;)s*1U&_UBwE zQkgXQxFFq~YJjNX@@OPZY-=}VI{)u+Jayj!v4W@}rjPbx?y+~S6}$Sua==~L_RiO8 z;kxXfGHben5=tPQ#bgtW>T|h90;4ES^5Nc#Q!YXX9Q@a`G~`gTVgn3h{*4^)4{ki8 zH@_^bp)|SXJLV!ThYXMk_id{xbzs{LP4p>j4~l~I z^y0J`2I8*@UP&hq$agVeLah{bz~#{r2=)+5{P};wW@LOYFz~-TRQJ50WSN}#itX@k zy%X~b8W!`JFcUEGdmxR%DNHigDR~K>&AvLiCDxJ++oS|CB9&(1a|NCJS?js>`JE|g znv3I29FnSUx}!H3N#zlnk7S)oJI6N8#?=fzRJK4@1)4ujW0OkLHl{Ppnwny zw@}@uhkX4QlH?<<>5lFEhdvw$y+q;tU-Q^cxdG+>GM80xGH?P~e+tF_KHvn#)XMk$ z>n~c)76eQsB@vcr{QMe@JqO-{)-zSshFY$($@4lL3^aam_^Q-VYJa{ z<%JN7OlM-@;*0zGxtXL+}Q66Xz%MLEfne|qqI54jsUIdgxP&CK-)C?wauT~;}zWJ?wdfC z<*^1zrqgnqaU~f*ggw9fFw>17dpsPyx&53k{k>O%%wX)88&!U!7CQR!qirO z8Y_upCM**D%hieK>B-sTWI(K`3tJRdf|8a3629=~Oucl@zP!)<$T?zdD^H}O4o%Zv zMdan>y_xXjyWpK1>wK?1nEcX$^KqFED`D)l6pe#dIv|aQJ}x18qa=uP853b7fcMZB z9Q=moRe%tFRmbFq-m86@TSYllh_H z@ipVnThHv-&rE)98{121F!@#EC+W_v=5$5^Gr5tL+hazAis5y@)N4Cp@xaTdPy0_+ z7CkgV1?WUZXn)FqgHe`FlHGCvukmPkKDyl#7QNUl{N)(sdQIp^@3#343khktXqOa` za`HI@a*5fp*jaYy@xq(+SavjX?4!kq+A+H(%KU1XV%8uFGFJ0r%bGuq;wSz$SA zmAit+Cdjw6G^~1(_$jRd#YJ&tt=_|${31QI#bn8rog!G6!Und$0zGJUDHn@Ck6BOX z9vIllZnIx;3$F#G{R~9n%ZTffPiL6xTUp z)FOZaxu2^Q2#7>dXMBm%cY87-#PSPGp9qtka8I?4MP40cms0$@6L z3CcMTJk?UFWjLv4I6PHZPmvl8($h8k&|QG;Jh(7S%Yl{2i#qHzNyqOBZV?HJcz$AL zDEF_Md?T}3*h%*Shj7WKNftTYqI1GRHYZie=GL85BOrXmdm?`IGznKL)pr+X{%tBdK#!`yJMO6VEse23tRvj~|$|0@z_C)0u z8ykp;wWRV$Vo0XWf0Jne$PYBDrq1Xy>fSQ&_4Kw^rj!LNbTN#p2)-vmJB5n{!SSaO zG}8XC8iiXM9taEb4w-t%zjtt=@O1LO0PP+9}9r$n0Y zA=$%WrG9DjD%v}9e9gGHC2643!+0qJj*XP6zrC2pO}onp;}c#_ z76yY?xaIoT;U~BiE$VGz;DnUQY89gckFivFE)N+(lRb@+3=Bx4^oNK8E{s z?bxDV=oDz7onl$)MgE^%xpwh*zT$l#9HSDIa?ji-9&ZvU|hSzY027HJ*aEOwHF zj|{n(5{TW@vEb?$gDNdLKasS<>>xmmo{8+i)zVmV?H8&ufOw)$8{tnpVnkv%(9fMn zZm7NxW7G4fw51^=Z;mcq{%YN|wl(IiD|-`-0xp()s@N)I{mRuiX(31 z_SH?4#YG`^6|b2T28&i-JDl5h4^_)bOibfDdQms(23{AXOEf`YYf*H+=$N4-z(Cvn zQHMA5l5CoINPn!Le(pqod9kdGdb|61OSX1tzrQ!kRxoTncbXD(LbeuIMTx=>ZF`H} zAX-e6Z7&$5S-ibEt;!4k5gt2Se<`1Tj)n&PW?jrA)60AcEY|ClF~c&;?gcWtPI(uN z?jB!5w11s6(!g?pY|ms)<-l@v-xwE9rJwGX6258n1#EGqWhKqLWwz zHvg&Ow-3*+b#mzbebR>S##Ee?wW@M-rHlKg|GYW&=JA(97GH0fQ-AIHRo4%zYIz=< zy}HejD|_atJFYvrGVfHh)4<2g&ySxuVCB-=vK-~qnafsOf3;$1N?=4a-)_&6ZuwUU zd=go_rNg|m0r}fAyz3+_D{Ng*YtH-jg>6bY&zU{)vVZ!Q!{QphU-QH5ur=x{MQ|dV*7L`JeFEq;BhMd@##n$geVy?f6>j?Pdkdg$zC!jb@bqxg+(hy zOuZMMmbEIUy!>u%i`E+PzNLdAj&>X|bn6Jkul03BE=A4cZ)mL8&{v zSMQAO@7Oe2_g(6F_Xl_W_4q@6@cvDYl3orsM)<`vd?U$A`tnchDfLHLtC1Ib-yGTH z?VxUxBERYWL7w?Cdy{T<(yp)-_s&hMm)X_#?Xsi8hr9@%^kep!d)4Onw=DYV(Ky|) z{H*=HzvbN=JL6HJ!=b>~#)mh#hic2W-q*L+rrhpXu8Ze)^UB%R=;l9hc|&?v+%|s1 zwB6D-ju%(>>oP9({?J(7XjNOutj=BYmN(m9;mOY11MBbqDo55Q{nq{T)!FyG0@wa} z{`b*)7i`N9Dmt*wW5$kzMZTB+9F{yTtlH*zuROhcPBw0LCds*blJmJpXWwg1@jC*f z?Wz_wo~~+_*tq4V>+&26Kg%DZ&b;9J{F6s}Q?p$Y!uOo^ITF1(b5*G^xpEi?iUW!(fpXP$|L>Ot^zlwd2dP_UyI&6 znsnahw`|vy-9vjcc01IhOZP8yT^}r)^6FXVnys_uK72i?xzBG6YPe53vG7#XZRcIL z6**tVKM$Cl_by{_;;w?+M)fZYiI^)pP>rIVAXQ`e1g$6eq!M`|UYro4!m9=a zAFXNPkRo2@J{qG?Nd%v-Lp@&+sc}lQRE}3m%EdB4hQU<{i0I&1%CPMlbt%~+spllg zO1#sOOiOJDZ%RQH@k3%T%^jssNhQJ_*O1~};pZiYlLf8PgaTE=J4c9R4OXE_hdNG_ zX@s?+VSNN(de;_)Ia8XY^20Ro659rALD6#TpxNbAEh%hOG%6nRnUqu~DwQ&+L?vAK zS`_zdiffl*jVQo$HXz!Nh!<9cCjG`HC5Sc1LD?u2O?t~FsU%|)5l)14qTRnURvye| zH<`x}KZQXql$jEQL53ZILLC8lKa1faF=mr6PHjwS39CrE!c&TAm)Yswi5w>}8%u?( zp|E(mYwXW?ntVJ=FussiGMgAbST0p2%JgfPWnk@Pv8eC)3Dp$cBfVs>gw0aV zSS)>S(i|J8ER9OcVqEhwYf)K5I$?E1mlZCEKZU4CESH%sopvZFp&$4qwQXZPPs}YD_opwK*(TX`xqLiT9izT|Uf!E(} zlasFsRsqnB0&YR`umTcLpai1|vBtO}&Mo$dBP)c9T{kL9+c zj*03Az1qTh=FW)w1+zm-9r`2$+#92N*A%_C2)cs%yp)eG^p@wl%Bx_pd$yE!+Xl zM?)1@Tj4?tuZt%xu+IS+8rFa3y}lFYDus-MjXpy;*Mw2Z!d+K zhj69lb@t#SNu!{^YM7hb3! zFU(!cJrPl>lOLNy3d%NJ`?F&^h9O#bB1+Rm{J9sKCFqnpWgA{zLct=@IGdUZNL2Ie z!^VY5)d=o<{&*>O-21~|1y}{NKA~*hK>diG&j&XiiLV`uqSnO#z?sr7w4V@~Gfz5*@s>mh;MlrddU)r&6H4idfJp1M zJkAI!os0X!JBYU#mR;Gn70bl`I-#h?anluBFph&OMG04IK~#&TDPU<}I=U^iOX2V$ zG3r3@X6tqJ`Lr`W=;ViB=tEcNo$*FaY2Lu#83R=Mp){K|)H!a%0>u8R=q<^~WaFjN z4w=fK2^owGhz@sIxMd(bZ7UR`4dyY&Fsw4=7>NA?CF&TJRHaUSV@)+gLOem zpd0;O#1t}2qj-G{Qy1%E5|;F{tAi&YR#rgQK|9*CB^*`&k{G3|(!=nUeI|kZkS7|{ z0*X}tkXEecN)F6sAvaZ*dpv;ACxS#lZ&ecwb<>*$v{4ftHQN~ly zYcE^J&IC(|g3_$<$JnfJ9M)wUZCa&z>fvxP1bYsv#|aioDUKN<9wliTuSt+`BiXMf zyD4@-uR_!)J^Ih%vP|h1Kb1`)W7DP6PW_$Y2uwof)o=X#Uzpw>O@d}Lyu)E6vfP*Gk+vbE+pm{W+vtnmfb3<0BrB!2s7F8oO8gkgrsBm^|O z&=O=dL3K0#7aKESo$rauk+9h|G%*)Sgc8+iPBxi=g42y1<@c`p9K}9jWxh&}EpZ{E z5Tdt;n2bTnwA=u=(aSB0T*Tk){J>-S#gm3Dgn@`F@s6CXo@7C!*oGb>N#k zJ-gUt=Ov&Yp*x`i+2nu9fM@G|U|zw??tnX@D@Fdobm_D|zOft5MrU?9ozoGG^giZR zekL&SYxfVlXp1GQ0MqJ~Tj`%E!BQD-j5_zE+fUA@$2YkBDRFzSh-&t6sutMdf|6ro ziLnx+RCe)dyt?kfmgiZijPgR{cF4O^+#swIYN`Tisv}*#Ps%2bU7ocQBG0}9_j}l{ zJ;m?#X&Ly8sR4<&n=+1kc|L8)g;-lb1ehKn!h3m7qy=`^ap&Zwhw%dp<_& zvk=2PDDLJO_HfzMNs_1u8$ctyHr=kLHL^wh3b`T7@%Z9Vz^}6D&0&Oz$SL=r6dwPD zz3741eobD9fg!$d!b1B;gl{j@r3Ar`_L~nb_6j2%fT3q&j~0pFEKb)QzKCEor@G}&#`W>iUYMAOO`kBa;K2fzrxt<&GR3A# zr>)T5CS9MCA$e0niiRy=HeSU^CeM-3pB|n3)ybv=1K60z;BzRa15wBCL&RMSf@l#P zyOvc%=@(2a=NP1Vqf6F1{0b3MtwhMY%POK1+fX?UPP5uT!~%%uWU2_#-6eD{qX@Qu zk3CkVEkJ1VMSasv`autV0i~NGg5)tWg<4Yjss*ljI`3KA8v}U}%*`2MobuyEn|2`t ziZ$W^3QVHddZ*0#LQay?UVzgr;J$wu;Edb?so5K?TT~{?CbN<7^Q6pPInXxU^cB-^ z=_t8a!@KJ9u)wDRnyaV}Dwv+{`S&S{l+yhgY(w=|cyC81B|3wfj&(;v%OFG@Zjch| z&(jy5Zu((D7ZjutJhdBTG7qfo@$|)}+8%)A7CAX7jFmzdjqF$AYuFF>?MOi-h$0|t z5G5#Uz<(2D91Zj$YZn+F>Zj;7h6EQs?It<}E%MNAyeKV{1OJm2?AqHr_+UgEh**!F zitZNYM%We;QM&KQP5(!VpFAdBAuuQS>PVIGp6G*hm@{^#%G6-ce-_3r*VrwdKRcsb z?&xA@QJb+x(3ZEb%f%C=FP-V;o(wU?n3HoHbL4*%#V*r)b@1d=1dYX*nzW_N_a@$c zL2UM?!`%nG26HT2jvj%YQ`pJoX}8Klx~%;mHvu_-(z-f?*{TUsk_*Xb!~4jLfdYQU zE{Zbq?_*O?^}eW+NJ?O9)&D2Z@ct=N^ySI4>Erv1DML;|e(vYoS!C0s|4RpUk>xS@ zy_-W^Z-}E~a0rrL_S@JL8+p2|26F?eg`oilU=ZD#GVKX$pS4?#$%5o7-G;3jSjrOE zYVo^&8}DlHpR2g(?(#e2QEbUI?>8i4YDsK=p5?*BoZF{bz;U7w3wu(fE13OXq?Gn( zJi8}W6wFxm7}9!LB}m51vy^6T0VI2DXRfQC*Y0CvGI;`UdW4<1hz(b(6BG0^)JUlU z8_}@yMR>~kY}EN)Br(?$uF#4y*8F8m31-a$RCsf?N-Adi@^bI9#TYn<`lI`$2VOs{ zW`cu~vASn3J>ZM@w||4~5984Jh~^ngdWc+77Jj3D+U{8abL_$GMww;A8YW-A*Csp# zewo0w$B4@NE`~$nzOaWYMf6$ABqHY^69Q&GcaapK8+n9oq!|Uw)iHq)*h()v-<9NL zebN!mAVNlIuh7iE3+#O38VGfa133)poD!_+U0D9 zgr{g*^ic2wUy= zhD+T}U20hZn?FH?Hm9hkPS~chW7d@ZqD|8l8GY-h2$KT^r?~qfE2kZO{{= z)p`MfwfU^W`aNVaG#2AFdi~`NP6r9uC?Z8E2^Yr+J4}vf-Q00QZC9bo9NKFANV?s5EdE zL3+fE6Y0yKwzdj)p5yl?n_ilVGFU|+j+OGP$ho&1HJ~QQC79gO`9Qb(f@J-;7aPD! zwb#pI+kN(eRl!~0@bEuQc%1oAPz1@yqSF&t7xr5aI6MtjuZI%Qy^`Z&K{W3R*torR zcI`$}HzB@|vw^gI3ZDu}Fmy_u7Tq3SDEq1C>)6$Bo1LiK0aPd}cve;s{bl;i_G|LMyzN@rJpwK}4VZLr=r#5=l>4&}<~Vr4 z1uHMuT&_GTCK(SH8Txih2|US?^dmLZaTp~w#h!jTswUw~BO~hZN?_aQ>Xo`T@4`lZ zz(#abZH?0t0(?UUKTzMm`IplU4H}si1P>dCp(h>rhSewoo~`@-Pu4xwKzDL(goZ!) z+fDGO=3)mLX%uNJ{~A|ld`5x- zwMq7+(V3~gJp}0%<2JV5>pfqWivac3XWX^XHLV57`lUIUSj{tX_*v8YVho(5$ZBfr z+u0`9;D>C5k3?trZigwJAx6Du{|;hB&uUwS{jG*muw;9M|DYxfd_t% z63`jpXV^PtQ%GM-<)lg(j!Ni{qu9tI)Q?@`m2!V8#ds`1XgbkF$>Bovm*!3Wj z@gJs`(y)ElNEq$_hURpNcD|pW7>fgnK{)6fqp?w*Wiz*>hoL;JP#${l(kIAXLBw+G zs&SPh^{W6fnE=qmD8?GUK-E|jgV6iSBXD9{k;uCQ^4-|L{6nx;FdU7pmf(>8B#2#} z`_~RV!C+K04Wj62!cb&8>?==*JV`M|BGhU;`Y$UXEB_l&7<%9_3ppnHq+q(O-)P9w z;(r-5+$$Fq^923om(;+cB36;?7GW5m@yLkkHDT{(FjRl^p7cD;Bg#^SX;!K4lS`){ zJj{!~eB4hm1x;WcnmKWZCDU+5RzFZJot}|p9(JV;=;Tx+fu55PS=}gGlIiwXFQ&c!F&zThpAz6HjJBLnm;ZDNX-YSTjq9W4zlam#jJ5eF?^yK zMLVaorbS2+FufA1Qi2uc*Y0vBC;@*x2vkD3-7qJ;)BFbMT{tZnQ zO#w%afIv3_&m=Z0iX?+@4xG}qA>9xpok0mc?-;B@o}j-06iI5zA;RM@6e=(H0z zU7-`EGhn61Uiim2f4!SDCmAEot5}Anc>yzcc>;S9Psm&&SAzE%Ji1oj&1LZPy$#um z$S&!aNL0yYcvPYcyg!==7$2`*SnTD@Hj z)Iy->Dc-l({$_=u-j-p=DDu@u$^E-^-hi5gs7aH(x7tbOnNMHY-LehFNTT{6w=`BW zes$V!ci2Z}oAttB+2SqW)POzcyls;#`?&leqIi7oC)o8X!b&7(n?M6}dct<@ryxC^ z8e3qIlD9Dna`RK|NJAO+;-;;x-D67*N#gRC;N*>_TC&mX7qkbO{B^cHWO8DGZTAUT z3zl3$eU3s{q3d(>KKsaQ?*1rE@-S!{j^Qx9PCo4*gNyY^^p4ozkM)msrE_RDSQFv5 zh4hnuqHm+KMXiq50;4es$D9Q1`h>TD?*g1oqxl`P18&SP@ELo1C2e{o82gqLJ}sv# zc%HVI|0dm&;QzA1=btPKz7yN>VFeF&iGcrTh3|N#EcniB{I=Xzsg;2zUz4D1uRT{5 zd>1zUMPr981aEDe>ley`|B{WLblbEL7Na>GMsxB(crji& z?exoK!ISkfqnENJ)m!((XC(?zT!q`d{H~Wl5*vPQWtVYvNF2jWZ`f*i+ZH@rqfjcb znv?Q1-g#pq5+}DRZbtU9R!5CE8*PPOopx!ywH$RjY{$@tdA8P?&JoMV7Oc6b06M$X z;0}xCFH^*fv0fVBiNG6b-u0S_RICF6JKgj7-nF03*1X5bCZWBcxhw2I4}7Y=V6hEK zvsn-JbdIP87THEbTVZYqi$(2rb2I%LPfDiE$QPS}(DHY|?fj`4d-;!TI;lL1RhVFf z%9gyDD~HBEguJ6wxfAIX?`+Cr8^}Ma!PmJk&QEX?dJufNTm`FP23|5IR$)7~VSvcj zW3-&PC-f|s2d$X-6>Ktjx(Sh2)`?;;TcVZIdOogXlgZOJd>AjQ778bOX~k?**(Q^x zL-^-f^Mh(sPlhWunXu_oTTFHhWp4}dz!3>iFK9{+cEt5qM8lhC^#9a*mxRZ8Uzhk? zb-@725%V&7s#08^m&;a>`)zJxQ_yLHv9FsuRyDq9F4*|G* Date: Sun, 26 Jan 2020 14:47:07 -0700 Subject: [PATCH 062/114] Fix invalid characters (brackets) in Javadocs (#122) --- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 2 +- src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 42c612e..862c857 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -211,7 +211,7 @@ public void setWrapColumn (int wrapColumn) { emitterConfig.setWrapColumn(wrapColumn); } - /** If false, tags will never be surrounded by angle brackets (eg, "!"). Default is false. */ + /** If false, tags will never be surrounded by angle brackets (eg, "!<java.util.LinkedList>"). Default is false. */ public void setUseVerbatimTags (boolean useVerbatimTags) { emitterConfig.setUseVerbatimTags(useVerbatimTags); } diff --git a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java index be73ae5..174fd7f 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java @@ -50,7 +50,7 @@ public void setWrapColumn (int wrapColumn) { this.wrapColumn = wrapColumn; } - /** If false, tags will never be surrounded by angle brackets (eg, "!"). Default is true. */ + /** If false, tags will never be surrounded by angle brackets (eg, "!<java.util.LinkedList>"). Default is true. */ public void setUseVerbatimTags (boolean useVerbatimTags) { this.useVerbatimTags = useVerbatimTags; } From 98cd178cda54d32159a1b61c92795846f8072bde Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 12 Feb 2020 12:56:14 +0100 Subject: [PATCH 063/114] Fixed processing urlencoded tag. closes #123 --- .../yamlbeans/tokenizer/Tokenizer.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index a2010c3..5eb35a2 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -310,8 +310,8 @@ private Token fetchMoreTokens () { } if (BEG.matcher(prefix(2)).find()) return fetchPlain(); if (ch == '\t') throw new TokenizerException("Tabs cannot be used for indentation."); - throw new TokenizerException("While scanning for the next token, a character that cannot begin a token was found: " - + ch(ch)); + throw new TokenizerException( + "While scanning for the next token, a character that cannot begin a token was found: " + ch(ch)); } private int nextPossibleSimpleKey () { @@ -568,14 +568,12 @@ private String scanDirectiveName () { length++; ch = peek(length); } - if (zlen) - throw new TokenizerException("While scanning for a directive name, expected an alpha or numeric character but found: " - + ch(ch)); + if (zlen) throw new TokenizerException( + "While scanning for a directive name, expected an alpha or numeric character but found: " + ch(ch)); String value = prefixForward(length); // forward(length); - if (NULL_BL_LINEBR.indexOf(peek()) == -1) - throw new TokenizerException("While scanning for a directive name, expected an alpha or numeric character but found: " - + ch(ch)); + if (NULL_BL_LINEBR.indexOf(peek()) == -1) throw new TokenizerException( + "While scanning for a directive name, expected an alpha or numeric character but found: " + ch(ch)); return value; } @@ -656,9 +654,8 @@ private Token scanAnchor (Token tok) { throw new TokenizerException("While scanning an " + name + ", a non-alpha, non-numeric character was found."); String value = prefixForward(length); // forward(length); - if (NON_ALPHA_OR_NUM.indexOf(peek()) == -1) - throw new TokenizerException("While scanning an " + name + ", expected an alpha or numeric character but found: " - + ch(peek())); + if (NON_ALPHA_OR_NUM.indexOf(peek()) == -1) throw new TokenizerException( + "While scanning an " + name + ", expected an alpha or numeric character but found: " + ch(peek())); if (tok instanceof AnchorToken) ((AnchorToken)tok).setInstanceName(value); else @@ -767,16 +764,14 @@ private Object[] scanBlockScalarIndicators () { ch = peek(); if (Character.isDigit(ch)) { increment = Integer.parseInt(("" + ch)); - if (increment == 0) - throw new TokenizerException( - "While scanning a black scaler, expected indentation indicator between 1 and 9 but found: 0"); + if (increment == 0) throw new TokenizerException( + "While scanning a black scaler, expected indentation indicator between 1 and 9 but found: 0"); forward(); } } else if (Character.isDigit(ch)) { increment = Integer.parseInt(("" + ch)); - if (increment == 0) - throw new TokenizerException( - "While scanning a black scaler, expected indentation indicator between 1 and 9 but found: 0"); + if (increment == 0) throw new TokenizerException( + "While scanning a black scaler, expected indentation indicator between 1 and 9 but found: 0"); forward(); ch = peek(); if (ch == '-' || ch == '+') { @@ -784,9 +779,8 @@ private Object[] scanBlockScalarIndicators () { forward(); } } - if (NULL_BL_LINEBR.indexOf(peek()) == -1) - throw new TokenizerException("While scanning a block scalar, expected chomping or indentation indicators but found: " - + ch(peek())); + if (NULL_BL_LINEBR.indexOf(peek()) == -1) throw new TokenizerException( + "While scanning a block scalar, expected chomping or indentation indicators but found: " + ch(peek())); return new Object[] {Boolean.valueOf(chomping), increment}; } @@ -1033,7 +1027,7 @@ private String scanUriEscapes (String name) { while (peek() == '%') { forward(); try { - bytes.append(Integer.parseInt(prefix(2), 16)); + bytes.append(Character.toChars(Integer.parseInt(prefix(2), 16))); } catch (NumberFormatException nfe) { throw new TokenizerException("While scanning a " + name + ", expected a URI escape sequence of 2 hexadecimal numbers but found: " + ch(peek(1)) + " and " + ch(peek(2))); From 000efea2db4f78a1828612d1f95150290a54e268 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 27 Apr 2020 20:10:18 +0800 Subject: [PATCH 064/114] Special String include colon and bracket, the program hang while yaml read (#124) --- src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index 5eb35a2..bdd76e0 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -51,7 +51,7 @@ public class Tokenizer { private final static Pattern NON_PRINTABLE = Pattern.compile("[^\u0009\n\r\u0020-\u007E\u0085\u00A0-\u00FF]"); private final static Pattern NOT_HEXA = Pattern.compile("[^0-9A-Fa-f]"); private final static Pattern NON_ALPHA = Pattern.compile("[^-0-9A-Za-z_]"); - private final static Pattern R_FLOWZERO = Pattern.compile("[\0 \t\r\n\u0085]|(:[\0 \t\r\n\u0028])"); + private final static Pattern R_FLOWZERO = Pattern.compile("[\0 \t\r\n\u0085]|(:[\0 \t\r\n\u0085])"); private final static Pattern R_FLOWNONZERO = Pattern.compile("[\0 \t\r\n\u0085\\[\\]{},:?]"); private final static Pattern END_OR_START = Pattern.compile("^(---|\\.\\.\\.)[\0 \t\r\n\u0085]$"); private final static Pattern ENDING = Pattern.compile("^---[\0 \t\r\n\u0085]$"); From 9251483a830600741bf5cb29c71a7fbc00ecce7b Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 28 Apr 2020 16:37:50 +0800 Subject: [PATCH 065/114] YamlConfig.readConfig.guessNumberTypes is ture, value is a numeric String, converted to Long or Double (#126) --- .../yamlbeans/YamlReader.java | 37 ++++++++++++------ .../yamlbeans/YamlReaderTest.java | 38 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 2740c9b..faa97c0 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -203,19 +203,13 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (config.readConfig.guessNumberTypes) { String value = ((ScalarEvent)event).value; if (value != null) { - try { - Integer convertedValue = Integer.decode(value); - if (anchor != null) anchors.put(anchor, convertedValue); + Number number = valueConvertedNumber(value); + if (number != null) { + if (anchor != null) { + anchors.put(anchor, number); + } parser.getNextEvent(); - return convertedValue; - } catch (NumberFormatException ex) { - } - try { - Float convertedValue = Float.valueOf(value); - if (anchor != null) anchors.put(anchor, convertedValue); - parser.getNextEvent(); - return convertedValue; - } catch (NumberFormatException ex) { + return number; } } } @@ -473,6 +467,25 @@ public YamlReaderException (String message) { } } + private Number valueConvertedNumber(String value) { + + Number number = null; + if (value.matches("-?[0-9]+.?[0-9]*")) { + if (value.contains(".")) { + try { + number = Double.parseDouble(value); + } catch (NumberFormatException e) { + } + } else { + try { + number = Long.parseLong(value); + } catch (NumberFormatException e) { + } + } + } + return number; + } + public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 0b76d9c..b8059ce 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -17,6 +17,8 @@ package com.esotericsoftware.yamlbeans; import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -447,4 +449,40 @@ public void testIgnoreTypeTagsEmbedded () throws YamlException { assertEquals(1, lake.fish.size()); assertEquals("Walleye", lake.fish.get(0).species); } + + /** + * issue #105 + * + * @throws YamlException + */ + public void testGuessNumberTypes() throws YamlException { + + long invoice = 3484312312313131l; + Map map = new HashMap(); + map.put("invoice", invoice); + map.put("date", "2001-01-23"); + + YamlConfig config = new YamlConfig(); + config.readConfig.guessNumberTypes = true; + StringWriter sw = new StringWriter(); + YamlWriter writer = new YamlWriter(sw, config); + writer.write(map); + writer.close(); + YamlReader reader = new YamlReader(sw.toString(), config); + Map result = (Map) reader.read(); + long invoiceValue = (Long) result.get("invoice"); + assertEquals(invoice, invoiceValue); + + Double money = 123.456; + map.put("money", money); + + writer = new YamlWriter(sw, config); + writer.write(map); + writer.close(); + + reader = new YamlReader(sw.toString(), config); + result = (Map) reader.read(); + double moneyValue = (Double) result.get("money"); + assertEquals(money, moneyValue); + } } From e10bc344ee77d009b8f199c3edac3503397f2a75 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 11 May 2020 16:04:03 +0800 Subject: [PATCH 066/114] change the version to 1.15-SNAPSHOT and modify profiles (#127) --- pom.xml | 104 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 44 deletions(-) diff --git a/pom.xml b/pom.xml index 67435c0..25d97d3 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.14 + 1.15-SNAPSHOT Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans @@ -136,49 +136,65 @@ - - release-sign-artifacts - - - performRelease - true - - - - - - org.nuiton - helper-maven-plugin - - - get-pgp-passphrase - - share-server-secret - - verify - - ${gpg.serverId} - gpg.keyname - gpg.passphrase - - - - - - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - - - - + + release-sign-artifacts + + + nexus-release + https://oss.sonatype.org/content/repositories/snapshots/ + + + nexus-release + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + package + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + From 5a62a5054c7a688fcda8e5264378b58afae98f90 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 12 May 2020 15:17:38 +0800 Subject: [PATCH 067/114] resolves #52 (#128) --- .../yamlbeans/YamlWriter.java | 5 +- .../issues/issue52/YamlEnumTest.java | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue52/YamlEnumTest.java diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 3643fe7..660abf1 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -151,8 +151,9 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (unknownType) fieldClass = valueClass; if (object instanceof Enum) { - emitter.emit( - new ScalarEvent(null, null, new boolean[] {true, true}, ((Enum)object).name(), this.config.writeConfig.quote.c)); + emitter.emit(new ScalarEvent(null, object.getClass().getName(), + new boolean[] { object.getClass().equals(fieldClass), object.getClass().equals(fieldClass) }, + ((Enum) object).name(), this.config.writeConfig.quote.c)); return; } diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue52/YamlEnumTest.java b/test/com/esotericsoftware/yamlbeans/issues/issue52/YamlEnumTest.java new file mode 100644 index 0000000..e9d21e0 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue52/YamlEnumTest.java @@ -0,0 +1,85 @@ +package com.esotericsoftware.yamlbeans.issues.issue52; + +import com.esotericsoftware.yamlbeans.YamlReader; +import com.esotericsoftware.yamlbeans.YamlWriter; +import junit.framework.TestCase; + +import java.io.IOException; +import java.io.StringWriter; + +public class YamlEnumTest extends TestCase { + + public static class TestObject { + public EnumIface[] enumArray = new EnumIface[] { CustomEnum.V1, CustomEnum.V2 }; + } + + public interface EnumIface { + + } + + public enum CustomEnum implements EnumIface { + V1, V2 + } + + public static class NonEnum implements EnumIface { + public String v1, v2; + + public NonEnum() { + } + + public NonEnum(String v1, String v2) { + this.v1 = v1; + this.v2 = v2; + } + } + + public YamlEnumTest() { + } + + public static String write(Object object) throws IOException { + StringWriter stringWriter = new StringWriter(); + YamlWriter writer = new YamlWriter(stringWriter); + + writer.getConfig().setPrivateFields(false); + writer.write(object); + writer.close(); + + String string = stringWriter.toString(); + System.out.println("Wrote: \n" + string); + + return string; + } + + public void read(String string) throws IOException { + YamlReader reader = new YamlReader(string); + reader.read(TestObject.class); + reader.close(); + } + + public void testNonEnum() throws IOException { + TestObject testObject = new TestObject(); + testObject.enumArray = new EnumIface[] { new NonEnum("v1", "v2"), new NonEnum("v1", "v2") }; + + read(write(testObject)); + } + + public void testOnlyEnums() throws IOException { + TestObject testObject = new TestObject(); + testObject.enumArray = new EnumIface[] { CustomEnum.V1, CustomEnum.V2 }; + + read(write(testObject)); + } + + public void testMixEnumsAndNonEnums() throws IOException { + TestObject testObject = new TestObject(); + testObject.enumArray = new EnumIface[] { new NonEnum("v1", "v2"), CustomEnum.V2 }; + + read(write(testObject)); + } + + public void testFixedEnum() throws IOException { + read("!com.esotericsoftware.yamlbeans.issues.issue52.YamlEnumTest$TestObject\n" + "enumArray:\n" + + "- !com.esotericsoftware.yamlbeans.issues.issue52.YamlEnumTest$CustomEnum V1\n" + + "- !com.esotericsoftware.yamlbeans.issues.issue52.YamlEnumTest$CustomEnum V2\n"); + } +} From 8f780cd16f331e677e389826c1e58b9e8b5154f7 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 12 May 2020 15:18:22 +0800 Subject: [PATCH 068/114] fix javadoc (#129) --- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 862c857..a6ab104 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -283,7 +283,7 @@ public void setClassTags (boolean classTags) { this.classTags = classTags; } - /** When true, if the type for a scalar value is unknown and it looks like a number, it is read as an integer or float. When + /** When true, if the type for a scalar value is unknown and it looks like a number, it is read as a double or long. When * false, if the type for a scalar value is unknown it is always read a string. Default is true. */ public void setGuessNumberTypes (boolean guessNumberTypes) { this.guessNumberTypes = guessNumberTypes; From 63b25d5b293b2ebde90c19beeb4215ed290a0b10 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 14 May 2020 16:47:26 +0800 Subject: [PATCH 069/114] improve Beans.class, repeated code integration (#130) --- src/com/esotericsoftware/yamlbeans/Beans.java | 105 ++++++++---------- .../yamlbeans/BooleanTest.java | 15 ++- 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 5be5e52..e45db6c 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -112,46 +112,12 @@ static public Object createObject (Class type, boolean privateConstructors) thro static public Set getProperties (Class type, boolean beanProperties, boolean privateFields, YamlConfig config) { if (type == null) throw new IllegalArgumentException("type cannot be null."); - Class[] noArgs = new Class[0], oneArg = new Class[1]; Set properties = config.writeConfig.keepBeanPropertyOrder ? new LinkedHashSet() : new TreeSet(); for (Field field : getAllFields(type)) { - String name = field.getName(); - - if (beanProperties) { - DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); - boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); - - String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); - Method getMethod = null, setMethod = null; - try { - oneArg[0] = field.getType(); - setMethod = type.getMethod("set" + nameUpper, oneArg); - } catch (Exception ignored) { - } - try { - getMethod = type.getMethod("get" + nameUpper, noArgs); - } catch (Exception ignored) { - } - if (getMethod == null && (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class))) { - try { - getMethod = type.getMethod("is" + nameUpper, noArgs); - } catch (Exception ignored) { - } - } - if (getMethod != null && (setMethod != null || constructorProperty)) { - properties.add(new MethodProperty(name, setMethod, getMethod)); - continue; - } + Property property = getProperty(type, beanProperties, privateFields, config, field); + if (property != null) { + properties.add(property); } - - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - if (!Modifier.isPublic(modifiers) && !privateFields) continue; - try { - field.setAccessible(true); - } catch (Exception ignored) { - } - properties.add(new FieldProperty(field)); } return properties; } @@ -171,47 +137,64 @@ static public Property getProperty (Class type, String name, boolean beanPropert if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); name = toJavaIdentifier(name); + Property property = null; + for (Field field : getAllFields(type)) { + if (field.getName().equals(name)) { + property = getProperty(type, beanProperties, privateFields, config, field); + break; + } + } + return property; + } + + private static Property getProperty(Class type, boolean beanProperties, boolean privateFields, YamlConfig config, + Field field) { + Property property = null; if (beanProperties) { + String name = field.getName(); DeferredConstruction deferredConstruction = getDeferredConstruction(type, config); boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name); String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1); + String setMethodName = "set" + nameUpper; + String getMethodName = "get" + nameUpper; + if (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class)) { + setMethodName = name.startsWith("is") + ? "set" + Character.toUpperCase(name.charAt(2)) + name.substring(3) + : setMethodName; + getMethodName = name.startsWith("is") ? name : "is" + nameUpper; + } + Method getMethod = null; + Method setMethod = null; try { - getMethod = type.getMethod("get" + nameUpper); + setMethod = type.getMethod(setMethodName, field.getType()); } catch (Exception ignored) { } - if (getMethod == null) { - try { - getMethod = type.getMethod("is" + nameUpper); - } catch (Exception ignored) { - } + try { + getMethod = type.getMethod(getMethodName); + } catch (Exception ignored) { } - if (getMethod != null) { - Method setMethod = null; + + // field.getType() is boolean, but getMethodName is getFieldName(E.g: getBool). + if (getMethod == null && (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class))) { try { - setMethod = type.getMethod("set" + nameUpper, getMethod.getReturnType()); + getMethod = type.getMethod("get" + nameUpper); } catch (Exception ignored) { } - if (getMethod != null && (setMethod != null || constructorProperty)) - return new MethodProperty(name, setMethod, getMethod); } - } - - for (Field field : getAllFields(type)) { - if (!field.getName().equals(name)) continue; - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - if (!Modifier.isPublic(modifiers) && !privateFields) continue; - try { - field.setAccessible(true); - } catch (Exception ignored) { + if (getMethod != null && (setMethod != null || constructorProperty)) { + property = new MethodProperty(name, setMethod, getMethod); } - return new FieldProperty(field); } - if (!name.startsWith("_")) return getProperty(type, "_" + name, beanProperties, privateFields, config); - return null; + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers) + && (Modifier.isPublic(modifiers) || privateFields)) { + field.setAccessible(true); + property = new FieldProperty(field); + } + return property; } static private ArrayList getAllFields (Class type) { diff --git a/test/com/esotericsoftware/yamlbeans/BooleanTest.java b/test/com/esotericsoftware/yamlbeans/BooleanTest.java index 238299e..ac7a69d 100644 --- a/test/com/esotericsoftware/yamlbeans/BooleanTest.java +++ b/test/com/esotericsoftware/yamlbeans/BooleanTest.java @@ -13,6 +13,7 @@ public void testBooleanBean() throws Exception { BeanWithBoolean val = new BeanWithBoolean(); val.setBool(true); val.setBoolObj(true); + val.setBoolean(true); // Store the bean ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -31,6 +32,7 @@ public void testBooleanBean() throws Exception { public static class BeanWithBoolean { private boolean bool = false; private Boolean boolObj = false; + private boolean isBoolean = false; public boolean isBool() { return bool; @@ -48,7 +50,15 @@ public void setBoolObj(Boolean boolObj) { this.boolObj = boolObj; } - @Override + public boolean isBoolean() { + return isBoolean; + } + + public void setBoolean(boolean isBoolean) { + this.isBoolean = isBoolean; + } + + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -56,6 +66,7 @@ public boolean equals(Object o) { BeanWithBoolean that = (BeanWithBoolean) o; if (bool != that.bool) return false; + if (isBoolean != that.isBoolean) return false; return !(boolObj != null ? !boolObj.equals(that.boolObj) : that.boolObj != null); } @@ -63,6 +74,7 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = (bool ? 1 : 0); + result += (isBoolean ? 2 : 0); result = 31 * result + (boolObj != null ? boolObj.hashCode() : 0); return result; } @@ -72,6 +84,7 @@ public String toString() { return "BeanWithBoolean{" + "bool=" + bool + ", boolObj=" + boolObj + + ", isBoolean=" + isBoolean + '}'; } } From b6024e591ae7f506591df0b5aecf601f15b5f614 Mon Sep 17 00:00:00 2001 From: NemesisMate Date: Thu, 14 May 2020 10:49:41 +0200 Subject: [PATCH 070/114] Replaced duplicated hashmap retrieval. (#51) Replaced duplicated hashmap retrieval by it already fetched value (anchor) --- src/com/esotericsoftware/yamlbeans/YamlWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 660abf1..1330562 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -163,7 +163,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (config.writeConfig.autoAnchor) { Integer count = referenceCount.get(object); if (count == null) { - emitter.emit(new AliasEvent(anchoredObjects.get(object))); + emitter.emit(new AliasEvent(anchor)); return; } if (count > 1) { From ffb9dc06cf67ea3b8c18970d5694967917156684 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 14 May 2020 18:43:29 +0800 Subject: [PATCH 071/114] Maven test (#132) * Create maven.yml * Update maven.yml * Update maven.yml * Update maven.yml * Update maven.yml * Update maven.yml * Rename maven.yml to maven-test.yml --- .github/workflows/maven-test.yml | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/maven-test.yml diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml new file mode 100644 index 0000000..457b723 --- /dev/null +++ b/.github/workflows/maven-test.yml @@ -0,0 +1,37 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + jdk8: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn test + jdk7: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.7 + uses: actions/setup-java@v1 + with: + java-version: 1.7 + - name: Build with Maven + run: mvn test + From 9581c687784a3c804a5b617781146671f7b52ced Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 25 May 2020 16:44:14 +0800 Subject: [PATCH 072/114] resolves #34 (#133) --- .../yamlbeans/tokenizer/Tokenizer.java | 3 ++- .../yamlbeans/YamlReaderTest.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index bdd76e0..7d97815 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -953,7 +953,8 @@ private Token scanPlain () { private String scanPlainSpaces () { StringBuilder chunks = new StringBuilder(); int length = 0; - while (peek(length) == ' ') + // YAML recognizes two white space characters: space and tab. + while (peek(length) == ' ' || peek(length) == '\t') length++; String whitespaces = prefixForward(length); // forward(length); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index b8059ce..13bde68 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -485,4 +485,24 @@ public void testGuessNumberTypes() throws YamlException { double moneyValue = (Double) result.get("money"); assertEquals(money, moneyValue); } + + /** + * issue #34 + * + * @throws YamlException + */ + public void testWhiteSpaceCharacters() throws YamlException { + + String stringWithSpace = "test test"; + YamlReader reader = new YamlReader(stringWithSpace); + assertEquals(stringWithSpace, reader.read(String.class)); + + String stringWithTab = "test\ttesttest"; + reader = new YamlReader(stringWithTab); + assertEquals(stringWithTab, reader.read(String.class)); + + String stringWithSpaceAndTab = "test test\ttest \ttest"; + reader = new YamlReader(stringWithSpaceAndTab); + assertEquals(stringWithSpaceAndTab, reader.read(String.class)); + } } From 9a7c9c27bdc7c852409928a48b34c976ac11a1fa Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 25 May 2020 20:12:03 +0800 Subject: [PATCH 073/114] fix pr 126, Support Octal and hex Number (#134) --- .../yamlbeans/YamlReader.java | 33 +++++++++---------- .../yamlbeans/YamlReaderTest.java | 17 ++++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index faa97c0..ea8dec0 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -467,24 +467,21 @@ public YamlReaderException (String message) { } } - private Number valueConvertedNumber(String value) { - - Number number = null; - if (value.matches("-?[0-9]+.?[0-9]*")) { - if (value.contains(".")) { - try { - number = Double.parseDouble(value); - } catch (NumberFormatException e) { - } - } else { - try { - number = Long.parseLong(value); - } catch (NumberFormatException e) { - } - } - } - return number; - } + private Number valueConvertedNumber(String value) { + + Number number = null; + try { + number = Long.decode(value); + } catch (NumberFormatException e) { + } + if (number == null) { + try { + number = Double.parseDouble(value); + } catch (NumberFormatException e) { + } + } + return number; + } public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 13bde68..834c48c 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -486,6 +486,23 @@ public void testGuessNumberTypes() throws YamlException { assertEquals(money, moneyValue); } + public void testGuessOctNumberAndHexNumber() throws YamlException { + + // Octal number string + String str = "{octNumber: 011}"; + YamlConfig config = new YamlConfig(); + config.readConfig.guessNumberTypes = true; + YamlReader reader = new YamlReader(str, config); + int octNumber = ((Map) reader.read()).get("octNumber").intValue(); + assertEquals(9, octNumber); + + // Hex number string + str = "{hexNumber: 0x11}"; + reader = new YamlReader(str, config); + int hexNumber = ((Map) reader.read()).get("hexNumber").intValue(); + assertEquals(17, hexNumber); + } + /** * issue #34 * From e592091e5ddf0627e1c90f14f8030047c39b879a Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 26 May 2020 16:13:08 +0800 Subject: [PATCH 074/114] [resolves #37], Support enum type custom Enum Serializer (#135) --- .../yamlbeans/YamlReader.java | 24 ++++++------ .../yamlbeans/YamlWriter.java | 16 ++++---- .../yamlbeans/issues/issue37/Issue37Test.java | 39 +++++++++++++++++++ .../yamlbeans/issues/issue37/SexType.java | 6 +++ .../issues/issue37/SexTypeSerializer.java | 15 +++++++ .../yamlbeans/issues/issue37/TestObject.java | 7 ++++ 6 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue37/Issue37Test.java create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue37/SexType.java create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue37/SexTypeSerializer.java create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue37/TestObject.java diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index ea8dec0..ddce97f 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -281,18 +281,6 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } } - if (Enum.class.isAssignableFrom(type)) { - Event event = parser.getNextEvent(); - if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); - String enumValueName = ((ScalarEvent)event).value; - if (enumValueName == null) return null; - try { - return Enum.valueOf(type, enumValueName); - } catch (Exception ex) { - throw new YamlReaderException("Unable to find enum value '" + enumValueName + "' for enum class: " + type.getName()); - } - } - for (Entry entry : config.scalarSerializers.entrySet()) { if (entry.getKey().isAssignableFrom(type)) { ScalarSerializer serializer = entry.getValue(); @@ -305,6 +293,18 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } } + if (Enum.class.isAssignableFrom(type)) { + Event event = parser.getNextEvent(); + if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for enum type but found: " + event.type); + String enumValueName = ((ScalarEvent)event).value; + if (enumValueName == null) return null; + try { + return Enum.valueOf(type, enumValueName); + } catch (Exception ex) { + throw new YamlReaderException("Unable to find enum value '" + enumValueName + "' for enum class: " + type.getName()); + } + } + Event event = parser.peekNextEvent(); switch (event.type) { case MAPPING_START: { diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 1330562..5222039 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -150,15 +150,8 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla boolean unknownType = fieldClass == null; if (unknownType) fieldClass = valueClass; - if (object instanceof Enum) { - emitter.emit(new ScalarEvent(null, object.getClass().getName(), - new boolean[] { object.getClass().equals(fieldClass), object.getClass().equals(fieldClass) }, - ((Enum) object).name(), this.config.writeConfig.quote.c)); - return; - } - String anchor = null; - if (!Beans.isScalar(valueClass)) { + if (!Beans.isScalar(valueClass) && !(object instanceof Enum)) { anchor = anchoredObjects.get(object); if (config.writeConfig.autoAnchor) { Integer count = referenceCount.get(object); @@ -213,6 +206,13 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla return; } + if (object instanceof Enum) { + emitter.emit(new ScalarEvent(null, object.getClass().getName(), + new boolean[] { object.getClass().equals(fieldClass), object.getClass().equals(fieldClass) }, + ((Enum) object).name(), this.config.writeConfig.quote.c)); + return; + } + if (object instanceof Collection) { emitter.emit(new SequenceStartEvent(anchor, tag, !showTag, false)); for (Object item : (Collection)object) { diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue37/Issue37Test.java b/test/com/esotericsoftware/yamlbeans/issues/issue37/Issue37Test.java new file mode 100644 index 0000000..a1c6e92 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue37/Issue37Test.java @@ -0,0 +1,39 @@ +package com.esotericsoftware.yamlbeans.issues.issue37; + +import static org.junit.Assert.assertEquals; + +import java.io.StringWriter; + +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; +import com.esotericsoftware.yamlbeans.YamlWriter; + +public class Issue37Test { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void test() throws YamlException { + + TestObject testObject = new TestObject(); + testObject.sexType = SexType.FEMALE; + + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.setScalarSerializer(SexType.class, new SexTypeSerializer()); + StringWriter sw = new StringWriter(); + YamlWriter writer = new YamlWriter(sw, yamlConfig); + writer.write(testObject); + writer.close(); + System.out.println(sw.toString()); + + assertEquals("!com.esotericsoftware.yamlbeans.issues.issue37.TestObject" + LINE_SEPARATOR + "sexType: female" + + LINE_SEPARATOR, sw.toString()); + + YamlReader reader = new YamlReader(sw.toString(), yamlConfig); + TestObject obj = reader.read(TestObject.class); + assertEquals(SexType.FEMALE, obj.sexType); + } +} diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue37/SexType.java b/test/com/esotericsoftware/yamlbeans/issues/issue37/SexType.java new file mode 100644 index 0000000..8e4833a --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue37/SexType.java @@ -0,0 +1,6 @@ +package com.esotericsoftware.yamlbeans.issues.issue37; + +public enum SexType { + + MALE, FEMALE; +} diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue37/SexTypeSerializer.java b/test/com/esotericsoftware/yamlbeans/issues/issue37/SexTypeSerializer.java new file mode 100644 index 0000000..91784b7 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue37/SexTypeSerializer.java @@ -0,0 +1,15 @@ +package com.esotericsoftware.yamlbeans.issues.issue37; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.scalar.ScalarSerializer; + +public class SexTypeSerializer implements ScalarSerializer { + + public String write(SexType object) throws YamlException { + return object.name().toLowerCase(); + } + + public SexType read(String value) throws YamlException { + return SexType.valueOf(value.toUpperCase()); + } +} diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue37/TestObject.java b/test/com/esotericsoftware/yamlbeans/issues/issue37/TestObject.java new file mode 100644 index 0000000..22c7fb4 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue37/TestObject.java @@ -0,0 +1,7 @@ +package com.esotericsoftware.yamlbeans.issues.issue37; + +public class TestObject { + + public SexType sexType; + +} From d0b84531292a5a35f550b9971c8911e90b8ca5d3 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Wed, 10 Jun 2020 16:41:42 +0800 Subject: [PATCH 075/114] Add Iterator (#137) --- .../yamlbeans/document/YamlDocument.java | 6 +++- .../yamlbeans/document/YamlMapping.java | 5 +++ .../yamlbeans/document/YamlSequence.java | 5 ++- .../yamlbeans/document/YamlDocumentTest.java | 36 +++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java b/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java index 8b8be9b..8401da1 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlDocument.java @@ -1,5 +1,7 @@ package com.esotericsoftware.yamlbeans.document; +import java.util.Iterator; + import com.esotericsoftware.yamlbeans.YamlException; public interface YamlDocument { @@ -23,5 +25,7 @@ public interface YamlDocument { void addElement(Number value) throws YamlException; void addElement(String value) throws YamlException; void addElement(YamlElement element) throws YamlException; - + + Iterator iterator(); + } diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java index 166c6ee..f34e494 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java @@ -1,6 +1,7 @@ package com.esotericsoftware.yamlbeans.document; import java.io.IOException; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -141,4 +142,8 @@ public void addElement(String element) throws YamlException { public void addElement(YamlElement element) throws YamlException { throw new YamlException("Can only add element on sequence!"); } + + public Iterator iterator() { + return entries.iterator(); + } } diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java index 2f94862..229ce6e 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java @@ -1,6 +1,7 @@ package com.esotericsoftware.yamlbeans.document; import java.io.IOException; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -120,5 +121,7 @@ public void addElement(String value) throws YamlException { elements.add(new YamlScalar(value)); } - + public Iterator iterator() { + return elements.iterator(); + } } diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java index 50d68ff..2420079 100644 --- a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -1,6 +1,7 @@ package com.esotericsoftware.yamlbeans.document; import java.io.StringWriter; +import java.util.Iterator; import org.junit.Test; @@ -130,6 +131,41 @@ public void testThatSequenceItemIsRemoved() throws YamlException { assertEquals("- 123\n", actual); } + @Test + public void testYamlSequenceIterator() throws YamlException { + YamlDocument yaml = readDocument("- 111\n- 222\n"); + Iterator iterator = yaml.iterator(); + assertEquals(true, iterator.hasNext()); + YamlScalar yamlScalar = iterator.next(); + assertEquals("111", yamlScalar.getValue()); + assertEquals(true, iterator.hasNext()); + yamlScalar = iterator.next(); + assertEquals("222", yamlScalar.getValue()); + assertEquals(false, iterator.hasNext()); + try { + iterator.next(); + fail("Already read to the end."); + } catch (Exception e) { + } + } + + @Test + public void testYamlMappingIterator() throws YamlException { + YamlDocument yaml = readDocument("name: Andi\nage: 18\n"); + Iterator iterator = yaml.iterator(); + assertEquals(true, iterator.hasNext()); + YamlEntry yamlEntry = iterator.next(); + assertEquals("Andi", ((YamlScalar) yamlEntry.getValue()).getValue()); + assertEquals(true, iterator.hasNext()); + yamlEntry = iterator.next(); + assertEquals("18", ((YamlScalar) yamlEntry.getValue()).getValue()); + assertEquals(false, iterator.hasNext()); + try { + iterator.next(); + fail("Already read to the end."); + } catch (Exception e) { + } + } private YamlDocument readDocument(String yaml) throws YamlException { YamlDocumentReader reader = new YamlDocumentReader(yaml); From 50325a69f9222577555f361cc3ec3c0d0e040f91 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 11 Jun 2020 16:18:25 +0800 Subject: [PATCH 076/114] Resolve PR #40 conflict (#136) --- .../yamlbeans/YamlReader.java | 54 ++++++++++++++++++- .../yamlbeans/YamlReaderTest.java | 18 ++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index ddce97f..abd761b 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -35,6 +35,7 @@ import com.esotericsoftware.yamlbeans.parser.AliasEvent; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.EventType; import com.esotericsoftware.yamlbeans.parser.Parser; import com.esotericsoftware.yamlbeans.parser.Parser.ParserException; import com.esotericsoftware.yamlbeans.parser.ScalarEvent; @@ -364,7 +365,21 @@ private Object readValueInternal (Class type, Class elementType, String anchor) Property property = Beans.getProperty(type, (String)key, config.beanProperties, config.privateFields, config); if (property == null) { - if (config.readConfig.ignoreUnknownProperties) continue; + if (config.readConfig.ignoreUnknownProperties) { + // if next event is sequence, mapping... start, go though all of it until + // corresponding sequence, mapping... end + Event nextEvent = parser.peekNextEvent(); + EventType nextType = nextEvent.type; + if (nextType == SEQUENCE_START || nextType == MAPPING_START + || nextType == DOCUMENT_START || nextType == EventType.STREAM_START) { + skipRange(); + } else { + // go though the next event, because this is a value of missing property + parser.getNextEvent(); + } + + continue; + } throw new YamlReaderException("Unable to find property '" + key + "' on class: " + type.getName()); } Class propertyElementType = config.propertyToElementType.get(property); @@ -483,6 +498,43 @@ private Number valueConvertedNumber(String value) { return number; } + private void skipRange() { + Event nextEvent; + int depth = 0; + do { + nextEvent = parser.getNextEvent(); + switch (nextEvent.type) { + case SEQUENCE_START: + depth++; + break; + case MAPPING_START: + depth++; + break; + case DOCUMENT_START: + depth++; + break; + case STREAM_START: + depth++; + break; + case SEQUENCE_END: + depth--; + break; + case MAPPING_END: + depth--; + break; + case DOCUMENT_END: + depth--; + break; + case STREAM_END: + depth--; + break; + default: + // ignore + break; + } + } while (depth > 0); + } + public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); System.out.println(reader.read()); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index 834c48c..cd91efe 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -339,7 +339,23 @@ public void testIgnoreUnknownProperties () { fail("Unknown properties were supposed to be allowed."); } } - + + public void testIgnoreUnknownPropertiesSprcialType() throws Exception { + String input = "a: 1\nb: 2\nc: 3\n" + "sequence: \n" + " - 1\n" + " - 2\n" + "mapping: \n key: value\n" + + "stream: {a: 7}\ndocument: ---\na: 9\n...\n"; + + YamlConfig config = new YamlConfig(); + config.readConfig.setIgnoreUnknownProperties(true); + try { + ACD pojo = new YamlReader(input, config).read(ACD.class); + assertEquals(1, pojo.a); + assertEquals(3, pojo.c); + assertEquals(4, pojo.d); + } catch (YamlException e) { + fail("Unknown properties were supposed to be allowed."); + } + } + public void testDuplicateKeysAreNotAllowedIfAllowDuplicateIsSetSo () { String inputWithDuplicates = "a: 1\na: 2\nc: 3"; YamlConfig yamlConfig = new YamlConfig(); From c8a5f8cd42efdc05f92f70f95497481518dd59e9 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 15 Jun 2020 18:50:44 +0800 Subject: [PATCH 077/114] Support reading multiple documents, and adding test cases (#138) --- .../document/YamlDocumentReader.java | 17 ++- .../yamlbeans/document/YamlDocumentTest.java | 51 +++++++ .../yamlbeans/document/YamlMappingTest.java | 136 ++++++++++++++++++ .../yamlbeans/document/YamlSequenceTest.java | 129 +++++++++++++++++ 4 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/document/YamlMappingTest.java create mode 100644 test/com/esotericsoftware/yamlbeans/document/YamlSequenceTest.java diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java index b2ebd99..99debe0 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java @@ -67,15 +67,20 @@ public YamlDocument read() throws YamlException { } private YamlDocument readDocument() { + YamlDocument yamlDocument = null; Event event = parser.peekNextEvent(); switch (event.type) { - case MAPPING_START: - return readMapping(); - case SEQUENCE_START: - return readSequence(); - default: - throw new IllegalStateException(); + case MAPPING_START: + yamlDocument = readMapping(); + break; + case SEQUENCE_START: + yamlDocument = readSequence(); + break; + default: + throw new IllegalStateException(); } + parser.getNextEvent(); // consume it(DOCUMENT_END) + return yamlDocument; } private YamlMapping readMapping() { diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java index 2420079..08e2f31 100644 --- a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -5,6 +5,7 @@ import org.junit.Test; +import com.esotericsoftware.yamlbeans.Version; import com.esotericsoftware.yamlbeans.YamlConfig; import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; import com.esotericsoftware.yamlbeans.YamlException; @@ -134,6 +135,7 @@ public void testThatSequenceItemIsRemoved() throws YamlException { @Test public void testYamlSequenceIterator() throws YamlException { YamlDocument yaml = readDocument("- 111\n- 222\n"); + @SuppressWarnings("unchecked") Iterator iterator = yaml.iterator(); assertEquals(true, iterator.hasNext()); YamlScalar yamlScalar = iterator.next(); @@ -152,6 +154,7 @@ public void testYamlSequenceIterator() throws YamlException { @Test public void testYamlMappingIterator() throws YamlException { YamlDocument yaml = readDocument("name: Andi\nage: 18\n"); + @SuppressWarnings("unchecked") Iterator iterator = yaml.iterator(); assertEquals(true, iterator.hasNext()); YamlEntry yamlEntry = iterator.next(); @@ -167,6 +170,54 @@ public void testYamlMappingIterator() throws YamlException { } } + @Test + public void testVersion1_0() throws YamlException { + String yaml = "version: !str 1.0"; + YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 0)); + YamlMapping yamlMapping = (YamlMapping) reader.read(); + assertEquals("1.0", ((YamlScalar) yamlMapping.getEntry("version").getValue()).getValue()); + } + + @Test + public void testVersion1_0ThrowsYamlException() { + String yaml = "version: !!str 1.1"; + YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 0)); + try { + reader.read(); + fail("1.0 Version tag is single '!'"); + } catch (YamlException e) { + } + } + + @Test + public void testVersion1_1() throws YamlException { + String yaml = "version: !!str 1.1"; + YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 1)); + YamlMapping yamlMapping = (YamlMapping) reader.read(); + assertEquals("1.1", ((YamlScalar) yamlMapping.getEntry("version").getValue()).getValue()); + } + + @Test + public void testReadMultipleDocuments() throws YamlException { + String yaml = "key: 111\n---\nkey: 222"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + assertEquals(true, reader.read() != null); + assertEquals(true, reader.read() != null); + assertEquals(true, reader.read() == null); + assertEquals(true, reader.read() == null); + } + + @Test + public void testReadThrowsYamlException() { + String yaml = "\tkey: value"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + try { + reader.read(); + fail("Tabs cannot be used for indentation."); + } catch (YamlException e) { + } + } + private YamlDocument readDocument(String yaml) throws YamlException { YamlDocumentReader reader = new YamlDocumentReader(yaml); return reader.read(); diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlMappingTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlMappingTest.java new file mode 100644 index 0000000..938d2d2 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/document/YamlMappingTest.java @@ -0,0 +1,136 @@ +package com.esotericsoftware.yamlbeans.document; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlException; + +public class YamlMappingTest { + + private YamlMapping yamlMapping = null; + + @Before + public void setUp() throws YamlException { + String yaml = "key1: value1\nkey2: value2\nkey3: value3\n"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + yamlMapping = (YamlMapping) reader.read(); + yamlMapping.setAnchor("mapping"); + yamlMapping.setTag("HashMap"); + } + + @Test + public void testSize() { + assertEquals(3, yamlMapping.size()); + } + + @Test + public void testAddEntry() throws YamlException { + YamlScalar key = new YamlScalar(); + key.setValue("test"); + YamlScalar value = new YamlScalar(); + value.setValue("111"); + YamlEntry entry = new YamlEntry(key, value); + yamlMapping.addEntry(entry); + + assertEquals(entry, yamlMapping.getEntry("test")); + } + + @Test + public void testDeleteEntry() { + assertEquals(true, yamlMapping.deleteEntry("key1")); + assertEquals(false, yamlMapping.deleteEntry("key4")); + } + + @Test + public void testGetEntryByKey() throws YamlException { + assertEquals(true, yamlMapping.getEntry("key1") != null); + assertEquals(true, yamlMapping.getEntry("key4") == null); + } + + @Test + public void testGetEntryByIndex() throws YamlException { + assertEquals(true, yamlMapping.getEntry(0) != null); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testGetEntryByIndexThrowsIndexOutOfBoundsException() throws YamlException { + yamlMapping.getEntry(3); + } + + @Test + public void testToString() { + assertEquals("&mapping !HashMap{key1:value1,key2:value2,key3:value3}", yamlMapping.toString()); + } + + @Test + public void testSetEntryUseBooleanValue() throws YamlException { + yamlMapping.setEntry("test", true); + assertEquals("true", ((YamlScalar) yamlMapping.getEntry("test").getValue()).getValue()); + } + + @Test + public void testSetEntryUseNumberValue() throws YamlException { + yamlMapping.setEntry("test", 1); + assertEquals("1", ((YamlScalar) yamlMapping.getEntry("test").getValue()).getValue()); + } + + @Test + public void testSetEntryUseStringValue() throws YamlException { + yamlMapping.setEntry("test", "test"); + assertEquals("test", ((YamlScalar) yamlMapping.getEntry("test").getValue()).getValue()); + } + + @Test(expected = YamlException.class) + public void testGetElementByItemThrowsYamlException() throws YamlException { + yamlMapping.getElement(0); + } + + @Test(expected = YamlException.class) + public void testDeleteElementThrowsYamlException() throws YamlException { + yamlMapping.deleteElement(0); + } + + @Test(expected = YamlException.class) + public void testSetElementUseBooleanThrowsYamlException() throws YamlException { + yamlMapping.setElement(0, true); + } + + @Test(expected = YamlException.class) + public void testSetElementUseNumberThrowsYamlException() throws YamlException { + yamlMapping.setElement(0, 1); + } + + @Test(expected = YamlException.class) + public void testSetElementUseStringThrowsYamlException() throws YamlException { + yamlMapping.setElement(0, "test"); + } + + @Test(expected = YamlException.class) + public void testSetElementUseYamlElementThrowsYamlException() throws YamlException { + YamlElement YamlElement = new YamlScalar(); + yamlMapping.setElement(0, YamlElement); + } + + @Test(expected = YamlException.class) + public void testAddElementUseBooleanThrowsYamlException() throws YamlException { + yamlMapping.addElement(true); + } + + @Test(expected = YamlException.class) + public void testAddElementUseNumberThrowsYamlException() throws YamlException { + yamlMapping.addElement(1); + } + + @Test(expected = YamlException.class) + public void testAddElementUseStringThrowsYamlException() throws YamlException { + yamlMapping.addElement("test"); + } + + @Test(expected = YamlException.class) + public void testAddElementUseYamlElementThrowsYamlException() throws YamlException { + YamlElement YamlElement = new YamlScalar(); + yamlMapping.addElement(YamlElement); + } +} diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlSequenceTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlSequenceTest.java new file mode 100644 index 0000000..3ab6f3b --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/document/YamlSequenceTest.java @@ -0,0 +1,129 @@ +package com.esotericsoftware.yamlbeans.document; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlException; + +public class YamlSequenceTest { + private YamlSequence yamlSequence = null; + + @Before + public void setUp() throws YamlException { + String yaml = "- 1\n- 2\n- 3\n"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + yamlSequence = (YamlSequence) reader.read(); + yamlSequence.setAnchor("sequence"); + yamlSequence.setTag("LinkedList"); + } + + @Test + public void testSize() { + assertEquals(3, yamlSequence.size()); + } + + @Test + public void testAddElement() throws YamlException { + YamlElement yamlElement = new YamlScalar(); + yamlSequence.addElement(yamlElement); + assertEquals(yamlElement, yamlSequence.getElement(3)); + } + + @Test + public void testDeleteElement() throws YamlException { + yamlSequence.deleteElement(0); + assertEquals(2, yamlSequence.size()); + } + + @Test + public void testToStrig() { + assertEquals("&sequence !LinkedList[1,2,3]", yamlSequence.toString()); + } + + @Test(expected = YamlException.class) + public void testGetEntryByKeyThrowsYamlException() throws YamlException { + yamlSequence.getEntry("key"); + } + + @Test(expected = YamlException.class) + public void testGetEntryByIndexThrowsYamlException() throws YamlException { + yamlSequence.getEntry(0); + } + + @Test(expected = YamlException.class) + public void testDeleteEntryByKeyThrowsYamlException() throws YamlException { + yamlSequence.deleteEntry("key"); + } + + @Test(expected = YamlException.class) + public void testSetEntryUseBooleanValueThrowsYamlException() throws YamlException { + yamlSequence.setEntry("key", true); + } + + @Test(expected = YamlException.class) + public void testSetEntryUseNumberValueThrowsYamlException() throws YamlException { + yamlSequence.setEntry("key", 1); + } + + @Test(expected = YamlException.class) + public void testSetEntryUseStringValueThrowsYamlException() throws YamlException { + yamlSequence.setEntry("key", "111"); + } + + @Test(expected = YamlException.class) + public void testSetEntryUseYamlElementValueThrowsYamlException() throws YamlException { + YamlElement yamlElement = new YamlScalar(); + yamlSequence.setEntry("key", yamlElement); + } + + @Test + public void testSetElementUseBooleanValue() throws YamlException { + yamlSequence.setElement(0, true); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(0); + assertEquals("true", yamlScalar.getValue()); + } + + @Test + public void testSetElementUseNumberValue() throws YamlException { + yamlSequence.setElement(0, 1); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(0); + assertEquals("1", yamlScalar.getValue()); + } + + @Test + public void testSetElementUseStringValue() throws YamlException { + yamlSequence.setElement(0, "testStr"); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(0); + assertEquals("testStr", yamlScalar.getValue()); + } + + @Test + public void testSetElementUseYamlElementValue() throws YamlException { + YamlElement yamlElement = new YamlScalar(); + yamlSequence.setElement(0, yamlElement); + assertEquals(yamlElement, yamlSequence.getElement(0)); + } + + @Test + public void testAddElementUseBoolean() throws YamlException { + yamlSequence.addElement(true); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(yamlSequence.size() - 1); + assertEquals("true", yamlScalar.getValue()); + } + + @Test + public void testAddElementUseNumber() throws YamlException { + yamlSequence.addElement(1); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(yamlSequence.size() - 1); + assertEquals("1", yamlScalar.getValue()); + } + + @Test + public void testAddElementUseString() throws YamlException { + yamlSequence.addElement("testStr"); + YamlScalar yamlScalar = (YamlScalar) yamlSequence.getElement(yamlSequence.size() - 1); + assertEquals("testStr", yamlScalar.getValue()); + } +} From 2fa34e7acf30cb8a56d0d50040f58843355ed5ed Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 18 Jun 2020 16:50:46 +0800 Subject: [PATCH 078/114] fix bug (#139) --- .../yamlbeans/tokenizer/Tokenizer.java | 2 +- .../yamlbeans/YamlReaderTest.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index 7d97815..186963a 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -945,7 +945,7 @@ private Token scanPlain () { chunks.append(prefixForward(length)); // forward(length); spaces = scanPlainSpaces(); - if (spaces == null || flowLevel == 0 && column < ind) break; + if (spaces.length() == 0 || flowLevel == 0 && column < ind) break; } return new ScalarToken(chunks.toString(), true); } diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index cd91efe..f860fa2 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -538,4 +538,23 @@ public void testWhiteSpaceCharacters() throws YamlException { reader = new YamlReader(stringWithSpaceAndTab); assertEquals(stringWithSpaceAndTab, reader.read(String.class)); } + + /** + * Test multiple documents, the document is a scalar, followed by "---" or + * "...", the test read is normal. + * + * @throws YamlException + */ + public void testReadMultiTpyeDocuments() throws YamlException { + String yaml = "scalar\n---\nkey: value\n---\nscalar\n...\n"; + YamlReader reader = new YamlReader(yaml); + String str = reader.read(String.class); + assertEquals("scalar", str); + + Map map = (Map) reader.read(HashMap.class); + assertEquals("value", map.get("key")); + + str = reader.read(String.class); + assertEquals("scalar", str); + } } From 127e7962e6eca55b837ca0a97b621f59018b426a Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:59:09 +0800 Subject: [PATCH 079/114] Add YamlIterator (#140) --- .../yamlbeans/YamlReader.java | 33 +++++++++++++ .../document/YamlDocumentReader.java | 49 ++++++++++++++++--- .../yamlbeans/YamlReaderTest.java | 47 ++++++++++++++++++ .../yamlbeans/document/YamlDocumentTest.java | 47 ++++++++++++++++++ 4 files changed, 169 insertions(+), 7 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index abd761b..df2c5b5 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; @@ -108,9 +109,41 @@ public T read (Class type, Class elementType) throws YamlException { throw new YamlException("Error parsing YAML.", ex); } catch (TokenizerException ex) { throw new YamlException("Error tokenizing YAML.", ex); + } finally{ + parser.getNextEvent(); // consume it(DOCUMENT_END) } } + /** + * Reads all documents from YAML into Objects. + * + * @param type specify Object type + * @return an iterator reads documents in order + */ + public Iterator readAll(final Class type) { + Iterator iterator = new Iterator() { + + public boolean hasNext() { + Event event = parser.peekNextEvent(); + return event != null && event.type != STREAM_END; + } + + public T next() { + try { + return read(type); + } catch (YamlException e) { + throw new RuntimeException("Iterative reading documents exception", e); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + return iterator; + } + /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. */ protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, TokenizerException { diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java index 99debe0..2e3f76f 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlDocumentReader.java @@ -4,6 +4,7 @@ import java.io.Reader; import java.io.StringReader; +import java.util.Iterator; import com.esotericsoftware.yamlbeans.Version; import com.esotericsoftware.yamlbeans.YamlException; @@ -39,6 +40,11 @@ public YamlDocumentReader(Reader reader, Version version) { } public YamlDocument read() throws YamlException { + return read(YamlDocument.class); + } + + @SuppressWarnings("unchecked") + public T read(Class type) throws YamlException { try { while (true) { Event event = parser.peekNextEvent(); @@ -53,7 +59,7 @@ public YamlDocument read() throws YamlException { return null; case DOCUMENT_START: parser.getNextEvent(); // consume it - return readDocument(); + return (T)readDocument(); default: throw new IllegalStateException(); } @@ -63,24 +69,53 @@ public YamlDocument read() throws YamlException { } catch (TokenizerException ex) { throw new YamlException("Error tokenizing YAML.", ex); } - } - private YamlDocument readDocument() { - YamlDocument yamlDocument = null; + public Iterator readAll(final Class type) { + Iterator iterator = new Iterator() { + + public boolean hasNext() { + Event event = parser.peekNextEvent(); + return event != null && event.type != STREAM_END; + } + + public T next() { + try { + return read(type); + } catch (YamlException e) { + throw new RuntimeException("Iterative reading documents exception", e); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + + return iterator; + } + + private YamlElement readDocument() { + YamlElement yamlElement = null; Event event = parser.peekNextEvent(); switch (event.type) { + case SCALAR: + yamlElement = readScalar(); + break; + case ALIAS: + yamlElement = readAlias(); + break; case MAPPING_START: - yamlDocument = readMapping(); + yamlElement = readMapping(); break; case SEQUENCE_START: - yamlDocument = readSequence(); + yamlElement = readSequence(); break; default: throw new IllegalStateException(); } parser.getNextEvent(); // consume it(DOCUMENT_END) - return yamlDocument; + return yamlElement; } private YamlMapping readMapping() { diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index f860fa2..b0ae7de 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -18,11 +18,16 @@ import java.io.StringReader; import java.io.StringWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlWriterTest.Obj; + import junit.framework.TestCase; /** @author Nathan Sweet */ @@ -557,4 +562,46 @@ public void testReadMultiTpyeDocuments() throws YamlException { str = reader.read(String.class); assertEquals("scalar", str); } + + public void testReadAll() throws YamlException { + + StringBuilder sb = new StringBuilder(); + sb.append("- 1").append("\n"); + sb.append("- 2").append("\n"); + sb.append("- 3").append("\n"); + sb.append("---\n").append("key: value").append("\n"); + sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); + sb.append("stringValue: test\n"); + YamlReader reader = new YamlReader(sb.toString()); + Iterator iterator = reader.readAll(Object.class); + List list = new ArrayList(); + while (iterator.hasNext()) { + list.add(iterator.next()); + } + assertEquals(3, list.size()); + assertEquals("test", ((Test) list.get(2)).stringValue); + } + + public void testReadAllCallingNextThrowsRuntimeException() { + String yaml = "\ttest"; + YamlReader reader = new YamlReader(yaml); + Iterator iterator = reader.readAll(Object.class); + try { + iterator.next(); + fail(); + } catch (Exception e) { + } + } + + public void testReadAllCallingRemoveUnsupportedOperationException() { + String yaml = "test"; + YamlReader reader = new YamlReader(yaml); + Iterator iterator = reader.readAll(Object.class); + try { + iterator.remove(); + ; + fail(); + } catch (Exception e) { + } + } } diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java index 08e2f31..96216a5 100644 --- a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -1,7 +1,9 @@ package com.esotericsoftware.yamlbeans.document; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.junit.Test; @@ -218,6 +220,51 @@ public void testReadThrowsYamlException() { } } + @Test + public void testReadAll() throws YamlException { + + StringBuilder sb = new StringBuilder(); + sb.append("&1 scalar").append("\n"); + sb.append("---\n").append("*1").append("\n"); + sb.append("---\n").append("key: value").append("\n"); + sb.append("---\n").append("- 1\n").append("- 2\n").append("- 3\n"); + YamlDocumentReader reader = new YamlDocumentReader(sb.toString()); + Iterator iterator = reader.readAll(YamlElement.class); + List list = new ArrayList(); + while (iterator.hasNext()) { + list.add(iterator.next()); + } + assertEquals(4, list.size()); + assertEquals(true, YamlScalar.class == list.get(0).getClass()); + assertEquals(true, YamlAlias.class == list.get(1).getClass()); + assertEquals(true, YamlMapping.class == list.get(2).getClass()); + assertEquals(true, YamlSequence.class == list.get(3).getClass()); + } + + @Test + public void testReadAllCallingNextThrowsRuntimeException() { + String yaml = "\ttest"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + Iterator iterator = reader.readAll(YamlElement.class); + try { + iterator.next(); + fail(""); + } catch (Exception e) { + } + } + + @Test + public void testReadAllCallingRemoveUnsupportedOperationException() { + String yaml = "test"; + YamlDocumentReader reader = new YamlDocumentReader(yaml); + Iterator iterator = reader.readAll(YamlElement.class); + try { + iterator.remove(); + fail(""); + } catch (Exception e) { + } + } + private YamlDocument readDocument(String yaml) throws YamlException { YamlDocumentReader reader = new YamlDocumentReader(yaml); return reader.read(); From 99edab5c099f70946d1dd1dca85016ea58be3062 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Sun, 28 Jun 2020 17:58:59 +0800 Subject: [PATCH 080/114] YamlReader class code adjustment and adding test cases (#141) --- .../yamlbeans/YamlReader.java | 93 ++------ .../esotericsoftware/yamlbeans/MergeTest.java | 45 ++++ .../yamlbeans/YamlReaderTest.java | 210 +++++++++++++++++- 3 files changed, 271 insertions(+), 77 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index df2c5b5..5ec2747 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -18,7 +18,6 @@ import static com.esotericsoftware.yamlbeans.parser.EventType.*; -import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; @@ -104,13 +103,13 @@ public T read (Class type, Class elementType) throws YamlException { if (event.type == STREAM_END) return null; if (event.type == DOCUMENT_START) break; } - return (T)readValue(type, elementType, null); + Object object = readValue(type, elementType, null); + parser.getNextEvent(); // consume it(DOCUMENT_END) + return (T) object; } catch (ParserException ex) { throw new YamlException("Error parsing YAML.", ex); } catch (TokenizerException ex) { throw new YamlException("Error tokenizing YAML.", ex); - } finally{ - parser.getNextEvent(); // consume it(DOCUMENT_END) } } @@ -257,14 +256,6 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } } - if (type == String.class) { - Event event = parser.getNextEvent(); - if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for String type but found: " + event.type); - String value = ((ScalarEvent)event).value; - if (anchor != null) anchors.put(anchor, value); - return value; - } - if (Beans.isScalar(type)) { Event event = parser.getNextEvent(); if (event.type != SCALAR) throw new YamlReaderException( @@ -272,40 +263,26 @@ private Object readValueInternal (Class type, Class elementType, String anchor) String value = ((ScalarEvent)event).value; try { Object convertedValue; - if (type == String.class) { + if (value == null) { + convertedValue = null; + } else if (type == String.class) { convertedValue = value; - } else if (type == Integer.TYPE) { - convertedValue = value == null ? null : Integer.decode(value); - } else if (type == Integer.class) { - convertedValue = value == null ? null : Integer.decode(value); - } else if (type == Boolean.TYPE) { - convertedValue = value == null ? null : Boolean.valueOf(value); - } else if (type == Boolean.class) { - convertedValue = value == null ? null : Boolean.valueOf(value); - } else if (type == Float.TYPE) { - convertedValue = value == null ? null : Float.valueOf(value); - } else if (type == Float.class) { - convertedValue = value == null ? null : Float.valueOf(value); - } else if (type == Double.TYPE) { - convertedValue = value == null ? null : Double.valueOf(value); - } else if (type == Double.class) { - convertedValue = value == null ? null : Double.valueOf(value); - } else if (type == Long.TYPE) { - convertedValue = value == null ? null : Long.decode(value); - } else if (type == Long.class) { - convertedValue = value == null ? null : Long.decode(value); - } else if (type == Short.TYPE) { - convertedValue = value == null ? null : Short.decode(value); - } else if (type == Short.class) { - convertedValue = value == null ? null : Short.decode(value); - } else if (type == Character.TYPE) { - convertedValue = value == null ? null : value.charAt(0); - } else if (type == Character.class) { - convertedValue = value == null ? null : value.charAt(0); - } else if (type == Byte.TYPE) { - convertedValue = value == null ? null : Byte.decode(value); - } else if (type == Byte.class) { - convertedValue = value == null ? null : Byte.decode(value); + } else if (type == Integer.TYPE || type == Integer.class) { + convertedValue = Integer.decode(value); + } else if (type == Boolean.TYPE || type == Boolean.class) { + convertedValue = Boolean.valueOf(value); + } else if (type == Float.TYPE || type == Float.class) { + convertedValue = Float.valueOf(value); + } else if (type == Double.TYPE || type == Double.class) { + convertedValue = Double.valueOf(value); + } else if (type == Long.TYPE || type == Long.class) { + convertedValue = Long.decode(value); + } else if (type == Short.TYPE || type == Short.class) { + convertedValue = Short.decode(value); + } else if (type == Character.TYPE || type == Character.class) { + convertedValue = value.charAt(0); + } else if (type == Byte.TYPE || type == Byte.class) { + convertedValue = Byte.decode(value); } else throw new YamlException("Unknown field type."); if (anchor != null) anchors.put(anchor, convertedValue); @@ -403,8 +380,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) // corresponding sequence, mapping... end Event nextEvent = parser.peekNextEvent(); EventType nextType = nextEvent.type; - if (nextType == SEQUENCE_START || nextType == MAPPING_START - || nextType == DOCUMENT_START || nextType == EventType.STREAM_START) { + if (nextType == SEQUENCE_START || nextType == MAPPING_START) { skipRange(); } else { // go though the next event, because this is a value of missing property @@ -468,12 +444,6 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (anchor != null) anchors.put(anchor, array); return array; } - case SCALAR: - if (((ScalarEvent)event).value == null) { - event = parser.getNextEvent(); - return null; - } - // Fall through. default: throw new YamlReaderException("Expected data for a " + type.getName() + " field but found: " + event.type); } @@ -543,33 +513,16 @@ private void skipRange() { case MAPPING_START: depth++; break; - case DOCUMENT_START: - depth++; - break; - case STREAM_START: - depth++; - break; case SEQUENCE_END: depth--; break; case MAPPING_END: depth--; break; - case DOCUMENT_END: - depth--; - break; - case STREAM_END: - depth--; - break; default: // ignore break; } } while (depth > 0); } - - public static void main (String[] args) throws Exception { - YamlReader reader = new YamlReader(new FileReader("test/test.yml")); - System.out.println(reader.read()); - } } diff --git a/test/com/esotericsoftware/yamlbeans/MergeTest.java b/test/com/esotericsoftware/yamlbeans/MergeTest.java index e8faba6..48e448b 100644 --- a/test/com/esotericsoftware/yamlbeans/MergeTest.java +++ b/test/com/esotericsoftware/yamlbeans/MergeTest.java @@ -8,10 +8,14 @@ import java.util.Map; import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlReader.YamlReaderException; + import static org.junit.Assert.*; public class MergeTest { + @SuppressWarnings("rawtypes") @Test public void testMerge() throws FileNotFoundException, YamlException { InputStream input = new FileInputStream("test/test-merge.yml"); @@ -24,4 +28,45 @@ public void testMerge() throws FileNotFoundException, YamlException { assertNull(stuff.get("<<")); } + @SuppressWarnings("unchecked") + @Test + public void testMergeMultipleMaps() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("test1: &1\n").append(" - key1: value1\n").append(" - key2: value2\n"); + sb.append("test2:\n").append(" << : *1"); + + YamlReader yamlReader = new YamlReader(sb.toString()); + Map map = (Map) yamlReader.read(); + assertEquals("value1", ((Map) map.get("test2")).get("key1")); + assertEquals("value2", ((Map) map.get("test2")).get("key2")); + } + + @SuppressWarnings("unchecked") + @Test + public void testMergeUpdateValue() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("test1: &1\n").append(" - key1: value1\n").append(" - key2: value2\n").append(" - key3: value3\n"); + + sb.append("test2:\n").append(" key2: value22\n").append(" << : *1\n").append(" key3: value33\n"); + + YamlReader yamlReader = new YamlReader(sb.toString()); + Map map = (Map) yamlReader.read(); + assertEquals("value1", ((Map) map.get("test2")).get("key1")); + assertEquals("value22", ((Map) map.get("test2")).get("key2")); + assertEquals("value33", ((Map) map.get("test2")).get("key3")); + } + + @Test + public void testMergeExpectThrowYamlReaderException() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("test1: &1 123\n"); + sb.append("<< : *1\n"); + + YamlReader yamlReader = new YamlReader(sb.toString()); + try { + yamlReader.read(); + fail("Expected a mapping or a sequence of mappings"); + } catch (YamlReaderException e) { + } + } } diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index b0ae7de..e884270 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -16,7 +16,6 @@ package com.esotericsoftware.yamlbeans; -import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; @@ -24,9 +23,8 @@ import java.util.List; import java.util.Map; -import org.junit.Test; -import com.esotericsoftware.yamlbeans.YamlWriterTest.Obj; +import com.esotericsoftware.yamlbeans.YamlReader.YamlReaderException; import junit.framework.TestCase; @@ -563,6 +561,14 @@ public void testReadMultiTpyeDocuments() throws YamlException { assertEquals("scalar", str); } + public void testRead() throws YamlException { + String yaml = "test"; + YamlReader reader = new YamlReader(yaml); + assertEquals("test", reader.read(String.class)); + assertEquals(null, reader.read()); + assertEquals(null, reader.read()); + } + public void testReadAll() throws YamlException { StringBuilder sb = new StringBuilder(); @@ -582,26 +588,216 @@ public void testReadAll() throws YamlException { assertEquals("test", ((Test) list.get(2)).stringValue); } - public void testReadAllCallingNextThrowsRuntimeException() { + public void testReadAllCallingNextExpectThrowsRuntimeException() { String yaml = "\ttest"; YamlReader reader = new YamlReader(yaml); Iterator iterator = reader.readAll(Object.class); try { iterator.next(); fail(); - } catch (Exception e) { + } catch (RuntimeException e) { } } - public void testReadAllCallingRemoveUnsupportedOperationException() { + public void testReadAllCallingRemoveExpectThrowsUnsupportedOperationException() { String yaml = "test"; YamlReader reader = new YamlReader(yaml); Iterator iterator = reader.readAll(Object.class); try { iterator.remove(); - ; fail(); } catch (Exception e) { } } + + public void testGetAnchor() throws YamlException { + String yaml = "&1 test\n---\naaa: &2 111\nbbb: &1 222"; + YamlReader reader = new YamlReader(yaml); + assertEquals(null, reader.get("1")); + reader.read(); + assertEquals("test", reader.get("1")); + + reader.read(); + assertEquals("222", reader.get("1")); + assertEquals("111", reader.get("2")); + } + + public void testGetAnchorsUseGuessNumberTypes() throws YamlException { + String yaml = "number: &1 123"; + YamlReader reader = new YamlReader(yaml); + reader.read(); + assertEquals("123", reader.get("1")); + + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.readConfig.guessNumberTypes = true; + reader = new YamlReader(yaml, yamlConfig); + reader.read(); + assertEquals(123, ((Long) reader.get("1")).intValue()); + } + + public void testReadExpectExpectThrowsParserException() { + String yaml = "[1,2,3"; + YamlReader reader = new YamlReader(yaml); + try { + reader.read(); + fail("Expect to throw ParserException."); + } catch (YamlException e) { + } + } + + public void testYamlConfigReadConfigClassLoader() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); + sb.append("stringValue: test\n"); + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.readConfig.classLoader = this.getClass().getClassLoader(); + YamlReader reader = new YamlReader(sb.toString(), yamlConfig); + assertEquals("test", reader.read(Test.class).stringValue); + } + + public void testYamlConfigReadConfigClassTags() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); + sb.append("stringValue: test\n"); + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.readConfig.classTags = false; + yamlConfig.setPropertyDefaultType(Test.class, "stringValue", String.class); + YamlReader reader = new YamlReader(sb.toString(), yamlConfig); + assertEquals("test", reader.read(Test.class).stringValue); + } + + public void testReadScalarTypeIsNull() throws YamlException { + String yaml = "key: "; + YamlReader reader = new YamlReader(yaml); + assertEquals(null, ((Map) reader.read()).get("key")); + } + + public void testReadScalarTypeIsString() throws YamlException { + String yaml = "String"; + YamlReader reader = new YamlReader(yaml); + assertEquals("String", reader.read(String.class)); + } + + public void testReadScalarTypeIsInteger() throws YamlException { + String yaml = "123"; + YamlReader reader = new YamlReader(yaml); + assertEquals(123, (int) reader.read(Integer.class)); + + reader = new YamlReader(yaml); + assertEquals(123, (int) reader.read(int.class)); + } + + public void testReadScalarTypeIsBoolean() throws YamlException { + String yaml = "true"; + YamlReader reader = new YamlReader(yaml); + assertEquals(true, (boolean) reader.read(Boolean.class)); + + reader = new YamlReader(yaml); + assertEquals(true, (boolean) reader.read(boolean.class)); + } + + public void testReadScalarTypeIsFloat() throws YamlException { + String yaml = "1.23"; + YamlReader reader = new YamlReader(yaml); + assertEquals(1.23f, reader.read(Float.class)); + + reader = new YamlReader(yaml); + assertEquals(1.23f, reader.read(float.class)); + } + + public void testReadScalarTypeIsDouble() throws YamlException { + String yaml = "1.23"; + YamlReader reader = new YamlReader(yaml); + assertEquals(1.23, reader.read(Double.class)); + + reader = new YamlReader(yaml); + assertEquals(1.23, reader.read(double.class)); + } + + public void testReadScalarTypeIsLong() throws YamlException { + String yaml = "100"; + YamlReader reader = new YamlReader(yaml); + assertEquals(100, (long) reader.read(Long.class)); + + reader = new YamlReader(yaml); + assertEquals(100, (long) reader.read(long.class)); + } + + public void testReadScalarTypeIsShort() throws YamlException { + String yaml = "100"; + YamlReader reader = new YamlReader(yaml); + assertEquals(100, (short) reader.read(Short.class)); + + reader = new YamlReader(yaml); + assertEquals(100, (short) reader.read(short.class)); + } + + public void testReadScalarTypeIsCharacter() throws YamlException { + String yaml = "A"; + YamlReader reader = new YamlReader(yaml); + assertEquals('A', (char) reader.read(Character.class)); + + reader = new YamlReader(yaml); + assertEquals('A', (char) reader.read(char.class)); + } + + public void testReadScalarTypeIsByte() throws YamlException { + String yaml = "1"; + YamlReader reader = new YamlReader(yaml); + assertEquals(1, (byte) reader.read(Byte.class)); + + reader = new YamlReader(yaml); + assertEquals(1, (byte) reader.read(byte.class)); + } + + public void testReadEnumExpectThrowsYamlReaderException() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); + sb.append("testEnum: d\n"); + YamlReader reader = new YamlReader(sb.toString()); + try { + reader.read(); + fail("Unable to find enum value"); + } catch (YamlReaderException e) { + } + } + + public void testReadExplicitKey() throws YamlException { + String yaml = "? key: value"; + YamlReader reader = new YamlReader(yaml); + assertEquals("value", ((Map) reader.read()).get("key")); + } + + public void testReadYamlConfigTagSuffix() throws YamlException { + String yaml = "key: !!str 123456"; + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.tagSuffix = "-test"; + YamlReader reader = new YamlReader(yaml, yamlConfig); + Map map = (Map) reader.read(); + assertEquals("tag:yaml.org,2002:str", map.get("key-test")); + + yaml = "info: !!map\n !!str key: !!str 123456"; + reader = new YamlReader(yaml, yamlConfig); + map = (Map) reader.read(); + assertEquals("tag:yaml.org,2002:map", map.get("info-test")); + } + + public void testReadYamlConfigAllowDuplicates() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); + sb.append("stringValue: test\n"); + sb.append("stringValue: test\n"); + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.allowDuplicates = true; + YamlReader reader = new YamlReader(sb.toString(), yamlConfig); + reader.read(); + + yamlConfig.allowDuplicates = false; + reader = new YamlReader(sb.toString(), yamlConfig); + try { + reader.read(); + fail("must be throws YamlReaderException"); + } catch (YamlReaderException e) { + } + } } From ae74cee8c8644546953d8c9e85335210796fa886 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:42:07 +0800 Subject: [PATCH 081/114] write version and tags (#142) --- src/com/esotericsoftware/yamlbeans/Version.java | 7 +++++++ src/com/esotericsoftware/yamlbeans/YamlConfig.java | 9 ++++++++- src/com/esotericsoftware/yamlbeans/YamlWriter.java | 3 ++- src/com/esotericsoftware/yamlbeans/emitter/Emitter.java | 2 +- .../yamlbeans/emitter/EmitterConfig.java | 9 --------- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Version.java b/src/com/esotericsoftware/yamlbeans/Version.java index b1bf27a..5477a68 100644 --- a/src/com/esotericsoftware/yamlbeans/Version.java +++ b/src/com/esotericsoftware/yamlbeans/Version.java @@ -31,11 +31,13 @@ public Version (String value) { if (dotIndex == -1) throw new IllegalArgumentException("value must contain a period: " + value); major = Integer.parseInt(value.substring(0, dotIndex)); minor = Integer.parseInt(value.substring(dotIndex + 1)); + check(); } public Version (int major, int minor) { this.major = major; this.minor = minor; + check(); } public String toString () { @@ -63,4 +65,9 @@ public boolean equals (Object obj) { if (minor != other.minor) return false; return true; } + + private void check() { + if (major != 1) throw new IllegalArgumentException("major must be 1."); + if (minor < 0 || minor > 1) throw new IllegalArgumentException("minor must be 0 or 1."); + } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index a6ab104..1180035 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -140,6 +140,8 @@ static public class WriteConfig { boolean keepBeanPropertyOrder = false; WriteClassName writeClassName = WriteClassName.AUTO; Quote quote = Quote.NONE; + Version version; + Map tags; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { @@ -193,7 +195,12 @@ public void setKeepBeanPropertyOrder (boolean keepBeanPropertyOrder) { /** Sets the YAML version to output. Default is 1.1. */ public void setVersion (Version version) { - emitterConfig.setVersion(version); + this.version = version; + } + + /** Sets the YAML tags to output. */ + public void setTags(Map tags) { + this.tags = tags; } /** If true, the YAML output will be canonical. Default is false. */ diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 5222039..1490da6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -92,7 +92,8 @@ private void writeInternal (Object object) throws YamlException { emitter.emit(Event.STREAM_START); started = true; } - emitter.emit(new DocumentStartEvent(config.writeConfig.explicitFirstDocument, null, null)); + emitter.emit(new DocumentStartEvent(config.writeConfig.explicitFirstDocument, config.writeConfig.version, + config.writeConfig.tags)); isRoot = true; writeValue(object, config.writeConfig.writeRootTags ? null : object.getClass(), null, null); emitter.emit(new DocumentEndEvent(config.writeConfig.explicitEndDocument)); diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 240a90f..71ed689 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -144,7 +144,7 @@ public void expect () throws IOException { throw new EmitterException("Unsupported YAML version: " + documentStartEvent.version); writer.writeVersionDirective(documentStartEvent.version.toString()); } - if ((documentStartEvent.version != null && documentStartEvent.version.equals(1, 0)) || config.version.equals(1, 0)) { + if ((documentStartEvent.version != null && documentStartEvent.version.equals(1, 0))) { isVersion10 = true; tagPrefixes = new HashMap(DEFAULT_TAG_PREFIXES_1_0); } else diff --git a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java index 174fd7f..bd80c99 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java @@ -16,23 +16,14 @@ package com.esotericsoftware.yamlbeans.emitter; -import com.esotericsoftware.yamlbeans.Version; - /** @author Nathan Sweet */ public class EmitterConfig { - Version version = new Version(1, 1); boolean canonical; boolean useVerbatimTags = true; int indentSize = 3; int wrapColumn = 100; boolean escapeUnicode = true; - /** Sets the YAML version to output. Default is 1.1. */ - public void setVersion (Version version) { - if (version == null) throw new IllegalArgumentException("version cannot be null."); - this.version = version; - } - /** If true, the YAML output will be canonical. Default is false. */ public void setCanonical (boolean canonical) { this.canonical = canonical; From b355ae920b367f28326e6baaa567c2e164b9960d Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Fri, 3 Jul 2020 16:59:03 +0800 Subject: [PATCH 082/114] add test cases (#143) --- .../yamlbeans/YamlConfigTest.java | 547 ++++++++++++++++++ .../yamlbeans/YamlReaderTest.java | 4 +- 2 files changed, 549 insertions(+), 2 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/YamlConfigTest.java diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java new file mode 100644 index 0000000..e1a6565 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -0,0 +1,547 @@ +package com.esotericsoftware.yamlbeans; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlConfig.Quote; +import com.esotericsoftware.yamlbeans.YamlReader.YamlReaderException; +import com.esotericsoftware.yamlbeans.scalar.DateSerializer; + +public class YamlConfigTest { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private static final String TESTOBJECT_TAG = "!com.esotericsoftware.yamlbeans.YamlConfigTest$TestObject"; + + private YamlConfig yamlConfig; + + @Before + public void setup() throws Exception { + yamlConfig = new YamlConfig(); + } + + @Test + public void testSetClassTag() throws YamlException { + yamlConfig.setClassTag("String", String.class); + String yaml = "!String test"; + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + assertEquals("test", yamlReader.read()); + + try { + yamlConfig.setClassTag(null, String.class); + } catch (IllegalArgumentException e) { + assertEquals("tag cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setClassTag("str", null); + } catch (IllegalArgumentException e) { + assertEquals("type cannot be null.", e.getMessage()); + } + } + + @Test + public void testSetScalarSerializer() throws YamlException { + TimeZone timeZone = TimeZone.getTimeZone("GMT+0"); + TimeZone.setDefault(timeZone); + + yamlConfig.setScalarSerializer(Date.class, new DateSerializer()); + String yaml = "!java.util.Date 1970-01-01 00:00:00"; + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + Object object = yamlReader.read(); + assertEquals(Date.class, object.getClass()); + assertEquals(0, ((Date) object).getTime()); + + try { + yamlConfig.setScalarSerializer(null, new DateSerializer()); + } catch (IllegalArgumentException e) { + assertEquals("type cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setScalarSerializer(Date.class, null); + } catch (IllegalArgumentException e) { + assertEquals("serializer cannot be null.", e.getMessage()); + } + } + + @Test + public void testSetPropertyElementType() throws YamlException { + yamlConfig.setPropertyElementType(TestObject.class, "objects", Date.class); + + StringBuilder sb = new StringBuilder(); + sb.append("objects:" + LINE_SEPARATOR); + sb.append("- 2020-06-30 00:00:00" + LINE_SEPARATOR); + + YamlReader yamlReader = new YamlReader(sb.toString(), yamlConfig); + TestObject testObject = yamlReader.read(TestObject.class); + assertEquals(Date.class, testObject.objects.get(0).getClass()); + + try { + yamlConfig.setPropertyElementType(null, "objects", Date.class); + } catch (IllegalArgumentException e) { + assertEquals("type cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyElementType(TestObject.class, null, Date.class); + } catch (IllegalArgumentException e) { + assertEquals("propertyName cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyElementType(TestObject.class, "objects", null); + } catch (IllegalArgumentException e) { + assertEquals("propertyType cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyElementType(TestObject.class, "aaa", Object.class); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("does not have a property named")); + } + + try { + yamlConfig.setPropertyElementType(TestObject.class, "object", Date.class); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("class must be a Collection or Map")); + } + } + + @Test + public void testSetPropertyDefaultType() throws YamlException { + yamlConfig.setPropertyDefaultType(TestObject.class, "object", Date.class); + + String yaml = "object: 2020-06-30 00:00:00"; + + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + TestObject testObject = yamlReader.read(TestObject.class); + assertEquals(Date.class, testObject.object.getClass()); + + try { + yamlConfig.setPropertyDefaultType(null, "object", Date.class); + } catch (IllegalArgumentException e) { + assertEquals("type cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyDefaultType(TestObject.class, null, Date.class); + } catch (IllegalArgumentException e) { + assertEquals("propertyName cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyDefaultType(TestObject.class, "object", null); + } catch (IllegalArgumentException e) { + assertEquals("defaultType cannot be null.", e.getMessage()); + } + + try { + yamlConfig.setPropertyDefaultType(TestObject.class, "aaa", Date.class); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("does not have a property named")); + } + } + + @Test + public void testSetBeanProperties() throws YamlException { + String yaml = "a: test"; + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + TestObject testObject = yamlReader.read(TestObject.class); + assertEquals("test", testObject.a); + + yamlConfig.setBeanProperties(false); + yamlReader = new YamlReader(yaml, yamlConfig); + try { + yamlReader.read(TestObject.class); + } catch (YamlReaderException e) { + assertTrue(e.getMessage().contains("Unable to find property")); + } + } + + @Test + public void testSetPrivateConstructors() throws YamlException { + String yaml = "a: test"; + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + TestObject testObject = yamlReader.read(TestObject.class); + assertEquals("test", testObject.a); + + yamlConfig.setPrivateConstructors(false); + yamlReader = new YamlReader(yaml, yamlConfig); + try { + yamlReader.read(TestObject.class); + } catch (YamlReaderException e) { + assertTrue(e.getMessage().contains("Error creating object")); + } + } + + @SuppressWarnings("unchecked") + @Test + public void testSetTagSuffix() throws YamlException { + StringWriter stringWriter = new StringWriter(); + yamlConfig.setTagSuffix("tag"); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + Map map = new HashMap(); + map.put("a", "111"); + map.put("btag", "222"); + yamlWriter.write(map); + yamlWriter.close(); + + YamlReader yamlReader = new YamlReader(stringWriter.toString(), yamlConfig); + Map result = yamlReader.read(Map.class); + assertTrue(result.containsKey("a")); + assertFalse(result.containsKey("btag")); + assertTrue(result.containsKey("atag")); + } + + @Test + public void testSetExplicitEndDocument() throws YamlException { + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + + assertEquals("test" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setExplicitEndDocument(true); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("test" + LINE_SEPARATOR + "..." + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetWriteRootTags() throws YamlException { + TestObject to = new TestObject(); + to.a = "test"; + + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(to); + yamlWriter.close(); + + assertEquals(TESTOBJECT_TAG + LINE_SEPARATOR + "a: test" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.writeConfig.setWriteRootTags(false); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(to); + yamlWriter.close(); + assertEquals("a: test" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetWriteRootElementTags() throws YamlException { + TestObject to1 = new TestObject(); + to1.a = "test"; + List list = new ArrayList(); + list.add(to1); + + TestObject to2 = new TestObject(); + to2.a = "test"; + Map map = new HashMap(); + map.put("test", to2); + + yamlConfig.writeConfig.setWriteRootElementTags(true); + int indentSize = 3; + StringWriter stringWriter = new StringWriter(); + yamlConfig.writeConfig.setIndentSize(indentSize); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.write(map); + yamlWriter.close(); + + assertEquals("- " + TESTOBJECT_TAG + LINE_SEPARATOR + multipleSpaces(indentSize) + "a: test" + LINE_SEPARATOR + + "--- " + LINE_SEPARATOR + "test: " + TESTOBJECT_TAG + LINE_SEPARATOR + multipleSpaces(indentSize) + + "a: test" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.writeConfig.setWriteRootElementTags(false); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.write(map); + yamlWriter.close(); + + assertEquals( + "-" + multipleSpaces(indentSize - 1) + "a: test" + LINE_SEPARATOR + "--- " + LINE_SEPARATOR + "test: " + + LINE_SEPARATOR + multipleSpaces(indentSize) + "a: test" + LINE_SEPARATOR, + stringWriter.toString()); + } + + @Test + public void testSetWriteDefaultValues() throws YamlException { + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + TestObject testObject = new TestObject(); + yamlWriter.write(testObject); + yamlWriter.close(); + + assertEquals(TESTOBJECT_TAG + " {}" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.writeConfig.setWriteDefaultValues(true); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(testObject); + yamlWriter.close(); + assertFalse((TESTOBJECT_TAG + " {}" + LINE_SEPARATOR).equals(stringWriter.toString())); + assertTrue(stringWriter.toString().contains(TESTOBJECT_TAG + LINE_SEPARATOR)); + assertTrue(stringWriter.toString().contains("a: " + LINE_SEPARATOR)); + + assertTrue(stringWriter.toString().contains("object: " + LINE_SEPARATOR)); + } + + @Test + public void testSetKeepBeanPropertyOrder() throws YamlException { + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + TestObject testObject = new TestObject(); + testObject.name = "Jack"; + testObject.age = 18; + yamlWriter.write(testObject); + yamlWriter.close(); + + assertEquals(TESTOBJECT_TAG + LINE_SEPARATOR + "name: Jack" + LINE_SEPARATOR + "age: 18" + LINE_SEPARATOR, + stringWriter.toString()); + + yamlConfig.writeConfig.setKeepBeanPropertyOrder(true); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(testObject); + yamlWriter.close(); + assertEquals(TESTOBJECT_TAG + LINE_SEPARATOR + "age: 18" + LINE_SEPARATOR + "name: Jack" + LINE_SEPARATOR, + stringWriter.toString()); + } + + @Test + public void testSetVersion() throws YamlException { + + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("test" + LINE_SEPARATOR, stringWriter.toString()); + + Version version = new Version(1, 0); + yamlConfig.writeConfig.setVersion(version); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("%YAML 1.0" + LINE_SEPARATOR + "--- test" + LINE_SEPARATOR, stringWriter.toString()); + + version = new Version(1, 1); + yamlConfig.writeConfig.setVersion(version); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("%YAML 1.1" + LINE_SEPARATOR + "--- test" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetTags() throws YamlException { + Map tags = new HashMap(); + tags.put("!foo!", "bar"); + yamlConfig.writeConfig.setTags(tags); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("%TAG !foo! bar" + LINE_SEPARATOR + "--- test" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetCanonical() throws YamlException { + yamlConfig.writeConfig.setCanonical(true); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test"); + yamlWriter.close(); + assertEquals("--- " + LINE_SEPARATOR + "!java.lang.String \"test\"" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetIndentSize() throws YamlException { + int indentSize = 5; + List list = new ArrayList(); + list.add(1); + + StringWriter stringWriter = new StringWriter(); + yamlConfig.writeConfig.setCanonical(true); + yamlConfig.writeConfig.setIndentSize(indentSize); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("--- " + LINE_SEPARATOR + "[" + LINE_SEPARATOR + multipleSpaces(indentSize) + + "!java.lang.Integer \"1\"," + LINE_SEPARATOR + "]" + LINE_SEPARATOR, stringWriter.toString()); + + try { + yamlConfig.writeConfig.setIndentSize(1); + } catch (Exception e) { + assertEquals("indentSize cannot be less than 2.", e.getMessage()); + } + } + + @Test + public void testSetWrapColumn() throws YamlException { + String yaml = "aaaaaa aaaaa"; + int indentSize = 3; + yamlConfig.writeConfig.setIndentSize(indentSize); + yamlConfig.writeConfig.setWrapColumn(5); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(yaml); + yamlWriter.close(); + + assertEquals("aaaaaa" + LINE_SEPARATOR + multipleSpaces(indentSize) + "aaaaa" + LINE_SEPARATOR, + stringWriter.toString()); + + YamlReader yamlReader = new YamlReader(stringWriter.toString()); + assertEquals(yaml, yamlReader.read(String.class)); + + try { + yamlConfig.writeConfig.setWrapColumn(3); + } catch (IllegalArgumentException e) { + assertEquals("wrapColumn must be greater than 4.", e.getMessage()); + } + + } + + @Test + public void testSetUseVerbatimTags() throws YamlException { + List list = new LinkedList(); + list.add(1); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + + assertEquals("!java.util.LinkedList" + LINE_SEPARATOR + "- 1" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setUseVerbatimTags(true); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + + assertEquals("!" + LINE_SEPARATOR + "- 1" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetQuoteChar() throws YamlException { + + StringWriter stringWriter = new StringWriter(); + yamlConfig.writeConfig.setQuoteChar(Quote.NONE); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("123"); + yamlWriter.close(); + assertEquals("123" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setQuoteChar(Quote.SINGLE); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("123"); + yamlWriter.close(); + assertEquals("\'123\'" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setQuoteChar(Quote.DOUBLE); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("123"); + yamlWriter.close(); + assertEquals("\"123\"" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testSetDefaultVersion() throws YamlException { + String yaml = "!!str test"; + yamlConfig.readConfig.setDefaultVersion(new Version(1, 1)); + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + assertEquals("test", yamlReader.read(String.class)); + + try { + yamlConfig.readConfig.setDefaultVersion(null); + } catch (IllegalArgumentException e) { + assertEquals("defaultVersion cannot be null.", e.getMessage()); + } + + } + + @Test + public void testConstructorParameters() throws YamlException { + yamlConfig.readConfig.setConstructorParameters(TestObject.class, new Class[] { String.class }, + new String[] { "a" }); + YamlReader yamlReader = new YamlReader("a: test", yamlConfig); + assertEquals("test", yamlReader.read(TestObject.class).a); + + try { + yamlConfig.readConfig.setConstructorParameters(null, new Class[] { String.class }, new String[] { "a" }); + } catch (IllegalArgumentException e) { + assertEquals("type cannot be null.", e.getMessage()); + } + + try { + yamlConfig.readConfig.setConstructorParameters(TestObject.class, null, new String[] { "a" }); + } catch (IllegalArgumentException e) { + assertEquals("parameterTypes cannot be null.", e.getMessage()); + } + + try { + yamlConfig.readConfig.setConstructorParameters(TestObject.class, new Class[] { String.class }, null); + } catch (IllegalArgumentException e) { + assertEquals("parameterNames cannot be null.", e.getMessage()); + } + + try { + yamlConfig.readConfig.setConstructorParameters(TestObject.class, new Class[] { String.class, String.class }, + new String[] { "a", "name" }); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().startsWith("Unable to find constructor: ")); + } + + } + + private String multipleSpaces(int indentSize) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < indentSize; i++) { + sb.append(" "); + } + return sb.toString(); + } + + static class TestObject { + private String a; + public int age; + public String name; + public Object object; + public List objects; + + private TestObject() { + } + + public TestObject(String a) { + this.a = a; + } + + public String getA() { + return a; + } + + public void setA(String a) { + this.a = a; + } + } +} diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index e884270..ff40eaa 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -482,7 +482,7 @@ public void testGuessNumberTypes() throws YamlException { map.put("date", "2001-01-23"); YamlConfig config = new YamlConfig(); - config.readConfig.guessNumberTypes = true; + config.readConfig.setGuessNumberTypes(true); StringWriter sw = new StringWriter(); YamlWriter writer = new YamlWriter(sw, config); writer.write(map); @@ -660,7 +660,7 @@ public void testYamlConfigReadConfigClassTags() throws YamlException { sb.append("--- !com.esotericsoftware.yamlbeans.YamlReaderTest$Test\n"); sb.append("stringValue: test\n"); YamlConfig yamlConfig = new YamlConfig(); - yamlConfig.readConfig.classTags = false; + yamlConfig.readConfig.setClassTags(false); yamlConfig.setPropertyDefaultType(Test.class, "stringValue", String.class); YamlReader reader = new YamlReader(sb.toString(), yamlConfig); assertEquals("test", reader.read(Test.class).stringValue); From e4cb67ed26be2898e4bd83f4d825f3ed401b5fea Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 6 Jul 2020 18:43:48 +0800 Subject: [PATCH 083/114] Remove the redundant commas (#144) --- .../yamlbeans/emitter/Emitter.java | 2 -- .../yamlbeans/YamlConfigTest.java | 27 ++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 71ed689..a4a4667 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -216,7 +216,6 @@ public void expect () throws IOException { indent = indents.remove(0); flowLevel--; if (config.canonical) { - writer.writeIndicator(",", false, false, false); writer.writeIndent(indent); } writer.writeIndicator("]", false, false, false); @@ -270,7 +269,6 @@ public void expect () throws IOException { indent = indents.remove(0); flowLevel--; if (config.canonical) { - writer.writeIndicator(",", false, false, false); writer.writeIndent(indent); } writer.writeIndicator("}", false, false, false); diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index e1a6565..33f2290 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -365,12 +365,37 @@ public void testSetTags() throws YamlException { @Test public void testSetCanonical() throws YamlException { + int indentSize = 3; + yamlConfig.writeConfig.setIndentSize(indentSize); yamlConfig.writeConfig.setCanonical(true); StringWriter stringWriter = new StringWriter(); YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); yamlWriter.write("test"); yamlWriter.close(); assertEquals("--- " + LINE_SEPARATOR + "!java.lang.String \"test\"" + LINE_SEPARATOR, stringWriter.toString()); + + List list = new ArrayList(); + list.add("111"); + list.add("222"); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("--- " + LINE_SEPARATOR + "[" + LINE_SEPARATOR + multipleSpaces(indentSize) + + "!java.lang.String \"111\"," + LINE_SEPARATOR + multipleSpaces(indentSize) + + "!java.lang.String \"222\"" + LINE_SEPARATOR + "]" + LINE_SEPARATOR, stringWriter.toString()); + + Map map = new HashMap(); + map.put("key", "value"); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals( + "--- " + LINE_SEPARATOR + "{" + LINE_SEPARATOR + multipleSpaces(indentSize) + + "? !java.lang.String \"key\"" + LINE_SEPARATOR + multipleSpaces(indentSize) + + ": !java.lang.String \"value\"" + LINE_SEPARATOR + "}" + LINE_SEPARATOR, + stringWriter.toString()); } @Test @@ -386,7 +411,7 @@ public void testSetIndentSize() throws YamlException { yamlWriter.write(list); yamlWriter.close(); assertEquals("--- " + LINE_SEPARATOR + "[" + LINE_SEPARATOR + multipleSpaces(indentSize) - + "!java.lang.Integer \"1\"," + LINE_SEPARATOR + "]" + LINE_SEPARATOR, stringWriter.toString()); + + "!java.lang.Integer \"1\"" + LINE_SEPARATOR + "]" + LINE_SEPARATOR, stringWriter.toString()); try { yamlConfig.writeConfig.setIndentSize(1); From c5de4ea147e8b6a9d5a807c7c20c44b7338b5831 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Wed, 8 Jul 2020 22:10:34 +0800 Subject: [PATCH 084/114] Restoration logic (#145) --- src/com/esotericsoftware/yamlbeans/Beans.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index e45db6c..382d735 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -184,7 +184,7 @@ private static Property getProperty(Class type, boolean beanProperties, boole } } if (getMethod != null && (setMethod != null || constructorProperty)) { - property = new MethodProperty(name, setMethod, getMethod); + return new MethodProperty(name, setMethod, getMethod); } } From 53d156f5ff6e2aae1d44104038433c14f5127ee6 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 9 Jul 2020 23:19:53 +0800 Subject: [PATCH 085/114] Anchor and Alias (#146) --- src/com/esotericsoftware/yamlbeans/YamlReader.java | 1 + .../esotericsoftware/yamlbeans/YamlReaderTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 5ec2747..f449aae 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -96,6 +96,7 @@ public T read (Class type) throws YamlException { /** Reads an array, Map, List, or Collection object of the specified type from YAML, using the specified element type. * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ public T read (Class type, Class elementType) throws YamlException { + anchors.clear(); try { while (true) { Event event = parser.getNextEvent(); diff --git a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java index ff40eaa..1af7479 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlReaderTest.java @@ -635,6 +635,19 @@ public void testGetAnchorsUseGuessNumberTypes() throws YamlException { assertEquals(123, ((Long) reader.get("1")).intValue()); } + public void testAnchorAndAlias() throws YamlException { + String yaml = "key: &1 value\nkey1: *1\n---\nkey: *1"; + YamlReader reader = new YamlReader(yaml); + assertEquals("value", ((Map) reader.read()).get("key1")); + assertEquals("value", reader.get("1")); + + try { + reader.read(); + } catch (YamlReaderException e) { + assertTrue(e.getMessage().contains("Unknown anchor: ")); + } + } + public void testReadExpectExpectThrowsParserException() { String yaml = "[1,2,3"; YamlReader reader = new YamlReader(yaml); From 817df2111c3db8353a49fe5427ae11ac1ceaee80 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Fri, 10 Jul 2020 21:35:45 +0800 Subject: [PATCH 086/114] Tag Directive code changes (#147) --- .../yamlbeans/YamlConfig.java | 1 + .../yamlbeans/YamlReader.java | 1 + .../yamlbeans/parser/Parser.java | 2 +- .../yamlbeans/TagDirectiveTest.java | 84 +++++++++++++++++++ .../yamlbeans/YamlConfigTest.java | 4 +- .../issues/issue101/Issue101Test.java | 34 ++++++++ 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java create mode 100644 test/com/esotericsoftware/yamlbeans/issues/issue101/Issue101Test.java diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 1180035..110c479 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -69,6 +69,7 @@ public void setAllowDuplicates (boolean allowDuplicates) { public void setClassTag (String tag, Class type) { if (tag == null) throw new IllegalArgumentException("tag cannot be null."); if (type == null) throw new IllegalArgumentException("type cannot be null."); + if (!tag.startsWith("!")) tag = "!" + tag; classNameToTag.put(type.getName(), tag); tagToClass.put(tag, type); } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index f449aae..0aaaf19 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -182,6 +182,7 @@ private Class chooseType (String tag, Class defaultType, Class provided ClassLoader classLoader = (config.readConfig.classLoader == null ? this.getClass().getClassLoader() : config.readConfig.classLoader); + tag = tag.replace("!", ""); try { Class loadedFromTag = findTagClass(tag, classLoader); if (loadedFromTag != null) { diff --git a/src/com/esotericsoftware/yamlbeans/parser/Parser.java b/src/com/esotericsoftware/yamlbeans/parser/Parser.java index 5c3e65d..eb3edc6 100644 --- a/src/com/esotericsoftware/yamlbeans/parser/Parser.java +++ b/src/com/esotericsoftware/yamlbeans/parser/Parser.java @@ -222,7 +222,7 @@ public Event produce () { if (tokenizer.peekNextTokenType() == ANCHOR) anchor = ((AnchorToken)tokenizer.getNextToken()).getInstanceName(); } String tag = null; - if (tagHandle != null && !tagHandle.equals("!")) { + if (tagHandle != null) { if (!tagHandles.containsKey(tagHandle)) throw new ParserException("Undefined tag handle: " + tagHandle); tag = tagHandles.get(tagHandle) + tagSuffix; } else diff --git a/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java b/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java new file mode 100644 index 0000000..31c2715 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java @@ -0,0 +1,84 @@ +package com.esotericsoftware.yamlbeans; + +import static org.junit.Assert.assertEquals; + +import java.util.Iterator; + +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.document.YamlDocumentReader; +import com.esotericsoftware.yamlbeans.document.YamlElement; +import com.esotericsoftware.yamlbeans.document.YamlSequence; + +public class TagDirectiveTest { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void testTagDirective() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("%TAG !yaml! tag:yaml.org,2002:").append(LINE_SEPARATOR); + sb.append("---").append(LINE_SEPARATOR); + sb.append("!yaml!str \"foo\""); + + YamlReader yamlReader = new YamlReader(sb.toString()); + assertEquals("foo", yamlReader.read().toString()); + } + + @Test(expected = YamlException.class) + public void testRepeatedTagDirective() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("%TAG %TAG ! !foo").append(LINE_SEPARATOR); + sb.append("%TAG %TAG ! !foo").append(LINE_SEPARATOR); + sb.append("---").append(LINE_SEPARATOR); + sb.append("bar"); + + new YamlReader(sb.toString()).read(); + } + + @Test + public void testTagHandles() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("%TAG ! !").append(LINE_SEPARATOR); + sb.append("%TAG !yaml! tag:yaml.org,2002:").append(LINE_SEPARATOR); + sb.append("%TAG !o! tag:ben-kiki.org,2000:").append(LINE_SEPARATOR); + sb.append("---").append(LINE_SEPARATOR); + sb.append("- !foo bar").append(LINE_SEPARATOR); + sb.append("- !!str string").append(LINE_SEPARATOR); + sb.append("- !o!type baz").append(LINE_SEPARATOR); + YamlDocumentReader yamlDocumentReader = new YamlDocumentReader(sb.toString()); + Iterator iterator = yamlDocumentReader.read(YamlSequence.class).iterator(); + + YamlElement yamlElement = iterator.next(); + assertEquals("!foo", yamlElement.getTag()); + + yamlElement = iterator.next(); + assertEquals("tag:yaml.org,2002:str", yamlElement.getTag()); + + yamlElement = iterator.next(); + assertEquals("tag:ben-kiki.org,2000:type", yamlElement.getTag()); + } + + @Test + public void testVersion() throws YamlException { + String yaml = "!str test"; + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.readConfig.setDefaultVersion(new Version(1, 0)); + YamlReader yamlReader = new YamlReader(yaml, yamlConfig); + assertEquals("test", yamlReader.read()); + + yaml = "!!str test"; + yamlConfig.readConfig.setDefaultVersion(new Version(1, 1)); + yamlReader = new YamlReader(yaml, yamlConfig); + assertEquals("test", yamlReader.read()); + + yaml = "!!str test"; + yamlConfig.readConfig.setDefaultVersion(new Version(1, 0)); + yamlReader = new YamlReader(yaml, yamlConfig); + try { + yamlReader.read(); + } catch (YamlException e) { + assertEquals("Error parsing YAML.", e.getMessage()); + } + } +} diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 33f2290..b8b2eab 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -36,9 +36,11 @@ public void setup() throws Exception { @Test public void testSetClassTag() throws YamlException { yamlConfig.setClassTag("String", String.class); - String yaml = "!String test"; + yamlConfig.setClassTag("!Int", Integer.class); + String yaml = "!String test\n---\n!Int 1"; YamlReader yamlReader = new YamlReader(yaml, yamlConfig); assertEquals("test", yamlReader.read()); + assertEquals(1, yamlReader.read()); try { yamlConfig.setClassTag(null, String.class); diff --git a/test/com/esotericsoftware/yamlbeans/issues/issue101/Issue101Test.java b/test/com/esotericsoftware/yamlbeans/issues/issue101/Issue101Test.java new file mode 100644 index 0000000..30791ed --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/issues/issue101/Issue101Test.java @@ -0,0 +1,34 @@ +package com.esotericsoftware.yamlbeans.issues.issue101; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; + +public class Issue101Test { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void test() throws YamlException { + StringBuilder sb = new StringBuilder(); + sb.append("!com.esotericsoftware.yamlbeans.issues.issue101.Issue101Test$TestObject").append(LINE_SEPARATOR); + sb.append("test: test"); + YamlReader yamlReader = new YamlReader(sb.toString()); + TestObject testObject = (TestObject) yamlReader.read(); + assertEquals("test", testObject.test); + + sb = new StringBuilder(); + sb.append("!").append(LINE_SEPARATOR); + sb.append("test: test"); + yamlReader = new YamlReader(sb.toString()); + testObject = (TestObject) yamlReader.read(); + assertEquals("test", testObject.test); + } + + static class TestObject { + public String test; + } +} From 15a6a3d2998246c8d0fffe1554b115ae3e7bc7d3 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 14 Jul 2020 15:51:27 +0800 Subject: [PATCH 087/114] Add test case to improve the YamlWriter line coverage (#148) --- .../esotericsoftware/yamlbeans/YamlWriter.java | 5 ----- .../yamlbeans/YamlConfigTest.java | 9 +++++---- .../yamlbeans/YamlWriterTest.java | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index 1490da6..e62a5f5 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -104,11 +104,6 @@ private void writeInternal (Object object) throws YamlException { } } - /** Returns the YAML emitter, which allows the YAML output to be configured. */ - public Emitter getEmitter () { - return emitter; - } - /** Writes any buffered objects, then resets the list of anchored objects. * @see WriteConfig#setAutoAnchor(boolean) */ public void clearAnchors () throws YamlException { diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index b8b2eab..9d085af 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -198,15 +198,16 @@ public void testSetTagSuffix() throws YamlException { YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); Map map = new HashMap(); map.put("a", "111"); - map.put("btag", "222"); + map.put("atag", "!222"); yamlWriter.write(map); yamlWriter.close(); + assertEquals("a: !222 111" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.setClassTag("!222", String.class); YamlReader yamlReader = new YamlReader(stringWriter.toString(), yamlConfig); Map result = yamlReader.read(Map.class); - assertTrue(result.containsKey("a")); - assertFalse(result.containsKey("btag")); - assertTrue(result.containsKey("atag")); + assertEquals(map.toString(), result.toString()); } @Test diff --git a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java index 35fddf4..9bf6d91 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlWriterTest.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -309,6 +310,23 @@ public String write (File file) throws YamlException { assertEquals(object.file, roundTrip.file); } + public void testSetAlias() throws YamlException { + Map map = new LinkedHashMap(); + Test test = new Test(); + test.stringValue = "test"; + map.put("key1", test); + map.put("key2", test); + + StringWriter sw = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(sw); + yamlWriter.setAlias(test, "t"); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("!java.util.LinkedHashMap" + LINE_SEPARATOR + + "key1: &t !com.esotericsoftware.yamlbeans.YamlWriterTest$Test" + LINE_SEPARATOR + + " stringValue: test" + LINE_SEPARATOR + "key2: *t" + LINE_SEPARATOR, sw.toString()); + } + private Object roundTrip (Object object) throws Exception { return roundTrip(object, null, new YamlConfig()); } From 1e2162594c19108de2001da2dfbd41121be7d944 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Wed, 22 Jul 2020 15:32:56 +0800 Subject: [PATCH 088/114] Version is changed to the enumeration class (#149) --- .../esotericsoftware/yamlbeans/Version.java | 76 ++++++++++--------- .../yamlbeans/YamlConfig.java | 2 +- .../yamlbeans/emitter/Emitter.java | 5 +- .../yamlbeans/parser/Parser.java | 10 +-- .../yamlbeans/TagDirectiveTest.java | 6 +- .../yamlbeans/VersionTest.java | 39 ++++++++++ .../yamlbeans/YamlConfigTest.java | 6 +- .../yamlbeans/document/YamlDocumentTest.java | 6 +- 8 files changed, 96 insertions(+), 54 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/VersionTest.java diff --git a/src/com/esotericsoftware/yamlbeans/Version.java b/src/com/esotericsoftware/yamlbeans/Version.java index 5477a68..e4c7a02 100644 --- a/src/com/esotericsoftware/yamlbeans/Version.java +++ b/src/com/esotericsoftware/yamlbeans/Version.java @@ -19,55 +19,57 @@ /** Represents the version of a YAML document. * @author Nathan Sweet */ public class Version { - - public static final Version DEFAULT_VERSION = new Version(1, 1); - - public final int major; - public final int minor; - public Version (String value) { - if (value == null) throw new IllegalArgumentException("value cannot be null."); - int dotIndex = value.indexOf('.'); - if (dotIndex == -1) throw new IllegalArgumentException("value must contain a period: " + value); - major = Integer.parseInt(value.substring(0, dotIndex)); - minor = Integer.parseInt(value.substring(dotIndex + 1)); - check(); - } + public static final Version V1_0 = new Version(1, 0); + + public static final Version V1_1 = new Version(1, 1); + + /** + * YAML 1.1 + */ + public static final Version DEFAULT_VERSION = V1_1; - public Version (int major, int minor) { + private final int major; + private final int minor; + + private Version (int major, int minor) { this.major = major; this.minor = minor; - check(); } - public String toString () { - return major + "." + minor; - } + public static Version getVersion(String value) { + Version version = null; + if (value != null) { + int dotIndex = value.indexOf('.'); + int major = 0; + int minor = 0; + if (dotIndex > 0) { + try { + major = Integer.parseInt(value.substring(0, dotIndex)); + minor = Integer.parseInt(value.substring(dotIndex + 1)); + } catch (NumberFormatException e) { + return null; + } + } - public int hashCode () { - final int prime = 31; - int result = 1; - result = prime * result + major; - result = prime * result + minor; - return result; + if (major == V1_0.major && minor == V1_0.minor) { + version = V1_0; + } else if (major == V1_1.major && minor == V1_1.minor) { + version = V1_1; + } + } + return version; } - public boolean equals (int minor, int major) { - return this.minor == minor && this.major == major; + public int getMajor() { + return major; } - public boolean equals (Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final Version other = (Version)obj; - if (major != other.major) return false; - if (minor != other.minor) return false; - return true; + public int getMinor() { + return minor; } - private void check() { - if (major != 1) throw new IllegalArgumentException("major must be 1."); - if (minor < 0 || minor > 1) throw new IllegalArgumentException("minor must be 0 or 1."); + public String toString () { + return major + "." + minor; } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 110c479..867a001 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -241,7 +241,7 @@ public void setQuoteChar (Quote quote) { } static public class ReadConfig { - Version defaultVersion = new Version(1, 1); + Version defaultVersion = Version.DEFAULT_VERSION; ClassLoader classLoader; final Map constructorParameters = new IdentityHashMap(); boolean ignoreUnknownProperties; diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index a4a4667..87868ce 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -18,6 +18,7 @@ import static com.esotericsoftware.yamlbeans.parser.EventType.*; +import com.esotericsoftware.yamlbeans.Version; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; import com.esotericsoftware.yamlbeans.parser.DocumentEndEvent; import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; @@ -140,11 +141,11 @@ public void expect () throws IOException { if (event.type == DOCUMENT_START) { DocumentStartEvent documentStartEvent = (DocumentStartEvent)event; if (documentStartEvent.version != null) { - if (documentStartEvent.version.major != 1) + if (documentStartEvent.version.getMajor() != 1) throw new EmitterException("Unsupported YAML version: " + documentStartEvent.version); writer.writeVersionDirective(documentStartEvent.version.toString()); } - if ((documentStartEvent.version != null && documentStartEvent.version.equals(1, 0))) { + if ((documentStartEvent.version == Version.V1_0)) { isVersion10 = true; tagPrefixes = new HashMap(DEFAULT_TAG_PREFIXES_1_0); } else diff --git a/src/com/esotericsoftware/yamlbeans/parser/Parser.java b/src/com/esotericsoftware/yamlbeans/parser/Parser.java index eb3edc6..4845aa1 100644 --- a/src/com/esotericsoftware/yamlbeans/parser/Parser.java +++ b/src/com/esotericsoftware/yamlbeans/parser/Parser.java @@ -51,7 +51,7 @@ public class Parser { Event peekedEvent; public Parser (Reader reader) { - this(reader, new Version(1, 1)); + this(reader, Version.DEFAULT_VERSION); } public Parser (Reader reader, Version defaultVersion) { @@ -590,9 +590,9 @@ DocumentStartEvent processDirectives (boolean explicit) { DirectiveToken token = (DirectiveToken)tokenizer.getNextToken(); if (token.getDirective().equals("YAML")) { if (documentVersion != null) throw new ParserException("Duplicate YAML directive."); - documentVersion = new Version(token.getValue()); - if (documentVersion.major != 1) - throw new ParserException("Unsupported YAML version (1.x is required): " + documentVersion); + documentVersion = Version.getVersion(token.getValue()); + if (documentVersion == null || documentVersion.getMajor() != 1) + throw new ParserException("Unsupported YAML version (1.x is required): " + token.getValue()); } else if (token.getDirective().equals("TAG")) { String[] values = token.getValue().split(" "); String handle = values[0]; @@ -610,7 +610,7 @@ DocumentStartEvent processDirectives (boolean explicit) { Map tags = null; if (!tagHandles.isEmpty()) tags = new HashMap(tagHandles); - Map baseTags = version.minor == 0 ? DEFAULT_TAGS_1_0 : DEFAULT_TAGS_1_1; + Map baseTags = version.getMinor() == 0 ? DEFAULT_TAGS_1_0 : DEFAULT_TAGS_1_1; for (String key : baseTags.keySet()) if (!tagHandles.containsKey(key)) tagHandles.put(key, baseTags.get(key)); return new DocumentStartEvent(explicit, version, tags); diff --git a/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java b/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java index 31c2715..1ab6adf 100644 --- a/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java +++ b/test/com/esotericsoftware/yamlbeans/TagDirectiveTest.java @@ -63,17 +63,17 @@ public void testTagHandles() throws YamlException { public void testVersion() throws YamlException { String yaml = "!str test"; YamlConfig yamlConfig = new YamlConfig(); - yamlConfig.readConfig.setDefaultVersion(new Version(1, 0)); + yamlConfig.readConfig.setDefaultVersion(Version.V1_0); YamlReader yamlReader = new YamlReader(yaml, yamlConfig); assertEquals("test", yamlReader.read()); yaml = "!!str test"; - yamlConfig.readConfig.setDefaultVersion(new Version(1, 1)); + yamlConfig.readConfig.setDefaultVersion(Version.V1_1); yamlReader = new YamlReader(yaml, yamlConfig); assertEquals("test", yamlReader.read()); yaml = "!!str test"; - yamlConfig.readConfig.setDefaultVersion(new Version(1, 0)); + yamlConfig.readConfig.setDefaultVersion(Version.V1_0); yamlReader = new YamlReader(yaml, yamlConfig); try { yamlReader.read(); diff --git a/test/com/esotericsoftware/yamlbeans/VersionTest.java b/test/com/esotericsoftware/yamlbeans/VersionTest.java new file mode 100644 index 0000000..d763010 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/VersionTest.java @@ -0,0 +1,39 @@ +package com.esotericsoftware.yamlbeans; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +public class VersionTest { + + @Test + public void testV1_0() { + Version version = Version.V1_0; + assertEquals(1, version.getMajor()); + assertEquals(0, version.getMinor()); + assertEquals("1.0", version.toString()); + } + + @Test + public void testV1_1() { + Version version = Version.V1_1; + assertEquals(1, version.getMajor()); + assertEquals(1, version.getMinor()); + assertEquals("1.1", version.toString()); + } + + @Test + public void testGetVersion() { + assertEquals(Version.V1_0, Version.getVersion("1.0")); + assertEquals(Version.V1_1, Version.getVersion("1.1")); + assertNull(Version.getVersion(null)); + assertNull(Version.getVersion("0")); + assertNull(Version.getVersion(".0")); + assertNull(Version.getVersion("0.1")); + assertNull(Version.getVersion("1.2")); + assertNull(Version.getVersion("2.0")); + assertNull(Version.getVersion("1.a")); + assertNull(Version.getVersion("a.1")); + } +} diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 9d085af..32c404d 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -337,7 +337,7 @@ public void testSetVersion() throws YamlException { yamlWriter.close(); assertEquals("test" + LINE_SEPARATOR, stringWriter.toString()); - Version version = new Version(1, 0); + Version version = Version.V1_0; yamlConfig.writeConfig.setVersion(version); stringWriter = new StringWriter(); yamlWriter = new YamlWriter(stringWriter, yamlConfig); @@ -345,7 +345,7 @@ public void testSetVersion() throws YamlException { yamlWriter.close(); assertEquals("%YAML 1.0" + LINE_SEPARATOR + "--- test" + LINE_SEPARATOR, stringWriter.toString()); - version = new Version(1, 1); + version = Version.V1_1; yamlConfig.writeConfig.setVersion(version); stringWriter = new StringWriter(); yamlWriter = new YamlWriter(stringWriter, yamlConfig); @@ -496,7 +496,7 @@ public void testSetQuoteChar() throws YamlException { @Test public void testSetDefaultVersion() throws YamlException { String yaml = "!!str test"; - yamlConfig.readConfig.setDefaultVersion(new Version(1, 1)); + yamlConfig.readConfig.setDefaultVersion(Version.V1_1); YamlReader yamlReader = new YamlReader(yaml, yamlConfig); assertEquals("test", yamlReader.read(String.class)); diff --git a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java index 96216a5..c2b6523 100644 --- a/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java +++ b/test/com/esotericsoftware/yamlbeans/document/YamlDocumentTest.java @@ -175,7 +175,7 @@ public void testYamlMappingIterator() throws YamlException { @Test public void testVersion1_0() throws YamlException { String yaml = "version: !str 1.0"; - YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 0)); + YamlDocumentReader reader = new YamlDocumentReader(yaml, Version.V1_0); YamlMapping yamlMapping = (YamlMapping) reader.read(); assertEquals("1.0", ((YamlScalar) yamlMapping.getEntry("version").getValue()).getValue()); } @@ -183,7 +183,7 @@ public void testVersion1_0() throws YamlException { @Test public void testVersion1_0ThrowsYamlException() { String yaml = "version: !!str 1.1"; - YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 0)); + YamlDocumentReader reader = new YamlDocumentReader(yaml, Version.V1_0); try { reader.read(); fail("1.0 Version tag is single '!'"); @@ -194,7 +194,7 @@ public void testVersion1_0ThrowsYamlException() { @Test public void testVersion1_1() throws YamlException { String yaml = "version: !!str 1.1"; - YamlDocumentReader reader = new YamlDocumentReader(yaml, new Version(1, 1)); + YamlDocumentReader reader = new YamlDocumentReader(yaml, Version.V1_1); YamlMapping yamlMapping = (YamlMapping) reader.read(); assertEquals("1.1", ((YamlScalar) yamlMapping.getEntry("version").getValue()).getValue()); } From 611b92a22845787a25f90324e4f457aec1d59fa7 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Fri, 24 Jul 2020 18:52:13 +0800 Subject: [PATCH 089/114] YamlConfig.WriteConfig add flowStyle setting (#150) --- .../yamlbeans/YamlConfig.java | 10 +++++ .../yamlbeans/YamlWriter.java | 4 +- .../yamlbeans/document/YamlMapping.java | 2 +- .../yamlbeans/document/YamlSequence.java | 2 +- .../yamlbeans/YamlConfigTest.java | 38 +++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 867a001..013ad91 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -143,6 +143,7 @@ static public class WriteConfig { Quote quote = Quote.NONE; Version version; Map tags; + boolean flowStyle; EmitterConfig emitterConfig = new EmitterConfig(); WriteConfig () { @@ -238,6 +239,15 @@ public void setWriteClassname (WriteClassName write) { public void setQuoteChar (Quote quote) { this.quote = quote; } + + /** If true, the YAML output will be flow. Default is false. */ + public void setFlowStyle(boolean flowStyle) { + this.flowStyle = flowStyle; + } + + public boolean isFlowStyle() { + return flowStyle; + } } static public class ReadConfig { diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index e62a5f5..df8597a 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -210,7 +210,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } if (object instanceof Collection) { - emitter.emit(new SequenceStartEvent(anchor, tag, !showTag, false)); + emitter.emit(new SequenceStartEvent(anchor, tag, !showTag, config.writeConfig.isFlowStyle())); for (Object item : (Collection)object) { if (isRoot && !config.writeConfig.writeRootElementTags) elementType = item.getClass(); writeValue(item, elementType, null, null); @@ -220,7 +220,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } if (object instanceof Map) { - emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); + emitter.emit(new MappingStartEvent(anchor, tag, !showTag, config.writeConfig.isFlowStyle())); Map map = (Map)object; for (Object item : map.entrySet()) { Entry entry = (Entry)item; diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java index f34e494..eae6097 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlMapping.java @@ -74,7 +74,7 @@ public String toString() { @Override public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { - emitter.emit(new MappingStartEvent(anchor, tag, tag==null, false)); + emitter.emit(new MappingStartEvent(anchor, tag, tag==null, config.isFlowStyle())); for(YamlEntry entry : entries) entry.emitEvent(emitter, config); emitter.emit(Event.MAPPING_END); diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java index 229ce6e..1519ac2 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlSequence.java @@ -59,7 +59,7 @@ public String toString() { @Override public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { - emitter.emit(new SequenceStartEvent(anchor, tag, tag==null, false)); + emitter.emit(new SequenceStartEvent(anchor, tag, tag==null, config.isFlowStyle())); for (YamlElement element : elements) element.emitEvent(emitter, config); emitter.emit(Event.SEQUENCE_END); diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 32c404d..89ba418 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -542,6 +542,44 @@ public void testConstructorParameters() throws YamlException { } + @Test + public void testSetFlowStyle() throws YamlException { + List list = new ArrayList(); + list.add("111"); + list.add("222"); + list.add("333"); + + Map map = new HashMap(); + map.put("key", "value"); + + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + + assertEquals("- 111" + LINE_SEPARATOR + "- 222" + LINE_SEPARATOR + "- 333" + LINE_SEPARATOR, + stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("key: value" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.writeConfig.setFlowStyle(true); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("[111, 222, 333]" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("{key: value}" + LINE_SEPARATOR, stringWriter.toString()); + } + private String multipleSpaces(int indentSize) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indentSize; i++) { From 1a8d5c59968d1a512575ba784c6a51f701fa4e5c Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 28 Jul 2020 20:35:15 +0800 Subject: [PATCH 090/114] Make yamlConfig.writeConfig.quote configuration take effect (#151) --- .../yamlbeans/YamlConfig.java | 8 ++++++ .../yamlbeans/YamlWriter.java | 28 ++++++------------- .../yamlbeans/document/YamlEntry.java | 2 +- .../yamlbeans/document/YamlScalar.java | 3 +- .../yamlbeans/YamlConfigTest.java | 19 ++++++++----- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 013ad91..02a7d74 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -240,6 +240,10 @@ public void setQuoteChar (Quote quote) { this.quote = quote; } + public Quote getQuote() { + return quote; + } + /** If true, the YAML output will be flow. Default is false. */ public void setFlowStyle(boolean flowStyle) { this.flowStyle = flowStyle; @@ -325,5 +329,9 @@ public static enum Quote { Quote (char c) { this.c = c; } + + public char getStyle() { + return c; + } } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index df8597a..b7fd74c 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -138,7 +138,8 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla ((YamlElement)object).emitEvent(emitter, config.writeConfig); return; } else if (object == null) { - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); + emitter.emit( + new ScalarEvent(null, null, new boolean[] { true, true }, null, this.config.writeConfig.quote.c)); return; } @@ -183,22 +184,15 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla for (Entry entry : config.scalarSerializers.entrySet()) { if (entry.getKey().isAssignableFrom(valueClass)) { ScalarSerializer serializer = entry.getValue(); - emitter.emit(new ScalarEvent(null, tag, new boolean[] {tag == null, tag == null}, serializer.write(object), (char)0)); + emitter.emit(new ScalarEvent(null, tag, new boolean[] { tag == null, tag == null }, + serializer.write(object), this.config.writeConfig.quote.c)); return; } } if (Beans.isScalar(valueClass)) { - char style = 0; - String string = String.valueOf(object); - if (valueClass == String.class) { - try { - Float.parseFloat(string); - style = this.config.writeConfig.quote.c; - } catch (NumberFormatException ignored) { - } - } - emitter.emit(new ScalarEvent(null, tag, new boolean[] {true, true}, string, style)); + emitter.emit(new ScalarEvent(null, tag, new boolean[] { true, true }, String.valueOf(object), + this.config.writeConfig.quote.c)); return; } @@ -234,15 +228,9 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (value instanceof String) { Object valueTag = map.get(key + config.tagSuffix); if (valueTag instanceof String) { - String string = (String)value; - char style = 0; - try { - Float.parseFloat(string); - style = this.config.writeConfig.quote.c; - } catch (NumberFormatException ignored) { - } writeValue(key, null, null, null); - emitter.emit(new ScalarEvent(null, (String)valueTag, new boolean[] {false, false}, string, style)); + emitter.emit(new ScalarEvent(null, (String) valueTag, new boolean[] { false, false }, + (String) value, this.config.writeConfig.quote.c)); continue; } } diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java b/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java index e290e6d..43af578 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlEntry.java @@ -57,7 +57,7 @@ public void setValue(String value) { public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { key.emitEvent(emitter, config); if(value==null) - emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, (char)0)); + emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, null, config.getQuote().getStyle())); else value.emitEvent(emitter, config); } diff --git a/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java b/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java index 84be98a..9050d3c 100644 --- a/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java +++ b/src/com/esotericsoftware/yamlbeans/document/YamlScalar.java @@ -44,7 +44,6 @@ public String toString() { @Override public void emitEvent(Emitter emitter, WriteConfig config) throws EmitterException, IOException { - char style = 0; // TODO determine style - emitter.emit(new ScalarEvent(anchor, tag, new boolean[] {true, true}, value, style)); + emitter.emit(new ScalarEvent(anchor, tag, new boolean[] {true, true}, value, config.getQuote().getStyle())); } } diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 89ba418..37bd6c4 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -470,27 +470,32 @@ public void testSetUseVerbatimTags() throws YamlException { @Test public void testSetQuoteChar() throws YamlException { - + TestObject testObject = new TestObject(); + testObject.age = 18; + testObject.name = "xxx"; StringWriter stringWriter = new StringWriter(); + yamlConfig.writeConfig.setWriteRootTags(false); yamlConfig.writeConfig.setQuoteChar(Quote.NONE); YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); - yamlWriter.write("123"); + yamlWriter.write(testObject); yamlWriter.close(); - assertEquals("123" + LINE_SEPARATOR, stringWriter.toString()); + assertEquals("name: xxx" + LINE_SEPARATOR + "age: 18" + LINE_SEPARATOR, stringWriter.toString()); stringWriter = new StringWriter(); yamlConfig.writeConfig.setQuoteChar(Quote.SINGLE); yamlWriter = new YamlWriter(stringWriter, yamlConfig); - yamlWriter.write("123"); + yamlWriter.write(testObject); yamlWriter.close(); - assertEquals("\'123\'" + LINE_SEPARATOR, stringWriter.toString()); + assertEquals("\'name\': \'xxx\'" + LINE_SEPARATOR + "\'age\': \'18\'" + LINE_SEPARATOR, + stringWriter.toString()); stringWriter = new StringWriter(); yamlConfig.writeConfig.setQuoteChar(Quote.DOUBLE); yamlWriter = new YamlWriter(stringWriter, yamlConfig); - yamlWriter.write("123"); + yamlWriter.write(testObject); yamlWriter.close(); - assertEquals("\"123\"" + LINE_SEPARATOR, stringWriter.toString()); + assertEquals("\"name\": \"xxx\"" + LINE_SEPARATOR + "\"age\": \"18\"" + LINE_SEPARATOR, + stringWriter.toString()); } @Test From 0924b0aece518d3d1b1f00b81baf98a28e87f755 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 30 Jul 2020 17:22:13 +0800 Subject: [PATCH 091/114] add literal scalar and folded scalar (#152) --- .../yamlbeans/YamlConfig.java | 2 +- .../yamlbeans/YamlWriter.java | 4 ++-- .../yamlbeans/emitter/Emitter.java | 20 ++++++++++++------- .../yamlbeans/YamlConfigTest.java | 18 +++++++++++++++++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 02a7d74..bea4c7a 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -322,7 +322,7 @@ public static enum WriteClassName { } public static enum Quote { - NONE('\0'), SINGLE('\''), DOUBLE('"'); + NONE('\0'), SINGLE('\''), DOUBLE('"'), LITERAL('|'), FOLDED('>'); char c; diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index b7fd74c..ba34909 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -244,7 +244,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla if (fieldClass.isArray()) { elementType = fieldClass.getComponentType(); - emitter.emit(new SequenceStartEvent(anchor, null, true, false)); + emitter.emit(new SequenceStartEvent(anchor, null, true, config.writeConfig.isFlowStyle())); for (int i = 0, n = Array.getLength(object); i < n; i++) writeValue(Array.get(object, i), elementType, null, null); emitter.emit(Event.SEQUENCE_END); @@ -267,7 +267,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla } Set properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config); - emitter.emit(new MappingStartEvent(anchor, tag, !showTag, false)); + emitter.emit(new MappingStartEvent(anchor, tag, !showTag, config.writeConfig.isFlowStyle())); for (Property property : properties) { try { Object propertyValue = property.get(object); diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 87868ce..8db5dea 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -525,14 +525,20 @@ private char chooseScalarStyle () { ScalarEvent ev = (ScalarEvent)event; if (analysis == null) analysis = ScalarAnalysis.analyze(ev.value, config.escapeUnicode); if (ev.style == '"' || config.canonical) return '"'; - if (ev.style == 0 && !(simpleKeyContext && (analysis.empty || analysis.multiline)) - && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) return 0; - if (ev.style == 0 && ev.implicit[0] && !(simpleKeyContext && (analysis.empty || analysis.multiline)) - && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) return 0; - if ((ev.style == '|' || ev.style == '>') && flowLevel == 0 && analysis.allowBlock) return '\''; - if ((ev.style == 0 || ev.style == '\'') && analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline)) + if ((ev.style == 0 || ev.style == '|' || ev.style == '>') + && !(simpleKeyContext && (analysis.empty || analysis.multiline)) + && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) { + return 0; + } + if ((ev.style == 0 || ev.style == '\'') && analysis.allowSingleQuoted + && !(simpleKeyContext && analysis.multiline)) { return '\''; - if (ev.style == 0 && analysis.multiline && flowLevel == 0 && analysis.allowBlock) return '|'; + } + if ((ev.style == 0 || ev.style == '|' || ev.style == '>') && analysis.multiline && flowLevel == 0 + && analysis.allowBlock) { + return ev.style == 0 ? '|' : ev.style; + } + return '"'; } diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 37bd6c4..aaa929c 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -496,6 +496,24 @@ public void testSetQuoteChar() throws YamlException { yamlWriter.close(); assertEquals("\"name\": \"xxx\"" + LINE_SEPARATOR + "\"age\": \"18\"" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setQuoteChar(Quote.LITERAL); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test\ntest"); + yamlWriter.close(); + System.out.println(stringWriter.toString()); + assertEquals("|-" + LINE_SEPARATOR + " test" + LINE_SEPARATOR + " test" + LINE_SEPARATOR, + stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setQuoteChar(Quote.FOLDED); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("test\ntest"); + yamlWriter.close(); + System.out.println(stringWriter.toString()); + assertEquals(">-" + LINE_SEPARATOR + " test" + LINE_SEPARATOR + LINE_SEPARATOR + " test" + LINE_SEPARATOR, + stringWriter.toString()); } @Test From 93bb8d2499aadf4a1f43aa4d1c40e42e66dae3ca Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 3 Aug 2020 16:57:25 +0800 Subject: [PATCH 092/114] Add emitter package to the unit test case (#153) --- .../yamlbeans/emitter/Emitter.java | 13 -- .../yamlbeans/emitter/EmitterTest.java | 160 ++++++++++++++++++ .../yamlbeans/emitter/EmitterWriterTest.java | 73 ++++++++ 3 files changed, 233 insertions(+), 13 deletions(-) create mode 100644 test/com/esotericsoftware/yamlbeans/emitter/EmitterTest.java create mode 100644 test/com/esotericsoftware/yamlbeans/emitter/EmitterWriterTest.java diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index 8db5dea..a8d038b 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -25,14 +25,11 @@ import com.esotericsoftware.yamlbeans.parser.Event; import com.esotericsoftware.yamlbeans.parser.MappingStartEvent; import com.esotericsoftware.yamlbeans.parser.NodeEvent; -import com.esotericsoftware.yamlbeans.parser.Parser; import com.esotericsoftware.yamlbeans.parser.ScalarEvent; import com.esotericsoftware.yamlbeans.parser.SequenceStartEvent; import java.io.BufferedWriter; -import java.io.FileReader; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; @@ -645,14 +642,4 @@ private interface EmitterState { DEFAULT_TAG_PREFIXES_1_1.put("!", "!"); DEFAULT_TAG_PREFIXES_1_1.put("tag:yaml.org,2002:", "!!"); } - - public static void main (String[] args) throws IOException { - Parser parser = new Parser(new FileReader("test/test.yml")); - Emitter emitter = new Emitter(new OutputStreamWriter(System.out)); - while (true) { - Event event = parser.getNextEvent(); - if (event == null) break; - emitter.emit(event); - } - } } diff --git a/test/com/esotericsoftware/yamlbeans/emitter/EmitterTest.java b/test/com/esotericsoftware/yamlbeans/emitter/EmitterTest.java new file mode 100644 index 0000000..aa773b8 --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/emitter/EmitterTest.java @@ -0,0 +1,160 @@ +package com.esotericsoftware.yamlbeans.emitter; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedWriter; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlWriter; +import com.esotericsoftware.yamlbeans.parser.DocumentStartEvent; +import com.esotericsoftware.yamlbeans.parser.Event; +import com.esotericsoftware.yamlbeans.parser.Parser; +import com.esotericsoftware.yamlbeans.parser.ScalarEvent; + +public class EmitterTest { + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private StringWriter stringWriter = null; + private Emitter emitter = null; + + @Before + public void setup() { + stringWriter = new StringWriter(); + emitter = new Emitter(stringWriter); + } + + @Test + public void testEmitterConstructor() throws IOException { + try { + new Emitter(null); + } catch (IllegalArgumentException e) { + assertEquals("stream cannot be null.", e.getMessage()); + } + + StringWriter writer = new StringWriter(); + new Emitter(writer); + + BufferedWriter bufferedWriter = new BufferedWriter(writer); + EmitterConfig emitterConfig = new EmitterConfig(); + new Emitter(bufferedWriter, emitterConfig); + } + + @Test(expected = EmitterException.class) + public void testStateStreamStart() throws IOException { + emitter.emit(Event.STREAM_START); + emitter.emit(new DocumentStartEvent(false, null, null)); + emitter.emit(Event.DOCUMENT_END_TRUE); + } + + @Test(expected = EmitterException.class) + public void testStateNOTHING() throws EmitterException, IOException { + emitter.emit(Event.STREAM_START); + emitter.emit(Event.STREAM_END); + assertEquals(3, emitter.state); + emitter.emit(Event.DOCUMENT_END_TRUE); + } + + @Test(expected = EmitterException.class) + public void testDocumentEnd() throws EmitterException, IOException { + emitter.emit(Event.STREAM_START); + emitter.emit(new DocumentStartEvent(false, null, null)); + emitter.emit(new ScalarEvent(null, null, new boolean[] { true, true }, "test", '\0')); + emitter.emit(Event.STREAM_END); + } + + @Test + public void testEmptySequence() throws YamlException { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setFlowStyle(true); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + List list = new ArrayList(); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("[]" + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test + public void testStateFlowMapingKey() throws YamlException { + Map map = new LinkedHashMap(); + map.put("key1", "value1"); + map.put("key2", "value2"); + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setFlowStyle(true); + yamlConfig.writeConfig.setWriteRootTags(false); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("{key1: value1, key2: value2}" + LINE_SEPARATOR, stringWriter.toString()); + + } + + @Test + public void testStateFlowMapingKeyCanonicalIsTrue() throws EmitterException, IOException { + Map map = new LinkedHashMap(); + map.put("key1", "value1"); + map.put("key2", "value2"); + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setFlowStyle(true); + yamlConfig.writeConfig.setWriteRootTags(false); + yamlConfig.writeConfig.setCanonical(true); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals( + "--- " + LINE_SEPARATOR + "{" + LINE_SEPARATOR + " ? !java.lang.String \"key1\"" + LINE_SEPARATOR + + " : !java.lang.String \"value1\"," + LINE_SEPARATOR + " ? !java.lang.String \"key2\"" + + LINE_SEPARATOR + " : !java.lang.String \"value2\"" + LINE_SEPARATOR + "}" + LINE_SEPARATOR, + stringWriter.toString()); + } + + @Test(expected = EmitterException.class) + public void testDocumentStart() throws EmitterException, IOException { + emitter.emit(Event.STREAM_START); + emitter.emit(new ScalarEvent(null, null, new boolean[] { true, true }, "test", '\0')); + } + + @Test + public void testBlockMappingValueMultiline() throws EmitterException, IOException { + Map map = new HashMap(); + map.put("555\n666", "value"); + YamlWriter yamlWriter = new YamlWriter(stringWriter); + yamlWriter.write(map); + yamlWriter.close(); + + assertEquals("? |-" + LINE_SEPARATOR + " 555" + LINE_SEPARATOR + " 666" + LINE_SEPARATOR + ": value" + + LINE_SEPARATOR, stringWriter.toString()); + } + + @Test(expected = EmitterException.class) + public void testExpectNode() throws EmitterException, IOException { + emitter.emit(Event.STREAM_START); + emitter.emit(new DocumentStartEvent(false, null, null)); + emitter.emit(Event.MAPPING_END); + } + + @Test + public void testParserToEmitter() throws EmitterException, IOException { + Parser parser = new Parser(new FileReader("test/test.yml")); + Emitter emitter = new Emitter(new OutputStreamWriter(System.out)); + while (true) { + Event event = parser.getNextEvent(); + if (event == null) + break; + emitter.emit(event); + } + emitter.close(); + } +} diff --git a/test/com/esotericsoftware/yamlbeans/emitter/EmitterWriterTest.java b/test/com/esotericsoftware/yamlbeans/emitter/EmitterWriterTest.java new file mode 100644 index 0000000..44e742e --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/emitter/EmitterWriterTest.java @@ -0,0 +1,73 @@ +package com.esotericsoftware.yamlbeans.emitter; + +import static org.junit.Assert.assertEquals; + +import java.io.StringWriter; +import org.junit.Test; + +import com.esotericsoftware.yamlbeans.YamlConfig; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlWriter; +import com.esotericsoftware.yamlbeans.YamlConfig.Quote; + +public class EmitterWriterTest { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void testWriteDoubleQuoted() throws YamlException { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setQuoteChar(Quote.DOUBLE); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("\u0001\u0011\u0111\u1111\u0007"); + yamlWriter.close(); + + assertEquals("\"\\u0001\\u0011\\u0111\\u1111\\a\"" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlConfig.writeConfig.setWrapColumn(5); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("testt est"); + yamlWriter.close(); + assertEquals("\"testt\\" + LINE_SEPARATOR + " \\ e\\" + LINE_SEPARATOR + " st\"" + LINE_SEPARATOR, + stringWriter.toString()); + } + + @Test + public void testWriteSingleQuoted() throws YamlException { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setQuoteChar(Quote.SINGLE); + + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("\ntest"); + yamlWriter.close(); + assertEquals("\'" + LINE_SEPARATOR + " test\'" + LINE_SEPARATOR, stringWriter.toString()); + + } + + @Test + public void testWriteFolded() throws YamlException { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setQuoteChar(Quote.FOLDED); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("111\n222 333"); + yamlWriter.close(); + assertEquals(">-" + LINE_SEPARATOR + " 111" + LINE_SEPARATOR + LINE_SEPARATOR + " 222 333" + LINE_SEPARATOR, + stringWriter.toString()); + } + + @Test + public void testWriteLiteral() throws YamlException { + YamlConfig yamlConfig = new YamlConfig(); + yamlConfig.writeConfig.setQuoteChar(Quote.LITERAL); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write("111\n222"); + yamlWriter.close(); + assertEquals("|-" + LINE_SEPARATOR + " 111" + LINE_SEPARATOR + " 222" + LINE_SEPARATOR, + stringWriter.toString()); + } +} From 61d949577a3561a6952f7387d28d7b64d475c85b Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Tue, 4 Aug 2020 22:41:48 +0800 Subject: [PATCH 093/114] A pretty flow YAML output (#154) --- .../yamlbeans/YamlConfig.java | 5 +++ .../yamlbeans/emitter/Emitter.java | 18 ++++++++ .../yamlbeans/emitter/EmitterConfig.java | 6 +++ .../yamlbeans/YamlConfigTest.java | 44 ++++++++++++++++++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index bea4c7a..12e5fe1 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -252,6 +252,11 @@ public void setFlowStyle(boolean flowStyle) { public boolean isFlowStyle() { return flowStyle; } + + /** If true, the YAML output will be pretty flow. Default is false. */ + public void setPrettyFlow(boolean prettyFlow) { + emitterConfig.setPrettyFlow(prettyFlow); + } } static public class ReadConfig { diff --git a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java index a8d038b..16735dc 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/Emitter.java @@ -202,6 +202,9 @@ public void expect () throws IOException { writer.writeIndicator("]", false, false, false); state = states.remove(0); } else { + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical || writer.column > config.wrapColumn) writer.writeIndent(indent); states.add(0, S_FLOW_SEQUENCE_ITEM); expectNode(false, true, false, false); @@ -213,6 +216,9 @@ public void expect () throws IOException { if (event.type == SEQUENCE_END) { indent = indents.remove(0); flowLevel--; + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical) { writer.writeIndent(indent); } @@ -220,6 +226,9 @@ public void expect () throws IOException { state = states.remove(0); } else { writer.writeIndicator(",", false, false, false); + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical || writer.column > config.wrapColumn) writer.writeIndent(indent); states.add(0, S_FLOW_SEQUENCE_ITEM); expectNode(false, true, false, false); @@ -234,6 +243,9 @@ public void expect () throws IOException { writer.writeIndicator("}", false, false, false); state = states.remove(0); } else { + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical || writer.column > config.wrapColumn) writer.writeIndent(indent); if (!config.canonical && checkSimpleKey()) { states.add(0, S_FLOW_MAPPING_SIMPLE_VALUE); @@ -266,6 +278,9 @@ public void expect () throws IOException { if (event.type == MAPPING_END) { indent = indents.remove(0); flowLevel--; + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical) { writer.writeIndent(indent); } @@ -273,6 +288,9 @@ public void expect () throws IOException { state = states.remove(0); } else { writer.writeIndicator(",", false, false, false); + if (config.prettyFlow) { + writer.writeIndent(flowLevel * config.indentSize); + } if (config.canonical || writer.column > config.wrapColumn) writer.writeIndent(indent); if (!config.canonical && checkSimpleKey()) { states.add(0, S_FLOW_MAPPING_SIMPLE_VALUE); diff --git a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java index bd80c99..5e313b5 100644 --- a/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java +++ b/src/com/esotericsoftware/yamlbeans/emitter/EmitterConfig.java @@ -23,6 +23,7 @@ public class EmitterConfig { int indentSize = 3; int wrapColumn = 100; boolean escapeUnicode = true; + boolean prettyFlow; /** If true, the YAML output will be canonical. Default is false. */ public void setCanonical (boolean canonical) { @@ -50,4 +51,9 @@ public void setUseVerbatimTags (boolean useVerbatimTags) { public void setEscapeUnicode (boolean escapeUnicode) { this.escapeUnicode = escapeUnicode; } + + /** If true, the YAML output will be pretty flow. Default is false. */ + public void setPrettyFlow(boolean prettyFlow) { + this.prettyFlow = prettyFlow; + } } diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index aaa929c..780d7a8 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -502,7 +503,6 @@ public void testSetQuoteChar() throws YamlException { yamlWriter = new YamlWriter(stringWriter, yamlConfig); yamlWriter.write("test\ntest"); yamlWriter.close(); - System.out.println(stringWriter.toString()); assertEquals("|-" + LINE_SEPARATOR + " test" + LINE_SEPARATOR + " test" + LINE_SEPARATOR, stringWriter.toString()); @@ -511,7 +511,6 @@ public void testSetQuoteChar() throws YamlException { yamlWriter = new YamlWriter(stringWriter, yamlConfig); yamlWriter.write("test\ntest"); yamlWriter.close(); - System.out.println(stringWriter.toString()); assertEquals(">-" + LINE_SEPARATOR + " test" + LINE_SEPARATOR + LINE_SEPARATOR + " test" + LINE_SEPARATOR, stringWriter.toString()); } @@ -603,6 +602,47 @@ public void testSetFlowStyle() throws YamlException { assertEquals("{key: value}" + LINE_SEPARATOR, stringWriter.toString()); } + @Test + public void testSetPrettyFlow() throws YamlException { + Map map = new LinkedHashMap(); + map.put("key1", "value1"); + map.put("key2", "value2"); + + List list = new ArrayList(); + list.add("111"); + list.add("222"); + list.add("333"); + + yamlConfig.writeConfig.setWriteRootTags(false); + yamlConfig.writeConfig.setFlowStyle(true); + StringWriter stringWriter = new StringWriter(); + YamlWriter yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("{key1: value1, key2: value2}" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("[111, 222, 333]" + LINE_SEPARATOR, stringWriter.toString()); + + yamlConfig.writeConfig.setPrettyFlow(true); + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(map); + yamlWriter.close(); + assertEquals("{" + LINE_SEPARATOR + " key1: value1," + LINE_SEPARATOR + " key2: value2" + LINE_SEPARATOR + + "}" + LINE_SEPARATOR, stringWriter.toString()); + + stringWriter = new StringWriter(); + yamlWriter = new YamlWriter(stringWriter, yamlConfig); + yamlWriter.write(list); + yamlWriter.close(); + assertEquals("[" + LINE_SEPARATOR + " 111," + LINE_SEPARATOR + " 222," + LINE_SEPARATOR + " 333" + + LINE_SEPARATOR + "]" + LINE_SEPARATOR, stringWriter.toString()); + } + private String multipleSpaces(int indentSize) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indentSize; i++) { From 3f1fcb516c940c362a5db475917820903331d170 Mon Sep 17 00:00:00 2001 From: Mr14huashao Date: Thu, 13 Aug 2020 09:32:49 +0800 Subject: [PATCH 094/114] 1.15 version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 25d97d3..a66e901 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.15-SNAPSHOT + 1.15 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 5b220da80e59685f66af9451217007ab72908063 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 13 Aug 2020 10:38:19 +0800 Subject: [PATCH 095/114] Update pom.xml 1.16-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a66e901..5220162 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.15 + 1.16-SNAPSHOT Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 298811fbba1178e3ea77172ad02f1fab2adc0c04 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Thu, 13 Aug 2020 15:01:51 +0800 Subject: [PATCH 096/114] Update project.yaml version 1.15 --- project.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.yaml b/project.yaml index 1ee82c0..79d7213 100644 --- a/project.yaml +++ b/project.yaml @@ -1,2 +1,2 @@ classpath: lib|*.jar|!junit*.jar -version: 1.14 +version: 1.15 From 26e445bfbf44a1089b470661c79ed5bca94ecbc0 Mon Sep 17 00:00:00 2001 From: Mr14huashao <57353473+Mr14huashao@users.noreply.github.com> Date: Mon, 28 Sep 2020 14:18:56 +0800 Subject: [PATCH 097/114] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ecb378..bad7301 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Chinese document [中文翻译文档](CN_README.md) . YamlBeans makes it easy to serialize and deserialize Java object graphs to and from YAML, a human-friendly data format. Replace XML and properties files with YAML for more expressive power (lists, maps, anchors, etc) and easier hand-editing. Maven Central: -http://repo1.maven.org/maven2/com/esotericsoftware/yamlbeans/yamlbeans/ +https://repo1.maven.org/maven2/com/esotericsoftware/yamlbeans/yamlbeans/ ## Basic deserialization From 7cc395a73485ecfe962892b99b2dbf48cd52e3e1 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 3 Mar 2021 21:08:29 +0100 Subject: [PATCH 098/114] Fixed access warnings. --- test/com/esotericsoftware/yamlbeans/YamlConfigTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java index 780d7a8..ddb2bb2 100644 --- a/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/YamlConfigTest.java @@ -21,6 +21,7 @@ import com.esotericsoftware.yamlbeans.YamlReader.YamlReaderException; import com.esotericsoftware.yamlbeans.scalar.DateSerializer; +@SuppressWarnings("synthetic-access") public class YamlConfigTest { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); From 36d50c853549296405a53c01d335af5b8e8aac05 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 3 Mar 2021 21:14:27 +0100 Subject: [PATCH 099/114] Reapply #66 fix. closes #159 --- src/com/esotericsoftware/yamlbeans/Beans.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/Beans.java b/src/com/esotericsoftware/yamlbeans/Beans.java index 382d735..3582b22 100644 --- a/src/com/esotericsoftware/yamlbeans/Beans.java +++ b/src/com/esotericsoftware/yamlbeans/Beans.java @@ -137,14 +137,13 @@ static public Property getProperty (Class type, String name, boolean beanPropert if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty."); name = toJavaIdentifier(name); - Property property = null; - for (Field field : getAllFields(type)) { - if (field.getName().equals(name)) { - property = getProperty(type, beanProperties, privateFields, config, field); - break; - } - } - return property; + for (Field field : getAllFields(type)) + if (field.getName().equals(name)) return getProperty(type, beanProperties, privateFields, config, field); + + // If a property is not found, try prepending `_` for reserved field names (eg "class" in the data can be a "_class" field). + if (!name.startsWith("_")) return getProperty(type, "_" + name, beanProperties, privateFields, config); + + return null; } private static Property getProperty(Class type, boolean beanProperties, boolean privateFields, YamlConfig config, From 53cd1f2f12372c8bb1dcda0ed624100f0f468dd6 Mon Sep 17 00:00:00 2001 From: Jesse Pavel Date: Sat, 26 Jun 2021 22:25:17 -0400 Subject: [PATCH 100/114] Support clip, strip, and keep chomp for block scalars. (#161) --- .../yamlbeans/tokenizer/Tokenizer.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java index 186963a..9bdd96a 100644 --- a/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java +++ b/src/com/esotericsoftware/yamlbeans/tokenizer/Tokenizer.java @@ -705,7 +705,7 @@ private Token scanBlockScalar (char style) { StringBuilder chunks = new StringBuilder(); forward(); Object[] chompi = scanBlockScalarIndicators(); - boolean chomping = ((Boolean)chompi[0]).booleanValue(); + int chomping = ((Integer)chompi[0]).intValue(); int increment = ((Integer)chompi[1]).intValue(); scanBlockScalarIgnoredLine(); int minIndent = indent + 1; @@ -746,7 +746,9 @@ private Token scanBlockScalar (char style) { break; } - if (chomping) { + if (chomping == 0) { + chunks.append(lineBreak); + } else if (chomping == 2) { chunks.append(lineBreak); chunks.append(breaks); } @@ -755,11 +757,11 @@ private Token scanBlockScalar (char style) { } private Object[] scanBlockScalarIndicators () { - boolean chomping = false; + int chomping = 0; // 0 = clip, 1 = strip, 2 = keep int increment = -1; char ch = peek(); if (ch == '-' || ch == '+') { - chomping = ch == '+'; + chomping = ch == '-' ? 1 : 2; forward(); ch = peek(); if (Character.isDigit(ch)) { @@ -775,13 +777,13 @@ private Object[] scanBlockScalarIndicators () { forward(); ch = peek(); if (ch == '-' || ch == '+') { - chomping = ch == '+'; + chomping = ch == '-' ? 1 : 2; forward(); } } if (NULL_BL_LINEBR.indexOf(peek()) == -1) throw new TokenizerException( "While scanning a block scalar, expected chomping or indentation indicators but found: " + ch(peek())); - return new Object[] {Boolean.valueOf(chomping), increment}; + return new Object[] {Integer.valueOf(chomping), increment}; } private String scanBlockScalarIgnoredLine () { From 7f52c09c942a1e87dbef7eb80b33a98e15159305 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 1 Mar 2022 10:38:47 -0400 Subject: [PATCH 101/114] Fixed test, javadocs. --- src/com/esotericsoftware/yamlbeans/YamlConfig.java | 2 +- src/com/esotericsoftware/yamlbeans/YamlReader.java | 10 +++++++++- test/test.yml | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 42c612e..3f9cced 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -221,7 +221,7 @@ public void setEscapeUnicode (boolean escapeUnicode) { emitterConfig.setEscapeUnicode(escapeUnicode); } - /** based on Ennumeration parameter, class name tags will be output. */ + /** Determines when class name tags are output. */ public void setWriteClassname (WriteClassName write) { writeClassName = write; } diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 16814a5..8071cc9 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -32,6 +33,7 @@ import java.util.Map.Entry; import com.esotericsoftware.yamlbeans.Beans.Property; +import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName; import com.esotericsoftware.yamlbeans.parser.AliasEvent; import com.esotericsoftware.yamlbeans.parser.CollectionStartEvent; import com.esotericsoftware.yamlbeans.parser.Event; @@ -476,6 +478,12 @@ public YamlReaderException (String message) { public static void main (String[] args) throws Exception { YamlReader reader = new YamlReader(new FileReader("test/test.yml")); - System.out.println(reader.read()); + Object object = reader.read(); + System.out.println(object); + StringWriter string = new StringWriter(); + YamlWriter writer = new YamlWriter(string); + writer.write(object); + writer.close(); + System.out.println(string); } } diff --git a/test/test.yml b/test/test.yml index c1bd737..d3b4786 100644 --- a/test/test.yml +++ b/test/test.yml @@ -1,14 +1,14 @@ ---- !net.sourceforge.yamlbeans.YamlWriterTest$Test +--- !com.esotericsoftware.yamlbeans.YamlWriterTest$Test booleanValue: true date: 2008-08-13 03:26:24 -child: &1 !net.sourceforge.yamlbeans.YamlWriterTest$Test2 +child: &1 !com.esotericsoftware.yamlbeans.YamlWriterTest$Test2 value: weeeee doubleValue: 1.5 floatValue: 1.2 intValue: 123 listValues: !java.util.LinkedList - woo -- !net.sourceforge.yamlbeans.YamlWriterTest$Test {} +- !com.esotericsoftware.yamlbeans.YamlWriterTest$Test {} - 123 - *1 mooCow: yay From 631d6295fe75e4d1d42cda42bda14b97c1ba5261 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 1 Mar 2022 10:50:05 -0400 Subject: [PATCH 102/114] Added AutoCloseable. --- src/com/esotericsoftware/yamlbeans/YamlReader.java | 2 +- src/com/esotericsoftware/yamlbeans/YamlWriter.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 291104d..46067a2 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -46,7 +46,7 @@ /** Deserializes Java objects from YAML. * @author Nathan Sweet */ -public class YamlReader { +public class YamlReader implements AutoCloseable { private final YamlConfig config; Parser parser; private final Map anchors = new HashMap(); diff --git a/src/com/esotericsoftware/yamlbeans/YamlWriter.java b/src/com/esotericsoftware/yamlbeans/YamlWriter.java index ba34909..9fee0f6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlWriter.java +++ b/src/com/esotericsoftware/yamlbeans/YamlWriter.java @@ -47,7 +47,7 @@ /** Serializes Java objects as YAML. * @author Nathan Sweet */ -public class YamlWriter { +public class YamlWriter implements AutoCloseable { private final YamlConfig config; private final Emitter emitter; private boolean started; From 6b0a6f29e3663caab9e7ea46101b1f6f16f39100 Mon Sep 17 00:00:00 2001 From: tarzan <15139679+tarzanwill@users.noreply.github.com> Date: Mon, 21 Mar 2022 20:12:15 +0800 Subject: [PATCH 103/114] Update CN_README.md (#162) The http link of the maven repository address is invalid. --- CN_README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CN_README.md b/CN_README.md index f8872ae..2829277 100644 --- a/CN_README.md +++ b/CN_README.md @@ -8,7 +8,7 @@ 而**YamlBeans**则可以让Java对象和YAML格式之间的转换(序列化和反序列化)变得更容易。 Maven 仓库: -http://repo1.maven.org/maven2/com/esotericsoftware/yamlbeans/yamlbeans/ +https://repo1.maven.org/maven2/com/esotericsoftware/yamlbeans/yamlbeans/ ## 基本的反序列化 From 64a14ed3cdcc19d4679f98e18479730c5fd1802c Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Fri, 9 Sep 2022 14:29:40 +0200 Subject: [PATCH 104/114] Update pom.xml --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5220162..284a175 100644 --- a/pom.xml +++ b/pom.xml @@ -19,8 +19,8 @@ - New BSD License - http://www.opensource.org/licenses/bsd-license.php + MIT License + https://opensource.org/licenses/MIT repo From f1ee429b11cfdec9006f006bbdcfe94a0dda9b90 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 26 Jan 2023 12:15:18 -0400 Subject: [PATCH 105/114] * Throw an exception if the YAML data specifies a type that is incompatible with the specified type. * Javadoc, clean up, set project encoding. --- .settings/org.eclipse.core.resources.prefs | 2 + .../yamlbeans/YamlReader.java | 69 +++++++++---------- 2 files changed, 35 insertions(+), 36 deletions(-) create mode 100644 .settings/org.eclipse.core.resources.prefs diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 46067a2..24c2990 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -84,19 +84,24 @@ public void close () throws IOException { } /** Reads the next YAML document and deserializes it into an object. The type of object is defined by the YAML tag. If there is - * no YAML tag, the object will be an {@link ArrayList}, {@link HashMap}, or String. */ + * no YAML tag, the object will be an {@link ArrayList}, {@link HashMap}, or String. + *

+ * This method creates and configures an instance of a class specified by the YAML data, so should be used only with YAML data + * from a trusted source. */ public Object read () throws YamlException { return read(null); } /** Reads an object of the specified type from YAML. - * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ + * @param type The type of object to read. If null, behaves the same as {{@link #read()}. + * @throws YamlReaderException if the YAML data specifies a type that is incompatible with the specified type. */ public T read (Class type) throws YamlException { return read(type, null); } /** Reads an array, Map, List, or Collection object of the specified type from YAML, using the specified element type. - * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ + * @param type The type of object to read. If null, behaves the same as {{@link #read()}. + * @throws YamlReaderException if the YAML data specifies a type that is incompatible with the specified type. */ public T read (Class type, Class elementType) throws YamlException { anchors.clear(); try { @@ -116,13 +121,10 @@ public T read (Class type, Class elementType) throws YamlException { } } - /** Reads all documents from YAML into Objects. - * - * @param type specify Object type - * @return an iterator reads documents in order */ + /** Returns an iterator that reads all documents from YAML into objects. + * @param type The type of object to read. If null, behaves the same as {{@link #read()}. */ public Iterator readAll (final Class type) { - Iterator iterator = new Iterator() { - + return new Iterator() { public boolean hasNext () { Event event = parser.peekNextEvent(); return event != null && event.type != STREAM_END; @@ -131,8 +133,8 @@ public boolean hasNext () { public T next () { try { return read(type); - } catch (YamlException e) { - throw new RuntimeException("Iterative reading documents exception", e); + } catch (YamlException ex) { + throw new RuntimeException("Error reading YAML document for iterator.", ex); } } @@ -140,11 +142,11 @@ public void remove () { throw new UnsupportedOperationException(); } }; - - return iterator; } - /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. */ + /** Reads an object from the YAML. Can be overidden to take some action for any of the objects returned. + * @param type May be null. + * @throws YamlReaderException if the YAML data specifies a type that is incompatible with the specified type. */ protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, TokenizerException { String tag = null, anchor = null; @@ -175,9 +177,7 @@ protected Object readValue (Class type, Class elementType, Class defaultType) private Class chooseType (String tag, Class defaultType, Class providedType) throws YamlReaderException { if (tag != null && config.readConfig.classTags) { Class userConfiguredByTag = config.tagToClass.get(tag); - if (userConfiguredByTag != null) { - return userConfiguredByTag; - } + if (userConfiguredByTag != null) return userConfiguredByTag; ClassLoader classLoader = (config.readConfig.classLoader == null ? this.getClass().getClassLoader() : config.readConfig.classLoader); @@ -186,6 +186,10 @@ private Class chooseType (String tag, Class defaultType, Class provided try { Class loadedFromTag = findTagClass(tag, classLoader); if (loadedFromTag != null) { + if (!providedType.isAssignableFrom(loadedFromTag)) { + throw new YamlReaderException("Class specified by tag is incompatible with expected type: " + + loadedFromTag.getName() + " (expected " + providedType.getName() + ")"); + } return loadedFromTag; } } catch (ClassNotFoundException e) { @@ -193,35 +197,28 @@ private Class chooseType (String tag, Class defaultType, Class provided } } - if (defaultType != null) { - return defaultType; - } + if (defaultType != null) return defaultType; - // This may be null. - return providedType; + return providedType; // May be null. } /** Used during reading when a tag is present, and {@link YamlConfig#setClassTag(String, Class)} was not used for that tag. * Attempts to load the class corresponding to that tag. - * + *

* If this returns a non-null Class, that will be used as the deserialization type regardless of whether a type was explicitly * asked for or if a default type exists. - * + *

* If this returns null, no guidance will be provided by the tag and we will fall back to the default type or a requested * target type, if any exist. - * + *

* If this throws a ClassNotFoundException, parsing will fail. - * - * The default implementation is simply - * - *

-	 *  {@code Class.forName(tag, true, classLoader);}
-	 * 
- * - * and never returns null. - * - * You can override this to handle cases where you do not want to respect the type tags found in a document - e.g., if they - * were output by another program using classes that do not exist on your classpath. */ + *

+ * The default implementation is simply {@code + * Class.forName(tag, true, classLoader); + * } and never returns null. + *

+ * You can override this to handle cases where you do not want to respect the type tags found in a document, eg if they were + * output by another program using classes that do not exist on your classpath. */ protected Class findTagClass (String tag, ClassLoader classLoader) throws ClassNotFoundException { return Class.forName(tag, true, classLoader); } From 08f4f7bc91709cc1c59e165e2882ca7c679d496f Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 26 Jan 2023 12:27:50 -0400 Subject: [PATCH 106/114] Added missing ReadConfig autoMerge setter. #87 --- .../yamlbeans/YamlConfig.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index a779746..78edd05 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -142,7 +142,7 @@ static public class WriteConfig { WriteClassName writeClassName = WriteClassName.AUTO; Quote quote = Quote.NONE; Version version; - Map tags; + Map tags; boolean flowStyle; EmitterConfig emitterConfig = new EmitterConfig(); @@ -201,7 +201,7 @@ public void setVersion (Version version) { } /** Sets the YAML tags to output. */ - public void setTags(Map tags) { + public void setTags (Map tags) { this.tags = tags; } @@ -240,21 +240,21 @@ public void setQuoteChar (Quote quote) { this.quote = quote; } - public Quote getQuote() { + public Quote getQuote () { return quote; } /** If true, the YAML output will be flow. Default is false. */ - public void setFlowStyle(boolean flowStyle) { + public void setFlowStyle (boolean flowStyle) { this.flowStyle = flowStyle; } - public boolean isFlowStyle() { + public boolean isFlowStyle () { return flowStyle; } /** If true, the YAML output will be pretty flow. Default is false. */ - public void setPrettyFlow(boolean prettyFlow) { + public void setPrettyFlow (boolean prettyFlow) { emitterConfig.setPrettyFlow(prettyFlow); } } @@ -310,6 +310,11 @@ public void setClassTags (boolean classTags) { this.classTags = classTags; } + /** When false, the merge key (<<) is not used to merge values into the current map. Default is true. */ + public void setAutoMerge (boolean autoMerge) { + this.autoMerge = autoMerge; + } + /** When true, if the type for a scalar value is unknown and it looks like a number, it is read as a double or long. When * false, if the type for a scalar value is unknown it is always read a string. Default is true. */ public void setGuessNumberTypes (boolean guessNumberTypes) { @@ -335,7 +340,7 @@ public static enum Quote { this.c = c; } - public char getStyle() { + public char getStyle () { return c; } } From b1122588e7610ae4e0d516c50d08c94ee87946e6 Mon Sep 17 00:00:00 2001 From: Joe Beeton Date: Wed, 1 Feb 2023 16:16:14 +0000 Subject: [PATCH 107/114] =?UTF-8?q?added=20a=20new=20read=20config=20flag?= =?UTF-8?q?=20of=20anchor=20to=20enable/disable=20following=20an=E2=80=A6?= =?UTF-8?q?=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added a new read config flag of anchor to enable/disable following anchors ( enabled by default ). Also added a new SafeYamlConfig class that disables Class Tags and Anchors to remediate CVE-2023-24620 CVE-2023-24621 * setAnchor() and setClassTag() now throws an IllegalArgumentException if an attempt is made to set them to true. --- .../yamlbeans/SafeYamlConfig.java | 50 ++++++++++++ .../yamlbeans/YamlConfig.java | 11 ++- .../yamlbeans/YamlReader.java | 25 ++++-- .../yamlbeans/SafeYamlConfigTest.java | 79 +++++++++++++++++++ 4 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java create mode 100644 test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java diff --git a/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java b/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java new file mode 100644 index 0000000..4d8b4f5 --- /dev/null +++ b/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java @@ -0,0 +1,50 @@ +package com.esotericsoftware.yamlbeans; + + +/** + * SafeYamlConfig extends YamlConfig and hard codes the read anchor and read class tag flags to false. + * When these flags are enabled, it is possible to perform a deserialization attack if the Yaml being parsed is from an + * untrusted source. + * Using SafeYamlConfig is the equivalent of using YamlConfig after setting + * yamlConfig.readConfig.setAnchors(false); + * yamlConfig.readConfig.setClassTags(false); + * + * It should be noted by setting these two values neither anchors or specifying class names are supported. + * It is still possible to deserialize back to a specific object, but you need to specify the Class type in the code. + * e.g + * SafeYamlConfig yamlConfig = new SafeYamlConfig(); + * YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); + * Data data = reader.read(Data.class); + * + */ +public class SafeYamlConfig extends YamlConfig { + + + public SafeYamlConfig () { + super(); + super.readConfig = new SafeReadConfig(); + } + + static public class SafeReadConfig extends ReadConfig { + + public SafeReadConfig(){ + super.anchors = false; + super.classTags = false; + } + + @Override + public void setClassTags(boolean classTags) { + if(classTags) { + throw new IllegalArgumentException("Class Tags cannot be enabled in SafeYamlConfig."); + } + } + + @Override + public void setAnchors(boolean anchors) { + if(anchors) { + throw new IllegalArgumentException("Anchors cannot be enabled in SafeYamlConfig."); + } + } + } + +} diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index 78edd05..fb1e017 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -37,7 +37,7 @@ public class YamlConfig { public final WriteConfig writeConfig = new WriteConfig(); /** Configuration for reading YAML. */ - public final ReadConfig readConfig = new ReadConfig(); + public ReadConfig readConfig = new ReadConfig(); final Map classNameToTag = new HashMap(); final Map tagToClass = new HashMap(); @@ -268,6 +268,10 @@ static public class ReadConfig { boolean classTags = true; boolean guessNumberTypes; + + + boolean anchors = true; + ReadConfig () { } @@ -320,6 +324,11 @@ public void setAutoMerge (boolean autoMerge) { public void setGuessNumberTypes (boolean guessNumberTypes) { this.guessNumberTypes = guessNumberTypes; } + + public void setAnchors(boolean anchors) { + this.anchors = anchors; + } + } static class ConstructorParameters { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 24c2990..6d6f722 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -78,6 +78,13 @@ public Object get (String alias) { return anchors.get(alias); } + private void addAnchor(String key, Object value) { + if(config.readConfig.anchors) { + anchors.put(key, value); + } + } + + public void close () throws IOException { parser.close(); anchors.clear(); @@ -157,7 +164,7 @@ protected Object readValue (Class type, Class elementType, Class defaultType) parser.getNextEvent(); anchor = ((AliasEvent)event).anchor; Object value = anchors.get(anchor); - if (value == null) throw new YamlReaderException("Unknown anchor: " + anchor); + if (value == null&&config.readConfig.anchors) throw new YamlReaderException("Unknown anchor: " + anchor); return value; case MAPPING_START: case SEQUENCE_START: @@ -238,7 +245,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) Number number = valueConvertedNumber(value); if (number != null) { if (anchor != null) { - anchors.put(anchor, number); + addAnchor(anchor, number); } parser.getNextEvent(); return number; @@ -284,7 +291,9 @@ private Object readValueInternal (Class type, Class elementType, String anchor) convertedValue = Byte.decode(value); } else throw new YamlException("Unknown field type."); - if (anchor != null) anchors.put(anchor, convertedValue); + if (anchor != null) { + addAnchor(anchor, convertedValue); + } return convertedValue; } catch (Exception ex) { throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); @@ -298,7 +307,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (event.type != SCALAR) throw new YamlReaderException("Expected scalar for type '" + type + "' to be deserialized by scalar serializer '" + serializer.getClass().getName() + "' but found: " + event.type); Object value = serializer.read(((ScalarEvent)event).value); - if (anchor != null) anchors.put(anchor, value); + if (anchor != null) addAnchor(anchor, value); return value; } } @@ -326,7 +335,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } catch (InvocationTargetException ex) { throw new YamlReaderException("Error creating object.", ex); } - if (anchor != null) anchors.put(anchor, object); + if (anchor != null) addAnchor(anchor, object); ArrayList keys = new ArrayList(); while (true) { if (parser.peekNextEvent().type == MAPPING_END) { @@ -404,7 +413,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (object instanceof DeferredConstruction) { try { object = ((DeferredConstruction)object).construct(); - if (anchor != null) anchors.put(anchor, object); // Update anchor with real object. + if (anchor != null) addAnchor(anchor, object); // Update anchor with real object. } catch (InvocationTargetException ex) { throw new YamlReaderException("Error creating object.", ex); } @@ -426,7 +435,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) elementType = type.getComponentType(); } else throw new YamlReaderException("A sequence is not a valid value for the type: " + type.getName()); - if (!type.isArray() && anchor != null) anchors.put(anchor, collection); + if (!type.isArray() && anchor != null) addAnchor(anchor, collection); while (true) { event = parser.peekNextEvent(); if (event.type == SEQUENCE_END) { @@ -440,7 +449,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) int i = 0; for (Object object : collection) Array.set(array, i++, object); - if (anchor != null) anchors.put(anchor, array); + if (anchor != null) addAnchor(anchor, array); return array; } default: diff --git a/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java new file mode 100644 index 0000000..9eb23ec --- /dev/null +++ b/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java @@ -0,0 +1,79 @@ +package com.esotericsoftware.yamlbeans; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +public class SafeYamlConfigTest { + + + private static final String TESTOBJECT_TAG = "!com.esotericsoftware.yamlbeans.SafeYamlConfigTest$TestObject"; + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + + @Test + public void testDeserializationOfClassTag() throws YamlException { + SafeYamlConfig yamlConfig = new SafeYamlConfig(); + StringBuilder yamlData = new StringBuilder(); + yamlData.append(TESTOBJECT_TAG).append(LINE_SEPARATOR) + .append("a: test").append(LINE_SEPARATOR); + YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); + Object data = reader.read(); + assertTrue(data instanceof HashMap); + Map dataMap = (Map) data; + assertTrue(dataMap.containsKey("a")); + assertEquals("test",dataMap.get("a")); + } + + + + @Test + public void testIgnoreAnchor() throws YamlException { + SafeYamlConfig yamlConfig = new SafeYamlConfig(); + StringBuilder yamlData = new StringBuilder(); + yamlData.append("oldest friend:").append(LINE_SEPARATOR) + .append(" &1 !contact").append(LINE_SEPARATOR) + .append(" name: Bob").append(LINE_SEPARATOR) + .append(" age: 29").append(LINE_SEPARATOR) + .append("best friend: *1").append(LINE_SEPARATOR); + YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); + Object data = reader.read(); + assertTrue(data instanceof HashMap); + Map dataMap = (Map) data; + assertTrue(dataMap.containsKey("oldest friend")); + Map old = (Map) dataMap.get("oldest friend"); + assertTrue(old.containsKey("name")); + assertEquals("Bob",old.get("name")); + assertNull(dataMap.get("best friend")); + } + + + static class TestObject { + private String a; + public int age; + public String name; + public Object object; + public List objects; + + private TestObject() { + } + + public TestObject(String a) { + this.a = a; + } + + public String getA() { + return a; + } + + public void setA(String a) { + this.a = a; + } + } +} From 33a0f91ddc524821f365e7a1fc0f5e9ed224a8f4 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 1 Feb 2023 12:20:31 -0400 Subject: [PATCH 108/114] Formatting, clean up. #164 --- .../yamlbeans/SafeYamlConfig.java | 82 +++++------ .../yamlbeans/YamlConfig.java | 7 +- .../yamlbeans/YamlReader.java | 17 +-- .../yamlbeans/SafeYamlConfigTest.java | 128 ++++++++---------- 4 files changed, 103 insertions(+), 131 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java b/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java index 4d8b4f5..e0cbad6 100644 --- a/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/SafeYamlConfig.java @@ -1,50 +1,42 @@ -package com.esotericsoftware.yamlbeans; +package com.esotericsoftware.yamlbeans; -/** - * SafeYamlConfig extends YamlConfig and hard codes the read anchor and read class tag flags to false. - * When these flags are enabled, it is possible to perform a deserialization attack if the Yaml being parsed is from an - * untrusted source. - * Using SafeYamlConfig is the equivalent of using YamlConfig after setting - * yamlConfig.readConfig.setAnchors(false); - * yamlConfig.readConfig.setClassTags(false); - * - * It should be noted by setting these two values neither anchors or specifying class names are supported. - * It is still possible to deserialize back to a specific object, but you need to specify the Class type in the code. - * e.g - * SafeYamlConfig yamlConfig = new SafeYamlConfig(); - * YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); - * Data data = reader.read(Data.class); - * +/** SafeYamlConfig extends YamlConfig and hard codes the read anchor and read class tag flags to false. When these flags are + * enabled, it is possible to perform a deserialization attack if the Yaml being parsed is from an untrusted source. + *

+ * Using SafeYamlConfig is the equivalent of using YamlConfig after setting: + * + *

+ * yamlConfig.readConfig.setAnchors(false);
+ * yamlConfig.readConfig.setClassTags(false);
+ * 
+ * + * It should be noted by setting these two values neither anchors or specifying class names are supported. It is still possible to + * deserialize back to a specific object, but you need to specify the Class type in the code. For example: + * + *
+ * SafeYamlConfig config = new SafeYamlConfig();
+ * YamlReader reader = new YamlReader(yamlData.toString(), config);
+ * Data data = reader.read(Data.class);
+ * 
*/ public class SafeYamlConfig extends YamlConfig { - - - public SafeYamlConfig () { - super(); - super.readConfig = new SafeReadConfig(); - } - - static public class SafeReadConfig extends ReadConfig { - - public SafeReadConfig(){ - super.anchors = false; - super.classTags = false; - } - - @Override - public void setClassTags(boolean classTags) { - if(classTags) { - throw new IllegalArgumentException("Class Tags cannot be enabled in SafeYamlConfig."); - } - } - - @Override - public void setAnchors(boolean anchors) { - if(anchors) { - throw new IllegalArgumentException("Anchors cannot be enabled in SafeYamlConfig."); - } - } - } - + public SafeYamlConfig () { + super.readConfig = new SafeReadConfig(); + } + + static public class SafeReadConfig extends ReadConfig { + public SafeReadConfig () { + super.anchors = false; + super.classTags = false; + } + + public void setClassTags (boolean classTags) { + if (classTags) throw new IllegalArgumentException("Class Tags cannot be enabled in SafeYamlConfig."); + } + + public void setAnchors (boolean anchors) { + if (anchors) throw new IllegalArgumentException("Anchors cannot be enabled in SafeYamlConfig."); + } + } } diff --git a/src/com/esotericsoftware/yamlbeans/YamlConfig.java b/src/com/esotericsoftware/yamlbeans/YamlConfig.java index fb1e017..b00053e 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlConfig.java +++ b/src/com/esotericsoftware/yamlbeans/YamlConfig.java @@ -267,9 +267,6 @@ static public class ReadConfig { boolean autoMerge = true; boolean classTags = true; boolean guessNumberTypes; - - - boolean anchors = true; ReadConfig () { @@ -325,10 +322,10 @@ public void setGuessNumberTypes (boolean guessNumberTypes) { this.guessNumberTypes = guessNumberTypes; } - public void setAnchors(boolean anchors) { + /** When false, anchors in the YAML are ignored. Default is true. */ + public void setAnchors (boolean anchors) { this.anchors = anchors; } - } static class ConstructorParameters { diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 6d6f722..f247cb6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -78,13 +78,10 @@ public Object get (String alias) { return anchors.get(alias); } - private void addAnchor(String key, Object value) { - if(config.readConfig.anchors) { - anchors.put(key, value); - } + private void addAnchor (String key, Object value) { + if (config.readConfig.anchors) anchors.put(key, value); } - public void close () throws IOException { parser.close(); anchors.clear(); @@ -164,7 +161,7 @@ protected Object readValue (Class type, Class elementType, Class defaultType) parser.getNextEvent(); anchor = ((AliasEvent)event).anchor; Object value = anchors.get(anchor); - if (value == null&&config.readConfig.anchors) throw new YamlReaderException("Unknown anchor: " + anchor); + if (value == null && config.readConfig.anchors) throw new YamlReaderException("Unknown anchor: " + anchor); return value; case MAPPING_START: case SEQUENCE_START: @@ -244,9 +241,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (value != null) { Number number = valueConvertedNumber(value); if (number != null) { - if (anchor != null) { - addAnchor(anchor, number); - } + if (anchor != null) addAnchor(anchor, number); parser.getNextEvent(); return number; } @@ -291,9 +286,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) convertedValue = Byte.decode(value); } else throw new YamlException("Unknown field type."); - if (anchor != null) { - addAnchor(anchor, convertedValue); - } + if (anchor != null) addAnchor(anchor, convertedValue); return convertedValue; } catch (Exception ex) { throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); diff --git a/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java b/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java index 9eb23ec..7d59e3c 100644 --- a/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java +++ b/test/com/esotericsoftware/yamlbeans/SafeYamlConfigTest.java @@ -1,79 +1,69 @@ + package com.esotericsoftware.yamlbeans; -import org.junit.Test; +import static junit.framework.Assert.*; import java.util.HashMap; import java.util.List; import java.util.Map; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; +import org.junit.Test; public class SafeYamlConfigTest { - - - private static final String TESTOBJECT_TAG = "!com.esotericsoftware.yamlbeans.SafeYamlConfigTest$TestObject"; - private static final String LINE_SEPARATOR = System.getProperty("line.separator"); - - - @Test - public void testDeserializationOfClassTag() throws YamlException { - SafeYamlConfig yamlConfig = new SafeYamlConfig(); - StringBuilder yamlData = new StringBuilder(); - yamlData.append(TESTOBJECT_TAG).append(LINE_SEPARATOR) - .append("a: test").append(LINE_SEPARATOR); - YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); - Object data = reader.read(); - assertTrue(data instanceof HashMap); - Map dataMap = (Map) data; - assertTrue(dataMap.containsKey("a")); - assertEquals("test",dataMap.get("a")); - } - - - - @Test - public void testIgnoreAnchor() throws YamlException { - SafeYamlConfig yamlConfig = new SafeYamlConfig(); - StringBuilder yamlData = new StringBuilder(); - yamlData.append("oldest friend:").append(LINE_SEPARATOR) - .append(" &1 !contact").append(LINE_SEPARATOR) - .append(" name: Bob").append(LINE_SEPARATOR) - .append(" age: 29").append(LINE_SEPARATOR) - .append("best friend: *1").append(LINE_SEPARATOR); - YamlReader reader = new YamlReader(yamlData.toString(),yamlConfig); - Object data = reader.read(); - assertTrue(data instanceof HashMap); - Map dataMap = (Map) data; - assertTrue(dataMap.containsKey("oldest friend")); - Map old = (Map) dataMap.get("oldest friend"); - assertTrue(old.containsKey("name")); - assertEquals("Bob",old.get("name")); - assertNull(dataMap.get("best friend")); - } - - - static class TestObject { - private String a; - public int age; - public String name; - public Object object; - public List objects; - - private TestObject() { - } - - public TestObject(String a) { - this.a = a; - } - - public String getA() { - return a; - } - - public void setA(String a) { - this.a = a; - } - } + private static final String TESTOBJECT_TAG = "!com.esotericsoftware.yamlbeans.SafeYamlConfigTest$TestObject"; + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void testDeserializationOfClassTag () throws YamlException { + SafeYamlConfig yamlConfig = new SafeYamlConfig(); + StringBuilder yamlData = new StringBuilder(); + yamlData.append(TESTOBJECT_TAG).append(LINE_SEPARATOR).append("a: test").append(LINE_SEPARATOR); + YamlReader reader = new YamlReader(yamlData.toString(), yamlConfig); + Object data = reader.read(); + assertTrue(data instanceof HashMap); + Map dataMap = (Map)data; + assertTrue(dataMap.containsKey("a")); + assertEquals("test", dataMap.get("a")); + } + + @Test + public void testIgnoreAnchor () throws YamlException { + SafeYamlConfig yamlConfig = new SafeYamlConfig(); + StringBuilder yamlData = new StringBuilder(); + yamlData.append("oldest friend:").append(LINE_SEPARATOR).append(" &1 !contact").append(LINE_SEPARATOR) + .append(" name: Bob").append(LINE_SEPARATOR).append(" age: 29").append(LINE_SEPARATOR).append("best friend: *1") + .append(LINE_SEPARATOR); + YamlReader reader = new YamlReader(yamlData.toString(), yamlConfig); + Object data = reader.read(); + assertTrue(data instanceof HashMap); + Map dataMap = (Map)data; + assertTrue(dataMap.containsKey("oldest friend")); + Map old = (Map)dataMap.get("oldest friend"); + assertTrue(old.containsKey("name")); + assertEquals("Bob", old.get("name")); + assertNull(dataMap.get("best friend")); + } + + static class TestObject { + private String a; + public int age; + public String name; + public Object object; + public List objects; + + private TestObject () { + } + + public TestObject (String a) { + this.a = a; + } + + public String getA () { + return a; + } + + public void setA (String a) { + this.a = a; + } + } } From 34eeff9671d3ead9587a217ba6408293cc0b940e Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 5 Sep 2023 16:44:42 -0400 Subject: [PATCH 109/114] 1.17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 284a175..c62aa9d 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.16-SNAPSHOT + 1.17 Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 8f35506dbadef79e569106a3b222a221a6035db3 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 5 Sep 2023 16:45:49 -0400 Subject: [PATCH 110/114] 1.18-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c62aa9d..76c4e53 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ YamlBeans com.esotericsoftware.yamlbeans yamlbeans - 1.17 + 1.18-SNAPSHOT Java object graphs, to and from YAML https://github.com/EsotericSoftware/yamlbeans From 9ca85f8e43835a10f9a53a8d0509bb9538e8744d Mon Sep 17 00:00:00 2001 From: Tony CHEMIT Date: Mon, 27 Nov 2023 18:23:10 +0100 Subject: [PATCH 111/114] Issue 173 (#174) * fix maven configuration (test resources) * Fix NPe in tests - Closes #173 --- pom.xml | 8 ++++++++ src/com/esotericsoftware/yamlbeans/YamlReader.java | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76c4e53..1316c92 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,14 @@ src test target/classes + + + test + + **/*.yml + + + maven-compiler-plugin diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index f247cb6..6c510d8 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -190,7 +190,7 @@ private Class chooseType (String tag, Class defaultType, Class provided try { Class loadedFromTag = findTagClass(tag, classLoader); if (loadedFromTag != null) { - if (!providedType.isAssignableFrom(loadedFromTag)) { + if (providedType != null && !providedType.isAssignableFrom(loadedFromTag)) { throw new YamlReaderException("Class specified by tag is incompatible with expected type: " + loadedFromTag.getName() + " (expected " + providedType.getName() + ")"); } From 763726d51c99824218051e485e1b44f7477456fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:47:31 +0100 Subject: [PATCH 112/114] Bump junit from 4.8.2 to 4.13.1 (#158) Bumps [junit](https://github.com/junit-team/junit4) from 4.8.2 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.8.2...r4.13.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1316c92..b8bb746 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ test junit junit - 4.8.2 + 4.13.1 From 5bb07f1cde3f03e7e48c83f7ebbefb4c09ba7ae9 Mon Sep 17 00:00:00 2001 From: Vaibhav Singh <0vaibhavsingh0@gmail.com> Date: Sat, 16 Mar 2024 16:07:15 -0300 Subject: [PATCH 113/114] Refactor Parser and YamlReader (#176) * refactor(parser): decompose conditional for implicit document for initProductionTable method * refactor(yaml-reader): rename variable anchors to anchorMap Makes the name more descriptive and readable. By reading the name you can tell it's a hashmap which was not clear previously. * refactor(yaml-reader): extract method readScalarValue from readValueInternal Extract out the deserialization of scalar values to a new method. Make readValueInternal more modular * refactor(yaml-reader): rename method valueConvertedNumber to parseNumber The method valueConvertedNumber has been renamed to parseNumber for improved clarity and consistency with naming conventions. Enhances the readability of the codebase and better reflects the purpose of the method, which is to parse a string value into a Number object. * revert "refactor(yaml-reader): rename variable anchors to anchorMap" This reverts commit 78d10cdd187a835a0eec596f134d0ac5906c1d1f. --- .../yamlbeans/YamlReader.java | 87 +++++++++++-------- .../yamlbeans/parser/Parser.java | 19 ++-- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/com/esotericsoftware/yamlbeans/YamlReader.java b/src/com/esotericsoftware/yamlbeans/YamlReader.java index 6c510d8..351d4a6 100644 --- a/src/com/esotericsoftware/yamlbeans/YamlReader.java +++ b/src/com/esotericsoftware/yamlbeans/YamlReader.java @@ -227,6 +227,55 @@ protected Class findTagClass (String tag, ClassLoader classLoader) throws Cla return Class.forName(tag, true, classLoader); } + /** + * Reads a scalar value from the parser and converts it to the specified type. + * @param type The type to convert the scalar to. + * @param anchor The anchor of the scalar, or null. + * @return The converted scalar value. + * @throws YamlException + * @throws ParserException + */ + private Object readScalarValue(Class type, String anchor) throws YamlException, ParserException { + Event event = parser.getNextEvent(); + if (event.type != SCALAR) { + throw new YamlReaderException("Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); + } + + String value = ((ScalarEvent) event).value; + try { + Object convertedValue; + if (value == null) { + convertedValue = null; + } else if (type == String.class) { + convertedValue = value; + } else if (type == Integer.TYPE || type == Integer.class) { + convertedValue = Integer.decode(value); + } else if (type == Boolean.TYPE || type == Boolean.class) { + convertedValue = Boolean.valueOf(value); + } else if (type == Float.TYPE || type == Float.class) { + convertedValue = Float.valueOf(value); + } else if (type == Double.TYPE || type == Double.class) { + convertedValue = Double.valueOf(value); + } else if (type == Long.TYPE || type == Long.class) { + convertedValue = Long.decode(value); + } else if (type == Short.TYPE || type == Short.class) { + convertedValue = Short.decode(value); + } else if (type == Character.TYPE || type == Character.class) { + convertedValue = value.charAt(0); + } else if (type == Byte.TYPE || type == Byte.class) { + convertedValue = Byte.decode(value); + } else { + throw new YamlException("Unknown field type."); + } + if (anchor != null) { + addAnchor(anchor, convertedValue); + } + return convertedValue; + } catch (Exception ex) { + throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); + } + } + private Object readValueInternal (Class type, Class elementType, String anchor) throws YamlException, ParserException, TokenizerException { if (type == null || type == Object.class) { @@ -239,7 +288,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) if (config.readConfig.guessNumberTypes) { String value = ((ScalarEvent)event).value; if (value != null) { - Number number = valueConvertedNumber(value); + Number number = parseNumber(value); if (number != null) { if (anchor != null) addAnchor(anchor, number); parser.getNextEvent(); @@ -258,39 +307,7 @@ private Object readValueInternal (Class type, Class elementType, String anchor) } if (Beans.isScalar(type)) { - Event event = parser.getNextEvent(); - if (event.type != SCALAR) throw new YamlReaderException( - "Expected scalar for primitive type '" + type.getClass() + "' but found: " + event.type); - String value = ((ScalarEvent)event).value; - try { - Object convertedValue; - if (value == null) { - convertedValue = null; - } else if (type == String.class) { - convertedValue = value; - } else if (type == Integer.TYPE || type == Integer.class) { - convertedValue = Integer.decode(value); - } else if (type == Boolean.TYPE || type == Boolean.class) { - convertedValue = Boolean.valueOf(value); - } else if (type == Float.TYPE || type == Float.class) { - convertedValue = Float.valueOf(value); - } else if (type == Double.TYPE || type == Double.class) { - convertedValue = Double.valueOf(value); - } else if (type == Long.TYPE || type == Long.class) { - convertedValue = Long.decode(value); - } else if (type == Short.TYPE || type == Short.class) { - convertedValue = Short.decode(value); - } else if (type == Character.TYPE || type == Character.class) { - convertedValue = value.charAt(0); - } else if (type == Byte.TYPE || type == Byte.class) { - convertedValue = Byte.decode(value); - } else - throw new YamlException("Unknown field type."); - if (anchor != null) addAnchor(anchor, convertedValue); - return convertedValue; - } catch (Exception ex) { - throw new YamlReaderException("Unable to convert value to required type \"" + type + "\": " + value, ex); - } + return readScalarValue(type, anchor); } for (Entry entry : config.scalarSerializers.entrySet()) { @@ -486,7 +503,7 @@ public YamlReaderException (String message) { } } - private Number valueConvertedNumber (String value) { + private Number parseNumber(String value) { Number number = null; try { number = Long.decode(value); diff --git a/src/com/esotericsoftware/yamlbeans/parser/Parser.java b/src/com/esotericsoftware/yamlbeans/parser/Parser.java index 4845aa1..0ffedf2 100644 --- a/src/com/esotericsoftware/yamlbeans/parser/Parser.java +++ b/src/com/esotericsoftware/yamlbeans/parser/Parser.java @@ -127,15 +127,24 @@ public Event produce () { }; table[P_IMPLICIT_DOCUMENT] = new Production() { public Event produce () { - TokenType type = tokenizer.peekNextTokenType(); - if (!(type == DIRECTIVE || type == DOCUMENT_START || type == STREAM_END)) { - parseStack.add(0, table[P_DOCUMENT_END]); - parseStack.add(0, table[P_BLOCK_NODE]); - parseStack.add(0, table[P_DOCUMENT_START_IMPLICIT]); + if (isNextTokenImplicitDocument()) { + parseImplicitDocument(); } return null; } + + private boolean isNextTokenImplicitDocument() { + TokenType type = tokenizer.peekNextTokenType(); + return type != DIRECTIVE && type != DOCUMENT_START && type != STREAM_END; + } + + private void parseImplicitDocument() { + parseStack.add(0, table[P_DOCUMENT_END]); + parseStack.add(0, table[P_BLOCK_NODE]); + parseStack.add(0, table[P_DOCUMENT_START_IMPLICIT]); + } }; + table[P_EXPLICIT_DOCUMENT] = new Production() { public Event produce () { if (tokenizer.peekNextTokenType() != STREAM_END) { From a7212ab2113b62cb377f1ad064c1b9158ea473e7 Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Wed, 4 Jun 2025 20:25:43 -0400 Subject: [PATCH 114/114] Java 8 --- .classpath | 6 +++++- .settings/org.eclipse.jdt.core.prefs | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.classpath b/.classpath index 0055a91..ef00c41 100644 --- a/.classpath +++ b/.classpath @@ -2,7 +2,11 @@ - + + + + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index f42de36..fb1077b 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,8 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=1.8