Search in sources :

Example 1 with ProvidenceConfigException

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;
}
Also used : PMessage(net.morimekta.providence.PMessage) TokenizerException(net.morimekta.providence.serializer.pretty.TokenizerException) ProvidenceConfigException(net.morimekta.providence.config.ProvidenceConfigException)

Example 2 with ProvidenceConfigException

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();
}
Also used : DEF(net.morimekta.providence.config.impl.ProvidenceConfigUtil.DEF) PMap(net.morimekta.providence.descriptor.PMap) Token(net.morimekta.providence.serializer.pretty.Token) TokenizerException(net.morimekta.providence.serializer.pretty.TokenizerException) ProvidenceConfigException(net.morimekta.providence.config.ProvidenceConfigException) LinkedHashMap(java.util.LinkedHashMap) PMessageBuilder(net.morimekta.providence.PMessageBuilder) PMessage(net.morimekta.providence.PMessage) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) PMap(net.morimekta.providence.descriptor.PMap)

Example 3 with ProvidenceConfigException

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());
}
Also used : LinkedHashSet(java.util.LinkedHashSet) PList(net.morimekta.providence.descriptor.PList) PEnumValue(net.morimekta.providence.PEnumValue) TokenizerException(net.morimekta.providence.serializer.pretty.TokenizerException) PEnumDescriptor(net.morimekta.providence.descriptor.PEnumDescriptor) ProvidenceConfigException(net.morimekta.providence.config.ProvidenceConfigException) LinkedHashMap(java.util.LinkedHashMap) PSet(net.morimekta.providence.descriptor.PSet) PMessageDescriptor(net.morimekta.providence.descriptor.PMessageDescriptor) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) PMap(net.morimekta.providence.descriptor.PMap)

Example 4 with ProvidenceConfigException

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"));
    }
}
Also used : Numeric(net.morimekta.util.Numeric) Database(net.morimekta.test.providence.config.Database) ArrayList(java.util.ArrayList) Service(net.morimekta.test.providence.config.Service) ProvidenceConfigUtil.asCollection(net.morimekta.providence.config.impl.ProvidenceConfigUtil.asCollection) Collection(java.util.Collection) ProvidenceConfigUtil.asString(net.morimekta.providence.config.impl.ProvidenceConfigUtil.asString) ProvidenceConfigUtil.asMap(net.morimekta.providence.config.impl.ProvidenceConfigUtil.asMap) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ImmutableSortedMap(com.google.common.collect.ImmutableSortedMap) PMap(net.morimekta.providence.descriptor.PMap) TreeMap(java.util.TreeMap) ProvidenceConfigException(net.morimekta.providence.config.ProvidenceConfigException) Test(org.junit.Test)

Example 5 with ProvidenceConfigException

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());
    }
}
Also used : SimpleTypeRegistry(net.morimekta.providence.util.SimpleTypeRegistry) ProvidenceTools(net.morimekta.providence.tools.common.ProvidenceTools) ArgumentException(net.morimekta.console.args.ArgumentException) ProvidenceConfig(net.morimekta.providence.config.ProvidenceConfig) File(java.io.File) SerializerException(net.morimekta.providence.serializer.SerializerException) ProvidenceConfigException(net.morimekta.providence.config.ProvidenceConfigException)

Aggregations

ProvidenceConfigException (net.morimekta.providence.config.ProvidenceConfigException)13 File (java.io.File)8 TokenizerException (net.morimekta.providence.serializer.pretty.TokenizerException)4 Test (org.junit.Test)4 LinkedHashMap (java.util.LinkedHashMap)3 Map (java.util.Map)3 PMap (net.morimekta.providence.descriptor.PMap)3 ArrayList (java.util.ArrayList)2 TreeMap (java.util.TreeMap)2 PMessage (net.morimekta.providence.PMessage)2 PMessageDescriptor (net.morimekta.providence.descriptor.PMessageDescriptor)2 ProvidenceTools (net.morimekta.providence.tools.common.ProvidenceTools)2 Database (net.morimekta.test.providence.config.Database)2 ImmutableMap (com.google.common.collect.ImmutableMap)1 ImmutableSortedMap (com.google.common.collect.ImmutableSortedMap)1 IOException (java.io.IOException)1 Collection (java.util.Collection)1 HashMap (java.util.HashMap)1 LinkedHashSet (java.util.LinkedHashSet)1 ArgumentException (net.morimekta.console.args.ArgumentException)1