1 /** 2 * Copyright (c) 2008, SnakeYAML 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package org.yaml.snakeyaml.representer; 15 16 import java.util.ArrayList; 17 import java.util.HashMap; 18 import java.util.IdentityHashMap; 19 import java.util.LinkedHashMap; 20 import java.util.List; 21 import java.util.Map; 22 import org.yaml.snakeyaml.DumperOptions; 23 import org.yaml.snakeyaml.DumperOptions.FlowStyle; 24 import org.yaml.snakeyaml.DumperOptions.ScalarStyle; 25 import org.yaml.snakeyaml.introspector.PropertyUtils; 26 import org.yaml.snakeyaml.nodes.AnchorNode; 27 import org.yaml.snakeyaml.nodes.MappingNode; 28 import org.yaml.snakeyaml.nodes.Node; 29 import org.yaml.snakeyaml.nodes.NodeTuple; 30 import org.yaml.snakeyaml.nodes.ScalarNode; 31 import org.yaml.snakeyaml.nodes.SequenceNode; 32 import org.yaml.snakeyaml.nodes.Tag; 33 34 /** 35 * Represent basic YAML structures: scalar, sequence, mapping 36 */ 37 public abstract class BaseRepresenter { 38 39 protected final Map<Class<?>, Represent> representers = new HashMap<Class<?>, Represent>(); 40 /** 41 * in Java 'null' is not a type. So we have to keep the null representer separately otherwise it 42 * will coincide with the default representer which is stored with the key null. 43 */ 44 protected Represent nullRepresenter; 45 // the order is important (map can be also a sequence of key-values) 46 protected final Map<Class<?>, Represent> multiRepresenters = 47 new LinkedHashMap<Class<?>, Represent>(); 48 protected DumperOptions.ScalarStyle defaultScalarStyle = null; // not explicitly defined 49 protected FlowStyle defaultFlowStyle = FlowStyle.AUTO; 50 protected final Map<Object, Node> representedObjects = new IdentityHashMap<Object, Node>() { 51 private static final long serialVersionUID = -5576159264232131854L; 52 53 public Node put(Object key, Node value) { 54 return super.put(key, new AnchorNode(value)); 55 } 56 }; 57 58 protected Object objectToRepresent; 59 private PropertyUtils propertyUtils; 60 private boolean explicitPropertyUtils = false; 61 represent(Object data)62 public Node represent(Object data) { 63 Node node = representData(data); 64 representedObjects.clear(); 65 objectToRepresent = null; 66 return node; 67 } 68 representData(Object data)69 protected final Node representData(Object data) { 70 objectToRepresent = data; 71 // check for identity 72 if (representedObjects.containsKey(objectToRepresent)) { 73 Node node = representedObjects.get(objectToRepresent); 74 return node; 75 } 76 // } 77 // check for null first 78 if (data == null) { 79 Node node = nullRepresenter.representData(null); 80 return node; 81 } 82 // check the same class 83 Node node; 84 Class<?> clazz = data.getClass(); 85 if (representers.containsKey(clazz)) { 86 Represent representer = representers.get(clazz); 87 node = representer.representData(data); 88 } else { 89 // check the parents 90 for (Class<?> repr : multiRepresenters.keySet()) { 91 if (repr != null && repr.isInstance(data)) { 92 Represent representer = multiRepresenters.get(repr); 93 node = representer.representData(data); 94 return node; 95 } 96 } 97 98 // check defaults 99 if (multiRepresenters.containsKey(null)) { 100 Represent representer = multiRepresenters.get(null); 101 node = representer.representData(data); 102 } else { 103 Represent representer = representers.get(null); 104 node = representer.representData(data); 105 } 106 } 107 return node; 108 } 109 representScalar(Tag tag, String value, DumperOptions.ScalarStyle style)110 protected Node representScalar(Tag tag, String value, DumperOptions.ScalarStyle style) { 111 if (style == null) { 112 style = this.defaultScalarStyle; 113 } 114 Node node = new ScalarNode(tag, value, null, null, style); 115 return node; 116 } 117 representScalar(Tag tag, String value)118 protected Node representScalar(Tag tag, String value) { 119 return representScalar(tag, value, null); 120 } 121 representSequence(Tag tag, Iterable<?> sequence, DumperOptions.FlowStyle flowStyle)122 protected Node representSequence(Tag tag, Iterable<?> sequence, 123 DumperOptions.FlowStyle flowStyle) { 124 int size = 10;// default for ArrayList 125 if (sequence instanceof List<?>) { 126 size = ((List<?>) sequence).size(); 127 } 128 List<Node> value = new ArrayList<Node>(size); 129 SequenceNode node = new SequenceNode(tag, value, flowStyle); 130 representedObjects.put(objectToRepresent, node); 131 DumperOptions.FlowStyle bestStyle = FlowStyle.FLOW; 132 for (Object item : sequence) { 133 Node nodeItem = representData(item); 134 if (!(nodeItem instanceof ScalarNode && ((ScalarNode) nodeItem).isPlain())) { 135 bestStyle = FlowStyle.BLOCK; 136 } 137 value.add(nodeItem); 138 } 139 if (flowStyle == FlowStyle.AUTO) { 140 if (defaultFlowStyle != FlowStyle.AUTO) { 141 node.setFlowStyle(defaultFlowStyle); 142 } else { 143 node.setFlowStyle(bestStyle); 144 } 145 } 146 return node; 147 } 148 representMapping(Tag tag, Map<?, ?> mapping, DumperOptions.FlowStyle flowStyle)149 protected Node representMapping(Tag tag, Map<?, ?> mapping, DumperOptions.FlowStyle flowStyle) { 150 List<NodeTuple> value = new ArrayList<NodeTuple>(mapping.size()); 151 MappingNode node = new MappingNode(tag, value, flowStyle); 152 representedObjects.put(objectToRepresent, node); 153 DumperOptions.FlowStyle bestStyle = FlowStyle.FLOW; 154 for (Map.Entry<?, ?> entry : mapping.entrySet()) { 155 Node nodeKey = representData(entry.getKey()); 156 Node nodeValue = representData(entry.getValue()); 157 if (!(nodeKey instanceof ScalarNode && ((ScalarNode) nodeKey).isPlain())) { 158 bestStyle = FlowStyle.BLOCK; 159 } 160 if (!(nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).isPlain())) { 161 bestStyle = FlowStyle.BLOCK; 162 } 163 value.add(new NodeTuple(nodeKey, nodeValue)); 164 } 165 if (flowStyle == FlowStyle.AUTO) { 166 if (defaultFlowStyle != FlowStyle.AUTO) { 167 node.setFlowStyle(defaultFlowStyle); 168 } else { 169 node.setFlowStyle(bestStyle); 170 } 171 } 172 return node; 173 } 174 setDefaultScalarStyle(ScalarStyle defaultStyle)175 public void setDefaultScalarStyle(ScalarStyle defaultStyle) { 176 this.defaultScalarStyle = defaultStyle; 177 } 178 getDefaultScalarStyle()179 public ScalarStyle getDefaultScalarStyle() { 180 if (defaultScalarStyle == null) { 181 return ScalarStyle.PLAIN; 182 } 183 return defaultScalarStyle; 184 } 185 setDefaultFlowStyle(FlowStyle defaultFlowStyle)186 public void setDefaultFlowStyle(FlowStyle defaultFlowStyle) { 187 this.defaultFlowStyle = defaultFlowStyle; 188 } 189 getDefaultFlowStyle()190 public FlowStyle getDefaultFlowStyle() { 191 return this.defaultFlowStyle; 192 } 193 setPropertyUtils(PropertyUtils propertyUtils)194 public void setPropertyUtils(PropertyUtils propertyUtils) { 195 this.propertyUtils = propertyUtils; 196 this.explicitPropertyUtils = true; 197 } 198 getPropertyUtils()199 public final PropertyUtils getPropertyUtils() { 200 if (propertyUtils == null) { 201 propertyUtils = new PropertyUtils(); 202 } 203 return propertyUtils; 204 } 205 isExplicitPropertyUtils()206 public final boolean isExplicitPropertyUtils() { 207 return explicitPropertyUtils; 208 } 209 } 210