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

com.graphql_java_generator.client.GraphqlUtils Maven / Gradle / Ivy

There is a newer version: 2.0RC1
Show newest version
/**
 * 
 */
package com.graphql_java_generator.client;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.graphql_java_generator.annotation.GraphQLNonScalar;
import com.graphql_java_generator.annotation.GraphQLScalar;
import com.graphql_java_generator.client.request.ObjectResponse;
import com.graphql_java_generator.client.response.GraphQLRequestPreparationException;

/**
 * @author EtienneSF
 */
public class GraphqlUtils {

	Pattern graphqlNamePattern = Pattern.compile("^[_A-Za-z][_0-9A-Za-z]*$");

	/**
	 * maps for all scalers, when they are mandatory. The key is the type name. The value is the class to use in the
	 * java code
	 */
	List> scalars = new ArrayList<>();

	public GraphqlUtils() {
		// Add of all predefined scalars
		scalars.add(String.class);
		scalars.add(int.class);
		scalars.add(Integer.class);
		scalars.add(float.class);
		scalars.add(Float.class);
		scalars.add(boolean.class);
		scalars.add(Boolean.class);
	}

	/**
	 * Checks that the given GraphQL name is valid.
	 * 
	 * @param graphqlIdentifier
	 * @throws NullPointerException
	 *             If name is null
	 * @throws GraphQLRequestPreparationException
	 *             If the given graphqlIdentifier is not a valid identifier
	 */
	public void checkName(String graphqlIdentifier) throws GraphQLRequestPreparationException {
		if (graphqlIdentifier == null) {
			throw new NullPointerException("A GraphQL identifier may not be null");
		}
		Matcher m = graphqlNamePattern.matcher(graphqlIdentifier);
		if (!m.matches()) {
			throw new GraphQLRequestPreparationException("<" + graphqlIdentifier + "> is not a valid GraphQL name");
		}
	}

	/**
	 * Check if the given field is owned by the class of this {@link ObjectResponse}. This method returns the class for
	 * this field.
	 * 
	 * @param name
	 *            The name of the field we want to check
	 * @param shouldBeScalar
	 *            if true: also checks that the field is a scalar (throws a GraphQLRequestPreparationException if not).
	 *            If false: also checks that the field is not a scalar (throws a GraphQLRequestPreparationException if
	 *            not). If null: no check whether the field is a scalar or not
	 * @param owningClass
	 *            The class in which will search for name as a GraphQL field
	 * @return the class of this field
	 * @throws NullPointerException
	 *             if name is null
	 * @throws GraphQLRequestPreparationException
	 *             if the check is KO
	 */
	public Class checkFieldOfGraphQLType(String name, Boolean shouldBeScalar, Class owningClass)
			throws GraphQLRequestPreparationException {

		// Let's be sure that the identifier is a valid GraphQL identifier (also checks that it's not null)
		checkName(name);

		// Let's check that this fieldName is either a method name or a field of the class for this ObjectResponse.
		Class fieldClass = null;
		for (java.lang.reflect.Field field : owningClass.getDeclaredFields()) {
			if (field.getName().equals(name)) {
				// If we need to check that this field is (or is not) a scalar
				fieldClass = checkIsScalar(field, shouldBeScalar);
				break;
			}
		}
		if (fieldClass == null && !owningClass.isInterface()) {
			// This class is a concrete class (not an interface). As the search field is not an attribute, the
			// owningClass should be a Query, a Mutation or a Subscription
			for (Method method : owningClass.getDeclaredMethods()) {
				if (method.getName().equals(name)) {
					// If we need to check that this field is (or is not) a scalar
					fieldClass = checkIsScalar(name, method, shouldBeScalar);
					break;
				}
			}
		}
		if (fieldClass == null && owningClass.isInterface()) {
			// The class is an interface. So it's logical we didn't find this field as an attribute. Let's search for
			// the relevant setter
			String expectedMethodName = "get" + getPascalCase(name);
			for (Method method : owningClass.getDeclaredMethods()) {
				if (method.getName().equals(expectedMethodName)) {
					// If we need to check that this field is (or is not) a scalar
					fieldClass = checkIsScalar(name, method, shouldBeScalar);
					break;
				}
			}
		}

		if (fieldClass == null) {
			throw new GraphQLRequestPreparationException(
					"The GraphQL type <" + owningClass.getSimpleName() + "> has no field of name <" + name + ">");
		}

		return fieldClass;
	}

	/**
	 * Returns the given name in PascalCase. For instance: theName -> TheName
	 * 
	 * @param name
	 * @return
	 */
	String getPascalCase(String name) {
		return name.substring(0, 1).toUpperCase() + name.substring(1);
	}

	/**
	 * This method checks whether the given field (as an attribute) of the given class is a GraphQL scalar, or not,
	 * depending on shouldBeScalar.
	 * 
	 * @param field
	 *            The field whose type should be (or not) a scalar
	 * @param shouldBeScalar
	 *            if true, this method checks that field's type is a scalar (if false, checks that it is not a scalar)
	 * @throws GraphQLRequestPreparationException
	 */
	Class checkIsScalar(java.lang.reflect.Field field, Boolean shouldBeScalar)
			throws GraphQLRequestPreparationException {
		// All types are generated in the same package. So, if the package for the field type is the same as the
		// package for our class, this means that the field is not a scalar
		// Note: we'll perhaps have to change that, depending on the way we manage scalar

		boolean isScalar = isScalar(field);

		if (shouldBeScalar != null) {
			if (shouldBeScalar & !isScalar) {
				throw new GraphQLRequestPreparationException("The field <" + field.getName() + "> of the GraphQL type <"
						+ field.getDeclaringClass().getName()
						+ "> is not a GraphQLScalar. At least one field must be defined for the server response.");
			}
			if (!shouldBeScalar & isScalar) {
				throw new GraphQLRequestPreparationException("The field <" + field.getName() + "> of the GraphQL type <"
						+ field.getDeclaringClass().getName()
						+ "> is not a GraphQLScalar. At least one field must be defined for the server response.");
			}
		}

		return getGraphQLType(field);
	}

	/**
	 * This method checks whether the given field (as a method: getter, query...) of the given class is a GraphQL
	 * scalar, or not, depending on shouldBeScalar.
	 * 
	 * @param fieldName
	 *            the name of the field represented by the given method.
	 * @param method
	 *            The method whose return should be (or not) a scalar. This method can be a setter, a getter (in which
	 *            case its name is different from the fieldName), or a query/mutation/subscription (in which case its
	 *            name is the fieldName)
	 * @param shouldBeScalar
	 *            if true, this method checks that method return type is a scalar (if false, checks that it is not a
	 *            scalar)
	 * @throws GraphQLRequestPreparationException
	 */
	Class checkIsScalar(String fieldName, Method method, Boolean shouldBeScalar)
			throws GraphQLRequestPreparationException {
		// All types are generated in the same package. So, if the package for the field type is the same as the
		// package for our class, this means that the field is not a scalar
		// Note: we'll perhaps have to change that, depending on the way we manage scalar
		boolean isScalar = isScalar(method);

		if (method.getReturnType() == null) {
			throw new GraphQLRequestPreparationException("There is a method of name <" + fieldName
					+ "> in the GraphQL type <" + method.getDeclaringClass().getName()
					+ ">, but this method is a void method: it can't represent the <" + fieldName + "> GraphQL field");
		}

		if (shouldBeScalar != null) {
			if (shouldBeScalar && !isScalar) {
				throw new GraphQLRequestPreparationException(
						"The field <" + fieldName + "> (accessed through its getter: " + method.getName()
								+ ">) of the GraphQL type <" + method.getDeclaringClass().getName()
								+ "> should be a scalar. But is is actually not a GraphQLScalar");
			}
			if (!shouldBeScalar && isScalar) {
				throw new GraphQLRequestPreparationException(
						"The field <" + fieldName + "> (accessed through its getter: <" + method.getName()
								+ ">) of the GraphQL type <" + method.getDeclaringClass().getName()
								+ "> should not be a scalar. But is is actually a GraphQLScalar");
			}
		}

		return getGraphQLType(method);
	}

	/**
	 * Indicates whether the given class is a scalar or not
	 * 
	 * @param fieldOrMethod
	 * @return true if clazz is a scalar type
	 * @throws GraphQLRequestPreparationException
	 */
	public boolean isScalar(AccessibleObject fieldOrMethod) throws GraphQLRequestPreparationException {
		if (fieldOrMethod.getAnnotation(GraphQLScalar.class) != null
				|| fieldOrMethod.getAnnotation(GraphQLNonScalar.class) != null) {
			// Ok, at least on of GraphQLScalar and GraphQLNonScalar annotation is set.
			return fieldOrMethod.getAnnotation(GraphQLScalar.class) != null;
		} else {
			// No GraphQLScalar or GraphQLNonScalar annotation: let's thrown an internal error.
			if (fieldOrMethod instanceof Field) {
				Field field = (Field) fieldOrMethod;
				throw new GraphQLRequestPreparationException(
						"The field <" + field.getName() + "> of the class <" + field.getDeclaringClass().getName()
								+ "> has none of the GraphQLScalar and GraphQLNonScalar annotation");
			} else {
				Method method = (Method) fieldOrMethod;
				throw new GraphQLRequestPreparationException(
						"The method <" + method.getName() + "> of the class <" + method.getDeclaringClass().getName()
								+ "> has none of the GraphQLScalar and GraphQLNonScalar annotation");
			}
		}
	}

	/**
	 * Returns the Class indicated as the value for the graphqlType attribute of the GraphQLScalar or GraphQLNonScalar
	 * annotation
	 * 
	 * @param fieldOrMethod
	 * @return
	 * @throws GraphQLRequestPreparationException
	 */
	public Class getGraphQLType(AccessibleObject fieldOrMethod) throws GraphQLRequestPreparationException {
		if (fieldOrMethod.getAnnotation(GraphQLScalar.class) != null) {
			// Ok, at least on of GraphQLScalar and GraphQLNonScalar annotation is set.
			return fieldOrMethod.getAnnotation(GraphQLScalar.class).graphqlType();
		} else if (fieldOrMethod.getAnnotation(GraphQLNonScalar.class) != null) {
			// Ok, at least on of GraphQLScalar and GraphQLNonScalar annotation is set.
			return fieldOrMethod.getAnnotation(GraphQLNonScalar.class).graphqlType();
		} else {
			// No GraphQLScalar or GraphQLNonScalar annotation: let's thrown an internal error.
			if (fieldOrMethod instanceof Field) {
				Field field = (Field) fieldOrMethod;
				throw new GraphQLRequestPreparationException(
						"The field <" + field.getName() + "> of the class <" + field.getDeclaringClass().getName()
								+ "> has none of the GraphQLScalar and GraphQLNonScalar annotation");
			} else {
				Method method = (Method) fieldOrMethod;
				throw new GraphQLRequestPreparationException(
						"The method <" + method.getName() + "> of the class <" + method.getDeclaringClass().getName()
								+ "> has none of the GraphQLScalar and GraphQLNonScalar annotation");
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy