All Downloads are FREE. Search and download functionalities are using the official Maven repository.

graphql.schema.idl.SchemaParseOrder Maven / Gradle / Ivy

There is a newer version: 230521-nf-execution
Show newest version
package graphql.schema.idl;

import com.google.common.collect.ImmutableMap;
import graphql.language.SDLDefinition;
import graphql.language.SDLNamedDefinition;
import graphql.language.SourceLocation;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLEnumValueDefinition;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInputObjectField;
import graphql.schema.GraphQLNamedSchemaElement;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLSchemaElement;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;

import static java.util.Optional.ofNullable;

/**
 * This class will track what order {@link SDLDefinition} were parsed in
 * via {@link SchemaParser} and {@link TypeDefinitionRegistry}
 */
public class SchemaParseOrder implements Serializable {

    private final Map>> definitionOrder = new LinkedHashMap<>();


    /**
     * This map is the order in which {@link SDLDefinition}s were parsed per unique {@link SourceLocation#getSourceName()}.  If there
     * is no source then the empty string "" is used.
     *
     * @return a map of source names to definitions in parsed order
     */
    public Map>> getInOrder() {
        return ImmutableMap.copyOf(definitionOrder);
    }

    /**
     * This map is the order in which {@link SDLDefinition}s were parsed per unique {@link SourceLocation#getSourceName()} and it
     * only contains {@link SDLNamedDefinition}s.  If there is no source then the empty string "" is used.
     *
     * @return a map of source names to definitions in parsed order
     */
    public Map>> getInNameOrder() {
        Map>> named = new LinkedHashMap<>();
        definitionOrder.forEach((location, def) -> {
            List> namedDefs = def.stream()
                    .filter(d -> d instanceof SDLNamedDefinition)
                    .map(d -> (SDLNamedDefinition) d)
                    .collect(Collectors.toList());
            named.put(location, namedDefs);
        });
        return named;
    }

    /**
     * This comparator will sort according to the original parsed order
     *
     * @param  is a {@link GraphQLSchemaElement}
     *
     * @return a comparator that sorts schema elements in parsed order
     */
    public  Comparator getElementComparator() {
        // relies in the fact that names in graphql schema are unique across ALL elements
        Map namedSortValues = buildNameIndex(getInNameOrder());
        return (e1, e2) -> {
            if (isAssignable(e1, GraphQLFieldDefinition.class, GraphQLInputObjectField.class, GraphQLEnumValueDefinition.class) ||
                    isAssignable(e2, GraphQLFieldDefinition.class, GraphQLInputObjectField.class, GraphQLEnumValueDefinition.class)
            ) {
                return 0; // as is - they are not parsed tracked
            }
            if (isAssignable(e1, GraphQLDirective.class, GraphQLNamedType.class) && isAssignable(e2, GraphQLDirective.class, GraphQLNamedType.class)) {
                int sortVal1 = sortValue((GraphQLNamedSchemaElement) e1, namedSortValues);
                int sortVal2 = sortValue((GraphQLNamedSchemaElement) e2, namedSortValues);
                return Integer.compare(sortVal1, sortVal2);
            }
            return -1; // sort to the top
        };
    }

    private  boolean isAssignable(T e1, Class... classes) {
        return Arrays.stream(classes).anyMatch(aClass -> aClass.isAssignableFrom(e1.getClass()));
    }

    private Integer sortValue(GraphQLNamedSchemaElement e1, Map namedSortValues) {
        return namedSortValues.getOrDefault(e1.getName(), -1);
    }

    private Map buildNameIndex(Map>> inNameOrder) {
        Map nameIndex = new HashMap<>();
        int sourceIndex = 0;
        for (Map.Entry>> entry : inNameOrder.entrySet()) {
            List> namedDefs = entry.getValue();
            for (int i = 0; i < namedDefs.size(); i++) {
                SDLNamedDefinition namedDefinition = namedDefs.get(i);
                // we will put it in parsed order AND with first sources first
                int index = i + (sourceIndex * 100_000_000);
                nameIndex.put(namedDefinition.getName(), index);
            }
            sourceIndex++;
        }
        return nameIndex;
    }

    /**
     * This adds a new {@link SDLDefinition} to the order
     *
     * @param sdlDefinition the SDL definition to add
     * @param            for two
     *
     * @return this {@link SchemaParseOrder} for fluent building
     */
    public > SchemaParseOrder addDefinition(T sdlDefinition) {
        if (sdlDefinition != null) {
            definitionList(sdlDefinition).add(sdlDefinition);
        }
        return this;
    }

    /**
     * This removes a {@link SDLDefinition} from the order
     *
     * @param sdlDefinition the SDL definition to remove
     * @param            for two
     *
     * @return this {@link SchemaParseOrder} for fluent building
     */
    public > SchemaParseOrder removeDefinition(T sdlDefinition) {
        definitionList(sdlDefinition).remove(sdlDefinition);
        return this;
    }

    private > List> definitionList(T sdlDefinition) {
        String location = ofNullable(sdlDefinition.getSourceLocation())
                .map(SourceLocation::getSourceName).orElse("");
        return computeIfAbsent(location);
    }

    private List> computeIfAbsent(String location) {
        return definitionOrder.computeIfAbsent(location, k -> new ArrayList<>());
    }

    @Override
    public String toString() {
        return new StringJoiner(", ", SchemaParseOrder.class.getSimpleName() + "[", "]")
                .add("definitionOrder=" + definitionOrder)
                .toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy