Search in sources :

Example 1 with PMap

use of net.morimekta.providence.descriptor.PMap in project providence by morimekta.

the class FastBinarySerializer method writeContainerEntry.

@SuppressWarnings("unchecked")
private int writeContainerEntry(LittleEndianBinaryWriter out, int typeid, PDescriptor descriptor, Object value) throws IOException {
    switch(typeid) {
        case VARINT:
            {
                if (value instanceof Boolean) {
                    return out.writeVarint(((Boolean) value ? 1 : 0));
                } else if (value instanceof Number) {
                    return out.writeZigzag(((Number) value).longValue());
                } else if (value instanceof PEnumValue) {
                    return out.writeZigzag(((PEnumValue) value).asInteger());
                } else {
                    throw new SerializerException("");
                }
            }
        case FIXED_64:
            {
                return out.writeDouble((Double) value);
            }
        case BINARY:
            {
                if (value instanceof CharSequence) {
                    byte[] bytes = ((String) value).getBytes(StandardCharsets.UTF_8);
                    int len = out.writeVarint(bytes.length);
                    out.write(bytes);
                    return len + bytes.length;
                } else if (value instanceof Binary) {
                    Binary bytes = (Binary) value;
                    int len = out.writeVarint(bytes.length());
                    bytes.write(out);
                    return len + bytes.length();
                } else {
                    throw new SerializerException("");
                }
            }
        case MESSAGE:
            {
                return writeMessage(out, (PMessage) value);
            }
        case COLLECTION:
            {
                if (value instanceof Map) {
                    Map<Object, Object> map = (Map<Object, Object>) value;
                    PMap<?, ?> desc = (PMap<?, ?>) descriptor;
                    int ktype = itemType(desc.keyDescriptor());
                    int vtype = itemType(desc.itemDescriptor());
                    int len = out.writeVarint(map.size() * 2);
                    len += out.writeVarint(ktype << 3 | vtype);
                    for (Map.Entry<Object, Object> entry : map.entrySet()) {
                        len += writeContainerEntry(out, ktype, desc.keyDescriptor(), entry.getKey());
                        len += writeContainerEntry(out, vtype, desc.itemDescriptor(), entry.getValue());
                    }
                    return len;
                } else if (value instanceof Collection) {
                    Collection<Object> coll = (Collection<Object>) value;
                    PContainer<?> desc = (PContainer<?>) descriptor;
                    int vtype = itemType(desc.itemDescriptor());
                    int len = out.writeVarint(coll.size());
                    len += out.writeVarint(vtype);
                    for (Object item : coll) {
                        len += writeContainerEntry(out, vtype, desc.itemDescriptor(), item);
                    }
                    return len;
                } else {
                    throw new SerializerException("");
                }
            }
        default:
            throw new SerializerException("");
    }
}
Also used : PEnumValue(net.morimekta.providence.PEnumValue) PMap(net.morimekta.providence.descriptor.PMap) PMessage(net.morimekta.providence.PMessage) PContainer(net.morimekta.providence.descriptor.PContainer) Collection(java.util.Collection) Binary(net.morimekta.util.Binary) Map(java.util.Map) PMap(net.morimekta.providence.descriptor.PMap)

Example 2 with PMap

use of net.morimekta.providence.descriptor.PMap in project providence by morimekta.

the class BaseTypeRegistryTest method testGetProvider_map.

@Test
public void testGetProvider_map() {
    PDescriptorProvider p1 = registry.getProvider("map<real,I>", "number", ImmutableMap.of("container", "sorted"));
    assertThat(p1.descriptor().getType(), is(PType.MAP));
    PMap map = (PMap) p1.descriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.DOUBLE));
    assertThat(map.itemDescriptor(), is(Imaginary.kDescriptor));
    p1 = registry.getProvider("map<real,map<i32,I>>", "number", ImmutableMap.of("container", "ordered"));
    assertThat(p1.descriptor().getType(), is(PType.MAP));
    map = (PMap) p1.descriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.DOUBLE));
    assertThat(map.itemDescriptor().getType(), is(PType.MAP));
    map = (PMap) map.itemDescriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.I32));
    assertThat(map.itemDescriptor(), is(Imaginary.kDescriptor));
}
Also used : PMap(net.morimekta.providence.descriptor.PMap) PDescriptorProvider(net.morimekta.providence.descriptor.PDescriptorProvider) Test(org.junit.Test)

Example 3 with PMap

use of net.morimekta.providence.descriptor.PMap in project providence by morimekta.

the class ProgramRegistryTest method testGetProvider_map.

@Test
public void testGetProvider_map() {
    PDescriptorProvider p1 = registry.getProvider("map<real,I>", "number", ImmutableMap.of("container", "sorted"));
    assertThat(p1.descriptor().getType(), is(PType.MAP));
    PMap map = (PMap) p1.descriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.DOUBLE));
    assertThat(map.itemDescriptor().getQualifiedName(), is(Imaginary.kDescriptor.getQualifiedName()));
    p1 = registry.getProvider("map<real,map<i32,I>>", "number", ImmutableMap.of("container", "ordered"));
    assertThat(p1.descriptor().getType(), is(PType.MAP));
    map = (PMap) p1.descriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.DOUBLE));
    assertThat(map.itemDescriptor().getType(), is(PType.MAP));
    map = (PMap) map.itemDescriptor();
    assertThat(map.keyDescriptor(), is(PPrimitive.I32));
    assertThat(map.itemDescriptor().getQualifiedName(), is(Imaginary.kDescriptor.getQualifiedName()));
}
Also used : PMap(net.morimekta.providence.descriptor.PMap) PDescriptorProvider(net.morimekta.providence.descriptor.PDescriptorProvider) Test(org.junit.Test)

Example 4 with PMap

use of net.morimekta.providence.descriptor.PMap 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 5 with PMap

use of net.morimekta.providence.descriptor.PMap 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)

Aggregations

PMap (net.morimekta.providence.descriptor.PMap)12 PList (net.morimekta.providence.descriptor.PList)6 PSet (net.morimekta.providence.descriptor.PSet)6 Map (java.util.Map)4 PDescriptor (net.morimekta.providence.descriptor.PDescriptor)4 PEnumDescriptor (net.morimekta.providence.descriptor.PEnumDescriptor)4 PEnumValue (net.morimekta.providence.PEnumValue)3 PMessageBuilder (net.morimekta.providence.PMessageBuilder)3 PContainer (net.morimekta.providence.descriptor.PContainer)3 LinkedHashMap (java.util.LinkedHashMap)2 PEnumBuilder (net.morimekta.providence.PEnumBuilder)2 PMessage (net.morimekta.providence.PMessage)2 ProvidenceConfigException (net.morimekta.providence.config.ProvidenceConfigException)2 PDescriptorProvider (net.morimekta.providence.descriptor.PDescriptorProvider)2 PMessageDescriptor (net.morimekta.providence.descriptor.PMessageDescriptor)2 SerializerException (net.morimekta.providence.serializer.SerializerException)2 TokenizerException (net.morimekta.providence.serializer.pretty.TokenizerException)2 TList (org.apache.thrift.protocol.TList)2 TMap (org.apache.thrift.protocol.TMap)2 TSet (org.apache.thrift.protocol.TSet)2