1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.yaml.snakeyaml.representer;
17
18 import java.io.UnsupportedEncodingException;
19 import java.math.BigInteger;
20 import java.util.Arrays;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TimeZone;
30 import java.util.regex.Pattern;
31
32 import org.yaml.snakeyaml.error.YAMLException;
33 import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
34 import org.yaml.snakeyaml.nodes.Node;
35 import org.yaml.snakeyaml.nodes.Tag;
36 import org.yaml.snakeyaml.reader.StreamReader;
37
38
39
40
41 class SafeRepresenter extends BaseRepresenter {
42
43 protected Map<Class<? extends Object>, Tag> classTags;
44 protected TimeZone timeZone = null;
45
46 public SafeRepresenter() {
47 this.nullRepresenter = new RepresentNull();
48 this.representers.put(String.class, new RepresentString());
49 this.representers.put(Boolean.class, new RepresentBoolean());
50 this.representers.put(Character.class, new RepresentString());
51 this.representers.put(byte[].class, new RepresentByteArray());
52 this.multiRepresenters.put(Number.class, new RepresentNumber());
53 this.multiRepresenters.put(List.class, new RepresentList());
54 this.multiRepresenters.put(Map.class, new RepresentMap());
55 this.multiRepresenters.put(Set.class, new RepresentSet());
56 this.multiRepresenters.put(Iterator.class, new RepresentIterator());
57 this.multiRepresenters.put(new Object[0].getClass(), new RepresentArray());
58 this.multiRepresenters.put(Date.class, new RepresentDate());
59 this.multiRepresenters.put(Enum.class, new RepresentEnum());
60 this.multiRepresenters.put(Calendar.class, new RepresentDate());
61 classTags = new HashMap<Class<? extends Object>, Tag>();
62 }
63
64 protected Tag getTag(Class<?> clazz, Tag defaultTag) {
65 if (classTags.containsKey(clazz)) {
66 return classTags.get(clazz);
67 } else {
68 return defaultTag;
69 }
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83 public Tag addClassTag(Class<? extends Object> clazz, String tag) {
84 return addClassTag(clazz, new Tag(tag));
85 }
86
87
88
89
90
91
92
93
94
95
96
97 public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
98 if (tag == null) {
99 throw new NullPointerException("Tag must be provided.");
100 }
101 return classTags.put(clazz, tag);
102 }
103
104 protected class RepresentNull implements Represent {
105 public Node representData(Object data) {
106 return representScalar(Tag.NULL, "null");
107 }
108 }
109
110 public static Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");
111
112 protected class RepresentString implements Represent {
113 public Node representData(Object data) {
114 Tag tag = Tag.STR;
115 Character style = null;
116 String value = data.toString();
117 if (StreamReader.NON_PRINTABLE.matcher(value).find()) {
118 tag = Tag.BINARY;
119 char[] binary;
120 try {
121 binary = Base64Coder.encode(value.getBytes("UTF-8"));
122 } catch (UnsupportedEncodingException e) {
123 throw new YAMLException(e);
124 }
125 value = String.valueOf(binary);
126 style = '|';
127 }
128
129
130 if (defaultScalarStyle == null && MULTILINE_PATTERN.matcher(value).find()) {
131 style = '|';
132 }
133 return representScalar(tag, value, style);
134 }
135 }
136
137 protected class RepresentBoolean implements Represent {
138 public Node representData(Object data) {
139 String value;
140 if (Boolean.TRUE.equals(data)) {
141 value = "true";
142 } else {
143 value = "false";
144 }
145 return representScalar(Tag.BOOL, value);
146 }
147 }
148
149 protected class RepresentNumber implements Represent {
150 public Node representData(Object data) {
151 Tag tag;
152 String value;
153 if (data instanceof Byte || data instanceof Short || data instanceof Integer
154 || data instanceof Long || data instanceof BigInteger) {
155 tag = Tag.INT;
156 value = data.toString();
157 } else {
158 Number number = (Number) data;
159 tag = Tag.FLOAT;
160 if (number.equals(Double.NaN)) {
161 value = ".NaN";
162 } else if (number.equals(Double.POSITIVE_INFINITY)) {
163 value = ".inf";
164 } else if (number.equals(Double.NEGATIVE_INFINITY)) {
165 value = "-.inf";
166 } else {
167 value = number.toString();
168 }
169 }
170 return representScalar(getTag(data.getClass(), tag), value);
171 }
172 }
173
174 protected class RepresentList implements Represent {
175 @SuppressWarnings("unchecked")
176 public Node representData(Object data) {
177 return representSequence(getTag(data.getClass(), Tag.SEQ), (List<Object>) data, null);
178 }
179 }
180
181 protected class RepresentIterator implements Represent {
182 @SuppressWarnings("unchecked")
183 public Node representData(Object data) {
184 Iterator<Object> iter = (Iterator<Object>) data;
185 return representSequence(getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter),
186 null);
187 }
188 }
189
190 private static class IteratorWrapper implements Iterable<Object> {
191 private Iterator<Object> iter;
192
193 public IteratorWrapper(Iterator<Object> iter) {
194 this.iter = iter;
195 }
196
197 public Iterator<Object> iterator() {
198 return iter;
199 }
200 }
201
202 protected class RepresentArray implements Represent {
203 public Node representData(Object data) {
204 Object[] array = (Object[]) data;
205 List<Object> list = Arrays.asList(array);
206 return representSequence(Tag.SEQ, list, null);
207 }
208 }
209
210 protected class RepresentMap implements Represent {
211 @SuppressWarnings("unchecked")
212 public Node representData(Object data) {
213 return representMapping(getTag(data.getClass(), Tag.MAP), (Map<Object, Object>) data,
214 null);
215 }
216 }
217
218 protected class RepresentSet implements Represent {
219 @SuppressWarnings("unchecked")
220 public Node representData(Object data) {
221 Map<Object, Object> value = new LinkedHashMap<Object, Object>();
222 Set<Object> set = (Set<Object>) data;
223 for (Object key : set) {
224 value.put(key, null);
225 }
226 return representMapping(getTag(data.getClass(), Tag.SET), value, null);
227 }
228 }
229
230 protected class RepresentDate implements Represent {
231 public Node representData(Object data) {
232
233 Calendar calendar;
234 if (data instanceof Calendar) {
235 calendar = (Calendar) data;
236 } else {
237 calendar = Calendar.getInstance(getTimeZone() == null ? TimeZone.getTimeZone("UTC")
238 : timeZone);
239 calendar.setTime((Date) data);
240 }
241 int years = calendar.get(Calendar.YEAR);
242 int months = calendar.get(Calendar.MONTH) + 1;
243 int days = calendar.get(Calendar.DAY_OF_MONTH);
244 int hour24 = calendar.get(Calendar.HOUR_OF_DAY);
245 int minutes = calendar.get(Calendar.MINUTE);
246 int seconds = calendar.get(Calendar.SECOND);
247 int millis = calendar.get(Calendar.MILLISECOND);
248 StringBuilder buffer = new StringBuilder(String.valueOf(years));
249 while (buffer.length() < 4) {
250
251 buffer.insert(0, "0");
252 }
253 buffer.append("-");
254 if (months < 10) {
255 buffer.append("0");
256 }
257 buffer.append(String.valueOf(months));
258 buffer.append("-");
259 if (days < 10) {
260 buffer.append("0");
261 }
262 buffer.append(String.valueOf(days));
263 buffer.append("T");
264 if (hour24 < 10) {
265 buffer.append("0");
266 }
267 buffer.append(String.valueOf(hour24));
268 buffer.append(":");
269 if (minutes < 10) {
270 buffer.append("0");
271 }
272 buffer.append(String.valueOf(minutes));
273 buffer.append(":");
274 if (seconds < 10) {
275 buffer.append("0");
276 }
277 buffer.append(String.valueOf(seconds));
278 if (millis > 0) {
279 if (millis < 10) {
280 buffer.append(".00");
281 } else if (millis < 100) {
282 buffer.append(".0");
283 } else {
284 buffer.append(".");
285 }
286 buffer.append(String.valueOf(millis));
287 }
288 if (TimeZone.getTimeZone("UTC").equals(calendar.getTimeZone())) {
289 buffer.append("Z");
290 } else {
291
292 int gmtOffset = calendar.getTimeZone().getOffset(calendar.get(Calendar.ERA),
293 calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
294 calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.DAY_OF_WEEK),
295 calendar.get(Calendar.MILLISECOND));
296 int minutesOffset = gmtOffset / (60 * 1000);
297 int hoursOffset = minutesOffset / 60;
298 int partOfHour = minutesOffset % 60;
299 buffer.append((hoursOffset > 0 ? "+" : "") + hoursOffset + ":"
300 + (partOfHour < 10 ? "0" + partOfHour : partOfHour));
301 }
302 return representScalar(getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), null);
303 }
304 }
305
306 protected class RepresentEnum implements Represent {
307 public Node representData(Object data) {
308 Tag tag = new Tag(data.getClass());
309 return representScalar(getTag(data.getClass(), tag), ((Enum<?>) data).name());
310 }
311 }
312
313 protected class RepresentByteArray implements Represent {
314 public Node representData(Object data) {
315 char[] binary = Base64Coder.encode((byte[]) data);
316 return representScalar(Tag.BINARY, String.valueOf(binary), '|');
317 }
318 }
319
320 public TimeZone getTimeZone() {
321 return timeZone;
322 }
323
324 public void setTimeZone(TimeZone timeZone) {
325 this.timeZone = timeZone;
326 }
327 }