0001: /***** BEGIN LICENSE BLOCK *****
0002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
0003: *
0004: * The contents of this file are subject to the Common Public
0005: * License Version 1.0 (the "License"); you may not use this file
0006: * except in compliance with the License. You may obtain a copy of
0007: * the License at http://www.eclipse.org/legal/cpl-v10.html
0008: *
0009: * Software distributed under the License is distributed on an "AS
0010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0011: * implied. See the License for the specific language governing
0012: * rights and limitations under the License.
0013: *
0014: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
0015: *
0016: * Alternatively, the contents of this file may be used under the terms of
0017: * either of the GNU General Public License Version 2 or later (the "GPL"),
0018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0019: * in which case the provisions of the GPL or the LGPL are applicable instead
0020: * of those above. If you wish to allow use of your version of this file only
0021: * under the terms of either the GPL or the LGPL, and not to allow others to
0022: * use your version of this file under the terms of the CPL, indicate your
0023: * decision by deleting the provisions above and replace them with the notice
0024: * and other provisions required by the GPL or the LGPL. If you do not delete
0025: * the provisions above, a recipient may use your version of this file under
0026: * the terms of any one of the CPL, the GPL or the LGPL.
0027: ***** END LICENSE BLOCK *****/package org.jvyamlb;
0028:
0029: import java.io.IOException;
0030: import java.io.OutputStream;
0031: import java.io.BufferedInputStream;
0032: import java.io.FileInputStream;
0033:
0034: import java.util.HashMap;
0035: import java.util.List;
0036: import java.util.Map;
0037: import java.util.ArrayList;
0038: import java.util.Iterator;
0039: import java.util.Set;
0040: import java.util.TreeSet;
0041:
0042: import java.util.regex.Pattern;
0043:
0044: import org.jvyamlb.events.Event;
0045: import org.jvyamlb.events.StreamStartEvent;
0046: import org.jvyamlb.events.StreamEndEvent;
0047: import org.jvyamlb.events.DocumentStartEvent;
0048: import org.jvyamlb.events.DocumentEndEvent;
0049: import org.jvyamlb.events.CollectionStartEvent;
0050: import org.jvyamlb.events.CollectionEndEvent;
0051: import org.jvyamlb.events.MappingStartEvent;
0052: import org.jvyamlb.events.SequenceStartEvent;
0053: import org.jvyamlb.events.MappingEndEvent;
0054: import org.jvyamlb.events.SequenceEndEvent;
0055: import org.jvyamlb.events.AliasEvent;
0056: import org.jvyamlb.events.ScalarEvent;
0057: import org.jvyamlb.events.NodeEvent;
0058:
0059: import org.jruby.util.ByteList;
0060:
0061: /**
0062: * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
0063: */
0064: public class EmitterImpl implements Emitter {
0065: private static class ScalarAnalysis {
0066: public ByteList scalar;
0067: public boolean empty;
0068: public boolean multiline;
0069: public boolean allowFlowPlain;
0070: public boolean allowBlockPlain;
0071: public boolean allowSingleQuoted;
0072: public boolean allowDoubleQuoted;
0073: public boolean allowBlock;
0074:
0075: public ScalarAnalysis(final ByteList scalar,
0076: final boolean empty, final boolean multiline,
0077: final boolean allowFlowPlain,
0078: final boolean allowBlockPlain,
0079: final boolean allowSingleQuoted,
0080: final boolean allowDoubleQuoted,
0081: final boolean allowBlock) {
0082: this .scalar = scalar;
0083: this .empty = empty;
0084: this .multiline = multiline;
0085: this .allowFlowPlain = allowFlowPlain;
0086: this .allowBlockPlain = allowBlockPlain;
0087: this .allowSingleQuoted = allowSingleQuoted;
0088: this .allowDoubleQuoted = allowDoubleQuoted;
0089: this .allowBlock = allowBlock;
0090: }
0091: }
0092:
0093: private static interface EmitterState {
0094: void expect(final EmitterEnvironment env) throws IOException;
0095: }
0096:
0097: private static final int STREAM_START = 0;
0098: private static final int FIRST_DOCUMENT_START = 1;
0099: private static final int DOCUMENT_ROOT = 2;
0100: private static final int NOTHING = 3;
0101: private static final int DOCUMENT_START = 4;
0102: private static final int DOCUMENT_END = 5;
0103: private static final int FIRST_FLOW_SEQUENCE_ITEM = 6;
0104: private static final int FLOW_SEQUENCE_ITEM = 7;
0105: private static final int FIRST_FLOW_MAPPING_KEY = 8;
0106: private static final int FLOW_MAPPING_SIMPLE_VALUE = 9;
0107: private static final int FLOW_MAPPING_VALUE = 10;
0108: private static final int FLOW_MAPPING_KEY = 11;
0109: private static final int BLOCK_SEQUENCE_ITEM = 12;
0110: private static final int FIRST_BLOCK_MAPPING_KEY = 13;
0111: private static final int BLOCK_MAPPING_SIMPLE_VALUE = 14;
0112: private static final int BLOCK_MAPPING_VALUE = 15;
0113: private static final int BLOCK_MAPPING_KEY = 16;
0114: private static final int FIRST_BLOCK_SEQUENCE_ITEM = 17;
0115:
0116: private static final EmitterState[] STATES = new EmitterState[18];
0117: static {
0118: STATES[STREAM_START] = new EmitterState() {
0119: public void expect(final EmitterEnvironment env) {
0120: env.expectStreamStart();
0121: }
0122: };
0123: STATES[FIRST_DOCUMENT_START] = new EmitterState() {
0124: public void expect(final EmitterEnvironment env)
0125: throws IOException {
0126: env.expectDocumentStart(true);
0127: }
0128: };
0129: STATES[DOCUMENT_ROOT] = new EmitterState() {
0130: public void expect(final EmitterEnvironment env)
0131: throws IOException {
0132: env.expectDocumentRoot();
0133: }
0134: };
0135: STATES[NOTHING] = new EmitterState() {
0136: public void expect(final EmitterEnvironment env) {
0137: env.expectNothing();
0138: }
0139: };
0140: STATES[DOCUMENT_START] = new EmitterState() {
0141: public void expect(final EmitterEnvironment env)
0142: throws IOException {
0143: env.expectDocumentStart(false);
0144: }
0145: };
0146: STATES[DOCUMENT_END] = new EmitterState() {
0147: public void expect(final EmitterEnvironment env)
0148: throws IOException {
0149: env.expectDocumentEnd();
0150: }
0151: };
0152: STATES[FIRST_FLOW_SEQUENCE_ITEM] = new EmitterState() {
0153: public void expect(final EmitterEnvironment env)
0154: throws IOException {
0155: env.expectFirstFlowSequenceItem();
0156: }
0157: };
0158: STATES[FLOW_SEQUENCE_ITEM] = new EmitterState() {
0159: public void expect(final EmitterEnvironment env)
0160: throws IOException {
0161: env.expectFlowSequenceItem();
0162: }
0163: };
0164: STATES[FIRST_FLOW_MAPPING_KEY] = new EmitterState() {
0165: public void expect(final EmitterEnvironment env)
0166: throws IOException {
0167: env.expectFirstFlowMappingKey();
0168: }
0169: };
0170: STATES[FLOW_MAPPING_SIMPLE_VALUE] = new EmitterState() {
0171: public void expect(final EmitterEnvironment env)
0172: throws IOException {
0173: env.expectFlowMappingSimpleValue();
0174: }
0175: };
0176: STATES[FLOW_MAPPING_VALUE] = new EmitterState() {
0177: public void expect(final EmitterEnvironment env)
0178: throws IOException {
0179: env.expectFlowMappingValue();
0180: }
0181: };
0182: STATES[FLOW_MAPPING_KEY] = new EmitterState() {
0183: public void expect(final EmitterEnvironment env)
0184: throws IOException {
0185: env.expectFlowMappingKey();
0186: }
0187: };
0188: STATES[BLOCK_SEQUENCE_ITEM] = new EmitterState() {
0189: public void expect(final EmitterEnvironment env)
0190: throws IOException {
0191: env.expectBlockSequenceItem(false);
0192: }
0193: };
0194: STATES[FIRST_BLOCK_MAPPING_KEY] = new EmitterState() {
0195: public void expect(final EmitterEnvironment env)
0196: throws IOException {
0197: env.expectFirstBlockMappingKey();
0198: }
0199: };
0200: STATES[BLOCK_MAPPING_SIMPLE_VALUE] = new EmitterState() {
0201: public void expect(final EmitterEnvironment env)
0202: throws IOException {
0203: env.expectBlockMappingSimpleValue();
0204: }
0205: };
0206: STATES[BLOCK_MAPPING_VALUE] = new EmitterState() {
0207: public void expect(final EmitterEnvironment env)
0208: throws IOException {
0209: env.expectBlockMappingValue();
0210: }
0211: };
0212: STATES[BLOCK_MAPPING_KEY] = new EmitterState() {
0213: public void expect(final EmitterEnvironment env)
0214: throws IOException {
0215: env.expectBlockMappingKey(false);
0216: }
0217: };
0218: STATES[FIRST_BLOCK_SEQUENCE_ITEM] = new EmitterState() {
0219: public void expect(final EmitterEnvironment env)
0220: throws IOException {
0221: env.expectBlockSequenceItem(true);
0222: }
0223: };
0224: }
0225:
0226: private final static Map DEFAULT_TAG_PREFIXES_1_0;
0227: private final static Map DEFAULT_TAG_PREFIXES_1_1;
0228: static {
0229: final Map defInit0 = new HashMap();
0230: defInit0.put("tag:yaml.org,2002:", "!");
0231: DEFAULT_TAG_PREFIXES_1_0 = java.util.Collections
0232: .unmodifiableMap(defInit0);
0233: final Map defInit = new HashMap();
0234: defInit.put("!", "!");
0235: defInit.put("tag:yaml.org,2002:", "!!");
0236: DEFAULT_TAG_PREFIXES_1_1 = java.util.Collections
0237: .unmodifiableMap(defInit);
0238: }
0239:
0240: private OutputStream stream;
0241: private YAMLConfig options;
0242: private EmitterEnvironment env;
0243:
0244: public EmitterImpl(final OutputStream stream, final YAMLConfig opts) {
0245: this .stream = stream;
0246: this .options = opts;
0247: this .env = new EmitterEnvironment();
0248: this .env.emitter = this ;
0249: this .env.canonical = this .options.canonical();
0250: final int propIndent = this .options.indent();
0251: if (propIndent >= 2 && propIndent < 10) {
0252: this .env.bestIndent = propIndent;
0253: }
0254: final int propWidth = this .options.bestWidth();
0255: if (propWidth != 0 && propWidth > (this .env.bestIndent * 2)) {
0256: this .env.bestWidth = propWidth;
0257: }
0258: }
0259:
0260: public YAMLConfig getOptions() {
0261: return options;
0262: }
0263:
0264: public void emit(final Event event) throws IOException {
0265: this .env.events.add(event);
0266: while (!this .env.needMoreEvents()) {
0267: this .env.event = (Event) this .env.events.remove(0);
0268: STATES[this .env.state].expect(env);
0269: this .env.event = null;
0270: }
0271: }
0272:
0273: private static class EmitterEnvironment {
0274: public List states = new ArrayList();
0275: public int state = STREAM_START;
0276: public List events = new ArrayList();
0277: public Event event;
0278: public int flowLevel = 0;
0279: public List indents = new ArrayList();
0280: public int indent = -1;
0281: public boolean rootContext = false;
0282: public boolean sequenceContext = false;
0283: public boolean mappingContext = false;
0284: public boolean simpleKeyContext = false;
0285:
0286: public int line = 0;
0287: public int column = 0;
0288: public boolean whitespace = true;
0289: public boolean indentation = true;
0290:
0291: public boolean canonical = false;
0292: public int bestIndent = 2;
0293: public int bestWidth = 80;
0294:
0295: public ByteList bestLinebreak = ByteList.create("\n");
0296:
0297: public Map tagPrefixes;
0298:
0299: public String preparedAnchor;
0300: public String preparedTag;
0301:
0302: public ScalarAnalysis analysis;
0303: public char style = 0;
0304:
0305: public EmitterImpl emitter;
0306:
0307: public boolean isVersion10 = false;
0308:
0309: public boolean needMoreEvents() {
0310: if (events.isEmpty()) {
0311: return true;
0312: }
0313: event = (Event) events.get(0);
0314: if (event instanceof DocumentStartEvent) {
0315: return needEvents(1);
0316: } else if (event instanceof SequenceStartEvent) {
0317: return needEvents(2);
0318: } else if (event instanceof MappingStartEvent) {
0319: return needEvents(3);
0320: } else {
0321: return false;
0322: }
0323: }
0324:
0325: private boolean needEvents(final int count) {
0326: int level = 0;
0327: final Iterator iter = events.iterator();
0328: iter.next();
0329: for (; iter.hasNext();) {
0330: final Object curr = iter.next();
0331: if (curr instanceof DocumentStartEvent
0332: || curr instanceof CollectionStartEvent) {
0333: level++;
0334: } else if (curr instanceof DocumentEndEvent
0335: || curr instanceof CollectionEndEvent) {
0336: level--;
0337: } else if (curr instanceof StreamEndEvent) {
0338: level = -1;
0339: }
0340: if (level < 0) {
0341: return false;
0342: }
0343: }
0344: return events.size() < count + 1;
0345: }
0346:
0347: private void increaseIndent(final boolean flow,
0348: final boolean indentless) {
0349: indents.add(0, new Integer(indent));
0350: if (indent == -1) {
0351: if (flow) {
0352: indent = bestIndent;
0353: } else {
0354: indent = 0;
0355: }
0356: } else if (!indentless) {
0357: indent += bestIndent;
0358: }
0359: }
0360:
0361: public void expectStreamStart() {
0362: if (this .event instanceof StreamStartEvent) {
0363: emitter.writeStreamStart();
0364: this .state = FIRST_DOCUMENT_START;
0365: } else {
0366: throw new EmitterException(
0367: "expected StreamStartEvent, but got "
0368: + this .event);
0369: }
0370: }
0371:
0372: public void expectNothing() {
0373: throw new EmitterException("expecting nothing, but got "
0374: + this .event);
0375: }
0376:
0377: public void expectDocumentStart(final boolean first)
0378: throws IOException {
0379: if (event instanceof DocumentStartEvent) {
0380: final DocumentStartEvent ev = (DocumentStartEvent) event;
0381: if (first) {
0382: if (null != ev.getVersion()) {
0383: emitter.writeVersionDirective(prepareVersion(ev
0384: .getVersion()));
0385: }
0386:
0387: if ((null != ev.getVersion() && ev.getVersion()[1] == 0)
0388: || emitter.getOptions().version().equals(
0389: "1.0")) {
0390: isVersion10 = true;
0391: tagPrefixes = new HashMap(
0392: DEFAULT_TAG_PREFIXES_1_0);
0393: } else {
0394: tagPrefixes = new HashMap(
0395: DEFAULT_TAG_PREFIXES_1_1);
0396: }
0397:
0398: if (null != ev.getTags()) {
0399: final Set handles = new TreeSet();
0400: handles.addAll(ev.getTags().keySet());
0401: for (final Iterator iter = handles.iterator(); iter
0402: .hasNext();) {
0403: final String handle = (String) iter.next();
0404: final String prefix = (String) ev.getTags()
0405: .get(handle);
0406: tagPrefixes.put(prefix, handle);
0407: final String handleText = prepareTagHandle(handle);
0408: final String prefixText = prepareTagPrefix(prefix);
0409: emitter.writeTagDirective(handleText,
0410: prefixText);
0411: }
0412: }
0413: }
0414:
0415: final boolean implicit = first && !ev.getExplicit()
0416: && !canonical && ev.getVersion() == null
0417: && ev.getTags() == null
0418: && !checkEmptyDocument();
0419: if (!implicit) {
0420: emitter.writeIndent();
0421: emitter.writeIndicator(ByteList.create("--- "),
0422: true, true, false);
0423: if (canonical) {
0424: emitter.writeIndent();
0425: }
0426: }
0427: state = DOCUMENT_ROOT;
0428: } else if (event instanceof StreamEndEvent) {
0429: emitter.writeStreamEnd();
0430: state = NOTHING;
0431: } else {
0432: throw new EmitterException(
0433: "expected DocumentStartEvent, but got " + event);
0434: }
0435: }
0436:
0437: public void expectDocumentRoot() throws IOException {
0438: states.add(0, new Integer(DOCUMENT_END));
0439: expectNode(true, false, false, false);
0440: }
0441:
0442: public void expectDocumentEnd() throws IOException {
0443: if (event instanceof DocumentEndEvent) {
0444: emitter.writeIndent();
0445: if (((DocumentEndEvent) event).getExplicit()) {
0446: emitter.writeIndicator(ByteList.create("..."),
0447: true, false, false);
0448: emitter.writeIndent();
0449: }
0450: emitter.flushStream();
0451: state = DOCUMENT_START;
0452: } else {
0453: throw new EmitterException(
0454: "expected DocumentEndEvent, but got " + event);
0455: }
0456: }
0457:
0458: public void expectFirstFlowSequenceItem() throws IOException {
0459: if (event instanceof SequenceEndEvent) {
0460: indent = ((Integer) indents.remove(0)).intValue();
0461: flowLevel--;
0462: emitter.writeIndicator(ByteList.create("]"), false,
0463: false, false);
0464: state = ((Integer) states.remove(0)).intValue();
0465: } else {
0466: if (canonical || column > bestWidth) {
0467: emitter.writeIndent();
0468: }
0469: states.add(0, new Integer(FLOW_SEQUENCE_ITEM));
0470: expectNode(false, true, false, false);
0471: }
0472: }
0473:
0474: public void expectFlowSequenceItem() throws IOException {
0475: if (event instanceof SequenceEndEvent) {
0476: indent = ((Integer) indents.remove(0)).intValue();
0477: flowLevel--;
0478: if (canonical) {
0479: emitter.writeIndicator(ByteList.create(","), false,
0480: false, false);
0481: emitter.writeIndent();
0482: }
0483: emitter.writeIndicator(ByteList.create("]"), false,
0484: false, false);
0485: state = ((Integer) states.remove(0)).intValue();
0486: } else {
0487: emitter.writeIndicator(ByteList.create(","), false,
0488: false, false);
0489: if (canonical || column > bestWidth) {
0490: emitter.writeIndent();
0491: }
0492: states.add(0, new Integer(FLOW_SEQUENCE_ITEM));
0493: expectNode(false, true, false, false);
0494: }
0495: }
0496:
0497: public void expectFirstFlowMappingKey() throws IOException {
0498: if (event instanceof MappingEndEvent) {
0499: indent = ((Integer) indents.remove(0)).intValue();
0500: flowLevel--;
0501: emitter.writeIndicator(ByteList.create("}"), false,
0502: false, false);
0503: state = ((Integer) states.remove(0)).intValue();
0504: } else {
0505: if (canonical || column > bestWidth) {
0506: emitter.writeIndent();
0507: }
0508: if (!canonical && checkSimpleKey()) {
0509: states.add(0,
0510: new Integer(FLOW_MAPPING_SIMPLE_VALUE));
0511: expectNode(false, false, true, true);
0512: } else {
0513: emitter.writeIndicator(ByteList.create("?"), true,
0514: false, false);
0515: states.add(0, new Integer(FLOW_MAPPING_VALUE));
0516: expectNode(false, false, true, false);
0517: }
0518: }
0519: }
0520:
0521: public void expectFlowMappingSimpleValue() throws IOException {
0522: emitter.writeIndicator(ByteList.create(": "), false, true,
0523: false);
0524: states.add(0, new Integer(FLOW_MAPPING_KEY));
0525: expectNode(false, false, true, false);
0526: }
0527:
0528: public void expectFlowMappingValue() throws IOException {
0529: if (canonical || column > bestWidth) {
0530: emitter.writeIndent();
0531: }
0532: emitter.writeIndicator(ByteList.create(": "), false, true,
0533: false);
0534: states.add(0, new Integer(FLOW_MAPPING_KEY));
0535: expectNode(false, false, true, false);
0536: }
0537:
0538: public void expectFlowMappingKey() throws IOException {
0539: if (event instanceof MappingEndEvent) {
0540: indent = ((Integer) indents.remove(0)).intValue();
0541: flowLevel--;
0542: if (canonical) {
0543: emitter.writeIndicator(ByteList.create(","), false,
0544: false, false);
0545: emitter.writeIndent();
0546: }
0547: emitter.writeIndicator(ByteList.create("}"), false,
0548: false, false);
0549: state = ((Integer) states.remove(0)).intValue();
0550: } else {
0551: emitter.writeIndicator(ByteList.create(","), false,
0552: false, false);
0553: if (canonical || column > bestWidth) {
0554: emitter.writeIndent();
0555: }
0556: if (!canonical && checkSimpleKey()) {
0557: states.add(0,
0558: new Integer(FLOW_MAPPING_SIMPLE_VALUE));
0559: expectNode(false, false, true, true);
0560: } else {
0561: emitter.writeIndicator(ByteList.create("?"), true,
0562: false, false);
0563: states.add(0, new Integer(FLOW_MAPPING_VALUE));
0564: expectNode(false, false, true, false);
0565: }
0566: }
0567: }
0568:
0569: public void expectBlockSequenceItem(final boolean first)
0570: throws IOException {
0571: if (!first && event instanceof SequenceEndEvent) {
0572: indent = ((Integer) indents.remove(0)).intValue();
0573: state = ((Integer) states.remove(0)).intValue();
0574: } else {
0575: emitter.writeIndent();
0576: emitter.writeIndicator(ByteList.create("-"), true,
0577: false, true);
0578: states.add(0, new Integer(BLOCK_SEQUENCE_ITEM));
0579: expectNode(false, true, false, false);
0580: }
0581: }
0582:
0583: public void expectFirstBlockMappingKey() throws IOException {
0584: expectBlockMappingKey(true);
0585: }
0586:
0587: public void expectBlockMappingSimpleValue() throws IOException {
0588: emitter.writeIndicator(ByteList.create(": "), false, true,
0589: false);
0590: states.add(0, new Integer(BLOCK_MAPPING_KEY));
0591: expectNode(false, false, true, false);
0592: }
0593:
0594: public void expectBlockMappingValue() throws IOException {
0595: emitter.writeIndent();
0596: emitter.writeIndicator(ByteList.create(": "), true, true,
0597: true);
0598: states.add(0, new Integer(BLOCK_MAPPING_KEY));
0599: expectNode(false, false, true, false);
0600: }
0601:
0602: public void expectBlockMappingKey(final boolean first)
0603: throws IOException {
0604: if (!first && event instanceof MappingEndEvent) {
0605: indent = ((Integer) indents.remove(0)).intValue();
0606: state = ((Integer) states.remove(0)).intValue();
0607: } else {
0608: emitter.writeIndent();
0609: if (checkSimpleKey()) {
0610: states.add(0, new Integer(
0611: BLOCK_MAPPING_SIMPLE_VALUE));
0612: expectNode(false, false, true, true);
0613: } else {
0614: emitter.writeIndicator(ByteList.create("?"), true,
0615: false, true);
0616: states.add(0, new Integer(BLOCK_MAPPING_VALUE));
0617: expectNode(false, false, true, false);
0618: }
0619: }
0620: }
0621:
0622: private void expectNode(final boolean root,
0623: final boolean sequence, final boolean mapping,
0624: final boolean simpleKey) throws IOException {
0625: rootContext = root;
0626: sequenceContext = sequence;
0627: mappingContext = mapping;
0628: simpleKeyContext = simpleKey;
0629: if (event instanceof AliasEvent) {
0630: expectAlias();
0631: } else if (event instanceof ScalarEvent
0632: || event instanceof CollectionStartEvent) {
0633: processAnchor(ByteList.create("&"));
0634: processTag();
0635: if (event instanceof ScalarEvent) {
0636: expectScalar();
0637: } else if (event instanceof SequenceStartEvent) {
0638: if (flowLevel != 0
0639: || canonical
0640: || ((SequenceStartEvent) event)
0641: .getFlowStyle()
0642: || checkEmptySequence()) {
0643: expectFlowSequence();
0644: } else {
0645: expectBlockSequence();
0646: }
0647: } else if (event instanceof MappingStartEvent) {
0648: if (flowLevel != 0
0649: || canonical
0650: || ((MappingStartEvent) event)
0651: .getFlowStyle()
0652: || checkEmptyMapping()) {
0653: expectFlowMapping();
0654: } else {
0655: expectBlockMapping();
0656: }
0657: }
0658: } else {
0659: throw new EmitterException(
0660: "expected NodeEvent, but got " + event);
0661: }
0662: }
0663:
0664: private void expectAlias() throws IOException {
0665: if (((NodeEvent) event).getAnchor() == null) {
0666: throw new EmitterException(
0667: "anchor is not specified for alias");
0668: }
0669: processAnchor(ByteList.create("*"));
0670: state = ((Integer) states.remove(0)).intValue();
0671: }
0672:
0673: private void expectScalar() throws IOException {
0674: increaseIndent(true, false);
0675: processScalar();
0676: indent = ((Integer) indents.remove(0)).intValue();
0677: state = ((Integer) states.remove(0)).intValue();
0678: }
0679:
0680: private void expectFlowSequence() throws IOException {
0681: emitter.writeIndicator(ByteList.create("["), true, true,
0682: false);
0683: flowLevel++;
0684: increaseIndent(true, false);
0685: state = FIRST_FLOW_SEQUENCE_ITEM;
0686: }
0687:
0688: private void expectBlockSequence() throws IOException {
0689: increaseIndent(false, mappingContext && !indentation);
0690: state = FIRST_BLOCK_SEQUENCE_ITEM;
0691: }
0692:
0693: private void expectFlowMapping() throws IOException {
0694: emitter.writeIndicator(ByteList.create("{"), true, true,
0695: false);
0696: flowLevel++;
0697: increaseIndent(true, false);
0698: state = FIRST_FLOW_MAPPING_KEY;
0699: }
0700:
0701: private void expectBlockMapping() throws IOException {
0702: increaseIndent(false, false);
0703: state = FIRST_BLOCK_MAPPING_KEY;
0704: }
0705:
0706: private boolean checkEmptySequence() {
0707: return event instanceof SequenceStartEvent
0708: && !events.isEmpty()
0709: && events.get(0) instanceof SequenceEndEvent;
0710: }
0711:
0712: private boolean checkEmptyMapping() {
0713: return event instanceof MappingStartEvent
0714: && !events.isEmpty()
0715: && events.get(0) instanceof MappingEndEvent;
0716: }
0717:
0718: private boolean checkEmptyDocument() {
0719: if (!(event instanceof DocumentStartEvent)
0720: || events.isEmpty()) {
0721: return false;
0722: }
0723: final Event ev = (Event) events.get(0);
0724: return ev instanceof ScalarEvent
0725: && ((ScalarEvent) ev).getAnchor() == null
0726: && ((ScalarEvent) ev).getTag() == null
0727: && ((ScalarEvent) ev).getImplicit() != null
0728: && ((ScalarEvent) ev).getValue().realSize == 0;
0729: }
0730:
0731: private boolean checkSimpleKey() {
0732: int length = 0;
0733: if (event instanceof NodeEvent
0734: && null != ((NodeEvent) event).getAnchor()) {
0735: if (null == preparedAnchor) {
0736: preparedAnchor = prepareAnchor(((NodeEvent) event)
0737: .getAnchor());
0738: }
0739: length += preparedAnchor.length();
0740: }
0741: String tag = null;
0742: if (event instanceof ScalarEvent) {
0743: tag = ((ScalarEvent) event).getTag();
0744: } else if (event instanceof CollectionStartEvent) {
0745: tag = ((CollectionStartEvent) event).getTag();
0746: }
0747: if (tag != null) {
0748: if (null == preparedTag) {
0749: preparedTag = emitter.prepareTag(tag);
0750: }
0751: length += preparedTag.length();
0752: }
0753: if (event instanceof ScalarEvent) {
0754: if (null == analysis) {
0755: analysis = analyzeScalar(((ScalarEvent) event)
0756: .getValue());
0757: length += analysis.scalar.length();
0758: }
0759: }
0760:
0761: return (length < 128 && (event instanceof AliasEvent
0762: || (event instanceof ScalarEvent && !analysis.multiline)
0763: || checkEmptySequence() || checkEmptyMapping()));
0764: }
0765:
0766: private void processAnchor(final ByteList indicator)
0767: throws IOException {
0768: final NodeEvent ev = (NodeEvent) event;
0769: if (null == ev.getAnchor()) {
0770: preparedAnchor = null;
0771: return;
0772: }
0773: if (null == preparedAnchor) {
0774: preparedAnchor = prepareAnchor(ev.getAnchor());
0775: }
0776: if (preparedAnchor != null && !"".equals(preparedAnchor)) {
0777: indicator.append(preparedAnchor.getBytes());
0778: if (ev instanceof CollectionStartEvent) {
0779: indentation = true;
0780: }
0781: emitter.writeIndicator(indicator, true, false, true);
0782: }
0783: preparedAnchor = null;
0784: }
0785:
0786: private void processTag() throws IOException {
0787: String tag = null;
0788: if (event instanceof ScalarEvent) {
0789: final ScalarEvent ev = (ScalarEvent) event;
0790: tag = ev.getTag();
0791: if (style == 0) {
0792: style = chooseScalarStyle();
0793: }
0794: if (((!canonical || tag == null) && ((0 == style && ev
0795: .getImplicit()[0]) || (0 != style && ev
0796: .getImplicit()[1])))) {
0797: preparedTag = null;
0798: return;
0799: }
0800: if (ev.getImplicit()[0] && null == tag) {
0801: tag = "!";
0802: preparedTag = null;
0803: }
0804: } else {
0805: final CollectionStartEvent ev = (CollectionStartEvent) event;
0806: tag = ev.getTag();
0807: if ((!canonical || tag == null) && ev.getImplicit()) {
0808: preparedTag = null;
0809: return;
0810: }
0811: indentation = true;
0812: }
0813: if (tag == null) {
0814: throw new EmitterException("tag is not specified");
0815: }
0816: if (null == preparedTag) {
0817: preparedTag = emitter.prepareTag(tag);
0818: }
0819: if (preparedTag != null && !"".equals(preparedTag)) {
0820: emitter.writeIndicator(ByteList.create(preparedTag),
0821: true, false, true);
0822: }
0823: preparedTag = null;
0824: }
0825:
0826: private char chooseScalarStyle() {
0827: final ScalarEvent ev = (ScalarEvent) event;
0828:
0829: if (null == analysis) {
0830: analysis = analyzeScalar(ev.getValue());
0831: }
0832:
0833: if (ev.getStyle() == '"'
0834: || this .canonical
0835: || (analysis.empty && ev.getTag().equals(
0836: "tag:yaml.org,2002:str"))) {
0837: return '"';
0838: }
0839:
0840: // if(ev.getStyle() == 0 && ev.getImplicit()[0]) {
0841: if (ev.getStyle() == 0) {
0842: if (!(simpleKeyContext && (analysis.empty || analysis.multiline))
0843: && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) {
0844: return 0;
0845: }
0846: }
0847: if (ev.getStyle() == 0
0848: && ev.getImplicit()[0]
0849: && (!(simpleKeyContext && (analysis.empty || analysis.multiline)) && (flowLevel != 0
0850: && analysis.allowFlowPlain || (flowLevel == 0 && analysis.allowBlockPlain)))) {
0851: return 0;
0852: }
0853: if ((ev.getStyle() == '|' || ev.getStyle() == '>')
0854: && flowLevel == 0 && analysis.allowBlock) {
0855: return '\'';
0856: }
0857: if ((ev.getStyle() == 0 || ev.getStyle() == '\'')
0858: && (analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline))) {
0859: return '\'';
0860: }
0861: if (analysis.multiline
0862: && !FIRST_SPACE.matcher(ev.getValue()).find()) {
0863: return '|';
0864: }
0865:
0866: return '"';
0867: }
0868:
0869: private void processScalar() throws IOException {
0870: final ScalarEvent ev = (ScalarEvent) event;
0871:
0872: if (null == analysis) {
0873: analysis = analyzeScalar(ev.getValue());
0874: }
0875: if (0 == style) {
0876: style = chooseScalarStyle();
0877: }
0878: final boolean split = !simpleKeyContext;
0879: if (style == '"') {
0880: emitter.writeDoubleQuoted(analysis.scalar, split);
0881: } else if (style == '\'') {
0882: emitter.writeSingleQuoted(analysis.scalar, split);
0883: } else if (style == '>') {
0884: emitter.writeFolded(analysis.scalar);
0885: } else if (style == '|') {
0886: emitter.writeLiteral(analysis.scalar);
0887: } else {
0888: emitter.writePlain(analysis.scalar, split);
0889: }
0890: analysis = null;
0891: style = 0;
0892: }
0893: }
0894:
0895: void writeStreamStart() {
0896: }
0897:
0898: void writeStreamEnd() throws IOException {
0899: flushStream();
0900: }
0901:
0902: void writeIndicator(final ByteList indicator,
0903: final boolean needWhitespace, final boolean whitespace,
0904: final boolean indentation) throws IOException {
0905: ByteList data = indicator;
0906: if (!(env.whitespace || !needWhitespace)) {
0907: data.prepend((byte) ' ');
0908: }
0909: env.whitespace = whitespace;
0910: env.indentation = env.indentation && indentation;
0911: env.column += data.length();
0912: stream.write(data.bytes, 0, data.realSize);
0913: }
0914:
0915: void writeIndent() throws IOException {
0916: int indent = 0;
0917: if (env.indent != -1) {
0918: indent = env.indent;
0919: }
0920:
0921: if (!env.indentation || env.column > indent
0922: || (env.column == indent && !env.whitespace)) {
0923: writeLineBreak(null);
0924: }
0925:
0926: if (env.column < indent) {
0927: env.whitespace = true;
0928: final ByteList data = new ByteList();
0929: for (int i = 0, j = (indent - env.column); i < j; i++) {
0930: data.append((byte) ' ');
0931: }
0932: env.column = indent;
0933: stream.write(data.bytes, 0, data.realSize);
0934: }
0935: }
0936:
0937: void writeVersionDirective(final String version_text)
0938: throws IOException {
0939: stream.write(("%YAML " + version_text).getBytes());
0940: writeLineBreak(null);
0941: }
0942:
0943: void writeTagDirective(final String handle, final String prefix)
0944: throws IOException {
0945: stream.write(("%TAG " + handle + " " + prefix).getBytes());
0946: writeLineBreak(null);
0947: }
0948:
0949: void writeDoubleQuoted(final ByteList text, final boolean split)
0950: throws IOException {
0951: writeIndicator(ByteList.create("\""), true, false, false);
0952: int start = 0;
0953: int ending = 0;
0954: ByteList data = null;
0955: while (ending <= text.length()) {
0956: char ch = 0;
0957: if (ending < text.length()) {
0958: ch = text.charAt(ending);
0959: }
0960: if (ch == 0 || "\"\\\u0085".indexOf(ch) != -1
0961: || !('\u0020' <= ch && ch <= '\u007E')) {
0962: if (start < ending) {
0963: data = (ByteList) text.subSequence(start, ending);
0964: env.column += data.length();
0965: stream.write(data.bytes, 0, data.realSize);
0966: start = ending;
0967: }
0968: if (ch != 0) {
0969: if (YAML.ESCAPE_REPLACEMENTS
0970: .containsKey(new Character(ch))) {
0971: data = ByteList.create("\\"
0972: + YAML.ESCAPE_REPLACEMENTS
0973: .get(new Character(ch)));
0974: } else if (ch <= '\u00FF') {
0975: String str = Integer.toString(ch, 16);
0976: if (str.length() == 1) {
0977: str = "0" + str;
0978: }
0979: data = ByteList.create("\\x" + str);
0980: }
0981: env.column += data.length();
0982: stream.write(data.bytes, 0, data.realSize);
0983: start = ending + 1;
0984: }
0985: }
0986:
0987: if ((0 < ending && ending < (text.length() - 1))
0988: && (ch == ' ' || start >= ending)
0989: && (env.column + (ending - start)) > env.bestWidth
0990: && split) {
0991: if (start < ending) {
0992: data = (ByteList) text.subSequence(start, ending);
0993: data.append(' ');
0994: data.append('\\');
0995: } else {
0996: data = ByteList.create("\\");
0997: }
0998:
0999: if (start < ending) {
1000: start = ending;
1001: }
1002: env.column += data.length();
1003: stream.write(data.bytes, 0, data.realSize);
1004: writeIndent();
1005: env.whitespace = false;
1006: env.indentation = false;
1007:
1008: if (ending < (text.length() + 1)
1009: && text.charAt(ending + 1) == ' ') {
1010: data = ByteList.create("\\");
1011: stream.write(data.bytes, 0, data.realSize);
1012: }
1013: }
1014: ending += 1;
1015: }
1016:
1017: writeIndicator(ByteList.create("\""), false, false, false);
1018: }
1019:
1020: void writeSingleQuoted(final ByteList text, final boolean split)
1021: throws IOException {
1022: writeIndicator(ByteList.create("'"), true, false, false);
1023: boolean spaces = false;
1024: boolean breaks = false;
1025: int start = 0, ending = 0;
1026: char ceh = 0;
1027: ByteList data = null;
1028: while (ending <= text.length()) {
1029: ceh = 0;
1030: if (ending < text.length()) {
1031: ceh = text.charAt(ending);
1032: }
1033: if (spaces) {
1034: if (ceh == 0 || ceh != 32) {
1035: if (start + 1 == ending
1036: && env.column > env.bestWidth && split
1037: && start != 0 && ending != text.length()) {
1038: writeIndent();
1039: } else {
1040: data = (ByteList) text.subSequence(start,
1041: ending);
1042: env.column += data.length();
1043: stream.write(data.bytes, 0, data.realSize);
1044: }
1045: start = ending;
1046: }
1047: } else if (breaks) {
1048: if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) {
1049: data = (ByteList) text.subSequence(start, ending);
1050: for (int i = 0, j = data.length(); i < j; i++) {
1051: char cha = data.charAt(i);
1052: if ('\n' == cha) {
1053: writeLineBreak(null);
1054: } else {
1055: writeLineBreak(ByteList.create("" + cha));
1056: }
1057: }
1058: writeIndent();
1059: start = ending;
1060: }
1061: } else {
1062: if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) {
1063: if (start < ending) {
1064: data = (ByteList) text.subSequence(start,
1065: ending);
1066: env.column += data.length();
1067: stream.write(data.bytes, 0, data.realSize);
1068: start = ending;
1069: }
1070: }
1071: }
1072: if (ceh == '\'') {
1073: data = ByteList.create("''");
1074: env.column += 2;
1075: stream.write(data.bytes, 0, data.realSize);
1076: start = ending + 1;
1077: }
1078: if (ceh != 0) {
1079: spaces = ceh == ' ';
1080: breaks = ceh == '\n' || ceh == '\u0085';
1081: }
1082: ending++;
1083: }
1084: writeIndicator(ByteList.create("'"), false, false, false);
1085: }
1086:
1087: void writeFolded(final ByteList text) throws IOException {
1088: String chomp = determineChomp(text);
1089: writeIndicator(ByteList.create(">" + chomp), true, false, false);
1090: writeIndent();
1091: boolean leadingSpace = false;
1092: boolean spaces = false;
1093: boolean breaks = false;
1094: int start = 0, ending = 0;
1095: ByteList data = null;
1096: while (ending <= text.length()) {
1097: char ceh = 0;
1098: if (ending < text.length()) {
1099: ceh = text.charAt(ending);
1100: }
1101: if (breaks) {
1102: if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) {
1103: if (!leadingSpace && ceh != 0 && ceh != ' '
1104: && text.charAt(start) == '\n') {
1105: writeLineBreak(null);
1106: }
1107: leadingSpace = ceh == ' ';
1108: data = (ByteList) text.subSequence(start, ending);
1109: for (int i = 0, j = data.length(); i < j; i++) {
1110: char cha = data.charAt(i);
1111: if ('\n' == cha) {
1112: writeLineBreak(null);
1113: } else {
1114: writeLineBreak(ByteList.create("" + cha));
1115: }
1116: }
1117: if (ceh != 0) {
1118: writeIndent();
1119: }
1120: start = ending;
1121: }
1122: } else if (spaces) {
1123: if (ceh != ' ') {
1124: if (start + 1 == ending
1125: && env.column > env.bestWidth) {
1126: writeIndent();
1127: } else {
1128: data = (ByteList) text.subSequence(start,
1129: ending);
1130: env.column += data.length();
1131: stream.write(data.bytes, 0, data.realSize);
1132: }
1133: start = ending;
1134: }
1135: } else {
1136: if (ceh == 0 || ' ' == ceh || '\n' == ceh
1137: || '\u0085' == ceh) {
1138: data = (ByteList) text.subSequence(start, ending);
1139: stream.write(data.bytes, 0, data.realSize);
1140: if (ceh == 0) {
1141: writeLineBreak(null);
1142: }
1143: start = ending;
1144: }
1145: }
1146: if (ceh != 0) {
1147: breaks = '\n' == ceh || '\u0085' == ceh;
1148: spaces = ceh == ' ';
1149: }
1150: ending++;
1151: }
1152: }
1153:
1154: void writeLiteral(final ByteList text) throws IOException {
1155: String chomp = determineChomp(text);
1156: writeIndicator(ByteList.create("|" + chomp), true, false, false);
1157: writeIndent();
1158: boolean breaks = false;
1159: int start = 0, ending = 0;
1160: ByteList data = null;
1161: while (ending <= text.length()) {
1162: char ceh = 0;
1163: if (ending < text.length()) {
1164: ceh = text.charAt(ending);
1165: }
1166: if (breaks) {
1167: if (ceh == 0 || !('\n' == ceh || '\u0085' == ceh)) {
1168: data = (ByteList) text.subSequence(start, ending);
1169: for (int i = 0, j = data.length(); i < j; i++) {
1170: char cha = data.charAt(i);
1171: if ('\n' == cha) {
1172: writeLineBreak(null);
1173: } else {
1174: writeLineBreak(ByteList.create("" + cha));
1175: }
1176: }
1177: if (ceh != 0) {
1178: writeIndent();
1179: }
1180: start = ending;
1181: }
1182: } else {
1183: if (ceh == 0 || '\n' == ceh || '\u0085' == ceh) {
1184: data = (ByteList) text.subSequence(start, ending);
1185: stream.write(data.bytes, 0, data.realSize);
1186: if (ceh == 0) {
1187: writeLineBreak(null);
1188: }
1189: start = ending;
1190: }
1191: }
1192: if (ceh != 0) {
1193: breaks = '\n' == ceh || '\u0085' == ceh;
1194: }
1195: ending++;
1196: }
1197: }
1198:
1199: void writePlain(final ByteList text, final boolean split)
1200: throws IOException {
1201: if (text == null || text.realSize == 0) {
1202: return;
1203: }
1204: ByteList data = null;
1205: if (!env.whitespace) {
1206: env.column += 1;
1207: stream.write(32); // space
1208: }
1209: env.whitespace = false;
1210: env.indentation = false;
1211: boolean spaces = false, breaks = false;
1212: int start = 0, ending = 0;
1213: while (ending <= text.length()) {
1214: char ceh = 0;
1215: if (ending < text.length()) {
1216: ceh = (char) (text.bytes[text.begin + ending] & 0xFF);
1217: }
1218: if (spaces) {
1219: if (ceh != ' ') {
1220: if (start + 1 == ending
1221: && env.column > env.bestWidth && split) {
1222: writeIndent();
1223: env.whitespace = false;
1224: env.indentation = false;
1225: } else {
1226: data = new ByteList(text, start, ending - start);
1227: env.column += data.length();
1228: stream.write(data.bytes, 0, data.realSize);
1229: }
1230: start = ending;
1231: }
1232: } else if (breaks) {
1233: if (ceh != '\n' && ceh != '\u0085') {
1234: if ((text.bytes[start] & 0xFF) == '\n') {
1235: writeLineBreak(null);
1236: }
1237: data = new ByteList(text, start, ending - start);
1238: for (int i = 0, j = data.length(); i < j; i++) {
1239: char cha = (char) (data.bytes[data.begin + i] & 0xFF);
1240: if ('\n' == cha) {
1241: writeLineBreak(null);
1242: } else {
1243: writeLineBreak(ByteList.create("" + cha));
1244: }
1245: }
1246: writeIndent();
1247: env.whitespace = false;
1248: env.indentation = false;
1249: start = ending;
1250: }
1251: } else {
1252: if (ceh == 0 || ' ' == ceh || '\n' == ceh
1253: || '\u0085' == ceh) {
1254: data = new ByteList(text, start, ending - start);
1255: env.column += data.length();
1256: stream.write(data.bytes, 0, data.realSize);
1257: start = ending;
1258: }
1259: }
1260: if (ceh != 0) {
1261: spaces = ceh == ' ';
1262: breaks = ceh == '\n' || ceh == '\u0085';
1263: }
1264: ending++;
1265: }
1266: }
1267:
1268: void writeLineBreak(final ByteList data) throws IOException {
1269: ByteList xdata = data;
1270: if (xdata == null) {
1271: xdata = env.bestLinebreak;
1272: }
1273: env.whitespace = true;
1274: env.indentation = true;
1275: env.line++;
1276: env.column = 0;
1277: stream.write(xdata.bytes, 0, xdata.realSize);
1278: }
1279:
1280: void flushStream() throws IOException {
1281: stream.flush();
1282: }
1283:
1284: static String prepareVersion(final int[] version) {
1285: if (version[0] != 1) {
1286: throw new EmitterException("unsupported YAML version: "
1287: + version[0] + "." + version[1]);
1288: }
1289: return "" + version[0] + "." + version[1];
1290: }
1291:
1292: private final static Pattern HANDLE_FORMAT = Pattern
1293: .compile("^![-\\w]*!$");
1294:
1295: static String prepareTagHandle(final String handle) {
1296: if (handle == null || "".equals(handle)) {
1297: throw new EmitterException("tag handle must not be empty");
1298: } else if (handle.charAt(0) != '!'
1299: || handle.charAt(handle.length() - 1) != '!') {
1300: throw new EmitterException(
1301: "tag handle must start and end with '!': " + handle);
1302: } else if (!"!".equals(handle)
1303: && !HANDLE_FORMAT.matcher(handle).matches()) {
1304: throw new EmitterException(
1305: "invalid syntax for tag handle: " + handle);
1306: }
1307: return handle;
1308: }
1309:
1310: static String prepareTagPrefix(final String prefix) {
1311: if (prefix == null || "".equals(prefix)) {
1312: throw new EmitterException("tag prefix must not be empty");
1313: }
1314: final StringBuffer chunks = new StringBuffer();
1315: int start = 0, ending = 0;
1316: if (prefix.charAt(0) == '!') {
1317: ending = 1;
1318: }
1319: while (ending < prefix.length()) {
1320: ending++;
1321: }
1322: if (start < ending) {
1323: chunks.append(prefix.substring(start, ending));
1324: }
1325: return chunks.toString();
1326: }
1327:
1328: private final static Pattern ANCHOR_FORMAT = Pattern
1329: .compile("^[-\\w]*$");
1330:
1331: static String prepareAnchor(final String anchor) {
1332: if (anchor == null || "".equals(anchor)) {
1333: throw new EmitterException("anchor must not be empty");
1334: }
1335: if (!ANCHOR_FORMAT.matcher(anchor).matches()) {
1336: throw new EmitterException("invalid syntax for anchor: "
1337: + anchor);
1338: }
1339: return anchor;
1340: }
1341:
1342: String prepareTag(final String tag) {
1343: if (tag == null || "".equals(tag)) {
1344: throw new EmitterException("tag must not be empty");
1345: }
1346: if (tag.equals("!")) {
1347: return tag;
1348: }
1349: String handle = null;
1350: String suffix = tag;
1351: for (final Iterator iter = env.tagPrefixes.keySet().iterator(); iter
1352: .hasNext();) {
1353: String prefix = (String) iter.next();
1354: if (Pattern.matches("^" + prefix + ".+$", tag)
1355: && (prefix.equals("!") || prefix.length() < tag
1356: .length())) {
1357: handle = (String) env.tagPrefixes.get(prefix);
1358: suffix = tag.substring(prefix.length());
1359: }
1360: }
1361: if (handle == null) {
1362: if (tag.startsWith("tag:") && tag.indexOf(':', 4) != -1) {
1363: int doti = tag.indexOf('.', 4);
1364: String first = tag.substring(4, doti);
1365: String rest = tag.substring(tag.indexOf(':', 4) + 1);
1366: handle = "!" + first + "/";
1367: suffix = rest;
1368: }
1369: }
1370:
1371: final StringBuffer chunks = new StringBuffer();
1372: int start = 0, ending = 0;
1373: while (ending < suffix.length()) {
1374: ending++;
1375: }
1376: if (start < ending) {
1377: chunks.append(suffix.substring(start, ending));
1378: }
1379: String suffixText = chunks.toString();
1380: if (tag.charAt(0) == '!' && env.isVersion10) {
1381: return tag;
1382: }
1383: if (handle != null) {
1384: return handle + suffixText;
1385: } else {
1386: return "!<" + suffixText + ">";
1387: }
1388: }
1389:
1390: private final static Pattern DOC_INDIC = Pattern
1391: .compile("^(---|\\.\\.\\.)");
1392: private final static Pattern FIRST_SPACE = Pattern
1393: .compile("(^|\n) ");
1394: private final static String NULL_BL_T_LINEBR = "\0 \t\r\n\u0085";
1395: private final static String SPECIAL_INDIC = "#,[]{}#&*!|>'\"%@`";
1396: private final static String FLOW_INDIC = ",?[]{}";
1397:
1398: static ScalarAnalysis analyzeScalar(final ByteList scalar) {
1399: if (scalar == null || scalar.realSize == 0) {
1400: return new ScalarAnalysis(scalar, true, false, false, true,
1401: true, true, false);
1402: }
1403: boolean blockIndicators = false;
1404: boolean flowIndicators = false;
1405: boolean lineBreaks = false;
1406: boolean specialCharacters = false;
1407:
1408: // Whitespaces.
1409: boolean inlineSpaces = false; // non-space space+ non-space
1410: boolean inlineBreaks = false; // non-space break+ non-space
1411: boolean leadingSpaces = false; // ^ space+ (non-space | $)
1412: boolean leadingBreaks = false; // ^ break+ (non-space | $)
1413: boolean trailingSpaces = false; // (^ | non-space) space+ $
1414: boolean trailingBreaks = false; // (^ | non-space) break+ $
1415: boolean inlineBreaksSpaces = false; // non-space break+ space+ non-space
1416: boolean mixedBreaksSpaces = false; // anything else
1417:
1418: if (DOC_INDIC.matcher(scalar).matches()) {
1419: blockIndicators = true;
1420: flowIndicators = true;
1421: }
1422:
1423: boolean preceededBySpace = true;
1424: boolean followedBySpace = scalar.length() == 1
1425: || NULL_BL_T_LINEBR.indexOf(scalar.charAt(1)) != -1;
1426:
1427: boolean spaces = false;
1428: boolean breaks = false;
1429: boolean mixed = false;
1430: boolean leading = false;
1431:
1432: int index = 0;
1433:
1434: while (index < scalar.length()) {
1435: char ceh = scalar.charAt(index);
1436: if (index == 0) {
1437: if (SPECIAL_INDIC.indexOf(ceh) != -1) {
1438: flowIndicators = true;
1439: blockIndicators = true;
1440: }
1441: if (ceh == '?' || ceh == ':') {
1442: flowIndicators = true;
1443: if (followedBySpace) {
1444: blockIndicators = true;
1445: }
1446: }
1447: if (ceh == '-' && followedBySpace) {
1448: flowIndicators = true;
1449: blockIndicators = true;
1450: }
1451: } else {
1452: if (FLOW_INDIC.indexOf(ceh) != -1) {
1453: flowIndicators = true;
1454: }
1455: if (ceh == ':') {
1456: flowIndicators = true;
1457: if (followedBySpace) {
1458: blockIndicators = true;
1459: }
1460: }
1461: if (ceh == '#' && preceededBySpace) {
1462: flowIndicators = true;
1463: blockIndicators = true;
1464: }
1465: }
1466: if (ceh == '\n' || '\u0085' == ceh) {
1467: lineBreaks = true;
1468: }
1469: if (!(ceh == '\n' || ('\u0020' <= ceh && ceh <= '\u007E'))) {
1470: specialCharacters = true;
1471:
1472: }
1473: if (' ' == ceh || '\n' == ceh || '\u0085' == ceh) {
1474: if (spaces && breaks) {
1475: if (ceh != ' ') {
1476: mixed = true;
1477: }
1478: } else if (spaces) {
1479: if (ceh != ' ') {
1480: breaks = true;
1481: mixed = true;
1482: }
1483: } else if (breaks) {
1484: if (ceh == ' ') {
1485: spaces = true;
1486: }
1487: } else {
1488: leading = (index == 0);
1489: if (ceh == ' ') {
1490: spaces = true;
1491: } else {
1492: breaks = true;
1493: }
1494: }
1495: } else if (spaces || breaks) {
1496: if (leading) {
1497: if (spaces && breaks) {
1498: mixedBreaksSpaces = true;
1499: } else if (spaces) {
1500: leadingSpaces = true;
1501: } else if (breaks) {
1502: leadingBreaks = true;
1503: }
1504: } else {
1505: if (mixed) {
1506: mixedBreaksSpaces = true;
1507: } else if (spaces && breaks) {
1508: inlineBreaksSpaces = true;
1509: } else if (spaces) {
1510: inlineSpaces = true;
1511: } else if (breaks) {
1512: inlineBreaks = true;
1513: }
1514: }
1515: spaces = breaks = mixed = leading = false;
1516: }
1517:
1518: if ((spaces || breaks) && (index == scalar.length() - 1)) {
1519: if (spaces && breaks) {
1520: mixedBreaksSpaces = true;
1521: } else if (spaces) {
1522: trailingSpaces = true;
1523: if (leading) {
1524: leadingSpaces = true;
1525: }
1526: } else if (breaks) {
1527: trailingBreaks = true;
1528: if (leading) {
1529: leadingBreaks = true;
1530: }
1531: }
1532: spaces = breaks = mixed = leading = false;
1533: }
1534: index++;
1535: preceededBySpace = NULL_BL_T_LINEBR.indexOf(ceh) != -1;
1536: followedBySpace = index + 1 >= scalar.length()
1537: || NULL_BL_T_LINEBR.indexOf(scalar
1538: .charAt(index + 1)) != -1;
1539: }
1540: boolean allowFlowPlain = true;
1541: boolean allowBlockPlain = true;
1542: boolean allowSingleQuoted = true;
1543: boolean allowDoubleQuoted = true;
1544: boolean allowBlock = true;
1545:
1546: if (leadingSpaces || leadingBreaks || trailingSpaces) {
1547: allowFlowPlain = allowBlockPlain = allowBlock = false;
1548: }
1549:
1550: if (trailingBreaks) {
1551: allowFlowPlain = allowBlockPlain = false;
1552: }
1553:
1554: if (inlineBreaksSpaces) {
1555: allowFlowPlain = allowBlockPlain = allowSingleQuoted = false;
1556: }
1557:
1558: if (mixedBreaksSpaces || specialCharacters) {
1559: allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false;
1560: }
1561:
1562: if (inlineBreaks) {
1563: allowFlowPlain = allowBlockPlain = allowSingleQuoted = false;
1564: }
1565:
1566: if (trailingBreaks) {
1567: allowSingleQuoted = false;
1568: }
1569:
1570: if (lineBreaks) {
1571: allowFlowPlain = allowBlockPlain = false;
1572: }
1573:
1574: if (flowIndicators) {
1575: allowFlowPlain = false;
1576: }
1577:
1578: if (blockIndicators) {
1579: allowBlockPlain = false;
1580: }
1581:
1582: return new ScalarAnalysis(scalar, false, lineBreaks,
1583: allowFlowPlain, allowBlockPlain, allowSingleQuoted,
1584: allowDoubleQuoted, allowBlock);
1585: }
1586:
1587: static String determineChomp(final ByteList text) {
1588: char ceh = ' ';
1589: char ceh2 = ' ';
1590: if (text.realSize > 0) {
1591: ceh = (char) (text.bytes[text.realSize - 1] & 0xFF);
1592: if (text.realSize > 1) {
1593: ceh2 = (char) (text.bytes[text.realSize - 2] & 0xFF);
1594: }
1595: }
1596: return (ceh == '\n' || ceh == '\u0085') ? ((ceh2 == '\n' || ceh2 == '\u0085') ? "+"
1597: : "")
1598: : "-";
1599: }
1600:
1601: public static void main(final String[] args) throws IOException {
1602: final String filename = args[0]; // filename to test against
1603: System.out.println("File contents:");
1604: final BufferedInputStream read = new BufferedInputStream(
1605: new FileInputStream(filename));
1606: int last = -1;
1607: while ((last = read.read()) != -1) {
1608: System.out.print((char) last);
1609: }
1610: read.close();
1611: System.out.println("--------------------------------");
1612: final Emitter emitter = new EmitterImpl(System.out, YAML
1613: .config());
1614: final Parser pars = new ParserImpl(new ScannerImpl(
1615: new FileInputStream(filename)));
1616: for (final Iterator iter = pars.eachEvent(); iter.hasNext();) {
1617: emitter.emit((Event) iter.next());
1618: }
1619: }
1620: }// EmitterImpl
|