blob: 7ca36d98266fe0ad9561d449e787aa306147d3fb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core.serializer.json;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.olingo.commons.api.edm.EdmAction;
import org.apache.olingo.commons.api.edm.EdmActionImport;
import org.apache.olingo.commons.api.edm.EdmAnnotatable;
import org.apache.olingo.commons.api.edm.EdmAnnotation;
import org.apache.olingo.commons.api.edm.EdmAnnotations;
import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmFunctionImport;
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
import org.apache.olingo.commons.api.edm.EdmMember;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.EdmOperation;
import org.apache.olingo.commons.api.edm.EdmParameter;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmReferentialConstraint;
import org.apache.olingo.commons.api.edm.EdmReturnType;
import org.apache.olingo.commons.api.edm.EdmSchema;
import org.apache.olingo.commons.api.edm.EdmSingleton;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmTerm;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.edm.TargetType;
import org.apache.olingo.commons.api.edm.annotation.EdmApply;
import org.apache.olingo.commons.api.edm.annotation.EdmCast;
import org.apache.olingo.commons.api.edm.annotation.EdmConstantExpression;
import org.apache.olingo.commons.api.edm.annotation.EdmDynamicExpression;
import org.apache.olingo.commons.api.edm.annotation.EdmExpression;
import org.apache.olingo.commons.api.edm.annotation.EdmIf;
import org.apache.olingo.commons.api.edm.annotation.EdmIsOf;
import org.apache.olingo.commons.api.edm.annotation.EdmLabeledElement;
import org.apache.olingo.commons.api.edm.annotation.EdmLabeledElementReference;
import org.apache.olingo.commons.api.edm.annotation.EdmLogicalOrComparisonExpression;
import org.apache.olingo.commons.api.edm.annotation.EdmNavigationPropertyPath;
import org.apache.olingo.commons.api.edm.annotation.EdmNot;
import org.apache.olingo.commons.api.edm.annotation.EdmNull;
import org.apache.olingo.commons.api.edm.annotation.EdmPath;
import org.apache.olingo.commons.api.edm.annotation.EdmPropertyPath;
import org.apache.olingo.commons.api.edm.annotation.EdmPropertyValue;
import org.apache.olingo.commons.api.edm.annotation.EdmRecord;
import org.apache.olingo.commons.api.edm.annotation.EdmUrlRef;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.edmx.EdmxReference;
import org.apache.olingo.commons.api.edmx.EdmxReferenceInclude;
import org.apache.olingo.commons.api.edmx.EdmxReferenceIncludeAnnotation;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.serializer.Kind;
import org.apache.olingo.server.api.serializer.SerializerException;
import com.fasterxml.jackson.core.JsonGenerator;
public class MetadataDocumentJsonSerializer {
private final ServiceMetadata serviceMetadata;
private final Map<String, String> namespaceToAlias = new HashMap<>();
private static final String DOLLAR = "$";
private static final String VERSION = DOLLAR + "Version";
private static final String REFERENCES = DOLLAR + "Reference";
private static final String INCLUDE = DOLLAR + "Include";
private static final String NAMESPACE = DOLLAR + "Namespace";
private static final String ALIAS = DOLLAR + "Alias";
private static final String INCLUDE_ANNOTATIONS = DOLLAR + "IncludeAnnotations";
private static final String TERM_NAMESPACE = DOLLAR + "TermNamespace";
private static final String TARGET_NAMESPACE = DOLLAR + "TargetNamespace";
private static final String QUALIFIER = DOLLAR + "Qualifier";
private static final String IS_FLAGS = DOLLAR + "IsFlags";
private static final String UNDERLYING_TYPE = DOLLAR + "UnderlyingType";
private static final String KIND = DOLLAR + "Kind";
private static final String MAX_LENGTH = DOLLAR + "MaxLength";
private static final String PRECISION = DOLLAR + "Precision";
private static final String SCALE = DOLLAR + "Scale";
private static final String SRID = DOLLAR + "SRID";
private static final String COLLECTION = DOLLAR + "Collection";
private static final String BASE_TYPE = DOLLAR + "BaseType";
private static final String HAS_STREAM = DOLLAR + "HasStream";
private static final String KEY = DOLLAR + "Key";
private static final String ABSTRACT = DOLLAR + "Abstract";
private static final String TYPE = DOLLAR + "Type";
private static final String NULLABLE = DOLLAR + "Nullable";
private static final String UNICODE = DOLLAR + "Unicode";
private static final String DEFAULT_VALUE = DOLLAR + "DefaultValue";
private static final String PARTNER = DOLLAR + "Partner";
private static final String CONTAINS_TARGET = DOLLAR + "ContainsTarget";
private static final String REFERENTIAL_CONSTRAINT = DOLLAR + "ReferentialConstraint";
private static final String ISBOUND = DOLLAR + "IsBound";
private static final String ENTITY_SET_PATH = DOLLAR + "EntitySetPath";
private static final String PARAMETER = DOLLAR + "Parameter";
private static final String RETURN_TYPE = DOLLAR + "ReturnType";
private static final String ISCOMPOSABLE = DOLLAR + "IsComposable";
private static final String PARAMETER_NAME = DOLLAR + "Name";
private static final String BASE_TERM = DOLLAR + "BaseTerm";
private static final String APPLIES_TO = DOLLAR + "AppliesTo";
private static final String NAVIGATION_PROPERTY_BINDING = DOLLAR + "NavigationPropertyBinding";
private static final String EXTENDS = DOLLAR + "Extends";
private static final String INCLUDE_IN_SERV_DOC = DOLLAR + "IncludeInServiceDocument";
private static final String ANNOTATION = DOLLAR + "Annotations";
private static final String ANNOTATION_PATH = DOLLAR + "Path";
private static final String NAME = DOLLAR + "Name";
public MetadataDocumentJsonSerializer(final ServiceMetadata serviceMetadata) throws SerializerException {
if (serviceMetadata == null || serviceMetadata.getEdm() == null) {
throw new SerializerException("Service Metadata and EDM must not be null for a service.",
SerializerException.MessageKeys.NULL_METADATA_OR_EDM);
}
this.serviceMetadata = serviceMetadata;
}
public void writeMetadataDocument(final JsonGenerator json) throws SerializerException, IOException {
json.writeStartObject();
json.writeStringField(VERSION, "4.01");
if (!serviceMetadata.getReferences().isEmpty()) {
appendReference(json);
}
appendDataServices(json);
json.writeEndObject();
}
private void appendDataServices(JsonGenerator json) throws SerializerException, IOException {
for (EdmSchema schema : serviceMetadata.getEdm().getSchemas()) {
appendSchema(json, schema);
}
}
private void appendSchema(JsonGenerator json, EdmSchema schema)
throws SerializerException, IOException {
json.writeFieldName(schema.getNamespace());
json.writeStartObject();
if (schema.getAlias() != null) {
json.writeStringField(ALIAS, schema.getAlias());
namespaceToAlias.put(schema.getNamespace(), schema.getAlias());
}
// EnumTypes
appendEnumTypes(json, schema.getEnumTypes());
// TypeDefinitions
appendTypeDefinitions(json, schema.getTypeDefinitions());
// EntityTypes
appendEntityTypes(json, schema.getEntityTypes());
// ComplexTypes
appendComplexTypes(json, schema.getComplexTypes());
// Actions
appendActions(json, schema.getActions());
// Functions
appendFunctions(json, schema.getFunctions());
//Terms
appendTerms(json, schema.getTerms());
// EntityContainer
appendEntityContainer(json, schema.getEntityContainer());
// AnnotationGroups
appendAnnotationGroups(json, schema.getAnnotationGroups());
appendAnnotations(json, schema, null);
json.writeEndObject();
}
private void appendAnnotationGroups(final JsonGenerator json,
final List<EdmAnnotations> annotationGroups) throws SerializerException, IOException {
if (!annotationGroups.isEmpty()) {
json.writeObjectFieldStart(ANNOTATION);
}
for (EdmAnnotations annotationGroup : annotationGroups) {
appendAnnotationGroup(json, annotationGroup);
}
if (!annotationGroups.isEmpty()) {
json.writeEndObject();
}
}
private void appendAnnotationGroup(final JsonGenerator json,
final EdmAnnotations annotationGroup) throws SerializerException, IOException {
String targetPath = annotationGroup.getTargetPath();
if (annotationGroup.getQualifier() != null) {
json.writeObjectFieldStart(targetPath + "#" + annotationGroup.getQualifier());
} else {
json.writeObjectFieldStart(targetPath);
}
appendAnnotations(json, annotationGroup, null);
json.writeEndObject();
}
private void appendEntityContainer(final JsonGenerator json,
final EdmEntityContainer container) throws SerializerException, IOException {
if (container != null) {
json.writeObjectFieldStart(container.getName());
json.writeStringField(KIND, Kind.EntityContainer.name());
FullQualifiedName parentContainerName = container.getParentContainerName();
if (parentContainerName != null) {
String parentContainerNameString;
if (namespaceToAlias.get(parentContainerName.getNamespace()) != null) {
parentContainerNameString =
namespaceToAlias.get(parentContainerName.getNamespace()) + "." + parentContainerName.getName();
} else {
parentContainerNameString = parentContainerName.getFullQualifiedNameAsString();
}
json.writeObjectFieldStart(Kind.Extending.name());
json.writeStringField(KIND, Kind.EntityContainer.name());
json.writeStringField(EXTENDS, parentContainerNameString);
json.writeEndObject();
}
// EntitySets
appendEntitySets(json, container.getEntitySets());
String containerNamespace;
if (namespaceToAlias.get(container.getNamespace()) != null) {
containerNamespace = namespaceToAlias.get(container.getNamespace());
} else {
containerNamespace = container.getNamespace();
}
// ActionImports
appendActionImports(json, container.getActionImports(), containerNamespace);
// FunctionImports
appendFunctionImports(json, container.getFunctionImports(), containerNamespace);
// Singletons
appendSingletons(json, container.getSingletons());
// Annotations
appendAnnotations(json, container, null);
json.writeEndObject();
}
}
private void appendSingletons(final JsonGenerator json,
final List<EdmSingleton> singletons) throws SerializerException, IOException {
for (EdmSingleton singleton : singletons) {
json.writeObjectFieldStart(singleton.getName());
json.writeStringField(KIND, Kind.Singleton.name());
json.writeStringField(TYPE, getAliasedFullQualifiedName(singleton.getEntityType()));
appendNavigationPropertyBindings(json, singleton);
appendAnnotations(json, singleton, null);
json.writeEndObject();
}
}
private void appendFunctionImports(final JsonGenerator json, final List<EdmFunctionImport> functionImports,
final String containerNamespace) throws SerializerException, IOException {
for (EdmFunctionImport functionImport : functionImports) {
json.writeObjectFieldStart(functionImport.getName());
json.writeStringField(KIND, Kind.FunctionImport.name());
String functionFQNString;
FullQualifiedName functionFqn = functionImport.getFunctionFqn();
if (namespaceToAlias.get(functionFqn.getNamespace()) != null) {
functionFQNString = namespaceToAlias.get(functionFqn.getNamespace()) + "." + functionFqn.getName();
} else {
functionFQNString = functionFqn.getFullQualifiedNameAsString();
}
json.writeStringField(DOLLAR + Kind.Function.name(), functionFQNString);
EdmEntitySet returnedEntitySet = functionImport.getReturnedEntitySet();
if (returnedEntitySet != null) {
json.writeStringField(DOLLAR + Kind.EntitySet.name(),
containerNamespace + "." + returnedEntitySet.getName());
}
// Default is false and we do not write the default
if (functionImport.isIncludeInServiceDocument()) {
json.writeBooleanField(INCLUDE_IN_SERV_DOC, functionImport.isIncludeInServiceDocument());
}
appendAnnotations(json, functionImport, null);
json.writeEndObject();
}
}
private void appendActionImports(final JsonGenerator json,
final List<EdmActionImport> actionImports, String containerNamespace)
throws SerializerException, IOException {
for (EdmActionImport actionImport : actionImports) {
json.writeObjectFieldStart(actionImport.getName());
json.writeStringField(KIND, Kind.ActionImport.name());
json.writeStringField(DOLLAR + Kind.Action.name(), getAliasedFullQualifiedName(actionImport.getUnboundAction()));
if (actionImport.getReturnedEntitySet() != null) {
json.writeStringField(DOLLAR + Kind.EntitySet.name(),
containerNamespace + "." + actionImport.getReturnedEntitySet().getName());
}
appendAnnotations(json, actionImport, null);
json.writeEndObject();
}
}
private void appendEntitySets(final JsonGenerator json,
final List<EdmEntitySet> entitySets) throws SerializerException, IOException {
for (EdmEntitySet entitySet : entitySets) {
json.writeObjectFieldStart(entitySet.getName());
json.writeStringField(KIND, Kind.EntitySet.name());
json.writeStringField(TYPE, getAliasedFullQualifiedName(entitySet.getEntityType()));
if (!entitySet.isIncludeInServiceDocument()) {
json.writeBooleanField(INCLUDE_IN_SERV_DOC, entitySet.isIncludeInServiceDocument());
}
appendNavigationPropertyBindings(json, entitySet);
appendAnnotations(json, entitySet, null);
json.writeEndObject();
}
}
private void appendNavigationPropertyBindings(final JsonGenerator json,
final EdmBindingTarget bindingTarget) throws SerializerException, IOException {
if (bindingTarget.getNavigationPropertyBindings() != null &&
!bindingTarget.getNavigationPropertyBindings().isEmpty()) {
json.writeObjectFieldStart(NAVIGATION_PROPERTY_BINDING);
for (EdmNavigationPropertyBinding binding : bindingTarget.getNavigationPropertyBindings()) {
json.writeStringField(binding.getPath(), binding.getTarget());
}
json.writeEndObject();
}
}
private void appendTerms(final JsonGenerator json, final List<EdmTerm> terms)
throws SerializerException, IOException {
for (EdmTerm term : terms) {
json.writeObjectFieldStart(term.getName());
json.writeStringField(KIND, Kind.Term.name());
json.writeStringField(TYPE, getAliasedFullQualifiedName(term.getType()));
if (term.getBaseTerm() != null) {
json.writeStringField(BASE_TERM, getAliasedFullQualifiedName(term.getBaseTerm().getFullQualifiedName()));
}
if (term.getAppliesTo() != null && !term.getAppliesTo().isEmpty()) {
String appliesToString = "";
boolean first = true;
for (TargetType target : term.getAppliesTo()) {
if (first) {
first = false;
appliesToString = target.toString();
} else {
appliesToString = appliesToString + " " + target.toString();
}
}
json.writeStringField(APPLIES_TO, appliesToString);
}
// Facets
if (!term.isNullable()) {
json.writeBooleanField(NULLABLE, term.isNullable());
}
if (term.getDefaultValue() != null) {
json.writeStringField(DEFAULT_VALUE, term.getDefaultValue());
}
if (term.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, term.getMaxLength());
}
if (term.getPrecision() != null) {
json.writeNumberField(PRECISION, term.getPrecision());
}
if (term.getScale() != null) {
json.writeNumberField(SCALE, term.getScale());
}
appendAnnotations(json, term, null);
json.writeEndObject();
}
}
private void appendFunctions(final JsonGenerator json,
final List<EdmFunction> functions) throws SerializerException, IOException {
Map<String, List<EdmFunction>> functionsMap = new HashMap<>();
for (EdmFunction function : functions) {
if (functionsMap.containsKey(function.getName())) {
List<EdmFunction> actionsWithSpecificActionName = functionsMap.get(function.getName());
actionsWithSpecificActionName.add(function);
functionsMap.put(function.getName(), actionsWithSpecificActionName);
} else {
List<EdmFunction> functionList = new ArrayList<>();
functionList.add(function);
functionsMap.put(function.getName(), functionList);
}
}
for (Entry<String, List<EdmFunction>> functionsMapEntry : functionsMap.entrySet()) {
json.writeArrayFieldStart(functionsMapEntry.getKey());
List<EdmFunction> functionEntry = functionsMapEntry.getValue();
for (EdmFunction function : functionEntry) {
json.writeStartObject();
json.writeStringField(KIND, Kind.Function.name());
if (function.getEntitySetPath() != null) {
json.writeStringField(ENTITY_SET_PATH, function.getEntitySetPath());
}
if (function.isBound()) {
json.writeBooleanField(ISBOUND, function.isBound());
}
if (function.isComposable()) {
json.writeBooleanField(ISCOMPOSABLE, function.isComposable());
}
appendOperationParameters(json, function);
appendOperationReturnType(json, function);
appendAnnotations(json, function, null);
json.writeEndObject();
}
json.writeEndArray();
}
}
private void appendActions(final JsonGenerator json,
final List<EdmAction> actions) throws SerializerException, IOException {
Map<String, List<EdmAction>> actionsMap = new HashMap<>();
for (EdmAction action : actions) {
if (actionsMap.containsKey(action.getName())) {
List<EdmAction> actionsWithSpecificActionName = actionsMap.get(action.getName());
actionsWithSpecificActionName.add(action);
actionsMap.put(action.getName(), actionsWithSpecificActionName);
} else {
List<EdmAction> actionList = new ArrayList<>();
actionList.add(action);
actionsMap.put(action.getName(), actionList);
}
}
for (Entry<String, List<EdmAction>> actionsMapEntry : actionsMap.entrySet()) {
json.writeArrayFieldStart(actionsMapEntry.getKey());
List<EdmAction> actionEntry = actionsMapEntry.getValue();
for (EdmAction action : actionEntry) {
json.writeStartObject();
json.writeStringField(KIND, Kind.Action.name());
if (action.getEntitySetPath() != null) {
json.writeStringField(ENTITY_SET_PATH, action.getEntitySetPath());
}
json.writeBooleanField(ISBOUND, action.isBound());
appendOperationParameters(json, action);
appendOperationReturnType(json, action);
appendAnnotations(json, action, null);
json.writeEndObject();
}
json.writeEndArray();
}
}
private void appendOperationReturnType(final JsonGenerator json,
final EdmOperation operation) throws SerializerException, IOException {
EdmReturnType returnType = operation.getReturnType();
if (returnType != null) {
json.writeObjectFieldStart(RETURN_TYPE);
String returnTypeFqnString;
if (EdmTypeKind.PRIMITIVE.equals(returnType.getType().getKind())) {
returnTypeFqnString = getFullQualifiedName(returnType.getType());
} else {
returnTypeFqnString = getAliasedFullQualifiedName(returnType.getType());
}
json.writeStringField(TYPE, returnTypeFqnString);
if (returnType.isCollection()) {
json.writeBooleanField(COLLECTION, returnType.isCollection());
}
appendReturnTypeFacets(json, returnType);
json.writeEndObject();
}
}
private void appendReturnTypeFacets(final JsonGenerator json,
final EdmReturnType returnType) throws SerializerException, IOException {
if (!returnType.isNullable()) {
json.writeBooleanField(NULLABLE, returnType.isNullable());
}
if (returnType.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, returnType.getMaxLength());
}
if (returnType.getPrecision() != null) {
json.writeNumberField(PRECISION, returnType.getPrecision());
}
if (returnType.getScale() != null) {
json.writeNumberField(SCALE, returnType.getScale());
}
}
private void appendOperationParameters(final JsonGenerator json,
final EdmOperation operation) throws SerializerException, IOException {
if (!operation.getParameterNames().isEmpty()) {
json.writeArrayFieldStart(PARAMETER);
}
for (String parameterName : operation.getParameterNames()) {
EdmParameter parameter = operation.getParameter(parameterName);
json.writeStartObject();
json.writeStringField(PARAMETER_NAME, parameterName);
String typeFqnString;
if (EdmTypeKind.PRIMITIVE.equals(parameter.getType().getKind())) {
typeFqnString = getFullQualifiedName(parameter.getType());
} else {
typeFqnString = getAliasedFullQualifiedName(parameter.getType());
}
json.writeStringField(TYPE, typeFqnString);
if (parameter.isCollection()) {
json.writeBooleanField(COLLECTION, parameter.isCollection());
}
appendParameterFacets(json, parameter);
appendAnnotations(json, parameter, null);
json.writeEndObject();
}
if (!operation.getParameterNames().isEmpty()) {
json.writeEndArray();
}
}
private void appendParameterFacets(final JsonGenerator json,
final EdmParameter parameter) throws SerializerException, IOException {
if (!parameter.isNullable()) {
json.writeBooleanField(NULLABLE, parameter.isNullable());
}
if (parameter.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, parameter.getMaxLength());
}
if (parameter.getPrecision() != null) {
json.writeNumberField(PRECISION, parameter.getPrecision());
}
if (parameter.getScale() != null) {
json.writeNumberField(SCALE, parameter.getScale());
}
}
private void appendComplexTypes(final JsonGenerator json,
final List<EdmComplexType> complexTypes) throws SerializerException, IOException {
for (EdmComplexType complexType : complexTypes) {
json.writeObjectFieldStart(complexType.getName());
json.writeStringField(KIND, Kind.ComplexType.name());
if (complexType.getBaseType() != null) {
json.writeStringField(BASE_TYPE, getAliasedFullQualifiedName(complexType.getBaseType()));
}
if (complexType.isAbstract()) {
json.writeBooleanField(ABSTRACT, complexType.isAbstract());
}
appendProperties(json, complexType);
appendNavigationProperties(json, complexType);
appendAnnotations(json, complexType, null);
json.writeEndObject();
}
}
private void appendEntityTypes(JsonGenerator json,
List<EdmEntityType> entityTypes) throws SerializerException, IOException {
for (EdmEntityType entityType : entityTypes) {
json.writeObjectFieldStart(entityType.getName());
json.writeStringField(KIND, Kind.EntityType.name());
if (entityType.hasStream()) {
json.writeBooleanField(HAS_STREAM, entityType.hasStream());
}
if (entityType.getBaseType() != null) {
json.writeStringField(BASE_TYPE, getAliasedFullQualifiedName(entityType.getBaseType()));
}
if (entityType.isAbstract()) {
json.writeBooleanField(ABSTRACT, entityType.isAbstract());
}
appendKey(json, entityType);
appendProperties(json, entityType);
appendNavigationProperties(json, entityType);
appendAnnotations(json, entityType, null);
json.writeEndObject();
}
}
private void appendNavigationProperties(final JsonGenerator json,
final EdmStructuredType type) throws SerializerException, IOException {
List<String> navigationPropertyNames = new ArrayList<>(type.getNavigationPropertyNames());
if (type.getBaseType() != null) {
navigationPropertyNames.removeAll(type.getBaseType().getNavigationPropertyNames());
}
for (String navigationPropertyName : navigationPropertyNames) {
EdmNavigationProperty navigationProperty = type.getNavigationProperty(navigationPropertyName);
json.writeObjectFieldStart(navigationPropertyName);
json.writeStringField(KIND, Kind.NavigationProperty.name());
json.writeStringField(TYPE, getAliasedFullQualifiedName(navigationProperty.getType()));
if (navigationProperty.isCollection()) {
json.writeBooleanField(COLLECTION, navigationProperty.isCollection());
}
if (!navigationProperty.isNullable()) {
json.writeBooleanField(NULLABLE, navigationProperty.isNullable());
}
if (navigationProperty.getPartner() != null) {
EdmNavigationProperty partner = navigationProperty.getPartner();
json.writeStringField(PARTNER, partner.getName());
}
if (navigationProperty.containsTarget()) {
json.writeBooleanField(CONTAINS_TARGET, navigationProperty.containsTarget());
}
if (navigationProperty.getReferentialConstraints() != null) {
for (EdmReferentialConstraint constraint : navigationProperty.getReferentialConstraints()) {
json.writeObjectFieldStart(REFERENTIAL_CONSTRAINT);
json.writeStringField(constraint.getPropertyName(), constraint.getReferencedPropertyName());
for (EdmAnnotation annotation : constraint.getAnnotations()) {
appendAnnotations(json, annotation, null);
}
json.writeEndObject();
}
}
appendAnnotations(json, navigationProperty, null);
json.writeEndObject();
}
}
private void appendProperties(final JsonGenerator json,
final EdmStructuredType type) throws SerializerException, IOException {
List<String> propertyNames = new ArrayList<>(type.getPropertyNames());
if (type.getBaseType() != null) {
propertyNames.removeAll(type.getBaseType().getPropertyNames());
}
for (String propertyName : propertyNames) {
EdmProperty property = type.getStructuralProperty(propertyName);
json.writeObjectFieldStart(propertyName);
String fqnString;
if (property.isPrimitive()) {
fqnString = getFullQualifiedName(property.getType());
} else {
fqnString = getAliasedFullQualifiedName(property.getType());
}
json.writeStringField(TYPE, fqnString);
if (property.isCollection()) {
json.writeBooleanField(COLLECTION, property.isCollection());
}
// Facets
if (!property.isNullable()) {
json.writeBooleanField(NULLABLE, property.isNullable());
}
if (!property.isUnicode()) {
json.writeBooleanField(UNICODE, property.isUnicode());
}
if (property.getDefaultValue() != null) {
json.writeStringField(DEFAULT_VALUE, property.getDefaultValue());
}
if (property.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, property.getMaxLength());
}
if (property.getPrecision() != null) {
json.writeNumberField(PRECISION, property.getPrecision());
}
if (property.getScale() != null) {
json.writeNumberField(SCALE, property.getScale());
}
if (property.getSrid() != null) {
json.writeStringField(SRID, "" + property.getSrid());
}
appendAnnotations(json, property, null);
json.writeEndObject();
}
}
private void appendKey(final JsonGenerator json,
final EdmEntityType entityType) throws SerializerException, IOException {
List<EdmKeyPropertyRef> keyPropertyRefs = entityType.getKeyPropertyRefs();
if (keyPropertyRefs != null && !keyPropertyRefs.isEmpty()) {
// Resolve Base Type key as it is shown in derived type
EdmEntityType baseType = entityType.getBaseType();
if (baseType != null && baseType.getKeyPropertyRefs() != null && !(baseType.getKeyPropertyRefs().isEmpty())) {
return;
}
json.writeArrayFieldStart(KEY);
for (EdmKeyPropertyRef keyRef : keyPropertyRefs) {
if (keyRef.getAlias() != null) {
json.writeStartObject();
json.writeStringField(keyRef.getAlias(), keyRef.getName());
json.writeEndObject();
} else {
json.writeString(keyRef.getName());
}
}
json.writeEndArray();
}
}
private String getAliasedFullQualifiedName(final EdmType type) {
FullQualifiedName fqn = type.getFullQualifiedName();
return getAliasedFullQualifiedName(fqn);
}
private void appendTypeDefinitions(final JsonGenerator json,
final List<EdmTypeDefinition> typeDefinitions) throws SerializerException, IOException {
for (EdmTypeDefinition definition : typeDefinitions) {
json.writeObjectFieldStart(definition.getName());
json.writeStringField(KIND, definition.getKind().name());
json.writeStringField(UNDERLYING_TYPE, getFullQualifiedName(definition.getUnderlyingType()));
// Facets
if (definition.getMaxLength() != null) {
json.writeStringField(MAX_LENGTH, "" + definition.getMaxLength());
}
if (definition.getPrecision() != null) {
json.writeStringField(PRECISION, "" + definition.getPrecision());
}
if (definition.getScale() != null) {
json.writeStringField(SCALE, "" + definition.getScale());
}
if (definition.getSrid() != null) {
json.writeStringField(SRID, "" + definition.getSrid());
}
appendAnnotations(json, definition, null);
json.writeEndObject();
}
}
private void appendEnumTypes(JsonGenerator json, List<EdmEnumType> enumTypes)
throws SerializerException, IOException {
for (EdmEnumType enumType : enumTypes) {
json.writeObjectFieldStart(enumType.getName());
json.writeStringField(KIND, Kind.EnumType.name());
json.writeBooleanField(IS_FLAGS, enumType.isFlags());
json.writeStringField(UNDERLYING_TYPE, getFullQualifiedName(enumType.getUnderlyingType()));
for (String memberName : enumType.getMemberNames()) {
EdmMember member = enumType.getMember(memberName);
if (member.getValue() != null) {
json.writeStringField(memberName, member.getValue());
}
appendAnnotations(json, member, memberName);
}
json.writeEndObject();
}
}
private void appendAnnotations(JsonGenerator json,
final EdmAnnotatable annotatable, String memberName) throws SerializerException, IOException {
List<EdmAnnotation> annotations = annotatable.getAnnotations();
if (annotations != null && !annotations.isEmpty()) {
for (EdmAnnotation annotation : annotations) {
String termName = memberName != null ? memberName : "";
if (annotation.getTerm() != null) {
termName += "@" + getAliasedFullQualifiedName(annotation.getTerm().getFullQualifiedName());
}
if (annotation.getQualifier() != null) {
termName += "#" + annotation.getQualifier();
}
if (annotation.getExpression() == null && termName.length() > 0) {
json.writeBooleanField(termName, true);
} else {
appendExpression(json, annotation.getExpression(), termName);
}
appendAnnotations(json, annotation, termName);
}
}
}
private void appendExpression(final JsonGenerator json,
final EdmExpression expression, String termName) throws SerializerException, IOException {
if (expression == null) {
return;
}
if (expression.isConstant()) {
appendConstantExpression(json, expression.asConstant(), termName);
} else if (expression.isDynamic()) {
appendDynamicExpression(json, expression.asDynamic(), termName);
} else {
throw new IllegalArgumentException("Unkown expressiontype in metadata");
}
}
private void appendDynamicExpression(JsonGenerator json,
EdmDynamicExpression dynExp, String termName) throws SerializerException, IOException {
json.writeFieldName(termName);
switch (dynExp.getExpressionType()) {
// Logical
case And:
appendLogicalOrComparisonExpression(json, dynExp.asAnd());
break;
case Or:
appendLogicalOrComparisonExpression(json, dynExp.asOr());
break;
case Not:
appendNotExpression(json, dynExp.asNot());
break;
// Comparison
case Eq:
appendLogicalOrComparisonExpression(json, dynExp.asEq());
break;
case Ne:
appendLogicalOrComparisonExpression(json, dynExp.asNe());
break;
case Gt:
appendLogicalOrComparisonExpression(json, dynExp.asGt());
break;
case Ge:
appendLogicalOrComparisonExpression(json, dynExp.asGe());
break;
case Lt:
appendLogicalOrComparisonExpression(json, dynExp.asLt());
break;
case Le:
appendLogicalOrComparisonExpression(json, dynExp.asLe());
break;
case AnnotationPath:
json.writeStartObject();
json.writeStringField(ANNOTATION_PATH, dynExp.asAnnotationPath().getValue());
json.writeEndObject();
break;
case Apply:
EdmApply asApply = dynExp.asApply();
json.writeStartObject();
json.writeArrayFieldStart(DOLLAR + asApply.getExpressionName());
for (EdmExpression parameter : asApply.getParameters()) {
appendExpression(json, parameter, null);
}
json.writeEndArray();
json.writeStringField(DOLLAR + Kind.Function.name(), asApply.getFunction());
appendAnnotations(json, asApply, null);
json.writeEndObject();
break;
case Cast:
EdmCast asCast = dynExp.asCast();
json.writeStartObject();
appendExpression(json, asCast.getValue(), DOLLAR + asCast.getExpressionName());
json.writeStringField(TYPE, getAliasedFullQualifiedName(asCast.getType()));
if (asCast.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, asCast.getMaxLength());
}
if (asCast.getPrecision() != null) {
json.writeNumberField(PRECISION, asCast.getPrecision());
}
if (asCast.getScale() != null) {
json.writeNumberField(SCALE, asCast.getScale());
}
appendAnnotations(json, asCast, null);
json.writeEndObject();
break;
case Collection:
json.writeStartArray();
for (EdmExpression item : dynExp.asCollection().getItems()) {
appendExpression(json, item, null);
}
json.writeEndArray();
break;
case If:
EdmIf asIf = dynExp.asIf();
json.writeStartObject();
json.writeArrayFieldStart(DOLLAR + asIf.getExpressionName());
appendExpression(json, asIf.getGuard(), null);
appendExpression(json, asIf.getThen(), null);
appendExpression(json, asIf.getElse(), null);
json.writeEndArray();
appendAnnotations(json, asIf, null);
json.writeEndObject();
break;
case IsOf:
EdmIsOf asIsOf = dynExp.asIsOf();
json.writeStartObject();
appendExpression(json, asIsOf.getValue(), DOLLAR + asIsOf.getExpressionName());
json.writeStringField(TYPE, getAliasedFullQualifiedName(asIsOf.getType()));
if (asIsOf.getMaxLength() != null) {
json.writeNumberField(MAX_LENGTH, asIsOf.getMaxLength());
}
if (asIsOf.getPrecision() != null) {
json.writeNumberField(PRECISION, asIsOf.getPrecision());
}
if (asIsOf.getScale() != null) {
json.writeNumberField(SCALE, asIsOf.getScale());
}
appendAnnotations(json, asIsOf, null);
json.writeEndObject();
break;
case LabeledElement:
EdmLabeledElement asLabeledElement = dynExp.asLabeledElement();
json.writeStartObject();
appendExpression(json, asLabeledElement.getValue(), DOLLAR + asLabeledElement.getExpressionName());
json.writeStringField(NAME, asLabeledElement.getName());
appendAnnotations(json, asLabeledElement, null);
json.writeEndObject();
break;
case LabeledElementReference:
EdmLabeledElementReference asLabeledElementReference = dynExp.asLabeledElementReference();
json.writeStartObject();
json.writeStringField(DOLLAR + asLabeledElementReference.getExpressionName(),
asLabeledElementReference.getValue());
json.writeEndObject();
break;
case Null:
EdmNull asNull = dynExp.asNull();
json.writeStartObject();
json.writeStringField(DOLLAR + asNull.getExpressionName(), null);
appendAnnotations(json, dynExp.asNull(), null);
json.writeEndObject();
break;
case NavigationPropertyPath:
EdmNavigationPropertyPath asNavigationPropertyPath = dynExp.asNavigationPropertyPath();
json.writeStartObject();
json.writeStringField(DOLLAR + asNavigationPropertyPath.getExpressionName(),
asNavigationPropertyPath.getValue());
json.writeEndObject();
break;
case Path:
EdmPath asPath = dynExp.asPath();
json.writeStartObject();
json.writeStringField(DOLLAR + asPath.getExpressionName(), asPath.getValue());
json.writeEndObject();
break;
case PropertyPath:
EdmPropertyPath asPropertyPath = dynExp.asPropertyPath();
json.writeStartObject();
json.writeStringField(DOLLAR + asPropertyPath.getExpressionName(), asPropertyPath.getValue());
json.writeEndObject();
break;
case Record:
EdmRecord asRecord = dynExp.asRecord();
json.writeStartObject();
EdmStructuredType type = asRecord.getType();
if (type != null) {
json.writeStringField(TYPE, getAliasedFullQualifiedName(type));
}
for (EdmPropertyValue propValue : asRecord.getPropertyValues()) {
appendExpression(json, propValue.getValue(), propValue.getProperty());
appendAnnotations(json, propValue, propValue.getProperty());
}
appendAnnotations(json, asRecord, null);
json.writeEndObject();
break;
case UrlRef:
EdmUrlRef asUrlRef = dynExp.asUrlRef();
json.writeStartObject();
appendExpression(json, asUrlRef.getValue(), DOLLAR + asUrlRef.getExpressionName());
appendAnnotations(json, asUrlRef, null);
json.writeEndObject();
break;
default:
throw new IllegalArgumentException("Unkown ExpressionType for dynamic expression: " + dynExp.getExpressionType());
}
}
private void appendNotExpression(final JsonGenerator json, final EdmNot exp)
throws SerializerException, IOException {
json.writeStartObject();
appendExpression(json, exp.getLeftExpression(), DOLLAR + exp.getExpressionName());
appendAnnotations(json, exp, null);
json.writeEndObject();
}
private void appendLogicalOrComparisonExpression(final JsonGenerator json,
final EdmLogicalOrComparisonExpression exp) throws SerializerException, IOException {
json.writeStartObject();
json.writeArrayFieldStart(DOLLAR + exp.getExpressionName());
appendExpression(json, exp.getLeftExpression(), null);
appendExpression(json, exp.getRightExpression(), null);
json.writeEndArray();
appendAnnotations(json, exp, null);
json.writeEndObject();
}
private void appendConstantExpression(final JsonGenerator json,
final EdmConstantExpression constExp, String termName) throws SerializerException, IOException {
switch (constExp.getExpressionType()) {
case Binary:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Date:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case DateTimeOffset:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Decimal:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Float:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Int:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Duration:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case EnumMember:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Guid:
json.writeObjectFieldStart(termName);
json.writeStringField("$" + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case TimeOfDay:
json.writeObjectFieldStart(termName);
json.writeStringField(DOLLAR + constExp.getExpressionName(), constExp.getValueAsString());
json.writeEndObject();
break;
case Bool:
if (termName != null && termName.length() > 0) {
json.writeBooleanField(termName, Boolean.valueOf(constExp.getValueAsString()));
} else {
json.writeBoolean(Boolean.valueOf(constExp.getValueAsString()));
}
break;
case String:
if (termName != null && termName.length() > 0) {
json.writeStringField(termName, constExp.getValueAsString());
} else {
json.writeString(constExp.getValueAsString());
}
break;
default:
throw new IllegalArgumentException("Unkown ExpressionType "
+ "for constant expression: " + constExp.getExpressionType());
}
}
private String getAliasedFullQualifiedName(final FullQualifiedName fqn) {
final String name;
if (namespaceToAlias.get(fqn.getNamespace()) != null) {
name = namespaceToAlias.get(fqn.getNamespace()) + "." + fqn.getName();
} else {
name = fqn.getFullQualifiedNameAsString();
}
return name;
}
private String getFullQualifiedName(final EdmType type) {
return type.getFullQualifiedName().getFullQualifiedNameAsString();
}
private void appendReference(JsonGenerator json) throws SerializerException, IOException {
json.writeObjectFieldStart(REFERENCES);
for (final EdmxReference reference : serviceMetadata.getReferences()) {
json.writeObjectFieldStart(reference.getUri().toASCIIString());
List<EdmxReferenceInclude> includes = reference.getIncludes();
if (!includes.isEmpty()) {
appendIncludes(json, includes);
}
List<EdmxReferenceIncludeAnnotation> includeAnnotations = reference.getIncludeAnnotations();
if (!includeAnnotations.isEmpty()) {
appendIncludeAnnotations(json, includeAnnotations);
}
json.writeEndObject();
}
json.writeEndObject();
}
private void appendIncludeAnnotations(JsonGenerator json,
List<EdmxReferenceIncludeAnnotation> includeAnnotations) throws SerializerException, IOException {
json.writeArrayFieldStart(INCLUDE_ANNOTATIONS);
for (EdmxReferenceIncludeAnnotation includeAnnotation : includeAnnotations) {
json.writeStartObject();
json.writeStringField(TERM_NAMESPACE, includeAnnotation.getTermNamespace());
if (includeAnnotation.getQualifier() != null) {
json.writeStringField(QUALIFIER, includeAnnotation.getQualifier());
}
if (includeAnnotation.getTargetNamespace() != null) {
json.writeStringField(TARGET_NAMESPACE, includeAnnotation.getTargetNamespace());
}
json.writeEndObject();
}
json.writeEndArray();
}
private void appendIncludes(JsonGenerator json,
List<EdmxReferenceInclude> includes) throws SerializerException, IOException {
json.writeArrayFieldStart(INCLUDE);
for (EdmxReferenceInclude include : includes) {
json.writeStartObject();
json.writeStringField(NAMESPACE, include.getNamespace());
if (include.getAlias() != null) {
namespaceToAlias.put(include.getNamespace(), include.getAlias());
// Reference Aliases are ignored for now since they are not V2 compatible
json.writeStringField(ALIAS, include.getAlias());
}
json.writeEndObject();
}
json.writeEndArray();
}
}