aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/yaml/snakeyaml/representer/Representer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/yaml/snakeyaml/representer/Representer.java')
-rw-r--r--src/main/java/org/yaml/snakeyaml/representer/Representer.java410
1 files changed, 213 insertions, 197 deletions
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
index e83a5ea2..5376b6e2 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
@@ -1,32 +1,32 @@
/**
- * Copyright (c) 2008, http://www.snakeyaml.org
+ * Copyright (c) 2008, SnakeYAML
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
*/
package org.yaml.snakeyaml.representer;
-import java.beans.IntrospectionException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-
+import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
-import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
@@ -40,209 +40,225 @@ import org.yaml.snakeyaml.nodes.Tag;
*/
public class Representer extends SafeRepresenter {
- public Representer() {
- this.representers.put(null, new RepresentJavaBean());
+ protected Map<Class<? extends Object>, TypeDescription> typeDefinitions = Collections.emptyMap();
+
+ public Representer() {
+ this.representers.put(null, new RepresentJavaBean());
+ }
+
+ public Representer(DumperOptions options) {
+ super(options);
+ this.representers.put(null, new RepresentJavaBean());
+ }
+
+ public TypeDescription addTypeDescription(TypeDescription td) {
+ if (Collections.EMPTY_MAP == typeDefinitions) {
+ typeDefinitions = new HashMap<Class<? extends Object>, TypeDescription>();
}
+ if (td.getTag() != null) {
+ addClassTag(td.getType(), td.getTag());
+ }
+ td.setPropertyUtils(getPropertyUtils());
+ return typeDefinitions.put(td.getType(), td);
+ }
- protected class RepresentJavaBean implements Represent {
- public Node representData(Object data) {
- try {
- return representJavaBean(getProperties(data.getClass()), data);
- } catch (IntrospectionException e) {
- throw new YAMLException(e);
- }
- }
+ @Override
+ public void setPropertyUtils(PropertyUtils propertyUtils) {
+ super.setPropertyUtils(propertyUtils);
+ Collection<TypeDescription> tds = typeDefinitions.values();
+ for (TypeDescription typeDescription : tds) {
+ typeDescription.setPropertyUtils(propertyUtils);
}
+ }
- /**
- * Tag logic:<br/>
- * - explicit root tag is set in serializer <br/>
- * - if there is a predefined class tag it is used<br/>
- * - a global tag with class name is always used as tag. The JavaBean parent
- * of the specified JavaBean may set another tag (tag:yaml.org,2002:map)
- * when the property class is the same as runtime class
- *
- * @param properties
- * JavaBean getters
- * @param javaBean
- * instance for Node
- * @return Node to get serialized
- */
- protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
- List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
- Tag tag;
- Tag customTag = classTags.get(javaBean.getClass());
- tag = customTag != null ? customTag : new Tag(javaBean.getClass());
- // flow style will be chosen by BaseRepresenter
- MappingNode node = new MappingNode(tag, value, null);
- representedObjects.put(javaBean, node);
- boolean bestStyle = true;
- for (Property property : properties) {
- Object memberValue = property.get(javaBean);
- Tag customPropertyTag = memberValue == null ? null : classTags.get(memberValue
- .getClass());
- NodeTuple tuple = representJavaBeanProperty(javaBean, property, memberValue,
- customPropertyTag);
- if (tuple == null) {
- continue;
- }
- if (((ScalarNode) tuple.getKeyNode()).getStyle() != null) {
- bestStyle = false;
- }
- Node nodeValue = tuple.getValueNode();
- if (!(nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).getStyle() == null)) {
- bestStyle = false;
- }
- value.add(tuple);
- }
- if (defaultFlowStyle != FlowStyle.AUTO) {
- node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
- } else {
- node.setFlowStyle(bestStyle);
- }
- return node;
+ protected class RepresentJavaBean implements Represent {
+
+ public Node representData(Object data) {
+ return representJavaBean(getProperties(data.getClass()), data);
+ }
+ }
+
+ /**
+ * Tag logic: - explicit root tag is set in serializer - if there is a predefined class tag it is
+ * used - a global tag with class name is always used as tag. The JavaBean parent of the specified
+ * JavaBean may set another tag (tag:yaml.org,2002:map) when the property class is the same as
+ * runtime class
+ *
+ * @param properties JavaBean getters
+ * @param javaBean instance for Node
+ * @return Node to get serialized
+ */
+ protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
+ List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
+ Tag tag;
+ Tag customTag = classTags.get(javaBean.getClass());
+ tag = customTag != null ? customTag : new Tag(javaBean.getClass());
+ // flow style will be chosen by BaseRepresenter
+ MappingNode node = new MappingNode(tag, value, FlowStyle.AUTO);
+ representedObjects.put(javaBean, node);
+ DumperOptions.FlowStyle bestStyle = FlowStyle.FLOW;
+ for (Property property : properties) {
+ Object memberValue = property.get(javaBean);
+ Tag customPropertyTag = memberValue == null ? null : classTags.get(memberValue.getClass());
+ NodeTuple tuple =
+ representJavaBeanProperty(javaBean, property, memberValue, customPropertyTag);
+ if (tuple == null) {
+ continue;
+ }
+ if (!((ScalarNode) tuple.getKeyNode()).isPlain()) {
+ bestStyle = FlowStyle.BLOCK;
+ }
+ Node nodeValue = tuple.getValueNode();
+ if (!(nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).isPlain())) {
+ bestStyle = FlowStyle.BLOCK;
+ }
+ value.add(tuple);
}
+ if (defaultFlowStyle != FlowStyle.AUTO) {
+ node.setFlowStyle(defaultFlowStyle);
+ } else {
+ node.setFlowStyle(bestStyle);
+ }
+ return node;
+ }
+
+ /**
+ * Represent one JavaBean property.
+ *
+ * @param javaBean - the instance to be represented
+ * @param property - the property of the instance
+ * @param propertyValue - value to be represented
+ * @param customTag - user defined Tag
+ * @return NodeTuple to be used in a MappingNode. Return null to skip the property
+ */
+ protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+ Object propertyValue, Tag customTag) {
+ ScalarNode nodeKey = (ScalarNode) representData(property.getName());
+ // the first occurrence of the node must keep the tag
+ boolean hasAlias = this.representedObjects.containsKey(propertyValue);
- /**
- * Represent one JavaBean property.
- *
- * @param javaBean
- * - the instance to be represented
- * @param property
- * - the property of the instance
- * @param propertyValue
- * - value to be represented
- * @param customTag
- * - user defined Tag
- * @return NodeTuple to be used in a MappingNode. Return null to skip the
- * property
- */
- protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
- Object propertyValue, Tag customTag) {
- ScalarNode nodeKey = (ScalarNode) representData(property.getName());
- // the first occurrence of the node must keep the tag
- boolean hasAlias = this.representedObjects.containsKey(propertyValue);
-
- Node nodeValue = representData(propertyValue);
-
- if (propertyValue != null && !hasAlias) {
- NodeId nodeId = nodeValue.getNodeId();
- if (customTag == null) {
- if (nodeId == NodeId.scalar) {
- if (propertyValue instanceof Enum<?>) {
- nodeValue.setTag(Tag.STR);
- }
- } else {
- if (nodeId == NodeId.mapping) {
- if (property.getType() == propertyValue.getClass()) {
- if (!(propertyValue instanceof Map<?, ?>)) {
- if (!nodeValue.getTag().equals(Tag.SET)) {
- nodeValue.setTag(Tag.MAP);
- }
- }
- }
- }
- checkGlobalTag(property, nodeValue, propertyValue);
+ Node nodeValue = representData(propertyValue);
+
+ if (propertyValue != null && !hasAlias) {
+ NodeId nodeId = nodeValue.getNodeId();
+ if (customTag == null) {
+ if (nodeId == NodeId.scalar) {
+ // generic Enum requires the full tag
+ if (property.getType() != java.lang.Enum.class) {
+ if (propertyValue instanceof Enum<?>) {
+ nodeValue.setTag(Tag.STR);
+ }
+ }
+ } else {
+ if (nodeId == NodeId.mapping) {
+ if (property.getType() == propertyValue.getClass()) {
+ if (!(propertyValue instanceof Map<?, ?>)) {
+ if (!nodeValue.getTag().equals(Tag.SET)) {
+ nodeValue.setTag(Tag.MAP);
}
+ }
}
+ }
+ checkGlobalTag(property, nodeValue, propertyValue);
}
+ }
+ }
- return new NodeTuple(nodeKey, nodeValue);
+ return new NodeTuple(nodeKey, nodeValue);
+ }
+
+ /**
+ * Remove redundant global tag for a type safe (generic) collection if it is the same as defined
+ * by the JavaBean property
+ *
+ * @param property - JavaBean property
+ * @param node - representation of the property
+ * @param object - instance represented by the node
+ */
+ @SuppressWarnings("unchecked")
+ protected void checkGlobalTag(Property property, Node node, Object object) {
+ // Skip primitive arrays.
+ if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {
+ return;
}
- /**
- * Remove redundant global tag for a type safe (generic) collection if it is
- * the same as defined by the JavaBean property
- *
- * @param property
- * - JavaBean property
- * @param node
- * - representation of the property
- * @param object
- * - instance represented by the node
- */
- @SuppressWarnings("unchecked")
- protected void checkGlobalTag(Property property, Node node, Object object) {
- // Skip primitive arrays.
- if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {
- return;
+ Class<?>[] arguments = property.getActualTypeArguments();
+ if (arguments != null) {
+ if (node.getNodeId() == NodeId.sequence) {
+ // apply map tag where class is the same
+ Class<? extends Object> t = arguments[0];
+ SequenceNode snode = (SequenceNode) node;
+ Iterable<Object> memberList = Collections.EMPTY_LIST;
+ if (object.getClass().isArray()) {
+ memberList = Arrays.asList((Object[]) object);
+ } else if (object instanceof Iterable<?>) {
+ // list
+ memberList = (Iterable<Object>) object;
}
-
- Class<?>[] arguments = property.getActualTypeArguments();
- if (arguments != null) {
- if (node.getNodeId() == NodeId.sequence) {
- // apply map tag where class is the same
- Class<? extends Object> t = arguments[0];
- SequenceNode snode = (SequenceNode) node;
- Iterable<Object> memberList = Collections.EMPTY_LIST;
- if (object.getClass().isArray()) {
- memberList = Arrays.asList((Object[]) object);
- } else if (object instanceof Iterable<?>) {
- // list
- memberList = (Iterable<Object>) object;
+ Iterator<Object> iter = memberList.iterator();
+ if (iter.hasNext()) {
+ for (Node childNode : snode.getValue()) {
+ Object member = iter.next();
+ if (member != null) {
+ if (t.equals(member.getClass())) {
+ if (childNode.getNodeId() == NodeId.mapping) {
+ childNode.setTag(Tag.MAP);
}
- Iterator<Object> iter = memberList.iterator();
- if (iter.hasNext()) {
- for (Node childNode : snode.getValue()) {
- Object member = iter.next();
- if (member != null) {
- if (t.equals(member.getClass()))
- if (childNode.getNodeId() == NodeId.mapping) {
- childNode.setTag(Tag.MAP);
- }
- }
- }
- }
- } else if (object instanceof Set) {
- Class<?> t = arguments[0];
- MappingNode mnode = (MappingNode) node;
- Iterator<NodeTuple> iter = mnode.getValue().iterator();
- Set<?> set = (Set<?>) object;
- for (Object member : set) {
- NodeTuple tuple = iter.next();
- Node keyNode = tuple.getKeyNode();
- if (t.equals(member.getClass())) {
- if (keyNode.getNodeId() == NodeId.mapping) {
- keyNode.setTag(Tag.MAP);
- }
- }
- }
- } else if (object instanceof Map) {
- Class<?> keyType = arguments[0];
- Class<?> valueType = arguments[1];
- MappingNode mnode = (MappingNode) node;
- for (NodeTuple tuple : mnode.getValue()) {
- resetTag(keyType, tuple.getKeyNode());
- resetTag(valueType, tuple.getValueNode());
- }
- } else {
- // the type for collection entries cannot be
- // detected
+ }
}
+ }
}
- }
-
- private void resetTag(Class<? extends Object> type, Node node) {
- Tag tag = node.getTag();
- if (tag.matches(type)) {
- if (Enum.class.isAssignableFrom(type)) {
- node.setTag(Tag.STR);
- } else {
- node.setTag(Tag.MAP);
+ } else if (object instanceof Set) {
+ Class<?> t = arguments[0];
+ MappingNode mnode = (MappingNode) node;
+ Iterator<NodeTuple> iter = mnode.getValue().iterator();
+ Set<?> set = (Set<?>) object;
+ for (Object member : set) {
+ NodeTuple tuple = iter.next();
+ Node keyNode = tuple.getKeyNode();
+ if (t.equals(member.getClass())) {
+ if (keyNode.getNodeId() == NodeId.mapping) {
+ keyNode.setTag(Tag.MAP);
}
+ }
+ }
+ } else if (object instanceof Map) { // NodeId.mapping ends-up here
+ Class<?> keyType = arguments[0];
+ Class<?> valueType = arguments[1];
+ MappingNode mnode = (MappingNode) node;
+ for (NodeTuple tuple : mnode.getValue()) {
+ resetTag(keyType, tuple.getKeyNode());
+ resetTag(valueType, tuple.getValueNode());
}
+ } else {
+ // the type for collection entries cannot be
+ // detected
+ }
+ }
+ }
+
+ private void resetTag(Class<? extends Object> type, Node node) {
+ Tag tag = node.getTag();
+ if (tag.matches(type)) {
+ if (Enum.class.isAssignableFrom(type)) {
+ node.setTag(Tag.STR);
+ } else {
+ node.setTag(Tag.MAP);
+ }
}
+ }
- /**
- * Get JavaBean properties to be serialised. The order is respected. This
- * method may be overridden to provide custom property selection or order.
- *
- * @param type
- * - JavaBean to inspect the properties
- * @return properties to serialise
- */
- protected Set<Property> getProperties(Class<? extends Object> type)
- throws IntrospectionException {
- return getPropertyUtils().getProperties(type);
+ /**
+ * Get JavaBean properties to be serialised. The order is respected. This method may be overridden
+ * to provide custom property selection or order.
+ *
+ * @param type - JavaBean to inspect the properties
+ * @return properties to serialise
+ */
+ protected Set<Property> getProperties(Class<? extends Object> type) {
+ if (typeDefinitions.containsKey(type)) {
+ return typeDefinitions.get(type).getProperties();
}
+ return getPropertyUtils().getProperties(type);
+ }
}