1/**
2 * Copyright (c) 2008, 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 */
16package examples.jodatime;
17
18import java.util.Date;
19import java.util.List;
20
21import junit.framework.TestCase;
22
23import org.joda.time.DateTime;
24import org.joda.time.DateTimeZone;
25import org.yaml.snakeyaml.DumperOptions;
26import org.yaml.snakeyaml.DumperOptions.FlowStyle;
27import org.yaml.snakeyaml.Yaml;
28import org.yaml.snakeyaml.constructor.Construct;
29import org.yaml.snakeyaml.constructor.Constructor;
30import org.yaml.snakeyaml.events.Event;
31import org.yaml.snakeyaml.events.ScalarEvent;
32import org.yaml.snakeyaml.nodes.Node;
33import org.yaml.snakeyaml.nodes.NodeId;
34import org.yaml.snakeyaml.nodes.Tag;
35
36public class JodaTimeFlowStylesTest extends TestCase {
37    private static final long timestamp = 1000000000000L;
38
39    /**
40     * @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=128"></a>
41     */
42    public void testLoadBeanWithBlockFlow() {
43        MyBean bean = new MyBean();
44        bean.setId("id123");
45        DateTime etalon = new DateTime(timestamp, DateTimeZone.UTC);
46        bean.setDate(etalon);
47        DumperOptions options = new DumperOptions();
48        options.setDefaultFlowStyle(FlowStyle.BLOCK);
49        Yaml dumper = new Yaml(new JodaTimeRepresenter(), options);
50        // compare Nodes with flow style AUTO and flow style BLOCK
51        Node node1 = dumper.represent(bean);
52        DumperOptions options2 = new DumperOptions();
53        options2.setDefaultFlowStyle(FlowStyle.AUTO);
54        Yaml dumper2 = new Yaml(new JodaTimeRepresenter(), options2);
55        Node node2 = dumper2.represent(bean);
56        assertEquals(node2.toString(), node1.toString());
57        // compare Events with flow style AUTO and flow style BLOCK
58        List<Event> events1 = dumper.serialize(node1);
59        List<Event> events2 = dumper2.serialize(node2);
60        assertEquals(events2.size(), events1.size());
61        int i = 0;
62        for (Event etalonEvent : events2) {
63            assertEquals(etalonEvent, events1.get(i++));
64            if (etalonEvent instanceof ScalarEvent) {
65                ScalarEvent scalar = (ScalarEvent) etalonEvent;
66                if (scalar.getValue().equals("2001-09-09T01:46:40Z")) {
67                    assertTrue(scalar.getImplicit().canOmitTagInPlainScalar());
68                    assertFalse(scalar.getImplicit().canOmitTagInNonPlainScalar());
69                }
70            }
71        }
72        // Nodes and Events are the same. Only emitter may influence the output.
73        String doc1 = dumper.dump(bean);
74        // System.out.println(doc1);
75        /*
76         * 'date' must be used only with the explicit '!!timestamp' tag.
77         * Implicit tag will not work because 'date' is the JavaBean property
78         * and in this case the empty constructor of the class will be used.
79         * Since this constructor does not exist for JodaTime an exception will
80         * be thrown.
81         */
82        assertEquals("!!examples.jodatime.MyBean\ndate: 2001-09-09T01:46:40Z\nid: id123\n", doc1);
83        /*
84         * provided JodaTimeContructor will be ignored because 'date' is a
85         * JavaBean property and its class gets more priority then the implicit
86         * '!!timestamp' tag.
87         */
88        Yaml loader = new Yaml(new JodaTimeImplicitContructor());
89        try {
90            loader.load(doc1);
91        } catch (Exception e) {
92            assertTrue(
93                    "The error must indicate that JodaTime cannot be created from the scalar value.",
94                    e.getMessage()
95                            .contains(
96                                    "No String constructor found. Exception=org.joda.time.DateTime.<init>(java.lang.String)"));
97        }
98        // we have to provide a special way to create JodaTime instances from
99        // scalars
100        Yaml loader2 = new Yaml(new JodaPropertyConstructor());
101        MyBean parsed = (MyBean) loader2.load(doc1);
102        assertEquals(etalon, parsed.getDate());
103    }
104
105    /**
106     * !!timestamp must be used, without it the implicit tag will be ignored
107     * because 'date' is the JavaBean property.
108     *
109     * Since the timestamp contains ':' character it cannot use plain scalar
110     * style in the FLOW mapping style. Emitter suggests single quoted scalar
111     * style and that is why the explicit '!!timestamp' is present in the YAML
112     * document.
113     *
114     * @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=128"></a>
115     *
116     */
117    public void testLoadBeanWithAutoFlow() {
118        MyBean bean = new MyBean();
119        bean.setId("id123");
120        DateTime etalon = new DateTime(timestamp, DateTimeZone.UTC);
121        bean.setDate(etalon);
122        DumperOptions options = new DumperOptions();
123        options.setDefaultFlowStyle(FlowStyle.AUTO);
124        Yaml dumper = new Yaml(new JodaTimeRepresenter(), options);
125        String doc = dumper.dump(bean);
126        // System.out.println(doc);
127        assertEquals(
128                "!!examples.jodatime.MyBean {date: !!timestamp '2001-09-09T01:46:40Z', id: id123}\n",
129                doc);
130        Yaml loader = new Yaml(new JodaTimeImplicitContructor());
131        MyBean parsed = (MyBean) loader.load(doc);
132        assertEquals(etalon, parsed.getDate());
133    }
134
135    private class JodaPropertyConstructor extends Constructor {
136        public JodaPropertyConstructor() {
137            yamlClassConstructors.put(NodeId.scalar, new TimeStampConstruct());
138        }
139
140        class TimeStampConstruct extends Constructor.ConstructScalar {
141            @Override
142            public Object construct(Node nnode) {
143                if (nnode.getTag().equals(new Tag("tag:yaml.org,2002:timestamp"))) {
144                    Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
145                    Date date = (Date) dateConstructor.construct(nnode);
146                    return new DateTime(date, DateTimeZone.UTC);
147                } else {
148                    return super.construct(nnode);
149                }
150            }
151
152        }
153    }
154}
155