Yaml.java revision 8db67301d5eeaa81c187f0ad5e4c55fa6d59c1b3
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
17package org.yaml.snakeyaml;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.Reader;
22import java.io.StringReader;
23import java.io.StringWriter;
24import java.io.Writer;
25import java.util.ArrayList;
26import java.util.Iterator;
27import java.util.List;
28import java.util.regex.Pattern;
29
30import org.yaml.snakeyaml.DumperOptions.FlowStyle;
31import org.yaml.snakeyaml.composer.Composer;
32import org.yaml.snakeyaml.constructor.BaseConstructor;
33import org.yaml.snakeyaml.constructor.Constructor;
34import org.yaml.snakeyaml.emitter.Emitable;
35import org.yaml.snakeyaml.emitter.Emitter;
36import org.yaml.snakeyaml.error.YAMLException;
37import org.yaml.snakeyaml.events.Event;
38import org.yaml.snakeyaml.introspector.BeanAccess;
39import org.yaml.snakeyaml.nodes.Node;
40import org.yaml.snakeyaml.nodes.Tag;
41import org.yaml.snakeyaml.parser.Parser;
42import org.yaml.snakeyaml.parser.ParserImpl;
43import org.yaml.snakeyaml.reader.StreamReader;
44import org.yaml.snakeyaml.reader.UnicodeReader;
45import org.yaml.snakeyaml.representer.Representer;
46import org.yaml.snakeyaml.resolver.Resolver;
47import org.yaml.snakeyaml.serializer.Serializer;
48
49/**
50 * Public YAML interface. Each Thread must have its own instance.
51 */
52public 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)
310     * @param flowStyle
311     *            flow style for the whole document. See Chapter 10. Collection
312     *            Styles http://yaml.org/spec/1.1/#id930798
313     *
314     * @return YAML String
315     */
316    public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) {
317        FlowStyle oldStyle = representer.getDefaultFlowStyle();
318        representer.setDefaultFlowStyle(flowStyle);
319        List<Object> list = new ArrayList<Object>(1);
320        list.add(data);
321        StringWriter buffer = new StringWriter();
322        dumpAll(list.iterator(), buffer, rootTag);
323        representer.setDefaultFlowStyle(oldStyle);
324        return buffer.toString();
325    }
326
327    /**
328     * <p>
329     * Serialize a Java object into a YAML string. Override the default root tag
330     * with <code>Tag.MAP</code>.
331     * </p>
332     * <p>
333     * This method is similar to <code>Yaml.dump(data)</code> except that the
334     * root tag for the whole document is replaced with <code>Tag.MAP</code> tag
335     * (implicit !!map).
336     * </p>
337     * <p>
338     * Block Mapping is used as the collection style. See 10.2.2. Block Mappings
339     * (http://yaml.org/spec/1.1/#id934537)
340     * </p>
341     *
342     * @param data
343     *            Java object to be serialized to YAML
344     * @return YAML String
345     */
346    public String dumpAs(Object data) {
347        return dumpAs(data, Tag.MAP, FlowStyle.BLOCK);
348    }
349
350    /**
351     * Serialize the representation tree into Events.
352     *
353     * @see http://yaml.org/spec/1.1/#id859333
354     * @param data
355     *            representation tree
356     * @return Event list
357     */
358    public List<Event> serialize(Node data) {
359        SilentEmitter emitter = new SilentEmitter();
360        @SuppressWarnings("deprecation")
361        Serializer serializer = new Serializer(emitter, resolver, dumperOptions,
362                dumperOptions.getExplicitRoot());
363        try {
364            serializer.open();
365            serializer.serialize(data);
366            serializer.close();
367        } catch (java.io.IOException e) {
368            throw new YAMLException(e);
369        }
370        return emitter.getEvents();
371    }
372
373    private class SilentEmitter implements Emitable {
374        private List<Event> events = new ArrayList<Event>(100);
375
376        public List<Event> getEvents() {
377            return events;
378        }
379
380        public void emit(Event event) throws IOException {
381            events.add(event);
382        }
383    }
384
385    /**
386     * Parse the only YAML document in a String and produce the corresponding
387     * Java object. (Because the encoding in known BOM is not respected.)
388     *
389     * @param yaml
390     *            YAML data to load from (BOM must not be present)
391     * @return parsed object
392     */
393    public Object load(String yaml) {
394        return loadFromReader(new StreamReader(yaml), Object.class);
395    }
396
397    /**
398     * Parse the only YAML document in a stream and produce the corresponding
399     * Java object.
400     *
401     * @param io
402     *            data to load from (BOM is respected and removed)
403     * @return parsed object
404     */
405    public Object load(InputStream io) {
406        return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class);
407    }
408
409    /**
410     * Parse the only YAML document in a stream and produce the corresponding
411     * Java object.
412     *
413     * @param io
414     *            data to load from (BOM must not be present)
415     * @return parsed object
416     */
417    public Object load(Reader io) {
418        return loadFromReader(new StreamReader(io), Object.class);
419    }
420
421    /**
422     * Parse the only YAML document in a stream and produce the corresponding
423     * Java object.
424     *
425     * @param <T>
426     *            Class is defined by the second argument
427     * @param io
428     *            data to load from (BOM must not be present)
429     * @param type
430     *            Class of the object to be created
431     * @return parsed object
432     */
433    @SuppressWarnings("unchecked")
434    public <T> T loadAs(Reader io, Class<T> type) {
435        return (T) loadFromReader(new StreamReader(io), type);
436    }
437
438    /**
439     * Parse the only YAML document in a String and produce the corresponding
440     * Java object. (Because the encoding in known BOM is not respected.)
441     *
442     * @param <T>
443     *            Class is defined by the second argument
444     * @param yaml
445     *            YAML data to load from (BOM must not be present)
446     * @param type
447     *            Class of the object to be created
448     * @return parsed object
449     */
450    @SuppressWarnings("unchecked")
451    public <T> T loadAs(String yaml, Class<T> type) {
452        return (T) loadFromReader(new StreamReader(yaml), type);
453    }
454
455    /**
456     * Parse the only YAML document in a stream and produce the corresponding
457     * Java object.
458     *
459     * @param <T>
460     *            Class is defined by the second argument
461     * @param input
462     *            data to load from (BOM is respected and removed)
463     * @param type
464     *            Class of the object to be created
465     * @return parsed object
466     */
467    @SuppressWarnings("unchecked")
468    public <T> T loadAs(InputStream input, Class<T> type) {
469        return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type);
470    }
471
472    private Object loadFromReader(StreamReader sreader, Class<?> type) {
473        Composer composer = new Composer(new ParserImpl(sreader), resolver);
474        constructor.setComposer(composer);
475        return constructor.getSingleData(type);
476    }
477
478    /**
479     * Parse all YAML documents in a String and produce corresponding Java
480     * objects. The documents are parsed only when the iterator is invoked.
481     *
482     * @param yaml
483     *            YAML data to load from (BOM must not be present)
484     * @return an iterator over the parsed Java objects in this String in proper
485     *         sequence
486     */
487    public Iterable<Object> loadAll(Reader yaml) {
488        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
489        constructor.setComposer(composer);
490        Iterator<Object> result = new Iterator<Object>() {
491            public boolean hasNext() {
492                return constructor.checkData();
493            }
494
495            public Object next() {
496                return constructor.getData();
497            }
498
499            public void remove() {
500                throw new UnsupportedOperationException();
501            }
502        };
503        return new YamlIterable(result);
504    }
505
506    private class YamlIterable implements Iterable<Object> {
507        private Iterator<Object> iterator;
508
509        public YamlIterable(Iterator<Object> iterator) {
510            this.iterator = iterator;
511        }
512
513        public Iterator<Object> iterator() {
514            return iterator;
515        }
516
517    }
518
519    /**
520     * Parse all YAML documents in a String and produce corresponding Java
521     * objects. (Because the encoding in known BOM is not respected.) The
522     * documents are parsed only when the iterator is invoked.
523     *
524     * @param yaml
525     *            YAML data to load from (BOM must not be present)
526     * @return an iterator over the parsed Java objects in this String in proper
527     *         sequence
528     */
529    public Iterable<Object> loadAll(String yaml) {
530        return loadAll(new StringReader(yaml));
531    }
532
533    /**
534     * Parse all YAML documents in a stream and produce corresponding Java
535     * objects. The documents are parsed only when the iterator is invoked.
536     *
537     * @param yaml
538     *            YAML data to load from (BOM is respected and ignored)
539     * @return an iterator over the parsed Java objects in this stream in proper
540     *         sequence
541     */
542    public Iterable<Object> loadAll(InputStream yaml) {
543        return loadAll(new UnicodeReader(yaml));
544    }
545
546    /**
547     * Parse the first YAML document in a stream and produce the corresponding
548     * representation tree. (This is the opposite of the represent() method)
549     *
550     * @see http://yaml.org/spec/1.1/#id859333
551     * @param yaml
552     *            YAML document
553     * @return parsed root Node for the specified YAML document
554     */
555    public Node compose(Reader yaml) {
556        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
557        constructor.setComposer(composer);
558        return composer.getSingleNode();
559    }
560
561    /**
562     * Parse all YAML documents in a stream and produce corresponding
563     * representation trees.
564     *
565     * @see http://yaml.org/spec/1.1/#id859333
566     * @param yaml
567     *            stream of YAML documents
568     * @return parsed root Nodes for all the specified YAML documents
569     */
570    public Iterable<Node> composeAll(Reader yaml) {
571        final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
572        constructor.setComposer(composer);
573        Iterator<Node> result = new Iterator<Node>() {
574            public boolean hasNext() {
575                return composer.checkNode();
576            }
577
578            public Node next() {
579                return composer.getNode();
580            }
581
582            public void remove() {
583                throw new UnsupportedOperationException();
584            }
585        };
586        return new NodeIterable(result);
587    }
588
589    private class NodeIterable implements Iterable<Node> {
590        private Iterator<Node> iterator;
591
592        public NodeIterable(Iterator<Node> iterator) {
593            this.iterator = iterator;
594        }
595
596        public Iterator<Node> iterator() {
597            return iterator;
598        }
599    }
600
601    /**
602     * Add an implicit scalar detector. If an implicit scalar value matches the
603     * given regexp, the corresponding tag is assigned to the scalar.
604     *
605     * @deprecated use Tag instead of String
606     * @param tag
607     *            tag to assign to the node
608     * @param regexp
609     *            regular expression to match against
610     * @param first
611     *            a sequence of possible initial characters or null (which means
612     *            any).
613     *
614     */
615    public void addImplicitResolver(String tag, Pattern regexp, String first) {
616        addImplicitResolver(new Tag(tag), regexp, first);
617    }
618
619    /**
620     * Add an implicit scalar detector. If an implicit scalar value matches the
621     * given regexp, the corresponding tag is assigned to the scalar.
622     *
623     * @param tag
624     *            tag to assign to the node
625     * @param regexp
626     *            regular expression to match against
627     * @param first
628     *            a sequence of possible initial characters or null (which means
629     *            any).
630     */
631    public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
632        resolver.addImplicitResolver(tag, regexp, first);
633    }
634
635    @Override
636    public String toString() {
637        return name;
638    }
639
640    /**
641     * Get a meaningful name. It simplifies debugging in a multi-threaded
642     * environment. If nothing is set explicitly the address of the instance is
643     * returned.
644     *
645     * @return human readable name
646     */
647    public String getName() {
648        return name;
649    }
650
651    /**
652     * Set a meaningful name to be shown in toString()
653     *
654     * @param name
655     *            human readable name
656     */
657    public void setName(String name) {
658        this.name = name;
659    }
660
661    /**
662     * Parse a YAML stream and produce parsing events.
663     *
664     * @see http://yaml.org/spec/1.1/#id859333
665     * @param yaml
666     *            YAML document(s)
667     * @return parsed events
668     */
669    public Iterable<Event> parse(Reader yaml) {
670        final Parser parser = new ParserImpl(new StreamReader(yaml));
671        Iterator<Event> result = new Iterator<Event>() {
672            public boolean hasNext() {
673                return parser.peekEvent() != null;
674            }
675
676            public Event next() {
677                return parser.getEvent();
678            }
679
680            public void remove() {
681                throw new UnsupportedOperationException();
682            }
683        };
684        return new EventIterable(result);
685    }
686
687    private class EventIterable implements Iterable<Event> {
688        private Iterator<Event> iterator;
689
690        public EventIterable(Iterator<Event> iterator) {
691            this.iterator = iterator;
692        }
693
694        public Iterator<Event> iterator() {
695            return iterator;
696        }
697    }
698
699    public void setBeanAccess(BeanAccess beanAccess) {
700        constructor.getPropertyUtils().setBeanAccess(beanAccess);
701        representer.getPropertyUtils().setBeanAccess(beanAccess);
702    }
703
704    // deprecated
705    /**
706     * @deprecated use with Constructor instead of Loader
707     */
708    public Yaml(Loader loader) {
709        this(loader, new Dumper(new DumperOptions()));
710    }
711
712    /**
713     * @deprecated use with Constructor instead of Loader
714     */
715    public Yaml(Loader loader, Dumper dumper) {
716        this(loader, dumper, new Resolver());
717    }
718
719    /**
720     * @deprecated use with Constructor instead of Loader
721     */
722    public Yaml(Loader loader, Dumper dumper, Resolver resolver) {
723        this(loader.constructor, dumper.representer, dumper.options, resolver);
724    }
725
726    /**
727     * Create Yaml instance. It is safe to create a few instances and use them
728     * in different Threads.
729     *
730     * @param dumper
731     *            Dumper to emit outgoing objects
732     */
733    @SuppressWarnings("deprecation")
734    public Yaml(Dumper dumper) {
735        this(new Constructor(), dumper.representer, dumper.options);
736    }
737}
738