View Javadoc

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 }