Home | History | Annotate | Download | only in snakeyaml
      1 /**
      2  * Copyright (c) 2008, 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      * Create Yaml instance.
     68      *
     69      * @param dumperOptions
     70      *            DumperOptions to configure outgoing objects
     71      */
     72     public Yaml(DumperOptions dumperOptions) {
     73         this(new Constructor(), new Representer(), dumperOptions);
     74     }
     75 
     76     /**
     77      * Create Yaml instance. It is safe to create a few instances and use them
     78      * in different Threads.
     79      *
     80      * @param representer
     81      *            Representer to emit outgoing objects
     82      */
     83     public Yaml(Representer representer) {
     84         this(new Constructor(), representer);
     85     }
     86 
     87     /**
     88      * Create Yaml instance. It is safe to create a few instances and use them
     89      * in different Threads.
     90      *
     91      * @param constructor
     92      *            BaseConstructor to construct incoming documents
     93      */
     94     public Yaml(BaseConstructor constructor) {
     95         this(constructor, new Representer());
     96     }
     97 
     98     /**
     99      * Create Yaml instance. It is safe to create a few instances and use them
    100      * in different Threads.
    101      *
    102      * @param constructor
    103      *            BaseConstructor to construct incoming documents
    104      * @param representer
    105      *            Representer to emit outgoing objects
    106      */
    107     public Yaml(BaseConstructor constructor, Representer representer) {
    108         this(constructor, representer, new DumperOptions());
    109     }
    110 
    111     /**
    112      * Create Yaml instance. It is safe to create a few instances and use them
    113      * in different Threads.
    114      *
    115      * @param representer
    116      *            Representer to emit outgoing objects
    117      * @param dumperOptions
    118      *            DumperOptions to configure outgoing objects
    119      */
    120     public Yaml(Representer representer, DumperOptions dumperOptions) {
    121         this(new Constructor(), representer, dumperOptions, new Resolver());
    122     }
    123 
    124     /**
    125      * Create Yaml instance. It is safe to create a few instances and use them
    126      * in different Threads.
    127      *
    128      * @param constructor
    129      *            BaseConstructor to construct incoming documents
    130      * @param representer
    131      *            Representer to emit outgoing objects
    132      * @param dumperOptions
    133      *            DumperOptions to configure outgoing objects
    134      */
    135     public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) {
    136         this(constructor, representer, dumperOptions, new Resolver());
    137     }
    138 
    139     /**
    140      * Create Yaml instance. It is safe to create a few instances and use them
    141      * in different Threads.
    142      *
    143      * @param constructor
    144      *            BaseConstructor to construct incoming documents
    145      * @param representer
    146      *            Representer to emit outgoing objects
    147      * @param dumperOptions
    148      *            DumperOptions to configure outgoing objects
    149      * @param resolver
    150      *            Resolver to detect implicit type
    151      */
    152     public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions,
    153             Resolver resolver) {
    154         if (!constructor.isExplicitPropertyUtils()) {
    155             constructor.setPropertyUtils(representer.getPropertyUtils());
    156         } else if (!representer.isExplicitPropertyUtils()) {
    157             representer.setPropertyUtils(constructor.getPropertyUtils());
    158         }
    159         this.constructor = constructor;
    160         representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle());
    161         representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle());
    162         representer.getPropertyUtils().setAllowReadOnlyProperties(
    163                 dumperOptions.isAllowReadOnlyProperties());
    164         representer.setTimeZone(dumperOptions.getTimeZone());
    165         this.representer = representer;
    166         this.dumperOptions = dumperOptions;
    167         this.resolver = resolver;
    168         this.name = "Yaml:" + System.identityHashCode(this);
    169     }
    170 
    171     /**
    172      * Serialize a Java object into a YAML String.
    173      *
    174      * @param data
    175      *            Java object to be Serialized to YAML
    176      * @return YAML String
    177      */
    178     public String dump(Object data) {
    179         List<Object> list = new ArrayList<Object>(1);
    180         list.add(data);
    181         return dumpAll(list.iterator());
    182     }
    183 
    184     /**
    185      * Produce the corresponding representation tree for a given Object.
    186      *
    187      * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing
    188      *      Overview</a>
    189      * @param data
    190      *            instance to build the representation tree for
    191      * @return representation tree
    192      */
    193     public Node represent(Object data) {
    194         return representer.represent(data);
    195     }
    196 
    197     /**
    198      * Serialize a sequence of Java objects into a YAML String.
    199      *
    200      * @param data
    201      *            Iterator with Objects
    202      * @return YAML String with all the objects in proper sequence
    203      */
    204     public String dumpAll(Iterator<? extends Object> data) {
    205         StringWriter buffer = new StringWriter();
    206         dumpAll(data, buffer, null);
    207         return buffer.toString();
    208     }
    209 
    210     /**
    211      * Serialize a Java object into a YAML stream.
    212      *
    213      * @param data
    214      *            Java object to be serialized to YAML
    215      * @param output
    216      *            stream to write to
    217      */
    218     public void dump(Object data, Writer output) {
    219         List<Object> list = new ArrayList<Object>(1);
    220         list.add(data);
    221         dumpAll(list.iterator(), output, null);
    222     }
    223 
    224     /**
    225      * Serialize a sequence of Java objects into a YAML stream.
    226      *
    227      * @param data
    228      *            Iterator with Objects
    229      * @param output
    230      *            stream to write to
    231      */
    232     public void dumpAll(Iterator<? extends Object> data, Writer output) {
    233         dumpAll(data, output, null);
    234     }
    235 
    236     private void dumpAll(Iterator<? extends Object> data, Writer output, Tag rootTag) {
    237         Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver,
    238                 dumperOptions, rootTag);
    239         try {
    240             serializer.open();
    241             while (data.hasNext()) {
    242                 Node node = representer.represent(data.next());
    243                 serializer.serialize(node);
    244             }
    245             serializer.close();
    246         } catch (IOException e) {
    247             throw new YAMLException(e);
    248         }
    249     }
    250 
    251     /**
    252      * <p>
    253      * Serialize a Java object into a YAML string. Override the default root tag
    254      * with <code>rootTag</code>.
    255      * </p>
    256      *
    257      * <p>
    258      * This method is similar to <code>Yaml.dump(data)</code> except that the
    259      * root tag for the whole document is replaced with the given tag. This has
    260      * two main uses.
    261      * </p>
    262      *
    263      * <p>
    264      * First, if the root tag is replaced with a standard YAML tag, such as
    265      * <code>Tag.MAP</code>, then the object will be dumped as a map. The root
    266      * tag will appear as <code>!!map</code>, or blank (implicit !!map).
    267      * </p>
    268      *
    269      * <p>
    270      * Second, if the root tag is replaced by a different custom tag, then the
    271      * document appears to be a different type when loaded. For example, if an
    272      * instance of MyClass is dumped with the tag !!YourClass, then it will be
    273      * handled as an instance of YourClass when loaded.
    274      * </p>
    275      *
    276      * @param data
    277      *            Java object to be serialized to YAML
    278      * @param rootTag
    279      *            the tag for the whole YAML document. The tag should be Tag.MAP
    280      *            for a JavaBean to make the tag disappear (to use implicit tag
    281      *            !!map). If <code>null</code> is provided then the standard tag
    282      *            with the full class name is used.
    283      * @param flowStyle
    284      *            flow style for the whole document. See Chapter 10. Collection
    285      *            Styles http://yaml.org/spec/1.1/#id930798. If
    286      *            <code>null</code> is provided then the flow style from
    287      *            DumperOptions is used.
    288      *
    289      * @return YAML String
    290      */
    291     public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) {
    292         FlowStyle oldStyle = representer.getDefaultFlowStyle();
    293         if (flowStyle != null) {
    294             representer.setDefaultFlowStyle(flowStyle);
    295         }
    296         List<Object> list = new ArrayList<Object>(1);
    297         list.add(data);
    298         StringWriter buffer = new StringWriter();
    299         dumpAll(list.iterator(), buffer, rootTag);
    300         representer.setDefaultFlowStyle(oldStyle);
    301         return buffer.toString();
    302     }
    303 
    304     /**
    305      * <p>
    306      * Serialize a Java object into a YAML string. Override the default root tag
    307      * with <code>Tag.MAP</code>.
    308      * </p>
    309      * <p>
    310      * This method is similar to <code>Yaml.dump(data)</code> except that the
    311      * root tag for the whole document is replaced with <code>Tag.MAP</code> tag
    312      * (implicit !!map).
    313      * </p>
    314      * <p>
    315      * Block Mapping is used as the collection style. See 10.2.2. Block Mappings
    316      * (http://yaml.org/spec/1.1/#id934537)
    317      * </p>
    318      *
    319      * @param data
    320      *            Java object to be serialized to YAML
    321      * @return YAML String
    322      */
    323     public String dumpAsMap(Object data) {
    324         return dumpAs(data, Tag.MAP, FlowStyle.BLOCK);
    325     }
    326 
    327     /**
    328      * Serialize the representation tree into Events.
    329      *
    330      * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
    331      * @param data
    332      *            representation tree
    333      * @return Event list
    334      */
    335     public List<Event> serialize(Node data) {
    336         SilentEmitter emitter = new SilentEmitter();
    337         Serializer serializer = new Serializer(emitter, resolver, dumperOptions, null);
    338         try {
    339             serializer.open();
    340             serializer.serialize(data);
    341             serializer.close();
    342         } catch (IOException e) {
    343             throw new YAMLException(e);
    344         }
    345         return emitter.getEvents();
    346     }
    347 
    348     private static class SilentEmitter implements Emitable {
    349         private List<Event> events = new ArrayList<Event>(100);
    350 
    351         public List<Event> getEvents() {
    352             return events;
    353         }
    354 
    355         public void emit(Event event) throws IOException {
    356             events.add(event);
    357         }
    358     }
    359 
    360     /**
    361      * Parse the only YAML document in a String and produce the corresponding
    362      * Java object. (Because the encoding in known BOM is not respected.)
    363      *
    364      * @param yaml
    365      *            YAML data to load from (BOM must not be present)
    366      * @return parsed object
    367      */
    368     public Object load(String yaml) {
    369         return loadFromReader(new StreamReader(yaml), Object.class);
    370     }
    371 
    372     /**
    373      * Parse the only YAML document in a stream and produce the corresponding
    374      * Java object.
    375      *
    376      * @param io
    377      *            data to load from (BOM is respected and removed)
    378      * @return parsed object
    379      */
    380     public Object load(InputStream io) {
    381         return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class);
    382     }
    383 
    384     /**
    385      * Parse the only YAML document in a stream and produce the corresponding
    386      * Java object.
    387      *
    388      * @param io
    389      *            data to load from (BOM must not be present)
    390      * @return parsed object
    391      */
    392     public Object load(Reader io) {
    393         return loadFromReader(new StreamReader(io), Object.class);
    394     }
    395 
    396     /**
    397      * Parse the only YAML document in a stream and produce the corresponding
    398      * Java object.
    399      *
    400      * @param <T>
    401      *            Class is defined by the second argument
    402      * @param io
    403      *            data to load from (BOM must not be present)
    404      * @param type
    405      *            Class of the object to be created
    406      * @return parsed object
    407      */
    408     @SuppressWarnings("unchecked")
    409     public <T> T loadAs(Reader io, Class<T> type) {
    410         return (T) loadFromReader(new StreamReader(io), type);
    411     }
    412 
    413     /**
    414      * Parse the only YAML document in a String and produce the corresponding
    415      * Java object. (Because the encoding in known BOM is not respected.)
    416      *
    417      * @param <T>
    418      *            Class is defined by the second argument
    419      * @param yaml
    420      *            YAML data to load from (BOM must not be present)
    421      * @param type
    422      *            Class of the object to be created
    423      * @return parsed object
    424      */
    425     @SuppressWarnings("unchecked")
    426     public <T> T loadAs(String yaml, Class<T> type) {
    427         return (T) loadFromReader(new StreamReader(yaml), type);
    428     }
    429 
    430     /**
    431      * Parse the only YAML document in a stream and produce the corresponding
    432      * Java object.
    433      *
    434      * @param <T>
    435      *            Class is defined by the second argument
    436      * @param input
    437      *            data to load from (BOM is respected and removed)
    438      * @param type
    439      *            Class of the object to be created
    440      * @return parsed object
    441      */
    442     @SuppressWarnings("unchecked")
    443     public <T> T loadAs(InputStream input, Class<T> type) {
    444         return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type);
    445     }
    446 
    447     private Object loadFromReader(StreamReader sreader, Class<?> type) {
    448         Composer composer = new Composer(new ParserImpl(sreader), resolver);
    449         constructor.setComposer(composer);
    450         return constructor.getSingleData(type);
    451     }
    452 
    453     /**
    454      * Parse all YAML documents in a String and produce corresponding Java
    455      * objects. The documents are parsed only when the iterator is invoked.
    456      *
    457      * @param yaml
    458      *            YAML data to load from (BOM must not be present)
    459      * @return an iterator over the parsed Java objects in this String in proper
    460      *         sequence
    461      */
    462     public Iterable<Object> loadAll(Reader yaml) {
    463         Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
    464         constructor.setComposer(composer);
    465         Iterator<Object> result = new Iterator<Object>() {
    466             public boolean hasNext() {
    467                 return constructor.checkData();
    468             }
    469 
    470             public Object next() {
    471                 return constructor.getData();
    472             }
    473 
    474             public void remove() {
    475                 throw new UnsupportedOperationException();
    476             }
    477         };
    478         return new YamlIterable(result);
    479     }
    480 
    481     private static class YamlIterable implements Iterable<Object> {
    482         private Iterator<Object> iterator;
    483 
    484         public YamlIterable(Iterator<Object> iterator) {
    485             this.iterator = iterator;
    486         }
    487 
    488         public Iterator<Object> iterator() {
    489             return iterator;
    490         }
    491     }
    492 
    493     /**
    494      * Parse all YAML documents in a String and produce corresponding Java
    495      * objects. (Because the encoding in known BOM is not respected.) The
    496      * documents are parsed only when the iterator is invoked.
    497      *
    498      * @param yaml
    499      *            YAML data to load from (BOM must not be present)
    500      * @return an iterator over the parsed Java objects in this String in proper
    501      *         sequence
    502      */
    503     public Iterable<Object> loadAll(String yaml) {
    504         return loadAll(new StringReader(yaml));
    505     }
    506 
    507     /**
    508      * Parse all YAML documents in a stream and produce corresponding Java
    509      * objects. The documents are parsed only when the iterator is invoked.
    510      *
    511      * @param yaml
    512      *            YAML data to load from (BOM is respected and ignored)
    513      * @return an iterator over the parsed Java objects in this stream in proper
    514      *         sequence
    515      */
    516     public Iterable<Object> loadAll(InputStream yaml) {
    517         return loadAll(new UnicodeReader(yaml));
    518     }
    519 
    520     /**
    521      * Parse the first YAML document in a stream and produce the corresponding
    522      * representation tree. (This is the opposite of the represent() method)
    523      *
    524      * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing
    525      *      Overview</a>
    526      * @param yaml
    527      *            YAML document
    528      * @return parsed root Node for the specified YAML document
    529      */
    530     public Node compose(Reader yaml) {
    531         Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
    532         constructor.setComposer(composer);
    533         return composer.getSingleNode();
    534     }
    535 
    536     /**
    537      * Parse all YAML documents in a stream and produce corresponding
    538      * representation trees.
    539      *
    540      * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
    541      * @param yaml
    542      *            stream of YAML documents
    543      * @return parsed root Nodes for all the specified YAML documents
    544      */
    545     public Iterable<Node> composeAll(Reader yaml) {
    546         final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
    547         constructor.setComposer(composer);
    548         Iterator<Node> result = new Iterator<Node>() {
    549             public boolean hasNext() {
    550                 return composer.checkNode();
    551             }
    552 
    553             public Node next() {
    554                 return composer.getNode();
    555             }
    556 
    557             public void remove() {
    558                 throw new UnsupportedOperationException();
    559             }
    560         };
    561         return new NodeIterable(result);
    562     }
    563 
    564     private static class NodeIterable implements Iterable<Node> {
    565         private Iterator<Node> iterator;
    566 
    567         public NodeIterable(Iterator<Node> iterator) {
    568             this.iterator = iterator;
    569         }
    570 
    571         public Iterator<Node> iterator() {
    572             return iterator;
    573         }
    574     }
    575 
    576     /**
    577      * Add an implicit scalar detector. If an implicit scalar value matches the
    578      * given regexp, the corresponding tag is assigned to the scalar.
    579      *
    580      * @param tag
    581      *            tag to assign to the node
    582      * @param regexp
    583      *            regular expression to match against
    584      * @param first
    585      *            a sequence of possible initial characters or null (which means
    586      *            any).
    587      */
    588     public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
    589         resolver.addImplicitResolver(tag, regexp, first);
    590     }
    591 
    592     @Override
    593     public String toString() {
    594         return name;
    595     }
    596 
    597     /**
    598      * Get a meaningful name. It simplifies debugging in a multi-threaded
    599      * environment. If nothing is set explicitly the address of the instance is
    600      * returned.
    601      *
    602      * @return human readable name
    603      */
    604     public String getName() {
    605         return name;
    606     }
    607 
    608     /**
    609      * Set a meaningful name to be shown in toString()
    610      *
    611      * @param name
    612      *            human readable name
    613      */
    614     public void setName(String name) {
    615         this.name = name;
    616     }
    617 
    618     /**
    619      * Parse a YAML stream and produce parsing events.
    620      *
    621      * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
    622      * @param yaml
    623      *            YAML document(s)
    624      * @return parsed events
    625      */
    626     public Iterable<Event> parse(Reader yaml) {
    627         final Parser parser = new ParserImpl(new StreamReader(yaml));
    628         Iterator<Event> result = new Iterator<Event>() {
    629             public boolean hasNext() {
    630                 return parser.peekEvent() != null;
    631             }
    632 
    633             public Event next() {
    634                 return parser.getEvent();
    635             }
    636 
    637             public void remove() {
    638                 throw new UnsupportedOperationException();
    639             }
    640         };
    641         return new EventIterable(result);
    642     }
    643 
    644     private static class EventIterable implements Iterable<Event> {
    645         private Iterator<Event> iterator;
    646 
    647         public EventIterable(Iterator<Event> iterator) {
    648             this.iterator = iterator;
    649         }
    650 
    651         public Iterator<Event> iterator() {
    652             return iterator;
    653         }
    654     }
    655 
    656     public void setBeanAccess(BeanAccess beanAccess) {
    657         constructor.getPropertyUtils().setBeanAccess(beanAccess);
    658         representer.getPropertyUtils().setBeanAccess(beanAccess);
    659     }
    660 
    661 }
    662