11adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov/**
211a89b445f3bde56bf07e6a0d04f0b0256dcb215Andrey Somov * Copyright (c) 2008, http://www.snakeyaml.org
31adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov *
41adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * Licensed under the Apache License, Version 2.0 (the "License");
51adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * you may not use this file except in compliance with the License.
61adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * You may obtain a copy of the License at
71adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov *
81adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov *     http://www.apache.org/licenses/LICENSE-2.0
91adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov *
101adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * Unless required by applicable law or agreed to in writing, software
111adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * distributed under the License is distributed on an "AS IS" BASIS,
121adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * See the License for the specific language governing permissions and
141adf3a94fce500e0bda933e4b495a004d5789bd7Andrey Somov * limitations under the License.
159629be70863521bead138c295351f3dec926ab1Andrey Somov */
169629be70863521bead138c295351f3dec926ab1Andrey Somovpackage org.yaml.snakeyaml.emitter;
179629be70863521bead138c295351f3dec926ab1Andrey Somov
189629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.io.IOException;
199629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.io.Writer;
209629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.HashMap;
219629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.Iterator;
229629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.LinkedHashMap;
239629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.Map;
249629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.Queue;
259629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.Set;
269629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.TreeSet;
27c294eb822a2d69f64d2f1226d8a6f639b59a179aAndrey Somovimport java.util.concurrent.ArrayBlockingQueue;
289629be70863521bead138c295351f3dec926ab1Andrey Somovimport java.util.regex.Pattern;
299629be70863521bead138c295351f3dec926ab1Andrey Somov
309629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.DumperOptions;
315476b912d698337c3bd69e38cadf15f60986e269Andrey Somovimport org.yaml.snakeyaml.DumperOptions.Version;
3234f59d77fd0a29b085d4c642eb1db6b8925fc3c3Andrey Somovimport org.yaml.snakeyaml.error.YAMLException;
339629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.AliasEvent;
349629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.CollectionEndEvent;
359629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.CollectionStartEvent;
369629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.DocumentEndEvent;
379629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.DocumentStartEvent;
389629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.Event;
399629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.MappingEndEvent;
409629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.MappingStartEvent;
419629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.NodeEvent;
429629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.ScalarEvent;
439629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.SequenceEndEvent;
449629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.SequenceStartEvent;
459629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.StreamEndEvent;
469629be70863521bead138c295351f3dec926ab1Andrey Somovimport org.yaml.snakeyaml.events.StreamStartEvent;
4787ff57908e8ccf62ef984bb067a61b815a9a1074Andrey Somovimport org.yaml.snakeyaml.nodes.Tag;
487361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somovimport org.yaml.snakeyaml.reader.StreamReader;
4965619eff85ddfd768e973825b34b2ed8bbde537dAndrey Somovimport org.yaml.snakeyaml.scanner.Constant;
5080ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somovimport org.yaml.snakeyaml.util.ArrayStack;
519629be70863521bead138c295351f3dec926ab1Andrey Somov
529629be70863521bead138c295351f3dec926ab1Andrey Somov/**
539629be70863521bead138c295351f3dec926ab1Andrey Somov * <pre>
549629be70863521bead138c295351f3dec926ab1Andrey Somov * Emitter expects events obeying the following grammar:
559629be70863521bead138c295351f3dec926ab1Andrey Somov * stream ::= STREAM-START document* STREAM-END
569629be70863521bead138c295351f3dec926ab1Andrey Somov * document ::= DOCUMENT-START node DOCUMENT-END
579629be70863521bead138c295351f3dec926ab1Andrey Somov * node ::= SCALAR | sequence | mapping
589629be70863521bead138c295351f3dec926ab1Andrey Somov * sequence ::= SEQUENCE-START node* SEQUENCE-END
599629be70863521bead138c295351f3dec926ab1Andrey Somov * mapping ::= MAPPING-START (node node)* MAPPING-END
609629be70863521bead138c295351f3dec926ab1Andrey Somov * </pre>
619629be70863521bead138c295351f3dec926ab1Andrey Somov */
6291afe6c6cb158fbf7b63481a60d68e8b98d57250Andrey Somovpublic final class Emitter implements Emitable {
639629be70863521bead138c295351f3dec926ab1Andrey Somov    private static final Map<Character, String> ESCAPE_REPLACEMENTS = new HashMap<Character, String>();
649629be70863521bead138c295351f3dec926ab1Andrey Somov    public static final int MIN_INDENT = 1;
659629be70863521bead138c295351f3dec926ab1Andrey Somov    public static final int MAX_INDENT = 10;
669629be70863521bead138c295351f3dec926ab1Andrey Somov
67d4c7e6efc571fe29d05cc8d0f6d0e713e8c4a35fAndrey Somov    private static final char[] SPACE = { ' ' };
68370faf2dd7606d5fe2f887012be73d40fa531559maslovalex
699629be70863521bead138c295351f3dec926ab1Andrey Somov    static {
708fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\0', "0");
718fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u0007', "a");
728fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u0008', "b");
738fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u0009', "t");
748fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\n', "n");
758fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u000B', "v");
768fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u000C', "f");
778fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\r', "r");
788fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u001B', "e");
798fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('"', "\"");
808fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\\', "\\");
818fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u0085', "N");
828fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u00A0', "_");
838fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u2028', "L");
848fb5a7218a84a1f88abadf4bac8b9f8a4555d8e8Andrey Somov        ESCAPE_REPLACEMENTS.put('\u2029', "P");
859629be70863521bead138c295351f3dec926ab1Andrey Somov    }
869629be70863521bead138c295351f3dec926ab1Andrey Somov
879629be70863521bead138c295351f3dec926ab1Andrey Somov    private final static Map<String, String> DEFAULT_TAG_PREFIXES = new LinkedHashMap<String, String>();
889629be70863521bead138c295351f3dec926ab1Andrey Somov    static {
899629be70863521bead138c295351f3dec926ab1Andrey Somov        DEFAULT_TAG_PREFIXES.put("!", "!");
9087ff57908e8ccf62ef984bb067a61b815a9a1074Andrey Somov        DEFAULT_TAG_PREFIXES.put(Tag.PREFIX, "!!");
919629be70863521bead138c295351f3dec926ab1Andrey Somov    }
929629be70863521bead138c295351f3dec926ab1Andrey Somov    // The stream should have the methods `write` and possibly `flush`.
939629be70863521bead138c295351f3dec926ab1Andrey Somov    private final Writer stream;
949629be70863521bead138c295351f3dec926ab1Andrey Somov
959629be70863521bead138c295351f3dec926ab1Andrey Somov    // Encoding is defined by Writer (cannot be overriden by STREAM-START.)
969629be70863521bead138c295351f3dec926ab1Andrey Somov    // private Charset encoding;
979629be70863521bead138c295351f3dec926ab1Andrey Somov
989629be70863521bead138c295351f3dec926ab1Andrey Somov    // Emitter is a state machine with a stack of states to handle nested
999629be70863521bead138c295351f3dec926ab1Andrey Somov    // structures.
10080ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov    private final ArrayStack<EmitterState> states;
1019629be70863521bead138c295351f3dec926ab1Andrey Somov    private EmitterState state;
1029629be70863521bead138c295351f3dec926ab1Andrey Somov
1039629be70863521bead138c295351f3dec926ab1Andrey Somov    // Current event and the event queue.
1049629be70863521bead138c295351f3dec926ab1Andrey Somov    private final Queue<Event> events;
1059629be70863521bead138c295351f3dec926ab1Andrey Somov    private Event event;
1069629be70863521bead138c295351f3dec926ab1Andrey Somov
1079629be70863521bead138c295351f3dec926ab1Andrey Somov    // The current indentation level and the stack of previous indents.
10880ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov    private final ArrayStack<Integer> indents;
1099629be70863521bead138c295351f3dec926ab1Andrey Somov    private Integer indent;
1109629be70863521bead138c295351f3dec926ab1Andrey Somov
1119629be70863521bead138c295351f3dec926ab1Andrey Somov    // Flow level.
1129629be70863521bead138c295351f3dec926ab1Andrey Somov    private int flowLevel;
1139629be70863521bead138c295351f3dec926ab1Andrey Somov
1149629be70863521bead138c295351f3dec926ab1Andrey Somov    // Contexts.
1159629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean rootContext;
1169629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean mappingContext;
1179629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean simpleKeyContext;
1189629be70863521bead138c295351f3dec926ab1Andrey Somov
1199629be70863521bead138c295351f3dec926ab1Andrey Somov    //
1209629be70863521bead138c295351f3dec926ab1Andrey Somov    // Characteristics of the last emitted character:
1219629be70863521bead138c295351f3dec926ab1Andrey Somov    // - current position.
1229629be70863521bead138c295351f3dec926ab1Andrey Somov    // - is it a whitespace?
1239629be70863521bead138c295351f3dec926ab1Andrey Somov    // - is it an indention character
1249629be70863521bead138c295351f3dec926ab1Andrey Somov    // (indentation space, '-', '?', or ':')?
125a5424f52a32314d70897f06a0aec907cf9231963Andrey Somov    // private int line; this variable is not used
1269629be70863521bead138c295351f3dec926ab1Andrey Somov    private int column;
1279629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean whitespace;
1289629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean indention;
1299629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean openEnded;
1309629be70863521bead138c295351f3dec926ab1Andrey Somov
1319629be70863521bead138c295351f3dec926ab1Andrey Somov    // Formatting details.
1329629be70863521bead138c295351f3dec926ab1Andrey Somov    private Boolean canonical;
1338cda6fba9cc20d9210a50e13a81b37affb84b39bobastard    // pretty print flow by adding extra line breaks
134fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov    private Boolean prettyFlow;
1358cda6fba9cc20d9210a50e13a81b37affb84b39bobastard
1369629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean allowUnicode;
1379629be70863521bead138c295351f3dec926ab1Andrey Somov    private int bestIndent;
138d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha    private int indicatorIndent;
1399629be70863521bead138c295351f3dec926ab1Andrey Somov    private int bestWidth;
140370faf2dd7606d5fe2f887012be73d40fa531559maslovalex    private char[] bestLineBreak;
1417fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela    private boolean splitLines;
1429629be70863521bead138c295351f3dec926ab1Andrey Somov
1439629be70863521bead138c295351f3dec926ab1Andrey Somov    // Tag prefixes.
1449629be70863521bead138c295351f3dec926ab1Andrey Somov    private Map<String, String> tagPrefixes;
1459629be70863521bead138c295351f3dec926ab1Andrey Somov
1469629be70863521bead138c295351f3dec926ab1Andrey Somov    // Prepared anchor and tag.
1479629be70863521bead138c295351f3dec926ab1Andrey Somov    private String preparedAnchor;
1489629be70863521bead138c295351f3dec926ab1Andrey Somov    private String preparedTag;
1499629be70863521bead138c295351f3dec926ab1Andrey Somov
1509629be70863521bead138c295351f3dec926ab1Andrey Somov    // Scalar analysis and style.
1519629be70863521bead138c295351f3dec926ab1Andrey Somov    private ScalarAnalysis analysis;
152d79a13b3448ff517eaa1a32ab5506db735d83c92as    private Character style;
1539629be70863521bead138c295351f3dec926ab1Andrey Somov
1549629be70863521bead138c295351f3dec926ab1Andrey Somov    public Emitter(Writer stream, DumperOptions opts) {
1559629be70863521bead138c295351f3dec926ab1Andrey Somov        // The stream should have the methods `write` and possibly `flush`.
1569629be70863521bead138c295351f3dec926ab1Andrey Somov        this.stream = stream;
1579629be70863521bead138c295351f3dec926ab1Andrey Somov        // Emitter is a state machine with a stack of states to handle nested
1589629be70863521bead138c295351f3dec926ab1Andrey Somov        // structures.
15980ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        this.states = new ArrayStack<EmitterState>(100);
1609629be70863521bead138c295351f3dec926ab1Andrey Somov        this.state = new ExpectStreamStart();
1619629be70863521bead138c295351f3dec926ab1Andrey Somov        // Current event and the event queue.
162c294eb822a2d69f64d2f1226d8a6f639b59a179aAndrey Somov        this.events = new ArrayBlockingQueue<Event>(100);
1639629be70863521bead138c295351f3dec926ab1Andrey Somov        this.event = null;
1649629be70863521bead138c295351f3dec926ab1Andrey Somov        // The current indentation level and the stack of previous indents.
16580ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        this.indents = new ArrayStack<Integer>(10);
1669629be70863521bead138c295351f3dec926ab1Andrey Somov        this.indent = null;
1679629be70863521bead138c295351f3dec926ab1Andrey Somov        // Flow level.
1689629be70863521bead138c295351f3dec926ab1Andrey Somov        this.flowLevel = 0;
1699629be70863521bead138c295351f3dec926ab1Andrey Somov        // Contexts.
1709629be70863521bead138c295351f3dec926ab1Andrey Somov        mappingContext = false;
1719629be70863521bead138c295351f3dec926ab1Andrey Somov        simpleKeyContext = false;
1729629be70863521bead138c295351f3dec926ab1Andrey Somov
1739629be70863521bead138c295351f3dec926ab1Andrey Somov        //
1749629be70863521bead138c295351f3dec926ab1Andrey Somov        // Characteristics of the last emitted character:
1759629be70863521bead138c295351f3dec926ab1Andrey Somov        // - current position.
1769629be70863521bead138c295351f3dec926ab1Andrey Somov        // - is it a whitespace?
1779629be70863521bead138c295351f3dec926ab1Andrey Somov        // - is it an indention character
1789629be70863521bead138c295351f3dec926ab1Andrey Somov        // (indentation space, '-', '?', or ':')?
1799629be70863521bead138c295351f3dec926ab1Andrey Somov        column = 0;
1809629be70863521bead138c295351f3dec926ab1Andrey Somov        whitespace = true;
1819629be70863521bead138c295351f3dec926ab1Andrey Somov        indention = true;
1829629be70863521bead138c295351f3dec926ab1Andrey Somov
1839629be70863521bead138c295351f3dec926ab1Andrey Somov        // Whether the document requires an explicit document indicator
1849629be70863521bead138c295351f3dec926ab1Andrey Somov        openEnded = false;
1859629be70863521bead138c295351f3dec926ab1Andrey Somov
1869629be70863521bead138c295351f3dec926ab1Andrey Somov        // Formatting details.
187fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov        this.canonical = opts.isCanonical();
188fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov        this.prettyFlow = opts.isPrettyFlow();
1899629be70863521bead138c295351f3dec926ab1Andrey Somov        this.allowUnicode = opts.isAllowUnicode();
1909629be70863521bead138c295351f3dec926ab1Andrey Somov        this.bestIndent = 2;
1919629be70863521bead138c295351f3dec926ab1Andrey Somov        if ((opts.getIndent() > MIN_INDENT) && (opts.getIndent() < MAX_INDENT)) {
1929629be70863521bead138c295351f3dec926ab1Andrey Somov            this.bestIndent = opts.getIndent();
1939629be70863521bead138c295351f3dec926ab1Andrey Somov        }
194d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        this.indicatorIndent = opts.getIndicatorIndent();
1959629be70863521bead138c295351f3dec926ab1Andrey Somov        this.bestWidth = 80;
1969629be70863521bead138c295351f3dec926ab1Andrey Somov        if (opts.getWidth() > this.bestIndent * 2) {
1979629be70863521bead138c295351f3dec926ab1Andrey Somov            this.bestWidth = opts.getWidth();
1989629be70863521bead138c295351f3dec926ab1Andrey Somov        }
199370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        this.bestLineBreak = opts.getLineBreak().getString().toCharArray();
2007fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela        this.splitLines = opts.getSplitLines();
2019629be70863521bead138c295351f3dec926ab1Andrey Somov
2029629be70863521bead138c295351f3dec926ab1Andrey Somov        // Tag prefixes.
2039629be70863521bead138c295351f3dec926ab1Andrey Somov        this.tagPrefixes = new LinkedHashMap<String, String>();
2049629be70863521bead138c295351f3dec926ab1Andrey Somov
2059629be70863521bead138c295351f3dec926ab1Andrey Somov        // Prepared anchor and tag.
2069629be70863521bead138c295351f3dec926ab1Andrey Somov        this.preparedAnchor = null;
2079629be70863521bead138c295351f3dec926ab1Andrey Somov        this.preparedTag = null;
2089629be70863521bead138c295351f3dec926ab1Andrey Somov
2099629be70863521bead138c295351f3dec926ab1Andrey Somov        // Scalar analysis and style.
2109629be70863521bead138c295351f3dec926ab1Andrey Somov        this.analysis = null;
211d79a13b3448ff517eaa1a32ab5506db735d83c92as        this.style = null;
2129629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2139629be70863521bead138c295351f3dec926ab1Andrey Somov
2149629be70863521bead138c295351f3dec926ab1Andrey Somov    public void emit(Event event) throws IOException {
215c294eb822a2d69f64d2f1226d8a6f639b59a179aAndrey Somov        this.events.add(event);
2169629be70863521bead138c295351f3dec926ab1Andrey Somov        while (!needMoreEvents()) {
2179629be70863521bead138c295351f3dec926ab1Andrey Somov            this.event = this.events.poll();
2189629be70863521bead138c295351f3dec926ab1Andrey Somov            this.state.expect();
2199629be70863521bead138c295351f3dec926ab1Andrey Somov            this.event = null;
2209629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2219629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2229629be70863521bead138c295351f3dec926ab1Andrey Somov
2239629be70863521bead138c295351f3dec926ab1Andrey Somov    // In some cases, we wait for a few next events before emitting.
2249629be70863521bead138c295351f3dec926ab1Andrey Somov
2259629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean needMoreEvents() {
2269629be70863521bead138c295351f3dec926ab1Andrey Somov        if (events.isEmpty()) {
2279629be70863521bead138c295351f3dec926ab1Andrey Somov            return true;
2289629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2299629be70863521bead138c295351f3dec926ab1Andrey Somov        Event event = events.peek();
2309629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof DocumentStartEvent) {
2319629be70863521bead138c295351f3dec926ab1Andrey Somov            return needEvents(1);
2329629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (event instanceof SequenceStartEvent) {
2339629be70863521bead138c295351f3dec926ab1Andrey Somov            return needEvents(2);
2349629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (event instanceof MappingStartEvent) {
2359629be70863521bead138c295351f3dec926ab1Andrey Somov            return needEvents(3);
2369629be70863521bead138c295351f3dec926ab1Andrey Somov        } else {
2379629be70863521bead138c295351f3dec926ab1Andrey Somov            return false;
2389629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2399629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2409629be70863521bead138c295351f3dec926ab1Andrey Somov
2419629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean needEvents(int count) {
2429629be70863521bead138c295351f3dec926ab1Andrey Somov        int level = 0;
2439629be70863521bead138c295351f3dec926ab1Andrey Somov        Iterator<Event> iter = events.iterator();
2449629be70863521bead138c295351f3dec926ab1Andrey Somov        iter.next();
2459629be70863521bead138c295351f3dec926ab1Andrey Somov        while (iter.hasNext()) {
2469629be70863521bead138c295351f3dec926ab1Andrey Somov            Event event = iter.next();
2479629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof DocumentStartEvent || event instanceof CollectionStartEvent) {
2489629be70863521bead138c295351f3dec926ab1Andrey Somov                level++;
2499629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (event instanceof DocumentEndEvent || event instanceof CollectionEndEvent) {
2509629be70863521bead138c295351f3dec926ab1Andrey Somov                level--;
2519629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (event instanceof StreamEndEvent) {
2529629be70863521bead138c295351f3dec926ab1Andrey Somov                level = -1;
2539629be70863521bead138c295351f3dec926ab1Andrey Somov            }
2549629be70863521bead138c295351f3dec926ab1Andrey Somov            if (level < 0) {
2559629be70863521bead138c295351f3dec926ab1Andrey Somov                return false;
2569629be70863521bead138c295351f3dec926ab1Andrey Somov            }
2579629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2589629be70863521bead138c295351f3dec926ab1Andrey Somov        return events.size() < count + 1;
2599629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2609629be70863521bead138c295351f3dec926ab1Andrey Somov
2619629be70863521bead138c295351f3dec926ab1Andrey Somov    private void increaseIndent(boolean flow, boolean indentless) {
26280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        indents.push(indent);
2639629be70863521bead138c295351f3dec926ab1Andrey Somov        if (indent == null) {
2649629be70863521bead138c295351f3dec926ab1Andrey Somov            if (flow) {
2659629be70863521bead138c295351f3dec926ab1Andrey Somov                indent = bestIndent;
2669629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
2679629be70863521bead138c295351f3dec926ab1Andrey Somov                indent = 0;
2689629be70863521bead138c295351f3dec926ab1Andrey Somov            }
2699629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (!indentless) {
2709629be70863521bead138c295351f3dec926ab1Andrey Somov            this.indent += bestIndent;
2719629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2729629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2739629be70863521bead138c295351f3dec926ab1Andrey Somov
2749629be70863521bead138c295351f3dec926ab1Andrey Somov    // States
2759629be70863521bead138c295351f3dec926ab1Andrey Somov
2769629be70863521bead138c295351f3dec926ab1Andrey Somov    // Stream handlers.
2779629be70863521bead138c295351f3dec926ab1Andrey Somov
2789629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectStreamStart implements EmitterState {
2799629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
2809629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof StreamStartEvent) {
2819629be70863521bead138c295351f3dec926ab1Andrey Somov                writeStreamStart();
2829629be70863521bead138c295351f3dec926ab1Andrey Somov                state = new ExpectFirstDocumentStart();
2839629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
2849629be70863521bead138c295351f3dec926ab1Andrey Somov                throw new EmitterException("expected StreamStartEvent, but got " + event);
2859629be70863521bead138c295351f3dec926ab1Andrey Somov            }
2869629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2879629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2889629be70863521bead138c295351f3dec926ab1Andrey Somov
2899629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectNothing implements EmitterState {
2909629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
2919629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("expecting nothing, but got " + event);
2929629be70863521bead138c295351f3dec926ab1Andrey Somov        }
2939629be70863521bead138c295351f3dec926ab1Andrey Somov    }
2949629be70863521bead138c295351f3dec926ab1Andrey Somov
2959629be70863521bead138c295351f3dec926ab1Andrey Somov    // Document handlers.
2969629be70863521bead138c295351f3dec926ab1Andrey Somov
2979629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFirstDocumentStart implements EmitterState {
2989629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
2999629be70863521bead138c295351f3dec926ab1Andrey Somov            new ExpectDocumentStart(true).expect();
3009629be70863521bead138c295351f3dec926ab1Andrey Somov        }
3019629be70863521bead138c295351f3dec926ab1Andrey Somov    }
3029629be70863521bead138c295351f3dec926ab1Andrey Somov
3039629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectDocumentStart implements EmitterState {
3049629be70863521bead138c295351f3dec926ab1Andrey Somov        private boolean first;
3059629be70863521bead138c295351f3dec926ab1Andrey Somov
3069629be70863521bead138c295351f3dec926ab1Andrey Somov        public ExpectDocumentStart(boolean first) {
3079629be70863521bead138c295351f3dec926ab1Andrey Somov            this.first = first;
3089629be70863521bead138c295351f3dec926ab1Andrey Somov        }
3099629be70863521bead138c295351f3dec926ab1Andrey Somov
3109629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
3119629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof DocumentStartEvent) {
3129629be70863521bead138c295351f3dec926ab1Andrey Somov                DocumentStartEvent ev = (DocumentStartEvent) event;
3139629be70863521bead138c295351f3dec926ab1Andrey Somov                if ((ev.getVersion() != null || ev.getTags() != null) && openEnded) {
3149629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("...", true, false, false);
3159629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
3169629be70863521bead138c295351f3dec926ab1Andrey Somov                }
3179629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ev.getVersion() != null) {
3189629be70863521bead138c295351f3dec926ab1Andrey Somov                    String versionText = prepareVersion(ev.getVersion());
3199629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeVersionDirective(versionText);
3209629be70863521bead138c295351f3dec926ab1Andrey Somov                }
3219629be70863521bead138c295351f3dec926ab1Andrey Somov                tagPrefixes = new LinkedHashMap<String, String>(DEFAULT_TAG_PREFIXES);
3229629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ev.getTags() != null) {
3239629be70863521bead138c295351f3dec926ab1Andrey Somov                    Set<String> handles = new TreeSet<String>(ev.getTags().keySet());
3249629be70863521bead138c295351f3dec926ab1Andrey Somov                    for (String handle : handles) {
3259629be70863521bead138c295351f3dec926ab1Andrey Somov                        String prefix = ev.getTags().get(handle);
3269629be70863521bead138c295351f3dec926ab1Andrey Somov                        tagPrefixes.put(prefix, handle);
3279629be70863521bead138c295351f3dec926ab1Andrey Somov                        String handleText = prepareTagHandle(handle);
3289629be70863521bead138c295351f3dec926ab1Andrey Somov                        String prefixText = prepareTagPrefix(prefix);
3299629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeTagDirective(handleText, prefixText);
3309629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
3319629be70863521bead138c295351f3dec926ab1Andrey Somov                }
3329629be70863521bead138c295351f3dec926ab1Andrey Somov                boolean implicit = first && !ev.getExplicit() && !canonical
333b0d589e0eaf2651114eaa7ffd48b753f466cff72Andrey Somov                        && ev.getVersion() == null
334b0d589e0eaf2651114eaa7ffd48b753f466cff72Andrey Somov                        && (ev.getTags() == null || ev.getTags().isEmpty())
335b0d589e0eaf2651114eaa7ffd48b753f466cff72Andrey Somov                        && !checkEmptyDocument();
3369629be70863521bead138c295351f3dec926ab1Andrey Somov                if (!implicit) {
3379629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
3389629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("---", true, false, false);
3399629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (canonical) {
3409629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
3419629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
3429629be70863521bead138c295351f3dec926ab1Andrey Somov                }
3439629be70863521bead138c295351f3dec926ab1Andrey Somov                state = new ExpectDocumentRoot();
3449629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (event instanceof StreamEndEvent) {
3459629be70863521bead138c295351f3dec926ab1Andrey Somov                // TODO fix 313 PyYAML changeset
3469629be70863521bead138c295351f3dec926ab1Andrey Somov                // if (openEnded) {
3479629be70863521bead138c295351f3dec926ab1Andrey Somov                // writeIndicator("...", true, false, false);
3489629be70863521bead138c295351f3dec926ab1Andrey Somov                // writeIndent();
3499629be70863521bead138c295351f3dec926ab1Andrey Somov                // }
3509629be70863521bead138c295351f3dec926ab1Andrey Somov                writeStreamEnd();
3519629be70863521bead138c295351f3dec926ab1Andrey Somov                state = new ExpectNothing();
3529629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
3539629be70863521bead138c295351f3dec926ab1Andrey Somov                throw new EmitterException("expected DocumentStartEvent, but got " + event);
3549629be70863521bead138c295351f3dec926ab1Andrey Somov            }
3559629be70863521bead138c295351f3dec926ab1Andrey Somov        }
3569629be70863521bead138c295351f3dec926ab1Andrey Somov    }
3579629be70863521bead138c295351f3dec926ab1Andrey Somov
3589629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectDocumentEnd implements EmitterState {
3599629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
3609629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof DocumentEndEvent) {
3619629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndent();
3629629be70863521bead138c295351f3dec926ab1Andrey Somov                if (((DocumentEndEvent) event).getExplicit()) {
3639629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("...", true, false, false);
3649629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
3659629be70863521bead138c295351f3dec926ab1Andrey Somov                }
3669629be70863521bead138c295351f3dec926ab1Andrey Somov                flushStream();
3679629be70863521bead138c295351f3dec926ab1Andrey Somov                state = new ExpectDocumentStart(false);
3689629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
3699629be70863521bead138c295351f3dec926ab1Andrey Somov                throw new EmitterException("expected DocumentEndEvent, but got " + event);
3709629be70863521bead138c295351f3dec926ab1Andrey Somov            }
3719629be70863521bead138c295351f3dec926ab1Andrey Somov        }
3729629be70863521bead138c295351f3dec926ab1Andrey Somov    }
3739629be70863521bead138c295351f3dec926ab1Andrey Somov
3749629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectDocumentRoot implements EmitterState {
3759629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
37680ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov            states.push(new ExpectDocumentEnd());
377e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov            expectNode(true, false, false);
3789629be70863521bead138c295351f3dec926ab1Andrey Somov        }
3799629be70863521bead138c295351f3dec926ab1Andrey Somov    }
3809629be70863521bead138c295351f3dec926ab1Andrey Somov
3819629be70863521bead138c295351f3dec926ab1Andrey Somov    // Node handlers.
38265df777c9b28ba6d5c4d3ac5a437280bf112f622Andrey Somov
383e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov    private void expectNode(boolean root, boolean mapping, boolean simpleKey) throws IOException {
3849629be70863521bead138c295351f3dec926ab1Andrey Somov        rootContext = root;
3859629be70863521bead138c295351f3dec926ab1Andrey Somov        mappingContext = mapping;
3869629be70863521bead138c295351f3dec926ab1Andrey Somov        simpleKeyContext = simpleKey;
3879629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof AliasEvent) {
3889629be70863521bead138c295351f3dec926ab1Andrey Somov            expectAlias();
3899629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (event instanceof ScalarEvent || event instanceof CollectionStartEvent) {
3909629be70863521bead138c295351f3dec926ab1Andrey Somov            processAnchor("&");
3919629be70863521bead138c295351f3dec926ab1Andrey Somov            processTag();
3929629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof ScalarEvent) {
3939629be70863521bead138c295351f3dec926ab1Andrey Somov                expectScalar();
3949629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (event instanceof SequenceStartEvent) {
3959629be70863521bead138c295351f3dec926ab1Andrey Somov                if (flowLevel != 0 || canonical || ((SequenceStartEvent) event).getFlowStyle()
3969629be70863521bead138c295351f3dec926ab1Andrey Somov                        || checkEmptySequence()) {
3979629be70863521bead138c295351f3dec926ab1Andrey Somov                    expectFlowSequence();
3989629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
3999629be70863521bead138c295351f3dec926ab1Andrey Somov                    expectBlockSequence();
4009629be70863521bead138c295351f3dec926ab1Andrey Somov                }
4019629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {// MappingStartEvent
4029629be70863521bead138c295351f3dec926ab1Andrey Somov                if (flowLevel != 0 || canonical || ((MappingStartEvent) event).getFlowStyle()
4039629be70863521bead138c295351f3dec926ab1Andrey Somov                        || checkEmptyMapping()) {
4049629be70863521bead138c295351f3dec926ab1Andrey Somov                    expectFlowMapping();
4059629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
4069629be70863521bead138c295351f3dec926ab1Andrey Somov                    expectBlockMapping();
4079629be70863521bead138c295351f3dec926ab1Andrey Somov                }
4089629be70863521bead138c295351f3dec926ab1Andrey Somov            }
4099629be70863521bead138c295351f3dec926ab1Andrey Somov        } else {
4109629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("expected NodeEvent, but got " + event);
4119629be70863521bead138c295351f3dec926ab1Andrey Somov        }
4129629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4139629be70863521bead138c295351f3dec926ab1Andrey Somov
4149629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectAlias() throws IOException {
4159629be70863521bead138c295351f3dec926ab1Andrey Somov        if (((NodeEvent) event).getAnchor() == null) {
4169629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("anchor is not specified for alias");
4179629be70863521bead138c295351f3dec926ab1Andrey Somov        }
4189629be70863521bead138c295351f3dec926ab1Andrey Somov        processAnchor("*");
41980ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        state = states.pop();
4209629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4219629be70863521bead138c295351f3dec926ab1Andrey Somov
4229629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectScalar() throws IOException {
4239629be70863521bead138c295351f3dec926ab1Andrey Somov        increaseIndent(true, false);
4249629be70863521bead138c295351f3dec926ab1Andrey Somov        processScalar();
42580ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        indent = indents.pop();
42680ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov        state = states.pop();
4279629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4289629be70863521bead138c295351f3dec926ab1Andrey Somov
4299629be70863521bead138c295351f3dec926ab1Andrey Somov    // Flow sequence handlers.
4309629be70863521bead138c295351f3dec926ab1Andrey Somov
4319629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectFlowSequence() throws IOException {
4329629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("[", true, true, false);
4339629be70863521bead138c295351f3dec926ab1Andrey Somov        flowLevel++;
4349629be70863521bead138c295351f3dec926ab1Andrey Somov        increaseIndent(true, false);
435fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov        if (prettyFlow) {
4368cda6fba9cc20d9210a50e13a81b37affb84b39bobastard            writeIndent();
4378cda6fba9cc20d9210a50e13a81b37affb84b39bobastard        }
438fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov        state = new ExpectFirstFlowSequenceItem();
4399629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4409629be70863521bead138c295351f3dec926ab1Andrey Somov
4419629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFirstFlowSequenceItem implements EmitterState {
4429629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
4439629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof SequenceEndEvent) {
44480ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
4459629be70863521bead138c295351f3dec926ab1Andrey Somov                flowLevel--;
4469629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator("]", false, false, false);
44780ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
4489629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
4497fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
4509629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
4519629be70863521bead138c295351f3dec926ab1Andrey Somov                }
45280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                states.push(new ExpectFlowSequenceItem());
453e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                expectNode(false, false, false);
4549629be70863521bead138c295351f3dec926ab1Andrey Somov            }
4559629be70863521bead138c295351f3dec926ab1Andrey Somov        }
4569629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4579629be70863521bead138c295351f3dec926ab1Andrey Somov
4589629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFlowSequenceItem implements EmitterState {
4599629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
4609629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof SequenceEndEvent) {
46180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
4629629be70863521bead138c295351f3dec926ab1Andrey Somov                flowLevel--;
4639629be70863521bead138c295351f3dec926ab1Andrey Somov                if (canonical) {
4649629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator(",", false, false, false);
4659629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
4669629be70863521bead138c295351f3dec926ab1Andrey Somov                }
4679629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator("]", false, false, false);
468fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov                if (prettyFlow) {
4698cda6fba9cc20d9210a50e13a81b37affb84b39bobastard                    writeIndent();
4708cda6fba9cc20d9210a50e13a81b37affb84b39bobastard                }
47180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
4729629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
4739629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator(",", false, false, false);
474c5a8071bf0313dfc294098d272dbecfdff7c98ceVille Koskela                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
4759629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
4769629be70863521bead138c295351f3dec926ab1Andrey Somov                }
47780ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                states.push(new ExpectFlowSequenceItem());
478e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                expectNode(false, false, false);
4799629be70863521bead138c295351f3dec926ab1Andrey Somov            }
4809629be70863521bead138c295351f3dec926ab1Andrey Somov        }
4819629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4829629be70863521bead138c295351f3dec926ab1Andrey Somov
4839629be70863521bead138c295351f3dec926ab1Andrey Somov    // Flow mapping handlers.
4849629be70863521bead138c295351f3dec926ab1Andrey Somov
4859629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectFlowMapping() throws IOException {
4869629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("{", true, true, false);
4879629be70863521bead138c295351f3dec926ab1Andrey Somov        flowLevel++;
4889629be70863521bead138c295351f3dec926ab1Andrey Somov        increaseIndent(true, false);
489fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov        if (prettyFlow) {
4908cda6fba9cc20d9210a50e13a81b37affb84b39bobastard            writeIndent();
4918cda6fba9cc20d9210a50e13a81b37affb84b39bobastard        }
4929629be70863521bead138c295351f3dec926ab1Andrey Somov        state = new ExpectFirstFlowMappingKey();
4939629be70863521bead138c295351f3dec926ab1Andrey Somov    }
4949629be70863521bead138c295351f3dec926ab1Andrey Somov
4959629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFirstFlowMappingKey implements EmitterState {
4969629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
4979629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof MappingEndEvent) {
49880ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
4999629be70863521bead138c295351f3dec926ab1Andrey Somov                flowLevel--;
5009629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator("}", false, false, false);
50180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
5029629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
5037fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
5049629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
5059629be70863521bead138c295351f3dec926ab1Andrey Somov                }
5069629be70863521bead138c295351f3dec926ab1Andrey Somov                if (!canonical && checkSimpleKey()) {
50780ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectFlowMappingSimpleValue());
508e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, true);
5099629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
5109629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("?", true, false, false);
51180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectFlowMappingValue());
512e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, false);
5139629be70863521bead138c295351f3dec926ab1Andrey Somov                }
5149629be70863521bead138c295351f3dec926ab1Andrey Somov            }
5159629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5169629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5179629be70863521bead138c295351f3dec926ab1Andrey Somov
5189629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFlowMappingKey implements EmitterState {
5199629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
5209629be70863521bead138c295351f3dec926ab1Andrey Somov            if (event instanceof MappingEndEvent) {
52180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
5229629be70863521bead138c295351f3dec926ab1Andrey Somov                flowLevel--;
5239629be70863521bead138c295351f3dec926ab1Andrey Somov                if (canonical) {
5249629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator(",", false, false, false);
5259629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
5269629be70863521bead138c295351f3dec926ab1Andrey Somov                }
527fd03e2da32bb87716104bcdfc5b454ae970b277aAndrey Somov                if (prettyFlow) {
5288cda6fba9cc20d9210a50e13a81b37affb84b39bobastard                    writeIndent();
5298cda6fba9cc20d9210a50e13a81b37affb84b39bobastard                }
5309629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator("}", false, false, false);
53180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
5329629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
5339629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator(",", false, false, false);
5347fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
5359629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
5369629be70863521bead138c295351f3dec926ab1Andrey Somov                }
5379629be70863521bead138c295351f3dec926ab1Andrey Somov                if (!canonical && checkSimpleKey()) {
53880ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectFlowMappingSimpleValue());
539e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, true);
5409629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
5419629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("?", true, false, false);
54280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectFlowMappingValue());
543e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, false);
5449629be70863521bead138c295351f3dec926ab1Andrey Somov                }
5459629be70863521bead138c295351f3dec926ab1Andrey Somov            }
5469629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5479629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5489629be70863521bead138c295351f3dec926ab1Andrey Somov
5499629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFlowMappingSimpleValue implements EmitterState {
5509629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
5519629be70863521bead138c295351f3dec926ab1Andrey Somov            writeIndicator(":", false, false, false);
55280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov            states.push(new ExpectFlowMappingKey());
553e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov            expectNode(false, true, false);
5549629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5559629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5569629be70863521bead138c295351f3dec926ab1Andrey Somov
5579629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFlowMappingValue implements EmitterState {
5589629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
5599cddfdc4dc0eb69b3c0e4f9f9a818177707e2ea0Andrey Somov            if (canonical || (column > bestWidth) || prettyFlow) {
5609629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndent();
5619629be70863521bead138c295351f3dec926ab1Andrey Somov            }
5629629be70863521bead138c295351f3dec926ab1Andrey Somov            writeIndicator(":", true, false, false);
56380ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov            states.push(new ExpectFlowMappingKey());
564e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov            expectNode(false, true, false);
5659629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5669629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5679629be70863521bead138c295351f3dec926ab1Andrey Somov
5689629be70863521bead138c295351f3dec926ab1Andrey Somov    // Block sequence handlers.
5699629be70863521bead138c295351f3dec926ab1Andrey Somov
5709629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectBlockSequence() throws IOException {
57125ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov        boolean indentless = mappingContext && !indention;
5729629be70863521bead138c295351f3dec926ab1Andrey Somov        increaseIndent(false, indentless);
5739629be70863521bead138c295351f3dec926ab1Andrey Somov        state = new ExpectFirstBlockSequenceItem();
5749629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5759629be70863521bead138c295351f3dec926ab1Andrey Somov
5769629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFirstBlockSequenceItem implements EmitterState {
5779629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
5789629be70863521bead138c295351f3dec926ab1Andrey Somov            new ExpectBlockSequenceItem(true).expect();
5799629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5809629be70863521bead138c295351f3dec926ab1Andrey Somov    }
5819629be70863521bead138c295351f3dec926ab1Andrey Somov
5829629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectBlockSequenceItem implements EmitterState {
5839629be70863521bead138c295351f3dec926ab1Andrey Somov        private boolean first;
5849629be70863521bead138c295351f3dec926ab1Andrey Somov
5859629be70863521bead138c295351f3dec926ab1Andrey Somov        public ExpectBlockSequenceItem(boolean first) {
5869629be70863521bead138c295351f3dec926ab1Andrey Somov            this.first = first;
5879629be70863521bead138c295351f3dec926ab1Andrey Somov        }
5889629be70863521bead138c295351f3dec926ab1Andrey Somov
5899629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
5909629be70863521bead138c295351f3dec926ab1Andrey Somov            if (!this.first && event instanceof SequenceEndEvent) {
59180ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
59280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
5939629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
5949629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndent();
595d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha                writeWhitespace(indicatorIndent);
5969629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndicator("-", true, false, true);
59780ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                states.push(new ExpectBlockSequenceItem(false));
598e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                expectNode(false, false, false);
5999629be70863521bead138c295351f3dec926ab1Andrey Somov            }
6009629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6019629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6029629be70863521bead138c295351f3dec926ab1Andrey Somov
6039629be70863521bead138c295351f3dec926ab1Andrey Somov    // Block mapping handlers.
6049629be70863521bead138c295351f3dec926ab1Andrey Somov    private void expectBlockMapping() throws IOException {
6059629be70863521bead138c295351f3dec926ab1Andrey Somov        increaseIndent(false, false);
6069629be70863521bead138c295351f3dec926ab1Andrey Somov        state = new ExpectFirstBlockMappingKey();
6079629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6089629be70863521bead138c295351f3dec926ab1Andrey Somov
6099629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectFirstBlockMappingKey implements EmitterState {
6109629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
6119629be70863521bead138c295351f3dec926ab1Andrey Somov            new ExpectBlockMappingKey(true).expect();
6129629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6139629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6149629be70863521bead138c295351f3dec926ab1Andrey Somov
6159629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectBlockMappingKey implements EmitterState {
6169629be70863521bead138c295351f3dec926ab1Andrey Somov        private boolean first;
6179629be70863521bead138c295351f3dec926ab1Andrey Somov
6189629be70863521bead138c295351f3dec926ab1Andrey Somov        public ExpectBlockMappingKey(boolean first) {
6199629be70863521bead138c295351f3dec926ab1Andrey Somov            this.first = first;
6209629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6219629be70863521bead138c295351f3dec926ab1Andrey Somov
6229629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
6239629be70863521bead138c295351f3dec926ab1Andrey Somov            if (!this.first && event instanceof MappingEndEvent) {
62480ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                indent = indents.pop();
62580ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                state = states.pop();
6269629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
6279629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndent();
6289629be70863521bead138c295351f3dec926ab1Andrey Somov                if (checkSimpleKey()) {
62980ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectBlockMappingSimpleValue());
630e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, true);
6319629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
6329629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndicator("?", true, false, true);
63380ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov                    states.push(new ExpectBlockMappingValue());
634e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov                    expectNode(false, true, false);
6359629be70863521bead138c295351f3dec926ab1Andrey Somov                }
6369629be70863521bead138c295351f3dec926ab1Andrey Somov            }
6379629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6389629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6399629be70863521bead138c295351f3dec926ab1Andrey Somov
6409629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectBlockMappingSimpleValue implements EmitterState {
6419629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
6429629be70863521bead138c295351f3dec926ab1Andrey Somov            writeIndicator(":", false, false, false);
64380ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov            states.push(new ExpectBlockMappingKey(false));
644e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov            expectNode(false, true, false);
6459629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6469629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6479629be70863521bead138c295351f3dec926ab1Andrey Somov
6489629be70863521bead138c295351f3dec926ab1Andrey Somov    private class ExpectBlockMappingValue implements EmitterState {
6499629be70863521bead138c295351f3dec926ab1Andrey Somov        public void expect() throws IOException {
6509629be70863521bead138c295351f3dec926ab1Andrey Somov            writeIndent();
6519629be70863521bead138c295351f3dec926ab1Andrey Somov            writeIndicator(":", true, false, true);
65280ff61204231b1f307cf354618f876e8ca1d0f09Andrey Somov            states.push(new ExpectBlockMappingKey(false));
653e46886ee2c0ee6d8819345e13b23baf91d82e2b0Andrey Somov            expectNode(false, true, false);
6549629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6559629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6569629be70863521bead138c295351f3dec926ab1Andrey Somov
6579629be70863521bead138c295351f3dec926ab1Andrey Somov    // Checkers.
6589629be70863521bead138c295351f3dec926ab1Andrey Somov
6599629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean checkEmptySequence() {
66025ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov        return event instanceof SequenceStartEvent && !events.isEmpty() && events.peek() instanceof SequenceEndEvent;
6619629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6629629be70863521bead138c295351f3dec926ab1Andrey Somov
6639629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean checkEmptyMapping() {
66425ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov        return event instanceof MappingStartEvent && !events.isEmpty() && events.peek() instanceof MappingEndEvent;
6659629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6669629be70863521bead138c295351f3dec926ab1Andrey Somov
6679629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean checkEmptyDocument() {
6689629be70863521bead138c295351f3dec926ab1Andrey Somov        if (!(event instanceof DocumentStartEvent) || events.isEmpty()) {
6699629be70863521bead138c295351f3dec926ab1Andrey Somov            return false;
6709629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6719629be70863521bead138c295351f3dec926ab1Andrey Somov        Event event = events.peek();
6729629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof ScalarEvent) {
6739629be70863521bead138c295351f3dec926ab1Andrey Somov            ScalarEvent e = (ScalarEvent) event;
67425ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov            return e.getAnchor() == null && e.getTag() == null && e.getImplicit() != null && e
67525ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                    .getValue().length() == 0;
6769629be70863521bead138c295351f3dec926ab1Andrey Somov        }
677370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        return false;
6789629be70863521bead138c295351f3dec926ab1Andrey Somov    }
6799629be70863521bead138c295351f3dec926ab1Andrey Somov
6809629be70863521bead138c295351f3dec926ab1Andrey Somov    private boolean checkSimpleKey() {
6819629be70863521bead138c295351f3dec926ab1Andrey Somov        int length = 0;
6829629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof NodeEvent && ((NodeEvent) event).getAnchor() != null) {
6839629be70863521bead138c295351f3dec926ab1Andrey Somov            if (preparedAnchor == null) {
6849629be70863521bead138c295351f3dec926ab1Andrey Somov                preparedAnchor = prepareAnchor(((NodeEvent) event).getAnchor());
6859629be70863521bead138c295351f3dec926ab1Andrey Somov            }
6869629be70863521bead138c295351f3dec926ab1Andrey Somov            length += preparedAnchor.length();
6879629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6889629be70863521bead138c295351f3dec926ab1Andrey Somov        String tag = null;
6899629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof ScalarEvent) {
6909629be70863521bead138c295351f3dec926ab1Andrey Somov            tag = ((ScalarEvent) event).getTag();
6919629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (event instanceof CollectionStartEvent) {
6929629be70863521bead138c295351f3dec926ab1Andrey Somov            tag = ((CollectionStartEvent) event).getTag();
6939629be70863521bead138c295351f3dec926ab1Andrey Somov        }
6949629be70863521bead138c295351f3dec926ab1Andrey Somov        if (tag != null) {
6959629be70863521bead138c295351f3dec926ab1Andrey Somov            if (preparedTag == null) {
6969629be70863521bead138c295351f3dec926ab1Andrey Somov                preparedTag = prepareTag(tag);
6979629be70863521bead138c295351f3dec926ab1Andrey Somov            }
6989629be70863521bead138c295351f3dec926ab1Andrey Somov            length += preparedTag.length();
6999629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7009629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof ScalarEvent) {
7019629be70863521bead138c295351f3dec926ab1Andrey Somov            if (analysis == null) {
7029629be70863521bead138c295351f3dec926ab1Andrey Somov                analysis = analyzeScalar(((ScalarEvent) event).getValue());
7039629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7049629be70863521bead138c295351f3dec926ab1Andrey Somov            length += analysis.scalar.length();
7059629be70863521bead138c295351f3dec926ab1Andrey Somov        }
70625ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov        return length < 128 && (event instanceof AliasEvent
7079629be70863521bead138c295351f3dec926ab1Andrey Somov                || (event instanceof ScalarEvent && !analysis.empty && !analysis.multiline)
70825ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                || checkEmptySequence() || checkEmptyMapping());
7099629be70863521bead138c295351f3dec926ab1Andrey Somov    }
7109629be70863521bead138c295351f3dec926ab1Andrey Somov
7119629be70863521bead138c295351f3dec926ab1Andrey Somov    // Anchor, Tag, and Scalar processors.
7129629be70863521bead138c295351f3dec926ab1Andrey Somov
7139629be70863521bead138c295351f3dec926ab1Andrey Somov    private void processAnchor(String indicator) throws IOException {
7149629be70863521bead138c295351f3dec926ab1Andrey Somov        NodeEvent ev = (NodeEvent) event;
7159629be70863521bead138c295351f3dec926ab1Andrey Somov        if (ev.getAnchor() == null) {
7169629be70863521bead138c295351f3dec926ab1Andrey Somov            preparedAnchor = null;
7179629be70863521bead138c295351f3dec926ab1Andrey Somov            return;
7189629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7199629be70863521bead138c295351f3dec926ab1Andrey Somov        if (preparedAnchor == null) {
7209629be70863521bead138c295351f3dec926ab1Andrey Somov            preparedAnchor = prepareAnchor(ev.getAnchor());
7219629be70863521bead138c295351f3dec926ab1Andrey Somov        }
722d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        writeIndicator(indicator + preparedAnchor, true, false, false);
7239629be70863521bead138c295351f3dec926ab1Andrey Somov        preparedAnchor = null;
7249629be70863521bead138c295351f3dec926ab1Andrey Somov    }
7259629be70863521bead138c295351f3dec926ab1Andrey Somov
7269629be70863521bead138c295351f3dec926ab1Andrey Somov    private void processTag() throws IOException {
7279629be70863521bead138c295351f3dec926ab1Andrey Somov        String tag = null;
7289629be70863521bead138c295351f3dec926ab1Andrey Somov        if (event instanceof ScalarEvent) {
7299629be70863521bead138c295351f3dec926ab1Andrey Somov            ScalarEvent ev = (ScalarEvent) event;
7309629be70863521bead138c295351f3dec926ab1Andrey Somov            tag = ev.getTag();
731d79a13b3448ff517eaa1a32ab5506db735d83c92as            if (style == null) {
7329629be70863521bead138c295351f3dec926ab1Andrey Somov                style = chooseScalarStyle();
7339629be70863521bead138c295351f3dec926ab1Andrey Somov            }
73425ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov            if ((!canonical || tag == null) && ((style == null && ev.getImplicit()
7358a6a5222f082f91aa99d15062e7ec8cd52125603Andrey Somov                    .canOmitTagInPlainScalar()) || (style != null && ev.getImplicit()
73625ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                    .canOmitTagInNonPlainScalar()))) {
7379629be70863521bead138c295351f3dec926ab1Andrey Somov                preparedTag = null;
7389629be70863521bead138c295351f3dec926ab1Andrey Somov                return;
7399629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7408a6a5222f082f91aa99d15062e7ec8cd52125603Andrey Somov            if (ev.getImplicit().canOmitTagInPlainScalar() && tag == null) {
7419629be70863521bead138c295351f3dec926ab1Andrey Somov                tag = "!";
7429629be70863521bead138c295351f3dec926ab1Andrey Somov                preparedTag = null;
7439629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7449629be70863521bead138c295351f3dec926ab1Andrey Somov        } else {
7459629be70863521bead138c295351f3dec926ab1Andrey Somov            CollectionStartEvent ev = (CollectionStartEvent) event;
7469629be70863521bead138c295351f3dec926ab1Andrey Somov            tag = ev.getTag();
7479629be70863521bead138c295351f3dec926ab1Andrey Somov            if ((!canonical || tag == null) && ev.getImplicit()) {
7489629be70863521bead138c295351f3dec926ab1Andrey Somov                preparedTag = null;
7499629be70863521bead138c295351f3dec926ab1Andrey Somov                return;
7509629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7519629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7529629be70863521bead138c295351f3dec926ab1Andrey Somov        if (tag == null) {
7539629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("tag is not specified");
7549629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7559629be70863521bead138c295351f3dec926ab1Andrey Somov        if (preparedTag == null) {
7569629be70863521bead138c295351f3dec926ab1Andrey Somov            preparedTag = prepareTag(tag);
7579629be70863521bead138c295351f3dec926ab1Andrey Somov        }
758d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        writeIndicator(preparedTag, true, false, false);
7599629be70863521bead138c295351f3dec926ab1Andrey Somov        preparedTag = null;
7609629be70863521bead138c295351f3dec926ab1Andrey Somov    }
7619629be70863521bead138c295351f3dec926ab1Andrey Somov
762d79a13b3448ff517eaa1a32ab5506db735d83c92as    private Character chooseScalarStyle() {
7639629be70863521bead138c295351f3dec926ab1Andrey Somov        ScalarEvent ev = (ScalarEvent) event;
7649629be70863521bead138c295351f3dec926ab1Andrey Somov        if (analysis == null) {
7659629be70863521bead138c295351f3dec926ab1Andrey Somov            analysis = analyzeScalar(ev.getValue());
7669629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7679629be70863521bead138c295351f3dec926ab1Andrey Somov        if (ev.getStyle() != null && ev.getStyle() == '"' || this.canonical) {
7689629be70863521bead138c295351f3dec926ab1Andrey Somov            return '"';
7699629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7708a6a5222f082f91aa99d15062e7ec8cd52125603Andrey Somov        if (ev.getStyle() == null && ev.getImplicit().canOmitTagInPlainScalar()) {
7719629be70863521bead138c295351f3dec926ab1Andrey Somov            if (!(simpleKeyContext && (analysis.empty || analysis.multiline))
7729629be70863521bead138c295351f3dec926ab1Andrey Somov                    && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) {
773d79a13b3448ff517eaa1a32ab5506db735d83c92as                return null;
7749629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7759629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7769629be70863521bead138c295351f3dec926ab1Andrey Somov        if (ev.getStyle() != null && (ev.getStyle() == '|' || ev.getStyle() == '>')) {
7779629be70863521bead138c295351f3dec926ab1Andrey Somov            if (flowLevel == 0 && !simpleKeyContext && analysis.allowBlock) {
7789629be70863521bead138c295351f3dec926ab1Andrey Somov                return ev.getStyle();
7799629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7809629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7819629be70863521bead138c295351f3dec926ab1Andrey Somov        if (ev.getStyle() == null || ev.getStyle() == '\'') {
7829629be70863521bead138c295351f3dec926ab1Andrey Somov            if (analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline)) {
7839629be70863521bead138c295351f3dec926ab1Andrey Somov                return '\'';
7849629be70863521bead138c295351f3dec926ab1Andrey Somov            }
7859629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7869629be70863521bead138c295351f3dec926ab1Andrey Somov        return '"';
7879629be70863521bead138c295351f3dec926ab1Andrey Somov    }
7889629be70863521bead138c295351f3dec926ab1Andrey Somov
7899629be70863521bead138c295351f3dec926ab1Andrey Somov    private void processScalar() throws IOException {
7909629be70863521bead138c295351f3dec926ab1Andrey Somov        ScalarEvent ev = (ScalarEvent) event;
7919629be70863521bead138c295351f3dec926ab1Andrey Somov        if (analysis == null) {
7929629be70863521bead138c295351f3dec926ab1Andrey Somov            analysis = analyzeScalar(ev.getValue());
7939629be70863521bead138c295351f3dec926ab1Andrey Somov        }
794d79a13b3448ff517eaa1a32ab5506db735d83c92as        if (style == null) {
7959629be70863521bead138c295351f3dec926ab1Andrey Somov            style = chooseScalarStyle();
7969629be70863521bead138c295351f3dec926ab1Andrey Somov        }
7977fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela        boolean split = !simpleKeyContext && splitLines;
798d79a13b3448ff517eaa1a32ab5506db735d83c92as        if (style == null) {
7999629be70863521bead138c295351f3dec926ab1Andrey Somov            writePlain(analysis.scalar, split);
800d79a13b3448ff517eaa1a32ab5506db735d83c92as        } else {
801d79a13b3448ff517eaa1a32ab5506db735d83c92as            switch (style) {
802d79a13b3448ff517eaa1a32ab5506db735d83c92as            case '"':
803d79a13b3448ff517eaa1a32ab5506db735d83c92as                writeDoubleQuoted(analysis.scalar, split);
804d79a13b3448ff517eaa1a32ab5506db735d83c92as                break;
805d79a13b3448ff517eaa1a32ab5506db735d83c92as            case '\'':
806d79a13b3448ff517eaa1a32ab5506db735d83c92as                writeSingleQuoted(analysis.scalar, split);
807d79a13b3448ff517eaa1a32ab5506db735d83c92as                break;
808d79a13b3448ff517eaa1a32ab5506db735d83c92as            case '>':
8097fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela                writeFolded(analysis.scalar, split);
810d79a13b3448ff517eaa1a32ab5506db735d83c92as                break;
811d79a13b3448ff517eaa1a32ab5506db735d83c92as            case '|':
812d79a13b3448ff517eaa1a32ab5506db735d83c92as                writeLiteral(analysis.scalar);
813d79a13b3448ff517eaa1a32ab5506db735d83c92as                break;
81434f59d77fd0a29b085d4c642eb1db6b8925fc3c3Andrey Somov            default:
81534f59d77fd0a29b085d4c642eb1db6b8925fc3c3Andrey Somov                throw new YAMLException("Unexpected style: " + style);
816d79a13b3448ff517eaa1a32ab5506db735d83c92as            }
8179629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8189629be70863521bead138c295351f3dec926ab1Andrey Somov        analysis = null;
819d79a13b3448ff517eaa1a32ab5506db735d83c92as        style = null;
8209629be70863521bead138c295351f3dec926ab1Andrey Somov    }
8219629be70863521bead138c295351f3dec926ab1Andrey Somov
8229629be70863521bead138c295351f3dec926ab1Andrey Somov    // Analyzers.
82365df777c9b28ba6d5c4d3ac5a437280bf112f622Andrey Somov
8245476b912d698337c3bd69e38cadf15f60986e269Andrey Somov    private String prepareVersion(Version version) {
825591473df603cfa63f199ee5f7a75e57f39cf2840Andrey Somov        if (version.major() != 1) {
8265476b912d698337c3bd69e38cadf15f60986e269Andrey Somov            throw new EmitterException("unsupported YAML version: " + version);
8279629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8285476b912d698337c3bd69e38cadf15f60986e269Andrey Somov        return version.getRepresentation();
8299629be70863521bead138c295351f3dec926ab1Andrey Somov    }
8309629be70863521bead138c295351f3dec926ab1Andrey Somov
8319629be70863521bead138c295351f3dec926ab1Andrey Somov    private final static Pattern HANDLE_FORMAT = Pattern.compile("^![-_\\w]*!$");
8329629be70863521bead138c295351f3dec926ab1Andrey Somov
8339629be70863521bead138c295351f3dec926ab1Andrey Somov    private String prepareTagHandle(String handle) {
834d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (handle.length() == 0) {
8359629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("tag handle must not be empty");
8369629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (handle.charAt(0) != '!' || handle.charAt(handle.length() - 1) != '!') {
8379629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("tag handle must start and end with '!': " + handle);
8389629be70863521bead138c295351f3dec926ab1Andrey Somov        } else if (!"!".equals(handle) && !HANDLE_FORMAT.matcher(handle).matches()) {
8399629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("invalid character in the tag handle: " + handle);
8409629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8419629be70863521bead138c295351f3dec926ab1Andrey Somov        return handle;
8429629be70863521bead138c295351f3dec926ab1Andrey Somov    }
8439629be70863521bead138c295351f3dec926ab1Andrey Somov
8449629be70863521bead138c295351f3dec926ab1Andrey Somov    private String prepareTagPrefix(String prefix) {
845d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (prefix.length() == 0) {
8469629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("tag prefix must not be empty");
8479629be70863521bead138c295351f3dec926ab1Andrey Somov        }
848c5335b097ea783bac0429e0393e01c5450e479a6Andrey Somov        StringBuilder chunks = new StringBuilder();
8499629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0;
8509629be70863521bead138c295351f3dec926ab1Andrey Somov        int end = 0;
8519629be70863521bead138c295351f3dec926ab1Andrey Somov        if (prefix.charAt(0) == '!') {
8529629be70863521bead138c295351f3dec926ab1Andrey Somov            end = 1;
8539629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8549629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end < prefix.length()) {
8559629be70863521bead138c295351f3dec926ab1Andrey Somov            end++;
8569629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8579629be70863521bead138c295351f3dec926ab1Andrey Somov        if (start < end) {
8589629be70863521bead138c295351f3dec926ab1Andrey Somov            chunks.append(prefix.substring(start, end));
8599629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8609629be70863521bead138c295351f3dec926ab1Andrey Somov        return chunks.toString();
8619629be70863521bead138c295351f3dec926ab1Andrey Somov    }
8629629be70863521bead138c295351f3dec926ab1Andrey Somov
8639629be70863521bead138c295351f3dec926ab1Andrey Somov    private String prepareTag(String tag) {
864d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (tag.length() == 0) {
8659629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("tag must not be empty");
8669629be70863521bead138c295351f3dec926ab1Andrey Somov        }
867a5424f52a32314d70897f06a0aec907cf9231963Andrey Somov        if ("!".equals(tag)) {
8689629be70863521bead138c295351f3dec926ab1Andrey Somov            return tag;
8699629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8709629be70863521bead138c295351f3dec926ab1Andrey Somov        String handle = null;
8719629be70863521bead138c295351f3dec926ab1Andrey Somov        String suffix = tag;
87234f59d77fd0a29b085d4c642eb1db6b8925fc3c3Andrey Somov        // shall the tag prefixes be sorted as in PyYAML?
8739629be70863521bead138c295351f3dec926ab1Andrey Somov        for (String prefix : tagPrefixes.keySet()) {
874a5424f52a32314d70897f06a0aec907cf9231963Andrey Somov            if (tag.startsWith(prefix) && ("!".equals(prefix) || prefix.length() < tag.length())) {
875370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                handle = prefix;
8769629be70863521bead138c295351f3dec926ab1Andrey Somov            }
8779629be70863521bead138c295351f3dec926ab1Andrey Somov        }
878370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        if (handle != null) {
879370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            suffix = tag.substring(handle.length());
880370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            handle = tagPrefixes.get(handle);
8819629be70863521bead138c295351f3dec926ab1Andrey Somov        }
882370faf2dd7606d5fe2f887012be73d40fa531559maslovalex
883370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        int end = suffix.length();
884370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        String suffixText = end > 0 ? suffix.substring(0, end) : "";
885370faf2dd7606d5fe2f887012be73d40fa531559maslovalex
8869629be70863521bead138c295351f3dec926ab1Andrey Somov        if (handle != null) {
8879629be70863521bead138c295351f3dec926ab1Andrey Somov            return handle + suffixText;
8889629be70863521bead138c295351f3dec926ab1Andrey Somov        }
889370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        return "!<" + suffixText + ">";
8909629be70863521bead138c295351f3dec926ab1Andrey Somov    }
8919629be70863521bead138c295351f3dec926ab1Andrey Somov
8929629be70863521bead138c295351f3dec926ab1Andrey Somov    private final static Pattern ANCHOR_FORMAT = Pattern.compile("^[-_\\w]*$");
8939629be70863521bead138c295351f3dec926ab1Andrey Somov
8949629be70863521bead138c295351f3dec926ab1Andrey Somov    static String prepareAnchor(String anchor) {
895d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (anchor.length() == 0) {
8969629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("anchor must not be empty");
8979629be70863521bead138c295351f3dec926ab1Andrey Somov        }
8989629be70863521bead138c295351f3dec926ab1Andrey Somov        if (!ANCHOR_FORMAT.matcher(anchor).matches()) {
8999629be70863521bead138c295351f3dec926ab1Andrey Somov            throw new EmitterException("invalid character in the anchor: " + anchor);
9009629be70863521bead138c295351f3dec926ab1Andrey Somov        }
9019629be70863521bead138c295351f3dec926ab1Andrey Somov        return anchor;
9029629be70863521bead138c295351f3dec926ab1Andrey Somov    }
9039629be70863521bead138c295351f3dec926ab1Andrey Somov
9049629be70863521bead138c295351f3dec926ab1Andrey Somov    private ScalarAnalysis analyzeScalar(String scalar) {
9059629be70863521bead138c295351f3dec926ab1Andrey Somov        // Empty scalar is a special case.
906d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (scalar.length() == 0) {
90787b055d1b6548ca3c6a605634b05b86134655a58Andrey Somov            return new ScalarAnalysis(scalar, true, false, false, true, true, false);
9089629be70863521bead138c295351f3dec926ab1Andrey Somov        }
9099629be70863521bead138c295351f3dec926ab1Andrey Somov        // Indicators and special characters.
9109629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean blockIndicators = false;
9119629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean flowIndicators = false;
9129629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean lineBreaks = false;
9139629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean specialCharacters = false;
9149629be70863521bead138c295351f3dec926ab1Andrey Somov
9159629be70863521bead138c295351f3dec926ab1Andrey Somov        // Important whitespace combinations.
9169629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean leadingSpace = false;
9179629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean leadingBreak = false;
9189629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean trailingSpace = false;
9199629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean trailingBreak = false;
9209629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean breakSpace = false;
9219629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean spaceBreak = false;
9229629be70863521bead138c295351f3dec926ab1Andrey Somov
9239629be70863521bead138c295351f3dec926ab1Andrey Somov        // Check document indicators.
9249629be70863521bead138c295351f3dec926ab1Andrey Somov        if (scalar.startsWith("---") || scalar.startsWith("...")) {
9259629be70863521bead138c295351f3dec926ab1Andrey Somov            blockIndicators = true;
9269629be70863521bead138c295351f3dec926ab1Andrey Somov            flowIndicators = true;
9279629be70863521bead138c295351f3dec926ab1Andrey Somov        }
9289629be70863521bead138c295351f3dec926ab1Andrey Somov        // First character or preceded by a whitespace.
9299629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean preceededByWhitespace = true;
93025ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov        boolean followedByWhitespace = scalar.length() == 1 || Constant.NULL_BL_T_LINEBR.has(scalar.charAt(1));
9319629be70863521bead138c295351f3dec926ab1Andrey Somov        // The previous character is a space.
9329629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean previousSpace = false;
9339629be70863521bead138c295351f3dec926ab1Andrey Somov
9349629be70863521bead138c295351f3dec926ab1Andrey Somov        // The previous character is a break.
9359629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean previousBreak = false;
9369629be70863521bead138c295351f3dec926ab1Andrey Somov
9379629be70863521bead138c295351f3dec926ab1Andrey Somov        int index = 0;
9389629be70863521bead138c295351f3dec926ab1Andrey Somov
9399629be70863521bead138c295351f3dec926ab1Andrey Somov        while (index < scalar.length()) {
9409629be70863521bead138c295351f3dec926ab1Andrey Somov            char ch = scalar.charAt(index);
9419629be70863521bead138c295351f3dec926ab1Andrey Somov            // Check for indicators.
9429629be70863521bead138c295351f3dec926ab1Andrey Somov            if (index == 0) {
9439629be70863521bead138c295351f3dec926ab1Andrey Somov                // Leading indicators are special characters.
9449629be70863521bead138c295351f3dec926ab1Andrey Somov                if ("#,[]{}&*!|>\'\"%@`".indexOf(ch) != -1) {
9459629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9469629be70863521bead138c295351f3dec926ab1Andrey Somov                    blockIndicators = true;
9479629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9489629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch == '?' || ch == ':') {
9499629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9509629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (followedByWhitespace) {
9519629be70863521bead138c295351f3dec926ab1Andrey Somov                        blockIndicators = true;
9529629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
9539629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9549629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch == '-' && followedByWhitespace) {
9559629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9569629be70863521bead138c295351f3dec926ab1Andrey Somov                    blockIndicators = true;
9579629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9589629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
9599629be70863521bead138c295351f3dec926ab1Andrey Somov                // Some indicators cannot appear within a scalar as well.
9609629be70863521bead138c295351f3dec926ab1Andrey Somov                if (",?[]{}".indexOf(ch) != -1) {
9619629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9629629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9639629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch == ':') {
9649629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9659629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (followedByWhitespace) {
9669629be70863521bead138c295351f3dec926ab1Andrey Somov                        blockIndicators = true;
9679629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
9689629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9699629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch == '#' && preceededByWhitespace) {
9709629be70863521bead138c295351f3dec926ab1Andrey Somov                    flowIndicators = true;
9719629be70863521bead138c295351f3dec926ab1Andrey Somov                    blockIndicators = true;
9729629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9739629be70863521bead138c295351f3dec926ab1Andrey Somov            }
9749629be70863521bead138c295351f3dec926ab1Andrey Somov            // Check for line breaks, special, and unicode characters.
975a6964fe528dbb6897698e75e3c0c6056f22b3e49Andrey Somov            boolean isLineBreak = Constant.LINEBR.has(ch);
976a6964fe528dbb6897698e75e3c0c6056f22b3e49Andrey Somov            if (isLineBreak) {
9779629be70863521bead138c295351f3dec926ab1Andrey Somov                lineBreaks = true;
9789629be70863521bead138c295351f3dec926ab1Andrey Somov            }
9799629be70863521bead138c295351f3dec926ab1Andrey Somov            if (!(ch == '\n' || ('\u0020' <= ch && ch <= '\u007E'))) {
9809629be70863521bead138c295351f3dec926ab1Andrey Somov                if ((ch == '\u0085' || ('\u00A0' <= ch && ch <= '\uD7FF') || ('\uE000' <= ch && ch <= '\uFFFD'))
9819629be70863521bead138c295351f3dec926ab1Andrey Somov                        && (ch != '\uFEFF')) {
9829629be70863521bead138c295351f3dec926ab1Andrey Somov                    // unicode is used
9839629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (!this.allowUnicode) {
9849629be70863521bead138c295351f3dec926ab1Andrey Somov                        specialCharacters = true;
9859629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
9869629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
9879629be70863521bead138c295351f3dec926ab1Andrey Somov                    specialCharacters = true;
9889629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9899629be70863521bead138c295351f3dec926ab1Andrey Somov            }
9909629be70863521bead138c295351f3dec926ab1Andrey Somov            // Detect important whitespace combinations.
9919629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch == ' ') {
9929629be70863521bead138c295351f3dec926ab1Andrey Somov                if (index == 0) {
9939629be70863521bead138c295351f3dec926ab1Andrey Somov                    leadingSpace = true;
9949629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9959629be70863521bead138c295351f3dec926ab1Andrey Somov                if (index == scalar.length() - 1) {
9969629be70863521bead138c295351f3dec926ab1Andrey Somov                    trailingSpace = true;
9979629be70863521bead138c295351f3dec926ab1Andrey Somov                }
9989629be70863521bead138c295351f3dec926ab1Andrey Somov                if (previousBreak) {
9999629be70863521bead138c295351f3dec926ab1Andrey Somov                    breakSpace = true;
10009629be70863521bead138c295351f3dec926ab1Andrey Somov                }
10019629be70863521bead138c295351f3dec926ab1Andrey Somov                previousSpace = true;
10029629be70863521bead138c295351f3dec926ab1Andrey Somov                previousBreak = false;
1003a6964fe528dbb6897698e75e3c0c6056f22b3e49Andrey Somov            } else if (isLineBreak) {
10049629be70863521bead138c295351f3dec926ab1Andrey Somov                if (index == 0) {
10059629be70863521bead138c295351f3dec926ab1Andrey Somov                    leadingBreak = true;
10069629be70863521bead138c295351f3dec926ab1Andrey Somov                }
10079629be70863521bead138c295351f3dec926ab1Andrey Somov                if (index == scalar.length() - 1) {
10089629be70863521bead138c295351f3dec926ab1Andrey Somov                    trailingBreak = true;
10099629be70863521bead138c295351f3dec926ab1Andrey Somov                }
10109629be70863521bead138c295351f3dec926ab1Andrey Somov                if (previousSpace) {
10119629be70863521bead138c295351f3dec926ab1Andrey Somov                    spaceBreak = true;
10129629be70863521bead138c295351f3dec926ab1Andrey Somov                }
10139629be70863521bead138c295351f3dec926ab1Andrey Somov                previousSpace = false;
10149629be70863521bead138c295351f3dec926ab1Andrey Somov                previousBreak = true;
10159629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
10169629be70863521bead138c295351f3dec926ab1Andrey Somov                previousSpace = false;
10179629be70863521bead138c295351f3dec926ab1Andrey Somov                previousBreak = false;
10189629be70863521bead138c295351f3dec926ab1Andrey Somov            }
10199629be70863521bead138c295351f3dec926ab1Andrey Somov
10209629be70863521bead138c295351f3dec926ab1Andrey Somov            // Prepare for the next character.
10219629be70863521bead138c295351f3dec926ab1Andrey Somov            index++;
1022a6964fe528dbb6897698e75e3c0c6056f22b3e49Andrey Somov            preceededByWhitespace = Constant.NULL_BL_T.has(ch) || isLineBreak;
102325ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov            followedByWhitespace = index + 1 >= scalar.length()
102425ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                    || (Constant.NULL_BL_T.has(scalar.charAt(index + 1))) || isLineBreak;
10259629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10269629be70863521bead138c295351f3dec926ab1Andrey Somov        // Let's decide what styles are allowed.
10279629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean allowFlowPlain = true;
10289629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean allowBlockPlain = true;
10299629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean allowSingleQuoted = true;
10309629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean allowBlock = true;
10319629be70863521bead138c295351f3dec926ab1Andrey Somov        // Leading and trailing whitespaces are bad for plain scalars.
10329629be70863521bead138c295351f3dec926ab1Andrey Somov        if (leadingSpace || leadingBreak || trailingSpace || trailingBreak) {
10339629be70863521bead138c295351f3dec926ab1Andrey Somov            allowFlowPlain = allowBlockPlain = false;
10349629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10359629be70863521bead138c295351f3dec926ab1Andrey Somov        // We do not permit trailing spaces for block scalars.
10369629be70863521bead138c295351f3dec926ab1Andrey Somov        if (trailingSpace) {
10379629be70863521bead138c295351f3dec926ab1Andrey Somov            allowBlock = false;
10389629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10399629be70863521bead138c295351f3dec926ab1Andrey Somov        // Spaces at the beginning of a new line are only acceptable for block
10409629be70863521bead138c295351f3dec926ab1Andrey Somov        // scalars.
10419629be70863521bead138c295351f3dec926ab1Andrey Somov        if (breakSpace) {
10429629be70863521bead138c295351f3dec926ab1Andrey Somov            allowFlowPlain = allowBlockPlain = allowSingleQuoted = false;
10439629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10449629be70863521bead138c295351f3dec926ab1Andrey Somov        // Spaces followed by breaks, as well as special character are only
10459629be70863521bead138c295351f3dec926ab1Andrey Somov        // allowed for double quoted scalars.
10469629be70863521bead138c295351f3dec926ab1Andrey Somov        if (spaceBreak || specialCharacters) {
10479629be70863521bead138c295351f3dec926ab1Andrey Somov            allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false;
10489629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10499629be70863521bead138c295351f3dec926ab1Andrey Somov        // Although the plain scalar writer supports breaks, we never emit
1050a3e7b19e567f571f6be130b0b8779d7eb40c88e0Andrey Somov        // multiline plain scalars in the flow context.
10519629be70863521bead138c295351f3dec926ab1Andrey Somov        if (lineBreaks) {
1052a3e7b19e567f571f6be130b0b8779d7eb40c88e0Andrey Somov            allowFlowPlain = false;
10539629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10549629be70863521bead138c295351f3dec926ab1Andrey Somov        // Flow indicators are forbidden for flow plain scalars.
10559629be70863521bead138c295351f3dec926ab1Andrey Somov        if (flowIndicators) {
10569629be70863521bead138c295351f3dec926ab1Andrey Somov            allowFlowPlain = false;
10579629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10589629be70863521bead138c295351f3dec926ab1Andrey Somov        // Block indicators are forbidden for block plain scalars.
10599629be70863521bead138c295351f3dec926ab1Andrey Somov        if (blockIndicators) {
10609629be70863521bead138c295351f3dec926ab1Andrey Somov            allowBlockPlain = false;
10619629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10629629be70863521bead138c295351f3dec926ab1Andrey Somov
10639629be70863521bead138c295351f3dec926ab1Andrey Somov        return new ScalarAnalysis(scalar, false, lineBreaks, allowFlowPlain, allowBlockPlain,
106487b055d1b6548ca3c6a605634b05b86134655a58Andrey Somov                allowSingleQuoted, allowBlock);
10659629be70863521bead138c295351f3dec926ab1Andrey Somov    }
10669629be70863521bead138c295351f3dec926ab1Andrey Somov
10679629be70863521bead138c295351f3dec926ab1Andrey Somov    // Writers.
10689629be70863521bead138c295351f3dec926ab1Andrey Somov
10699629be70863521bead138c295351f3dec926ab1Andrey Somov    void flushStream() throws IOException {
10709629be70863521bead138c295351f3dec926ab1Andrey Somov        stream.flush();
10719629be70863521bead138c295351f3dec926ab1Andrey Somov    }
10729629be70863521bead138c295351f3dec926ab1Andrey Somov
10739629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeStreamStart() {
10749629be70863521bead138c295351f3dec926ab1Andrey Somov        // BOM is written by Writer.
10759629be70863521bead138c295351f3dec926ab1Andrey Somov    }
10769629be70863521bead138c295351f3dec926ab1Andrey Somov
10779629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeStreamEnd() throws IOException {
10789629be70863521bead138c295351f3dec926ab1Andrey Somov        flushStream();
10799629be70863521bead138c295351f3dec926ab1Andrey Somov    }
10809629be70863521bead138c295351f3dec926ab1Andrey Somov
10819629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeIndicator(String indicator, boolean needWhitespace, boolean whitespace,
10829629be70863521bead138c295351f3dec926ab1Andrey Somov            boolean indentation) throws IOException {
1083370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        if (!this.whitespace && needWhitespace) {
1084370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            this.column++;
1085370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            stream.write(SPACE);
10869629be70863521bead138c295351f3dec926ab1Andrey Somov        }
10879629be70863521bead138c295351f3dec926ab1Andrey Somov        this.whitespace = whitespace;
10889629be70863521bead138c295351f3dec926ab1Andrey Somov        this.indention = this.indention && indentation;
1089370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        this.column += indicator.length();
10909629be70863521bead138c295351f3dec926ab1Andrey Somov        openEnded = false;
1091370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write(indicator);
10929629be70863521bead138c295351f3dec926ab1Andrey Somov    }
10939629be70863521bead138c295351f3dec926ab1Andrey Somov
10949629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeIndent() throws IOException {
10959629be70863521bead138c295351f3dec926ab1Andrey Somov        int indent;
10969629be70863521bead138c295351f3dec926ab1Andrey Somov        if (this.indent != null) {
10979629be70863521bead138c295351f3dec926ab1Andrey Somov            indent = this.indent;
10989629be70863521bead138c295351f3dec926ab1Andrey Somov        } else {
10999629be70863521bead138c295351f3dec926ab1Andrey Somov            indent = 0;
11009629be70863521bead138c295351f3dec926ab1Andrey Somov        }
11019629be70863521bead138c295351f3dec926ab1Andrey Somov
11029629be70863521bead138c295351f3dec926ab1Andrey Somov        if (!this.indention || this.column > indent || (this.column == indent && !this.whitespace)) {
11039629be70863521bead138c295351f3dec926ab1Andrey Somov            writeLineBreak(null);
11049629be70863521bead138c295351f3dec926ab1Andrey Somov        }
11059629be70863521bead138c295351f3dec926ab1Andrey Somov
1106d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        writeWhitespace(indent - this.column);
1107d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha    }
1108d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha
1109d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha    private void writeWhitespace(int length) throws IOException {
1110d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        if (length <= 0) {
1111d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha            return;
1112d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        }
1113d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        this.whitespace = true;
1114d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        char[] data = new char[length];
1115d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        for (int i = 0; i < data.length; i++) {
1116d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha            data[i] = ' ';
11179629be70863521bead138c295351f3dec926ab1Andrey Somov        }
1118d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        this.column += length;
1119d1dd873170bcc2a9df0a512ae5fa5f073a8b64cftareq sha        stream.write(data);
11209629be70863521bead138c295351f3dec926ab1Andrey Somov    }
11219629be70863521bead138c295351f3dec926ab1Andrey Somov
11229629be70863521bead138c295351f3dec926ab1Andrey Somov    private void writeLineBreak(String data) throws IOException {
11239629be70863521bead138c295351f3dec926ab1Andrey Somov        this.whitespace = true;
11249629be70863521bead138c295351f3dec926ab1Andrey Somov        this.indention = true;
11259629be70863521bead138c295351f3dec926ab1Andrey Somov        this.column = 0;
1126370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        if (data == null) {
1127370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            stream.write(this.bestLineBreak);
1128370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        } else {
1129370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            stream.write(data);
1130370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        }
11319629be70863521bead138c295351f3dec926ab1Andrey Somov    }
11329629be70863521bead138c295351f3dec926ab1Andrey Somov
11339629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeVersionDirective(String versionText) throws IOException {
1134370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write("%YAML ");
1135370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write(versionText);
11369629be70863521bead138c295351f3dec926ab1Andrey Somov        writeLineBreak(null);
11379629be70863521bead138c295351f3dec926ab1Andrey Somov    }
11389629be70863521bead138c295351f3dec926ab1Andrey Somov
11399629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeTagDirective(String handleText, String prefixText) throws IOException {
114065df777c9b28ba6d5c4d3ac5a437280bf112f622Andrey Somov        // XXX: not sure 4 invocations better then StringBuilders created by str
114165df777c9b28ba6d5c4d3ac5a437280bf112f622Andrey Somov        // + str
1142370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write("%TAG ");
1143370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write(handleText);
1144370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write(SPACE);
1145370faf2dd7606d5fe2f887012be73d40fa531559maslovalex        stream.write(prefixText);
11469629be70863521bead138c295351f3dec926ab1Andrey Somov        writeLineBreak(null);
11479629be70863521bead138c295351f3dec926ab1Andrey Somov    }
11489629be70863521bead138c295351f3dec926ab1Andrey Somov
11499629be70863521bead138c295351f3dec926ab1Andrey Somov    // Scalar streams.
11509629be70863521bead138c295351f3dec926ab1Andrey Somov    private void writeSingleQuoted(String text, boolean split) throws IOException {
11519629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("'", true, false, false);
11529629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean spaces = false;
11539629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean breaks = false;
11549629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0, end = 0;
11559629be70863521bead138c295351f3dec926ab1Andrey Somov        char ch;
11569629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end <= text.length()) {
11579629be70863521bead138c295351f3dec926ab1Andrey Somov            ch = 0;
11589629be70863521bead138c295351f3dec926ab1Andrey Somov            if (end < text.length()) {
11599629be70863521bead138c295351f3dec926ab1Andrey Somov                ch = text.charAt(end);
11609629be70863521bead138c295351f3dec926ab1Andrey Somov            }
11619629be70863521bead138c295351f3dec926ab1Andrey Somov            if (spaces) {
11629629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch == 0 || ch != ' ') {
11639629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (start + 1 == end && this.column > this.bestWidth && split && start != 0
11649629be70863521bead138c295351f3dec926ab1Andrey Somov                            && end != text.length()) {
11659629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
11669629be70863521bead138c295351f3dec926ab1Andrey Somov                    } else {
1167370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        int len = end - start;
1168370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        this.column += len;
1169370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        stream.write(text, start, len);
11709629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
11719629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
11729629be70863521bead138c295351f3dec926ab1Andrey Somov                }
11739629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (breaks) {
1174b693d5a6b13314c20bee0e912ed209af6e0499efAndrey Somov                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
11759629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (text.charAt(start) == '\n') {
11769629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeLineBreak(null);
11779629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
11789629be70863521bead138c295351f3dec926ab1Andrey Somov                    String data = text.substring(start, end);
11799629be70863521bead138c295351f3dec926ab1Andrey Somov                    for (char br : data.toCharArray()) {
11809629be70863521bead138c295351f3dec926ab1Andrey Somov                        if (br == '\n') {
11819629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(null);
11829629be70863521bead138c295351f3dec926ab1Andrey Somov                        } else {
11839629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(String.valueOf(br));
11849629be70863521bead138c295351f3dec926ab1Andrey Somov                        }
11859629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
11869629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
11879629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
11889629be70863521bead138c295351f3dec926ab1Andrey Somov                }
11899629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
11908e50e7a86555d3ef47829d853d254eace03e8157Andrey Somov                if (Constant.LINEBR.has(ch, "\0 \'")) {
11919629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (start < end) {
1192370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        int len = end - start;
1193370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        this.column += len;
1194370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        stream.write(text, start, len);
11959629be70863521bead138c295351f3dec926ab1Andrey Somov                        start = end;
11969629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
11979629be70863521bead138c295351f3dec926ab1Andrey Somov                }
11989629be70863521bead138c295351f3dec926ab1Andrey Somov            }
11999629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch == '\'') {
12009629be70863521bead138c295351f3dec926ab1Andrey Somov                this.column += 2;
1201370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                stream.write("''");
12029629be70863521bead138c295351f3dec926ab1Andrey Somov                start = end + 1;
12039629be70863521bead138c295351f3dec926ab1Andrey Somov            }
12049629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch != 0) {
12059629be70863521bead138c295351f3dec926ab1Andrey Somov                spaces = ch == ' ';
120665619eff85ddfd768e973825b34b2ed8bbde537dAndrey Somov                breaks = Constant.LINEBR.has(ch);
12079629be70863521bead138c295351f3dec926ab1Andrey Somov            }
12089629be70863521bead138c295351f3dec926ab1Andrey Somov            end++;
12099629be70863521bead138c295351f3dec926ab1Andrey Somov        }
12109629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("'", false, false, false);
12119629be70863521bead138c295351f3dec926ab1Andrey Somov    }
12129629be70863521bead138c295351f3dec926ab1Andrey Somov
12139629be70863521bead138c295351f3dec926ab1Andrey Somov    private void writeDoubleQuoted(String text, boolean split) throws IOException {
12149629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("\"", true, false, false);
12159629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0;
12169629be70863521bead138c295351f3dec926ab1Andrey Somov        int end = 0;
12179629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end <= text.length()) {
12189629be70863521bead138c295351f3dec926ab1Andrey Somov            Character ch = null;
12199629be70863521bead138c295351f3dec926ab1Andrey Somov            if (end < text.length()) {
12209629be70863521bead138c295351f3dec926ab1Andrey Somov                ch = text.charAt(end);
12219629be70863521bead138c295351f3dec926ab1Andrey Somov            }
12229629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch == null || "\"\\\u0085\u2028\u2029\uFEFF".indexOf(ch) != -1
12239629be70863521bead138c295351f3dec926ab1Andrey Somov                    || !('\u0020' <= ch && ch <= '\u007E')) {
12249629be70863521bead138c295351f3dec926ab1Andrey Somov                if (start < end) {
1225370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    int len = end - start;
1226370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    this.column += len;
1227370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    stream.write(text, start, len);
12289629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
12299629be70863521bead138c295351f3dec926ab1Andrey Somov                }
12309629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch != null) {
12319629be70863521bead138c295351f3dec926ab1Andrey Somov                    String data;
1232d4c7e6efc571fe29d05cc8d0f6d0e713e8c4a35fAndrey Somov                    if (ESCAPE_REPLACEMENTS.containsKey(ch)) {
1233d4c7e6efc571fe29d05cc8d0f6d0e713e8c4a35fAndrey Somov                        data = "\\" + ESCAPE_REPLACEMENTS.get(ch);
12347361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                    } else if (!this.allowUnicode || !StreamReader.isPrintable(ch)) {
12357361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                        // if !allowUnicode or the character is not printable,
12367361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                        // we must encode it
123718cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                        if (ch <= '\u00FF') {
123818cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                            String s = "0" + Integer.toString(ch, 16);
123918cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                            data = "\\x" + s.substring(s.length() - 2);
12407361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                        } else if (ch >= '\uD800' && ch <= '\uDBFF') {
12417361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                            if (end + 1 < text.length()) {
12427361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                                Character ch2 = text.charAt(++end);
12437361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                                String s = "000" + Long.toHexString(Character.toCodePoint(ch, ch2));
12447361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                                data = "\\U" + s.substring(s.length() - 8);
12457361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                            } else {
12467361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                                String s = "000" + Integer.toString(ch, 16);
12477361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                                data = "\\u" + s.substring(s.length() - 4);
12487361ced8b2bc1e37c3064923b0ccc124af39b57aAndrey Somov                            }
124918cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                        } else {
125018cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                            String s = "000" + Integer.toString(ch, 16);
125118cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                            data = "\\u" + s.substring(s.length() - 4);
125218cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                        }
12539629be70863521bead138c295351f3dec926ab1Andrey Somov                    } else {
125418cc7805e3e5877e7e8659de766f246e0fc9c414Andrey Somov                        data = String.valueOf(ch);
12559629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
12569629be70863521bead138c295351f3dec926ab1Andrey Somov                    this.column += data.length();
12579629be70863521bead138c295351f3dec926ab1Andrey Somov                    stream.write(data);
12589629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end + 1;
12599629be70863521bead138c295351f3dec926ab1Andrey Somov                }
12609629be70863521bead138c295351f3dec926ab1Andrey Somov            }
12619629be70863521bead138c295351f3dec926ab1Andrey Somov            if ((0 < end && end < (text.length() - 1)) && (ch == ' ' || start >= end)
12629629be70863521bead138c295351f3dec926ab1Andrey Somov                    && (this.column + (end - start)) > this.bestWidth && split) {
12639629be70863521bead138c295351f3dec926ab1Andrey Somov                String data;
12649629be70863521bead138c295351f3dec926ab1Andrey Somov                if (start >= end) {
12659629be70863521bead138c295351f3dec926ab1Andrey Somov                    data = "\\";
12669629be70863521bead138c295351f3dec926ab1Andrey Somov                } else {
12679629be70863521bead138c295351f3dec926ab1Andrey Somov                    data = text.substring(start, end) + "\\";
12689629be70863521bead138c295351f3dec926ab1Andrey Somov                }
12699629be70863521bead138c295351f3dec926ab1Andrey Somov                if (start < end) {
12709629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
12719629be70863521bead138c295351f3dec926ab1Andrey Somov                }
12729629be70863521bead138c295351f3dec926ab1Andrey Somov                this.column += data.length();
12739629be70863521bead138c295351f3dec926ab1Andrey Somov                stream.write(data);
12749629be70863521bead138c295351f3dec926ab1Andrey Somov                writeIndent();
12759629be70863521bead138c295351f3dec926ab1Andrey Somov                this.whitespace = false;
12769629be70863521bead138c295351f3dec926ab1Andrey Somov                this.indention = false;
12779629be70863521bead138c295351f3dec926ab1Andrey Somov                if (text.charAt(start) == ' ') {
12789629be70863521bead138c295351f3dec926ab1Andrey Somov                    data = "\\";
12799629be70863521bead138c295351f3dec926ab1Andrey Somov                    this.column += data.length();
12809629be70863521bead138c295351f3dec926ab1Andrey Somov                    stream.write(data);
12819629be70863521bead138c295351f3dec926ab1Andrey Somov                }
12829629be70863521bead138c295351f3dec926ab1Andrey Somov            }
12839629be70863521bead138c295351f3dec926ab1Andrey Somov            end += 1;
12849629be70863521bead138c295351f3dec926ab1Andrey Somov        }
12859629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("\"", false, false, false);
12869629be70863521bead138c295351f3dec926ab1Andrey Somov    }
12879629be70863521bead138c295351f3dec926ab1Andrey Somov
12889629be70863521bead138c295351f3dec926ab1Andrey Somov    private String determineBlockHints(String text) {
1289c5335b097ea783bac0429e0393e01c5450e479a6Andrey Somov        StringBuilder hints = new StringBuilder();
1290d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (Constant.LINEBR.has(text.charAt(0), " ")) {
1291d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov            hints.append(bestIndent);
1292d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        }
1293d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        char ch1 = text.charAt(text.length() - 1);
1294d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (Constant.LINEBR.hasNo(ch1)) {
1295d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov            hints.append("-");
1296d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        } else if (text.length() == 1 || Constant.LINEBR.has(text.charAt(text.length() - 2))) {
1297d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov            hints.append("+");
12989629be70863521bead138c295351f3dec926ab1Andrey Somov        }
12999629be70863521bead138c295351f3dec926ab1Andrey Somov        return hints.toString();
13009629be70863521bead138c295351f3dec926ab1Andrey Somov    }
13019629be70863521bead138c295351f3dec926ab1Andrey Somov
13027fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela    void writeFolded(String text, boolean split) throws IOException {
13039629be70863521bead138c295351f3dec926ab1Andrey Somov        String hints = determineBlockHints(text);
13049629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator(">" + hints, true, false, false);
13059629be70863521bead138c295351f3dec926ab1Andrey Somov        if (hints.length() > 0 && (hints.charAt(hints.length() - 1) == '+')) {
13069629be70863521bead138c295351f3dec926ab1Andrey Somov            openEnded = true;
13079629be70863521bead138c295351f3dec926ab1Andrey Somov        }
13089629be70863521bead138c295351f3dec926ab1Andrey Somov        writeLineBreak(null);
13099629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean leadingSpace = true;
13109629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean spaces = false;
13119629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean breaks = true;
13129629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0, end = 0;
13139629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end <= text.length()) {
13149629be70863521bead138c295351f3dec926ab1Andrey Somov            char ch = 0;
13159629be70863521bead138c295351f3dec926ab1Andrey Somov            if (end < text.length()) {
13169629be70863521bead138c295351f3dec926ab1Andrey Somov                ch = text.charAt(end);
13179629be70863521bead138c295351f3dec926ab1Andrey Somov            }
13189629be70863521bead138c295351f3dec926ab1Andrey Somov            if (breaks) {
1319b693d5a6b13314c20bee0e912ed209af6e0499efAndrey Somov                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
13209629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (!leadingSpace && ch != 0 && ch != ' ' && text.charAt(start) == '\n') {
13219629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeLineBreak(null);
13229629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
132325ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                    leadingSpace = ch == ' ';
13249629be70863521bead138c295351f3dec926ab1Andrey Somov                    String data = text.substring(start, end);
13259629be70863521bead138c295351f3dec926ab1Andrey Somov                    for (char br : data.toCharArray()) {
13269629be70863521bead138c295351f3dec926ab1Andrey Somov                        if (br == '\n') {
13279629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(null);
13289629be70863521bead138c295351f3dec926ab1Andrey Somov                        } else {
13299629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(String.valueOf(br));
13309629be70863521bead138c295351f3dec926ab1Andrey Somov                        }
13319629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13329629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (ch != 0) {
13339629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
13349629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13359629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
13369629be70863521bead138c295351f3dec926ab1Andrey Somov                }
13379629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (spaces) {
13389629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch != ' ') {
13397fbc6562f9ae67c2b9de8b8c5c74c62c42006cebVille Koskela                    if (start + 1 == end && this.column > this.bestWidth && split) {
13409629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
13419629be70863521bead138c295351f3dec926ab1Andrey Somov                    } else {
1342370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        int len = end - start;
1343370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        this.column += len;
1344370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        stream.write(text, start, len);
13459629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13469629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
13479629be70863521bead138c295351f3dec926ab1Andrey Somov                }
13489629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
13498e50e7a86555d3ef47829d853d254eace03e8157Andrey Somov                if (Constant.LINEBR.has(ch, "\0 ")) {
1350370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    int len = end - start;
1351370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    this.column += len;
1352370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    stream.write(text, start, len);
13539629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (ch == 0) {
13549629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeLineBreak(null);
13559629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13569629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
13579629be70863521bead138c295351f3dec926ab1Andrey Somov                }
13589629be70863521bead138c295351f3dec926ab1Andrey Somov            }
13599629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch != 0) {
136065619eff85ddfd768e973825b34b2ed8bbde537dAndrey Somov                breaks = Constant.LINEBR.has(ch);
136125ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                spaces = ch == ' ';
13629629be70863521bead138c295351f3dec926ab1Andrey Somov            }
13639629be70863521bead138c295351f3dec926ab1Andrey Somov            end++;
13649629be70863521bead138c295351f3dec926ab1Andrey Somov        }
13659629be70863521bead138c295351f3dec926ab1Andrey Somov    }
13669629be70863521bead138c295351f3dec926ab1Andrey Somov
13679629be70863521bead138c295351f3dec926ab1Andrey Somov    void writeLiteral(String text) throws IOException {
13689629be70863521bead138c295351f3dec926ab1Andrey Somov        String hints = determineBlockHints(text);
13699629be70863521bead138c295351f3dec926ab1Andrey Somov        writeIndicator("|" + hints, true, false, false);
13709629be70863521bead138c295351f3dec926ab1Andrey Somov        if (hints.length() > 0 && (hints.charAt(hints.length() - 1)) == '+') {
13719629be70863521bead138c295351f3dec926ab1Andrey Somov            openEnded = true;
13729629be70863521bead138c295351f3dec926ab1Andrey Somov        }
13739629be70863521bead138c295351f3dec926ab1Andrey Somov        writeLineBreak(null);
13749629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean breaks = true;
13759629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0, end = 0;
13769629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end <= text.length()) {
13779629be70863521bead138c295351f3dec926ab1Andrey Somov            char ch = 0;
13789629be70863521bead138c295351f3dec926ab1Andrey Somov            if (end < text.length()) {
13799629be70863521bead138c295351f3dec926ab1Andrey Somov                ch = text.charAt(end);
13809629be70863521bead138c295351f3dec926ab1Andrey Somov            }
13819629be70863521bead138c295351f3dec926ab1Andrey Somov            if (breaks) {
1382b693d5a6b13314c20bee0e912ed209af6e0499efAndrey Somov                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
13839629be70863521bead138c295351f3dec926ab1Andrey Somov                    String data = text.substring(start, end);
13849629be70863521bead138c295351f3dec926ab1Andrey Somov                    for (char br : data.toCharArray()) {
13859629be70863521bead138c295351f3dec926ab1Andrey Somov                        if (br == '\n') {
13869629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(null);
13879629be70863521bead138c295351f3dec926ab1Andrey Somov                        } else {
13889629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(String.valueOf(br));
13899629be70863521bead138c295351f3dec926ab1Andrey Somov                        }
13909629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13919629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (ch != 0) {
13929629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
13939629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
13949629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
13959629be70863521bead138c295351f3dec926ab1Andrey Somov                }
13969629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
139765619eff85ddfd768e973825b34b2ed8bbde537dAndrey Somov                if (ch == 0 || Constant.LINEBR.has(ch)) {
1398370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    stream.write(text, start, end - start);
13999629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (ch == 0) {
14009629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeLineBreak(null);
14019629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
14029629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
14039629be70863521bead138c295351f3dec926ab1Andrey Somov                }
14049629be70863521bead138c295351f3dec926ab1Andrey Somov            }
14059629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch != 0) {
140625ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                breaks = Constant.LINEBR.has(ch);
14079629be70863521bead138c295351f3dec926ab1Andrey Somov            }
14089629be70863521bead138c295351f3dec926ab1Andrey Somov            end++;
14099629be70863521bead138c295351f3dec926ab1Andrey Somov        }
14109629be70863521bead138c295351f3dec926ab1Andrey Somov    }
14119629be70863521bead138c295351f3dec926ab1Andrey Somov
14129629be70863521bead138c295351f3dec926ab1Andrey Somov    void writePlain(String text, boolean split) throws IOException {
14139629be70863521bead138c295351f3dec926ab1Andrey Somov        if (rootContext) {
14149629be70863521bead138c295351f3dec926ab1Andrey Somov            openEnded = true;
14159629be70863521bead138c295351f3dec926ab1Andrey Somov        }
1416d830de2dc8edea813a00c18193802d2bab3fded0Andrey Somov        if (text.length() == 0) {
14179629be70863521bead138c295351f3dec926ab1Andrey Somov            return;
14189629be70863521bead138c295351f3dec926ab1Andrey Somov        }
14199629be70863521bead138c295351f3dec926ab1Andrey Somov        if (!this.whitespace) {
1420370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            this.column++;
1421370faf2dd7606d5fe2f887012be73d40fa531559maslovalex            stream.write(SPACE);
14229629be70863521bead138c295351f3dec926ab1Andrey Somov        }
14239629be70863521bead138c295351f3dec926ab1Andrey Somov        this.whitespace = false;
14249629be70863521bead138c295351f3dec926ab1Andrey Somov        this.indention = false;
14259629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean spaces = false;
14269629be70863521bead138c295351f3dec926ab1Andrey Somov        boolean breaks = false;
14279629be70863521bead138c295351f3dec926ab1Andrey Somov        int start = 0, end = 0;
14289629be70863521bead138c295351f3dec926ab1Andrey Somov        while (end <= text.length()) {
14299629be70863521bead138c295351f3dec926ab1Andrey Somov            char ch = 0;
14309629be70863521bead138c295351f3dec926ab1Andrey Somov            if (end < text.length()) {
14319629be70863521bead138c295351f3dec926ab1Andrey Somov                ch = text.charAt(end);
14329629be70863521bead138c295351f3dec926ab1Andrey Somov            }
14339629be70863521bead138c295351f3dec926ab1Andrey Somov            if (spaces) {
14349629be70863521bead138c295351f3dec926ab1Andrey Somov                if (ch != ' ') {
14359629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (start + 1 == end && this.column > this.bestWidth && split) {
14369629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeIndent();
14379629be70863521bead138c295351f3dec926ab1Andrey Somov                        this.whitespace = false;
14389629be70863521bead138c295351f3dec926ab1Andrey Somov                        this.indention = false;
14399629be70863521bead138c295351f3dec926ab1Andrey Somov                    } else {
1440370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        int len = end - start;
1441370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        this.column += len;
1442370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                        stream.write(text, start, len);
14439629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
14449629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
14459629be70863521bead138c295351f3dec926ab1Andrey Somov                }
14469629be70863521bead138c295351f3dec926ab1Andrey Somov            } else if (breaks) {
1447b693d5a6b13314c20bee0e912ed209af6e0499efAndrey Somov                if (Constant.LINEBR.hasNo(ch)) {
14489629be70863521bead138c295351f3dec926ab1Andrey Somov                    if (text.charAt(start) == '\n') {
14499629be70863521bead138c295351f3dec926ab1Andrey Somov                        writeLineBreak(null);
14509629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
14519629be70863521bead138c295351f3dec926ab1Andrey Somov                    String data = text.substring(start, end);
14529629be70863521bead138c295351f3dec926ab1Andrey Somov                    for (char br : data.toCharArray()) {
14539629be70863521bead138c295351f3dec926ab1Andrey Somov                        if (br == '\n') {
14549629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(null);
14559629be70863521bead138c295351f3dec926ab1Andrey Somov                        } else {
14569629be70863521bead138c295351f3dec926ab1Andrey Somov                            writeLineBreak(String.valueOf(br));
14579629be70863521bead138c295351f3dec926ab1Andrey Somov                        }
14589629be70863521bead138c295351f3dec926ab1Andrey Somov                    }
14599629be70863521bead138c295351f3dec926ab1Andrey Somov                    writeIndent();
14609629be70863521bead138c295351f3dec926ab1Andrey Somov                    this.whitespace = false;
14619629be70863521bead138c295351f3dec926ab1Andrey Somov                    this.indention = false;
14629629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
14639629be70863521bead138c295351f3dec926ab1Andrey Somov                }
14649629be70863521bead138c295351f3dec926ab1Andrey Somov            } else {
146565619eff85ddfd768e973825b34b2ed8bbde537dAndrey Somov                if (ch == 0 || Constant.LINEBR.has(ch)) {
1466370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    int len = end - start;
1467370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    this.column += len;
1468370faf2dd7606d5fe2f887012be73d40fa531559maslovalex                    stream.write(text, start, len);
14699629be70863521bead138c295351f3dec926ab1Andrey Somov                    start = end;
14709629be70863521bead138c295351f3dec926ab1Andrey Somov                }
14719629be70863521bead138c295351f3dec926ab1Andrey Somov            }
14729629be70863521bead138c295351f3dec926ab1Andrey Somov            if (ch != 0) {
147325ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                spaces = ch == ' ';
147425ac3b280b35ddb9167e0528626e2ec14488f173Andrey Somov                breaks = Constant.LINEBR.has(ch);
14759629be70863521bead138c295351f3dec926ab1Andrey Somov            }
14769629be70863521bead138c295351f3dec926ab1Andrey Somov            end++;
14779629be70863521bead138c295351f3dec926ab1Andrey Somov        }
14789629be70863521bead138c295351f3dec926ab1Andrey Somov    }
14799629be70863521bead138c295351f3dec926ab1Andrey Somov}
1480