diff options
Diffstat (limited to 'dcaedt_validator/kwalify/src/main/java/kwalify/PlainYamlParser.java')
-rw-r--r-- | dcaedt_validator/kwalify/src/main/java/kwalify/PlainYamlParser.java | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/dcaedt_validator/kwalify/src/main/java/kwalify/PlainYamlParser.java b/dcaedt_validator/kwalify/src/main/java/kwalify/PlainYamlParser.java new file mode 100644 index 0000000..5f23a19 --- /dev/null +++ b/dcaedt_validator/kwalify/src/main/java/kwalify/PlainYamlParser.java @@ -0,0 +1,742 @@ +/* + * copyright(c) 2005 kuwata-lab all rights reserved. + */ + +package kwalify; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.regex.Matcher; +import java.util.Calendar; +import java.util.TimeZone; + +/** + * plain yaml parser class which is a parent of YamlParser class. + */ +public class PlainYamlParser implements Parser { + + private static final String ANCHOR = "anchor '"; + private static final String ENDFLAG_EOF = "<EOF>"; + private static final String ENDFLAG_DOC_BEGIN = "---"; + private static final String ENDFLAG_DOC_END = "..."; + private static final String REGEXP1 = "^( *)(.*)"; + private static final String REGEXP2 = "^((?::?[-.\\w]+|'.*?'|\".*?\"|=|<<) *):(( +)(.*))?$"; + + public static class Alias { + private String label; + private int lineNum; + + Alias(String label, int lineNum) { + this.label = label; + this.lineNum = lineNum; + } + + String getLabel() { return label; } + + int getLineNumber() { return lineNum; } + } + + + private String[] lines; + private String line = null; + private int linenum = 0; + private Map<String,Object> anchors = new HashMap<>(); + private Map<String,Integer> aliases = new HashMap<>(); + private String endFlag = null; + private String sbuf = null; + private int index = 0; + + PlainYamlParser(String yamlStr) { + List list = Util.toListOfLines(yamlStr); + int len = list.size(); + lines = new String[len + 1]; + for (int i = 0; i < len; i++) { + lines[i + 1] = (String)list.get(i); + } + } + + public Object parse() throws SyntaxException { + Object data = parseChild(0); + if (data == null && endFlag.equals(ENDFLAG_DOC_BEGIN)) { + data = parseChild(0); + } + if (aliases.size() > 0) { + resolveAliases(data); + } + return data; + } + + public boolean hasNext() { + return !endFlag.equals(ENDFLAG_EOF); + } + + private List createSequence() { + return new ArrayList(); + } + + private void addSequenceValue(List seq, Object value) { + seq.add(value); + } + + private void setSequenceValueAt(List seq, int index, Object value) { + seq.set(index, value); + } + + Map createMapping() { + return new DefaultableHashMap(); + } + + private void setMappingValueWith(Map map, Object key, Object value) { + map.put(key, value); + } + + void setMappingDefault(Map map, Object value) { + if (map instanceof Defaultable) { + ((Defaultable)map).setDefault(value); + } + } + + private void mergeMapping(Map map, Map map2) { + for (Object key : map2.keySet()) { + if (!map.containsKey(key)) { + Object value = map2.get(key); + map.put(key, value); + } + } + } + + private void mergeList(Map map, List maplist) throws SyntaxException { + for (Object elem : maplist) { + mergeCollection(map, elem); + } + } + + private void mergeCollection(Map map, Object collection) throws SyntaxException { + if (collection instanceof Map) { + mergeMapping(map, (Map)collection); + } else if (collection instanceof List) { + mergeList(map, (List)collection); + } else { + throw syntaxError("'<<' requires collection (mapping, or sequence of mapping)."); + } + } + + private Object createScalar(Object value) { + return value; + } + + private String currentLine() { + return line; + } + + int currentLineNumber() { + return linenum; + } + + protected String getLine() { + String currentLine; + do { + currentLine = getCurrentLine(); + } while (currentLine != null && Util.matches(currentLine, "^\\s*($|#)")); + return currentLine; + } + + private String getCurrentLine() { + if (++linenum < lines.length) { + line = lines[linenum]; + if (Util.matches(line, "^\\.\\.\\.$")) { + line = null; + endFlag = ENDFLAG_DOC_END; + } else if (Util.matches(line, "^---( [!%].*)?$")) { + line = null; + endFlag = ENDFLAG_DOC_BEGIN; + } + } else { + line = null; + endFlag = ENDFLAG_EOF; + } + return line; + } + + private void resetBuffer(String str) { + sbuf = str.charAt(str.length() - 1) == '\n' ? str : str + "\n"; + index = -1; + } + + private int getCurrentCharacter() { + if (index + 1 < sbuf.length()) { + index++; + } else { + String currentLine = getLine(); + if (currentLine == null) { + return -1; + } + resetBuffer(currentLine); + index++; + } + return sbuf.charAt(index); + } + + private int getChar() { + int ch; + do { + ch = getCurrentCharacter(); + } while (ch >= 0 && isWhite(ch)); + return ch; + } + + private int getCharOrNewline() { + int ch; + do { + ch = getCurrentCharacter(); + } while (ch >= 0 && isWhite(ch) && ch != '\n'); + return ch; + } + + private int currentChar() { + return sbuf.charAt(index); + } + + private SyntaxException syntaxError(String message, int linenum) { + return new YamlSyntaxException(message, linenum); + } + + private SyntaxException syntaxError(String message) { + return new SyntaxException(message, linenum); + } + + private Object parseChild(int column) throws SyntaxException { + String currentLine = getLine(); + if (currentLine == null) { + return createScalar(null); + } + Matcher m = Util.matcher(currentLine, REGEXP1); + if (! m.find()) { + assert false; + return null; + } + int indent = m.group(1).length(); + if (indent < column) { + return createScalar(null); + } + String value = m.group(2); + return parseValue(column, value, indent); + } + + private Object parseValue(int column, String value, int valueStartColumn) throws SyntaxException { + Object data; + if (Util.matches(value, "^-( |$)")) { + data = parseSequence(valueStartColumn, value); + } else if (Util.matches(value, REGEXP2)) { + data = parseMapping(valueStartColumn, value); + } else if (Util.matches(value, "^[\\[\\{]")) { + data = parseFlowStyle(value); + } else if (Util.matches(value, "^\\&[-\\w]+( |$)")) { + data = parseAnchor(column, value); + } else if (Util.matches(value, "^\\*[-\\w]+( |$)")) { + data = parseAlias(value); + } else if (Util.matches(value, "^[|>]")) { + data = parseBlockText(column, value); + } else if (Util.matches(value, "^!")) { + data = parseTag(column, value); + } else if (Util.matches(value, "^\\#")) { + data = parseChild(column); + } else { + data = parseScalar(value); + } + return data; + } + + private static boolean isWhite(int ch) { + return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; + } + + + private Object parseFlowStyle(String value) throws SyntaxException { + resetBuffer(value); + getChar(); + Object data = parseFlow(0); + int ch = currentChar(); + assert ch == ']' || ch == '}'; + ch = getCharOrNewline(); + if (ch != '\n' && ch != '#' && ch >= 0) { + throw syntaxError("flow style sequence is closed buf got '" + ((char)ch) + "'."); + } + if (ch >= 0) { + getLine(); + } + return data; + } + + private Object parseFlow(int depth) throws SyntaxException { + int ch = currentChar(); + if (ch < 0) { + throw syntaxError("found EOF when parsing flow style."); + } + Object data; + if (ch == '[') { + data = parseFlowSequence(depth); + } else if (ch == '{') { + data = parseFlowMapping(depth); + } else { + data = parseFlowScalar(); + } + return data; + } + + private List parseFlowSequence(int depth) throws SyntaxException { + assert currentChar() == '['; + List seq = createSequence(); + int ch = getChar(); + if (ch != '}') { + addSequenceValue(seq, parseFlowSequenceItem(depth + 1)); + while ((ch = currentChar()) == ',') { + ch = getChar(); + if (ch == '}') { + throw syntaxError("sequence item required (or last comma is extra)."); + } + addSequenceValue(seq, parseFlowSequenceItem(depth + 1)); + } + } + if (currentChar() != ']') { + throw syntaxError("flow style sequence requires ']'."); + } + if (depth > 0) { + getChar(); + } + return seq; + } + + private Object parseFlowSequenceItem(int depth) throws SyntaxException { + return parseFlow(depth); + } + + private Map parseFlowMapping(int depth) throws SyntaxException { + assert currentChar() == '{'; + Map map = createMapping(); + int ch = getChar(); + if (ch != '}') { + Object[] pair = parseFlowMappingItem(depth + 1); + Object key = pair[0]; + Object value = pair[1]; + setMappingValueWith(map, key, value); + while ((ch = currentChar()) == ',') { + ch = getChar(); + if (ch == '}') { + throw syntaxError("mapping item required (or last comman is extra."); + } + pair = parseFlowMappingItem(depth + 1); + key = pair[0]; + value = pair[1]; + setMappingValueWith(map, key, value); + } + } + if (currentChar() != '}') { + throw syntaxError("flow style mapping requires '}'."); + } + if (depth > 0) { + getChar(); + } + return map; + } + + private Object[] parseFlowMappingItem(int depth) throws SyntaxException { + Object key = parseFlow(depth); + int ch = currentChar(); + if (ch != ':') { + String s = ch >= 0 ? "'" + ((char)ch) + "'" : "EOF"; + throw syntaxError("':' expected but got " + s); + } + getChar(); + Object value = parseFlow(depth); + return new Object[] { key, value }; + } + + private Object parseFlowScalar() { + int ch = currentChar(); + Object scalar; + StringBuilder sb = new StringBuilder(); + if (ch == '"' || ch == '\'') { + int endch = ch; + while ((ch = getCurrentCharacter()) >= 0 && ch != endch) { + sb.append((char)ch); + } + getChar(); + scalar = sb.toString(); + } else { + sb.append((char)ch); + while ((ch = getCurrentCharacter()) >= 0 && ch != ':' && ch != ',' && ch != ']' && ch != '}') { + sb.append((char)ch); + } + scalar = toScalar(sb.toString().trim()); + } + return createScalar(scalar); + } + + private Object parseTag(int column, String value) throws SyntaxException { + assert Util.matches(value, "^!\\S+"); + Matcher m = Util.matcher(value, "^!(\\S+)((\\s+)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String tag = m.group(1); + String space = m.group(3); + String value2 = m.group(4); + Object data; + if (value2 != null && value2.length() > 0) { + int valueStartColumn = column + 1 + tag.length() + space.length(); + data = parseValue(column, value2, valueStartColumn); + } else { + data = parseChild(column); + } + return data; + } + + private Object parseAnchor(int column, String value) throws SyntaxException { + assert Util.matches(value, "^\\&([-\\w]+)(( *)(.*))?$"); + Matcher m = Util.matcher(value, "^\\&([-\\w]+)(( *)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String label = m.group(1); + String space = m.group(3); + String value2 = m.group(4); + Object data; + if (value2 != null && value2.length() > 0) { + int valueStartColumn = column + 1 + label.length() + space.length(); + data = parseValue(column, value2, valueStartColumn); + } else { + data = parseChild(column); + } + registerAnchor(label, data); + return data; + } + + private void registerAnchor(String label, Object data) throws SyntaxException { + if (anchors.containsKey(label)) { + throw syntaxError(ANCHOR + label + "' is already used."); + } + anchors.put(label, data); + } + + private Object parseAlias(String value) throws SyntaxException { + assert value.matches("^\\*([-\\w]+)(( *)(.*))?$"); + Matcher m = Util.matcher(value, "^\\*([-\\w]+)(( *)(.*))?$"); + if (! m.find()) { + assert false; + return null; + } + String label = m.group(1); + String value2 = m.group(4); + if (value2 != null && value2.length() > 0 && value2.charAt(0) != '#') { + throw syntaxError("alias cannot take any data."); + } + Object data = anchors.get(label); + if (data == null) { + data = registerAlias(label); + } + getLine(); + return data; + } + + private Alias registerAlias(String label) { + aliases.merge(label, 1, (a, b) -> a + b); + return new Alias(label, linenum); + } + + + private void resolveAliases(Object data) throws SyntaxException { + Map resolved = new IdentityHashMap(); + resolveAliases(data, resolved); + } + + + private void resolveAliases(Object data, Map resolved) throws SyntaxException { + if (resolved.containsKey(data)) { + return; + } + resolved.put(data, data); + if (data instanceof List) { + resolveAliases((List)data, resolved); + } else if (data instanceof Map) { + resolveAliases((Map)data, resolved); + } else { + assert !(data instanceof Alias); + } + if (data instanceof Defaultable) { + Object defaultValue = ((Defaultable)data).getDefault(); + if (defaultValue != null) { + resolveAliases(defaultValue, resolved); + } + } + } + + private void resolveAliases(List seq, Map resolved) throws SyntaxException { + int len = seq.size(); + for (int i = 0; i < len; i++) { + Object val = seq.get(i); + if (val instanceof Alias) { + Alias alias = (Alias)val; + String label = alias.getLabel(); + if (anchors.containsKey(label)) { + setSequenceValueAt(seq, i, anchors.get(label)); + } else { + throw syntaxError(ANCHOR + alias.getLabel() + "' not found."); + } + } else if (val instanceof List || val instanceof Map) { + resolveAliases(val, resolved); + } + } + } + + private void resolveAliases(Map map, Map resolved) throws SyntaxException { + for (Object key : map.keySet()) { + Object val = map.get(key); + if (val instanceof Alias) { + Alias alias = (Alias) val; + String label = alias.getLabel(); + if (anchors.containsKey(label)) { + setMappingValueWith(map, key, anchors.get(label)); + } else { + throw syntaxError(ANCHOR + alias.getLabel() + "' not found.", alias.getLineNumber()); + } + } else if (val instanceof List || val instanceof Map) { + resolveAliases(val, resolved); + } + } + } + + private Object parseBlockText(int column, String value) throws SyntaxException { + assert Util.matches(value, "^[>|]"); + Matcher m = Util.matcher(value, "^([>|])([-+]?)(\\d*)\\s*(.*)$"); + if (! m.find()) { + assert false; + return null; + } + char blockChar = m.group(1).length() > 0 ? m.group(1).charAt(0) : '\0'; + char indicator = m.group(2).length() > 0 ? m.group(2).charAt(0) : '\0'; + int indent = m.group(3).length() > 0 ? Integer.parseInt(m.group(3)) : -1; + String text = m.group(4); + char sep = blockChar == '|' ? '\n' : ' '; + String currentLine; + StringBuilder sb = new StringBuilder(); + int n = 0; + while ((currentLine = getCurrentLine()) != null) { + m = Util.matcher(currentLine, "^( *)(.*)$"); + m.find(); + String space = m.group(1); + String str = m.group(2); + if (indent < 0) { + indent = space.length(); + } + if (str.length() == 0) { + n++; + } else { + int slen = space.length(); + if (slen < column) { + break; + } else if (slen < indent) { + throw syntaxError("invalid indent in block text."); + } else { + if (n > 0) { + if (blockChar == '>' && sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + for (int i = 0; i < n; i++) { + sb.append('\n'); + } + n = 0; + } + str = currentLine.substring(indent); + } + } + sb.append(str); + if ((blockChar == '>') && (sb.charAt(sb.length() - 1) == '\n')) { + sb.setCharAt(sb.length() - 1, ' '); + } + } + if (currentLine != null && Util.matches(currentLine, "^ *#")) { + getLine(); + } + switch (indicator) { + case '+': + handlePlus(blockChar, sb, n); + break; + case '-': + handleMinus(sep, sb); + break; + default: + if (blockChar == '>') { + sb.setCharAt(sb.length() - 1, '\n'); + } + } + return createScalar(text + sb.toString()); + } + + private void handleMinus(char sep, StringBuilder sb) { + if (sb.charAt(sb.length() - 1) == sep) { + sb.deleteCharAt(sb.length() - 1); + } + } + + private void handlePlus(char blockChar, StringBuilder sb, int n) { + if (n > 0) { + if (blockChar == '>') { + sb.setCharAt(sb.length() - 1, '\n'); + } + for (int i = 0; i < n; i++) { + sb.append('\n'); + } + } + } + + + private List parseSequence(int column, String value) throws SyntaxException { + assert Util.matches(value, "^-(( +)(.*))?$"); + List seq = createSequence(); + while (true) { + Matcher m = Util.matcher(value, "^-(( +)(.*))?$"); + if (! m.find()) { + throw syntaxError("sequence item is expected."); + } + String space = m.group(2); + String value2 = m.group(3); + int column2 = column + 1; + + Object elem; + if (value2 == null || value2.length() == 0) { + elem = parseChild(column2); + } else { + int valueStartColumn = column2 + space.length(); + elem = parseValue(column2, value2, valueStartColumn); + } + addSequenceValue(seq, elem); + + String currentLine = currentLine(); + if (currentLine == null) { + break; + } + Matcher m2 = Util.matcher(currentLine, REGEXP1); + m2.find(); + int indent = m2.group(1).length(); + if (indent < column) { + break; + } else if (indent > column) { + throw syntaxError("invalid indent of sequence."); + } + value = m2.group(2); + } + return seq; + } + + + private Map parseMapping(int column, String value) throws SyntaxException { + assert Util.matches(value, REGEXP2); + Map map = createMapping(); + while (true) { + Matcher m = Util.matcher(value, REGEXP2); + if (! m.find()) { + throw syntaxError("mapping item is expected."); + } + String v = m.group(1).trim(); + Object key = toScalar(v); + String value2 = m.group(4); + int column2 = column + 1; + + Object elem; + if (value2 == null || value2.length() == 0) { + elem = parseChild(column2); + } else { + int valueStartColumn = column2 + m.group(1).length() + m.group(3).length(); + elem = parseValue(column2, value2, valueStartColumn); + } + if ("=".equals(v)) { + setMappingDefault(map, elem); + } else if ("<<".equals(v)) { + mergeCollection(map, elem); + } else { + setMappingValueWith(map, key, elem); + } + + String currentLine = currentLine(); + if (currentLine == null) { + break; + } + Matcher m2 = Util.matcher(currentLine, REGEXP1); + m2.find(); + int indent = m2.group(1).length(); + if (indent < column) { + break; + } else if (indent > column) { + throw syntaxError("invalid indent of mapping."); + } + value = m2.group(2); + } + return map; + } + + + private Object parseScalar(String value) { + Object data = createScalar(toScalar(value)); + getLine(); + return data; + } + + + private Object toScalar(String value) { + Matcher m; + if ((m = Util.matcher(value, "^\"(.*)\"([ \t]*#.*$)?")).find()) { + return m.group(1); + } else if ((m = Util.matcher(value, "^'(.*)'([ \t]*#.*$)?")).find()) { + return m.group(1); + } else if ((m = Util.matcher(value, "^(.*\\S)[ \t]*#")).find()) { + value = m.group(1); + } + + if (Util.matches(value, "^-?0x\\d+$")) { + return Integer.parseInt(value, 16); + } else if (Util.matches(value, "^-?0\\d+$")) { + return Integer.parseInt(value, 8); + } else if (Util.matches(value, "^-?\\d+$")) { + return Integer.parseInt(value, 10); + } else if (Util.matches(value, "^-?\\d+\\.\\d+$")) { + return Double.parseDouble(value); + } else if (Util.matches(value, "^(true|yes|on)$")) { + return Boolean.TRUE; + } else if (Util.matches(value, "^(false|no|off)$")) { + return Boolean.FALSE; + } else if (Util.matches(value, "^(null|~)$")){ + return null; + } else if (Util.matches(value, "^:(\\w+)$")) { + return value; + } else if ((m = Util.matcher(value, "^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)$")).find()) { + int year = Integer.parseInt(m.group(1)); + int month = Integer.parseInt(m.group(2)); + int day = Integer.parseInt(m.group(3)); + Calendar cal = Calendar.getInstance(); + cal.set(year, month, day, 0, 0, 0); + return cal.getTime(); + } else if ((m = Util.matcher(value, "^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)(?:[Tt]|[ \t]+)(\\d\\d?):(\\d\\d):(\\d\\d)(\\.\\d*)?(?:Z|[ \t]*([-+]\\d\\d?)(?::(\\d\\d))?)?$")).find()) { + int year = Integer.parseInt(m.group(1)); + int month = Integer.parseInt(m.group(2)); + int day = Integer.parseInt(m.group(3)); + int hour = Integer.parseInt(m.group(4)); + int min = Integer.parseInt(m.group(5)); + int sec = Integer.parseInt(m.group(6)); + + String timezone = "GMT" + m.group(8) + ":" + m.group(9); + Calendar cal = Calendar.getInstance(); + cal.set(year, month, day, hour, min, sec); + cal.setTimeZone(TimeZone.getTimeZone(timezone)); + return cal.getTime(); + } else { + return value; + } + } + +} |