com.yahoo.config.codegen.DefLine Maven / Gradle / Ivy
// Copyright 2017 Yahoo Holdings. 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 + "'");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy