View Javadoc

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.representer;
18  
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.external.biz.base64Coder.Base64Coder;
33  import org.yaml.snakeyaml.nodes.Node;
34  import org.yaml.snakeyaml.nodes.Tag;
35  
36  /**
37   * Represent standard Java classes
38   */
39  class SafeRepresenter extends BaseRepresenter {
40  
41      protected Map<Class<? extends Object>, Tag> classTags;
42  
43      public SafeRepresenter() {
44          this.nullRepresenter = new RepresentNull();
45          this.representers.put(String.class, new RepresentString());
46          this.representers.put(Boolean.class, new RepresentBoolean());
47          this.representers.put(Character.class, new RepresentString());
48          this.representers.put(byte[].class, new RepresentByteArray());
49          this.multiRepresenters.put(Number.class, new RepresentNumber());
50          this.multiRepresenters.put(List.class, new RepresentList());
51          this.multiRepresenters.put(Map.class, new RepresentMap());
52          this.multiRepresenters.put(Set.class, new RepresentSet());
53          this.multiRepresenters.put(Iterator.class, new RepresentIterator());
54          this.multiRepresenters.put(new Object[0].getClass(), new RepresentArray());
55          this.multiRepresenters.put(Date.class, new RepresentDate());
56          this.multiRepresenters.put(Enum.class, new RepresentEnum());
57          this.multiRepresenters.put(Calendar.class, new RepresentDate());
58          classTags = new HashMap<Class<? extends Object>, Tag>();
59      }
60  
61      protected Tag getTag(Class<?> clazz, Tag defaultTag) {
62          if (classTags.containsKey(clazz)) {
63              return classTags.get(clazz);
64          } else {
65              return defaultTag;
66          }
67      }
68  
69      /**
70       * Define a tag for the <code>Class</code> to serialize
71       * 
72       * @deprecated use Tag instead of String
73       * @param clazz
74       *            <code>Class</code> which tag is changed
75       * @param tag
76       *            new tag to be used for every instance of the specified
77       *            <code>Class</code>
78       * @return the previous tag associated with the <code>Class</code>
79       */
80      public Tag addClassTag(Class<? extends Object> clazz, String tag) {
81          return addClassTag(clazz, new Tag(tag));
82      }
83  
84      /**
85       * Define a tag for the <code>Class</code> to serialize.
86       * 
87       * @param clazz
88       *            <code>Class</code> which tag is changed
89       * @param tag
90       *            new tag to be used for every instance of the specified
91       *            <code>Class</code>
92       * @return the previous tag associated with the <code>Class</code>
93       */
94      public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
95          if (tag == null) {
96              throw new NullPointerException("Tag must be provided.");
97          }
98          return classTags.put(clazz, tag);
99      }
100 
101     protected class RepresentNull implements Represent {
102         public Node representData(Object data) {
103             return representScalar(Tag.NULL, "null");
104         }
105     }
106 
107     public static Pattern BINARY_PATTERN = Pattern.compile("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]");
108 
109     protected class RepresentString implements Represent {
110         public Node representData(Object data) {
111             Tag tag = Tag.STR;
112             Character style = null;
113             String value = data.toString();
114             if (BINARY_PATTERN.matcher(value).find()) {
115                 tag = Tag.BINARY;
116                 char[] binary;
117                 binary = Base64Coder.encode(value.getBytes());
118                 value = String.valueOf(binary);
119                 style = '|';
120             }
121             return representScalar(tag, value, style);
122         }
123     }
124 
125     protected class RepresentBoolean implements Represent {
126         public Node representData(Object data) {
127             String value;
128             if (Boolean.TRUE.equals(data)) {
129                 value = "true";
130             } else {
131                 value = "false";
132             }
133             return representScalar(Tag.BOOL, value);
134         }
135     }
136 
137     protected class RepresentNumber implements Represent {
138         public Node representData(Object data) {
139             Tag tag;
140             String value;
141             if (data instanceof Byte || data instanceof Short || data instanceof Integer
142                     || data instanceof Long || data instanceof BigInteger) {
143                 tag = Tag.INT;
144                 value = data.toString();
145             } else {
146                 Number number = (Number) data;
147                 tag = Tag.FLOAT;
148                 if (number.equals(Double.NaN)) {
149                     value = ".NaN";
150                 } else if (number.equals(Double.POSITIVE_INFINITY)) {
151                     value = ".inf";
152                 } else if (number.equals(Double.NEGATIVE_INFINITY)) {
153                     value = "-.inf";
154                 } else {
155                     value = number.toString();
156                 }
157             }
158             return representScalar(getTag(data.getClass(), tag), value);
159         }
160     }
161 
162     protected class RepresentList implements Represent {
163         @SuppressWarnings("unchecked")
164         public Node representData(Object data) {
165             return representSequence(getTag(data.getClass(), Tag.SEQ), (List<Object>) data, null);
166         }
167     }
168 
169     protected class RepresentIterator implements Represent {
170         @SuppressWarnings("unchecked")
171         public Node representData(Object data) {
172             Iterator<Object> iter = (Iterator<Object>) data;
173             return representSequence(getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter),
174                     null);
175         }
176     }
177 
178     private class IteratorWrapper implements Iterable<Object> {
179         private Iterator<Object> iter;
180 
181         public IteratorWrapper(Iterator<Object> iter) {
182             this.iter = iter;
183         }
184 
185         public Iterator<Object> iterator() {
186             return iter;
187         }
188     }
189 
190     protected class RepresentArray implements Represent {
191         public Node representData(Object data) {
192             Object[] array = (Object[]) data;
193             List<Object> list = Arrays.asList(array);
194             return representSequence(Tag.SEQ, list, null);
195         }
196     }
197 
198     protected class RepresentMap implements Represent {
199         @SuppressWarnings("unchecked")
200         public Node representData(Object data) {
201             return representMapping(getTag(data.getClass(), Tag.MAP), (Map<Object, Object>) data,
202                     null);
203         }
204     }
205 
206     protected class RepresentSet implements Represent {
207         @SuppressWarnings("unchecked")
208         public Node representData(Object data) {
209             Map<Object, Object> value = new LinkedHashMap<Object, Object>();
210             Set<Object> set = (Set<Object>) data;
211             for (Object key : set) {
212                 value.put(key, null);
213             }
214             return representMapping(getTag(data.getClass(), Tag.SET), value, null);
215         }
216     }
217 
218     protected class RepresentDate implements Represent {
219         public Node representData(Object data) {
220             // because SimpleDateFormat ignores timezone we have to use Calendar
221             Calendar calendar;
222             if (data instanceof Calendar) {
223                 calendar = (Calendar) data;
224             } else {
225                 calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
226                 calendar.setTime((Date) data);
227             }
228             int years = calendar.get(Calendar.YEAR);
229             int months = calendar.get(Calendar.MONTH) + 1; // 0..12
230             int days = calendar.get(Calendar.DAY_OF_MONTH); // 1..31
231             int hour24 = calendar.get(Calendar.HOUR_OF_DAY); // 0..24
232             int minutes = calendar.get(Calendar.MINUTE); // 0..59
233             int seconds = calendar.get(Calendar.SECOND); // 0..59
234             int millis = calendar.get(Calendar.MILLISECOND);
235             StringBuilder buffer = new StringBuilder(String.valueOf(years));
236             while (buffer.length() < 4) {
237                 // ancient years
238                 buffer.insert(0, "0");
239             }
240             buffer.append("-");
241             if (months < 10) {
242                 buffer.append("0");
243             }
244             buffer.append(String.valueOf(months));
245             buffer.append("-");
246             if (days < 10) {
247                 buffer.append("0");
248             }
249             buffer.append(String.valueOf(days));
250             buffer.append("T");
251             if (hour24 < 10) {
252                 buffer.append("0");
253             }
254             buffer.append(String.valueOf(hour24));
255             buffer.append(":");
256             if (minutes < 10) {
257                 buffer.append("0");
258             }
259             buffer.append(String.valueOf(minutes));
260             buffer.append(":");
261             if (seconds < 10) {
262                 buffer.append("0");
263             }
264             buffer.append(String.valueOf(seconds));
265             if (millis > 0) {
266                 if (millis < 10) {
267                     buffer.append(".00");
268                 } else if (millis < 100) {
269                     buffer.append(".0");
270                 } else {
271                     buffer.append(".");
272                 }
273                 buffer.append(String.valueOf(millis));
274             }
275             if (TimeZone.getTimeZone("UTC").equals(calendar.getTimeZone())) {
276                 buffer.append("Z");
277             } else {
278                 // Get the Offset from GMT taking DST into account
279                 int gmtOffset = calendar.getTimeZone().getOffset(calendar.get(Calendar.ERA),
280                         calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
281                         calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.DAY_OF_WEEK),
282                         calendar.get(Calendar.MILLISECOND));
283                 int minutesOffset = gmtOffset / (60 * 1000);
284                 int hoursOffset = minutesOffset / 60;
285                 int partOfHour = minutesOffset % 60;
286                 buffer.append((hoursOffset > 0 ? "+" : "") + hoursOffset + ":"
287                         + (partOfHour < 10 ? "0" + partOfHour : partOfHour));
288             }
289             return representScalar(getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), null);
290         }
291     }
292 
293     protected class RepresentEnum implements Represent {
294         public Node representData(Object data) {
295             Tag tag = new Tag(data.getClass());
296             return representScalar(getTag(data.getClass(), tag), ((Enum<?>) data).name());
297         }
298     }
299 
300     protected class RepresentByteArray implements Represent {
301         public Node representData(Object data) {
302             char[] binary = Base64Coder.encode((byte[]) data);
303             return representScalar(Tag.BINARY, String.valueOf(binary), '|');
304         }
305     }
306 }