1. GraphQL Schema AST Classes
// GraphQLSchema.java import java.util.*; public class GraphQLSchema { private final List<TypeDefinition> typeDefinitions; private final List<DirectiveDefinition> directiveDefinitions; private final SchemaDefinition schemaDefinition; public GraphQLSchema(SchemaDefinition schemaDefinition, List<TypeDefinition> typeDefinitions, List<DirectiveDefinition> directiveDefinitions) { this.schemaDefinition = schemaDefinition; this.typeDefinitions = typeDefinitions != null ? typeDefinitions : new ArrayList<>(); this.directiveDefinitions = directiveDefinitions != null ? directiveDefinitions : new ArrayList<>(); } public Optional<SchemaDefinition> getSchemaDefinition() { return Optional.ofNullable(schemaDefinition); } public List<TypeDefinition> getTypeDefinitions() { return Collections.unmodifiableList(typeDefinitions); } public List<DirectiveDefinition> getDirectiveDefinitions() { return Collections.unmodifiableList(directiveDefinitions); } public Optional<TypeDefinition> getTypeDefinition(String name) { return typeDefinitions.stream() .filter(type -> type.getName().equals(name)) .findFirst(); } public List<ObjectTypeDefinition> getObjectTypeDefinitions() { return typeDefinitions.stream() .filter(type -> type instanceof ObjectTypeDefinition) .map(type -> (ObjectTypeDefinition) type) .toList(); } } // Base interfaces and classes interface Definition { String getName(); List<Directive> getDirectives(); } abstract class NamedDefinition implements Definition { protected final String name; protected final String description; protected final List<Directive> directives; protected NamedDefinition(String name, String description, List<Directive> directives) { this.name = name; this.description = description; this.directives = directives != null ? directives : new ArrayList<>(); } @Override public String getName() { return name; } public String getDescription() { return description; } @Override public List<Directive> getDirectives() { return Collections.unmodifiableList(directives); } } interface Type { String getName(); boolean isNonNull(); boolean isList(); Type getBaseType(); } 2. Type Definitions
// Type Definitions class SchemaDefinition extends NamedDefinition { private final List<OperationTypeDefinition> operationTypeDefinitions; public SchemaDefinition(List<OperationTypeDefinition> operationTypeDefinitions, List<Directive> directives) { super("Schema", null, directives); this.operationTypeDefinitions = operationTypeDefinitions != null ? operationTypeDefinitions : new ArrayList<>(); } public List<OperationTypeDefinition> getOperationTypeDefinitions() { return Collections.unmodifiableList(operationTypeDefinitions); } } class OperationTypeDefinition { private final String operation; private final String typeName; public OperationTypeDefinition(String operation, String typeName) { this.operation = operation; this.typeName = typeName; } public String getOperation() { return operation; } public String getTypeName() { return typeName; } } interface TypeDefinition extends Definition {} class ObjectTypeDefinition extends NamedDefinition implements TypeDefinition { private final List<FieldDefinition> fieldDefinitions; private final List<String> implementsInterfaces; public ObjectTypeDefinition(String name, String description, List<FieldDefinition> fieldDefinitions, List<String> implementsInterfaces, List<Directive> directives) { super(name, description, directives); this.fieldDefinitions = fieldDefinitions != null ? fieldDefinitions : new ArrayList<>(); this.implementsInterfaces = implementsInterfaces != null ? implementsInterfaces : new ArrayList<>(); } public List<FieldDefinition> getFieldDefinitions() { return Collections.unmodifiableList(fieldDefinitions); } public List<String> getImplementsInterfaces() { return Collections.unmodifiableList(implementsInterfaces); } } class InterfaceTypeDefinition extends NamedDefinition implements TypeDefinition { private final List<FieldDefinition> fieldDefinitions; public InterfaceTypeDefinition(String name, String description, List<FieldDefinition> fieldDefinitions, List<Directive> directives) { super(name, description, directives); this.fieldDefinitions = fieldDefinitions != null ? fieldDefinitions : new ArrayList<>(); } public List<FieldDefinition> getFieldDefinitions() { return Collections.unmodifiableList(fieldDefinitions); } } class EnumTypeDefinition extends NamedDefinition implements TypeDefinition { private final List<EnumValueDefinition> enumValueDefinitions; public EnumTypeDefinition(String name, String description, List<EnumValueDefinition> enumValueDefinitions, List<Directive> directives) { super(name, description, directives); this.enumValueDefinitions = enumValueDefinitions != null ? enumValueDefinitions : new ArrayList<>(); } public List<EnumValueDefinition> getEnumValueDefinitions() { return Collections.unmodifiableList(enumValueDefinitions); } } class ScalarTypeDefinition extends NamedDefinition implements TypeDefinition { public ScalarTypeDefinition(String name, String description, List<Directive> directives) { super(name, description, directives); } } class InputObjectTypeDefinition extends NamedDefinition implements TypeDefinition { private final List<InputValueDefinition> inputFieldDefinitions; public InputObjectTypeDefinition(String name, String description, List<InputValueDefinition> inputFieldDefinitions, List<Directive> directives) { super(name, description, directives); this.inputFieldDefinitions = inputFieldDefinitions != null ? inputFieldDefinitions : new ArrayList<>(); } public List<InputValueDefinition> getInputFieldDefinitions() { return Collections.unmodifiableList(inputFieldDefinitions); } } class UnionTypeDefinition extends NamedDefinition implements TypeDefinition { private final List<String> memberTypes; public UnionTypeDefinition(String name, String description, List<String> memberTypes, List<Directive> directives) { super(name, description, directives); this.memberTypes = memberTypes != null ? memberTypes : new ArrayList<>(); } public List<String> getMemberTypes() { return Collections.unmodifiableList(memberTypes); } } 3. Field and Value Definitions
// Field and Value Definitions class FieldDefinition { private final String name; private final String description; private final Type type; private final List<InputValueDefinition> arguments; private final List<Directive> directives; public FieldDefinition(String name, String description, Type type, List<InputValueDefinition> arguments, List<Directive> directives) { this.name = name; this.description = description; this.type = type; this.arguments = arguments != null ? arguments : new ArrayList<>(); this.directives = directives != null ? directives : new ArrayList<>(); } public String getName() { return name; } public String getDescription() { return description; } public Type getType() { return type; } public List<InputValueDefinition> getArguments() { return Collections.unmodifiableList(arguments); } public List<Directive> getDirectives() { return Collections.unmodifiableList(directives); } } class InputValueDefinition { private final String name; private final String description; private final Type type; private final Value defaultValue; private final List<Directive> directives; public InputValueDefinition(String name, String description, Type type, Value defaultValue, List<Directive> directives) { this.name = name; this.description = description; this.type = type; this.defaultValue = defaultValue; this.directives = directives != null ? directives : new ArrayList<>(); } public String getName() { return name; } public String getDescription() { return description; } public Type getType() { return type; } public Optional<Value> getDefaultValue() { return Optional.ofNullable(defaultValue); } public List<Directive> getDirectives() { return Collections.unmodifiableList(directives); } } class EnumValueDefinition extends NamedDefinition { public EnumValueDefinition(String name, String description, List<Directive> directives) { super(name, description, directives); } } 4. Type System
// Type System class NamedType implements Type { private final String name; public NamedType(String name) { this.name = name; } @Override public String getName() { return name; } @Override public boolean isNonNull() { return false; } @Override public boolean isList() { return false; } @Override public Type getBaseType() { return this; } @Override public String toString() { return name; } } class NonNullType implements Type { private final Type wrappedType; public NonNullType(Type wrappedType) { this.wrappedType = wrappedType; } @Override public String getName() { return wrappedType.getName() + "!"; } @Override public boolean isNonNull() { return true; } @Override public boolean isList() { return false; } @Override public Type getBaseType() { return wrappedType.getBaseType(); } @Override public String toString() { return wrappedType.toString() + "!"; } } class ListType implements Type { private final Type wrappedType; public ListType(Type wrappedType) { this.wrappedType = wrappedType; } @Override public String getName() { return "[" + wrappedType.getName() + "]"; } @Override public boolean isNonNull() { return false; } @Override public boolean isList() { return true; } @Override public Type getBaseType() { return wrappedType.getBaseType(); } @Override public String toString() { return "[" + wrappedType.toString() + "]"; } } 5. Directives and Values
// Directives and Values class DirectiveDefinition extends NamedDefinition { private final List<InputValueDefinition> arguments; private final List<String> locations; public DirectiveDefinition(String name, String description, List<InputValueDefinition> arguments, List<String> locations, List<Directive> directives) { super(name, description, directives); this.arguments = arguments != null ? arguments : new ArrayList<>(); this.locations = locations != null ? locations : new ArrayList<>(); } public List<InputValueDefinition> getArguments() { return Collections.unmodifiableList(arguments); } public List<String> getLocations() { return Collections.unmodifiableList(locations); } } class Directive { private final String name; private final List<Argument> arguments; public Directive(String name, List<Argument> arguments) { this.name = name; this.arguments = arguments != null ? arguments : new ArrayList<>(); } public String getName() { return name; } public List<Argument> getArguments() { return Collections.unmodifiableList(arguments); } } class Argument { private final String name; private final Value value; public Argument(String name, Value value) { this.name = name; this.value = value; } public String getName() { return name; } public Value getValue() { return value; } } interface Value { Object getValue(); } class StringValue implements Value { private final String value; public StringValue(String value) { this.value = value; } @Override public Object getValue() { return value; } @Override public String toString() { return "\"" + value.replace("\"", "\\\"") + "\""; } } class IntValue implements Value { private final int value; public IntValue(int value) { this.value = value; } @Override public Object getValue() { return value; } @Override public String toString() { return String.valueOf(value); } } class BooleanValue implements Value { private final boolean value; public BooleanValue(boolean value) { this.value = value; } @Override public Object getValue() { return value; } @Override public String toString() { return String.valueOf(value); } } class EnumValue implements Value { private final String value; public EnumValue(String value) { this.value = value; } @Override public Object getValue() { return value; } @Override public String toString() { return value; } } class ListValue implements Value { private final List<Value> values; public ListValue(List<Value> values) { this.values = values != null ? values : new ArrayList<>(); } @Override public Object getValue() { return values.stream().map(Value::getValue).toList(); } @Override public String toString() { return "[" + values.stream().map(Object::toString).collect(java.util.stream.Collectors.joining(", ")) + "]"; } } class ObjectValue implements Value { private final List<Argument> fields; public ObjectValue(List<Argument> fields) { this.fields = fields != null ? fields : new ArrayList<>(); } @Override public Object getValue() { Map<String, Object> result = new HashMap<>(); for (Argument field : fields) { result.put(field.getName(), field.getValue().getValue()); } return result; } @Override public String toString() { return "{" + fields.stream() .map(f -> f.getName() + ": " + f.getValue()) .collect(java.util.stream.Collectors.joining(", ")) + "}"; } } 6. GraphQL Schema Parser
// GraphQLSchemaParser.java import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; public class GraphQLSchemaParser { private String input; private int position; private final int length; private static final Pattern WHITESPACE = Pattern.compile("\\s"); private static final Pattern NAME_PATTERN = Pattern.compile("[_a-zA-Z][_a-zA-Z0-9]*"); public GraphQLSchemaParser(String input) { this.input = input; this.position = 0; this.length = input.length(); } public GraphQLSchema parse() { List<TypeDefinition> typeDefinitions = new ArrayList<>(); List<DirectiveDefinition> directiveDefinitions = new ArrayList<>(); SchemaDefinition schemaDefinition = null; skipWhitespaceAndComments(); while (position < length) { if (peekKeyword("schema")) { schemaDefinition = parseSchemaDefinition(); } else if (peekKeyword("type")) { typeDefinitions.add(parseObjectTypeDefinition()); } else if (peekKeyword("interface")) { typeDefinitions.add(parseInterfaceTypeDefinition()); } else if (peekKeyword("enum")) { typeDefinitions.add(parseEnumTypeDefinition()); } else if (peekKeyword("scalar")) { typeDefinitions.add(parseScalarTypeDefinition()); } else if (peekKeyword("input")) { typeDefinitions.add(parseInputObjectTypeDefinition()); } else if (peekKeyword("union")) { typeDefinitions.add(parseUnionTypeDefinition()); } else if (peekKeyword("directive")) { directiveDefinitions.add(parseDirectiveDefinition()); } else { throw new GraphQLParseException("Unexpected token at position " + position); } skipWhitespaceAndComments(); } return new GraphQLSchema(schemaDefinition, typeDefinitions, directiveDefinitions); } private SchemaDefinition parseSchemaDefinition() { expectKeyword("schema"); List<Directive> directives = parseDirectives(); expect("{"); List<OperationTypeDefinition> operationTypes = new ArrayList<>(); while (!peek("}")) { String operation = parseName(); expect(":"); String typeName = parseName(); operationTypes.add(new OperationTypeDefinition(operation, typeName)); skipWhitespaceAndComments(); } expect("}"); return new SchemaDefinition(operationTypes, directives); } private ObjectTypeDefinition parseObjectTypeDefinition() { expectKeyword("type"); String name = parseName(); String description = null; // Could parse description from comments List<String> implementsInterfaces = new ArrayList<>(); if (peekKeyword("implements")) { expectKeyword("implements"); implementsInterfaces.add(parseName()); while (peek("&")) { expect("&"); implementsInterfaces.add(parseName()); } } List<Directive> directives = parseDirectives(); List<FieldDefinition> fields = parseFieldsDefinition(); return new ObjectTypeDefinition(name, description, fields, implementsInterfaces, directives); } private InterfaceTypeDefinition parseInterfaceTypeDefinition() { expectKeyword("interface"); String name = parseName(); String description = null; List<Directive> directives = parseDirectives(); List<FieldDefinition> fields = parseFieldsDefinition(); return new InterfaceTypeDefinition(name, description, fields, directives); } private List<FieldDefinition> parseFieldsDefinition() { expect("{"); List<FieldDefinition> fields = new ArrayList<>(); while (!peek("}")) { fields.add(parseFieldDefinition()); skipWhitespaceAndComments(); } expect("}"); return fields; } private FieldDefinition parseFieldDefinition() { String name = parseName(); String description = null; List<InputValueDefinition> arguments = new ArrayList<>(); if (peek("(")) { arguments = parseArgumentsDefinition(); } expect(":"); Type type = parseType(); List<Directive> directives = parseDirectives(); return new FieldDefinition(name, description, type, arguments, directives); } private List<InputValueDefinition> parseArgumentsDefinition() { expect("("); List<InputValueDefinition> arguments = new ArrayList<>(); while (!peek(")")) { arguments.add(parseInputValueDefinition()); skipWhitespaceAndComments(); } expect(")"); return arguments; } private InputValueDefinition parseInputValueDefinition() { String name = parseName(); String description = null; expect(":"); Type type = parseType(); Value defaultValue = null; if (peek("=")) { expect("="); defaultValue = parseValue(); } List<Directive> directives = parseDirectives(); return new InputValueDefinition(name, description, type, defaultValue, directives); } private Type parseType() { Type type; if (peek("[")) { expect("["); type = new ListType(parseType()); expect("]"); } else { type = new NamedType(parseName()); } if (peek("!")) { expect("!"); type = new NonNullType(type); } return type; } private List<Directive> parseDirectives() { List<Directive> directives = new ArrayList<>(); while (peek("@")) { directives.add(parseDirective()); skipWhitespaceAndComments(); } return directives; } private Directive parseDirective() { expect("@"); String name = parseName(); List<Argument> arguments = new ArrayList<>(); if (peek("(")) { arguments = parseArguments(); } return new Directive(name, arguments); } private List<Argument> parseArguments() { expect("("); List<Argument> arguments = new ArrayList<>(); while (!peek(")")) { String name = parseName(); expect(":"); Value value = parseValue(); arguments.add(new Argument(name, value)); skipWhitespaceAndComments(); } expect(")"); return arguments; } private Value parseValue() { skipWhitespaceAndComments(); char current = input.charAt(position); if (current == '"') { return parseStringValue(); } else if (Character.isDigit(current) || current == '-') { return parseIntValue(); } else if (peekKeyword("true") || peekKeyword("false")) { return parseBooleanValue(); } else if (peek("[")) { return parseListValue(); } else if (peek("{")) { return parseObjectValue(); } else { // Enum value or variable (simplified) return new EnumValue(parseName()); } } private StringValue parseStringValue() { expect("\""); StringBuilder sb = new StringBuilder(); while (position < length && input.charAt(position) != '"') { if (input.charAt(position) == '\\') { position++; // Handle escape sequences (simplified) } sb.append(input.charAt(position)); position++; } expect("\""); return new StringValue(sb.toString()); } private IntValue parseIntValue() { StringBuilder sb = new StringBuilder(); while (position < length && Character.isDigit(input.charAt(position))) { sb.append(input.charAt(position)); position++; } return new IntValue(Integer.parseInt(sb.toString())); } private BooleanValue parseBooleanValue() { if (peekKeyword("true")) { expectKeyword("true"); return new BooleanValue(true); } else { expectKeyword("false"); return new BooleanValue(false); } } private ListValue parseListValue() { expect("["); List<Value> values = new ArrayList<>(); while (!peek("]")) { values.add(parseValue()); skipWhitespaceAndComments(); } expect("]"); return new ListValue(values); } private ObjectValue parseObjectValue() { expect("{"); List<Argument> fields = new ArrayList<>(); while (!peek("}")) { String name = parseName(); expect(":"); Value value = parseValue(); fields.add(new Argument(name, value)); skipWhitespaceAndComments(); } expect("}"); return new ObjectValue(fields); } // Helper parsing methods for other type definitions... private EnumTypeDefinition parseEnumTypeDefinition() { expectKeyword("enum"); String name = parseName(); String description = null; List<Directive> directives = parseDirectives(); expect("{"); List<EnumValueDefinition> values = new ArrayList<>(); while (!peek("}")) { String valueName = parseName(); List<Directive> valueDirectives = parseDirectives(); values.add(new EnumValueDefinition(valueName, null, valueDirectives)); skipWhitespaceAndComments(); } expect("}"); return new EnumTypeDefinition(name, description, values, directives); } private ScalarTypeDefinition parseScalarTypeDefinition() { expectKeyword("scalar"); String name = parseName(); String description = null; List<Directive> directives = parseDirectives(); return new ScalarTypeDefinition(name, description, directives); } private InputObjectTypeDefinition parseInputObjectTypeDefinition() { expectKeyword("input"); String name = parseName(); String description = null; List<Directive> directives = parseDirectives(); expect("{"); List<InputValueDefinition> fields = new ArrayList<>(); while (!peek("}")) { fields.add(parseInputValueDefinition()); skipWhitespaceAndComments(); } expect("}"); return new InputObjectTypeDefinition(name, description, fields, directives); } private UnionTypeDefinition parseUnionTypeDefinition() { expectKeyword("union"); String name = parseName(); String description = null; List<Directive> directives = parseDirectives(); expect("="); List<String> members = new ArrayList<>(); members.add(parseName()); while (peek("|")) { expect("|"); members.add(parseName()); } return new UnionTypeDefinition(name, description, members, directives); } private DirectiveDefinition parseDirectiveDefinition() { expectKeyword("directive"); expect("@"); String name = parseName(); String description = null; List<InputValueDefinition> arguments = new ArrayList<>(); if (peek("(")) { arguments = parseArgumentsDefinition(); } expectKeyword("on"); List<String> locations = new ArrayList<>(); locations.add(parseName()); while (peek("|")) { expect("|"); locations.add(parseName()); } return new DirectiveDefinition(name, description, arguments, locations, null); } // Utility parsing methods private String parseName() { skipWhitespaceAndComments(); StringBuilder sb = new StringBuilder(); while (position < length && (Character.isLetterOrDigit(input.charAt(position)) || input.charAt(position) == '_')) { sb.append(input.charAt(position)); position++; } String name = sb.toString(); if (!NAME_PATTERN.matcher(name).matches()) { throw new GraphQLParseException("Invalid name: " + name); } return name; } private void expect(String expected) { skipWhitespaceAndComments(); if (position + expected.length() > length) { throw new GraphQLParseException("Expected '" + expected + "' but reached end of input"); } String actual = input.substring(position, position + expected.length()); if (!actual.equals(expected)) { throw new GraphQLParseException("Expected '" + expected + "' but found '" + actual + "'"); } position += expected.length(); skipWhitespaceAndComments(); } private void expectKeyword(String keyword) { skipWhitespaceAndComments(); // Check if we have the keyword followed by non-name character if (!peekKeyword(keyword)) { throw new GraphQLParseException("Expected keyword '" + keyword + "'"); } position += keyword.length(); skipWhitespaceAndComments(); } private boolean peek(String expected) { skipWhitespaceAndComments(); if (position + expected.length() > length) { return false; } return input.substring(position, position + expected.length()).equals(expected); } private boolean peekKeyword(String keyword) { int savedPosition = position; skipWhitespaceAndComments(); if (position + keyword.length() > length) { position = savedPosition; return false; } boolean matches = input.substring(position, position + keyword.length()).equals(keyword); // Check that it's not part of a larger name if (matches && position + keyword.length() < length) { char nextChar = input.charAt(position + keyword.length()); if (Character.isLetterOrDigit(nextChar) || nextChar == '_') { matches = false; } } position = savedPosition; return matches; } private void skipWhitespaceAndComments() { while (position < length) { char current = input.charAt(position); if (WHITESPACE.matcher(String.valueOf(current)).matches()) { position++; } else if (current == '#') { // Skip comment until end of line while (position < length && input.charAt(position) != '\n') { position++; } } else { break; } } } } class GraphQLParseException extends RuntimeException { public GraphQLParseException(String message) { super(message); } public GraphQLParseException(String message, Throwable cause) { super(message, cause); } } 7. Usage Example
// Example usage public class GraphQLParserExample { public static void main(String[] args) { String schemaString = """ type Query { user(id: ID!): User users: [User!]! } type User { id: ID! name: String! email: String! posts: [Post!]! } type Post { id: ID! title: String! content: String! author: User! } input CreateUserInput { name: String! email: String! } enum Role { ADMIN USER GUEST } directive @auth(role: Role!) on FIELD_DEFINITION """; try { GraphQLSchemaParser parser = new GraphQLSchemaParser(schemaString); GraphQLSchema schema = parser.parse(); // Print all type definitions for (TypeDefinition typeDef : schema.getTypeDefinitions()) { if (typeDef instanceof ObjectTypeDefinition) { ObjectTypeDefinition objectType = (ObjectTypeDefinition) typeDef; System.out.println("Type: " + objectType.getName()); for (FieldDefinition field : objectType.getFieldDefinitions()) { System.out.println(" Field: " + field.getName() + ": " + field.getType()); } } } } catch (GraphQLParseException e) { System.err.println("Parse error: " + e.getMessage()); } } } Key Features:
- Complete SDL Support: Parses all GraphQL schema definition types
- Type System: Handles named types, lists, and non-null types
- Directives: Supports directive definitions and applications
- Values: Parses various value types (strings, integers, booleans, enums, lists, objects)
- Error Handling: Comprehensive error reporting with position information
- AST Building: Constructs complete abstract syntax tree
- Extensible: Easy to extend with additional features
This parser provides a solid foundation for building GraphQL tools, validators, code generators, or schema management systems in Java.