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