GQLMethodCall.java

package net.morimekta.providence.graphql.gql;

import net.morimekta.providence.PMessage;
import net.morimekta.providence.descriptor.PField;
import net.morimekta.providence.descriptor.PServiceMethod;
import net.morimekta.util.collect.UnmodifiableList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import java.util.List;
import java.util.Objects;

import static net.morimekta.providence.graphql.gql.GQLUtil.toArgumentString;

/**
 * Representation of a method call in an operation. This is distinct
 * from a field request, as this level the call and response is ultimately
 * required, where as the fields are added per availability. It also
 * represents the call to an actual service which will provide the
 * response to be returned for the named JSON field.
 *
 * {@inheritDoc}
 */
@Immutable
public class GQLMethodCall implements GQLSelection {
    private final String             alias;
    private final PServiceMethod     method;
    private final PMessage<?>        arguments;
    private final List<GQLSelection> selectionSet;

    /**
     * Make a full field selection definition.
     *
     * @param method The method call to be represented.
     * @param alias The alias to be used in the JSON output.
     * @param arguments Arguments used for the method call.
     * @param selectionSet SelectionSet for sub-field selection.
     */
    public GQLMethodCall(@Nonnull PServiceMethod method,
                         @Nullable String alias,
                         @Nullable PMessage<?> arguments,
                         @Nullable List<GQLSelection> selectionSet) {
        this.alias = alias;
        this.method = method;
        this.arguments = arguments;
        if (selectionSet != null && selectionSet.size() > 0) {
            this.selectionSet = UnmodifiableList.copyOf(selectionSet);
        } else {
            this.selectionSet = null;
        }
    }

    /**
     * @return The output field alias.
     */
    @Nullable
    public String getAlias() {
        return alias;
    }

    /**
     * @return The method to be called to get field value. If message field null.
     */
    @Nonnull
    public PServiceMethod getMethod() {
        return method;
    }

    /**
     * @param <M> The argument message type.
     * @return The argument struct. If no arguments or params then null.
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public <M extends PMessage<M>>
    M getArguments() {
        return (M) arguments;
    }

    /**
     * @return The field's selection set, or null if none.
     */
    @Nullable
    public List<GQLSelection> getSelectionSet() {
        return selectionSet;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        if (alias != null) {
            builder.append(alias).append(": ");
        }
        builder.append(method.getName());

        if (arguments != null) {
            builder.append("(");
            boolean first = true;
            for (PField<?> pf : arguments.descriptor().getFields()) {
                if (arguments.has(pf.getId())) {
                    if (!first) {
                        builder.append(", ");
                    } else {
                        first = false;
                    }
                    builder.append(pf.getName())
                           .append(": ")
                           .append(toArgumentString(arguments.get(pf.getId())));
                }
            }
            builder.append(")");
        }
        if (selectionSet != null && selectionSet.size() > 0) {
            if (arguments != null) {
                builder.append(" ");
            }

            builder.append("{");
            boolean first = true;
            for (GQLSelection field : selectionSet) {
                if (!first) {
                    builder.append(", ");
                } else {
                    first = false;
                }
                builder.append(field.toString());
            }
            builder.append("}");
        }
        return builder.toString();
    }

    @Override
    public int hashCode() {
        return Objects.hash(GQLMethodCall.class,
                            alias, method, arguments, selectionSet);
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof GQLMethodCall)) return false;
        GQLMethodCall other = (GQLMethodCall) o;
        return Objects.equals(alias, other.alias) &&
               Objects.equals(method, other.method) &&
               Objects.equals(arguments, other.arguments) &&
               Objects.equals(selectionSet, other.selectionSet);
    }
}