diff options
Diffstat (limited to 'configgen/src/main/java/com/yahoo/config/codegen/DefLine.java')
-rw-r--r-- | configgen/src/main/java/com/yahoo/config/codegen/DefLine.java | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java b/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java new file mode 100644 index 00000000000..f58c202f70f --- /dev/null +++ b/configgen/src/main/java/com/yahoo/config/codegen/DefLine.java @@ -0,0 +1,274 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.codegen; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + */ +public class DefLine { + private final static Pattern defaultPattern = Pattern.compile("^\\s*default\\s*=\\s*(\\S+)"); + private final static Pattern rangePattern = Pattern.compile("^\\s*range\\s*=\\s*([\\(\\[].*?[\\)\\]])"); + private final static Pattern restartPattern = Pattern.compile("^\\s*restart\\s*"); + private final static Pattern wordPattern = Pattern.compile("\\S+"); + private final static Pattern enumPattern = Pattern.compile("\\s*\\{(\\s*\\w+\\s*)+(\\s*,\\s*\\w+\\s*)*\\s*\\}"); + private final static Pattern enumPattern2 = Pattern.compile("\\s*,\\s*"); + private final static Pattern wordPattern2 = Pattern.compile("\\w+"); + private final static Pattern digitPattern = Pattern.compile("\\d"); + private final static Pattern namePattern = Pattern.compile("\\s*[a-zA-Z0-9_]+\\s*"); + private final static Pattern whitespacePattern = Pattern.compile("\\s+"); + + private String name = null; + private final Type type = new Type(); + + private DefaultValue defaultValue = null; + + private String range = null; + private boolean restart = false; + + String enumString = null; + final String[] enumArray = null; + + private final static Pattern defaultNullPattern = Pattern.compile("^\\s*default\\s*=\\s*null"); + + public DefLine(String line) { + StringBuilder sb = new StringBuilder(line); + int parsed = parseNameType(sb); + sb.delete(0, parsed); + if (type.name.equals("enum")) { + parsed = parseEnum(sb); + sb.delete(0, parsed); + } + + while (sb.length() > 0) { + parsed = parseOptions(sb); + sb.delete(0, parsed); + } + validateName(); + validateReservedWords(); + } + + /** + * Currently (2012-03-05) not used. Ranges are not checked by the + */ + public String getRange() { + return range; + } + + public DefaultValue getDefault() { + return defaultValue; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public boolean getRestart() { + return restart; + } + + public String getEnumString() { + return enumString; + } + + public String[] getEnumArray() { + return enumArray; + } + + /** + * Special function that searches through s and returns the index + * of the first occurrence of " that is not escaped. + */ + private String findStringEnd(CharSequence s, int from) { + boolean escaped = false; + for (int i = from; i < s.length(); i++) { + switch (s.charAt(i)) { + case'\\': + escaped = !escaped; + break; + case'"': + if (!escaped) { + return s.subSequence(from, i).toString(); + } + break; + } + } + return null; + } + + + private int parseOptions(CharSequence string) { + Matcher defaultNullMatcher = defaultNullPattern.matcher(string); + Matcher defaultMatcher = defaultPattern.matcher(string); + Matcher rangeMatcher = rangePattern.matcher(string); + Matcher restartMatcher = restartPattern.matcher(string); + + if (defaultNullMatcher.find()) { + throw new IllegalArgumentException("Null default value is not allowed: " + string.toString()); + } else if (defaultMatcher.find()) { + String deflt = defaultMatcher.group(1); + if (deflt.charAt(0) == '"') { + int begin = defaultMatcher.start(1) + 1; + deflt = findStringEnd(string, begin); + if (deflt == null) { + throw new IllegalArgumentException(string.toString()); + } + defaultValue = new DefaultValue(deflt, type); + return begin + deflt.length() + 1; + } else { + defaultValue = new DefaultValue(deflt, type); + } + return defaultMatcher.end(); + } else if (rangeMatcher.find()) { + range = rangeMatcher.group(1); + return rangeMatcher.end(); + } else if (restartMatcher.find()) { + restart = true; + return restartMatcher.end(); + } else { + throw new IllegalArgumentException(string.toString()); + } + } + + private int parseNameType(CharSequence string) { + Matcher wordMatcher = wordPattern.matcher(string); + if (wordMatcher.find()) { + name = wordMatcher.group(); + } + if (wordMatcher.find()) { + type.name = wordMatcher.group(); + } + if (type.name == null || name == null) { + throw new IllegalArgumentException(string.toString()); + } + return wordMatcher.end(); + } + + private int parseEnum(CharSequence string) { + Matcher enumMatcher = enumPattern.matcher(string); + if (enumMatcher.find()) { + enumString = enumMatcher.group(0).trim(); + } + if (enumString == null) { + throw new IllegalArgumentException(string + " is not valid syntax"); + } + enumString = enumString.replaceFirst("\\{\\s*", ""); + enumString = enumString.replaceFirst("\\s*\\}", ""); + String result[] = enumPattern2.split(enumString); + type.enumArray = new String[result.length]; + for (int i = 0; i < result.length; i++) { + String s = result[i].trim(); + type.enumArray[i] = s; + Matcher wordMatcher2 = wordPattern2.matcher(s); + if (!wordMatcher2.matches()) { + throw new IllegalArgumentException(s + " is not valid syntax"); + } + } + return enumMatcher.end(); + } + + public static class Type { + String name; + String[] enumArray; + + public Type(String name) { + this.name=name; + } + + public Type() { + } + + public String getName() { + return name; + } + + public String[] getEnumArray() { + return enumArray; + } + + public Type setEnumArray(String[] enumArray) { + this.enumArray = enumArray; + return this; + } + + public String toString() { + return "type " + name; + } + + } + + // A naive approach to imitate the checking previously done in make-config-preproc.pl + // TODO: method too long + void validateName() { + Matcher digitMatcher; + Matcher nameMatcher; + Matcher whitespaceMatcher; + + boolean atStart = true; + boolean arrayOk = true; + boolean mapOk = true; + for (int i = 0; i < name.length(); i++) { + String s = name.substring(i, i + 1); + digitMatcher = digitPattern.matcher(s); + nameMatcher = namePattern.matcher(s); + whitespaceMatcher = whitespacePattern.matcher(s); + if (atStart) { + if (digitMatcher.matches()) { + throw new IllegalArgumentException(name + " must start with a non-digit character"); + } + if (!nameMatcher.matches()) { + throw new IllegalArgumentException(name + " contains unexpected character"); + } + atStart = false; + } else { + if (nameMatcher.matches()) { + // do nothing + } else if (s.equals(".")) { + arrayOk = true; + mapOk = true; + atStart = true; + } else if (s.equals("[")) { + if (!arrayOk) { + throw new IllegalArgumentException(name + " Arrays cannot be multidimensional"); + } + arrayOk = false; + if ((i > (name.length() - 2)) || !(name.substring(i + 1, i + 2).equals("]"))) { + throw new IllegalArgumentException(name + " Expected ] to terminate array definition"); + } + i++; + } else if (s.equals("{")) { + if (!mapOk) { + throw new IllegalArgumentException(name + " Maps cannot be multidimensional"); + } + mapOk = false; + if ((i > (name.length() - 2)) || !(name.substring(i + 1, i + 2).equals("}"))) { + throw new IllegalArgumentException(name + " Expected } to terminate map definition"); + } + i++; + } else if (whitespaceMatcher.matches()) { + break; + } else { + throw new IllegalArgumentException(name + " contains unexpected character"); + } + } + } + } + + void validateReservedWords() { + if (ReservedWords.isReservedWord(name)) { + throw new IllegalArgumentException(name + " is a reserved word in " + + ReservedWords.getLanguageForReservedWord(name)); + } + if (ReservedWords.capitalizedPattern.matcher(name).matches()) { + throw new IllegalArgumentException("'" + name + "' cannot start with an uppercase letter"); + } + if (ReservedWords.internalPrefixPattern.matcher(name).matches()) { + throw new IllegalArgumentException("'" + name + "' cannot start with '" + ReservedWords.INTERNAL_PREFIX + "'"); + } + } +} + |