Repository: olingo-odata4 Updated Branches: refs/heads/OLINGO-811 [created] 5dee97f76
OLINGO-811: implementing the =nav/ and =nav/ Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/5dee97f7 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/5dee97f7 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/5dee97f7 Branch: refs/heads/OLINGO-811 Commit: 5dee97f7628c793e2bbcd3f89d8d5c52e07639ea Parents: 3786699 Author: Ramesh Reddy <[email protected]> Authored: Tue Mar 22 18:22:27 2016 -0500 Committer: Ramesh Reddy <[email protected]> Committed: Tue Mar 22 18:24:43 2016 -0500 ---------------------------------------------------------------------- .../ExpandWithSystemQueryOptionsITCase.java | 95 ++++++++++++++++++-- .../olingo/client/api/uri/URIBuilder.java | 15 ++++ .../core/serialization/JsonDeserializer.java | 14 +++ .../core/serialization/ODataBinderImpl.java | 59 +++++++++++- .../olingo/client/core/uri/URIBuilderImpl.java | 11 ++- .../olingo/client/core/uri/URIBuilderTest.java | 15 ++++ .../server/api/uri/queryoption/ExpandItem.java | 8 +- .../serializer/json/ODataJsonSerializer.java | 65 +++++++++++--- .../core/serializer/xml/ODataXmlSerializer.java | 47 +++++++--- .../server/core/uri/parser/ExpandParser.java | 2 + .../core/uri/queryoption/ExpandItemImpl.java | 10 +++ .../processor/TechnicalEntityProcessor.java | 4 +- .../ExpandSystemQueryOptionHandler.java | 41 ++++----- 13 files changed, 317 insertions(+), 69 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java index 0f3743b..5f44351 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java @@ -37,7 +37,6 @@ import org.apache.olingo.client.api.domain.ClientEntity; import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.uri.QueryOption; import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.junit.Ignore; import org.junit.Test; public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCase { @@ -209,8 +208,7 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas } @Test - @Ignore("Server do not support navigation property count annotations") - public void count() { + public void count() throws Exception{ final ODataClient client = getEdmEnabledClient(); Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class); options.put(QueryOption.SELECT, "PropertyInt16"); @@ -220,6 +218,7 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions( NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options).addQueryOption(QueryOption.SELECT, "PropertyInt16,PropertyString").build(); + final ODataRetrieveResponse<ClientEntitySet> response = client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute(); @@ -232,13 +231,13 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas final ClientEntitySet entitySet = entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); - if (propInt16.equals(1) && propString.equals("1")) { - assertEquals(Integer.valueOf(2), entitySet.getCount()); - } else if (propInt16.equals(1) && propString.equals("2")) { + if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) { assertEquals(Integer.valueOf(2), entitySet.getCount()); - } else if (propInt16.equals(2) && propString.equals("1")) { - assertEquals(Integer.valueOf(2), entitySet.getCount()); - } else if (propInt16.equals(3) && propString.equals("1")) { + } else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) { + assertEquals(Integer.valueOf(1), entitySet.getCount()); + } else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) { + assertEquals(Integer.valueOf(1), entitySet.getCount()); + } else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) { assertEquals(Integer.valueOf(0), entitySet.getCount()); } else { fail(); @@ -247,6 +246,84 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas } @Test + public void countOnly() throws Exception { + final ODataClient client = getEdmEnabledClient(); + Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class); + + final URI uri = + client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions( + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, false, true, options).addQueryOption(QueryOption.SELECT, + "PropertyInt16,PropertyString").build(); + final ODataRetrieveResponse<ClientEntitySet> response = + client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute(); + + final List<ClientEntity> entities = response.getBody().getEntities(); + assertEquals(4, entities.size()); + + for (final ClientEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue(); + final ClientEntitySet entitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) { + assertEquals(Integer.valueOf(2), entitySet.getCount()); + } else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) { + assertEquals(Integer.valueOf(1), entitySet.getCount()); + } else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) { + assertEquals(Integer.valueOf(1), entitySet.getCount()); + } else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) { + assertEquals(Integer.valueOf(0), entitySet.getCount()); + } else { + fail(); + } + } + } + + @Test + public void reference() throws Exception { + final ODataClient client = getEdmEnabledClient(); + Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class); + + final URI uri = + client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions( + NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, true, false, options).addQueryOption(QueryOption.SELECT, + "PropertyInt16,PropertyString").build(); + final ODataRetrieveResponse<ClientEntitySet> response = + client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute(); + + final List<ClientEntity> entities = response.getBody().getEntities(); + assertEquals(4, entities.size()); + + for (final ClientEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue(); + final ClientEntitySet entitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) { + assertEquals(2, entitySet.getEntities().size()); + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", + entitySet.getEntities().get(0).getId().toString()); + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", + entitySet.getEntities().get(1).getId().toString()); + } else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) { + assertEquals(1, entitySet.getEntities().size()); + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", + entitySet.getEntities().get(0).getId().toString()); + } else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) { + assertEquals(1, entitySet.getEntities().size()); + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", + entitySet.getEntities().get(0).getId().toString()); + } else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) { + assertEquals(0, entitySet.getEntities().size()); + } else { + fail(); + } + } + } + + @Test public void singleEntityWithExpand() { /* A single entity request will be dispatched to a different processor method than entity set request */ final ODataClient client = getEdmEnabledClient(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/URIBuilder.java ---------------------------------------------------------------------- diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/URIBuilder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/URIBuilder.java index 7cf0177..b73c68f 100644 --- a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/URIBuilder.java +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/URIBuilder.java @@ -351,6 +351,21 @@ public interface URIBuilder { URIBuilder expandWithOptions(String expandItem, Map<QueryOption, Object> options); /** + * The set of expanded entities can be refined through the application of expand options, expressed as a + * semicolon-separated list of system query options, enclosed in parentheses, see [OData-URL]. + * + * @param expandItem item to be expanded. + * @param pathRef include the /$ref at the end of the $expand item's path;if true pathCount MUST be false + * @param pathCount include /$count at the end of the $expand item's path;if true pathRef MUST be false + * @param options System query options. Allowed query options are: $filter, $select, $orderby, $skip, $top, $count, + * $search, $expand, and $levels. + * @return current URIBuilder instance. + * @see org.apache.olingo.client.api.uri.QueryOption#EXPAND + */ + URIBuilder expandWithOptions(String expandItem, boolean pathRef, + boolean pathCount, Map<QueryOption, Object> options); + + /** * Properties of related entities can be specified by including the $select query option within the $expand. * <br /> * <tt>http://host/service/Products?$expand=Category($select=Name)</tt> http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java index fbfb0f5..3691d8b 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java @@ -94,6 +94,12 @@ public class JsonDeserializer implements ODataDeserializer { final ObjectCodec codec, final Link link) throws IOException { final String entityNamePrefix = name.substring(0, name.indexOf(suffix)); + + Integer count = null; + if (tree.hasNonNull(entityNamePrefix+Constants.JSON_COUNT)) { + count = tree.get(entityNamePrefix+Constants.JSON_COUNT).asInt(); + } + if (tree.has(entityNamePrefix)) { final JsonNode inline = tree.path(entityNamePrefix); JsonEntityDeserializer entityDeserializer = new JsonEntityDeserializer(serverMode); @@ -106,6 +112,9 @@ public class JsonDeserializer implements ODataDeserializer { link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE); final EntityCollection entitySet = new EntityCollection(); + if (count != null) { + entitySet.setCount(count); + } for (final Iterator<JsonNode> entries = inline.elements(); entries.hasNext();) { entitySet.getEntities().add(entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload()); } @@ -247,6 +256,11 @@ public class JsonDeserializer implements ODataDeserializer { } } else if (type == null && field.getKey().endsWith(getJSONAnnotation(Constants.JSON_TYPE))) { type = field.getValue().asText(); + } else if (field.getKey().endsWith(getJSONAnnotation(Constants.JSON_COUNT))) { + final Property property = new Property(); + property.setName(field.getKey()); + property.setValue(ValueType.PRIMITIVE, Integer.parseInt(field.getValue().asText())); + properties.add(property); } else if (annotation == null && customAnnotation.matches() && !"odata".equals(customAnnotation.group(2))) { annotation = new Annotation(); annotation.setTerm(customAnnotation.group(2) + "." + customAnnotation.group(3)); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java index f9f29e7..a67de48 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java @@ -23,7 +23,9 @@ import java.net.URI; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.olingo.client.api.EdmEnabledODataClient; @@ -565,23 +567,50 @@ public class ODataBinderImpl implements ODataBinder { return type; } - private ClientLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName) { + private ClientLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName, + final Integer count) { if (property.isCollection()) { EntityCollection inlineEntitySet = new EntityCollection(); for (final Object inlined : property.asCollection()) { Entity inlineEntity = new Entity(); inlineEntity.setType(propertyTypeName); inlineEntity.getProperties().addAll(((ComplexValue) inlined).getValue()); + copyAnnotations(inlineEntity, (ComplexValue) inlined); inlineEntitySet.getEntities().add(inlineEntity); } + if (count != null) { + inlineEntitySet.setCount(count); + } return createODataInlineEntitySet(inlineEntitySet, null, property.getName(), null); } else { Entity inlineEntity = new Entity(); inlineEntity.setType(propertyTypeName); inlineEntity.getProperties().addAll(property.asComplex().getValue()); + copyAnnotations(inlineEntity, property.asComplex()); return createODataInlineEntity(inlineEntity, null, property.getName(), null); } } + + private void copyAnnotations(Entity inlineEntity, ComplexValue complex) { + for (Annotation annotation:complex.getAnnotations()) { + if (annotation.getTerm().equals(Constants.JSON_TYPE.substring(1))){ + inlineEntity.setType((String)annotation.asPrimitive()); + } else if (annotation.getTerm().equals(Constants.JSON_ID.substring(1))){ + inlineEntity.setId(URI.create((String)annotation.asPrimitive())); + } else if (annotation.getTerm().equals(Constants.JSON_ETAG.substring(1))){ + inlineEntity.setETag((String)annotation.asPrimitive()); + } + } + } + + private ClientLink createLinkFromEmptyNavigationProperty(final String propertyName, + final Integer count) { + EntityCollection inlineEntitySet = new EntityCollection(); + if (count != null) { + inlineEntitySet.setCount(count); + } + return createODataInlineEntitySet(inlineEntitySet, null, propertyName, null); + } @Override public ClientEntity getODataEntity(final ResWrap<Entity> resource) { @@ -650,21 +679,45 @@ public class ODataBinderImpl implements ODataBinder { entity.setMediaETag(resource.getPayload().getMediaETag()); } + Map<String, Integer> countMap = new HashMap<String, Integer>(); for (final Property property : resource.getPayload().getProperties()) { EdmType propertyType = null; if (edmType instanceof EdmEntityType) { - final EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName()); + EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName()); if (edmProperty != null) { propertyType = edmProperty.getType(); if (edmProperty instanceof EdmNavigationProperty && !property.isNull()) { final String propertyTypeName = propertyType.getFullQualifiedName().getFullQualifiedNameAsString(); - entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName)); + entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName, + countMap.remove(property.getName()))); continue; } + } else { + int idx = property.getName().indexOf(Constants.JSON_COUNT); + if (idx != -1) { + String navigationName = property.getName().substring(0, idx); + edmProperty = ((EdmEntityType) edmType).getProperty(navigationName); + if (edmProperty != null) { + if (edmProperty instanceof EdmNavigationProperty) { + ClientLink link = entity.getNavigationLink(navigationName); + if (link == null) { + countMap.put(navigationName, (Integer)property.getValue()); + } else { + link.asInlineEntitySet().getEntitySet().setCount((Integer)property.getValue()); + } + } + } + } } } add(entity, getODataProperty(propertyType, property)); } + + if (!countMap.isEmpty()) { + for (String name:countMap.keySet()) { + entity.addLink(createLinkFromEmptyNavigationProperty(name, countMap.get(name))); + } + } entity.setId(resource.getPayload().getId()); odataAnnotations(resource.getPayload(), entity); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIBuilderImpl.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIBuilderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIBuilderImpl.java index b29aca6..dad21ad 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIBuilderImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/URIBuilderImpl.java @@ -446,13 +446,20 @@ public class URIBuilderImpl implements URIBuilder { @Override public URIBuilder expandWithOptions(final String expandItem, final Map<QueryOption, Object> options) { + return expandWithOptions(expandItem, false, false, options); + } + + @Override + public URIBuilder expandWithOptions(String expandItem, boolean pathRef, + boolean pathCount, Map<QueryOption, Object> options) { final Map<String, Object> _options = new LinkedHashMap<String, Object>(); for (Map.Entry<QueryOption, Object> entry : options.entrySet()) { _options.put("$" + entry.getKey().toString(), entry.getValue()); } - return expand(expandItem + buildMultiKeySegment(_options, false, ';')); + String path = pathRef?"/$ref":pathCount?"/$count":StringUtils.EMPTY; + return expand(expandItem + buildMultiKeySegment(_options, false, ';')+path); } - + @Override public URIBuilder expandWithSelect(final String expandItem, final String... selectItems) { return expand(expandItem + "($select=" + StringUtils.join(selectItems, ",") + ")"); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/client-core/src/test/java/org/apache/olingo/client/core/uri/URIBuilderTest.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/uri/URIBuilderTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/uri/URIBuilderTest.java index 5f9cc8b..ff0be56 100644 --- a/lib/client-core/src/test/java/org/apache/olingo/client/core/uri/URIBuilderTest.java +++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/uri/URIBuilderTest.java @@ -87,6 +87,21 @@ public class URIBuilderTest extends AbstractTest { assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)"). addParameter("$expand", "ProductDetails($expand=ProductInfo;$select=Price),Orders,Customers").build(), uri); } + + @Test + public void expandWithOptionsCount() throws URISyntaxException { + final URI uri = client.newURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(5). + expandWithOptions("ProductDetails", false, true, new LinkedHashMap<QueryOption, Object>() { + private static final long serialVersionUID = 3109256773218160485L; + { + put(QueryOption.EXPAND, "ProductInfo"); + put(QueryOption.SELECT, "Price"); + } + }).expand("Orders", "Customers").build(); + assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)"). + addParameter("$expand", "ProductDetails($expand=ProductInfo;$select=Price)/$count,Orders,Customers") + .build(), uri); + } public void expandWithLevels() throws URISyntaxException { final URI uri = client.newURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(1). http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java index 6954831..4e71157 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java @@ -85,11 +85,17 @@ public interface ExpandItem { /** * @return A $ref is used within $expand. - * For example: ...?$expand=$ref + * For example: ...?$expand=navigation/$ref */ boolean isRef(); /** + * @return A $count is used within $expand. + * For example: ...?$expand=navigation/$count + */ + boolean hasCountPath(); + + /** * @return Before resource path segments which should be expanded a type filter may be used. * For example: ...persons?$expand=namespace.managertype/team */ http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/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 cb60b4e..904bc9a 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 @@ -26,10 +26,10 @@ import java.util.List; import java.util.Set; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.AbstractEntityCollection; import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.Entity; -import org.apache.olingo.commons.api.data.AbstractEntityCollection; import org.apache.olingo.commons.api.data.EntityIterator; import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Linked; @@ -60,11 +60,12 @@ import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerStreamResult; import org.apache.olingo.server.api.uri.UriHelper; +import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; -import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.ODataWritableContent; +import org.apache.olingo.server.core.serializer.AbstractODataSerializer; import org.apache.olingo.server.core.serializer.SerializerResultImpl; import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper; @@ -402,30 +403,54 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } writeExpandedNavigationProperty(metadata, property, navigationLink, innerOptions == null ? null : innerOptions.getExpandOption(), - innerOptions == null ? null : innerOptions.getSelectOption(), json); + innerOptions == null ? null : innerOptions.getSelectOption(), + innerOptions == null ? null : innerOptions.getCountOption(), + innerOptions == null ? false : innerOptions.hasCountPath(), + innerOptions == null ? false : innerOptions.isRef(), + json); } } } } - - protected void writeExpandedNavigationProperty(final ServiceMetadata metadata, final EdmNavigationProperty property, - final Link navigationLink, final ExpandOption innerExpand, final SelectOption innerSelect, + + protected void writeExpandedNavigationProperty( + final ServiceMetadata metadata, final EdmNavigationProperty property, + final Link navigationLink, final ExpandOption innerExpand, + final SelectOption innerSelect, final CountOption innerCount, + final boolean writeOnlyCount, final boolean writeOnlyRef, final JsonGenerator json) throws IOException, SerializerException { - json.writeFieldName(property.getName()); + if (property.isCollection()) { - if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { - json.writeStartArray(); - json.writeEndArray(); - } else { - writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, - innerSelect, false, json); + if (writeOnlyCount) { + if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { + writeInlineCount(property.getName(), 0, json); + } else { + writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json); + } + } else { + if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { + if (innerCount != null && innerCount.getValue()) { + writeInlineCount(property.getName(), 0, json); + } + json.writeFieldName(property.getName()); + json.writeStartArray(); + json.writeEndArray(); + } else { + if (innerCount != null && innerCount.getValue()) { + writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json); + } + json.writeFieldName(property.getName()); + writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, + innerSelect, writeOnlyRef, json); + } } } else { + json.writeFieldName(property.getName()); if (navigationLink == null || navigationLink.getInlineEntity() == null) { json.writeNull(); } else { writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null, - innerExpand, innerSelect, false, json); + innerExpand, innerSelect, writeOnlyRef, json); } } } @@ -852,6 +877,18 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } } + void writeInlineCount(final String propertyName, + final Integer count, final JsonGenerator json) + throws IOException { + if (count != null) { + if (isIEEE754Compatible) { + json.writeStringField(propertyName+Constants.JSON_COUNT, String.valueOf(count)); + } else { + json.writeNumberField(propertyName+Constants.JSON_COUNT, count); + } + } + } + void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json) throws IOException { if (entitySet.getNext() != null) { json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java index fa607bd..fecf920 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java @@ -66,6 +66,7 @@ import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.serializer.SerializerResult; import org.apache.olingo.server.api.serializer.SerializerStreamResult; +import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; @@ -254,11 +255,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeNextLink(entitySet, writer); } + boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences()); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, writer); + writeEntitySet(metadata, entityType, entitySet, null, null, null, writer, writeOnlyRef); } else { writeEntitySet(metadata, entityType, entitySet, - options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer); + options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef); } writer.writeEndElement(); @@ -310,11 +312,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeCount(entitySet, writer); } + boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences()); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, writer); + writeEntitySet(metadata, entityType, entitySet, null, null, null, writer, writeOnlyRef); } else { writeEntitySet(metadata, entityType, entitySet, - options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer); + options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef); } writer.writeEndElement(); @@ -355,7 +358,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { options == null ? null : options.getExpand(), options == null ? null : options.getSelect(), options == null ? null : options.xml10InvalidCharReplacement(), - writer, true); + writer, true, false); writer.writeEndDocument(); writer.flush(); @@ -395,19 +398,24 @@ public class ODataXmlSerializer extends AbstractODataSerializer { protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select, - final String xml10InvalidCharReplacement,final XMLStreamWriter writer) + final String xml10InvalidCharReplacement,final XMLStreamWriter writer, final boolean writeOnlyRef) throws XMLStreamException, SerializerException { for (final Entity entity : entitySet) { - writeEntity(metadata, entityType, entity, null, expand, select, xml10InvalidCharReplacement, writer, false); + writeEntity(metadata, entityType, entity, null, expand, select, + xml10InvalidCharReplacement, writer, false, writeOnlyRef); } } protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity, final ContextURL contextURL, final ExpandOption expand, final SelectOption select, final String xml10InvalidCharReplacement, - final XMLStreamWriter writer, final boolean top) + final XMLStreamWriter writer, final boolean top, final boolean writeOnlyRef) throws XMLStreamException, SerializerException { + if (writeOnlyRef) { + writeReference(entity, contextURL, writer, top); + return; + } writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM); if (top) { writer.writeNamespace(ATOM, NS_ATOM); @@ -591,7 +599,10 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeExpandedNavigationProperty(metadata, property, navigationLink, innerOptions == null ? null : innerOptions.getExpandOption(), innerOptions == null ? null : innerOptions.getSelectOption(), - xml10InvalidCharReplacement, writer); + innerOptions == null ? null : innerOptions.getCountOption(), + innerOptions == null ? false : innerOptions.hasCountPath(), + innerOptions == null ? false : innerOptions.isRef(), + xml10InvalidCharReplacement, writer); writer.writeEndElement(); writer.writeEndElement(); } @@ -650,19 +661,27 @@ public class ODataXmlSerializer extends AbstractODataSerializer { protected void writeExpandedNavigationProperty(final ServiceMetadata metadata, final EdmNavigationProperty property, final Link navigationLink, - final ExpandOption innerExpand, final SelectOption innerSelect, final String xml10InvalidCharReplacement, + final ExpandOption innerExpand, final SelectOption innerSelect, final CountOption coutOption, + final boolean writeNavigationCount, final boolean writeOnlyRef,final String xml10InvalidCharReplacement, final XMLStreamWriter writer) throws XMLStreamException, SerializerException { if (property.isCollection()) { if (navigationLink != null && navigationLink.getInlineEntitySet() != null) { writer.writeStartElement(ATOM, Constants.ATOM_ELEM_FEED, NS_ATOM); - writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, - innerSelect, xml10InvalidCharReplacement, writer); + if (writeNavigationCount) { + writeCount(navigationLink.getInlineEntitySet(), writer); + } else { + if (coutOption != null && coutOption.getValue()) { + writeCount(navigationLink.getInlineEntitySet(), writer); + } + writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, + innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef); + } writer.writeEndElement(); } } else { if (navigationLink != null && navigationLink.getInlineEntity() != null) { writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null, - innerExpand, innerSelect, xml10InvalidCharReplacement, writer, false); + innerExpand, innerSelect, xml10InvalidCharReplacement, writer, false, writeOnlyRef); } } } @@ -1173,7 +1192,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { private void writeCount(final AbstractEntityCollection entitySet, final XMLStreamWriter writer) throws XMLStreamException { writer.writeStartElement(METADATA, Constants.ATOM_ELEM_COUNT, NS_METADATA); - writer.writeCharacters(String.valueOf(entitySet.getCount())); + writer.writeCharacters(String.valueOf(entitySet.getCount()==null?0:entitySet.getCount())); writer.writeEndElement(); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java index 2fc0faf..bae2bdd 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java @@ -160,10 +160,12 @@ public class ExpandParser { if (hasSlash || tokenizer.next(TokenKind.SLASH)) { if (tokenizer.next(TokenKind.REF)) { resource.addResourcePart(new UriResourceRefImpl()); + item.setIsRef(true); parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false); } else { ParserHelper.requireNext(tokenizer, TokenKind.COUNT); resource.addResourcePart(new UriResourceCountImpl()); + item.setCountPath(true); parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true); } } else { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java index 3ccea06..c21aa26 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java @@ -51,6 +51,7 @@ public class ExpandItemImpl implements ExpandItem { private boolean isStar; private boolean isRef; + private boolean hasCountPath; private EdmType startTypeFilter; public ExpandItemImpl setSystemQueryOption(final SystemQueryOption sysItem) { @@ -187,4 +188,13 @@ public class ExpandItemImpl implements ExpandItem { this.startTypeFilter = startTypeFilter; return this; } + + @Override + public boolean hasCountPath() { + return this.hasCountPath; + } + + public void setCountPath(boolean value) { + this.hasCountPath = value; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java index 770d90f..c59f3ef 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java @@ -439,7 +439,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final SelectOption select = uriInfo.getSelectOption(); final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); - final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand); + final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand, null); expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand, uriInfo, serviceMetadata.getEdm()); @@ -515,7 +515,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); final EntityCollection entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet, edmEntitySet, - expand); + expand, null); expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand, uriInfo, serviceMetadata.getEdm()); final CountOption countOption = uriInfo.getCountOption(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5dee97f7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java index 4cc88f8..f3d707a 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java @@ -96,11 +96,8 @@ public class ExpandSystemQueryOptionHandler { } } else { final List<UriResource> uriResourceParts = item.getResourcePath().getUriResourceParts(); - if (uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourceNavigation) { + if (uriResourceParts.get(0) instanceof UriResourceNavigation) { navigationProperties.add(((UriResourceNavigation) uriResourceParts.get(0)).getProperty()); - } else { - throw new ODataApplicationException("Not supported resource part in expand system query option", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); } } @@ -146,19 +143,22 @@ public class ExpandSystemQueryOptionHandler { } public EntityCollection transformEntitySetGraphToTree(final EntityCollection entitySet, - final EdmBindingTarget edmBindingTarget, final ExpandOption expand) throws ODataApplicationException { + final EdmBindingTarget edmBindingTarget, final ExpandOption expand, + final ExpandItem expandItem) throws ODataApplicationException { final EntityCollection newEntitySet = newEntitySet(entitySet); for (final Entity entity : entitySet.getEntities()) { - newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand)); + newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand, expandItem)); + } + if (expandItem != null && expandItem.hasCountPath()) { + newEntitySet.setCount(entitySet.getEntities().size()); } - return newEntitySet; } public Entity transformEntityGraphToTree(final Entity entity, final EdmBindingTarget edmEntitySet, - final ExpandOption expand) throws ODataApplicationException { + final ExpandOption expand, final ExpandItem parentExpandItem) throws ODataApplicationException { final Entity newEntity = newEntity(entity); if (hasExpandItems(expand)) { final boolean expandAll = expandAll(expand); @@ -173,16 +173,14 @@ public class ExpandSystemQueryOptionHandler { final EdmBindingTarget edmBindingTarget = edmEntitySet.getRelatedBindingTarget(propertyName); final Link newLink = newLink(link); newEntity.getNavigationLinks().add(newLink); - final ExpandOption innerExpandOption = getInnerExpandOption(expand, propertyName); + final ExpandItem expandItem = getInnerExpandItem(expand, propertyName); if (edmNavigationProperty.isCollection()) { newLink.setInlineEntitySet(transformEntitySetGraphToTree(link.getInlineEntitySet(), - edmBindingTarget, - innerExpandOption)); + edmBindingTarget, expandItem.getExpandOption(), expandItem)); } else { newLink.setInlineEntity(transformEntityGraphToTree(link.getInlineEntity(), - edmBindingTarget, - innerExpandOption)); + edmBindingTarget,expandItem.getExpandOption(), expandItem)); } } } @@ -250,29 +248,24 @@ public class ExpandSystemQueryOptionHandler { Set<String> expanded = new HashSet<String>(); for (final ExpandItem item : expandItems) { final List<UriResource> resourceParts = item.getResourcePath().getUriResourceParts(); - if (resourceParts.size() == 1) { - final UriResource resource = resourceParts.get(0); - if (resource instanceof UriResourceNavigation) { - expanded.add(((UriResourceNavigation) resource).getProperty().getName()); - } - } else { - throw new ODataApplicationException("Expand is not supported within complex properties.", - HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + final UriResource resource = resourceParts.get(0); + if (resource instanceof UriResourceNavigation) { + expanded.add(((UriResourceNavigation) resource).getProperty().getName()); } } return expanded; } - private ExpandOption getInnerExpandOption(final ExpandOption expand, final String propertyName) { + private ExpandItem getInnerExpandItem(final ExpandOption expand, final String propertyName) { for (final ExpandItem item : expand.getExpandItems()) { if(item.isStar()) { - return item.getExpandOption(); + return item; } final UriResource resource = item.getResourcePath().getUriResourceParts().get(0); if (resource instanceof UriResourceNavigation && propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) { - return item.getExpandOption(); + return item; } }
