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