View Javadoc

1   /**
2    * Copyright (c) 2008-2012, 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 http://code.google.com/p/snakeyaml/issues/detail?id=128
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 http://code.google.com/p/snakeyaml/issues/detail?id=128
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("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 }