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