Repository: olingo-odata4
Updated Branches:
  refs/heads/jsonMetadata 91c2f6a45 -> 99c201eaf


[OLINGO-570] JsonMetadatadocument serializer part1


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/99c201ea
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/99c201ea
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/99c201ea

Branch: refs/heads/jsonMetadata
Commit: 99c201eaf6f6fe03de31c0e0ca870c8eabc3d8e5
Parents: 91c2f6a
Author: Christian Amend <[email protected]>
Authored: Mon Jun 15 14:26:09 2015 +0200
Committer: Christian Amend <[email protected]>
Committed: Mon Jun 15 14:26:09 2015 +0200

----------------------------------------------------------------------
 .../json/MetadataDocumentJsonSerializer.java    | 647 +++++++++++++++++++
 .../serializer/json/ODataJsonSerializer.java    |  25 +-
 .../MetadataDocumentJsonSerializerTest.java     | 222 +++++++
 3 files changed, 892 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/99c201ea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
new file mode 100644
index 0000000..4cf9abb
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
@@ -0,0 +1,647 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonGenerator;
+import org.apache.olingo.commons.api.edm.*;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.edmx.EdmxReference;
+import org.apache.olingo.server.api.edmx.EdmxReferenceInclude;
+import org.apache.olingo.server.api.edmx.EdmxReferenceIncludeAnnotation;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MetadataDocumentJsonSerializer {
+
+    private final ServiceMetadata serviceMetadata;
+    private final Map<String, String> namespaceToAlias = new HashMap<String, 
String>();
+    private static final String CONSTANT_SCHEMA = "$schema";
+    private static final String 
DEFAULT_SCHEMA="http://docs.oasis-open.org/odata/odata-json-csdl/v4.0/edm.json#";;
+    private static final String 
CONSTANT_DEFINITION_REFERENCE=DEFAULT_SCHEMA+"/definitions/";
+    private static final String CONSTANT_REFERENCE_IDENTIFIER="$ref";
+    private static final String CONSTANT_REFERENCES = "references";
+    private static final String CONSTANT_INCLUDE = "includes";
+    private static final String CONSTANT_INCLUDE_ANNOTATIONS = 
"includeAnnotations";
+    private static final String CONSTANT_ALIAS = "alias";
+    private static final String CONSTANT_SCHEMAS= "schemas";
+    private static final String CONSTANT_ENUM= "enum";
+    private static final String CONSTANT_JSON_VALUE = "@odata.value";
+    private static final String CONSTANT_DEFINITIONS= "definitions";
+    private static final String CONSTANT_TARGET_NAMESPACE = "targetNamespace";
+    private static final String CONSTANT_TERM_NAMESPACE = "termNamespace";
+    private static final String CONSTANT_QUALIFIER = "qualifier";
+    private static final String CONSTANT_SCALE = "scale";
+    private static final String CONSTANT_PRECISION = "precision";
+    private static final String CONSTANT_NULLABLE = "nullable";
+    private static final String CONSTANT_MAX_LENGTH = "maxLength";
+    private static final String CONSTANT_ACTIONS = "actions";
+    private static final String CONSTANT_FUNCTIONS = "functions";
+    private static final String CONSTANT_NAME = "name";
+    private static final String CONSTANT_TYPE = "type";
+    private static final String CONSTANT_IS_BOUND = "isBound";
+    private static final String CONSTANT_IS_COMPOSABLE = "isComposable";
+    private static final String CONSTANT_PARAMETERS = "parameters";
+    private static final String CONSTANT_RETURN_TYPE = "returnType";
+    private static final String CONSTANT_ENTITY_SET_PATH= "entitySetPath";
+    private static final String CONSTANT_ENTITY_SET= "entitySet";
+    private static final String CONSTANT_FUNCTION_IMPORTS = "functionImports";
+    private static final String CONSTANT_ACTION_IMPORTS = "actionImports";
+    private static final String CONSTANT_FUNCTION_IDENTIFIER = "function";
+    private static final String CONSTANT_ACTION_IDENTIFIER = "action";
+    private static final String CONSTANT_IS_INCLUDED_IN_SERVICE_DOCUMENT = 
"includeInServiceDocument";
+    private static final String CONSTANT_NAVIGATION_PROPERTY_BINDINGS = 
"navigationPropertyBindings";
+    private static final String CONSTANT_SINGLETONS = "singletons";
+    private static final String CONSTANT_TARGET = "target";
+    private static final String CONSTANT_PROPERTIES = "properties";
+    private static final String CONSTANT_OBJECT = "object";
+    private static final String CONSTANT_ABSTRACT = "abstract";
+    private static final String CONSTANT_BASE_TYPE = "baseType";
+    private static final String CONSTANT_IS_UNICODE = "unicode";
+    private static final String CONSTANT_DEFAULT_VALUE = "defaultValue";
+    private static final String CONSTANT_PARTNER = "partner";
+    private static final String CONSTANT_CONTAINS_TARGET = "containsTarget";
+    private static final String CONSTANT_RELATIONSHIP = "relationship";
+    private static final String CONSTANT_REFERENTIAL_CONSTRAINTS = 
"referentialConstraints";
+    private static final String CONSTANT_REFERENCED_PROPERTY = 
"referencedProperty";
+    private static final String CONSTANT_HAS_STREAM = "hasStream";
+    private static final String CONSTANT_OPEN_TYPE= "openType";
+    private static final String CONSTANT_KEYS = "keys";
+    private static final String CONSTANT_ENTITY_SETS = "entitySets";
+    private static final String CONSTANT_ENTITY_TYPE = "entityType";
+    private static final String CONSTANT_ENTITY_CONTAINER = "entityContainer";
+    private static final String CONSTANT_EXTEND = "extend";
+
+    public MetadataDocumentJsonSerializer(final ServiceMetadata 
serviceMetadata) {
+        this.serviceMetadata = serviceMetadata;
+    }
+
+    public void writeMetadataDocument(final JsonGenerator gen) throws 
IOException {
+        gen.writeStartObject();
+        gen.writeObjectField(CONSTANT_SCHEMA, DEFAULT_SCHEMA);
+        appendDefinitions(gen);
+        appendSchemas(gen);
+        appendReference(gen);
+        gen.writeEndObject();
+    }
+
+    private void appendSchemas(final JsonGenerator gen) throws IOException {
+        if (!serviceMetadata.getEdm().getSchemas().isEmpty()) {
+            gen.writeFieldName(CONSTANT_SCHEMAS);
+            gen.writeStartObject();
+            for (EdmSchema schema : serviceMetadata.getEdm().getSchemas()) {
+                appendSchema(gen, schema);
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendDefinitions(final JsonGenerator gen) throws IOException 
{
+        if (!serviceMetadata.getEdm().getSchemas().isEmpty()) {
+            gen.writeFieldName(CONSTANT_DEFINITIONS);
+            gen.writeStartObject();
+            for (EdmSchema schema : serviceMetadata.getEdm().getSchemas()) {
+                appendSchemaDefinitions(gen, schema);
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendSchemaDefinitions(final JsonGenerator gen, final 
EdmSchema schema) throws IOException {
+        appendEnumTypes(gen, schema.getEnumTypes());
+        appendTypeDefinitions(gen, schema.getTypeDefinitions());
+        appendEntityTypes(gen, schema.getEntityTypes());
+        appendComplexTypes(gen, schema.getComplexTypes());
+    }
+
+    private void appendSchema(final JsonGenerator gen, final EdmSchema schema) 
throws IOException {
+        gen.writeFieldName(schema.getNamespace());
+        gen.writeStartObject();
+        if (schema.getAlias() != null) {
+            gen.writeStringField(CONSTANT_ALIAS,schema.getAlias());
+            namespaceToAlias.put(schema.getNamespace(), schema.getAlias());
+        }else{
+            gen.writeNullField(CONSTANT_ALIAS);
+        }
+        appendActions(gen, schema.getActions());
+        appendFunctions(gen, schema.getFunctions());
+        appendEntityContainer(gen, schema.getEntityContainer());
+        gen.writeEndObject();
+    }
+
+    private void appendTypeDefinitions(final JsonGenerator gen, final 
List<EdmTypeDefinition> typeDefinitions)
+            throws IOException {
+        for (EdmTypeDefinition definition : typeDefinitions) {
+            gen.writeFieldName(getAliasedFullQualifiedName(definition, false));
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_REFERENCE_IDENTIFIER,
+                    CONSTANT_DEFINITION_REFERENCE + 
getFullQualifiedName(definition.getUnderlyingType(), false));
+
+            if (definition.getMaxLength() != null) {
+                gen.writeNumberField(CONSTANT_MAX_LENGTH, 
definition.getMaxLength());
+            }
+
+            if (definition.getPrecision() != null) {
+                gen.writeNumberField(CONSTANT_PRECISION, 
definition.getPrecision());
+            }
+
+            if (definition.getScale() != null) {
+                gen.writeNumberField(CONSTANT_SCALE, definition.getScale());
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendActions(final JsonGenerator gen, final List<EdmAction> 
actions) throws IOException {
+        if(!actions.isEmpty()){
+            gen.writeFieldName(CONSTANT_ACTIONS);
+            gen.writeStartArray();
+            for (EdmAction action : actions) {
+                gen.writeStartObject();
+                gen.writeStringField(CONSTANT_NAME, action.getName());
+                if (action.getEntitySetPath() != null) {
+                    gen.writeStringField(CONSTANT_ENTITY_SET_PATH, 
action.getEntitySetPath());
+                }
+                gen.writeBooleanField(CONSTANT_IS_BOUND, action.isBound());
+                appendOperationParameters(gen, action);
+                appendOperationReturnType(gen, action);
+                gen.writeEndObject();
+            }
+            gen.writeEndArray();
+        }
+    }
+
+    private void appendFunctions(final JsonGenerator gen, final 
List<EdmFunction> functions)
+            throws IOException {
+        if(!functions.isEmpty()) {
+            gen.writeFieldName(CONSTANT_FUNCTIONS);
+            gen.writeStartArray();
+            for (EdmFunction function : functions) {
+                gen.writeStartObject();
+                gen.writeStringField(CONSTANT_NAME, function.getName());
+                if (function.getEntitySetPath() != null) {
+                    gen.writeStringField(CONSTANT_ENTITY_SET_PATH, 
function.getEntitySetPath());
+                }
+                if (function.isBound()) {
+                    gen.writeBooleanField(CONSTANT_IS_BOUND, 
function.isBound());
+                }
+
+                if (function.isComposable()) {
+                    gen.writeBooleanField(CONSTANT_IS_COMPOSABLE, 
function.isComposable());
+                }
+                appendOperationParameters(gen, function);
+                appendOperationReturnType(gen, function);
+                gen.writeEndObject();
+            }
+            gen.writeEndArray();
+        }
+    }
+
+    private void appendOperationReturnType(final JsonGenerator gen, final 
EdmOperation operation)
+            throws IOException {
+        EdmReturnType returnType = operation.getReturnType();
+        if (returnType != null) {
+            gen.writeFieldName(CONSTANT_RETURN_TYPE);
+            gen.writeStartObject();
+            String returnTypeFqnString;
+            if (EdmTypeKind.PRIMITIVE.equals(returnType.getType().getKind())) {
+                returnTypeFqnString = 
getFullQualifiedName(returnType.getType(), returnType.isCollection());
+            } else {
+                returnTypeFqnString = 
getAliasedFullQualifiedName(returnType.getType(), returnType.isCollection());
+            }
+            gen.writeStringField(CONSTANT_TYPE,returnTypeFqnString);
+            appendReturnTypeFacets(gen, returnType);
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendOperationParameters(final JsonGenerator gen, final 
EdmOperation operation)
+            throws IOException {
+        gen.writeFieldName(CONSTANT_PARAMETERS);
+        gen.writeStartObject();
+        for (String parameterName : operation.getParameterNames()) {
+            gen.writeFieldName(parameterName);
+            gen.writeStartObject();
+            EdmParameter parameter = operation.getParameter(parameterName);
+            String typeFqnString;
+            if (EdmTypeKind.PRIMITIVE.equals(parameter.getType().getKind())) {
+                typeFqnString = getFullQualifiedName(parameter.getType(), 
parameter.isCollection());
+            } else {
+                typeFqnString = 
getAliasedFullQualifiedName(parameter.getType(), parameter.isCollection());
+            }
+            gen.writeStringField(CONSTANT_TYPE, typeFqnString);
+            appendParameterFacets(gen, parameter);
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendReturnTypeFacets(final JsonGenerator gen, final 
EdmReturnType returnType)
+            throws IOException {
+        if (returnType.isNullable() == false) {
+            gen.writeBooleanField(CONSTANT_NULLABLE, returnType.isNullable());
+        }
+        if (returnType.getMaxLength() != null) {
+            gen.writeNumberField(CONSTANT_MAX_LENGTH, 
returnType.getMaxLength());
+        }
+        if (returnType.getPrecision() != null) {
+            gen.writeNumberField(CONSTANT_PRECISION, 
returnType.getPrecision());
+        }
+        if (returnType.getScale() != null) {
+            gen.writeNumberField(CONSTANT_SCALE, returnType.getScale());
+        }
+    }
+
+    private void appendParameterFacets(final JsonGenerator gen, final 
EdmParameter parameter)
+            throws IOException {
+        if (parameter.isNullable() == false) {
+            gen.writeBooleanField(CONSTANT_NULLABLE,parameter.isNullable());
+        }
+        if (parameter.getMaxLength() != null) {
+            gen.writeNumberField(CONSTANT_MAX_LENGTH, 
parameter.getMaxLength());
+        }
+        if (parameter.getPrecision() != null) {
+            gen.writeNumberField(CONSTANT_PRECISION, parameter.getPrecision());
+        }
+        if (parameter.getScale() != null) {
+            gen.writeNumberField(CONSTANT_SCALE, parameter.getScale());
+        }
+    }
+
+    private void appendEntityContainer(final JsonGenerator gen, final 
EdmEntityContainer container)
+            throws IOException {
+        if (container != null) {
+            gen.writeFieldName(CONSTANT_ENTITY_CONTAINER);
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_NAME,container.getName());
+            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();
+                }
+                
gen.writeStringField(CONSTANT_EXTEND,parentContainerNameString);
+            }
+            appendEntitySets(gen, container.getEntitySets());
+            appendActionImports(gen, container.getActionImports());
+            String containerNamespace;
+            if (namespaceToAlias.get(container.getNamespace()) != null) {
+                containerNamespace = 
namespaceToAlias.get(container.getNamespace());
+            } else {
+                containerNamespace = container.getNamespace();
+            }
+            appendFunctionImports(gen, container.getFunctionImports(), 
containerNamespace);
+            appendSingletons(gen, container.getSingletons());
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendFunctionImports(final JsonGenerator gen, final 
List<EdmFunctionImport> functionImports,
+                                       final String containerNamespace) throws 
IOException {
+        gen.writeFieldName(CONSTANT_FUNCTION_IMPORTS);
+        gen.writeStartObject();
+        for (EdmFunctionImport functionImport : functionImports) {
+            gen.writeFieldName(functionImport.getName());
+            gen.writeStartObject();
+            String functionFQNString;
+            FullQualifiedName functionFqn = functionImport.getFunctionFqn();
+            if (namespaceToAlias.get(functionFqn.getNamespace()) != null) {
+                functionFQNString = 
namespaceToAlias.get(functionFqn.getNamespace()) + "." + functionFqn.getName();
+            } else {
+                functionFQNString = functionFqn.getFullQualifiedNameAsString();
+            }
+            
gen.writeStringField(CONSTANT_FUNCTION_IDENTIFIER,functionFQNString);
+            EdmEntitySet returnedEntitySet = 
functionImport.getReturnedEntitySet();
+            if (returnedEntitySet != null) {
+                gen.writeStringField(CONSTANT_ENTITY_SET, containerNamespace + 
"." + returnedEntitySet.getName());
+            }
+            if (functionImport.isIncludeInServiceDocument()) {
+                gen.writeBooleanField(CONSTANT_IS_INCLUDED_IN_SERVICE_DOCUMENT,
+                        functionImport.isIncludeInServiceDocument());
+            }
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendActionImports(final JsonGenerator gen, final 
List<EdmActionImport> actionImports)
+            throws IOException {
+        gen.writeFieldName(CONSTANT_ACTION_IMPORTS);
+        gen.writeStartObject();
+        for (EdmActionImport actionImport : actionImports) {
+            gen.writeFieldName(actionImport.getName());
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_ACTION_IDENTIFIER,
+                    
getAliasedFullQualifiedName(actionImport.getUnboundAction(), false));
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendSingletons(final JsonGenerator gen, final 
List<EdmSingleton> singletons)
+            throws IOException {
+        gen.writeFieldName(CONSTANT_SINGLETONS);
+        gen.writeStartObject();
+        for (EdmSingleton singleton : singletons) {
+            gen.writeFieldName(singleton.getName());
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_TYPE, 
getAliasedFullQualifiedName(singleton.getEntityType(), false));
+            appendNavigationPropertyBindings(gen, singleton);
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendNavigationPropertyBindings(final JsonGenerator gen, 
final EdmBindingTarget bindingTarget)
+            throws IOException {
+        if (bindingTarget.getNavigationPropertyBindings() != null) {
+            gen.writeFieldName(CONSTANT_NAVIGATION_PROPERTY_BINDINGS);
+            gen.writeStartObject();
+            for (EdmNavigationPropertyBinding binding : 
bindingTarget.getNavigationPropertyBindings()) {
+                gen.writeFieldName(binding.getPath());
+                gen.writeStartObject();
+                gen.writeStringField(CONSTANT_TARGET,binding.getTarget());
+                gen.writeEndObject();
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendEntitySets(final JsonGenerator gen, final 
List<EdmEntitySet> entitySets)
+            throws IOException {
+        gen.writeFieldName(CONSTANT_ENTITY_SETS);
+        gen.writeStartObject();
+        for (EdmEntitySet entitySet : entitySets) {
+            gen.writeFieldName(entitySet.getName());
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_ENTITY_TYPE, 
getAliasedFullQualifiedName(entitySet.getEntityType(), false));
+            if (!entitySet.isIncludeInServiceDocument()) {
+                
gen.writeBooleanField(CONSTANT_IS_INCLUDED_IN_SERVICE_DOCUMENT, 
entitySet.isIncludeInServiceDocument());
+            }
+            appendNavigationPropertyBindings(gen, entitySet);
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+
+    private void appendComplexTypes(final JsonGenerator gen, final 
List<EdmComplexType> complexTypes)
+            throws IOException {
+        for (EdmComplexType complexType : complexTypes) {
+            gen.writeFieldName(getFullQualifiedName(complexType, false));
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_TYPE, CONSTANT_OBJECT);
+            if (complexType.getBaseType() != null) {
+                gen.writeStringField(CONSTANT_BASE_TYPE,
+                        getAliasedFullQualifiedName(complexType.getBaseType(), 
false));
+            }
+            if (complexType.isAbstract()) {
+                gen.writeBooleanField(CONSTANT_ABSTRACT, 
complexType.isAbstract());
+            }
+            appendProperties(gen, complexType);
+            appendNavigationProperties(gen, complexType);
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendEntityTypes(final JsonGenerator gen, final 
List<EdmEntityType> entityTypes)
+            throws IOException {
+        for (EdmEntityType entityType : entityTypes) {
+            gen.writeFieldName(getFullQualifiedName(entityType, false));
+            gen.writeStartObject();
+            gen.writeStringField(CONSTANT_TYPE, CONSTANT_OBJECT);
+            if (entityType.hasStream()) {
+                gen.writeBooleanField(CONSTANT_HAS_STREAM, 
entityType.hasStream());
+            }
+            if (entityType.getBaseType() != null) {
+                gen.writeStringField(CONSTANT_BASE_TYPE, 
getAliasedFullQualifiedName(entityType.getBaseType(), false));
+            }
+            if (entityType.isAbstract()) {
+                gen.writeBooleanField(CONSTANT_ABSTRACT, 
entityType.isAbstract());
+            }
+            if(entityType.isOpenType()){
+                
gen.writeBooleanField(CONSTANT_OPEN_TYPE,entityType.isOpenType());
+            }
+            appendKey(gen, entityType);
+            appendProperties(gen, entityType);
+            appendNavigationProperties(gen, entityType);
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendNavigationProperties(final JsonGenerator gen, final 
EdmStructuredType type)
+            throws IOException {
+        gen.writeFieldName(CONSTANT_PROPERTIES);
+        gen.writeStartObject();
+        List<String> navigationPropertyNames = new 
ArrayList<String>(type.getNavigationPropertyNames());
+        if (type.getBaseType() != null) {
+            
navigationPropertyNames.removeAll(type.getBaseType().getNavigationPropertyNames());
+        }
+        for (String navigationPropertyName : navigationPropertyNames) {
+            gen.writeFieldName(navigationPropertyName);
+            gen.writeStartObject();
+            EdmNavigationProperty navigationProperty = 
type.getNavigationProperty(navigationPropertyName);
+            gen.writeStringField(CONSTANT_REFERENCE_IDENTIFIER, 
CONSTANT_DEFINITION_REFERENCE +
+                    getAliasedFullQualifiedName(navigationProperty.getType(), 
false));
+            if (navigationProperty.isNullable() == false) {
+                gen.writeBooleanField(CONSTANT_NULLABLE, 
navigationProperty.isNullable());
+            }
+            gen.writeFieldName(CONSTANT_RELATIONSHIP);
+            gen.writeStartObject();
+            if (navigationProperty.getPartner() != null) {
+                EdmNavigationProperty partner = 
navigationProperty.getPartner();
+                gen.writeStringField(CONSTANT_PARTNER, partner.getName());
+            }
+            if (navigationProperty.containsTarget()) {
+                gen.writeBooleanField(CONSTANT_CONTAINS_TARGET, 
navigationProperty.containsTarget());
+            }
+
+            if (navigationProperty.getReferentialConstraints() != null) {
+                gen.writeFieldName(CONSTANT_REFERENTIAL_CONSTRAINTS);
+                gen.writeStartObject();
+                for (EdmReferentialConstraint constraint : 
navigationProperty.getReferentialConstraints()) {
+                    gen.writeFieldName(constraint.getPropertyName());
+                    gen.writeStartObject();
+                    gen.writeStringField(CONSTANT_REFERENCED_PROPERTY, 
constraint.getReferencedPropertyName());
+                    gen.writeEndObject();
+                }
+                gen.writeEndObject();
+            }
+            gen.writeEndObject();
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendProperties(final JsonGenerator gen, final 
EdmStructuredType type) throws IOException {
+        gen.writeFieldName(CONSTANT_PROPERTIES);
+        gen.writeStartObject();
+        List<String> propertyNames = new 
ArrayList<String>(type.getPropertyNames());
+        if (type.getBaseType() != null) {
+            propertyNames.removeAll(type.getBaseType().getPropertyNames());
+        }
+        for (String propertyName : propertyNames) {
+            EdmProperty property = type.getStructuralProperty(propertyName);
+            gen.writeFieldName(propertyName);
+            gen.writeStartObject();
+            String fqnString;
+            if (property.isPrimitive()) {
+                fqnString = getFullQualifiedName(property.getType(), 
property.isCollection());
+            } else {
+                fqnString = getAliasedFullQualifiedName(property.getType(), 
property.isCollection());
+            }
+            gen.writeStringField(CONSTANT_TYPE,fqnString);
+
+            if (property.isNullable() == false) {
+                gen.writeBooleanField(CONSTANT_NULLABLE, 
property.isNullable());
+            }
+
+            if (property.isUnicode() == false) {
+                gen.writeBooleanField(CONSTANT_IS_UNICODE, 
property.isUnicode());
+            }
+
+            if (property.getDefaultValue() != null) {
+                gen.writeStringField(CONSTANT_DEFAULT_VALUE, 
property.getDefaultValue());
+            }
+
+            if (property.getMaxLength() != null) {
+                gen.writeNumberField(CONSTANT_MAX_LENGTH, 
property.getMaxLength());
+            }
+
+            if (property.getPrecision() != null) {
+                gen.writeNumberField(CONSTANT_PRECISION, 
property.getPrecision());
+            }
+
+            if (property.getScale() != null) {
+                gen.writeNumberField(CONSTANT_SCALE, property.getScale());
+            }
+            gen.writeEndObject();
+        }
+        gen.writeEndObject();
+    }
+
+    private void appendKey(final JsonGenerator gen, final EdmEntityType 
entityType) throws IOException {
+        List<EdmKeyPropertyRef> keyPropertyRefs = 
entityType.getKeyPropertyRefs();
+        if (keyPropertyRefs != null && !keyPropertyRefs.isEmpty()) {
+            EdmEntityType baseType = entityType.getBaseType();
+            if (baseType != null && baseType.getKeyPropertyRefs() != null
+                    && !(baseType.getKeyPropertyRefs().isEmpty())) {
+                return;
+            }
+            gen.writeFieldName(CONSTANT_KEYS);
+            gen.writeStartArray();
+            for (EdmKeyPropertyRef keyRef : keyPropertyRefs) {
+                gen.writeStartObject();
+                gen.writeStringField(CONSTANT_NAME,keyRef.getName());
+                if (keyRef.getAlias() != null) {
+                    gen.writeStringField(CONSTANT_ALIAS, keyRef.getAlias());
+                }
+                gen.writeEndObject();
+            }
+
+            gen.writeEndArray();
+        }
+    }
+
+    private void appendEnumTypes(final JsonGenerator gen, final 
List<EdmEnumType> enumTypes)
+            throws IOException {
+        for (EdmEnumType enumType : enumTypes) {
+            gen.writeFieldName(getAliasedFullQualifiedName(enumType, false));
+            gen.writeStartObject();
+            gen.writeFieldName(CONSTANT_ENUM);
+            gen.writeStartArray();
+            for (String memberName : enumType.getMemberNames()){
+                gen.writeString(memberName);
+            }
+            gen.writeEndArray();
+            for (String memberName : enumType.getMemberNames()) {
+                
gen.writeStringField(memberName+CONSTANT_JSON_VALUE,enumType.getMember(memberName).getValue());
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private void appendReference(final JsonGenerator gen) throws IOException {
+        if (!serviceMetadata.getReferences().isEmpty()) {
+            gen.writeFieldName(CONSTANT_REFERENCES);
+            gen.writeStartObject();
+            for (final EdmxReference reference : 
serviceMetadata.getReferences()) {
+                gen.writeFieldName(reference.getUri().toASCIIString());
+                gen.writeStartObject();
+                List<EdmxReferenceInclude> includes = reference.getIncludes();
+                if(!includes.isEmpty()) {
+                    gen.writeFieldName(CONSTANT_INCLUDE);
+                    gen.writeStartObject();
+                    for (EdmxReferenceInclude include : includes) {
+                        gen.writeFieldName(include.getNamespace());
+                        gen.writeStartObject();
+                        if (include.getAlias() != null) {
+                            gen.writeStringField(CONSTANT_ALIAS, 
include.getAlias());
+                        }
+                        gen.writeEndObject();
+                    }
+                    gen.writeEndObject();
+                }
+                List<EdmxReferenceIncludeAnnotation> includeAnnotations = 
reference.getIncludeAnnotations();
+                if (!includeAnnotations.isEmpty()) {
+                    gen.writeFieldName(CONSTANT_INCLUDE_ANNOTATIONS);
+                    gen.writeStartArray();
+                    for (EdmxReferenceIncludeAnnotation includeAnnotation : 
includeAnnotations) {
+                        gen.writeStartObject();
+                        gen.writeStringField(CONSTANT_TERM_NAMESPACE, 
includeAnnotation.getTermNamespace());
+                        if (includeAnnotation.getQualifier() != null) {
+                            gen.writeStringField(CONSTANT_QUALIFIER, 
includeAnnotation.getQualifier());
+                        }
+                        if (includeAnnotation.getTargetNamespace() != null) {
+                            gen.writeStringField(CONSTANT_TARGET_NAMESPACE, 
includeAnnotation.getTargetNamespace());
+                        }
+                        gen.writeEndObject();
+                    }
+                    gen.writeEndArray();
+                }
+                gen.writeEndObject();
+            }
+            gen.writeEndObject();
+        }
+    }
+
+    private String getFullQualifiedName(final EdmType type, final boolean 
isCollection) {
+        final String name = 
type.getFullQualifiedName().getFullQualifiedNameAsString();
+        return isCollection ? "Collection(" + name + ")" : name;
+    }
+
+    private String getAliasedFullQualifiedName(final EdmType type, final 
boolean isCollection) {
+        FullQualifiedName fqn = type.getFullQualifiedName();
+        final String name;
+        if (namespaceToAlias.get(fqn.getNamespace()) != null) {
+            name = namespaceToAlias.get(fqn.getNamespace()) + "." + 
fqn.getName();
+        } else {
+            name = fqn.getFullQualifiedNameAsString();
+        }
+        return isCollection ? "Collection(" + name + ")" : name;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/99c201ea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index d5cd3c6..d1da05a 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -113,8 +113,29 @@ public class ODataJsonSerializer implements 
ODataSerializer {
 
   @Override
   public SerializerResult metadataDocument(final ServiceMetadata 
serviceMetadata) throws SerializerException {
-    throw new SerializerException("Metadata in JSON format not supported!",
-        SerializerException.MessageKeys.JSON_METADATA);
+    CircleStreamBuffer buffer;
+    JsonGenerator gen = null;
+    try {
+      buffer = new CircleStreamBuffer();
+      gen = new JsonFactory().createGenerator(buffer.getOutputStream())
+                  .setPrettyPrinter(new DefaultPrettyPrinter());
+      new 
MetadataDocumentJsonSerializer(serviceMetadata).writeMetadataDocument(gen);
+      gen.close();
+      return 
SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final IOException e) {
+      log.error(e.getMessage(), e);
+      throw new SerializerException("An I/O exception occurred.", e,
+                                           
SerializerException.MessageKeys.IO_EXCEPTION);
+    } finally {
+      if (gen != null) {
+        try {
+          gen.close();
+        } catch (IOException e) {
+          throw new SerializerException("An I/O exception occurred.", e,
+                                           
SerializerException.MessageKeys.IO_EXCEPTION);
+        }
+      }
+    }
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/99c201ea/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializerTest.java
 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializerTest.java
new file mode 100644
index 0000000..3c4a38e
--- /dev/null
+++ 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializerTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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 org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmSchema;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.edmx.EdmxReference;
+import org.apache.olingo.server.api.edmx.EdmxReferenceInclude;
+import org.apache.olingo.server.api.edmx.EdmxReferenceIncludeAnnotation;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class MetadataDocumentJsonSerializerTest {
+
+    private static ODataSerializer serializer;
+
+    @BeforeClass
+    public static void init() throws SerializerException {
+        serializer = OData.newInstance().createSerializer(ODataFormat.JSON);
+    }
+
+    @Test
+    public void writeMetadataWithEmptyMockedEdm() throws Exception {
+        final Edm edm = mock(Edm.class);
+        ServiceMetadata metadata = mock(ServiceMetadata.class);
+        when(metadata.getEdm()).thenReturn(edm);
+        String resultString = 
IOUtils.toString(serializer.metadataDocument(metadata).getContent());
+        String expectedString = "{\n" +
+                "  \"$schema\" : 
\"http://docs.oasis-open.org/odata/odata-json-csdl/v4.0/edm.json#\"\n"; +
+                "}";
+
+        assertEquals(expectedString, resultString);
+    }
+
+    @Test
+    public void writeMetadataWithEmptySchema() throws Exception {
+        EdmSchema schema = mock(EdmSchema.class);
+        when(schema.getNamespace()).thenReturn("MyNamespace");
+        Edm edm = mock(Edm.class);
+        when(edm.getSchemas()).thenReturn(Arrays.asList(schema));
+        ServiceMetadata serviceMetadata = mock(ServiceMetadata.class);
+        when(serviceMetadata.getEdm()).thenReturn(edm);
+        InputStream metadata = 
serializer.metadataDocument(serviceMetadata).getContent();
+        String resultString = IOUtils.toString(metadata);
+        String expectedString = "{\n" +
+                "  \"$schema\" : 
\"http://docs.oasis-open.org/odata/odata-json-csdl/v4.0/edm.json#\",\n"; +
+                "  \"definitions\" : { },\n" +
+                "  \"schemas\" : {\n" +
+                "    \"MyNamespace\" : {\n" +
+                "      \"alias\" : null\n" +
+                "    }\n" +
+                "  }\n" +
+                "}";
+        assertNotNull(metadata);
+        assertEquals(expectedString, resultString);
+    }
+
+    @Test
+    public void writeEdmxWithLocalTestEdm() throws Exception {
+        List<EdmxReference> edmxReferences = new ArrayList<EdmxReference>();
+        EdmxReference reference = new 
EdmxReference(URI.create("http://example.com";));
+        edmxReferences.add(reference);
+
+        EdmxReference referenceWithInclude = new EdmxReference(
+                
URI.create("http://localhost/odata/odata/v4.0/referenceWithInclude";));
+        EdmxReferenceInclude include = new 
EdmxReferenceInclude("Org.OData.Core.V1", "Core");
+        referenceWithInclude.addInclude(include);
+        edmxReferences.add(referenceWithInclude);
+
+        EdmxReference referenceWithTwoIncludes = new EdmxReference(
+                
URI.create("http://localhost/odata/odata/v4.0/referenceWithTwoIncludes";));
+        referenceWithTwoIncludes.addInclude(new 
EdmxReferenceInclude("Org.OData.Core.2", "Core2"));
+        referenceWithTwoIncludes.addInclude(new 
EdmxReferenceInclude("Org.OData.Core.3", "Core3"));
+        edmxReferences.add(referenceWithTwoIncludes);
+
+        EdmxReference referenceWithIncludeAnnos = new EdmxReference(
+                
URI.create("http://localhost/odata/odata/v4.0/referenceWithIncludeAnnos";));
+        referenceWithIncludeAnnos.addIncludeAnnotation(
+                new EdmxReferenceIncludeAnnotation("TermNs.2", "Q.2", 
"TargetNS.2"));
+        referenceWithIncludeAnnos.addIncludeAnnotation(
+                new EdmxReferenceIncludeAnnotation("TermNs.3", "Q.3", 
"TargetNS.3"));
+        edmxReferences.add(referenceWithIncludeAnnos);
+
+        EdmxReference referenceWithAll = new EdmxReference(
+                
URI.create("http://localhost/odata/odata/v4.0/referenceWithAll";));
+        referenceWithAll.addInclude(new 
EdmxReferenceInclude("ReferenceWithAll.1", "Core1"));
+        referenceWithAll.addInclude(new 
EdmxReferenceInclude("ReferenceWithAll.2", "Core2"));
+        referenceWithAll.addIncludeAnnotation(
+                new EdmxReferenceIncludeAnnotation("ReferenceWithAllTermNs.4", 
"Q.4", "TargetNS.4"));
+        referenceWithAll.addIncludeAnnotation(
+                new EdmxReferenceIncludeAnnotation("ReferenceWithAllTermNs.5", 
"Q.5", "TargetNS.5"));
+        edmxReferences.add(referenceWithAll);
+
+        EdmxReference referenceWithAllAndNull = new EdmxReference(
+                
URI.create("http://localhost/odata/odata/v4.0/referenceWithAllAndNull";));
+        referenceWithAllAndNull.addInclude(new 
EdmxReferenceInclude("referenceWithAllAndNull.1"));
+        referenceWithAllAndNull.addInclude(new 
EdmxReferenceInclude("referenceWithAllAndNull.2", null));
+        referenceWithAllAndNull.addIncludeAnnotation(
+                new 
EdmxReferenceIncludeAnnotation("ReferenceWithAllTermNs.4"));
+        referenceWithAllAndNull.addIncludeAnnotation(
+                new 
EdmxReferenceIncludeAnnotation("ReferenceWithAllTermAndNullNs.5", "Q.5", null));
+        referenceWithAllAndNull.addIncludeAnnotation(
+                new 
EdmxReferenceIncludeAnnotation("ReferenceWithAllTermAndNullNs.6", null, 
"TargetNS"));
+        referenceWithAllAndNull.addIncludeAnnotation(
+                new 
EdmxReferenceIncludeAnnotation("ReferenceWithAllTermAndNullNs.7", null, null));
+        edmxReferences.add(referenceWithAllAndNull);
+
+        ServiceMetadata serviceMetadata = mock(ServiceMetadata.class);
+        final Edm edm = mock(Edm.class);
+        when(serviceMetadata.getEdm()).thenReturn(edm);
+        when(serviceMetadata.getReferences()).thenReturn(edmxReferences);
+
+        InputStream metadata = 
serializer.metadataDocument(serviceMetadata).getContent();
+        assertNotNull(metadata);
+        final String resultString = IOUtils.toString(metadata);
+        String expectedString = "{\n" +
+                "  \"$schema\" : 
\"http://docs.oasis-open.org/odata/odata-json-csdl/v4.0/edm.json#\",\n"; +
+                "  \"references\" : {\n" +
+                "    \"http://example.com\"; : { },\n" +
+                "    
\"http://localhost/odata/odata/v4.0/referenceWithInclude\"; : {\n" +
+                "      \"includes\" : {\n" +
+                "        \"Org.OData.Core.V1\" : {\n" +
+                "          \"alias\" : \"Core\"\n" +
+                "        }\n" +
+                "      }\n" +
+                "    },\n" +
+                "    
\"http://localhost/odata/odata/v4.0/referenceWithTwoIncludes\"; : {\n" +
+                "      \"includes\" : {\n" +
+                "        \"Org.OData.Core.2\" : {\n" +
+                "          \"alias\" : \"Core2\"\n" +
+                "        },\n" +
+                "        \"Org.OData.Core.3\" : {\n" +
+                "          \"alias\" : \"Core3\"\n" +
+                "        }\n" +
+                "      }\n" +
+                "    },\n" +
+                "    
\"http://localhost/odata/odata/v4.0/referenceWithIncludeAnnos\"; : {\n" +
+                "      \"includeAnnotations\" : [ {\n" +
+                "        \"termNamespace\" : \"TermNs.2\",\n" +
+                "        \"qualifier\" : \"Q.2\",\n" +
+                "        \"targetNamespace\" : \"TargetNS.2\"\n" +
+                "      }, {\n" +
+                "        \"termNamespace\" : \"TermNs.3\",\n" +
+                "        \"qualifier\" : \"Q.3\",\n" +
+                "        \"targetNamespace\" : \"TargetNS.3\"\n" +
+                "      } ]\n" +
+                "    },\n" +
+                "    \"http://localhost/odata/odata/v4.0/referenceWithAll\"; : 
{\n" +
+                "      \"includes\" : {\n" +
+                "        \"ReferenceWithAll.1\" : {\n" +
+                "          \"alias\" : \"Core1\"\n" +
+                "        },\n" +
+                "        \"ReferenceWithAll.2\" : {\n" +
+                "          \"alias\" : \"Core2\"\n" +
+                "        }\n" +
+                "      },\n" +
+                "      \"includeAnnotations\" : [ {\n" +
+                "        \"termNamespace\" : \"ReferenceWithAllTermNs.4\",\n" +
+                "        \"qualifier\" : \"Q.4\",\n" +
+                "        \"targetNamespace\" : \"TargetNS.4\"\n" +
+                "      }, {\n" +
+                "        \"termNamespace\" : \"ReferenceWithAllTermNs.5\",\n" +
+                "        \"qualifier\" : \"Q.5\",\n" +
+                "        \"targetNamespace\" : \"TargetNS.5\"\n" +
+                "      } ]\n" +
+                "    },\n" +
+                "    
\"http://localhost/odata/odata/v4.0/referenceWithAllAndNull\"; : {\n" +
+                "      \"includes\" : {\n" +
+                "        \"referenceWithAllAndNull.1\" : { },\n" +
+                "        \"referenceWithAllAndNull.2\" : { }\n" +
+                "      },\n" +
+                "      \"includeAnnotations\" : [ {\n" +
+                "        \"termNamespace\" : \"ReferenceWithAllTermNs.4\"\n" +
+                "      }, {\n" +
+                "        \"termNamespace\" : 
\"ReferenceWithAllTermAndNullNs.5\",\n" +
+                "        \"qualifier\" : \"Q.5\"\n" +
+                "      }, {\n" +
+                "        \"termNamespace\" : 
\"ReferenceWithAllTermAndNullNs.6\",\n" +
+                "        \"targetNamespace\" : \"TargetNS\"\n" +
+                "      }, {\n" +
+                "        \"termNamespace\" : 
\"ReferenceWithAllTermAndNullNs.7\"\n" +
+                "      } ]\n" +
+                "    }\n" +
+                "  }\n" +
+                "}";
+
+        assertEquals(expectedString, resultString);
+    }
+}

Reply via email to