001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jvyamlb;
028:
029: import java.io.IOException;
030: import java.io.Serializable;
031:
032: import java.lang.reflect.Method;
033:
034: import java.text.DateFormat;
035: import java.text.SimpleDateFormat;
036:
037: import java.util.Iterator;
038: import java.util.Map;
039: import java.util.IdentityHashMap;
040: import java.util.HashMap;
041: import java.util.List;
042: import java.util.ArrayList;
043: import java.util.LinkedList;
044: import java.util.Set;
045: import java.util.Date;
046: import java.util.Calendar;
047:
048: import org.jvyamlb.nodes.Node;
049: import org.jvyamlb.nodes.LinkNode;
050: import org.jvyamlb.nodes.CollectionNode;
051: import org.jvyamlb.nodes.MappingNode;
052: import org.jvyamlb.nodes.ScalarNode;
053: import org.jvyamlb.nodes.SequenceNode;
054:
055: import org.jruby.RubyHash;
056: import org.jruby.util.ByteList;
057: import org.jruby.util.collections.IntHashMap;
058:
059: /**
060: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
061: */
062: public class RepresenterImpl implements Representer {
063: private final Serializer serializer;
064: private final char defaultStyle;
065: private final Map representedObjects;
066: private final Map links;
067:
068: public RepresenterImpl(final Serializer serializer,
069: final YAMLConfig opts) {
070: this .serializer = serializer;
071: this .defaultStyle = opts.useDouble() ? '"'
072: : (opts.useSingle() ? '\'' : 0);
073: this .representedObjects = new IdentityHashMap();
074: this .links = new IdentityHashMap();
075: }
076:
077: private Node representData(final Object data) throws IOException {
078: Node node = null;
079:
080: boolean ignoreAlias = ignoreAliases(data);
081:
082: if (!ignoreAlias) {
083: if (this .representedObjects.containsKey(data)) {
084: node = (Node) this .representedObjects.get(data);
085: if (null == node) {
086: node = new LinkNode();
087: List ll = (List) links.get(data);
088: if (ll == null) {
089: ll = new ArrayList();
090: links.put(data, ll);
091: }
092: ll.add(node);
093: }
094: return node;
095: }
096: this .representedObjects.put(data, null);
097: }
098:
099: node = getNodeCreatorFor(data).toYamlNode(this );
100:
101: if (!ignoreAlias) {
102: this .representedObjects.put(data, node);
103: List ll = (List) this .links.remove(data);
104: if (ll != null) {
105: for (Iterator iter = ll.iterator(); iter.hasNext();) {
106: ((LinkNode) iter.next()).setAnchor(node);
107: }
108: }
109: }
110: return node;
111: }
112:
113: public Node scalar(final String tag, final ByteList value,
114: char style) throws IOException {
115: return representScalar(tag, value, style);
116: }
117:
118: public Node representScalar(final String tag, final ByteList value,
119: char style) throws IOException {
120: char realStyle = style == 0 ? this .defaultStyle : style;
121: return new ScalarNode(tag, value, style);
122: }
123:
124: public Node seq(final String tag, final List sequence,
125: final boolean flowStyle) throws IOException {
126: return representSequence(tag, sequence, flowStyle);
127: }
128:
129: public Node representSequence(final String tag,
130: final List sequence, final boolean flowStyle)
131: throws IOException {
132: List value = new ArrayList(sequence.size());
133: for (final Iterator iter = sequence.iterator(); iter.hasNext();) {
134: value.add(representData(iter.next()));
135: }
136: return new SequenceNode(tag, value, flowStyle);
137: }
138:
139: public Node map(final String tag, final Map mapping,
140: final boolean flowStyle) throws IOException {
141: return representMapping(tag, mapping, flowStyle);
142: }
143:
144: public Node representMapping(final String tag, final Map mapping,
145: final boolean flowStyle) throws IOException {
146: Map value = new HashMap();
147: final Iterator iter = (mapping instanceof RubyHash) ? ((RubyHash) mapping)
148: .directEntrySet().iterator()
149: : mapping.entrySet().iterator();
150: while (iter.hasNext()) {
151: Map.Entry entry = (Map.Entry) iter.next();
152: value.put(representData(entry.getKey()),
153: representData(entry.getValue()));
154: }
155: return new MappingNode(tag, value, flowStyle);
156: }
157:
158: public void represent(final Object data) throws IOException {
159: Node node = representData(data);
160: this .serializer.serialize(node);
161: this .representedObjects.clear();
162: }
163:
164: protected boolean ignoreAliases(final Object data) {
165: return false;
166: }
167:
168: protected YAMLNodeCreator getNodeCreatorFor(final Object data) {
169: if (data instanceof YAMLNodeCreator) {
170: return (YAMLNodeCreator) data;
171: } else if (data instanceof Map) {
172: return new MappingYAMLNodeCreator(data);
173: } else if (data instanceof List) {
174: return new SequenceYAMLNodeCreator(data);
175: } else if (data instanceof Set) {
176: return new SetYAMLNodeCreator(data);
177: } else if (data instanceof Date) {
178: return new DateYAMLNodeCreator(data);
179: } else if (data instanceof String) {
180: return new StringYAMLNodeCreator(data);
181: } else if (data instanceof ByteList) {
182: return new ByteListYAMLNodeCreator(data);
183: } else if (data instanceof Number) {
184: return new NumberYAMLNodeCreator(data);
185: } else if (data instanceof Boolean) {
186: return new ScalarYAMLNodeCreator("tag:yaml.org,2002:bool",
187: data);
188: } else if (data == null) {
189: return new ScalarYAMLNodeCreator("tag:yaml.org,2002:null",
190: "");
191: } else if (data.getClass().isArray()) {
192: return new ArrayYAMLNodeCreator(data);
193: } else { // Fallback, handles JavaBeans and other
194: return new JavaBeanYAMLNodeCreator(data);
195: }
196: }
197:
198: public static class DateYAMLNodeCreator implements YAMLNodeCreator {
199: private final Date data;
200:
201: public DateYAMLNodeCreator(final Object data) {
202: this .data = (Date) data;
203: }
204:
205: public String taguri() {
206: return "tag:yaml.org,2002:timestamp";
207: }
208:
209: private static DateFormat dateOutput = new SimpleDateFormat(
210: "yyyy-MM-dd HH:mm:ss Z");
211: private static DateFormat dateOutputUsec = new SimpleDateFormat(
212: "yyyy-MM-dd HH:mm:ss.SSS Z");
213:
214: public Node toYamlNode(final Representer representer)
215: throws IOException {
216: final Calendar c = Calendar.getInstance();
217: c.setTime(data);
218: String out = null;
219: if (c.get(Calendar.MILLISECOND) != 0) {
220: out = dateOutputUsec.format(data);
221: } else {
222: out = dateOutput.format(data);
223: }
224: out = out.substring(0, 23) + ":" + out.substring(23);
225: return representer.scalar(taguri(), ByteList.create(out),
226: (char) 0);
227: }
228: }
229:
230: public static class SetYAMLNodeCreator implements YAMLNodeCreator {
231: private final Set data;
232:
233: public SetYAMLNodeCreator(final Object data) {
234: this .data = (Set) data;
235: }
236:
237: public String taguri() {
238: return "tag:yaml.org,2002:set";
239: }
240:
241: public Node toYamlNode(final Representer representer)
242: throws IOException {
243: final Map entries = new HashMap();
244: for (final Iterator iter = data.iterator(); iter.hasNext();) {
245: entries.put(iter.next(), null);
246: }
247: return representer.map(taguri(), entries, false);
248: }
249: }
250:
251: public static class ArrayYAMLNodeCreator implements YAMLNodeCreator {
252: private final Object data;
253:
254: public ArrayYAMLNodeCreator(final Object data) {
255: this .data = data;
256: }
257:
258: public String taguri() {
259: return "tag:yaml.org,2002:seq";
260: }
261:
262: public Node toYamlNode(final Representer representer)
263: throws IOException {
264: final int l = java.lang.reflect.Array.getLength(data);
265: final List lst = new ArrayList(l);
266: for (int i = 0; i < l; i++) {
267: lst.add(java.lang.reflect.Array.get(data, i));
268: }
269: return representer.seq(taguri(), lst, false);
270: }
271: }
272:
273: public static class NumberYAMLNodeCreator implements
274: YAMLNodeCreator {
275: private final Number data;
276:
277: public NumberYAMLNodeCreator(final Object data) {
278: this .data = (Number) data;
279: }
280:
281: public String taguri() {
282: if (data instanceof Float || data instanceof Double
283: || data instanceof java.math.BigDecimal) {
284: return "tag:yaml.org,2002:float";
285: } else {
286: return "tag:yaml.org,2002:int";
287: }
288: }
289:
290: public Node toYamlNode(Representer representer)
291: throws IOException {
292: String str = data.toString();
293: if (str.equals("Infinity")) {
294: str = ".inf";
295: } else if (str.equals("-Infinity")) {
296: str = "-.inf";
297: } else if (str.equals("NaN")) {
298: str = ".nan";
299: }
300: return representer.scalar(taguri(), ByteList.create(str),
301: (char) 0);
302: }
303: }
304:
305: public static class ScalarYAMLNodeCreator implements
306: YAMLNodeCreator {
307: private final String tag;
308: private final Object data;
309:
310: public ScalarYAMLNodeCreator(final String tag, final Object data) {
311: this .tag = tag;
312: this .data = data;
313: }
314:
315: public String taguri() {
316: return this .tag;
317: }
318:
319: public Node toYamlNode(Representer representer)
320: throws IOException {
321: return representer.scalar(taguri(), ByteList.create(data
322: .toString()), (char) 0);
323: }
324: }
325:
326: public static class StringYAMLNodeCreator implements
327: YAMLNodeCreator {
328: private final Object data;
329:
330: public StringYAMLNodeCreator(final Object data) {
331: this .data = data;
332: }
333:
334: public String taguri() {
335: if (data instanceof String) {
336: return "tag:yaml.org,2002:str";
337: } else {
338: return "tag:yaml.org,2002:str:"
339: + data.getClass().getName();
340: }
341: }
342:
343: public Node toYamlNode(Representer representer)
344: throws IOException {
345: return representer.scalar(taguri(), ByteList.create(data
346: .toString()), (char) 0);
347: }
348: }
349:
350: public static class ByteListYAMLNodeCreator implements
351: YAMLNodeCreator {
352: private final Object data;
353:
354: public ByteListYAMLNodeCreator(final Object data) {
355: this .data = data;
356: }
357:
358: public String taguri() {
359: return "tag:yaml.org,2002:str";
360: }
361:
362: public Node toYamlNode(Representer representer)
363: throws IOException {
364: return representer.scalar(taguri(), (ByteList) data,
365: (char) 0);
366: }
367: }
368:
369: public static class SequenceYAMLNodeCreator implements
370: YAMLNodeCreator {
371: private final List data;
372:
373: public SequenceYAMLNodeCreator(final Object data) {
374: this .data = (List) data;
375: }
376:
377: public String taguri() {
378: if (data instanceof ArrayList) {
379: return "tag:yaml.org,2002:seq";
380: } else {
381: return "tag:yaml.org,2002:seq:"
382: + data.getClass().getName();
383: }
384: }
385:
386: public Node toYamlNode(Representer representer)
387: throws IOException {
388: return representer.seq(taguri(), data, false);
389: }
390: }
391:
392: public static class MappingYAMLNodeCreator implements
393: YAMLNodeCreator {
394: private final Map data;
395:
396: public MappingYAMLNodeCreator(final Object data) {
397: this .data = (Map) data;
398: }
399:
400: public String taguri() {
401: if (data instanceof HashMap) {
402: return "tag:yaml.org,2002:map";
403: } else {
404: return "tag:yaml.org,2002:map:"
405: + data.getClass().getName();
406: }
407: }
408:
409: public Node toYamlNode(Representer representer)
410: throws IOException {
411: return representer.map(taguri(), data, false);
412: }
413: }
414:
415: public static class JavaBeanYAMLNodeCreator implements
416: YAMLNodeCreator {
417: private final Object data;
418:
419: public JavaBeanYAMLNodeCreator(final Object data) {
420: this .data = data;
421: }
422:
423: public String taguri() {
424: return "!java/object:" + data.getClass().getName();
425: }
426:
427: public Node toYamlNode(Representer representer)
428: throws IOException {
429: final Map values = new HashMap();
430: final Method[] ems = data.getClass().getMethods();
431: for (int i = 0, j = ems.length; i < j; i++) {
432: if (ems[i].getParameterTypes().length == 0) {
433: final String name = ems[i].getName();
434: if (name.equals("getClass")) {
435: continue;
436: }
437: String pname = null;
438: if (name.startsWith("get")) {
439: pname = ""
440: + Character.toLowerCase(name.charAt(3))
441: + name.substring(4);
442: } else if (name.startsWith("is")) {
443: pname = ""
444: + Character.toLowerCase(name.charAt(2))
445: + name.substring(3);
446: }
447: if (null != pname) {
448: try {
449: values
450: .put(pname, ems[i].invoke(data,
451: null));
452: } catch (final Exception exe) {
453: values.put(pname, null);
454: }
455: }
456: }
457: }
458: return representer.map(taguri(), values, false);
459: }
460: }
461:
462: public static void main(final String[] args) throws IOException {
463: final YAMLConfig cfg = YAML.config();
464: final Serializer s = new SerializerImpl(new EmitterImpl(
465: System.out, cfg), new ResolverImpl(), cfg);
466: s.open();
467: final Representer r = new RepresenterImpl(s, cfg);
468: final Map test1 = new HashMap();
469: final List test1Val = new LinkedList();
470: test1Val.add("hello");
471: test1Val.add(Boolean.TRUE);
472: test1Val.add(new Integer(31337));
473: test1.put("val1", test1Val);
474: final List test2Val = new ArrayList();
475: test2Val.add("hello");
476: test2Val.add(Boolean.FALSE);
477: test2Val.add(new Integer(31337));
478: test1.put("val2", test2Val);
479: test1.put("afsdf", "hmm");
480: TestJavaBean bean1 = new TestJavaBean();
481: bean1.setName("Ola");
482: bean1.setSurName("Bini");
483: bean1.setAge(24);
484: test1.put(new Integer(25), bean1);
485: r.represent(test1);
486: s.close();
487: }
488:
489: private static class TestJavaBean implements Serializable {
490: private String val1;
491: private String val2;
492: private int val3;
493:
494: public TestJavaBean() {
495: }
496:
497: public void setName(final String name) {
498: this .val1 = name;
499: }
500:
501: public String getName() {
502: return this .val1;
503: }
504:
505: public void setSurName(final String sname) {
506: this .val2 = sname;
507: }
508:
509: public String getSurName() {
510: return this .val2;
511: }
512:
513: public void setAge(final int age) {
514: this .val3 = age;
515: }
516:
517: public int getAge() {
518: return this .val3;
519: }
520: }
521: }// RepresenterImpl
|