View Javadoc

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 }