com.yahoo.yqlplus.api.types.YQLStructType.java Source code

Java tutorial

Introduction

Here is the source code for com.yahoo.yqlplus.api.types.YQLStructType.java

Source

/*
 * Copyright (c) 2016 Yahoo Inc.
 * Licensed under the terms of the Apache version 2.0 license.
 * See LICENSE file for terms.
 */

package com.yahoo.yqlplus.api.types;

import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

public final class YQLStructType extends YQLType {
    public static boolean is(YQLType type) {
        return type instanceof YQLStructType;
    }

    public static class FieldBuilder {
        private final Annotations.Builder annotations = Annotations.builder();
        private final Builder builder;
        private final String name;
        private final YQLType type;

        public FieldBuilder(Builder builder, String name, YQLType type) {
            this.builder = builder;
            this.name = name;
            this.type = type;
        }

        public Builder add() {
            YQLNamePair field = new YQLNamePair(name, type, annotations.build());
            builder.addField(field);
            return builder;
        }

        public FieldBuilder annotate(String name, int value) {
            annotations.put(name, value);
            return this;
        }

        public FieldBuilder annotate(String name, String value) {
            annotations.put(name, value);
            return this;
        }
    }

    public static class Builder {
        private final Annotations.Builder annotations = Annotations.builder();
        private String name;
        private Map<String, YQLNamePair> fields = new LinkedHashMap<>();
        private boolean closed = true;

        public Builder merge(Builder other) {
            for (YQLNamePair field : other.fields.values()) {
                addField(field);
            }
            return this;
        }

        public Builder merge(YQLStructType other) {
            for (YQLNamePair field : other.fields.values()) {
                addField(field);
            }
            return this;
        }

        public Builder addField(YQLNamePair field) {
            YQLNamePair existing = getField(field.getName());
            if (existing != null && !existing.equals(field)) {
                throw new YQLTypeException("Adding conflicting definition of field named '" + field.getName()
                        + "'; " + existing + " != " + field);
            } else if (existing == null) {
                fields.put(field.getName(), field);
            }
            return this;
        }

        public Builder addField(String name, YQLType type) {
            addField(new YQLNamePair(name, type, Annotations.EMPTY));
            return this;
        }

        public FieldBuilder buildField(String name, YQLType type) {
            return new FieldBuilder(this, name, type);
        }

        public Builder addField(String name, YQLType type, boolean optional) {
            addField(new YQLNamePair(name,
                    optional ? YQLOptionalType.create(type) : YQLOptionalType.deoptional(type), Annotations.EMPTY));
            return this;
        }

        public YQLNamePair getField(String name) {
            return fields.get(name);
        }

        public String getName() {
            return name;
        }

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public boolean isClosed() {
            return closed;
        }

        public Builder setClosed(boolean closed) {
            this.closed = closed;
            return this;
        }

        public YQLStructType build() {
            if (name == null) {
                Hasher digest = Hashing.md5().newHasher();
                for (YQLNamePair field : fields.values()) {
                    field.hashTo(digest);
                }
                this.name = "struct_" + digest.hash().toString();
            }
            return new YQLStructType(annotations.build(), name, fields, closed);
        }

        public Builder annotate(String name, int value) {
            annotations.put(name, value);
            return this;
        }

        public Builder annotate(String name, String value) {
            annotations.put(name, value);
            return this;
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    private final Map<String, YQLNamePair> fields;
    private final boolean closed;

    YQLStructType(Annotations annotations, String name, Map<String, YQLNamePair> fields, boolean closed) {
        super(annotations, YQLCoreType.STRUCT, name);
        this.fields = Collections.unmodifiableMap(fields);
        this.closed = closed;
    }

    public Iterable<YQLNamePair> getFields() {
        return fields.values();
    }

    public YQLNamePair get(String name) {
        return fields.get(name);
    }

    public boolean isClosed() {
        return closed;
    }

    @Override
    public YQLType withAnnotations(Annotations newAnnotations) {
        return new YQLStructType(newAnnotations, getName(), fields, closed);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        YQLStructType that = (YQLStructType) o;

        if (closed != that.closed)
            return false;
        if (!fields.equals(that.fields))
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = fields.hashCode();
        result = 31 * result + (closed ? 1 : 0);
        return result;
    }

    @Override
    public void hashTo(Hasher digest) {
        super.hashTo(digest);
        for (YQLNamePair field : fields.values()) {
            field.hashTo(digest);
        }
        digest.putBoolean(closed);
    }

    @Override
    public String toString() {
        StringBuilder out = new StringBuilder();
        out.append("struct{");
        boolean first = true;
        for (YQLNamePair field : fields.values()) {
            if (!first) {
                out.append(",");
            }
            first = false;
            out.append(field.getName()).append(":").append(field.getValueType().toString());
        }
        out.append("}");
        return out.toString();
    }

    @Override
    protected boolean internalAssignableFrom(YQLType source) {
        if (getCoreType() != source.getCoreType()) {
            return false;
        }
        YQLStructType that = (YQLStructType) source;
        for (YQLNamePair field : fields.values()) {
            YQLNamePair otherField = that.get(field.getName());
            if (otherField == null && field.isRequired()) {
                return false;
            }
            if (!field.isAssignableFrom(otherField)) {
                return false;
            }
        }
        return true;
    }
}