Yaml.java revision 74f18c7830b06bedef2e4e20de6bd2b80928d089
1/**
2 * Copyright (c) 2008-2010, 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.InputStream;
20import java.io.Reader;
21import java.io.StringReader;
22import java.io.StringWriter;
23import java.io.Writer;
24import java.util.ArrayList;
25import java.util.Iterator;
26import java.util.List;
27import java.util.regex.Pattern;
28
29import org.yaml.snakeyaml.composer.Composer;
30import org.yaml.snakeyaml.constructor.BaseConstructor;
31import org.yaml.snakeyaml.constructor.Constructor;
32import org.yaml.snakeyaml.emitter.Emitter;
33import org.yaml.snakeyaml.error.YAMLException;
34import org.yaml.snakeyaml.events.Event;
35import org.yaml.snakeyaml.introspector.BeanAccess;
36import org.yaml.snakeyaml.nodes.Node;
37import org.yaml.snakeyaml.nodes.Tag;
38import org.yaml.snakeyaml.parser.Parser;
39import org.yaml.snakeyaml.parser.ParserImpl;
40import org.yaml.snakeyaml.reader.StreamReader;
41import org.yaml.snakeyaml.reader.UnicodeReader;
42import org.yaml.snakeyaml.representer.Representer;
43import org.yaml.snakeyaml.resolver.Resolver;
44import org.yaml.snakeyaml.serializer.Serializer;
45
46/**
47 * Public YAML interface. Each Thread must have its own instance.
48 */
49public class Yaml {
50    protected final Resolver resolver;
51    private String name;
52    protected BaseConstructor constructor;
53    protected Representer representer;
54    protected DumperOptions dumperOptions;
55    protected LoaderOptions loaderOptions;
56
57    /**
58     * Create Yaml instance. It is safe to create a few instances and use them
59     * in different Threads.
60     */
61    public Yaml() {
62        this(new Constructor(), new LoaderOptions(), new Representer(), new DumperOptions(),
63                new Resolver());
64    }
65
66    public Yaml(LoaderOptions loaderOptions) {
67        this(new Constructor(), loaderOptions, new Representer(), new DumperOptions(),
68                new Resolver());
69    }
70
71    /**
72     * Create Yaml instance.
73     *
74     * @param dumperOptions
75     *            DumperOptions to configure outgoing objects
76     */
77    public Yaml(DumperOptions dumperOptions) {
78        this(new Constructor(), new Representer(), dumperOptions);
79    }
80
81    /**
82     * Create Yaml instance. It is safe to create a few instances and use them
83     * in different Threads.
84     *
85     * @param representer
86     *            Representer to emit outgoing objects
87     */
88    public Yaml(Representer representer) {
89        this(new Constructor(), representer);
90    }
91
92    /**
93     * Create Yaml instance. It is safe to create a few instances and use them
94     * in different Threads.
95     *
96     * @param constructor
97     *            BaseConstructor to construct incoming documents
98     */
99    public Yaml(BaseConstructor constructor) {
100        this(constructor, new Representer());
101    }
102
103    /**
104     * Create Yaml instance. It is safe to create a few instances and use them
105     * in different Threads.
106     *
107     * @param constructor
108     *            BaseConstructor to construct incoming documents
109     * @param representer
110     *            Representer to emit outgoing objects
111     */
112    public Yaml(BaseConstructor constructor, Representer representer) {
113        this(constructor, representer, new DumperOptions());
114    }
115
116    /**
117     * Create Yaml instance. It is safe to create a few instances and use them
118     * in different Threads.
119     *
120     * @param representer
121     *            Representer to emit outgoing objects
122     * @param dumperOptions
123     *            DumperOptions to configure outgoing objects
124     */
125    public Yaml(Representer representer, DumperOptions dumperOptions) {
126        this(new Constructor(), representer, dumperOptions, new Resolver());
127    }
128
129    /**
130     * Create Yaml instance. It is safe to create a few instances and use them
131     * in different Threads.
132     *
133     * @param constructor
134     *            BaseConstructor to construct incoming documents
135     * @param representer
136     *            Representer to emit outgoing objects
137     * @param dumperOptions
138     *            DumperOptions to configure outgoing objects
139     */
140    public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) {
141        this(constructor, representer, dumperOptions, new Resolver());
142    }
143
144    /**
145     * Create Yaml instance. It is safe to create a few instances and use them
146     * in different Threads.
147     *
148     * @param constructor
149     *            BaseConstructor to construct incoming documents
150     * @param representer
151     *            Representer to emit outgoing objects
152     * @param dumperOptions
153     *            DumperOptions to configure outgoing objects
154     * @param resolver
155     *            Resolver to detect implicit type
156     */
157    public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions,
158            Resolver resolver) {
159        this(constructor, new LoaderOptions(), representer, dumperOptions, resolver);
160    }
161
162    /**
163     * Create Yaml instance. It is safe to create a few instances and use them
164     * in different Threads.
165     *
166     * @param constructor
167     *            BaseConstructor to construct incoming documents
168     * @param loaderOptions
169     *            LoaderOptions to control construction process
170     * @param representer
171     *            Representer to emit outgoing objects
172     * @param dumperOptions
173     *            DumperOptions to configure outgoing objects
174     * @param resolver
175     *            Resolver to detect implicit type
176     */
177    public Yaml(BaseConstructor constructor, LoaderOptions loaderOptions, Representer representer,
178            DumperOptions dumperOptions, Resolver resolver) {
179        if (!constructor.isExplicitPropertyUtils()) {
180            constructor.setPropertyUtils(representer.getPropertyUtils());
181        } else if (!representer.isExplicitPropertyUtils()) {
182            representer.setPropertyUtils(constructor.getPropertyUtils());
183        }
184        this.constructor = constructor;
185        this.loaderOptions = loaderOptions;
186        representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle());
187        representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle());
188        representer.getPropertyUtils().setAllowReadOnlyProperties(
189                dumperOptions.isAllowReadOnlyProperties());
190        this.representer = representer;
191        this.dumperOptions = dumperOptions;
192        this.resolver = resolver;
193        this.name = "Yaml:" + System.identityHashCode(this);
194    }
195
196    /**
197     * Serialize a Java object into a YAML String.
198     *
199     * @param data
200     *            Java object to be Serialized to YAML
201     * @return YAML String
202     */
203    public String dump(Object data) {
204        List<Object> list = new ArrayList<Object>(1);
205        list.add(data);
206        return dumpAll(list.iterator());
207    }
208
209    /**
210     * Serialize a sequence of Java objects into a YAML String.
211     *
212     * @param data
213     *            Iterator with Objects
214     * @return YAML String with all the objects in proper sequence
215     */
216    public String dumpAll(Iterator<? extends Object> data) {
217        StringWriter buffer = new StringWriter();
218        dumpAll(data, buffer);
219        return buffer.toString();
220    }
221
222    /**
223     * Serialize a Java object into a YAML stream.
224     *
225     * @param data
226     *            Java object to be Serialized to YAML
227     * @param output
228     *            stream to write to
229     */
230    public void dump(Object data, Writer output) {
231        List<Object> list = new ArrayList<Object>(1);
232        list.add(data);
233        dumpAll(list.iterator(), output);
234    }
235
236    /**
237     * Serialize a sequence of Java objects into a YAML stream.
238     *
239     * @param data
240     *            Iterator with Objects
241     * @param output
242     *            stream to write to
243     */
244    public void dumpAll(Iterator<? extends Object> data, Writer output) {
245        Serializer s = new Serializer(new Emitter(output, dumperOptions), resolver, dumperOptions);
246        try {
247            s.open();
248            while (data.hasNext()) {
249                representer.represent(s, data.next());
250            }
251            s.close();
252        } catch (java.io.IOException e) {
253            throw new YAMLException(e);
254        }
255    }
256
257    /**
258     * Parse the only YAML document in a String and produce the corresponding
259     * Java object. (Because the encoding in known BOM is not respected.)
260     *
261     * @param yaml
262     *            YAML data to load from (BOM must not be present)
263     * @return parsed object
264     */
265    public Object load(String yaml) {
266        return load(new StringReader(yaml));
267    }
268
269    /**
270     * Parse the only YAML document in a stream and produce the corresponding
271     * Java object.
272     *
273     * @param io
274     *            data to load from (BOM is respected and removed)
275     * @return parsed object
276     */
277    public Object load(InputStream io) {
278        return load(new UnicodeReader(io));
279    }
280
281    /**
282     * Parse the only YAML document in a stream and produce the corresponding
283     * Java object.
284     *
285     * @param io
286     *            data to load from (BOM must not be present)
287     * @return parsed object
288     */
289    public Object load(Reader io) {
290        Composer composer = new Composer(new ParserImpl(new StreamReader(io, loaderOptions
291                .isWithMarkContext())), resolver);
292        constructor.setComposer(composer);
293        return constructor.getSingleData();
294    }
295
296    /**
297     * Parse all YAML documents in a String and produce corresponding Java
298     * objects. The documents are parsed only when the iterator is invoked.
299     *
300     * @param yaml
301     *            YAML data to load from (BOM must not be present)
302     * @return an iterator over the parsed Java objects in this String in proper
303     *         sequence
304     */
305    public Iterable<Object> loadAll(Reader yaml) {
306        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml, loaderOptions
307                .isWithMarkContext())), resolver);
308        constructor.setComposer(composer);
309        Iterator<Object> result = new Iterator<Object>() {
310            public boolean hasNext() {
311                return constructor.checkData();
312            }
313
314            public Object next() {
315                return constructor.getData();
316            }
317
318            public void remove() {
319                throw new UnsupportedOperationException();
320            }
321        };
322        return new YamlIterable(result);
323    }
324
325    private class YamlIterable implements Iterable<Object> {
326        private Iterator<Object> iterator;
327
328        public YamlIterable(Iterator<Object> iterator) {
329            this.iterator = iterator;
330        }
331
332        public Iterator<Object> iterator() {
333            return iterator;
334        }
335
336    }
337
338    /**
339     * Parse all YAML documents in a String and produce corresponding Java
340     * objects. (Because the encoding in known BOM is not respected.) The
341     * documents are parsed only when the iterator is invoked.
342     *
343     * @param yaml
344     *            YAML data to load from (BOM must not be present)
345     * @return an iterator over the parsed Java objects in this String in proper
346     *         sequence
347     */
348    public Iterable<Object> loadAll(String yaml) {
349        return loadAll(new StringReader(yaml));
350    }
351
352    /**
353     * Parse all YAML documents in a stream and produce corresponding Java
354     * objects. The documents are parsed only when the iterator is invoked.
355     *
356     * @param yaml
357     *            YAML data to load from (BOM is respected and ignored)
358     * @return an iterator over the parsed Java objects in this stream in proper
359     *         sequence
360     */
361    public Iterable<Object> loadAll(InputStream yaml) {
362        return loadAll(new UnicodeReader(yaml));
363    }
364
365    /**
366     * Parse the first YAML document in a stream and produce the corresponding
367     * representation tree.
368     *
369     * @param yaml
370     *            YAML document
371     * @return parsed root Node for the specified YAML document
372     */
373    public Node compose(Reader yaml) {
374        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml, loaderOptions
375                .isWithMarkContext())), resolver);
376        constructor.setComposer(composer);
377        return composer.getSingleNode();
378    }
379
380    /**
381     * Parse all YAML documents in a stream and produce corresponding
382     * representation trees.
383     *
384     * @param yaml
385     *            stream of YAML documents
386     * @return parsed root Nodes for all the specified YAML documents
387     */
388    public Iterable<Node> composeAll(Reader yaml) {
389        final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml, loaderOptions
390                .isWithMarkContext())), resolver);
391        constructor.setComposer(composer);
392        Iterator<Node> result = new Iterator<Node>() {
393            public boolean hasNext() {
394                return composer.checkNode();
395            }
396
397            public Node next() {
398                return composer.getNode();
399            }
400
401            public void remove() {
402                throw new UnsupportedOperationException();
403            }
404        };
405        return new NodeIterable(result);
406    }
407
408    private class NodeIterable implements Iterable<Node> {
409        private Iterator<Node> iterator;
410
411        public NodeIterable(Iterator<Node> iterator) {
412            this.iterator = iterator;
413        }
414
415        public Iterator<Node> iterator() {
416            return iterator;
417        }
418    }
419
420    /**
421     * Add an implicit scalar detector. If an implicit scalar value matches the
422     * given regexp, the corresponding tag is assigned to the scalar.
423     *
424     * @deprecated use Tag instead of String
425     * @param tag
426     *            tag to assign to the node
427     * @param regexp
428     *            regular expression to match against
429     * @param first
430     *            a sequence of possible initial characters or null (which means
431     *            any).
432     *
433     */
434    public void addImplicitResolver(String tag, Pattern regexp, String first) {
435        addImplicitResolver(new Tag(tag), regexp, first);
436    }
437
438    /**
439     * Add an implicit scalar detector. If an implicit scalar value matches the
440     * given regexp, the corresponding tag is assigned to the scalar.
441     *
442     * @param tag
443     *            tag to assign to the node
444     * @param regexp
445     *            regular expression to match against
446     * @param first
447     *            a sequence of possible initial characters or null (which means
448     *            any).
449     */
450    public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
451        resolver.addImplicitResolver(tag, regexp, first);
452    }
453
454    @Override
455    public String toString() {
456        return name;
457    }
458
459    /**
460     * Get a meaningful name. It simplifies debugging in a multi-threaded
461     * environment. If nothing is set explicitly the address of the instance is
462     * returned.
463     *
464     * @return human readable name
465     */
466    public String getName() {
467        return name;
468    }
469
470    /**
471     * Set a meaningful name to be shown in toString()
472     *
473     * @param name
474     *            human readable name
475     */
476    public void setName(String name) {
477        this.name = name;
478    }
479
480    /**
481     * Parse a YAML stream and produce parsing events.
482     *
483     * @param yaml
484     *            YAML document(s)
485     * @return parsed events
486     */
487    public Iterable<Event> parse(Reader yaml) {
488        final Parser parser = new ParserImpl(new StreamReader(yaml, loaderOptions
489                .isWithMarkContext()));
490        Iterator<Event> result = new Iterator<Event>() {
491            public boolean hasNext() {
492                return parser.peekEvent() != null;
493            }
494
495            public Event next() {
496                return parser.getEvent();
497            }
498
499            public void remove() {
500                throw new UnsupportedOperationException();
501            }
502        };
503        return new EventIterable(result);
504    }
505
506    private class EventIterable implements Iterable<Event> {
507        private Iterator<Event> iterator;
508
509        public EventIterable(Iterator<Event> iterator) {
510            this.iterator = iterator;
511        }
512
513        public Iterator<Event> iterator() {
514            return iterator;
515        }
516    }
517
518    public void setBeanAccess(BeanAccess beanAccess) {
519        constructor.getPropertyUtils().setBeanAccess(beanAccess);
520        representer.getPropertyUtils().setBeanAccess(beanAccess);
521    }
522
523    // deprecated
524    /**
525     * @deprecated use with Constructor instead of Loader
526     */
527    public Yaml(Loader loader) {
528        this(loader, new Dumper(new DumperOptions()));
529    }
530
531    /**
532     * @deprecated use with Constructor instead of Loader
533     */
534    public Yaml(Loader loader, Dumper dumper) {
535        this(loader, dumper, new Resolver());
536    }
537
538    /**
539     * @deprecated use with Constructor instead of Loader
540     */
541    public Yaml(Loader loader, Dumper dumper, Resolver resolver) {
542        this(loader.constructor, dumper.representer, dumper.options, resolver);
543    }
544
545    /**
546     * Create Yaml instance. It is safe to create a few instances and use them
547     * in different Threads.
548     *
549     * @param dumper
550     *            Dumper to emit outgoing objects
551     */
552    @SuppressWarnings("deprecation")
553    public Yaml(Dumper dumper) {
554        this(new Constructor(), dumper.representer, dumper.options);
555    }
556}
557