public Event produce() { // Parse the document end. Token token = scanner.peekToken(); Mark startMark = token.getStartMark(); Mark endMark = startMark; boolean explicit = false; if (scanner.checkToken(Token.ID.DocumentEnd)) { token = scanner.getToken(); endMark = token.getEndMark(); explicit = true; } Event event = new DocumentEndEvent(startMark, endMark, explicit); // Prepare the next state. state = new ParseDocumentStart(); return event; }
private void fetchStreamEnd() { // Set the current intendation to -1. unwindIndent(-1); // Reset simple keys. removePossibleSimpleKey(); this.allowSimpleKey = false; this.possibleSimpleKeys.clear(); // Read the token. Mark mark = reader.getMark(); // Add STREAM-END. Token token = new StreamEndToken(mark, mark); this.tokens.add(token); // The stream is finished. this.done = true; }
private String scanDirectiveIgnoredLine(Mark startMark) { // See the specification for details. while (reader.peek() == ' ') { reader.forward(); } if (reader.peek() == '#') { while (Constant.NULL_OR_LINEBR.hasNo(reader.peek())) { reader.forward(); } } int c = reader.peek(); String lineBreak = scanLineBreak(); if (lineBreak.length() == 0 && c != '\0') { final String s = String.valueOf(Character.toChars(c)); throw new ScannerException("while scanning a directive", startMark, "expected a comment or a line break, but found " + s + "(" + c + ")", reader.getMark()); } return lineBreak; }
/** * <p> * Read a %TAG directive value: * * <pre> * s-ignored-space+ c-tag-handle s-ignored-space+ ns-tag-prefix s-l-comments * </pre> * * </p> * * @see <a href="http://www.yaml.org/spec/1.1/#id896044"></a> */ private List<String> scanTagDirectiveValue(Mark startMark) { // See the specification for details. while (reader.peek() == ' ') { reader.forward(); } String handle = scanTagDirectiveHandle(startMark); while (reader.peek() == ' ') { reader.forward(); } String prefix = scanTagDirectivePrefix(startMark); List<String> result = new ArrayList<String>(2); result.add(handle); result.add(prefix); return result; }
private String scanFlowScalarBreaks(Mark startMark) { // See the specification for details. StringBuilder chunks = new StringBuilder(); while (true) { // Instead of checking indentation, we check for document // separators. String prefix = reader.prefix(3); if (("---".equals(prefix) || "...".equals(prefix)) && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) { throw new ScannerException("while scanning a quoted scalar", startMark, "found unexpected document separator", reader.getMark()); } // Scan past any number of spaces and tabs, ignoring them while (" \t".indexOf(reader.peek()) != -1) { reader.forward(); } // If we stopped at a line break, add that; otherwise, return the // assembled set of scalar breaks. String lineBreak = scanLineBreak(); if (lineBreak.length() != 0) { chunks.append(lineBreak); } else { return chunks.toString(); } } }
public ParserImpl(Scanner scanner) { this.scanner = scanner; currentEvent = null; directives = new VersionTagsTuple(null, new HashMap<String, String>(DEFAULT_TAGS)); states = new ArrayStack<Production>(100); marks = new ArrayStack<Mark>(10); state = new ParseStreamStart(); }
public DirectiveToken(String name, List<T> value, Mark startMark, Mark endMark) { super(startMark, endMark); this.name = name; if (value != null && value.size() != 2) { throw new YAMLException("Two strings must be provided instead of " + String.valueOf(value.size())); } this.value = value; }
public SequenceNode(Tag tag, boolean resolved, List<Node> value, Mark startMark, Mark endMark, Boolean flowStyle) { super(tag, startMark, endMark, flowStyle); if (value == null) { throw new NullPointerException("value in a Node is required."); } this.value = value; this.resolved = resolved; }
public ScalarNode(Tag tag, boolean resolved, String value, Mark startMark, Mark endMark, Character style) { super(tag, startMark, endMark); if (value == null) { throw new NullPointerException("value in a Node is required."); } this.value = value; this.style = style; this.resolved = resolved; }
public Node(Tag tag, Mark startMark, Mark endMark) { setTag(tag); this.startMark = startMark; this.endMark = endMark; this.type = Object.class; this.twoStepsConstruction = false; this.resolved = true; this.useClassConstructor = null; }
public MappingNode(Tag tag, boolean resolved, List<NodeTuple> value, Mark startMark, Mark endMark, Boolean flowStyle) { super(tag, startMark, endMark, flowStyle); if (value == null) { throw new NullPointerException("value in a Node is required."); } this.value = value; this.resolved = resolved; }
public DocumentStartEvent(Mark startMark, Mark endMark, boolean explicit, Version version, Map<String, String> tags) { super(startMark, endMark); this.explicit = explicit; this.version = version; // TODO enforce not null // if (tags == null) { // throw new NullPointerException("Tags must be provided."); // } this.tags = tags; }
/** * Fetch an entry in the block style. * * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a> */ private void fetchBlockEntry() { // Block context needs additional checks. if (this.flowLevel == 0) { // Are we allowed to start a new entry? if (!this.allowSimpleKey) { throw new ScannerException(null, null, "sequence entries are not allowed here", reader.getMark()); } // We may need to add BLOCK-SEQUENCE-START. if (addIndent(this.reader.getColumn())) { Mark mark = reader.getMark(); this.tokens.add(new BlockSequenceStartToken(mark, mark)); } } else { // It's an error for the block entry to occur in the flow // context,but we let the parser detect this. } // Simple keys are allowed after '-'. this.allowSimpleKey = true; // Reset possible simple key on the current level. removePossibleSimpleKey(); // Add BLOCK-ENTRY. Mark startMark = reader.getMark(); reader.forward(); Mark endMark = reader.getMark(); Token token = new BlockEntryToken(startMark, endMark); this.tokens.add(token); }
/** * Fetch a key in a block-style mapping. * * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a> */ private void fetchKey() { // Block context needs additional checks. if (this.flowLevel == 0) { // Are we allowed to start a key (not necessary a simple)? if (!this.allowSimpleKey) { throw new ScannerException(null, null, "mapping keys are not allowed here", reader.getMark()); } // We may need to add BLOCK-MAPPING-START. if (addIndent(this.reader.getColumn())) { Mark mark = reader.getMark(); this.tokens.add(new BlockMappingStartToken(mark, mark)); } } // Simple keys are allowed after '?' in the block context. this.allowSimpleKey = this.flowLevel == 0; // Reset possible simple key on the current level. removePossibleSimpleKey(); // Add KEY. Mark startMark = reader.getMark(); reader.forward(); Mark endMark = reader.getMark(); Token token = new KeyToken(startMark, endMark); this.tokens.add(token); }
public CollectionStartEvent(String anchor, String tag, boolean implicit, Mark startMark, Mark endMark, Boolean flowStyle) { super(anchor, startMark, endMark); this.tag = tag; this.implicit = implicit; this.flowStyle = flowStyle; }
public SimpleKey(int tokenNumber, boolean required, int index, int line, int column, Mark mark) { this.tokenNumber = tokenNumber; this.required = required; this.index = index; this.line = line; this.column = column; this.mark = mark; }
/** * Fetch an entry in the flow style. Flow-style entries occur either * immediately after the start of a collection, or else after a comma. * * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a> */ private void fetchFlowEntry() { // Simple keys are allowed after ','. this.allowSimpleKey = true; // Reset possible simple key on the current level. removePossibleSimpleKey(); // Add FLOW-ENTRY. Mark startMark = reader.getMark(); reader.forward(); Mark endMark = reader.getMark(); Token token = new FlowEntryToken(startMark, endMark); this.tokens.add(token); }
/** * <p> * Scan a sequence of %-escaped URI escape codes and convert them into a * String representing the unescaped values. * </p> * * FIXME This method fails for more than 256 bytes' worth of URI-encoded * characters in a row. Is this possible? Is this a use-case? * * @see <a href="http://www.ietf.org/rfc/rfc2396.txt"></a>, section 2.4, Escaped Encoding. */ private String scanUriEscapes(String name, Mark startMark) { // First, look ahead to see how many URI-escaped characters we should // expect, so we can use the correct buffer size. int length = 1; while (reader.peek(length * 3) == '%') { length++; } // See the specification for details. // URIs containing 16 and 32 bit Unicode characters are // encoded in UTF-8, and then each octet is written as a // separate character. Mark beginningMark = reader.getMark(); ByteBuffer buff = ByteBuffer.allocate(length); while (reader.peek() == '%') { reader.forward(); try { byte code = (byte) Integer.parseInt(reader.prefix(2), 16); buff.put(code); } catch (NumberFormatException nfe) { int c1 = reader.peek(); final String s1 = String.valueOf(Character.toChars(c1)); int c2 = reader.peek(1); final String s2 = String.valueOf(Character.toChars(c2)); throw new ScannerException("while scanning a " + name, startMark, "expected URI escape sequence of 2 hexadecimal numbers, but found " + s1 + "(" + c1 + ") and " + s2 + "(" + c2 + ")", reader.getMark()); } reader.forward(2); } buff.flip(); try { return UriEncoder.decode(buff); } catch (CharacterCodingException e) { throw new ScannerException("while scanning a " + name, startMark, "expected URI in UTF-8: " + e.getMessage(), beginningMark); } }
public ScalarEvent(String anchor, String tag, ImplicitTuple implicit, String value, Mark startMark, Mark endMark, Character style) { super(anchor, startMark, endMark); this.tag = tag; this.implicit = implicit; this.value = value; this.style = style; }
/** * We always add STREAM-START as the first token and STREAM-END as the last * token. */ private void fetchStreamStart() { // Read the token. Mark mark = reader.getMark(); // Add STREAM-START. Token token = new StreamStartToken(mark, mark); this.tokens.add(token); }
private Object[] scanBlockScalarBreaks(int indent) { // See the specification for details. StringBuilder chunks = new StringBuilder(); Mark endMark = reader.getMark(); int col = this.reader.getColumn(); // Scan for up to the expected indentation-level of spaces, then move // forward past that amount. while (col < indent && reader.peek() == ' ') { reader.forward(); col++; } // Consume one or more line breaks followed by any amount of spaces, // until we find something that isn't a line-break. String lineBreak = null; while ((lineBreak = scanLineBreak()).length() != 0) { chunks.append(lineBreak); endMark = reader.getMark(); // Scan past up to (indent) spaces on the next line, then forward // past them. col = this.reader.getColumn(); while (col < indent && reader.peek() == ' ') { reader.forward(); col++; } } // Return both the assembled intervening string and the end-mark. return new Object[] { chunks.toString(), endMark }; }
@SuppressWarnings({ "unchecked", "rawtypes" }) private Token scanDirective() { // See the specification for details. Mark startMark = reader.getMark(); Mark endMark; reader.forward(); String name = scanDirectiveName(startMark); List<?> value = null; if ("YAML".equals(name)) { value = scanYamlDirectiveValue(startMark); endMark = reader.getMark(); } else if ("TAG".equals(name)) { value = scanTagDirectiveValue(startMark); endMark = reader.getMark(); } else { endMark = reader.getMark(); int ff = 0; while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(ff))) { ff++; } if (ff > 0) { reader.forward(ff); } } scanDirectiveIgnoredLine(startMark); return new DirectiveToken(name, value, startMark, endMark); }
/** * Read a %YAML directive number: this is either the major or the minor * part. Stop reading at a non-digit character (usually either '.' or '\n'). * * @see <a href="http://www.yaml.org/spec/1.1/#id895631"></a> * @see <a href="http://www.yaml.org/spec/1.1/#ns-dec-digit"></a> */ private Integer scanYamlDirectiveNumber(Mark startMark) { // See the specification for details. char ch = reader.peek(); if (!Character.isDigit(ch)) { throw new ScannerException("while scanning a directive", startMark, "expected a digit, but found " + ch + "(" + ((int) ch) + ")", reader.getMark()); } int length = 0; while (Character.isDigit(reader.peek(length))) { length++; } Integer value = Integer.parseInt(reader.prefixForward(length)); return value; }
/** * Scan a %TAG directive's handle. This is YAML's c-tag-handle. * * @see <a href="http://www.yaml.org/spec/1.1/#id896876"></a> * @param startMark * @return */ private String scanTagDirectiveHandle(Mark startMark) { // See the specification for details. String value = scanTagHandle("directive", startMark); char ch = reader.peek(); if (ch != ' ') { throw new ScannerException("while scanning a directive", startMark, "expected ' ', but found " + reader.peek() + "(" + ch + ")", reader.getMark()); } return value; }
/** * Scan a %TAG directive's handle. This is YAML's c-tag-handle. * * @see <a href="http://www.yaml.org/spec/1.1/#id896876"></a> * @param startMark * @return */ private String scanTagDirectiveHandle(Mark startMark) { // See the specification for details. String value = scanTagHandle("directive", startMark); int c = reader.peek(); if (c != ' ') { final String s = String.valueOf(Character.toChars(c)); throw new ScannerException("while scanning a directive", startMark, "expected ' ', but found " + s + "(" + c + ")", reader.getMark()); } return value; }
private Object[] scanBlockScalarBreaks(int indent) { // See the specification for details. StringBuilder chunks = new StringBuilder(); Mark endMark = reader.getMark(); int ff = 0; int col = this.reader.getColumn(); // Scan for up to the expected indentation-level of spaces, then move // forward past that amount. while (col < indent && reader.peek(ff) == ' ') { ff++; col++; } if (ff > 0) { reader.forward(ff); } // Consume one or more line breaks followed by any amount of spaces, // until we find something that isn't a line-break. String lineBreak = null; while ((lineBreak = scanLineBreak()).length() != 0) { chunks.append(lineBreak); endMark = reader.getMark(); // Scan past up to (indent) spaces on the next line, then forward // past them. ff = 0; col = this.reader.getColumn(); while (col < indent && reader.peek(ff) == ' ') { ff++; col++; } if (ff > 0) { reader.forward(ff); } } // Return both the assembled intervening string and the end-mark. return new Object[] { chunks.toString(), endMark }; }
private String scanFlowScalarSpaces(Mark startMark) { // See the specification for details. StringBuilder chunks = new StringBuilder(); int length = 0; // Scan through any number of whitespace (space, tab) characters, // consuming them. while (" \t".indexOf(reader.peek(length)) != -1) { length++; } String whitespaces = reader.prefixForward(length); char ch = reader.peek(); if (ch == '\0') { // A flow scalar cannot end with an end-of-stream throw new ScannerException("while scanning a quoted scalar", startMark, "found unexpected end of stream", reader.getMark()); } // If we encounter a line break, scan it into our assembled string... String lineBreak = scanLineBreak(); if (lineBreak.length() != 0) { String breaks = scanFlowScalarBreaks(startMark); if (!"\n".equals(lineBreak)) { chunks.append(lineBreak); } else if (breaks.length() == 0) { chunks.append(" "); } chunks.append(breaks); } else { chunks.append(whitespaces); } return chunks.toString(); }
/** * <p> * Scan a Tag handle. A Tag handle takes one of three forms: * * <pre> * "!" (c-primary-tag-handle) * "!!" (ns-secondary-tag-handle) * "!(name)!" (c-named-tag-handle) * </pre> * * Where (name) must be formatted as an ns-word-char. * </p> * * @see <a href="http://www.yaml.org/spec/1.1/#c-tag-handle"></a> * @see <a href="http://www.yaml.org/spec/1.1/#ns-word-char"></a> * * <pre> * See the specification for details. * For some strange reasons, the specification does not allow '_' in * tag handles. I have allowed it anyway. * </pre> */ private String scanTagHandle(String name, Mark startMark) { char ch = reader.peek(); if (ch != '!') { throw new ScannerException("while scanning a " + name, startMark, "expected '!', but found " + ch + "(" + ((int) ch) + ")", reader.getMark()); } // Look for the next '!' in the stream, stopping if we hit a // non-word-character. If the first character is a space, then the // tag-handle is a c-primary-tag-handle ('!'). int length = 1; ch = reader.peek(length); if (ch != ' ') { // Scan through 0+ alphabetic characters. // FIXME According to the specification, these should be // ns-word-char only, which prohibits '_'. This might be a // candidate for a configuration option. while (Constant.ALPHA.has(ch)) { length++; ch = reader.peek(length); } // Found the next non-word-char. If this is not a space and not an // '!', then this is an error, as the tag-handle was specified as: // !(name) or similar; the trailing '!' is missing. if (ch != '!') { reader.forward(length); throw new ScannerException("while scanning a " + name, startMark, "expected '!', but found " + ch + "(" + ((int) ch) + ")", reader.getMark()); } length++; } String value = reader.prefixForward(length); return value; }
/** * <p> * Scan a Tag URI. This scanning is valid for both local and global tag * directives, because both appear to be valid URIs as far as scanning is * concerned. The difference may be distinguished later, in parsing. This * method will scan for ns-uri-char*, which covers both cases. * </p> * * <p> * This method performs no verification that the scanned URI conforms to any * particular kind of URI specification. * </p> * * @see <a href="http://www.yaml.org/spec/1.1/#ns-uri-char"></a> */ private String scanTagUri(String name, Mark startMark) { // See the specification for details. // Note: we do not check if URI is well-formed. StringBuilder chunks = new StringBuilder(); // Scan through accepted URI characters, which includes the standard // URI characters, plus the start-escape character ('%'). When we get // to a start-escape, scan the escaped sequence, then return. int length = 0; char ch = reader.peek(length); while (Constant.URI_CHARS.has(ch)) { if (ch == '%') { chunks.append(reader.prefixForward(length)); length = 0; chunks.append(scanUriEscapes(name, startMark)); } else { length++; } ch = reader.peek(length); } // Consume the last "chunk", which would not otherwise be consumed by // the loop above. if (length != 0) { chunks.append(reader.prefixForward(length)); length = 0; } if (chunks.length() == 0) { // If no URI was found, an error has occurred. throw new ScannerException("while scanning a " + name, startMark, "expected URI, but found " + ch + "(" + ((int) ch) + ")", reader.getMark()); } return chunks.toString(); }
/** * <p> * Scan a sequence of %-escaped URI escape codes and convert them into a * String representing the unescaped values. * </p> * * FIXME This method fails for more than 256 bytes' worth of URI-encoded * characters in a row. Is this possible? Is this a use-case? * * @see <a href="http://www.ietf.org/rfc/rfc2396.txt"></a>, section 2.4, Escaped Encoding. */ private String scanUriEscapes(String name, Mark startMark) { // First, look ahead to see how many URI-escaped characters we should // expect, so we can use the correct buffer size. int length = 1; while (reader.peek(length * 3) == '%') { length++; } // See the specification for details. // URIs containing 16 and 32 bit Unicode characters are // encoded in UTF-8, and then each octet is written as a // separate character. Mark beginningMark = reader.getMark(); ByteBuffer buff = ByteBuffer.allocate(length); while (reader.peek() == '%') { reader.forward(); try { byte code = (byte) Integer.parseInt(reader.prefix(2), 16); buff.put(code); } catch (NumberFormatException nfe) { throw new ScannerException("while scanning a " + name, startMark, "expected URI escape sequence of 2 hexadecimal numbers, but found " + reader.peek() + "(" + ((int) reader.peek()) + ") and " + reader.peek(1) + "(" + ((int) reader.peek(1)) + ")", reader.getMark()); } reader.forward(2); } buff.flip(); try { return UriEncoder.decode(buff); } catch (CharacterCodingException e) { throw new ScannerException("while scanning a " + name, startMark, "expected URI in UTF-8: " + e.getMessage(), beginningMark); } }
public Token(Mark startMark, Mark endMark) { if (startMark == null || endMark == null) { throw new YAMLException("Token requires marks."); } this.startMark = startMark; this.endMark = endMark; }
/** * Scan to the end of the line after a block scalar has been scanned; the * only things that are permitted at this time are comments and spaces. */ private String scanBlockScalarIgnoredLine(Mark startMark) { // See the specification for details. // Forward past any number of trailing spaces while (reader.peek() == ' ') { reader.forward(); } // If a comment occurs, scan to just before the end of line. if (reader.peek() == '#') { while (Constant.NULL_OR_LINEBR.hasNo(reader.peek())) { reader.forward(); } } // If the next character is not a null or line break, an error has // occurred. int c = reader.peek(); String lineBreak = scanLineBreak(); if (lineBreak.length() == 0 && c != '\0') { final String s = String.valueOf(Character.toChars(c)); throw new ScannerException("while scanning a block scalar", startMark, "expected a comment or a line break, but found " + s + "(" + c + ")", reader.getMark()); } return lineBreak; }
/** * Scan a %TAG directive's prefix. This is YAML's ns-tag-prefix. * * @see <a href="http://www.yaml.org/spec/1.1/#ns-tag-prefix"></a> */ private String scanTagDirectivePrefix(Mark startMark) { // See the specification for details. String value = scanTagUri("directive", startMark); int c = reader.peek(); if (Constant.NULL_BL_LINEBR.hasNo(c)) { final String s = String.valueOf(Character.toChars(c)); throw new ScannerException("while scanning a directive", startMark, "expected ' ', but found " + s + "(" + c + ")", reader.getMark()); } return value; }