Coverage Report - org.yaml.snakeyaml.representer.Representer
 
Classes in this File Line Coverage Branch Coverage Complexity
Representer
100%
85/85
100%
64/64
6
Representer$RepresentJavaBean
50%
2/4
N/A
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  
 }