[OLINGO-317] Added complex type handling
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/71bdaed3 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/71bdaed3 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/71bdaed3 Branch: refs/heads/master Commit: 71bdaed3003a6c903888a3465c9a338703596138 Parents: 839e960 Author: Michael Bolz <[email protected]> Authored: Tue Jul 1 13:11:36 2014 +0200 Committer: Michael Bolz <[email protected]> Committed: Tue Jul 1 16:21:42 2014 +0200 ---------------------------------------------------------------------- .../serializer/json/ODataJsonSerializer.java | 107 +++++++++---------- .../json/ODataJsonSerializerTest.java | 61 ++++++----- .../tecsvc/processor/SampleJsonProcessor.java | 82 +++++++++++--- 3 files changed, 151 insertions(+), 99 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/71bdaed3/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 8cb5f0c..c3fe720 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 @@ -18,36 +18,24 @@ */ package org.apache.olingo.server.core.serializer.json; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.util.List; - +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import org.apache.olingo.commons.api.Constants; import org.apache.olingo.commons.api.ODataRuntimeException; -import org.apache.olingo.commons.api.data.ContextURL; -import org.apache.olingo.commons.api.data.Entity; -import org.apache.olingo.commons.api.data.EntitySet; -import org.apache.olingo.commons.api.data.LinkedComplexValue; -import org.apache.olingo.commons.api.data.Property; -import org.apache.olingo.commons.api.edm.Edm; -import org.apache.olingo.commons.api.edm.EdmComplexType; -import org.apache.olingo.commons.api.edm.EdmEntitySet; -import org.apache.olingo.commons.api.edm.EdmEntityType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; -import org.apache.olingo.commons.api.edm.EdmProperty; +import org.apache.olingo.commons.api.data.*; +import org.apache.olingo.commons.api.edm.*; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.util.List; public class ODataJsonSerializer implements ODataSerializer { @@ -148,38 +136,14 @@ public class ODataJsonSerializer implements ODataSerializer { } } else { if (edmProperty.isPrimitive()) { - if (property.isPrimitive()) { - writePrimitiveValue(edmProperty, property.asPrimitive(), json); - } else if (property.isGeospatial()) { - throw new ODataRuntimeException("Property type not yet supported!"); - } else if (property.isEnum()) { - json.writeString(property.asEnum().toString()); - } else { - throw new ODataRuntimeException("Inconsistent property type!"); - } + handlePrimitive(edmProperty, property, json); } else if (edmProperty.isCollection()) { - json.writeStartArray(); - for (Object value : property.asCollection()) { - switch (property.getValueType()) { - case COLLECTION_PRIMITIVE: - writePrimitiveValue(edmProperty, value, json); - break; - case COLLECTION_GEOSPATIAL: - throw new ODataRuntimeException("Property type not yet supported!"); - case COLLECTION_ENUM: - json.writeString(value.toString()); - break; - case COLLECTION_LINKED_COMPLEX: - writeLinkedComplexValue(edmProperty, (LinkedComplexValue) value, json); - break; - default: - throw new ODataRuntimeException("Property type not yet supported!"); - } - } - json.writeEndArray(); + handleCollection(edmProperty, property, json); } else { if (property.isLinkedComplex()) { - writeLinkedComplexValue(edmProperty, property.asLinkedComplex(), json); + writeComplexValue(edmProperty, property.asLinkedComplex().getValue(), json); + } else if(property.isComplex()) { + writeComplexValue(edmProperty, property.asComplex(), json); } else { throw new ODataRuntimeException("Property type not yet supported!"); } @@ -187,6 +151,42 @@ public class ODataJsonSerializer implements ODataSerializer { } } + private void handleCollection(EdmProperty edmProperty, Property property, JsonGenerator json) + throws IOException, EdmPrimitiveTypeException { + json.writeStartArray(); + for (Object value : property.asCollection()) { + switch (property.getValueType()) { + case COLLECTION_PRIMITIVE: + writePrimitiveValue(edmProperty, value, json); + break; + case COLLECTION_GEOSPATIAL: + throw new ODataRuntimeException("Property type not yet supported!"); + case COLLECTION_ENUM: + json.writeString(value.toString()); + break; + case COLLECTION_LINKED_COMPLEX: + writeComplexValue(edmProperty, ((LinkedComplexValue) value).getValue(), json); + break; + default: + throw new ODataRuntimeException("Property type not yet supported!"); + } + } + json.writeEndArray(); + } + + private void handlePrimitive(EdmProperty edmProperty, Property property, JsonGenerator json) + throws EdmPrimitiveTypeException, IOException { + if (property.isPrimitive()) { + writePrimitiveValue(edmProperty, property.asPrimitive(), json); + } else if (property.isGeospatial()) { + throw new ODataRuntimeException("Property type not yet supported!"); + } else if (property.isEnum()) { + json.writeString(property.asEnum().toString()); + } else { + throw new ODataRuntimeException("Inconsistent property type!"); + } + } + protected void writePrimitiveValue(final EdmProperty edmProperty, final Object primitiveValue, JsonGenerator json) throws EdmPrimitiveTypeException, IOException { final EdmPrimitiveType type = (EdmPrimitiveType) edmProperty.getType(); @@ -209,10 +209,9 @@ public class ODataJsonSerializer implements ODataSerializer { } } - private void writeLinkedComplexValue(final EdmProperty edmProperty, final LinkedComplexValue linkedComplexValue, - JsonGenerator json) throws IOException, EdmPrimitiveTypeException { + private void writeComplexValue(final EdmProperty edmProperty, final List<Property> properties, + JsonGenerator json) throws IOException, EdmPrimitiveTypeException { final EdmComplexType type = (EdmComplexType) edmProperty.getType(); - final List<Property> properties = linkedComplexValue.getValue(); json.writeStartObject(); for (final String propertyName : type.getPropertyNames()) { final Property property = findProperty(propertyName, properties); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/71bdaed3/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index 1008c5f..e7563b0 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -18,15 +18,10 @@ */ package org.apache.olingo.server.core.serializer.json; -import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.ValueType; -import org.apache.olingo.commons.api.edm.EdmEntitySet; -import org.apache.olingo.commons.api.edm.EdmEntityType; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; -import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.api.edm.EdmType; +import org.apache.olingo.commons.api.edm.*; import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; import org.apache.olingo.commons.core.data.EntityImpl; import org.apache.olingo.commons.core.data.EntitySetImpl; @@ -36,17 +31,11 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -65,11 +54,14 @@ public class ODataJsonSerializerTest { public static class TecComplexProperty implements TechProperty { - final String name; final String typeName; - public TecComplexProperty(String typeName, String name) { - this.name = name; + final String name; + final List<EdmProperty> properties; + + public TecComplexProperty(String typeName, String name, List<EdmProperty> propertyNames) { this.typeName = typeName; + this.name = name; + this.properties = new ArrayList<EdmProperty>(propertyNames); } @Override public String getName() { @@ -83,6 +75,9 @@ public class ODataJsonSerializerTest { public EdmPrimitiveTypeKind getType() { return null; } + public List<EdmProperty> getProperties() { + return properties; + } } enum TecSimpleProperty implements TechProperty { @@ -181,7 +176,7 @@ public class ODataJsonSerializerTest { Mockito.when(edmETCompAllPrim.getName()).thenReturn(ETCompAllPrim); List<EdmProperty> capProperties = Arrays.asList( mockProperty(TecSimpleProperty.Int16, false), - mockProperty(new TecComplexProperty(CTAllPrim_Type, CTAllPrim), false, true) + mockProperty(new TecComplexProperty(CTAllPrim_Type, CTAllPrim, properties), false) ); List<String> capPropertyNames = new ArrayList<String>(); @@ -193,22 +188,27 @@ public class ODataJsonSerializerTest { } private EdmProperty mockProperty(TechProperty name) { - return mockProperty(name, true, false); + return mockProperty(name, true); } - private EdmProperty mockProperty(TechProperty name, boolean nullable) { - return mockProperty(name, nullable, false); - } - - private EdmProperty mockProperty(TechProperty tecProperty, boolean nullable, boolean complex) { + private EdmProperty mockProperty(TechProperty tecProperty, boolean nullable) { EdmProperty edmElement = Mockito.mock(EdmProperty.class); Mockito.when(edmElement.getName()).thenReturn(tecProperty.getName()); - if(complex) { + if (tecProperty instanceof TecComplexProperty) { + TecComplexProperty complexProperty = (TecComplexProperty) tecProperty; Mockito.when(edmElement.isPrimitive()).thenReturn(false); - EdmType type = Mockito.mock(EdmType.class); + EdmComplexType type = Mockito.mock(EdmComplexType.class); Mockito.when(type.getKind()).thenReturn(EdmTypeKind.COMPLEX); Mockito.when(type.getName()).thenReturn(tecProperty.getTypeName()); Mockito.when(edmElement.getType()).thenReturn(type); + + List<String> propertyNames = new ArrayList<String>(); + List<EdmProperty> properties = complexProperty.getProperties(); + for (EdmProperty property : properties) { + propertyNames.add(property.getName()); + Mockito.when(type.getProperty(property.getName())).thenReturn(property); + } + Mockito.when(type.getPropertyNames()).thenReturn(propertyNames); } else { Mockito.when(edmElement.isPrimitive()).thenReturn(true); // TODO: set default values @@ -287,20 +287,23 @@ public class ODataJsonSerializerTest { Assert.assertEquals(100, count); } - @Test(expected = ODataRuntimeException.class) + @Test public void entityETCompAllPrim() throws Exception { Entity complexCtAllPrim = createETAllPrim(); Entity entity = new EntityImpl(); entity.addProperty(new PropertyImpl("Edm.Int16", TecSimpleProperty.Int16.name, ValueType.PRIMITIVE, 4711)); - entity.addProperty(createProperty(new TecComplexProperty(CTAllPrim_Type, CTAllPrim), - ValueType.COMPLEX, complexCtAllPrim)); + entity.addProperty(createProperty( + new TecComplexProperty(CTAllPrim_Type, CTAllPrim, Collections.<EdmProperty>emptyList()), + ValueType.COMPLEX, complexCtAllPrim.getProperties())); InputStream result = serializer.entity(edmETCompAllPrim, entity, contextUrl); String resultString = streamToString(result); String expectedResult = "{" + "\"@odata.context\":\"http://localhost:8080/test.svc\"," + "\"PropertyInt16\":4711," + + "\"CTAllPrim\":{" + + "\"PropertyInt16\":4711," + "\"PropertyString\":\"StringValue\"," + "\"PropertyBoolean\":true," + "\"PropertyByte\":19," + @@ -316,7 +319,7 @@ public class ODataJsonSerializerTest { "\"PropertyDuration\":\"P16148383DT8H0S\"," + "\"PropertyGuid\":\"0000aaaa-00bb-00cc-00dd-000000ffffff\"," + "\"PropertyTimeOfDay\":\"10:12:00\"" + - "}"; + "}}"; Assert.assertEquals(expectedResult, resultString); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/71bdaed3/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/SampleJsonProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/SampleJsonProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/SampleJsonProcessor.java index 8cf00e9..cf40489 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/SampleJsonProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/SampleJsonProcessor.java @@ -19,6 +19,8 @@ package org.apache.olingo.server.tecsvc.processor; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import org.apache.olingo.commons.api.data.ContextURL; @@ -27,7 +29,10 @@ import org.apache.olingo.commons.api.data.EntitySet; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.ValueType; import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.http.HttpStatusCode; @@ -41,6 +46,7 @@ import org.apache.olingo.server.api.processor.EntityProcessor; import org.apache.olingo.server.api.processor.EntitySetProcessor; import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,16 +66,13 @@ public class SampleJsonProcessor implements EntitySetProcessor, EntityProcessor public void readEntitySet(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) { long time = System.nanoTime(); - EntitySet entitySet = createEntitySet(); - LOG.info((System.nanoTime() - time) / 1000 + " microseconds"); time = System.nanoTime(); ODataSerializer serializer = odata.createSerializer(ODataFormat.JSON); - response.setContent(serializer.entitySet( - edm.getEntityContainer(new FullQualifiedName("com.sap.odata.test1", "Container")) - .getEntitySet("ESAllPrim"), - entitySet, - ContextURL.getInstance(URI.create("dummyContextURL")))); + EdmEntitySet edmEntitySet = getEntitySet(uriInfo); + EntitySet entitySet = createEntitySet(edmEntitySet.getEntityType()); + response.setContent(serializer.entitySet(edmEntitySet, entitySet, + getContextUrl(request, edmEntitySet.getEntityType()))); LOG.info("Finished in " + (System.nanoTime() - time) / 1000 + " microseconds"); response.setStatusCode(HttpStatusCode.OK.getStatusCode()); @@ -79,23 +82,45 @@ public class SampleJsonProcessor implements EntitySetProcessor, EntityProcessor @Override public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, String format) { long time = System.nanoTime(); - Entity entity = createEntity(); LOG.info((System.nanoTime() - time) / 1000 + " microseconds"); time = System.nanoTime(); ODataSerializer serializer = odata.createSerializer(ODataFormat.JSON); - response.setContent(serializer.entity( - edm.getEntityContainer(new FullQualifiedName("com.sap.odata.test1", "Container")) - .getEntitySet("ESAllPrim").getEntityType(), - entity, - ContextURL.getInstance(URI.create("dummyContextURL")))); + EdmEntityType entityType = getEntityType(uriInfo); + Entity entity = createEntity(entityType); + + response.setContent(serializer.entity(entityType, entity, + getContextUrl(request, entityType))); LOG.info("Finished in " + (System.nanoTime() - time) / 1000 + " microseconds"); response.setStatusCode(HttpStatusCode.OK.getStatusCode()); response.setHeader("Content-Type", ContentType.APPLICATION_JSON.toContentTypeString()); } - protected Entity createEntity() { + private ContextURL getContextUrl(ODataRequest request, EdmEntityType entityType) { + return ContextURL.getInstance(URI.create(request.getRawBaseUri() + "/" + entityType.getName())); + } + + public EdmEntityType getEntityType(UriInfo uriInfo) { + return getEntitySet(uriInfo).getEntityType(); + } + + public EdmEntitySet getEntitySet(UriInfo uriInfo) { + List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); + if(resourcePaths.isEmpty()) { + throw new RuntimeException("Invalid resource path."); + } + String entitySetName = resourcePaths.get(resourcePaths.size()-1).toString(); + return edm.getEntityContainer(new FullQualifiedName("com.sap.odata.test1", "Container")) + .getEntitySet(entitySetName); + } + + protected Entity createEntity(EdmEntityType entityType) { + boolean complex = (entityType.getName().contains("Comp")); + return createEntity(complex); + } + + protected Entity createEntity(boolean complex) { Entity entity = new EntityImpl(); Property property = new PropertyImpl(); property.setName("PropertyString"); @@ -109,15 +134,40 @@ public class SampleJsonProcessor implements EntitySetProcessor, EntityProcessor propertyGuid.setName("PropertyGuid"); propertyGuid.setValue(ValueType.PRIMITIVE, UUID.randomUUID()); entity.getProperties().add(propertyGuid); + + if(complex) { + entity.addProperty(createComplexProperty()); + } + return entity; } - protected EntitySet createEntitySet() { + protected Property createComplexProperty() { + List<Property> properties = new ArrayList<Property>(); + Property property = new PropertyImpl(); + property.setName("PropertyString"); + property.setValue(ValueType.PRIMITIVE, "dummyValue"); + properties.add(property); + Property propertyInt = new PropertyImpl(); + propertyInt.setName("PropertyInt16"); + propertyInt.setValue(ValueType.PRIMITIVE, 42); + properties.add(propertyInt); + Property propertyGuid = new PropertyImpl(); + propertyGuid.setName("PropertyGuid"); + propertyGuid.setValue(ValueType.PRIMITIVE, UUID.randomUUID()); + properties.add(propertyGuid); + + return new PropertyImpl("com.sap.odata.test1.ETCompAllPrim", "PropertyComplex", ValueType.COMPLEX, + properties); + } + + + protected EntitySet createEntitySet(EdmEntityType edmEntityType) { EntitySet entitySet = new EntitySetImpl(); entitySet.setCount(4242); entitySet.setNext(URI.create("nextLinkURI")); for (int i = 0; i < 1000; i++) { - entitySet.getEntities().add(createEntity()); + entitySet.getEntities().add(createEntity(edmEntityType)); } return entitySet; }
