[OLINGO-545] Expand supports the system query options $top, $skip, $filter, $orderby, $select
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/4f15c7c7 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/4f15c7c7 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/4f15c7c7 Branch: refs/heads/master Commit: 4f15c7c7246c99a8a59f1334ed2b8fdecfe0eb55 Parents: b982751 Author: Christian Holzer <[email protected]> Authored: Thu Mar 12 16:51:45 2015 +0100 Committer: Christian Holzer <[email protected]> Committed: Thu Mar 19 15:56:08 2015 +0100 ---------------------------------------------------------------------- .../ExpandWithSystemQueryOptionsITCase.java | 260 +++++++++++++++++++ .../olingo/server/tecsvc/data/DataCreator.java | 6 +- .../processor/TechnicalEntityProcessor.java | 14 +- .../ExpandSystemQueryOptionHandler.java | 152 +++++++++++ .../tecsvc/provider/ComplexTypeProvider.java | 4 +- .../tecsvc/provider/EntityTypeProvider.java | 2 +- .../tecsvc/provider/PropertyProvider.java | 16 +- 7 files changed, 441 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/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 new file mode 100644 index 0000000..a6b6eb9 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java @@ -0,0 +1,260 @@ +/* + * 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.fit.tecsvc.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.olingo.client.api.EdmEnabledODataClient; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest; +import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; +import org.apache.olingo.client.api.uri.QueryOption; +import org.apache.olingo.client.core.ODataClientFactory; +import org.apache.olingo.commons.api.domain.ODataEntity; +import org.apache.olingo.commons.api.domain.ODataEntitySet; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.junit.Test; + +public class ExpandWithSystemQueryOptionsITCase extends AbstractBaseTestITCase { + + private static final String ES_KEY_NAV = "ESKeyNav"; + private static final String ES_TWO_KEY_NAV = "ESTwoKeyNav"; + private static final String NAV_PROPERTY_ET_KEY_NAV_MANY = "NavPropertyETKeyNavMany"; + private static final String NAV_PROPERTY_ET_TWO_KEY_NAV_MANY = "NavPropertyETTwoKeyNavMany"; + private static final String SERVICE_URI = TecSvcConst.BASE_URI; + private static final String PROPERTY_INT16 = "PropertyInt16"; + private static final String PROPERTY_STRING = "PropertyString"; + + @Test + public void testFilter() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.FILTER, "PropertyString eq '2'"); + + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_TWO_KEY_NAV, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + assertEquals(4, entities.size()); + + for (final ODataEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue(); + final ODataEntitySet inlineEntitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if (propInt16.equals(1) && propString.equals("1")) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(1, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals("2", inlineEntity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + } else if (propInt16.equals(1) && propString.equals("2")) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } else if (propInt16.equals(2) && propString.equals("1")) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(1, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals("2", inlineEntity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + } else if (propInt16.equals(3) && propString.equals("1")) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } else { + fail(); + } + } + } + + @Test + public void testOrderBy() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.ORDERBY, "PropertyString desc"); + + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_TWO_KEY_NAV, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + assertEquals(4, entities.size()); + + for (final ODataEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue(); + final ODataEntitySet inlineEntitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if (propInt16.equals(1) && propString.equals("1")) { + assertEquals(2, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity1 = inlineEntitySet.getEntities().get(0); + final ODataEntity inlineEntity2 = inlineEntitySet.getEntities().get(1); + + assertEquals(1, inlineEntity1.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals("2", inlineEntity1.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + + assertEquals(1, inlineEntity2.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals("1", inlineEntity2.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + } + } + } + + @Test + public void testSkip() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.SKIP, "1"); + + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_KEY_NAV, NAV_PROPERTY_ET_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + assertEquals(3, entities.size()); + + for (final ODataEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final ODataEntitySet inlineEntitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if (propInt16.equals(1)) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(2, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + } else if (propInt16.equals(2)) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(3, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + } else if (propInt16.equals(3)) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } + } + } + + @Test + public void testTop() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.TOP, "1"); + + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_KEY_NAV, NAV_PROPERTY_ET_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + assertEquals(3, entities.size()); + + for (final ODataEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final ODataEntitySet inlineEntitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if (propInt16.equals(1)) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(1, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + } else if (propInt16.equals(2)) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(2, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + } else if (propInt16.equals(3)) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } + } + } + + @Test + public void testCombinedSystemQueryOptions() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.SELECT, "PropertyInt16,PropertyString"); + options.put(QueryOption.FILTER, "PropertyInt16 eq 1"); + options.put(QueryOption.SKIP, "1"); + + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_TWO_KEY_NAV, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + assertEquals(4, entities.size()); + + for (final ODataEntity entity : entities) { + final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue(); + final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue(); + final ODataEntitySet inlineEntitySet = + entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet(); + + if (propInt16.equals(1) && propString.equals("1")) { + assertEquals(1, inlineEntitySet.getEntities().size()); + final ODataEntity inlineEntity = inlineEntitySet.getEntities().get(0); + + assertEquals(1, inlineEntity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); + assertEquals("2", inlineEntity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + } else if (propInt16.equals(1) && propString.equals("2")) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } else if (propInt16.equals(2) && propString.equals("1")) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } else if (propInt16.equals(3) && propString.equals("1")) { + assertEquals(0, inlineEntitySet.getEntities().size()); + } else { + fail(); + } + } + } + + @Test + public void testURIEscaping() { + final Map<QueryOption, Object> options = new HashMap<QueryOption, Object>(); + options.put(QueryOption.FILTER, "PropertyInt16 eq 1" + + " and PropertyComp/PropertyComp/PropertyDuration eq duration'PT1S' and length(PropertyString) gt 4"); + final ODataRetrieveResponse<ODataEntitySet> response = + buildRequest(ES_TWO_KEY_NAV, NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options); + final List<ODataEntity> entities = response.getBody().getEntities(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + assertEquals(4, entities.size()); + } + + private ODataRetrieveResponse<ODataEntitySet> buildRequest(final String entitySet, final String navigationProperty, + final Map<QueryOption, Object> expandOptions) { + return buildRequest(entitySet, navigationProperty, expandOptions, null); + } + + private ODataRetrieveResponse<ODataEntitySet> buildRequest(final String entitySet, final String navigationProperty, + final Map<QueryOption, Object> expandOptions, final String cookie) { + final ODataClient client = getClient(); + final URI uri = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(entitySet) + .expandWithOptions(navigationProperty, expandOptions) + .build(); + + final ODataEntitySetRequest<ODataEntitySet> request = client.getRetrieveRequestFactory().getEntitySetRequest(uri); + + if (cookie != null) { + request.addCustomHeader(HttpHeader.COOKIE, cookie); + } + + return request.execute(); + } + + @Override + protected ODataClient getClient() { + EdmEnabledODataClient odata = ODataClientFactory.getEdmEnabledClient(SERVICE_URI); + odata.getConfiguration().setDefaultPubFormat(ODataFormat.JSON); + return odata; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java index 1e4a707..28781a3 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java @@ -664,9 +664,9 @@ public class DataCreator { // NavPropertyETTwoKeyNavMany setLinks(entitySet.getEntities().get(0), "NavPropertyETTwoKeyNavMany", - esKeyNavTargets.get(0), esKeyNavTargets.get(1)); - setLinks(entitySet.getEntities().get(1), "NavPropertyETTwoKeyNavMany", esKeyNavTargets.get(0)); - setLinks(entitySet.getEntities().get(2), "NavPropertyETTwoKeyNavMany", esKeyNavTargets.get(1)); + esTwoKeyNavTargets.get(0), esTwoKeyNavTargets.get(1)); + setLinks(entitySet.getEntities().get(1), "NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(0)); + setLinks(entitySet.getEntities().get(2), "NavPropertyETTwoKeyNavMany", esTwoKeyNavTargets.get(1)); } protected static Property createPrimitive(final String name, final Object value) { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/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 3021db3..b66482a 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 @@ -55,6 +55,7 @@ import org.apache.olingo.server.api.uri.UriResourceFunction; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.tecsvc.data.DataProvider; +import org.apache.olingo.server.tecsvc.processor.queryoptions.ExpandSystemQueryOptionHandler; import org.apache.olingo.server.tecsvc.processor.queryoptions.options.CountHandler; import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler; import org.apache.olingo.server.tecsvc.processor.queryoptions.options.OrderByHandler; @@ -104,12 +105,21 @@ public class TechnicalEntityProcessor extends TechnicalProcessor entitySet, edmEntitySet, request.getRawRequestUri()); - + + // Apply expand system query option final ODataFormat format = ODataFormat.fromContentType(requestedContentType); ODataSerializer serializer = odata.createSerializer(format); final ExpandOption expand = uriInfo.getExpandOption(); final SelectOption select = uriInfo.getSelectOption(); - response.setContent(serializer.entityCollection(edmEntityType, entitySet, + + // Create a shallow copy of each entity. So the expanded navigation properties can be modified for serialization, + // without affecting the data stored in the database. + final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler(); + final EntitySet entitySetSerialization = expandHandler.copyEntitySetShallowRekursive(entitySet); + expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand); + + // Serialize + response.setContent(serializer.entityCollection(edmEntityType, entitySetSerialization, EntityCollectionSerializerOptions.with() .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : getContextUrl(edmEntitySet, edmEntityType, false, expand, select)) http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/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 new file mode 100644 index 0000000..61fe162 --- /dev/null +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/queryoptions/ExpandSystemQueryOptionHandler.java @@ -0,0 +1,152 @@ +/* + * 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.tecsvc.processor.queryoptions; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.commons.core.data.EntityImpl; +import org.apache.olingo.commons.core.data.EntitySetImpl; +import org.apache.olingo.commons.core.data.LinkImpl; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceNavigation; +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.FilterOption; +import org.apache.olingo.server.api.uri.queryoption.OrderByOption; +import org.apache.olingo.server.api.uri.queryoption.SkipOption; +import org.apache.olingo.server.api.uri.queryoption.TopOption; +import org.apache.olingo.server.tecsvc.processor.queryoptions.options.FilterHandler; +import org.apache.olingo.server.tecsvc.processor.queryoptions.options.OrderByHandler; +import org.apache.olingo.server.tecsvc.processor.queryoptions.options.SkipHandler; +import org.apache.olingo.server.tecsvc.processor.queryoptions.options.TopHandler; + +public class ExpandSystemQueryOptionHandler { + private HashMap<Entity, Entity> copiedEntities = new HashMap<Entity, Entity>(); + private HashMap<EntitySet, EntitySet> copiedEntitySets = new HashMap<EntitySet, EntitySet>(); + + public void applyExpandQueryOptions(final EntitySet entitySet, final EdmEntitySet edmEntitySet, + final ExpandOption expandOption) throws ODataApplicationException { + final EdmEntityType entityType = edmEntitySet.getEntityType(); + if (expandOption == null) { + return; + } + + for (ExpandItem item : expandOption.getExpandItems()) { + final List<UriResource> uriResourceParts = item.getResourcePath().getUriResourceParts(); + if (uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourceNavigation) { + final String navPropertyName = ((UriResourceNavigation) uriResourceParts.get(0)).getProperty().getName(); + final EdmEntitySet targetEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(navPropertyName); + + for (final Entity entity : entitySet.getEntities()) { + final Link link = entity.getNavigationLink(navPropertyName); + if (link != null && entityType.getNavigationProperty(navPropertyName).isCollection()) { + applyOptionsToEntityCollection(link.getInlineEntitySet(), targetEdmEntitySet, item.getFilterOption(), + item.getOrderByOption(), item.getCountOption(), item.getSkipOption(), item.getTopOption()); + } + } + } else { + throw new ODataApplicationException("Not supported resource part in expand system query option", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + } + } + + private void applyOptionsToEntityCollection(final EntitySet entitySet, final EdmEntitySet edmEntitySet, + final FilterOption filterOption, final OrderByOption orderByOption, final CountOption countOption, + final SkipOption skipOption, final TopOption topOption) throws ODataApplicationException { + + FilterHandler.applyFilterSystemQuery(filterOption, entitySet, edmEntitySet); + OrderByHandler.applyOrderByOption(orderByOption, entitySet, edmEntitySet); + SkipHandler.applySkipSystemQueryHandler(skipOption, entitySet); + TopHandler.applyTopSystemQueryOption(topOption, entitySet); + } + + public EntitySet copyEntitySetShallowRekursive(final EntitySet entitySet) { + if (!copiedEntitySets.containsKey(entitySet)) { + final EntitySet copiedEntitySet = new EntitySetImpl(); + copiedEntitySet.setCount(entitySet.getCount()); + copiedEntitySet.setDeltaLink(entitySet.getDeltaLink()); + copiedEntitySet.setNext(entitySet.getNext()); + + copiedEntitySets.put(entitySet, copiedEntitySet); + copiedEntitySets.put(copiedEntitySet, copiedEntitySet); + + for (Entity entity : entitySet.getEntities()) { + copiedEntitySet.getEntities().add(copyEntityShallowRekursive(entity)); + } + return copiedEntitySet; + } + return copiedEntitySets.get(entitySet); + } + + private Entity copyEntityShallowRekursive(final Entity entity) { + if (!copiedEntities.containsKey(entity)) { + final Entity copiedEntity = new EntityImpl(); + copiedEntity.getProperties().addAll(entity.getProperties()); + copiedEntity.getAnnotations().addAll(entity.getAnnotations()); + copiedEntity.getAssociationLinks().addAll(entity.getAssociationLinks()); + copiedEntity.setEditLink(entity.getEditLink()); + copiedEntity.setId(entity.getId()); + copiedEntity.setMediaContentSource(entity.getMediaContentSource()); + copiedEntity.setMediaContentType(entity.getMediaContentType()); + copiedEntity.setMediaETag(entity.getMediaETag()); + copiedEntity.getOperations().addAll(entity.getOperations()); + copiedEntity.setSelfLink(entity.getSelfLink()); + copiedEntity.setType(entity.getType()); + copiedEntity.getNavigationBindings().addAll(entity.getNavigationBindings()); + + copiedEntities.put(entity, copiedEntity); + copiedEntities.put(copiedEntity, copiedEntity); + + // The system query options change the amount and sequence of inline entities (feeds) + // So we have to make a shallow copy of all navigation link lists + // Make sure, that each entity is only copied once. + // Otherwise an infinite loop can occur caused by cyclic navigation relationships. + + for (final Link link : entity.getNavigationLinks()) { + final Link newLink = new LinkImpl(); + newLink.setMediaETag(link.getMediaETag()); + newLink.setTitle(link.getTitle()); + newLink.setType(link.getType()); + newLink.setRel(link.getRel()); + + final EntitySet inlineEntitySet = link.getInlineEntitySet(); + if (inlineEntitySet != null) { + newLink.setInlineEntitySet(copyEntitySetShallowRekursive(inlineEntitySet)); + } else if (link.getInlineEntity() != null) { + newLink.setInlineEntity(copyEntityShallowRekursive(link.getInlineEntity())); + } + copiedEntity.getNavigationLinks().add(newLink); + } + + return copiedEntity; + } + return copiedEntities.get(entity); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ComplexTypeProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ComplexTypeProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ComplexTypeProvider.java index 767d639..ad0f182 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ComplexTypeProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ComplexTypeProvider.java @@ -140,7 +140,7 @@ public class ComplexTypeProvider { .setProperties(Arrays.asList(PropertyProvider.propertyInt16)) .setNavigationProperties((Arrays.asList( PropertyProvider.collectionNavPropertyETTwoKeyNavOne_ETTwoKeyNav, - PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav, + PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav_WithPartnerERKeyNavOne, new NavigationProperty() .setName("NavPropertyETMediaOne") .setType(EntityTypeProvider.nameETMedia), @@ -154,7 +154,7 @@ public class ComplexTypeProvider { .setName("CTBasePrimCompNav") .setBaseType(nameCTPrimComp) .setNavigationProperties(Arrays.asList( - PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav, + PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav_WithPartnerERKeyNavOne, PropertyProvider.collectionNavPropertyETTwoKeyNavOne_ETTwoKeyNav, PropertyProvider.navPropertyETKeyNavOne_ETKeyNav, PropertyProvider.collectionNavPropertyETKeyNavMany_ETKeyNav)); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java index 8004b17..cfcbcdc 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java @@ -315,7 +315,7 @@ public class EntityTypeProvider { .setNavigationProperties( Arrays.asList( PropertyProvider.navPropertyETTwoKeyNavOne_ETTwoKeyNav_NotNullable, - PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav, + PropertyProvider.collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav_WithPartnerERKeyNavOne, PropertyProvider.navPropertyETKeyNavOne_ETKeyNav, PropertyProvider.collectionNavPropertyETKeyNavMany_ETKeyNav, PropertyProvider.navPropertyETMediaOne_ETMedia, http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4f15c7c7/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java index aa77abf..43b7a63 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java @@ -632,12 +632,18 @@ public class PropertyProvider { .setType(EntityTypeProvider.nameETMedia) .setCollection(true); + public static final NavigationProperty collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav_WithPartnerERKeyNavOne + = new NavigationProperty() + .setName("NavPropertyETTwoKeyNavMany") + .setType(EntityTypeProvider.nameETTwoKeyNav) + .setCollection(true) + .setPartner("NavPropertyETKeyNavOne"); + public static final NavigationProperty collectionNavPropertyETTwoKeyNavMany_ETTwoKeyNav = new NavigationProperty() - .setName("NavPropertyETTwoKeyNavMany") - .setType(EntityTypeProvider.nameETTwoKeyNav) - .setCollection(true) - .setPartner("NavPropertyETKeyNavOne"); - + .setName("NavPropertyETTwoKeyNavMany") + .setType(EntityTypeProvider.nameETTwoKeyNav) + .setCollection(true); + public static final NavigationProperty collectionNavPropertyETTwoKeyNavOne_ETTwoKeyNav = new NavigationProperty() .setName("NavPropertyETTwoKeyNavOne") .setType(EntityTypeProvider.nameETTwoKeyNav);
