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