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-2012, 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  
 package org.yaml.snakeyaml.representer;
 17  
 
 18  
 import java.beans.IntrospectionException;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Arrays;
 21  
 import java.util.Iterator;
 22  
 import java.util.List;
 23  
 import java.util.Map;
 24  
 import java.util.Set;
 25  
 
 26  
 import org.yaml.snakeyaml.DumperOptions.FlowStyle;
 27  
 import org.yaml.snakeyaml.error.YAMLException;
 28  
 import org.yaml.snakeyaml.introspector.Property;
 29  
 import org.yaml.snakeyaml.nodes.MappingNode;
 30  
 import org.yaml.snakeyaml.nodes.Node;
 31  
 import org.yaml.snakeyaml.nodes.NodeId;
 32  
 import org.yaml.snakeyaml.nodes.NodeTuple;
 33  
 import org.yaml.snakeyaml.nodes.ScalarNode;
 34  
 import org.yaml.snakeyaml.nodes.SequenceNode;
 35  
 import org.yaml.snakeyaml.nodes.Tag;
 36  
 
 37  
 /**
 38  
  * Represent JavaBeans
 39  
  */
 40  
 public class Representer extends SafeRepresenter {
 41  
 
 42  132938
     public Representer() {
 43  132938
         this.representers.put(null, new RepresentJavaBean());
 44  132938
     }
 45  
 
 46  132938
     protected class RepresentJavaBean implements Represent {
 47  
         public Node representData(Object data) {
 48  
             try {
 49  45028
                 return representJavaBean(getProperties(data.getClass()), data);
 50  0
             } catch (IntrospectionException e) {
 51  0
                 throw new YAMLException(e);
 52  
             }
 53  
         }
 54  
     }
 55  
 
 56  
     /**
 57  
      * Tag logic:<br/>
 58  
      * - explicit root tag is set in serializer <br/>
 59  
      * - if there is a predefined class tag it is used<br/>
 60  
      * - a global tag with class name is always used as tag. The JavaBean parent
 61  
      * of the specified JavaBean may set another tag (tag:yaml.org,2002:map)
 62  
      * when the property class is the same as runtime class
 63  
      * 
 64  
      * @param properties
 65  
      *            JavaBean getters
 66  
      * @param javaBean
 67  
      *            instance for Node
 68  
      * @return Node to get serialized
 69  
      */
 70  
     protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
 71  45024
         List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
 72  
         Tag tag;
 73  45024
         Tag customTag = classTags.get(javaBean.getClass());
 74  45024
         tag = customTag != null ? customTag : new Tag(javaBean.getClass());
 75  
         // flow style will be chosen by BaseRepresenter
 76  45024
         MappingNode node = new MappingNode(tag, value, null);
 77  45024
         representedObjects.put(javaBean, node);
 78  45024
         boolean bestStyle = true;
 79  45024
         for (Property property : properties) {
 80  173707
             Object memberValue = property.get(javaBean);
 81  173706
             Tag customPropertyTag = memberValue == null ? null : classTags.get(memberValue
 82  
                     .getClass());
 83  173706
             NodeTuple tuple = representJavaBeanProperty(javaBean, property, memberValue,
 84  
                     customPropertyTag);
 85  170063
             if (tuple == null) {
 86  8
                 continue;
 87  
             }
 88  170055
             if (((ScalarNode) tuple.getKeyNode()).getStyle() != null) {
 89  39
                 bestStyle = false;
 90  
             }
 91  170055
             Node nodeValue = tuple.getValueNode();
 92  170055
             if (!((nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).getStyle() == null))) {
 93  50366
                 bestStyle = false;
 94  
             }
 95  170055
             value.add(tuple);
 96  170055
         }
 97  41380
         if (defaultFlowStyle != FlowStyle.AUTO) {
 98  25175
             node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
 99  
         } else {
 100  16205
             node.setFlowStyle(bestStyle);
 101  
         }
 102  41380
         return node;
 103  
     }
 104  
 
 105  
     /**
 106  
      * Represent one JavaBean property.
 107  
      * 
 108  
      * @param javaBean
 109  
      *            - the instance to be represented
 110  
      * @param property
 111  
      *            - the property of the instance
 112  
      * @param propertyValue
 113  
      *            - value to be represented
 114  
      * @param customTag
 115  
      *            - user defined Tag
 116  
      * @return NodeTuple to be used in a MappingNode. Return null to skip the
 117  
      *         property
 118  
      */
 119  
     protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
 120  
             Object propertyValue, Tag customTag) {
 121  173702
         ScalarNode nodeKey = (ScalarNode) representData(property.getName());
 122  
         // the first occurrence of the node must keep the tag
 123  173701
         boolean hasAlias = this.representedObjects.containsKey(propertyValue);
 124  
 
 125  173701
         Node nodeValue = representData(propertyValue);
 126  
 
 127  170059
         if (propertyValue != null && !hasAlias) {
 128  163859
             NodeId nodeId = nodeValue.getNodeId();
 129  163859
             if (customTag == null) {
 130  163853
                 if (nodeId == NodeId.scalar) {
 131  125624
                     if (propertyValue instanceof Enum<?>) {
 132  1
                         nodeValue.setTag(Tag.STR);
 133  
                     }
 134  
                 } else {
 135  38229
                     if (nodeId == NodeId.mapping) {
 136  22116
                         if (property.getType() == propertyValue.getClass()) {
 137  12050
                             if (!(propertyValue instanceof Map<?, ?>)) {
 138  12048
                                 if (!nodeValue.getTag().equals(Tag.SET)) {
 139  12046
                                     nodeValue.setTag(Tag.MAP);
 140  
                                 }
 141  
                             }
 142  
                         }
 143  
                     }
 144  38229
                     checkGlobalTag(property, nodeValue, propertyValue);
 145  
                 }
 146  
             }
 147  
         }
 148  
 
 149  170059
         return new NodeTuple(nodeKey, nodeValue);
 150  
     }
 151  
 
 152  
     /**
 153  
      * Remove redundant global tag for a type safe (generic) collection if it is
 154  
      * the same as defined by the JavaBean property
 155  
      * 
 156  
      * @param property
 157  
      *            - JavaBean property
 158  
      * @param node
 159  
      *            - representation of the property
 160  
      * @param object
 161  
      *            - instance represented by the node
 162  
      */
 163  
     @SuppressWarnings("unchecked")
 164  
     protected void checkGlobalTag(Property property, Node node, Object object) {
 165  38229
         Class<?>[] arguments = property.getActualTypeArguments();
 166  38229
         if (arguments != null) {
 167  26171
             if (node.getNodeId() == NodeId.sequence) {
 168  
                 // apply map tag where class is the same
 169  16107
                 Class<? extends Object> t = arguments[0];
 170  16107
                 SequenceNode snode = (SequenceNode) node;
 171  
                 Iterable<Object> memberList;
 172  16107
                 if (object.getClass().isArray()) {
 173  13
                     memberList = Arrays.asList((Object[]) object);
 174  
                 } else {
 175  
                     // list
 176  16094
                     memberList = (Iterable<Object>) object;
 177  
                 }
 178  16107
                 Iterator<Object> iter = memberList.iterator();
 179  16107
                 for (Node childNode : snode.getValue()) {
 180  112232
                     Object member = iter.next();
 181  112232
                     if (member != null) {
 182  112219
                         if (t.equals(member.getClass()))
 183  112185
                             if (childNode.getNodeId() == NodeId.mapping) {
 184  12072
                                 childNode.setTag(Tag.MAP);
 185  
                             }
 186  
                     }
 187  112232
                 }
 188  16107
             } else if (object instanceof Set) {
 189  27
                 Class<?> t = arguments[0];
 190  27
                 MappingNode mnode = (MappingNode) node;
 191  27
                 Iterator<NodeTuple> iter = mnode.getValue().iterator();
 192  27
                 Set<?> set = (Set<?>) object;
 193  27
                 for (Object member : set) {
 194  34
                     NodeTuple tuple = iter.next();
 195  34
                     Node keyNode = tuple.getKeyNode();
 196  34
                     if (t.equals(member.getClass())) {
 197  29
                         if (keyNode.getNodeId() == NodeId.mapping) {
 198  9
                             keyNode.setTag(Tag.MAP);
 199  
                         }
 200  
                     }
 201  34
                 }
 202  27
             } else if (object instanceof Map) {
 203  10035
                 Class<?> keyType = arguments[0];
 204  10035
                 Class<?> valueType = arguments[1];
 205  10035
                 MappingNode mnode = (MappingNode) node;
 206  10035
                 for (NodeTuple tuple : mnode.getValue()) {
 207  100062
                     resetTag(keyType, tuple.getKeyNode());
 208  100062
                     resetTag(valueType, tuple.getValueNode());
 209  
                 }
 210  
             } else {
 211  
                 // the type for collection entries cannot be
 212  
                 // detected
 213  
             }
 214  
         }
 215  38229
     }
 216  
 
 217  
     private void resetTag(Class<? extends Object> type, Node node) {
 218  200124
         Tag tag = node.getTag();
 219  200124
         if (tag.matches(type)) {
 220  28
             if (Enum.class.isAssignableFrom(type)) {
 221  12
                 node.setTag(Tag.STR);
 222  
             } else {
 223  16
                 node.setTag(Tag.MAP);
 224  
             }
 225  
         }
 226  200124
     }
 227  
 
 228  
     /**
 229  
      * Get JavaBean properties to be serialised. The order is respected. This
 230  
      * method may be overridden to provide custom property selection or order.
 231  
      * 
 232  
      * @param type
 233  
      *            - JavaBean to inspect the properties
 234  
      * @return properties to serialise
 235  
      */
 236  
     protected Set<Property> getProperties(Class<? extends Object> type)
 237  
             throws IntrospectionException {
 238  45028
         return getPropertyUtils().getProperties(type);
 239  
     }
 240  
 }