##### Coverage Report - org.yaml.snakeyaml.extensions.compactnotation.CompactConstructor

Classes in this File | Line Coverage | Branch Coverage | Complexity
CompactConstructor |
98% | 84/85
94% | 36/38
| 5.333333333333333;5.333
CompactConstructor$ConstructCompactObject |
100% | 14/14
100% | 2/2
| 5.333333333333333;5.333

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.extensions.compactnotation;
18 |  |
19 |  |
     import java.beans.IntrospectionException;
20 |  |
     import java.util.HashMap;
21 |  |
     import java.util.Iterator;
22 |  |
     import java.util.List;
23 |  |
     import java.util.Map;
24 |  |
     import java.util.Set;
25 |  |
     import java.util.regex.Matcher;
26 |  |
     import java.util.regex.Pattern;
27 |  |
28 |  |
     import org.yaml.snakeyaml.constructor.AbstractConstruct;
29 |  |
     import org.yaml.snakeyaml.constructor.Construct;
30 |  |
     import org.yaml.snakeyaml.constructor.Constructor;
31 |  |
     import org.yaml.snakeyaml.error.YAMLException;
32 |  |
     import org.yaml.snakeyaml.introspector.Property;
33 |  |
     import org.yaml.snakeyaml.nodes.MappingNode;
34 |  |
     import org.yaml.snakeyaml.nodes.Node;
35 |  |
     import org.yaml.snakeyaml.nodes.NodeTuple;
36 |  |
     import org.yaml.snakeyaml.nodes.ScalarNode;
37 |  |
38 |  |
     /**
39 |  |
      * Construct a custom Java instance out of a compact object notation format.
40 |  |
      */
41 | 53 |
     public class CompactConstructor extends Constructor {
42 | 1 |
         private static final Pattern FIRST_PATTERN = Pattern.compile("(\\p{Alpha}.*)(\\s*)\\((.*?)\\)");
43 | 1 |
         private static final Pattern PROPERTY_NAME_PATTERN = Pattern
44 |  |
                 .compile("\\s*(\\p{Alpha}\\w*)\\s*=(.+)");
45 |  |
46 |  |
         @Override
47 |  |
         protected Object constructScalar(ScalarNode node) {
48 | 98 |
             CompactData data = getCompactData(node.getValue());
49 | 98 |
             if (data != null) {
50 | 33 |
                 return constructCompactFormat(node, data);
51 |  |
             } else {
52 | 65 |
                 return super.constructScalar(node);
53 |  |
             }
54 |  |
         }
55 |  |
56 |  |
         protected Object constructCompactFormat(ScalarNode node, CompactData data) {
57 |  |
             try {
58 | 33 |
                 Object obj = createInstance(node, data);
59 | 31 |
                 Map<String, Object> properties = new HashMap<String, Object>(data.getProperties());
60 | 31 |
                 setProperties(obj, properties);
61 | 29 |
                 return obj;
62 | 4 |
             } catch (Exception e) {
63 | 4 |
                 throw new YAMLException(e);
64 |  |
             }
65 |  |
         }
66 |  |
67 |  |
         protected Object createInstance(ScalarNode node, CompactData data) throws Exception {
68 | 33 |
             Class<?> clazz = getClassForName(data.getPrefix());
69 | 32 |
             Class<?>[] args = new Class[data.getArguments().size()];
70 | 70 |
             for (int i = 0; i < args.length; i++) {
71 |  |
                 // assume all the arguments are Strings
72 | 38 |
                 args[i] = String.class;
73 |  |
             }
74 | 32 |
             java.lang.reflect.Constructor<?> c = clazz.getDeclaredConstructor(args);
75 | 31 |
             c.setAccessible(true);
76 | 31 |
             return c.newInstance(data.getArguments().toArray());
77 |  |
78 |  |
         }
79 |  |
80 |  |
         protected void setProperties(Object bean, Map<String, Object> data) throws Exception {
81 | 45 |
             if (data == null) {
82 | 0 |
                 throw new NullPointerException("Data for Compact Object Notation cannot be null.");
83 |  |
             }
84 | 45 |
             for (String key : data.keySet()) {
85 | 47 |
                 Property property = getPropertyUtils().getProperty(bean.getClass(), key);
86 |  |
                 try {
87 | 45 |
                     property.set(bean, data.get(key));
88 | 1 |
                 } catch (IllegalArgumentException e) {
89 | 1 |
                     throw new YAMLException("Cannot set property='" + key + "' with value='"
90 |  |
                             + data.get(key) + "' (" + data.get(key).getClass() + ") in " + bean);
91 | 44 |
                 }
92 | 44 |
             }
93 | 42 |
         }
94 |  |
95 |  |
         public CompactData getCompactData(String scalar) {
96 | 147 |
             if (!scalar.endsWith(")")) {
97 | 82 |
                 return null;
98 |  |
             }
99 | 65 |
             if (scalar.indexOf('(') < 0) {
100 | 1 |
                 return null;
101 |  |
             }
102 | 64 |
             Matcher m = FIRST_PATTERN.matcher(scalar);
103 | 64 |
             if (m.matches()) {
104 | 63 |
                 String tag = m.group(1).trim();
105 | 63 |
                 String content = m.group(3);
106 | 63 |
                 CompactData data = new CompactData(tag);
107 | 63 |
                 if (content.length() == 0)
108 | 1 |
                     return data;
109 | 62 |
                 String[] names = content.split("\\s*,\\s*");
110 | 171 |
                 for (int i = 0; i < names.length; i++) {
111 | 111 |
                     String section = names[i];
112 | 111 |
                     if (section.indexOf('=') < 0) {
113 | 69 |
                         data.getArguments().add(section);
114 |  |
                     } else {
115 | 42 |
                         Matcher sm = PROPERTY_NAME_PATTERN.matcher(section);
116 | 42 |
                         if (sm.matches()) {
117 | 40 |
                             String name = sm.group(1);
118 | 40 |
                             String value = sm.group(2).trim();
119 | 40 |
                             data.getProperties().put(name, value);
120 | 40 |
                         } else {
121 | 2 |
                             return null;
122 |  |
                         }
123 |  |
                     }
124 |  |
                 }
125 | 60 |
                 return data;
126 |  |
             }
127 | 1 |
             return null;
128 |  |
         }
129 |  |
130 |  |
         @Override
131 |  |
         protected Construct getConstructor(Node node) {
132 | 146 |
             if (node instanceof MappingNode) {
133 | 41 |
                 MappingNode mnode = (MappingNode) node;
134 | 41 |
                 List<NodeTuple> list = mnode.getValue();
135 | 41 |
                 if (list.size() == 1) {
136 | 31 |
                     NodeTuple tuple = list.get(0);
137 | 31 |
                     Node key = tuple.getKeyNode();
138 | 31 |
                     if (key instanceof ScalarNode) {
139 | 31 |
                         ScalarNode scalar = (ScalarNode) key;
140 | 31 |
                         CompactData data = getCompactData(scalar.getValue());
141 | 31 |
                         if (data != null) {
142 | 20 |
                             return new ConstructCompactObject();
143 |  |
                         }
144 |  |
                     }
145 |  |
                 }
146 |  |
             }
147 | 126 |
             return super.getConstructor(node);
148 |  |
         }
149 |  |
150 | 20 |
         public class ConstructCompactObject extends AbstractConstruct {
151 |  |
             @SuppressWarnings("unchecked")
152 |  |
             public Object construct(Node node) {
153 | 20 |
                 Map<Object, Object> map = constructMapping((MappingNode) node);
154 |  |
                 // Compact Object Notation may contain only one entry
155 | 19 |
                 Map.Entry<Object, Object> entry = map.entrySet().iterator().next();
156 | 19 |
                 Object result = entry.getKey();
157 | 19 |
                 Object value = entry.getValue();
158 | 19 |
                 if (value instanceof Map) {
159 | 14 |
                     Map<String, Object> properties = (Map<String, Object>) value;
160 |  |
                     try {
161 | 14 |
                         setProperties(result, properties);
162 | 1 |
                     } catch (Exception e) {
163 | 1 |
                         throw new YAMLException(e);
164 | 13 |
                     }
165 | 13 |
                 } else {
166 |  |
                     // value is a list
167 | 5 |
                     applySequence(result, (List<?>) value);
168 |  |
                 }
169 | 16 |
                 return result;
170 |  |
             }
171 |  |
         }
172 |  |
173 |  |
         protected void applySequence(Object bean, List<?> value) {
174 |  |
             try {
175 | 5 |
                 Property property = getPropertyUtils().getProperty(bean.getClass(),
176 |  |
                         getSequencePropertyName(bean.getClass()));
177 | 3 |
                 property.set(bean, value);
178 | 2 |
             } catch (Exception e) {
179 | 2 |
                 throw new YAMLException(e);
180 | 3 |
             }
181 | 3 |
         }
182 |  |
183 |  |
         /**
184 |  |
          * Provide the name of the property which is used when the entries form a
185 |  |
          * sequence. The property must be a List.
186 |  |
          *
187 |  |
          * @throws IntrospectionException
188 |  |
          */
189 |  |
         protected String getSequencePropertyName(Class<?> bean) throws IntrospectionException {
190 | 5 |
             Set<Property> properties = getPropertyUtils().getProperties(bean);
191 | 5 |
             for (Iterator<Property> iterator = properties.iterator(); iterator.hasNext();) {
192 | 8 |
                 Property property = iterator.next();
193 | 8 |
                 if (!List.class.isAssignableFrom(property.getType())) {
194 | 3 |
                     iterator.remove();
195 |  |
                 }
196 | 8 |
             }
197 | 5 |
             if (properties.size() == 0) {
198 | 1 |
                 throw new YAMLException("No list property found in " + bean);
199 | 4 |
             } else if (properties.size() > 1) {
200 | 1 |
                 throw new YAMLException(
201 |  |
                         "Many list properties found in "
202 |  |
                                 + bean
203 |  |
                                 + "; Please override getSequencePropertyName() to specify which property to use.");
204 |  |
             }
205 | 3 |
             return properties.iterator().next().getName();
206 |  |
         }
207 |  |
     }