GQLSelection.java
package net.morimekta.providence.graphql.gql;
import net.morimekta.providence.descriptor.PField;
import net.morimekta.util.collect.UnmodifiableList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import static net.morimekta.providence.graphql.gql.GQLUtil.baseField;
/**
* When defining a graphql each point in a selection set is called a
* 'selection'. A selection may be a field or a fragment, each having
* a distinct definition and uses.
*
* Most selections can be recursive, i.e. containing a selection set
* of it's own. The selection set is ordered, therefore it is returned
* as a list, not a set.
*/
public interface GQLSelection {
/**
* @return List if selections contained within this selection.
*/
@Nullable
List<GQLSelection> getSelectionSet();
/**
* Check if <b>any</b> of the given fields are in the selection.
* It will only look the the current struct, but check in all contained
* and referenced fragments. If only one field is given, this method
* will return true if and only if {@link #getSelection(PField)} returns
* a non-empty list.
*
* @param fields Fields to check selection for.
* @return True only if the current selection set contains ony
* of the provided fields.
*/
default boolean hasSelection(@Nonnull PField<?>... fields) {
// Everything requested.
if (getSelectionSet() == null) return true;
if (fields.length == 0) return false;
for (GQLSelection sel : getSelectionSet()) {
if (sel instanceof GQLField) {
GQLField sf = (GQLField) sel;
for (PField<?> field : fields) {
if (sf.representing(field)) {
return true;
}
}
} else if (sel instanceof GQLFragment) {
GQLFragment fragment = (GQLFragment) sel;
if (fragment.hasSelection(fields)) {
return true;
}
}
}
return false;
}
/**
* Browse downward fields so that we figure out if the selection (based on
* a field path based on the returning structure) if selected. E.g.
* <code>hasSelectionPath("a", "b")</code> will first look for the selection
* of "a", then for selection of "b" within a. Will treat a non-selection
* as the selection being there.
*
* @param fields Fields as in a field path to check from the current
* selection.
* @return If the selection path is selected or on per default.
*/
default boolean hasSelectionPath(@Nonnull PField<?>... fields) {
if (fields.length == 0) {
throw new IllegalArgumentException("No fields to check selection for");
}
if (fields.length == 1) {
return hasSelection(fields[0]);
}
// Part of default selection. Everything is selected...
if (getSelectionSet() == null) {
return true;
}
PField<?> field = fields[0];
PField<?>[] args = new PField[fields.length - 1];
System.arraycopy(fields, 1, args, 0, args.length);
for (GQLSelection sel : getSelection(field)) {
// Get field selection for the requested field.
if (sel.hasSelectionPath(args)) {
return true;
}
}
return false;
}
/**
* Get all selection entries related to the given field. It will
* only look at the current struct, but look at all levels of
* fragments.
*
* The result will contain one entry for each time the field is
* requested, so if multiple fragments requests the same field, this
* will return one entry per reference. If it contains more than one,
* the query <b>should</b> only have one such without alias, and all
* others <b>should</b> use unique aliases, but is not required.
*
* This method will return a non-empty list if and only if
* {@link #hasSelection(PField[])} called with the same field returns
* true.
*
* @param field The field to get selection for.
* @return List of selection related to the field.
*/
@Nonnull
default List<GQLSelection> getSelection(@Nonnull PField<?> field) {
if (getSelectionSet() == null) return UnmodifiableList.listOf();
field = baseField(field);
List<GQLSelection> selection = new ArrayList<>();
for (GQLSelection sel : getSelectionSet()) {
if (sel instanceof GQLField) {
GQLField sf = (GQLField) sel;
if (sf.representing(field)) {
selection.add(sf);
}
} else if (sel instanceof GQLFragment) {
GQLFragment fragment = (GQLFragment) sel;
selection.addAll(fragment.getSelection(field));
}
}
return UnmodifiableList.copyOf(selection);
}
}