Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.esotericsoftware.yamlbeans.YamlWriter Maven / Gradle / Ivy
/*
* Copyright (c) 2008 Nathan Sweet
*
* 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;
import java.io.IOException;
import java.io.Writer;
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.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.esotericsoftware.yamlbeans.Beans.Property;
import com.esotericsoftware.yamlbeans.YamlConfig.WriteClassName;
import com.esotericsoftware.yamlbeans.YamlConfig.WriteConfig;
import com.esotericsoftware.yamlbeans.document.YamlElement;
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 implements AutoCloseable {
private final YamlConfig config;
private final Emitter emitter;
private boolean started;
private Map defaultValuePrototypes = new IdentityHashMap();
private final List queuedObjects = new ArrayList();
private final Map referenceCount = new IdentityHashMap();
private final Map anchoredObjects = new HashMap();
private int nextAnchor = 1;
private boolean isRoot;
public YamlWriter (Writer writer) {
this(writer, new YamlConfig());
}
public YamlWriter (Writer writer, YamlConfig config) {
this.config = config;
emitter = new Emitter(writer, config.writeConfig.emitterConfig);
}
public void setAlias (Object object, String alias) {
anchoredObjects.put(object, alias);
}
public void write (Object object) throws YamlException {
if (config.writeConfig.autoAnchor) {
countObjectReferences(object);
queuedObjects.add(object);
return;
}
writeInternal(object);
}
public YamlConfig getConfig () {
return config;
}
private void writeInternal (Object object) throws YamlException {
try {
if (!started) {
emitter.emit(Event.STREAM_START);
started = true;
}
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));
} catch (EmitterException ex) {
throw new YamlException("Error writing YAML.", ex);
} catch (IOException ex) {
throw new YamlException("Error writing YAML.", ex);
}
}
/** Writes any buffered objects, then resets the list of anchored objects.
* @see WriteConfig#setAutoAnchor(boolean) */
public void clearAnchors () throws YamlException {
for (Object object : queuedObjects)
writeInternal(object);
queuedObjects.clear();
referenceCount.clear();
nextAnchor = 1;
}
/** 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 {
clearAnchors();
defaultValuePrototypes.clear();
try {
emitter.emit(Event.STREAM_END);
emitter.close();
} catch (EmitterException ex) {
throw new YamlException(ex);
} catch (IOException ex) {
throw new YamlException(ex);
}
}
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) {
((YamlElement)object).emitEvent(emitter, config.writeConfig);
return;
} else if (object == null) {
emitter.emit(
new ScalarEvent(null, null, new boolean[] { true, true }, null, this.config.writeConfig.quote.c));
return;
}
Class valueClass = object.getClass();
boolean unknownType = fieldClass == null;
if (unknownType) fieldClass = valueClass;
String anchor = null;
if (!Beans.isScalar(valueClass) && !(object instanceof Enum)) {
anchor = anchoredObjects.get(object);
if (config.writeConfig.autoAnchor) {
Integer count = referenceCount.get(object);
if (count == null) {
emitter.emit(new AliasEvent(anchor));
return;
}
if (count > 1) {
referenceCount.remove(object);
if (anchor == null) {
anchor = String.valueOf(nextAnchor++);
anchoredObjects.put(object, anchor);
}
}
}
}
String tag = null;
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 (showTag) {
tag = config.classNameToTag.get(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), this.config.writeConfig.quote.c));
return;
}
}
if (Beans.isScalar(valueClass)) {
emitter.emit(new ScalarEvent(null, tag, new boolean[] { true, true }, String.valueOf(object),
this.config.writeConfig.quote.c));
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, config.writeConfig.isFlowStyle()));
for (Object item : (Collection)object) {
if (isRoot && !config.writeConfig.writeRootElementTags) elementType = item.getClass();
writeValue(item, elementType, null, null);
}
emitter.emit(Event.SEQUENCE_END);
return;
}
if (object instanceof Map) {
emitter.emit(new MappingStartEvent(anchor, tag, !showTag, config.writeConfig.isFlowStyle()));
Map map = (Map)object;
for (Object item : map.entrySet()) {
Entry entry = (Entry)item;
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) {
writeValue(key, null, null, null);
emitter.emit(new ScalarEvent(null, (String) valueTag, new boolean[] { false, false },
(String) value, this.config.writeConfig.quote.c));
continue;
}
}
}
writeValue(key, null, null, null);
writeValue(value, elementType, null, null);
}
emitter.emit(Event.MAPPING_END);
return;
}
if (fieldClass.isArray()) {
elementType = fieldClass.getComponentType();
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);
return;
}
// Value must be a bean.
Object prototype = null;
if (!config.writeConfig.writeDefaultValues && valueClass != Class.class) {
prototype = defaultValuePrototypes.get(valueClass);
if (prototype == null && Beans.getDeferredConstruction(valueClass, config) == null) {
try {
prototype = Beans.createObject(valueClass, config.privateConstructors);
} catch (InvocationTargetException ex) {
throw new YamlException("Error creating object prototype to determine default values.", ex);
}
defaultValuePrototypes.put(valueClass, prototype);
}
}
Set properties = Beans.getProperties(valueClass, config.beanProperties, config.privateFields, config);
emitter.emit(new MappingStartEvent(anchor, tag, !showTag, config.writeConfig.isFlowStyle()));
for (Property property : properties) {
try {
Object propertyValue = property.get(object);
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;
}
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);
} catch (Exception ex) {
throw new YamlException("Error getting property '" + property + "' on class: " + valueClass.getName(), ex);
}
}
emitter.emit(Event.MAPPING_END);
}
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);
if (count != null) {
referenceCount.put(object, count + 1);
return;
}
referenceCount.put(object, 1);
if (object instanceof Collection) {
for (Object item : (Collection)object)
countObjectReferences(item);
return;
}
if (object instanceof Map) {
for (Object value : ((Map)object).values())
countObjectReferences(value);
return;
}
if (object.getClass().isArray()) {
for (int i = 0, n = Array.getLength(object); i < n; i++)
countObjectReferences(Array.get(object, i));
return;
}
// Value must be an object.
Set properties = Beans.getProperties(object.getClass(), config.beanProperties, config.privateFields, config);
for (Property property : properties) {
if (Beans.isScalar(property.getType())) continue;
Object propertyValue;
try {
propertyValue = property.get(object);
} catch (Exception ex) {
throw new YamlException("Error getting property '" + property + "' on class: " + object.getClass().getName(), ex);
}
countObjectReferences(propertyValue);
}
}
}