use of net.morimekta.providence.config.ProvidenceConfigException in project providence by morimekta.
the class ProvidenceConfigParser method resolveAny.
private static Object resolveAny(ProvidenceConfigContext context, Token token, Tokenizer tokenizer) throws TokenizerException {
String key = token.asString();
String name = key;
String subKey = null;
if (key.contains(IDENTIFIER_SEP)) {
int idx = key.indexOf(IDENTIFIER_SEP);
name = key.substring(0, idx);
subKey = key.substring(idx + 1);
}
Object value = context.getReference(name, token, tokenizer);
if (subKey != null) {
if (!(value instanceof PMessage)) {
throw new TokenizerException(token, "Reference name " + key + " not declared");
}
try {
return ProvidenceConfigUtil.getInMessage((PMessage) value, subKey, null);
} catch (ProvidenceConfigException e) {
throw new TokenizerException(token, e.getMessage()).setLine(tokenizer.getLine()).initCause(e);
}
}
return value;
}
use of net.morimekta.providence.config.ProvidenceConfigException in project providence by morimekta.
the class ProvidenceConfigParser method parseMessage.
@SuppressWarnings("unchecked")
<M extends PMessage<M, F>, F extends PField> M parseMessage(@Nonnull Tokenizer tokenizer, @Nonnull ProvidenceConfigContext context, @Nonnull PMessageBuilder<M, F> builder) throws IOException {
PMessageDescriptor<M, F> descriptor = builder.descriptor();
Token token = tokenizer.expect("object end or field");
while (!token.isSymbol(Token.kMessageEnd)) {
if (!token.isIdentifier()) {
throw new TokenizerException(token, "Invalid field name: " + token.asString()).setLine(tokenizer.getLine());
}
F field = descriptor.findFieldByName(token.asString());
if (field == null) {
if (strict) {
throw new TokenizerException("No such field " + token.asString() + " in " + descriptor.getQualifiedName()).setLine(tokenizer.getLine());
} else {
token = tokenizer.expect("field value sep, message start or reference start");
if (token.isSymbol(DEFINE_REFERENCE)) {
context.setReference(context.initReference(tokenizer.expectIdentifier("reference name"), tokenizer), null);
// Ignore reference.
token = tokenizer.expect("field value sep or message start");
}
if (token.isSymbol(Token.kFieldValueSep)) {
token = tokenizer.expect("value declaration");
} else if (!token.isSymbol(Token.kMessageStart)) {
throw new TokenizerException(token, "Expected field-value separator or inherited message").setLine(tokenizer.getLine());
}
// Non-strict will just consume unknown fields, this way
// we can be forward-compatible when reading config.
consumeValue(context, tokenizer, token);
token = nextNotLineSep(tokenizer, "field or message end");
continue;
}
}
if (field.getType() == PType.MESSAGE) {
// go recursive with optional
String reference = null;
char symbol = tokenizer.expectSymbol("Message assigner or start", Token.kFieldValueSep, Token.kMessageStart, DEFINE_REFERENCE);
if (symbol == DEFINE_REFERENCE) {
Token ref = tokenizer.expectIdentifier("reference name");
if (strict) {
throw tokenizer.failure(ref, "Reusable objects are not allowed in strict mode.");
}
reference = context.initReference(ref, tokenizer);
symbol = tokenizer.expectSymbol("Message assigner or start after " + reference, Token.kFieldValueSep, Token.kMessageStart);
}
PMessageBuilder bld;
if (symbol == Token.kFieldValueSep) {
token = tokenizer.expect("reference or message start");
if (UNDEFINED.equals(token.asString())) {
// unset.
builder.clear(field.getId());
context.setReference(reference, null);
// special casing this, as we don't want to duplicate the parse line below.
token = nextNotLineSep(tokenizer, "field or message end");
continue;
}
// overwrite with new.
bld = ((PMessageDescriptor) field.getDescriptor()).builder();
if (token.isReferenceIdentifier()) {
// Inherit from reference.
try {
PMessage ref = resolve(context, token, tokenizer, field.getDescriptor());
if (ref != null) {
bld.merge(ref);
} else {
if (tokenizer.peek().isSymbol(Token.kMessageStart)) {
throw new TokenizerException(token, "Inherit from unknown reference %s", token.asString()).setLine(tokenizer.getLine());
} else if (strict) {
throw new TokenizerException(token, "Unknown reference %s", token.asString()).setLine(tokenizer.getLine());
}
}
} catch (ProvidenceConfigException e) {
throw new TokenizerException(token, "Unknown inherited reference '%s'", token.asString()).setLine(tokenizer.getLine());
}
token = tokenizer.expect("after message reference");
// we assume a new field or end of current message.
if (!token.isSymbol(Token.kMessageStart)) {
builder.set(field.getId(), context.setReference(reference, bld.build()));
continue;
}
} else if (!token.isSymbol(Token.kMessageStart)) {
throw new TokenizerException(token, "Unexpected token " + token.asString() + ", expected message start").setLine(tokenizer.getLine());
}
} else {
// extend in-line.
bld = builder.mutator(field.getId());
}
builder.set(field.getId(), context.setReference(reference, parseMessage(tokenizer, context, bld)));
} else if (field.getType() == PType.MAP) {
// maps can be extended the same way as
token = tokenizer.expect("field sep or value start");
Map baseValue = new LinkedHashMap<>();
String reference = null;
if (token.isSymbol(DEFINE_REFERENCE)) {
Token ref = tokenizer.expectIdentifier("reference name");
if (strict) {
throw tokenizer.failure(ref, "Reusable objects are not allowed in strict mode.");
}
reference = context.initReference(ref, tokenizer);
token = tokenizer.expect("field sep or value start");
}
if (token.isSymbol(Token.kFieldValueSep)) {
token = tokenizer.expect("field id or start");
if (UNDEFINED.equals(token.asString())) {
builder.clear(field.getId());
context.setReference(reference, null);
token = tokenizer.expect("message end or field");
continue;
} else if (token.isReferenceIdentifier()) {
try {
baseValue = resolve(context, token, tokenizer, field.getDescriptor());
} catch (ProvidenceConfigException e) {
throw new TokenizerException(token, e.getMessage()).setLine(tokenizer.getLine());
}
token = tokenizer.expect("map start or next field");
if (!token.isSymbol(Token.kMessageStart)) {
builder.set(field.getId(), context.setReference(reference, baseValue));
continue;
} else if (baseValue == null) {
baseValue = new LinkedHashMap<>();
}
}
} else {
baseValue.putAll(builder.build().get(field.getId()));
}
if (!token.isSymbol(Token.kMessageStart)) {
throw new TokenizerException(token, "Expected map start, but got '%s'", token.asString()).setLine(tokenizer.getLine());
}
Map map = parseMapValue(tokenizer, context, (PMap) field.getDescriptor(), baseValue);
builder.set(field.getId(), context.setReference(reference, map));
} else {
String reference = null;
// Simple fields *must* have the '=' separation, may have '&' reference.
if (tokenizer.expectSymbol("field value sep", Token.kFieldValueSep, DEFINE_REFERENCE) == DEFINE_REFERENCE) {
Token ref = tokenizer.expectIdentifier("reference name");
if (strict) {
throw tokenizer.failure(ref, "Reusable objects are not allowed in strict mode.");
}
reference = context.initReference(ref, tokenizer);
tokenizer.expectSymbol("field value sep", Token.kFieldValueSep);
}
token = tokenizer.expect("field value");
if (UNDEFINED.equals(token.asString())) {
builder.clear(field.getId());
context.setReference(reference, null);
} else {
Object value = parseFieldValue(token, tokenizer, context, field.getDescriptor(), strict);
builder.set(field.getId(), context.setReference(reference, value));
}
}
token = nextNotLineSep(tokenizer, "field or message end");
}
return builder.build();
}
use of net.morimekta.providence.config.ProvidenceConfigException in project providence by morimekta.
the class ProvidenceConfigParser method parseFieldValue.
@SuppressWarnings("unchecked")
Object parseFieldValue(Token next, Tokenizer tokenizer, ProvidenceConfigContext context, PDescriptor descriptor, boolean requireEnumValue) throws IOException {
try {
switch(descriptor.getType()) {
case BOOL:
if (TRUE.equals(next.asString())) {
return true;
} else if (FALSE.equals(next.asString())) {
return false;
} else if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
}
break;
case BYTE:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isInteger()) {
return (byte) next.parseInteger();
}
break;
case I16:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isInteger()) {
return (short) next.parseInteger();
}
break;
case I32:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isInteger()) {
return (int) next.parseInteger();
}
break;
case I64:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isInteger()) {
return next.parseInteger();
}
break;
case DOUBLE:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isInteger() || next.isReal()) {
return next.parseDouble();
}
break;
case STRING:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isStringLiteral()) {
return next.decodeLiteral(strict);
}
break;
case BINARY:
if (Token.B64.equals(next.asString())) {
tokenizer.expectSymbol("binary data enclosing start", Token.kParamsStart);
return Binary.fromBase64(tokenizer.readBinary(Token.kParamsEnd));
} else if (Token.HEX.equals(next.asString())) {
tokenizer.expectSymbol("binary data enclosing start", Token.kParamsStart);
return Binary.fromHexString(tokenizer.readBinary(Token.kParamsEnd));
} else if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
}
break;
case ENUM:
{
PEnumDescriptor ed = (PEnumDescriptor) descriptor;
PEnumValue value;
String name = next.asString();
if (next.isInteger()) {
value = ed.findById((int) next.parseInteger());
} else if (next.isIdentifier()) {
value = ed.findByName(name);
if (value == null && context.containsReference(name)) {
value = resolve(context, next, tokenizer, ed);
}
} else if (next.isReferenceIdentifier()) {
value = resolve(context, next, tokenizer, descriptor);
} else {
break;
}
if (value == null && (strict || requireEnumValue)) {
PEnumValue option = null;
if (next.isIdentifier()) {
for (PEnumValue o : ed.getValues()) {
if (o.getName().equalsIgnoreCase(name)) {
option = o;
break;
}
}
}
if (option != null) {
throw new TokenizerException(next, "No such enum value '%s' for %s, did you mean '%s'?", name, ed.getQualifiedName(), option.getName()).setLine(tokenizer.getLine());
}
throw new TokenizerException(next, "No such enum value '%s' for %s.", name, ed.getQualifiedName()).setLine(tokenizer.getLine());
}
return value;
}
case MESSAGE:
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isSymbol(Token.kMessageStart)) {
return parseMessage(tokenizer, context, ((PMessageDescriptor) descriptor).builder());
}
break;
case MAP:
{
if (next.isReferenceIdentifier()) {
Map resolved;
try {
// Make sure the reference is to a map.
resolved = resolve(context, next, tokenizer, descriptor);
} catch (ClassCastException e) {
throw new TokenizerException(next, "Reference %s is not a map field ", next.asString()).setLine(tokenizer.getLine());
}
return resolved;
} else if (next.isSymbol(Token.kMessageStart)) {
return parseMapValue(tokenizer, context, (PMap) descriptor, new LinkedHashMap());
}
break;
}
case SET:
{
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isSymbol(Token.kListStart)) {
@SuppressWarnings("unchecked") PSet<Object> ct = (PSet) descriptor;
Set<Object> value = new LinkedHashSet<>();
next = tokenizer.expect("set value or end");
while (!next.isSymbol(Token.kListEnd)) {
Object item = parseFieldValue(next, tokenizer, context, ct.itemDescriptor(), strict);
if (item != null) {
value.add(item);
}
// sets require separator, and allows separator after last.
if (tokenizer.expectSymbol("set separator or end", Token.kLineSep1, Token.kListEnd) == Token.kListEnd) {
break;
}
next = tokenizer.expect("set value or end");
}
return ct.builder().addAll(value).build();
}
break;
}
case LIST:
{
if (next.isReferenceIdentifier()) {
return resolve(context, next, tokenizer, descriptor);
} else if (next.isSymbol(Token.kListStart)) {
@SuppressWarnings("unchecked") PList<Object> ct = (PList) descriptor;
PList.Builder<Object> builder = ct.builder();
next = tokenizer.expect("list value or end");
while (!next.isSymbol(Token.kListEnd)) {
Object item = parseFieldValue(next, tokenizer, context, ct.itemDescriptor(), strict);
if (item != null) {
builder.add(item);
}
// lists require separator, and allows separator after last.
if (tokenizer.expectSymbol("list separator or end", Token.kLineSep1, Token.kListEnd) == Token.kListEnd) {
break;
}
next = tokenizer.expect("list value or end");
}
return builder.build();
}
break;
}
default:
{
throw new TokenizerException(next, descriptor.getType() + " not supported!").setLine(tokenizer.getLine());
}
}
} catch (ProvidenceConfigException e) {
throw new TokenizerException(next, e.getMessage()).setLine(tokenizer.getLine());
}
throw new TokenizerException(next, "Unhandled value \"%s\" for type %s", next.asString(), descriptor.getType()).setLine(tokenizer.getLine());
}
use of net.morimekta.providence.config.ProvidenceConfigException in project providence by morimekta.
the class ProvidenceConfigUtilTest method testAsType.
@Test
public void testAsType() throws ProvidenceConfigException {
// null value.
assertThat(asType(PPrimitive.STRING, null), is(nullValue()));
// ENUM
assertThat(asType(Value.kDescriptor, Value.SECOND), is(Value.SECOND));
assertThat(asType(Value.kDescriptor, 2), is(Value.SECOND));
assertThat(asType(Value.kDescriptor, "SECOND"), is(Value.SECOND));
assertThat(asType(Value.kDescriptor, (Numeric) () -> 2), is(Value.SECOND));
try {
asType(Value.kDescriptor, Value.kDescriptor);
fail("no exception");
} catch (ProvidenceConfigException e) {
assertThat(e.getMessage(), is("Unable to cast _Descriptor to enum config.Value"));
}
try {
asType(Value.kDescriptor, PApplicationExceptionType.INVALID_MESSAGE_TYPE);
fail("no exception");
} catch (ProvidenceConfigException e) {
assertThat(e.getMessage(), is("Unable to cast PApplicationExceptionType to enum config.Value"));
}
// MESSAGE
Service service = Service.builder().build();
Database db = Database.builder().build();
assertThat(asType(Service.kDescriptor, service), is(service));
try {
asType(Service.kDescriptor, db);
fail("no exception");
} catch (ProvidenceConfigException e) {
assertThat(e.getMessage(), is("Message type mismatch: config.Database is not compatible with config.Service"));
}
try {
asType(Service.kDescriptor, "foo");
fail("no exception");
} catch (ProvidenceConfigException e) {
assertThat(e.getMessage(), is("String is not compatible with message config.Service"));
}
// BINARY
assertThat(asType(PPrimitive.BINARY, Binary.fromHexString("abcd")), is(Binary.fromHexString("abcd")));
try {
asType(PPrimitive.BINARY, 123);
fail("no exception");
} catch (ProvidenceConfigException e) {
assertThat(e.getMessage(), is("Integer is not compatible with binary"));
}
// LIST
assertThat(asType(PList.provider(PPrimitive.STRING.provider()).descriptor(), ImmutableList.of(1, 2)), is(ImmutableList.of("1", "2")));
// SET
assertThat(new ArrayList((Collection) asType(PSet.sortedProvider(PPrimitive.STRING.provider()).descriptor(), ImmutableList.of(3, 4, 2, 1))), is(ImmutableList.of("1", "2", "3", "4")));
// MAP
Map<String, String> map = (Map) asType(PMap.sortedProvider(PPrimitive.STRING.provider(), PPrimitive.STRING.provider()).descriptor(), ImmutableMap.of(1, 2, 3, 4));
assertThat(map, is(instanceOf(ImmutableSortedMap.class)));
assertThat(map, is(ImmutableMap.of("1", "2", "3", "4")));
// General Failure
try {
asType(PPrimitive.VOID, "true");
fail("no exception");
} catch (IllegalStateException e) {
assertThat(e.getMessage(), is("Unhandled field type: void"));
}
}
use of net.morimekta.providence.config.ProvidenceConfigException in project providence by morimekta.
the class FormatUtils method collectConfigIncludes.
public static void collectConfigIncludes(File rc, Map<String, File> includes) throws IOException {
if (!rc.exists()) {
return;
}
rc = rc.getCanonicalFile();
if (!rc.isFile()) {
throw new ProvidenceConfigException("Rc file is not a file " + rc.getPath());
}
try {
SimpleTypeRegistry registry = new SimpleTypeRegistry();
registry.registerRecursively(ProvidenceTools.kDescriptor);
ProvidenceConfig loader = new ProvidenceConfig(registry);
ProvidenceTools config = loader.getConfig(rc);
if (config.hasIncludes()) {
File basePath = rc.getParentFile();
if (config.hasIncludesBasePath()) {
String base = config.getIncludesBasePath();
if (base.charAt(0) == '~') {
base = System.getenv("HOME") + base.substring(1);
}
basePath = new File(base);
if (!basePath.exists() || !basePath.isDirectory()) {
throw new ProvidenceConfigException("Includes Base path in " + rc.getPath() + " is not a directory: " + basePath);
}
}
for (String path : config.getIncludes()) {
File include = new File(basePath, path);
collectIncludes(include, includes);
}
}
} catch (SerializerException e) {
System.err.println("Config error: " + e.getMessage());
System.err.println(e.asString());
System.err.println();
throw new ArgumentException(e, "Exception when parsing " + rc.getCanonicalFile());
}
}
Aggregations