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  */
16 package examples.jodatime;
17 
18 import java.util.Date;
19 import java.util.List;
20 
21 import junit.framework.TestCase;
22 
23 import org.joda.time.DateTime;
24 import org.joda.time.DateTimeZone;
25 import org.yaml.snakeyaml.DumperOptions;
26 import org.yaml.snakeyaml.DumperOptions.FlowStyle;
27 import org.yaml.snakeyaml.Yaml;
28 import org.yaml.snakeyaml.constructor.Construct;
29 import org.yaml.snakeyaml.constructor.Constructor;
30 import org.yaml.snakeyaml.events.Event;
31 import org.yaml.snakeyaml.events.ScalarEvent;
32 import org.yaml.snakeyaml.nodes.Node;
33 import org.yaml.snakeyaml.nodes.NodeId;
34 import org.yaml.snakeyaml.nodes.Tag;
35 
36 public 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      */
testLoadBeanWithBlockFlow()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      */
testLoadBeanWithAutoFlow()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 {
JodaPropertyConstructor()136         public JodaPropertyConstructor() {
137             yamlClassConstructors.put(NodeId.scalar, new TimeStampConstruct());
138         }
139 
140         class TimeStampConstruct extends Constructor.ConstructScalar {
141             @Override
construct(Node nnode)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