GQLField.java
package net.morimekta.providence.graphql.gql;
import net.morimekta.providence.PMessage;
import net.morimekta.providence.descriptor.PField;
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.baseField;
import static net.morimekta.providence.graphql.gql.GQLUtil.toArgumentString;
/**
* Representation of a field in the response JSON. This is called
* a 'field' as it references a field of data in the JSON response,
* unrelated to message fields.
*
* This may represent a message field or a method call, but not both.
* And may always have an alias.
*
* If the type of the field, or return type of the message is a message
* or collection of messages, it may have a selection set.
*
* {@inheritDoc}
*/
@Immutable
public class GQLField implements GQLSelection {
@Nullable
private final String alias;
@Nonnull
private final PField field;
@Nullable
private final PMessage<?> arguments;
@Nullable
private final List<GQLSelection> selectionSet;
/**
* Make a simple field selection based on a message field.
*
* @param field The field to be represented.
*/
public GQLField(@Nonnull PField field) {
this(field, null, null, null);
}
/**
* Make a simple field selection based on a message field.
*
* @param field The field to be represented.
* @param selectionSet SelectionSet for sub-field selection.
*/
public GQLField(@Nonnull PField field, @Nullable List<GQLSelection> selectionSet) {
this(field, null, null, selectionSet);
}
/**
* Make a full field selection definition.
*
* @param field The field to be represented.
* @param alias The alias to be used in the JSON output.
* @param arguments Arguments used for field mutators.
* @param selectionSet SelectionSet for sub-field selection.
*/
public GQLField(@Nonnull PField field,
@Nullable String alias,
@Nullable PMessage<?> arguments,
@Nullable List<GQLSelection> selectionSet) {
this.alias = alias;
this.field = field;
this.arguments = arguments;
if (selectionSet != null && selectionSet.size() > 0) {
this.selectionSet = UnmodifiableList.copyOf(selectionSet);
} else {
this.selectionSet = null;
}
}
/**
* Check if the input field is represented with this field selection. It will
* match if it is the same field as this, or if it is the same field as what
* this field is overriding (from implementing interface).
* <p>
* E.g. if 'A' is an interface field, and 'B' and 'C' are the equivalent field
* in two different messages, and this selection is for 'B', then it is
* representing 'A', and 'B', but not 'C'. But if this is a selection for 'A',
* then all three will match.
*
* @param inf The field to check.
* @return If the field is represented by this selection.
*/
public boolean representing(PField inf) {
return field.equals(inf) || baseField(field).equals(inf) || field.equals(baseField(inf));
}
/**
* @return The output field alias.
*/
@Nullable
public String getAlias() {
return alias;
}
/**
* @return The message field to be represented.
*/
@Nonnull
public PField getField() {
return field;
}
/**
* @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(field.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(GQLField.class,
alias, field, arguments, selectionSet);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof GQLField)) return false;
GQLField other = (GQLField) o;
return Objects.equals(alias, other.alias) &&
Objects.equals(field, other.field) &&
Objects.equals(arguments, other.arguments) &&
Objects.equals(selectionSet, other.selectionSet);
}
}