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