##### Coverage Report - org.yaml.snakeyaml.representer.Representer

Classes in this File | Line Coverage | Branch Coverage | Complexity
Representer |
100% | 85/85
100% | 64/64
| 6.0;6
Representer$RepresentJavaBean |
50% | 2/4
N/A | N/A
| 6.0;6

1 |  |
     /**
2 |  |
      * Copyright (c) 2008-2011, http://www.snakeyaml.org
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License");
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 |  |
      * See the License for the specific language governing permissions and
14 |  |
      * limitations under the License.
15 |  |
      */
16 |  |
17 |  |
     package org.yaml.snakeyaml.representer;
18 |  |
19 |  |
     import java.beans.IntrospectionException;
20 |  |
     import java.util.ArrayList;
21 |  |
     import java.util.Arrays;
22 |  |
     import java.util.Iterator;
23 |  |
     import java.util.List;
24 |  |
     import java.util.Map;
25 |  |
     import java.util.Set;
26 |  |
27 |  |
     import org.yaml.snakeyaml.DumperOptions.FlowStyle;
28 |  |
     import org.yaml.snakeyaml.error.YAMLException;
29 |  |
     import org.yaml.snakeyaml.introspector.Property;
30 |  |
     import org.yaml.snakeyaml.nodes.MappingNode;
31 |  |
     import org.yaml.snakeyaml.nodes.Node;
32 |  |
     import org.yaml.snakeyaml.nodes.NodeId;
33 |  |
     import org.yaml.snakeyaml.nodes.NodeTuple;
34 |  |
     import org.yaml.snakeyaml.nodes.ScalarNode;
35 |  |
     import org.yaml.snakeyaml.nodes.SequenceNode;
36 |  |
     import org.yaml.snakeyaml.nodes.Tag;
37 |  |
38 |  |
     /**
39 |  |
      * Represent JavaBeans
40 |  |
      */
41 |  |
     public class Representer extends SafeRepresenter {
42 |  |
43 | 3965 |
         public Representer() {
44 | 3965 |
             this.representers.put(null, new RepresentJavaBean());
45 | 3965 |
         }
46 |  |
47 | 3965 |
         protected class RepresentJavaBean implements Represent {
48 |  |
             public Node representData(Object data) {
49 |  |
                 try {
50 | 41382 |
                     return representJavaBean(getProperties(data.getClass()), data);
51 | 0 |
                 } catch (IntrospectionException e) {
52 | 0 |
                     throw new YAMLException(e);
53 |  |
                 }
54 |  |
             }
55 |  |
         }
56 |  |
57 |  |
         /**
58 |  |
          * Tag logic:<br/>
59 |  |
          * - explicit root tag is set in serializer <br/>
60 |  |
          * - if there is a predefined class tag it is used<br/>
61 |  |
          * - a global tag with class name is always used as tag. The JavaBean parent
62 |  |
          * of the specified JavaBean may set another tag (tag:yaml.org,2002:map)
63 |  |
          * when the property class is the same as runtime class
64 |  |
          *
65 |  |
          * @param properties
66 |  |
          *            JavaBean getters
67 |  |
          * @param javaBean
68 |  |
          *            instance for Node
69 |  |
          * @return Node to get serialized
70 |  |
          */
71 |  |
         protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
72 | 41378 |
             List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
73 |  |
             Tag tag;
74 | 41378 |
             Tag customTag = classTags.get(javaBean.getClass());
75 | 41378 |
             tag = customTag != null ? customTag : new Tag(javaBean.getClass());
76 |  |
             // flow style will be chosen by BaseRepresenter
77 | 41378 |
             MappingNode node = new MappingNode(tag, value, null);
78 | 41378 |
             representedObjects.put(javaBean, node);
79 | 41378 |
             boolean bestStyle = true;
80 | 41378 |
             for (Property property : properties) {
81 | 170048 |
                 Object memberValue = property.get(javaBean);
82 | 170047 |
                 Tag customPropertyTag = memberValue == null ? null : classTags.get(memberValue
83 |  |
                         .getClass());
84 | 170047 |
                 NodeTuple tuple = representJavaBeanProperty(javaBean, property, memberValue,
85 |  |
                         customPropertyTag);
86 | 170046 |
                 if (tuple == null) {
87 | 7 |
                     continue;
88 |  |
                 }
89 | 170039 |
                 if (((ScalarNode) tuple.getKeyNode()).getStyle() != null) {
90 | 39 |
                     bestStyle = false;
91 |  |
                 }
92 | 170039 |
                 Node nodeValue = tuple.getValueNode();
93 | 170039 |
                 if (!((nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).getStyle() == null))) {
94 | 44357 |
                     bestStyle = false;
95 |  |
                 }
96 | 170039 |
                 value.add(tuple);
97 | 170039 |
             }
98 | 41376 |
             if (defaultFlowStyle != FlowStyle.AUTO) {
99 | 25175 |
                 node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
100 |  |
             } else {
101 | 16201 |
                 node.setFlowStyle(bestStyle);
102 |  |
             }
103 | 41376 |
             return node;
104 |  |
         }
105 |  |
106 |  |
         /**
107 |  |
          * Represent one JavaBean property.
108 |  |
          *
109 |  |
          * @param javaBean
110 |  |
          *            - the instance to be represented
111 |  |
          * @param property
112 |  |
          *            - the property of the instance
113 |  |
          * @param propertyValue
114 |  |
          *            - value to be represented
115 |  |
          * @param customTag
116 |  |
          *            - user defined Tag
117 |  |
          * @return NodeTuple to be used in a MappingNode. Return null to skip the
118 |  |
          *         property
119 |  |
          */
120 |  |
         protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
121 |  |
                 Object propertyValue, Tag customTag) {
122 | 170044 |
             ScalarNode nodeKey = (ScalarNode) representData(property.getName());
123 |  |
             // the first occurrence of the node must keep the tag
124 | 170044 |
             boolean hasAlias = this.representedObjects.containsKey(propertyValue);
125 |  |
126 | 170044 |
             Node nodeValue = representData(propertyValue);
127 |  |
128 | 170043 |
             if (propertyValue != null && !hasAlias) {
129 | 163843 |
                 NodeId nodeId = nodeValue.getNodeId();
130 | 163843 |
                 if (customTag == null) {
131 | 163837 |
                     if (nodeId == NodeId.scalar) {
132 | 125612 |
                         if (propertyValue instanceof Enum<?>) {
133 | 1 |
                             nodeValue.setTag(Tag.STR);
134 |  |
                         }
135 |  |
                     } else {
136 | 38225 |
                         if (nodeId == NodeId.mapping) {
137 | 22114 |
                             if (property.getType() == propertyValue.getClass()) {
138 | 12050 |
                                 if (!(propertyValue instanceof Map<?, ?>)) {
139 | 12048 |
                                     if (!nodeValue.getTag().equals(Tag.SET)) {
140 | 12046 |
                                         nodeValue.setTag(Tag.MAP);
141 |  |
                                     }
142 |  |
                                 }
143 |  |
                             }
144 |  |
                         }
145 | 38225 |
                         checkGlobalTag(property, nodeValue, propertyValue);
146 |  |
                     }
147 |  |
                 }
148 |  |
             }
149 |  |
150 | 170043 |
             return new NodeTuple(nodeKey, nodeValue);
151 |  |
         }
152 |  |
153 |  |
         /**
154 |  |
          * Remove redundant global tag for a type safe (generic) collection if it is
155 |  |
          * the same as defined by the JavaBean property
156 |  |
          *
157 |  |
          * @param property
158 |  |
          *            - JavaBean property
159 |  |
          * @param node
160 |  |
          *            - representation of the property
161 |  |
          * @param object
162 |  |
          *            - instance represented by the node
163 |  |
          */
164 |  |
         @SuppressWarnings("unchecked")
165 |  |
         protected void checkGlobalTag(Property property, Node node, Object object) {
166 | 38225 |
             Class<?>[] arguments = property.getActualTypeArguments();
167 | 38225 |
             if (arguments != null) {
168 | 26167 |
                 if (node.getNodeId() == NodeId.sequence) {
169 |  |
                     // apply map tag where class is the same
170 | 16105 |
                     Class<? extends Object> t = arguments[0];
171 | 16105 |
                     SequenceNode snode = (SequenceNode) node;
172 |  |
                     Iterable<Object> memberList;
173 | 16105 |
                     if (object.getClass().isArray()) {
174 | 13 |
                         memberList = Arrays.asList((Object[]) object);
175 |  |
                     } else {
176 |  |
                         // list
177 | 16092 |
                         memberList = (Iterable<Object>) object;
178 |  |
                     }
179 | 16105 |
                     Iterator<Object> iter = memberList.iterator();
180 | 16105 |
                     for (Node childNode : snode.getValue()) {
181 | 112222 |
                         Object member = iter.next();
182 | 112222 |
                         if (member != null) {
183 | 112209 |
                             if (t.equals(member.getClass()))
184 | 112175 |
                                 if (childNode.getNodeId() == NodeId.mapping) {
185 | 12072 |
                                     childNode.setTag(Tag.MAP);
186 |  |
                                 }
187 |  |
                         }
188 | 112222 |
                     }
189 | 16105 |
                 } else if (object instanceof Set) {
190 | 27 |
                     Class<?> t = arguments[0];
191 | 27 |
                     MappingNode mnode = (MappingNode) node;
192 | 27 |
                     Iterator<NodeTuple> iter = mnode.getValue().iterator();
193 | 27 |
                     Set<?> set = (Set<?>) object;
194 | 27 |
                     for (Object member : set) {
195 | 34 |
                         NodeTuple tuple = iter.next();
196 | 34 |
                         Node keyNode = tuple.getKeyNode();
197 | 34 |
                         if (t.equals(member.getClass())) {
198 | 29 |
                             if (keyNode.getNodeId() == NodeId.mapping) {
199 | 9 |
                                 keyNode.setTag(Tag.MAP);
200 |  |
                             }
201 |  |
                         }
202 | 34 |
                     }
203 | 27 |
                 } else if (object instanceof Map) {
204 | 10033 |
                     Class<?> keyType = arguments[0];
205 | 10033 |
                     Class<?> valueType = arguments[1];
206 | 10033 |
                     MappingNode mnode = (MappingNode) node;
207 | 10033 |
                     for (NodeTuple tuple : mnode.getValue()) {
208 | 100058 |
                         resetTag(keyType, tuple.getKeyNode());
209 | 100058 |
                         resetTag(valueType, tuple.getValueNode());
210 |  |
                     }
211 |  |
                 } else {
212 |  |
                     // the type for collection entries cannot be
213 |  |
                     // detected
214 |  |
                 }
215 |  |
             }
216 | 38225 |
         }
217 |  |
218 |  |
         private void resetTag(Class<? extends Object> type, Node node) {
219 | 200116 |
             Tag tag = node.getTag();
220 | 200116 |
             if (tag.matches(type)) {
221 | 28 |
                 if (Enum.class.isAssignableFrom(type)) {
222 | 12 |
                     node.setTag(Tag.STR);
223 |  |
                 } else {
224 | 16 |
                     node.setTag(Tag.MAP);
225 |  |
                 }
226 |  |
             }
227 | 200116 |
         }
228 |  |
229 |  |
         /**
230 |  |
          * Get JavaBean properties to be serialised. The order is respected. This
231 |  |
          * method may be overridden to provide custom property selection or order.
232 |  |
          *
233 |  |
          * @param type
234 |  |
          *            - JavaBean to inspect the properties
235 |  |
          * @return properties to serialise
236 |  |
          */
237 |  |
         protected Set<Property> getProperties(Class<? extends Object> type)
238 |  |
                 throws IntrospectionException {
239 | 41382 |
             return getPropertyUtils().getProperties(type);
240 |  |
         }
241 |  |
     }