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 org.yaml.snakeyaml; 17 18 import java.io.IOException; 19 import java.io.InputStream; 20 import java.io.Reader; 21 import java.io.StringReader; 22 import java.io.StringWriter; 23 import java.io.Writer; 24 import java.util.ArrayList; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.regex.Pattern; 28 29 import org.yaml.snakeyaml.DumperOptions.FlowStyle; 30 import org.yaml.snakeyaml.composer.Composer; 31 import org.yaml.snakeyaml.constructor.BaseConstructor; 32 import org.yaml.snakeyaml.constructor.Constructor; 33 import org.yaml.snakeyaml.emitter.Emitable; 34 import org.yaml.snakeyaml.emitter.Emitter; 35 import org.yaml.snakeyaml.error.YAMLException; 36 import org.yaml.snakeyaml.events.Event; 37 import org.yaml.snakeyaml.introspector.BeanAccess; 38 import org.yaml.snakeyaml.nodes.Node; 39 import org.yaml.snakeyaml.nodes.Tag; 40 import org.yaml.snakeyaml.parser.Parser; 41 import org.yaml.snakeyaml.parser.ParserImpl; 42 import org.yaml.snakeyaml.reader.StreamReader; 43 import org.yaml.snakeyaml.reader.UnicodeReader; 44 import org.yaml.snakeyaml.representer.Representer; 45 import org.yaml.snakeyaml.resolver.Resolver; 46 import org.yaml.snakeyaml.serializer.Serializer; 47 48 /** 49 * Public YAML interface. Each Thread must have its own instance. 50 */ 51 public class Yaml { 52 protected final Resolver resolver; 53 private String name; 54 protected BaseConstructor constructor; 55 protected Representer representer; 56 protected DumperOptions dumperOptions; 57 58 /** 59 * Create Yaml instance. It is safe to create a few instances and use them 60 * in different Threads. 61 */ 62 public Yaml() { 63 this(new Constructor(), new Representer(), new DumperOptions(), new Resolver()); 64 } 65 66 /** 67 * Create Yaml instance. 68 * 69 * @param dumperOptions 70 * DumperOptions to configure outgoing objects 71 */ 72 public Yaml(DumperOptions dumperOptions) { 73 this(new Constructor(), new Representer(), dumperOptions); 74 } 75 76 /** 77 * Create Yaml instance. It is safe to create a few instances and use them 78 * in different Threads. 79 * 80 * @param representer 81 * Representer to emit outgoing objects 82 */ 83 public Yaml(Representer representer) { 84 this(new Constructor(), representer); 85 } 86 87 /** 88 * Create Yaml instance. It is safe to create a few instances and use them 89 * in different Threads. 90 * 91 * @param constructor 92 * BaseConstructor to construct incoming documents 93 */ 94 public Yaml(BaseConstructor constructor) { 95 this(constructor, new Representer()); 96 } 97 98 /** 99 * Create Yaml instance. It is safe to create a few instances and use them 100 * in different Threads. 101 * 102 * @param constructor 103 * BaseConstructor to construct incoming documents 104 * @param representer 105 * Representer to emit outgoing objects 106 */ 107 public Yaml(BaseConstructor constructor, Representer representer) { 108 this(constructor, representer, new DumperOptions()); 109 } 110 111 /** 112 * Create Yaml instance. It is safe to create a few instances and use them 113 * in different Threads. 114 * 115 * @param representer 116 * Representer to emit outgoing objects 117 * @param dumperOptions 118 * DumperOptions to configure outgoing objects 119 */ 120 public Yaml(Representer representer, DumperOptions dumperOptions) { 121 this(new Constructor(), representer, dumperOptions, new Resolver()); 122 } 123 124 /** 125 * Create Yaml instance. It is safe to create a few instances and use them 126 * in different Threads. 127 * 128 * @param constructor 129 * BaseConstructor to construct incoming documents 130 * @param representer 131 * Representer to emit outgoing objects 132 * @param dumperOptions 133 * DumperOptions to configure outgoing objects 134 */ 135 public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) { 136 this(constructor, representer, dumperOptions, new Resolver()); 137 } 138 139 /** 140 * Create Yaml instance. It is safe to create a few instances and use them 141 * in different Threads. 142 * 143 * @param constructor 144 * BaseConstructor to construct incoming documents 145 * @param representer 146 * Representer to emit outgoing objects 147 * @param dumperOptions 148 * DumperOptions to configure outgoing objects 149 * @param resolver 150 * Resolver to detect implicit type 151 */ 152 public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions, 153 Resolver resolver) { 154 if (!constructor.isExplicitPropertyUtils()) { 155 constructor.setPropertyUtils(representer.getPropertyUtils()); 156 } else if (!representer.isExplicitPropertyUtils()) { 157 representer.setPropertyUtils(constructor.getPropertyUtils()); 158 } 159 this.constructor = constructor; 160 representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle()); 161 representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle()); 162 representer.getPropertyUtils().setAllowReadOnlyProperties( 163 dumperOptions.isAllowReadOnlyProperties()); 164 representer.setTimeZone(dumperOptions.getTimeZone()); 165 this.representer = representer; 166 this.dumperOptions = dumperOptions; 167 this.resolver = resolver; 168 this.name = "Yaml:" + System.identityHashCode(this); 169 } 170 171 /** 172 * Serialize a Java object into a YAML String. 173 * 174 * @param data 175 * Java object to be Serialized to YAML 176 * @return YAML String 177 */ 178 public String dump(Object data) { 179 List<Object> list = new ArrayList<Object>(1); 180 list.add(data); 181 return dumpAll(list.iterator()); 182 } 183 184 /** 185 * Produce the corresponding representation tree for a given Object. 186 * 187 * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing 188 * Overview</a> 189 * @param data 190 * instance to build the representation tree for 191 * @return representation tree 192 */ 193 public Node represent(Object data) { 194 return representer.represent(data); 195 } 196 197 /** 198 * Serialize a sequence of Java objects into a YAML String. 199 * 200 * @param data 201 * Iterator with Objects 202 * @return YAML String with all the objects in proper sequence 203 */ 204 public String dumpAll(Iterator<? extends Object> data) { 205 StringWriter buffer = new StringWriter(); 206 dumpAll(data, buffer, null); 207 return buffer.toString(); 208 } 209 210 /** 211 * Serialize a Java object into a YAML stream. 212 * 213 * @param data 214 * Java object to be serialized to YAML 215 * @param output 216 * stream to write to 217 */ 218 public void dump(Object data, Writer output) { 219 List<Object> list = new ArrayList<Object>(1); 220 list.add(data); 221 dumpAll(list.iterator(), output, null); 222 } 223 224 /** 225 * Serialize a sequence of Java objects into a YAML stream. 226 * 227 * @param data 228 * Iterator with Objects 229 * @param output 230 * stream to write to 231 */ 232 public void dumpAll(Iterator<? extends Object> data, Writer output) { 233 dumpAll(data, output, null); 234 } 235 236 private void dumpAll(Iterator<? extends Object> data, Writer output, Tag rootTag) { 237 Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver, 238 dumperOptions, rootTag); 239 try { 240 serializer.open(); 241 while (data.hasNext()) { 242 Node node = representer.represent(data.next()); 243 serializer.serialize(node); 244 } 245 serializer.close(); 246 } catch (IOException e) { 247 throw new YAMLException(e); 248 } 249 } 250 251 /** 252 * <p> 253 * Serialize a Java object into a YAML string. Override the default root tag 254 * with <code>rootTag</code>. 255 * </p> 256 * 257 * <p> 258 * This method is similar to <code>Yaml.dump(data)</code> except that the 259 * root tag for the whole document is replaced with the given tag. This has 260 * two main uses. 261 * </p> 262 * 263 * <p> 264 * First, if the root tag is replaced with a standard YAML tag, such as 265 * <code>Tag.MAP</code>, then the object will be dumped as a map. The root 266 * tag will appear as <code>!!map</code>, or blank (implicit !!map). 267 * </p> 268 * 269 * <p> 270 * Second, if the root tag is replaced by a different custom tag, then the 271 * document appears to be a different type when loaded. For example, if an 272 * instance of MyClass is dumped with the tag !!YourClass, then it will be 273 * handled as an instance of YourClass when loaded. 274 * </p> 275 * 276 * @param data 277 * Java object to be serialized to YAML 278 * @param rootTag 279 * the tag for the whole YAML document. The tag should be Tag.MAP 280 * for a JavaBean to make the tag disappear (to use implicit tag 281 * !!map). If <code>null</code> is provided then the standard tag 282 * with the full class name is used. 283 * @param flowStyle 284 * flow style for the whole document. See Chapter 10. Collection 285 * Styles http://yaml.org/spec/1.1/#id930798. If 286 * <code>null</code> is provided then the flow style from 287 * DumperOptions is used. 288 * 289 * @return YAML String 290 */ 291 public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) { 292 FlowStyle oldStyle = representer.getDefaultFlowStyle(); 293 if (flowStyle != null) { 294 representer.setDefaultFlowStyle(flowStyle); 295 } 296 List<Object> list = new ArrayList<Object>(1); 297 list.add(data); 298 StringWriter buffer = new StringWriter(); 299 dumpAll(list.iterator(), buffer, rootTag); 300 representer.setDefaultFlowStyle(oldStyle); 301 return buffer.toString(); 302 } 303 304 /** 305 * <p> 306 * Serialize a Java object into a YAML string. Override the default root tag 307 * with <code>Tag.MAP</code>. 308 * </p> 309 * <p> 310 * This method is similar to <code>Yaml.dump(data)</code> except that the 311 * root tag for the whole document is replaced with <code>Tag.MAP</code> tag 312 * (implicit !!map). 313 * </p> 314 * <p> 315 * Block Mapping is used as the collection style. See 10.2.2. Block Mappings 316 * (http://yaml.org/spec/1.1/#id934537) 317 * </p> 318 * 319 * @param data 320 * Java object to be serialized to YAML 321 * @return YAML String 322 */ 323 public String dumpAsMap(Object data) { 324 return dumpAs(data, Tag.MAP, FlowStyle.BLOCK); 325 } 326 327 /** 328 * Serialize the representation tree into Events. 329 * 330 * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> 331 * @param data 332 * representation tree 333 * @return Event list 334 */ 335 public List<Event> serialize(Node data) { 336 SilentEmitter emitter = new SilentEmitter(); 337 Serializer serializer = new Serializer(emitter, resolver, dumperOptions, null); 338 try { 339 serializer.open(); 340 serializer.serialize(data); 341 serializer.close(); 342 } catch (IOException e) { 343 throw new YAMLException(e); 344 } 345 return emitter.getEvents(); 346 } 347 348 private static class SilentEmitter implements Emitable { 349 private List<Event> events = new ArrayList<Event>(100); 350 351 public List<Event> getEvents() { 352 return events; 353 } 354 355 public void emit(Event event) throws IOException { 356 events.add(event); 357 } 358 } 359 360 /** 361 * Parse the only YAML document in a String and produce the corresponding 362 * Java object. (Because the encoding in known BOM is not respected.) 363 * 364 * @param yaml 365 * YAML data to load from (BOM must not be present) 366 * @return parsed object 367 */ 368 public Object load(String yaml) { 369 return loadFromReader(new StreamReader(yaml), Object.class); 370 } 371 372 /** 373 * Parse the only YAML document in a stream and produce the corresponding 374 * Java object. 375 * 376 * @param io 377 * data to load from (BOM is respected and removed) 378 * @return parsed object 379 */ 380 public Object load(InputStream io) { 381 return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class); 382 } 383 384 /** 385 * Parse the only YAML document in a stream and produce the corresponding 386 * Java object. 387 * 388 * @param io 389 * data to load from (BOM must not be present) 390 * @return parsed object 391 */ 392 public Object load(Reader io) { 393 return loadFromReader(new StreamReader(io), Object.class); 394 } 395 396 /** 397 * Parse the only YAML document in a stream and produce the corresponding 398 * Java object. 399 * 400 * @param <T> 401 * Class is defined by the second argument 402 * @param io 403 * data to load from (BOM must not be present) 404 * @param type 405 * Class of the object to be created 406 * @return parsed object 407 */ 408 @SuppressWarnings("unchecked") 409 public <T> T loadAs(Reader io, Class<T> type) { 410 return (T) loadFromReader(new StreamReader(io), type); 411 } 412 413 /** 414 * Parse the only YAML document in a String and produce the corresponding 415 * Java object. (Because the encoding in known BOM is not respected.) 416 * 417 * @param <T> 418 * Class is defined by the second argument 419 * @param yaml 420 * YAML data to load from (BOM must not be present) 421 * @param type 422 * Class of the object to be created 423 * @return parsed object 424 */ 425 @SuppressWarnings("unchecked") 426 public <T> T loadAs(String yaml, Class<T> type) { 427 return (T) loadFromReader(new StreamReader(yaml), type); 428 } 429 430 /** 431 * Parse the only YAML document in a stream and produce the corresponding 432 * Java object. 433 * 434 * @param <T> 435 * Class is defined by the second argument 436 * @param input 437 * data to load from (BOM is respected and removed) 438 * @param type 439 * Class of the object to be created 440 * @return parsed object 441 */ 442 @SuppressWarnings("unchecked") 443 public <T> T loadAs(InputStream input, Class<T> type) { 444 return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type); 445 } 446 447 private Object loadFromReader(StreamReader sreader, Class<?> type) { 448 Composer composer = new Composer(new ParserImpl(sreader), resolver); 449 constructor.setComposer(composer); 450 return constructor.getSingleData(type); 451 } 452 453 /** 454 * Parse all YAML documents in a String and produce corresponding Java 455 * objects. The documents are parsed only when the iterator is invoked. 456 * 457 * @param yaml 458 * YAML data to load from (BOM must not be present) 459 * @return an iterator over the parsed Java objects in this String in proper 460 * sequence 461 */ 462 public Iterable<Object> loadAll(Reader yaml) { 463 Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 464 constructor.setComposer(composer); 465 Iterator<Object> result = new Iterator<Object>() { 466 public boolean hasNext() { 467 return constructor.checkData(); 468 } 469 470 public Object next() { 471 return constructor.getData(); 472 } 473 474 public void remove() { 475 throw new UnsupportedOperationException(); 476 } 477 }; 478 return new YamlIterable(result); 479 } 480 481 private static class YamlIterable implements Iterable<Object> { 482 private Iterator<Object> iterator; 483 484 public YamlIterable(Iterator<Object> iterator) { 485 this.iterator = iterator; 486 } 487 488 public Iterator<Object> iterator() { 489 return iterator; 490 } 491 } 492 493 /** 494 * Parse all YAML documents in a String and produce corresponding Java 495 * objects. (Because the encoding in known BOM is not respected.) The 496 * documents are parsed only when the iterator is invoked. 497 * 498 * @param yaml 499 * YAML data to load from (BOM must not be present) 500 * @return an iterator over the parsed Java objects in this String in proper 501 * sequence 502 */ 503 public Iterable<Object> loadAll(String yaml) { 504 return loadAll(new StringReader(yaml)); 505 } 506 507 /** 508 * Parse all YAML documents in a stream and produce corresponding Java 509 * objects. The documents are parsed only when the iterator is invoked. 510 * 511 * @param yaml 512 * YAML data to load from (BOM is respected and ignored) 513 * @return an iterator over the parsed Java objects in this stream in proper 514 * sequence 515 */ 516 public Iterable<Object> loadAll(InputStream yaml) { 517 return loadAll(new UnicodeReader(yaml)); 518 } 519 520 /** 521 * Parse the first YAML document in a stream and produce the corresponding 522 * representation tree. (This is the opposite of the represent() method) 523 * 524 * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing 525 * Overview</a> 526 * @param yaml 527 * YAML document 528 * @return parsed root Node for the specified YAML document 529 */ 530 public Node compose(Reader yaml) { 531 Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 532 constructor.setComposer(composer); 533 return composer.getSingleNode(); 534 } 535 536 /** 537 * Parse all YAML documents in a stream and produce corresponding 538 * representation trees. 539 * 540 * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> 541 * @param yaml 542 * stream of YAML documents 543 * @return parsed root Nodes for all the specified YAML documents 544 */ 545 public Iterable<Node> composeAll(Reader yaml) { 546 final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver); 547 constructor.setComposer(composer); 548 Iterator<Node> result = new Iterator<Node>() { 549 public boolean hasNext() { 550 return composer.checkNode(); 551 } 552 553 public Node next() { 554 return composer.getNode(); 555 } 556 557 public void remove() { 558 throw new UnsupportedOperationException(); 559 } 560 }; 561 return new NodeIterable(result); 562 } 563 564 private static class NodeIterable implements Iterable<Node> { 565 private Iterator<Node> iterator; 566 567 public NodeIterable(Iterator<Node> iterator) { 568 this.iterator = iterator; 569 } 570 571 public Iterator<Node> iterator() { 572 return iterator; 573 } 574 } 575 576 /** 577 * Add an implicit scalar detector. If an implicit scalar value matches the 578 * given regexp, the corresponding tag is assigned to the scalar. 579 * 580 * @param tag 581 * tag to assign to the node 582 * @param regexp 583 * regular expression to match against 584 * @param first 585 * a sequence of possible initial characters or null (which means 586 * any). 587 */ 588 public void addImplicitResolver(Tag tag, Pattern regexp, String first) { 589 resolver.addImplicitResolver(tag, regexp, first); 590 } 591 592 @Override 593 public String toString() { 594 return name; 595 } 596 597 /** 598 * Get a meaningful name. It simplifies debugging in a multi-threaded 599 * environment. If nothing is set explicitly the address of the instance is 600 * returned. 601 * 602 * @return human readable name 603 */ 604 public String getName() { 605 return name; 606 } 607 608 /** 609 * Set a meaningful name to be shown in toString() 610 * 611 * @param name 612 * human readable name 613 */ 614 public void setName(String name) { 615 this.name = name; 616 } 617 618 /** 619 * Parse a YAML stream and produce parsing events. 620 * 621 * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a> 622 * @param yaml 623 * YAML document(s) 624 * @return parsed events 625 */ 626 public Iterable<Event> parse(Reader yaml) { 627 final Parser parser = new ParserImpl(new StreamReader(yaml)); 628 Iterator<Event> result = new Iterator<Event>() { 629 public boolean hasNext() { 630 return parser.peekEvent() != null; 631 } 632 633 public Event next() { 634 return parser.getEvent(); 635 } 636 637 public void remove() { 638 throw new UnsupportedOperationException(); 639 } 640 }; 641 return new EventIterable(result); 642 } 643 644 private static class EventIterable implements Iterable<Event> { 645 private Iterator<Event> iterator; 646 647 public EventIterable(Iterator<Event> iterator) { 648 this.iterator = iterator; 649 } 650 651 public Iterator<Event> iterator() { 652 return iterator; 653 } 654 } 655 656 public void setBeanAccess(BeanAccess beanAccess) { 657 constructor.getPropertyUtils().setBeanAccess(beanAccess); 658 representer.getPropertyUtils().setBeanAccess(beanAccess); 659 } 660 661 } 662