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