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

com.distelli.graphql.apigen.STModel Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
package com.distelli.graphql.apigen;

import graphql.language.Definition;
import graphql.language.EnumTypeDefinition;
import graphql.language.EnumValueDefinition;
import graphql.language.FieldDefinition;
import graphql.language.InputObjectTypeDefinition;
import graphql.language.InputValueDefinition;
import graphql.language.InterfaceTypeDefinition;
import graphql.language.ListType;
import graphql.language.NonNullType;
import graphql.language.ObjectTypeDefinition;
import graphql.language.OperationTypeDefinition;
import graphql.language.ScalarTypeDefinition;
import graphql.language.SchemaDefinition;
import graphql.language.Type;
import graphql.language.TypeName;
import graphql.language.UnionTypeDefinition;
import graphql.language.Value;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class STModel {
    private static Map BUILTINS = new HashMap(){{
            put("Int", null);
            put("Long", null);
            put("Float", null);
            put("String", null);
            put("Boolean", null);
            put("ID", null);
            put("BigInteger", "java.math.BigInteger");
            put("BigDecimal", "java.math.BigDecimal");
            put("Byte", null);
            put("Short", null);
            put("Char", null);
        }};
    private static Map RENAME = new HashMap(){{
            put("Int", "Integer");
            put("ID", "String");
            put("Char", "Character");
        }};
    public static class Builder {
        private TypeEntry typeEntry;
        private Map referenceTypes;
        public Builder withTypeEntry(TypeEntry typeEntry) {
            this.typeEntry = typeEntry;
            return this;
        }
        public Builder withReferenceTypes(Map referenceTypes) {
            this.referenceTypes = referenceTypes;
            return this;
        }
        public STModel build() {
            return new STModel(this);
        }
    }
    public static class DataResolver {
        public String fieldName;
        public String fieldType;
        public int listDepth;
    }
    public static class Arg {
        public String name;
        public String type;
        public String graphQLType;
        public String defaultValue;
        public Arg(String name, String type) {
            this.name = name;
            this.type = type;
        }
        public String getUcname() {
            return ucFirst(name);
        }
    }
    // Field of Interface, Object, InputObject, UnionType (no names), Enum (no types)
    public static class Field {
        public String name;
        public String type;
        public DataResolver dataResolver;
        public String graphQLType;
        public List args;
        public String defaultValue;
        public Field(String name, String type) {
            this.name = name;
            this.type = type;
        }
        public String getUcname() {
            return ucFirst(name);
        }
    }
    private TypeEntry typeEntry;
    private Map referenceTypes;
    private List fields;
    private List imports;
    private Field idField;
    private boolean gotIdField = false;
    private STModel(Builder builder) {
        this.typeEntry = builder.typeEntry;
        this.referenceTypes = builder.referenceTypes;
    }

    public void validate() {
        // TODO: Validate that any Object "implements" actually implements
        // the interface so we can error before compile time...

        // these throw if there are any inconsistencies...
        getFields();
        getImports();
    }

    public boolean isObjectType() {
        return typeEntry.getDefinition() instanceof ObjectTypeDefinition;
    }

    public boolean isInterfaceType() {
        return typeEntry.getDefinition() instanceof InterfaceTypeDefinition;
    }

    public boolean isEnumType() {
        return typeEntry.getDefinition() instanceof EnumTypeDefinition;
    }

    public boolean isScalarType() {
        return typeEntry.getDefinition() instanceof ScalarTypeDefinition;
    }

    public boolean isUnionType() {
        return typeEntry.getDefinition() instanceof UnionTypeDefinition;
    }

    public boolean isInputObjectType() {
        return typeEntry.getDefinition() instanceof InputObjectTypeDefinition;
    }

    public boolean isSchemaType() {
        return typeEntry.getDefinition() instanceof SchemaDefinition;
    }

    public String getPackageName() {
        return typeEntry.getPackageName();
    }

    public String getName() {
        return typeEntry.getName();
    }

    public String getUcname() {
        return ucFirst(getName());
    }

    private static String ucFirst(String name) {
        if ( null == name || name.length() < 1 ) return name;
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }

    private static String lcFirst(String name) {
        if ( null == name || name.length() < 1 ) return name;
        return name.substring(0, 1).toLowerCase() + name.substring(1);
    }

    public synchronized Field getIdField() {
        if ( ! gotIdField ) {
            for ( Field field : getFields() ) {
                if ( "id".equals(field.name) ) {
                    idField = field;
                    break;
                }
            }
            gotIdField = true;
        }
        return idField;
    }

    public List getDataResolvers() {
        Map resolvers = new LinkedHashMap<>();
        for ( Field field : getFields() ) {
            DataResolver resolver = field.dataResolver;
            if ( null == resolver ) continue;
            resolvers.put(resolver.fieldType, resolver);
        }
        return new ArrayList<>(resolvers.values());
    }

    public synchronized List getImports() {
        if ( null == imports ) {
            Definition def = typeEntry.getDefinition();
            Set names = new TreeSet();
            if ( isObjectType() ) {
                addImports(names, (ObjectTypeDefinition)def);
            } else if ( isInterfaceType() ) {
                addImports(names, (InterfaceTypeDefinition)def);
            } else if ( isInputObjectType() ) {
                addImports(names, (InputObjectTypeDefinition)def);
            } else if ( isUnionType() ) {
                addImports(names, (UnionTypeDefinition)def);
            } else if ( isEnumType() ) {
                addImports(names, (EnumTypeDefinition)def);
            } else if ( isSchemaType() ) {
                addImports(names, (SchemaDefinition)def);
            }
            imports = new ArrayList<>(names);
        }
        return imports;
    }

    public synchronized List getFields() {
        if ( null == fields ) {
            Definition def = typeEntry.getDefinition();
            if ( isObjectType() ) {
                fields = getFields((ObjectTypeDefinition)def);
            } else if ( isInterfaceType() ) {
                fields = getFields((InterfaceTypeDefinition)def);
            } else if ( isInputObjectType() ) {
                fields = getFields((InputObjectTypeDefinition)def);
            } else if ( isUnionType() ) {
                fields = getFields((UnionTypeDefinition)def);
            } else if ( isEnumType() ) {
                fields = getFields((EnumTypeDefinition)def);
            } else if ( isSchemaType() ) {
                fields = getFields((SchemaDefinition)def);
            } else {
                fields = Collections.emptyList();
            }
        }
        return fields;
    }

    private List getFields(ObjectTypeDefinition def) {
        List fields = new ArrayList();
        for ( FieldDefinition fieldDef : def.getFieldDefinitions() ) {
            Field field = new Field(fieldDef.getName(), toJavaTypeName(fieldDef.getType()));
            field.graphQLType = toGraphQLType(fieldDef.getType());
            field.dataResolver = toDataResolver(fieldDef.getType());
            field.args = toArgs(fieldDef.getInputValueDefinitions());
            fields.add(field);
        }
        return fields;
    }

    private List getFields(InterfaceTypeDefinition def) {
        List fields = new ArrayList();
        for ( FieldDefinition fieldDef : def.getFieldDefinitions() ) {
            Field field = new Field(fieldDef.getName(), toJavaTypeName(fieldDef.getType()));
            field.args = toArgs(fieldDef.getInputValueDefinitions());
            fields.add(field);
        }
        return fields;
    }

    private List getFields(InputObjectTypeDefinition def) {
        List fields = new ArrayList();
        for ( InputValueDefinition fieldDef : def.getInputValueDefinitions() ) {
            Field field = new Field(fieldDef.getName(), toJavaTypeName(fieldDef.getType()));
            field.graphQLType = toGraphQLType(fieldDef.getType());
            field.defaultValue = toJavaValue(fieldDef.getDefaultValue());
            fields.add(field);
        }
        return fields;
    }

    private List getFields(UnionTypeDefinition def) {
        List fields = new ArrayList();
        for ( Type type : def.getMemberTypes() ) {
            fields.add(new Field(null, toJavaTypeName(type)));
        }
        return fields;
    }

    private List getFields(EnumTypeDefinition def) {
        List fields = new ArrayList();
        for ( EnumValueDefinition fieldDef : def.getEnumValueDefinitions() ) {
            fields.add(new Field(fieldDef.getName(), null));
        }
        return fields;
    }

    private List getFields(SchemaDefinition def) {
        List fields = new ArrayList();
        for ( OperationTypeDefinition fieldDef : def.getOperationTypeDefinitions() ) {
            fields.add(new Field(fieldDef.getName(), toJavaTypeName(fieldDef.getType())));
        }
        return fields;
    }

    private List toArgs(List defs) {
        List result = new ArrayList<>();
        for ( InputValueDefinition def : defs ) {
            Arg arg = new Arg(def.getName(), toJavaTypeName(def.getType()));
            arg.graphQLType = toGraphQLType(def.getType());
            arg.defaultValue = toJavaValue(def.getDefaultValue());
            result.add(arg);
        }
        return result;
    }

    private String toJavaValue(Value value) {
        // TODO: Implement me!
        return null;
    }

    private DataResolver toDataResolver(Type type) {
        if ( type instanceof ListType ) {
            DataResolver resolver = toDataResolver(((ListType)type).getType());
            if ( null == resolver ) return null;
            resolver.listDepth++;
            return resolver;
        } else if ( type instanceof NonNullType ) {
            return toDataResolver(((NonNullType)type).getType());
        } else if ( type instanceof TypeName ) {
            String typeName = ((TypeName)type).getName();
            if ( BUILTINS.containsKey(typeName) ) return null;
            TypeEntry typeEntry = referenceTypes.get(typeName);
            if ( !typeEntry.hasIdField() ) return null;
            DataResolver resolver = new DataResolver();
            resolver.fieldType = typeName + ".Resolver";
            resolver.fieldName = "_" + lcFirst(typeName) + "Resolver";
            return resolver;
        } else {
            throw new UnsupportedOperationException("Unknown Type="+type.getClass().getName());
        }
    }

    private String toGraphQLType(Type type) {
        if ( type instanceof ListType ) {
            return "new GraphQLList(" + toGraphQLType(((ListType)type).getType()) + ")";
        } else if ( type instanceof NonNullType ) {
            return toGraphQLType(((NonNullType)type).getType());
        } else if ( type instanceof TypeName ) {
            String name = ((TypeName)type).getName();
            if ( BUILTINS.containsKey(name) ) {
                return "Scalars.GraphQL" + name;
            }
            return "new GraphQLTypeReference(\""+name+"\")";
        } else {
            throw new UnsupportedOperationException("Unknown Type="+type.getClass().getName());
        }
    }

    private String toJavaTypeName(Type type) {
        if ( type instanceof ListType ) {
            return "List<" + toJavaTypeName(((ListType)type).getType()) + ">";
        } else if ( type instanceof NonNullType ) {
            return toJavaTypeName(((NonNullType)type).getType());
        } else if ( type instanceof TypeName ) {
            String name = ((TypeName)type).getName();
            String rename = RENAME.get(name);
            // TODO: scalar type directive to get implementation class...
            if ( null != rename ) return rename;
            return name;
        } else {
            throw new UnsupportedOperationException("Unknown Type="+type.getClass().getName());
        }
    }

    private void addImports(Collection imports, ObjectTypeDefinition def) {
        for ( FieldDefinition fieldDef : def.getFieldDefinitions() ) {
            addImports(imports, fieldDef.getType());
        }
    }

    private void addImports(Collection imports, InterfaceTypeDefinition def) {
        for ( FieldDefinition fieldDef : def.getFieldDefinitions() ) {
            addImports(imports, fieldDef.getType());
        }
    }

    private void addImports(Collection imports, InputObjectTypeDefinition def) {
        for ( InputValueDefinition fieldDef : def.getInputValueDefinitions() ) {
            addImports(imports, fieldDef.getType());
        }
    }

    private void addImports(Collection imports, UnionTypeDefinition def) {
        for ( Type type : def.getMemberTypes() ) {
            addImports(imports, type);
        }
    }

    private void addImports(Collection imports, EnumTypeDefinition def) {
        // No imports should be necessary...
    }

    private void addImports(Collection imports, SchemaDefinition def) {
        for ( OperationTypeDefinition fieldDef : def.getOperationTypeDefinitions() ) {
            addImports(imports, fieldDef.getType());
        }
    }

    private void addImports(Collection imports, Type type) {
        if ( type instanceof ListType ) {
            imports.add("java.util.List");
            addImports(imports, ((ListType)type).getType());
        } else if ( type instanceof NonNullType ) {
            addImports(imports, ((NonNullType)type).getType());
        } else if ( type instanceof TypeName ) {
            String name = ((TypeName)type).getName();
            if ( BUILTINS.containsKey(name) ) {
                String importName = BUILTINS.get(name);
                if ( null == importName ) return;
                imports.add(importName);
            } else {
                TypeEntry refEntry = referenceTypes.get(name);
                // TODO: scalar name may be different... should read annotations for scalars.
                if ( null == refEntry ) {
                    throw new RuntimeException("Unknown type '"+name+"' was not defined in the schema");
                } else {
                    imports.add(refEntry.getPackageName() + "." + name);
                }
            }
        } else {
            throw new RuntimeException("Unknown Type="+type.getClass().getName());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy