blob: e6af0489cae3f9131f11d8ab05e4c818d2a52fe5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2019 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.services.graphql.emf.internal.schema;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Function;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.sirius.services.graphql.common.api.SiriusGraphQLFilterStatus;
import graphql.Scalars;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLNonNull;
import graphql.schema.GraphQLOutputType;
import graphql.schema.GraphQLTypeReference;
/**
* Used to compute the fields of an EClass.
*
* @author sbegaudeau
*/
public class FieldsBuilder {
/**
* The EClass.
*/
private EClass eClass;
/**
* The cache of the EDataType to GraphQL output type.
*/
private HashMap<EDataType, GraphQLOutputType> eDataTypeToOutputTypeCache;
/**
* The EStructuralFeature filter.
*/
private Function<EStructuralFeature, SiriusGraphQLFilterStatus> eStructuralFeatureFilter = eStructuralFeature -> SiriusGraphQLFilterStatus.KEEP;
/**
* The constructor.
*
* @param eClass
* The EClass
* @param eDataTypeToOutputTypeCache
* The cache of the EDataType to GraphQL output type
*/
public FieldsBuilder(EClass eClass, HashMap<EDataType, GraphQLOutputType> eDataTypeToOutputTypeCache) {
this.eClass = eClass;
this.eDataTypeToOutputTypeCache = eDataTypeToOutputTypeCache;
}
/**
* Sets the EStructuralFeature filter.
*
* @param eStructuralFeatureFilter
* The EStructuralFeature
* @return The current builder
*/
@SuppressWarnings({ "checkstyle:HiddenField" })
public FieldsBuilder eStructuralFeatureFilter(Function<EStructuralFeature, SiriusGraphQLFilterStatus> eStructuralFeatureFilter) {
this.eStructuralFeatureFilter = eStructuralFeatureFilter;
return this;
}
/**
* Returns the list of fields.
*
* @return The lsit of fields
*/
public List<GraphQLFieldDefinition> getFields() {
List<GraphQLFieldDefinition> fields = new ArrayList<>();
// @formatter:off
this.eClass.getEAllStructuralFeatures().stream()
.filter(this::isSupported)
.map(this::getField)
.forEach(fields::add);
// @formatter:on
return fields;
}
/**
* Indicates if the given EStructuralFeature is supported.
*
* @param eStructuralFeature
* The EStructuralFeature
* @return <code>true</code> if the EStructuralFeature is supported, <code>false</code> otherwise
*/
private boolean isSupported(EStructuralFeature eStructuralFeature) {
boolean isSupported = eStructuralFeature instanceof EAttribute;
isSupported = isSupported && SiriusGraphQLFilterStatus.KEEP == this.eStructuralFeatureFilter.apply(eStructuralFeature);
isSupported = isSupported && this.getScalar(((EAttribute) eStructuralFeature).getEAttributeType()) != null;
isSupported = isSupported || eStructuralFeature instanceof EReference;
return isSupported;
}
/**
* Returns the field for the given EStructuralFeature.
*
* @param eStructuralFeature
* The EStructuralFeature
* @return The field for the given EStructuralFeature
*/
private GraphQLFieldDefinition getField(EStructuralFeature eStructuralFeature) {
// @formatter:off
return GraphQLFieldDefinition.newFieldDefinition()
.name(eStructuralFeature.getName())
.type(this.getType(eStructuralFeature))
.build();
// @formatter:on
}
/**
* Returns the type of the given EStructuralFeature.
*
* @param eStructuralFeature
* The EStructuralFeature
* @return The type of the given EStructuralFeature
*/
private GraphQLOutputType getType(EStructuralFeature eStructuralFeature) {
GraphQLOutputType type = null;
if (eStructuralFeature instanceof EAttribute) {
EAttribute eAttribute = (EAttribute) eStructuralFeature;
type = this.getScalar(eAttribute.getEAttributeType());
if (Scalars.GraphQLBoolean.equals(type) || Scalars.GraphQLInt.equals(type)) {
type = new GraphQLNonNull(type);
}
} else if (eStructuralFeature instanceof EReference) {
EReference eReference = (EReference) eStructuralFeature;
if (eReference.isMany()) {
type = new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(new GraphQLTypeReference(eReference.getEReferenceType().getName()))));
} else {
type = new GraphQLTypeReference(eReference.getEReferenceType().getName());
}
}
return type;
}
/**
* Returns the scalar matching the given EDataType.
*
* @param eDataType
* The EDataType
* @return The scalar matching the given EDataType
*/
private GraphQLOutputType getScalar(EDataType eDataType) {
GraphQLOutputType type = null;
if (EcorePackage.eINSTANCE.getEBigDecimal().equals(eDataType)) {
type = Scalars.GraphQLBigDecimal;
} else if (EcorePackage.eINSTANCE.getEBigInteger().equals(eDataType)) {
type = Scalars.GraphQLBigInteger;
} else if (EcorePackage.eINSTANCE.getEBoolean().equals(eDataType)) {
type = Scalars.GraphQLBoolean;
} else if (EcorePackage.eINSTANCE.getEByte().equals(eDataType)) {
type = Scalars.GraphQLByte;
} else if (EcorePackage.eINSTANCE.getEChar().equals(eDataType)) {
type = Scalars.GraphQLChar;
} else if (EcorePackage.eINSTANCE.getEFloat().equals(eDataType)) {
type = Scalars.GraphQLFloat;
} else if (EcorePackage.eINSTANCE.getEInt().equals(eDataType)) {
type = Scalars.GraphQLInt;
} else if (EcorePackage.eINSTANCE.getELong().equals(eDataType)) {
type = Scalars.GraphQLLong;
} else if (EcorePackage.eINSTANCE.getEShort().equals(eDataType)) {
type = Scalars.GraphQLShort;
} else if (EcorePackage.eINSTANCE.getEString().equals(eDataType)) {
type = Scalars.GraphQLString;
} else {
type = this.eDataTypeToOutputTypeCache.computeIfAbsent(eDataType, (dataType) -> {
GraphQLOutputType graphQLOutputType = null;
if (dataType instanceof EEnum) {
EEnum eEnum = (EEnum) dataType;
graphQLOutputType = new EEnumTypeBuilder(eEnum).getType();
} else if (dataType.isSerializable()) {
graphQLOutputType = new EStructuralFeatureScalarTypeBuilder(eDataType).getType();
}
return graphQLOutputType;
});
}
return type;
}
}