Search in sources :

Example 1 with PMessageBuilder

use of net.morimekta.providence.PMessageBuilder 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 2 with PMessageBuilder

use of net.morimekta.providence.PMessageBuilder in project providence by morimekta.

the class OverrideConfigSupplier method buildOverrideConfig.

private static <Message extends PMessage<Message, Field>, Field extends PField> Message buildOverrideConfig(Message parent, Map<String, String> overrides, boolean strict) throws ProvidenceConfigException {
    PMessageBuilder<Message, Field> builder = parent.mutate();
    for (Map.Entry<String, String> override : overrides.entrySet()) {
        String[] path = override.getKey().split("[.]");
        String fieldName = lastFieldName(path);
        PMessageBuilder containedBuilder = builderForField(strict, builder, path);
        if (containedBuilder == null) {
            continue;
        }
        PField field = containedBuilder.descriptor().findFieldByName(fieldName);
        if (field == null) {
            if (strict) {
                throw new ProvidenceConfigException("No such field %s in %s [%s]", fieldName, containedBuilder.descriptor().getQualifiedName(), String.join(".", path));
            }
            continue;
        }
        if (UNDEFINED.equals(override.getValue())) {
            containedBuilder.clear(field.getId());
        } else {
            containedBuilder.set(field.getId(), readFieldValue(override.getKey(), override.getValue(), field.getDescriptor()));
        }
    }
    return builder.build();
}
Also used : PField(net.morimekta.providence.descriptor.PField) PMessage(net.morimekta.providence.PMessage) PMessageBuilder(net.morimekta.providence.PMessageBuilder) PField(net.morimekta.providence.descriptor.PField) ImmutableMap(com.google.common.collect.ImmutableMap) TreeMap(java.util.TreeMap) Map(java.util.Map)

Example 3 with PMessageBuilder

use of net.morimekta.providence.PMessageBuilder in project providence by morimekta.

the class ProvidenceConfigParser method parseDefinitionValue.

@SuppressWarnings("unchecked")
Object parseDefinitionValue(ProvidenceConfigContext context, Tokenizer tokenizer) throws IOException {
    Token token = tokenizer.expect("Start of def value");
    if (token.isReal()) {
        return Double.parseDouble(token.asString());
    } else if (token.isInteger()) {
        return Long.parseLong(token.asString());
    } else if (token.isStringLiteral()) {
        return token.decodeLiteral(strict);
    } else if (TRUE.equalsIgnoreCase(token.asString())) {
        return Boolean.TRUE;
    } else if (FALSE.equalsIgnoreCase(token.asString())) {
        return Boolean.FALSE;
    } else if (Token.B64.equals(token.asString())) {
        tokenizer.expectSymbol("binary data enclosing start", Token.kParamsStart);
        return Binary.fromBase64(tokenizer.readBinary(Token.kParamsEnd));
    } else if (Token.HEX.equals(token.asString())) {
        tokenizer.expectSymbol("binary data enclosing start", Token.kParamsStart);
        return Binary.fromHexString(tokenizer.readBinary(Token.kParamsEnd));
    } else if (token.isDoubleQualifiedIdentifier()) {
        // this may be an enum reference, must be
        // - package.EnumType.IDENTIFIER
        String id = token.asString();
        int l = id.lastIndexOf(Token.kIdentifierSep);
        try {
            PEnumDescriptor ed = registry.getEnumType(id.substring(0, l));
            PEnumValue val = ed.findByName(id.substring(l + 1));
            if (val == null && strict) {
                throw new TokenizerException(token, "Unknown %s value: %s", id.substring(0, l), id.substring(l + 1)).setLine(tokenizer.getLine());
            }
            // Note that unknown enum value results in null. Therefore we don't catch null values here.
            return val;
        } catch (IllegalArgumentException e) {
            // No such declared type.
            if (strict) {
                throw new TokenizerException(token, "Unknown enum identifier: %s", id.substring(0, l)).setLine(tokenizer.getLine());
            }
            consumeValue(context, tokenizer, token);
        } catch (ClassCastException e) {
            // Not an enum.
            throw new TokenizerException(token, "Identifier " + id + " does not reference an enum, from " + token.asString()).setLine(tokenizer.getLine());
        }
    } else if (token.isQualifiedIdentifier()) {
        // Message type.
        PMessageDescriptor descriptor;
        try {
            descriptor = registry.getMessageType(token.asString());
        } catch (IllegalArgumentException e) {
            // - strict mode: all types must be known.
            if (strict) {
                throw new TokenizerException(token, "Unknown declared type: %s", token.asString()).setLine(tokenizer.getLine());
            }
            consumeValue(context, tokenizer, token);
            return null;
        }
        PMessageBuilder builder = descriptor.builder();
        if (tokenizer.expectSymbol("message start or inherits", '{', ':') == ':') {
            token = tokenizer.expect("inherits reference");
            PMessage inheritsFrom = resolve(context, token, tokenizer, descriptor);
            if (inheritsFrom == null) {
                throw new TokenizerException(token, "Inheriting from null reference: %s", token.asString()).setLine(tokenizer.getLine());
            }
            builder.merge(inheritsFrom);
            tokenizer.expectSymbol("message start", '{');
        }
        return parseMessage(tokenizer, context, builder);
    } else {
        throw new TokenizerException(token, "Invalid define value " + token.asString()).setLine(tokenizer.getLine());
    }
    return null;
}
Also used : PMessageBuilder(net.morimekta.providence.PMessageBuilder) PMessage(net.morimekta.providence.PMessage) PEnumValue(net.morimekta.providence.PEnumValue) Token(net.morimekta.providence.serializer.pretty.Token) TokenizerException(net.morimekta.providence.serializer.pretty.TokenizerException) PMessageDescriptor(net.morimekta.providence.descriptor.PMessageDescriptor) PEnumDescriptor(net.morimekta.providence.descriptor.PEnumDescriptor)

Example 4 with PMessageBuilder

use of net.morimekta.providence.PMessageBuilder in project providence by morimekta.

the class HazelcastMessageBuilderStorage method putAllBuilders.

@Nonnull
@Override
@SuppressWarnings("unchecked")
public <B extends PMessageBuilder<Message, Field>> Map<Key, B> putAllBuilders(@Nonnull Map<Key, B> builders) {
    Map<Key, ICompletableFuture<Builder>> futureMap = new HashMap<>();
    builders.forEach((key, builder) -> futureMap.put(key, hazelcastMap.putAsync(key, (Builder) builder)));
    Map<Key, B> ret = new HashMap<>();
    futureMap.forEach((key, future) -> {
        try {
            Builder value = future.get();
            if (value != null) {
                ret.put(key, (B) value);
            }
        } catch (ExecutionException | InterruptedException e) {
            // TODO: Figure out if we timed out or were interrupted...
            throw new RuntimeException(e.getMessage(), e);
        }
    });
    return ret;
}
Also used : ICompletableFuture(com.hazelcast.core.ICompletableFuture) HashMap(java.util.HashMap) PMessageBuilder(net.morimekta.providence.PMessageBuilder) ExecutionException(java.util.concurrent.ExecutionException) Nonnull(javax.annotation.Nonnull)

Aggregations

PMessageBuilder (net.morimekta.providence.PMessageBuilder)4 PMessage (net.morimekta.providence.PMessage)3 Map (java.util.Map)2 Token (net.morimekta.providence.serializer.pretty.Token)2 TokenizerException (net.morimekta.providence.serializer.pretty.TokenizerException)2 ImmutableMap (com.google.common.collect.ImmutableMap)1 ICompletableFuture (com.hazelcast.core.ICompletableFuture)1 HashMap (java.util.HashMap)1 LinkedHashMap (java.util.LinkedHashMap)1 TreeMap (java.util.TreeMap)1 ExecutionException (java.util.concurrent.ExecutionException)1 Nonnull (javax.annotation.Nonnull)1 PEnumValue (net.morimekta.providence.PEnumValue)1 ProvidenceConfigException (net.morimekta.providence.config.ProvidenceConfigException)1 DEF (net.morimekta.providence.config.impl.ProvidenceConfigUtil.DEF)1 PEnumDescriptor (net.morimekta.providence.descriptor.PEnumDescriptor)1 PField (net.morimekta.providence.descriptor.PField)1 PMap (net.morimekta.providence.descriptor.PMap)1 PMessageDescriptor (net.morimekta.providence.descriptor.PMessageDescriptor)1