You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by tb...@apache.org on 2013/12/20 16:18:14 UTC

[12/12] git commit: [OLINGO-67] Merge remote-tracking branch 'origin/master' into ODataServlet

[OLINGO-67] Merge remote-tracking branch 'origin/master' into ODataServlet


Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/commit/80379ec1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/tree/80379ec1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/diff/80379ec1

Branch: refs/heads/ODataServlet
Commit: 80379ec10a99080b8b012814216107dbaf6951e6
Parents: 22e54c4 33aaa3a
Author: Tamara Boehm <ta...@sap.com>
Authored: Fri Dec 20 16:14:58 2013 +0100
Committer: Tamara Boehm <ta...@sap.com>
Committed: Fri Dec 20 16:14:58 2013 +0100

----------------------------------------------------------------------
 .../processor/api/AnnotationServiceFactory.java |  118 ++
 .../olingo/odata2/api/data/DataSource.java      |  200 ---
 .../odata2/api/data/DataSourceProcessor.java    |   47 -
 .../olingo/odata2/api/data/ValueAccess.java     |   69 -
 .../core/AnnotationServiceFactoryImpl.java      |   73 +
 .../processor/core/DataSourceProcessor.java     |   49 +
 .../processor/core/ListsProcessor.java          | 1640 +++++++++++++++++
 .../core/datasource/AnnotationInMemoryDs.java   |  384 ++++
 .../core/datasource/AnnotationValueAccess.java  |  108 ++
 .../core/datasource/BeanPropertyAccess.java     |  185 ++
 .../processor/core/datasource/DataSource.java   |  200 +++
 .../processor/core/datasource/DataStore.java    |  294 ++++
 .../processor/core/datasource/ValueAccess.java  |   69 +
 .../core/edm/AnnotationEdmProvider.java         |  769 ++++++++
 .../processor/core/util/AnnotationHelper.java   |  777 +++++++++
 .../processor/core/util/ClassHelper.java        |  140 ++
 .../annotation/data/AnnotationInMemoryDs.java   |  376 ----
 .../annotation/data/AnnotationValueAccess.java  |  109 --
 .../annotation/data/BeanPropertyAccess.java     |  186 --
 .../odata2/core/annotation/data/DataStore.java  |  294 ----
 .../annotation/edm/AnnotationEdmProvider.java   |  769 --------
 .../annotation/processor/ListsProcessor.java    | 1641 ------------------
 .../core/annotation/util/AnnotationHelper.java  |  777 ---------
 .../core/annotation/util/ClassHelper.java       |  140 --
 .../data/AnnotationsInMemoryDsTest.java         |   10 +-
 .../edm/AnnotationEdmProviderTest.java          |    1 +
 .../ref/AnnotationRefServiceFactory.java        |  215 +++
 .../processor/ref/model/Building.java           |   97 ++
 .../annotation/processor/ref/model/City.java    |   61 +
 .../processor/ref/model/Employee.java           |  192 ++
 .../processor/ref/model/Location.java           |   60 +
 .../annotation/processor/ref/model/Manager.java |   43 +
 .../ref/model/ModelSharedConstants.java         |   25 +
 .../annotation/processor/ref/model/Photo.java   |  128 ++
 .../annotation/processor/ref/model/RefBase.java |   78 +
 .../processor/ref/model/ResourceHelper.java     |   96 +
 .../annotation/processor/ref/model/Room.java    |   88 +
 .../annotation/processor/ref/model/Team.java    |   73 +
 .../odata2/ref/annotation/model/Building.java   |   97 --
 .../odata2/ref/annotation/model/City.java       |   61 -
 .../odata2/ref/annotation/model/Employee.java   |  192 --
 .../odata2/ref/annotation/model/Location.java   |   60 -
 .../odata2/ref/annotation/model/Manager.java    |   43 -
 .../annotation/model/ModelSharedConstants.java  |   25 -
 .../odata2/ref/annotation/model/Photo.java      |  128 --
 .../odata2/ref/annotation/model/RefBase.java    |   78 -
 .../ref/annotation/model/ResourceHelper.java    |   96 -
 .../odata2/ref/annotation/model/Room.java       |   88 -
 .../odata2/ref/annotation/model/Team.java       |   73 -
 .../processor/AnnotationServiceFactory.java     |  215 ---
 .../src/main/webapp/WEB-INF/web.xml             |    2 +-
 .../olingo/odata2/api/edm/EdmException.java     |    7 +
 .../olingo/odata2/api/rt/RuntimeDelegate.java   |    7 +
 .../odata2/api/uri/ExpandSelectTreeNode.java    |   91 +-
 .../odata2/core/rt/RuntimeDelegateImpl.java     |    8 +
 .../core/uri/ExpandSelectTreeNodeImpl.java      |  117 +-
 .../src/main/resources/i18n.properties          |    4 +
 .../ExpandSelectProducerWithBuilderTest.java    |  204 +++
 .../core/ep/producer/XmlExpandProducerTest.java |   24 +
 .../uri/ExpandSelectTreeNodeBuilderTest.java    |  282 +++
 .../olingo/odata2/fit/ref/AbstractRefTest.java  |    4 +-
 .../odata2/fit/ref/EntryXmlReadOnlyTest.java    |    2 +-
 .../AbstractContentNegotiationTest.java         |    4 +-
 .../ref/processor/ScenarioDataSource.java       |    2 +-
 .../ref/processor/ScenarioServiceFactory.java   |    4 +-
 .../olingo/odata2/ref/read/EntitySetTest.java   |    4 +-
 .../olingo/odata2/ref/read/EntityTest.java      |    4 +-
 67 files changed, 6720 insertions(+), 5787 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/80379ec1/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/ListsProcessor.java
----------------------------------------------------------------------
diff --cc odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/ListsProcessor.java
index 0000000,85e011c..de0a2da
mode 000000,100644..100644
--- a/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/ListsProcessor.java
+++ b/odata2-annotation-processor/annotation-processor-core/src/main/java/org/apache/olingo/odata2/annotation/processor/core/ListsProcessor.java
@@@ -1,0 -1,1640 +1,1640 @@@
+ /*******************************************************************************
+  * 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.odata2.annotation.processor.core;
+ 
+ import java.io.InputStream;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Collections;
+ import java.util.Comparator;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Locale;
+ import java.util.Map;
+ 
+ import org.apache.olingo.odata2.annotation.processor.core.datasource.DataSource;
+ import org.apache.olingo.odata2.annotation.processor.core.datasource.ValueAccess;
+ import org.apache.olingo.odata2.annotation.processor.core.datasource.DataSource.BinaryData;
+ import org.apache.olingo.odata2.api.ODataCallback;
+ import org.apache.olingo.odata2.api.batch.BatchHandler;
+ import org.apache.olingo.odata2.api.batch.BatchRequestPart;
+ import org.apache.olingo.odata2.api.batch.BatchResponsePart;
+ import org.apache.olingo.odata2.api.commons.HttpContentType;
+ import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
+ import org.apache.olingo.odata2.api.commons.InlineCount;
+ import org.apache.olingo.odata2.api.edm.Edm;
+ import org.apache.olingo.odata2.api.edm.EdmConcurrencyMode;
+ import org.apache.olingo.odata2.api.edm.EdmEntitySet;
+ import org.apache.olingo.odata2.api.edm.EdmEntityType;
+ import org.apache.olingo.odata2.api.edm.EdmException;
+ import org.apache.olingo.odata2.api.edm.EdmFunctionImport;
+ import org.apache.olingo.odata2.api.edm.EdmLiteral;
+ import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
+ import org.apache.olingo.odata2.api.edm.EdmMapping;
+ import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
+ import org.apache.olingo.odata2.api.edm.EdmNavigationProperty;
+ import org.apache.olingo.odata2.api.edm.EdmProperty;
+ import org.apache.olingo.odata2.api.edm.EdmSimpleType;
+ import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException;
+ import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+ import org.apache.olingo.odata2.api.edm.EdmStructuralType;
+ import org.apache.olingo.odata2.api.edm.EdmType;
+ import org.apache.olingo.odata2.api.edm.EdmTypeKind;
+ import org.apache.olingo.odata2.api.edm.EdmTyped;
+ import org.apache.olingo.odata2.api.ep.EntityProvider;
+ import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
+ import org.apache.olingo.odata2.api.ep.EntityProviderException;
+ import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
+ import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+ import org.apache.olingo.odata2.api.ep.callback.OnWriteEntryContent;
+ import org.apache.olingo.odata2.api.ep.callback.OnWriteFeedContent;
+ import org.apache.olingo.odata2.api.ep.callback.WriteCallbackContext;
+ import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackContext;
+ import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackResult;
+ import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackContext;
+ import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackResult;
+ import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+ import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+ import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+ import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
+ import org.apache.olingo.odata2.api.exception.ODataException;
+ import org.apache.olingo.odata2.api.exception.ODataHttpException;
+ import org.apache.olingo.odata2.api.exception.ODataNotFoundException;
+ import org.apache.olingo.odata2.api.exception.ODataNotImplementedException;
+ import org.apache.olingo.odata2.api.processor.ODataContext;
+ import org.apache.olingo.odata2.api.processor.ODataRequest;
+ import org.apache.olingo.odata2.api.processor.ODataResponse;
+ import org.apache.olingo.odata2.api.uri.ExpandSelectTreeNode;
+ import org.apache.olingo.odata2.api.uri.KeyPredicate;
+ import org.apache.olingo.odata2.api.uri.NavigationSegment;
+ import org.apache.olingo.odata2.api.uri.PathInfo;
+ import org.apache.olingo.odata2.api.uri.UriParser;
+ import org.apache.olingo.odata2.api.uri.expression.BinaryExpression;
+ import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
+ import org.apache.olingo.odata2.api.uri.expression.ExpressionKind;
+ import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
+ import org.apache.olingo.odata2.api.uri.expression.LiteralExpression;
+ import org.apache.olingo.odata2.api.uri.expression.MemberExpression;
+ import org.apache.olingo.odata2.api.uri.expression.MethodExpression;
+ import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
+ import org.apache.olingo.odata2.api.uri.expression.OrderExpression;
+ import org.apache.olingo.odata2.api.uri.expression.PropertyExpression;
+ import org.apache.olingo.odata2.api.uri.expression.SortOrder;
+ import org.apache.olingo.odata2.api.uri.expression.UnaryExpression;
+ import org.apache.olingo.odata2.api.uri.info.DeleteUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetComplexPropertyUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntityCountUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntityLinkCountUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntityLinkUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntitySetCountUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntitySetLinksCountUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntitySetLinksUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntitySetUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetEntityUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetFunctionImportUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetMediaResourceUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.GetSimplePropertyUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.PostUriInfo;
+ import org.apache.olingo.odata2.api.uri.info.PutMergePatchUriInfo;
+ 
+ /**
+  * Implementation of the centralized parts of OData processing,
+  * allowing to use the simplified {@link DataSource} for the
+  * actual data handling.
+  * 
+  */
+ public class ListsProcessor extends DataSourceProcessor {
+ 
+   // TODO: Paging size should be configurable.
+   private static final int SERVER_PAGING_SIZE = 100;
+ 
+   public ListsProcessor(final DataSource dataSource, final ValueAccess valueAccess) {
+     super(dataSource, valueAccess);
+   }
+ 
+   @Override
+   public ODataResponse readEntitySet(final GetEntitySetUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     ArrayList<Object> data = new ArrayList<Object>();
+     try {
+       data.addAll((List<?>) retrieveData(
+           uriInfo.getStartEntitySet(),
+           uriInfo.getKeyPredicates(),
+           uriInfo.getFunctionImport(),
+           mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+           uriInfo.getNavigationSegments()));
+     } catch (final ODataNotFoundException e) {
+       data.clear();
+     }
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+     final InlineCount inlineCountType = uriInfo.getInlineCount();
+     final Integer count = applySystemQueryOptions(
+         entitySet,
+         data,
+         uriInfo.getFilter(),
+         inlineCountType,
+         uriInfo.getOrderBy(),
+         uriInfo.getSkipToken(),
+         uriInfo.getSkip(),
+         uriInfo.getTop());
+ 
+     ODataContext context = getContext();
+     String nextLink = null;
+ 
+     // Limit the number of returned entities and provide a "next" link
+     // if there are further entities.
+     // Almost all system query options in the current request must be carried
+     // over to the URI for the "next" link, with the exception of $skiptoken
+     // and $skip.
+     if (data.size() > SERVER_PAGING_SIZE) {
+       if (uriInfo.getOrderBy() == null
+           && uriInfo.getSkipToken() == null
+           && uriInfo.getSkip() == null
+           && uriInfo.getTop() == null) {
+         sortInDefaultOrder(entitySet, data);
+       }
+ 
+       // TODO: Percent-encode "next" link.
+       nextLink = context.getPathInfo().getServiceRoot().relativize(context.getPathInfo().getRequestUri()).toString()
+           .replaceAll("\\$skiptoken=.+?&?", "")
+           .replaceAll("\\$skip=.+?&?", "")
+           .replaceFirst("(?:\\?|&)$", ""); // Remove potentially trailing "?" or "&" left over from remove actions
+                                            // above.
+       nextLink += (nextLink.contains("?") ? "&" : "?")
+           + "$skiptoken=" + getSkipToken(entitySet, data.get(SERVER_PAGING_SIZE));
+ 
+       while (data.size() > SERVER_PAGING_SIZE) {
+         data.remove(SERVER_PAGING_SIZE);
+       }
+     }
+ 
+     final EdmEntityType entityType = entitySet.getEntityType();
+     List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
+     for (final Object entryData : data) {
+       values.add(getStructuralTypeValueMap(entryData, entityType));
+     }
+ 
+     final EntityProviderWriteProperties feedProperties = EntityProviderWriteProperties
+         .serviceRoot(context.getPathInfo().getServiceRoot())
+         .inlineCountType(inlineCountType)
+         .inlineCount(count)
+         .expandSelectTree(UriParser.createExpandSelectTree(uriInfo.getSelect(), uriInfo.getExpand()))
+         .callbacks(getCallbacks(data, entityType))
+         .nextLink(nextLink)
+         .build();
+ 
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeFeed");
+     final ODataResponse response = EntityProvider.writeFeed(contentType, entitySet, values, feedProperties);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return ODataResponse.fromResponse(response).build();
+   }
+ 
+   @Override
+   public ODataResponse countEntitySet(final GetEntitySetCountUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     ArrayList<Object> data = new ArrayList<Object>();
+     try {
+       data.addAll((List<?>) retrieveData(
+           uriInfo.getStartEntitySet(),
+           uriInfo.getKeyPredicates(),
+           uriInfo.getFunctionImport(),
+           mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+           uriInfo.getNavigationSegments()));
+     } catch (final ODataNotFoundException e) {
+       data.clear();
+     }
+ 
+     applySystemQueryOptions(
+         uriInfo.getTargetEntitySet(),
+         data,
+         uriInfo.getFilter(),
+         null,
+         null,
+         null,
+         uriInfo.getSkip(),
+         uriInfo.getTop());
+ 
+     return ODataResponse.fromResponse(EntityProvider.writeText(String.valueOf(data.size()))).build();
+   }
+ 
+   @Override
+   public ODataResponse readEntityLinks(final GetEntitySetLinksUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     ArrayList<Object> data = new ArrayList<Object>();
+     try {
+       data.addAll((List<?>) retrieveData(
+           uriInfo.getStartEntitySet(),
+           uriInfo.getKeyPredicates(),
+           uriInfo.getFunctionImport(),
+           mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+           uriInfo.getNavigationSegments()));
+     } catch (final ODataNotFoundException e) {
+       data.clear();
+     }
+ 
+     final Integer count = applySystemQueryOptions(
+         uriInfo.getTargetEntitySet(),
+         data,
+         uriInfo.getFilter(),
+         uriInfo.getInlineCount(),
+         null, // uriInfo.getOrderBy(),
+         uriInfo.getSkipToken(),
+         uriInfo.getSkip(),
+         uriInfo.getTop());
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+ 
+     List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
+     for (final Object entryData : data) {
+       Map<String, Object> entryValues = new HashMap<String, Object>();
+       for (final EdmProperty property : entitySet.getEntityType().getKeyProperties()) {
+         entryValues.put(property.getName(), valueAccess.getPropertyValue(entryData, property));
+       }
+       values.add(entryValues);
+     }
+ 
+     ODataContext context = getContext();
+     final EntityProviderWriteProperties entryProperties = EntityProviderWriteProperties
+         .serviceRoot(context.getPathInfo().getServiceRoot())
+         .inlineCountType(uriInfo.getInlineCount())
+         .inlineCount(count)
+         .build();
+ 
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeLinks");
+ 
+     final ODataResponse response = EntityProvider.writeLinks(contentType, entitySet, values, entryProperties);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return ODataResponse.fromResponse(response).build();
+   }
+ 
+   @Override
+   public ODataResponse countEntityLinks(final GetEntitySetLinksCountUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     return countEntitySet((GetEntitySetCountUriInfo) uriInfo, contentType);
+   }
+ 
+   @Override
+   public ODataResponse readEntity(final GetEntityUriInfo uriInfo, final String contentType) throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final ExpandSelectTreeNode expandSelectTreeNode =
+         UriParser.createExpandSelectTree(uriInfo.getSelect(), uriInfo.getExpand());
+     ODataResponse odr =
+         ODataResponse.fromResponse(writeEntry(uriInfo.getTargetEntitySet(), expandSelectTreeNode, data, contentType))
+             .build();
+ 
+     return odr;
+   }
+ 
+   @Override
+   public ODataResponse existsEntity(final GetEntityCountUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     return ODataResponse.fromResponse(EntityProvider.writeText(appliesFilter(data, uriInfo.getFilter()) ? "1" : "0"))
+         .build();
+   }
+ 
+   @Override
+   public ODataResponse deleteEntity(final DeleteUriInfo uriInfo, final String contentType) throws ODataException {
+     dataSource.deleteData(
+         uriInfo.getStartEntitySet(),
+         mapKey(uriInfo.getKeyPredicates()));
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse createEntity(final PostUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+     final EdmEntityType entityType = entitySet.getEntityType();
+ 
+     Object data = dataSource.newDataObject(entitySet);
+     ExpandSelectTreeNode expandSelectTree = null;
+ 
+     if (entityType.hasStream()) {
+       dataSource.createData(entitySet, data);
+       dataSource.writeBinaryData(entitySet, data,
+           new BinaryData(EntityProvider.readBinary(content), requestContentType));
+ 
+     } else {
+       final EntityProviderReadProperties properties = EntityProviderReadProperties.init()
+           .mergeSemantic(false)
+           .addTypeMappings(getStructuralTypeTypeMap(data, entityType))
+           .build();
+       final ODataEntry entryValues = parseEntry(entitySet, content, requestContentType, properties);
+ 
+       setStructuralTypeValuesFromMap(data, entityType, entryValues.getProperties(), false);
+ 
+       dataSource.createData(entitySet, data);
+ 
+       createInlinedEntities(entitySet, data, entryValues);
+ 
+       expandSelectTree = entryValues.getExpandSelectTree();
+     }
+ 
+     // Link back to the entity the target entity set is related to, if any.
+     final List<NavigationSegment> navigationSegments = uriInfo.getNavigationSegments();
+     if (!navigationSegments.isEmpty()) {
+       final List<NavigationSegment> previousSegments = navigationSegments.subList(0, navigationSegments.size() - 1);
+       final Object sourceData = retrieveData(
+           uriInfo.getStartEntitySet(),
+           uriInfo.getKeyPredicates(),
+           uriInfo.getFunctionImport(),
+           mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+           previousSegments);
+       final EdmEntitySet previousEntitySet = previousSegments.isEmpty() ?
+           uriInfo.getStartEntitySet() : previousSegments.get(previousSegments.size() - 1).getEntitySet();
+       dataSource.writeRelation(previousEntitySet, sourceData, entitySet, getStructuralTypeValueMap(data, entityType));
+     }
+ 
+     return ODataResponse.fromResponse(writeEntry(uriInfo.getTargetEntitySet(), expandSelectTree, data, contentType))
+         .eTag(constructETag(entitySet, data)).build();
+   }
+ 
+   @Override
+   public ODataResponse updateEntity(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final boolean merge, final String contentType) throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+     final EdmEntityType entityType = entitySet.getEntityType();
+     final EntityProviderReadProperties properties = EntityProviderReadProperties.init()
+         .mergeSemantic(merge)
+         .addTypeMappings(getStructuralTypeTypeMap(data, entityType))
+         .build();
+     final ODataEntry entryValues = parseEntry(entitySet, content, requestContentType, properties);
+ 
+     setStructuralTypeValuesFromMap(data, entityType, entryValues.getProperties(), merge);
+ 
+     return ODataResponse.newBuilder().eTag(constructETag(entitySet, data)).build();
+   }
+ 
+   @Override
+   public ODataResponse readEntityLink(final GetEntityLinkUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     // if (!appliesFilter(data, uriInfo.getFilter()))
+     if (data == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+ 
+     Map<String, Object> values = new HashMap<String, Object>();
+     for (final EdmProperty property : entitySet.getEntityType().getKeyProperties()) {
+       values.put(property.getName(), valueAccess.getPropertyValue(data, property));
+     }
+ 
+     ODataContext context = getContext();
+     final EntityProviderWriteProperties entryProperties = EntityProviderWriteProperties
+         .serviceRoot(context.getPathInfo().getServiceRoot())
+         .build();
+ 
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeLink");
+ 
+     final ODataResponse response = EntityProvider.writeLink(contentType, entitySet, values, entryProperties);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return ODataResponse.fromResponse(response).build();
+   }
+ 
+   @Override
+   public ODataResponse existsEntityLink(final GetEntityLinkCountUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     return existsEntity((GetEntityCountUriInfo) uriInfo, contentType);
+   }
+ 
+   @Override
+   public ODataResponse deleteEntityLink(final DeleteUriInfo uriInfo, final String contentType) throws ODataException {
+     final List<NavigationSegment> navigationSegments = uriInfo.getNavigationSegments();
+     final List<NavigationSegment> previousSegments = navigationSegments.subList(0, navigationSegments.size() - 1);
+ 
+     final Object sourceData = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         previousSegments);
+ 
+     final EdmEntitySet entitySet = previousSegments.isEmpty() ?
+         uriInfo.getStartEntitySet() : previousSegments.get(previousSegments.size() - 1).getEntitySet();
+     final EdmEntitySet targetEntitySet = uriInfo.getTargetEntitySet();
+     final Map<String, Object> keys = mapKey(uriInfo.getTargetKeyPredicates());
+ 
+     final Object targetData = dataSource.readRelatedData(entitySet, sourceData, targetEntitySet, keys);
+ 
+     // if (!appliesFilter(targetData, uriInfo.getFilter()))
+     if (targetData == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     dataSource.deleteRelation(entitySet, sourceData, targetEntitySet, keys);
+ 
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse createEntityLink(final PostUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     final List<NavigationSegment> navigationSegments = uriInfo.getNavigationSegments();
+     final List<NavigationSegment> previousSegments = navigationSegments.subList(0, navigationSegments.size() - 1);
+ 
+     final Object sourceData = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         previousSegments);
+ 
+     final EdmEntitySet entitySet = previousSegments.isEmpty() ?
+         uriInfo.getStartEntitySet() : previousSegments.get(previousSegments.size() - 1).getEntitySet();
+     final EdmEntitySet targetEntitySet = uriInfo.getTargetEntitySet();
+ 
+     final Map<String, Object> targetKeys = parseLink(targetEntitySet, content, requestContentType);
+ 
+     dataSource.writeRelation(entitySet, sourceData, targetEntitySet, targetKeys);
+ 
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse updateEntityLink(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     final List<NavigationSegment> navigationSegments = uriInfo.getNavigationSegments();
+     final List<NavigationSegment> previousSegments = navigationSegments.subList(0, navigationSegments.size() - 1);
+ 
+     final Object sourceData = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         previousSegments);
+ 
+     final EdmEntitySet entitySet = previousSegments.isEmpty() ?
+         uriInfo.getStartEntitySet() : previousSegments.get(previousSegments.size() - 1).getEntitySet();
+     final EdmEntitySet targetEntitySet = uriInfo.getTargetEntitySet();
+     final Map<String, Object> keys = mapKey(uriInfo.getTargetKeyPredicates());
+ 
+     final Object targetData = dataSource.readRelatedData(entitySet, sourceData, targetEntitySet, keys);
+ 
+     if (!appliesFilter(targetData, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     dataSource.deleteRelation(entitySet, sourceData, targetEntitySet, keys);
+ 
+     final Map<String, Object> newKeys = parseLink(targetEntitySet, content, requestContentType);
+ 
+     dataSource.writeRelation(entitySet, sourceData, targetEntitySet, newKeys);
+ 
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse readEntityComplexProperty(final GetComplexPropertyUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     // if (!appliesFilter(data, uriInfo.getFilter()))
+     if (data == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+     final Object value = property.isSimple() ?
+         property.getMapping() == null || property.getMapping().getMimeType() == null ?
+             getPropertyValue(data, propertyPath) : getSimpleTypeValueMap(data, propertyPath) :
+         getStructuralTypeValueMap(getPropertyValue(data, propertyPath), (EdmStructuralType) property.getType());
+ 
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeProperty");
+ 
+     final ODataResponse response = EntityProvider.writeProperty(contentType, property, value);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return ODataResponse.fromResponse(response).eTag(constructETag(uriInfo.getTargetEntitySet(), data)).build();
+   }
+ 
+   @Override
+   public ODataResponse readEntitySimpleProperty(final GetSimplePropertyUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     return readEntityComplexProperty((GetComplexPropertyUriInfo) uriInfo, contentType);
+   }
+ 
+   @Override
+   public ODataResponse readEntitySimplePropertyValue(final GetSimplePropertyUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     // if (!appliesFilter(data, uriInfo.getFilter()))
+     if (data == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+     final Object value = property.getMapping() == null || property.getMapping().getMimeType() == null ?
+         getPropertyValue(data, propertyPath) : getSimpleTypeValueMap(data, propertyPath);
+ 
+     return ODataResponse.fromResponse(EntityProvider.writePropertyValue(property, value)).eTag(
+         constructETag(uriInfo.getTargetEntitySet(), data)).build();
+   }
+ 
+   @Override
+   public ODataResponse deleteEntitySimplePropertyValue(final DeleteUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (data == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+ 
+     data = getPropertyValue(data, propertyPath.subList(0, propertyPath.size() - 1));
+     valueAccess.setPropertyValue(data, property, null);
+     valueAccess.setMappingValue(data, property.getMapping(), null);
+ 
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse updateEntityComplexProperty(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final boolean merge, final String contentType) throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+ 
+     data = getPropertyValue(data, propertyPath.subList(0, propertyPath.size() - 1));
+ 
+     ODataContext context = getContext();
+     int timingHandle = context.startRuntimeMeasurement("EntityConsumer", "readProperty");
+ 
+     Map<String, Object> values;
+     try {
+       values =
+           EntityProvider.readProperty(requestContentType, property, content, EntityProviderReadProperties.init()
+               .mergeSemantic(merge).build());
+     } catch (final EntityProviderException e) {
+       throw new ODataBadRequestException(ODataBadRequestException.BODY, e);
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     final Object value = values.get(property.getName());
+     if (property.isSimple()) {
+       valueAccess.setPropertyValue(data, property, value);
+     } else {
+       @SuppressWarnings("unchecked")
+       final Map<String, Object> propertyValue = (Map<String, Object>) value;
+       setStructuralTypeValuesFromMap(valueAccess.getPropertyValue(data, property),
+           (EdmStructuralType) property.getType(), propertyValue, merge);
+     }
+ 
+     return ODataResponse.newBuilder().eTag(constructETag(uriInfo.getTargetEntitySet(), data)).build();
+   }
+ 
+   @Override
+   public ODataResponse updateEntitySimpleProperty(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     return updateEntityComplexProperty(uriInfo, content, requestContentType, false, contentType);
+   }
+ 
+   @Override
+   public ODataResponse updateEntitySimplePropertyValue(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final List<EdmProperty> propertyPath = uriInfo.getPropertyPath();
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+ 
+     data = getPropertyValue(data, propertyPath.subList(0, propertyPath.size() - 1));
+ 
+     ODataContext context = getContext();
+     int timingHandle = context.startRuntimeMeasurement("EntityConsumer", "readPropertyValue");
+ 
+     Object value;
+     try {
+       value = EntityProvider.readPropertyValue(property, content);
+     } catch (final EntityProviderException e) {
+       throw new ODataBadRequestException(ODataBadRequestException.BODY, e);
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     valueAccess.setPropertyValue(data, property, value);
+     valueAccess.setMappingValue(data, property.getMapping(), requestContentType);
+ 
+     return ODataResponse.newBuilder().eTag(constructETag(uriInfo.getTargetEntitySet(), data)).build();
+   }
+ 
+   @Override
+   public ODataResponse readEntityMedia(final GetMediaResourceUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+     final BinaryData binaryData = dataSource.readBinaryData(entitySet, data);
+     if (binaryData == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     final String mimeType = binaryData.getMimeType() == null ?
+         HttpContentType.APPLICATION_OCTET_STREAM : binaryData.getMimeType();
+ 
+     return ODataResponse.fromResponse(EntityProvider.writeBinary(mimeType, binaryData.getData())).eTag(
+         constructETag(entitySet, data)).build();
+   }
+ 
+   @Override
+   public ODataResponse deleteEntityMedia(final DeleteUriInfo uriInfo, final String contentType) throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (data == null) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     dataSource.writeBinaryData(uriInfo.getTargetEntitySet(), data, new BinaryData(null, null));
+ 
+     return ODataResponse.newBuilder().build();
+   }
+ 
+   @Override
+   public ODataResponse updateEntityMedia(final PutMergePatchUriInfo uriInfo, final InputStream content,
+       final String requestContentType, final String contentType) throws ODataException {
+     final Object data = retrieveData(
+         uriInfo.getStartEntitySet(),
+         uriInfo.getKeyPredicates(),
+         uriInfo.getFunctionImport(),
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         uriInfo.getNavigationSegments());
+ 
+     if (!appliesFilter(data, uriInfo.getFilter())) {
+       throw new ODataNotFoundException(ODataNotFoundException.ENTITY);
+     }
+ 
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "readBinary");
+ 
+     final byte[] value = EntityProvider.readBinary(content);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     final EdmEntitySet entitySet = uriInfo.getTargetEntitySet();
+     dataSource.writeBinaryData(entitySet, data, new BinaryData(value, requestContentType));
+ 
+     return ODataResponse.newBuilder().eTag(constructETag(entitySet, data)).build();
+   }
+ 
+   @Override
+   public ODataResponse executeFunctionImport(final GetFunctionImportUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     final EdmFunctionImport functionImport = uriInfo.getFunctionImport();
+     final EdmType type = functionImport.getReturnType().getType();
+ 
+     final Object data = dataSource.readData(
+         functionImport,
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         null);
+ 
+     if (data == null) {
+       throw new ODataNotFoundException(ODataHttpException.COMMON);
+     }
+ 
+     Object value;
+     if (type.getKind() == EdmTypeKind.SIMPLE) {
+       value = type == EdmSimpleTypeKind.Binary.getEdmSimpleTypeInstance() ?
+           ((BinaryData) data).getData() : data;
+     } else if (functionImport.getReturnType().getMultiplicity() == EdmMultiplicity.MANY) {
+       List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
+       for (final Object typeData : (List<?>) data) {
+         values.add(getStructuralTypeValueMap(typeData, (EdmStructuralType) type));
+       }
+       value = values;
+     } else {
+       value = getStructuralTypeValueMap(data, (EdmStructuralType) type);
+     }
+ 
+     ODataContext context = getContext();
+ 
+     final EntityProviderWriteProperties entryProperties = EntityProviderWriteProperties
+         .serviceRoot(context.getPathInfo().getServiceRoot()).build();
+ 
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeFunctionImport");
+ 
+     final ODataResponse response =
+         EntityProvider.writeFunctionImport(contentType, functionImport, value, entryProperties);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return ODataResponse.fromResponse(response).build();
+   }
+ 
+   @Override
+   public ODataResponse executeFunctionImportValue(final GetFunctionImportUriInfo uriInfo, final String contentType)
+       throws ODataException {
+     final EdmFunctionImport functionImport = uriInfo.getFunctionImport();
+     final EdmSimpleType type = (EdmSimpleType) functionImport.getReturnType().getType();
+ 
+     final Object data = dataSource.readData(
+         functionImport,
+         mapFunctionParameters(uriInfo.getFunctionImportParameters()),
+         null);
+ 
+     if (data == null) {
+       throw new ODataNotFoundException(ODataHttpException.COMMON);
+     }
+ 
+     ODataResponse response;
+     if (type == EdmSimpleTypeKind.Binary.getEdmSimpleTypeInstance()) {
+       response = EntityProvider.writeBinary(((BinaryData) data).getMimeType(), ((BinaryData) data).getData());
+     } else {
+       final String value = type.valueToString(data, EdmLiteralKind.DEFAULT, null);
+       response = EntityProvider.writeText(value == null ? "" : value);
+     }
+     return ODataResponse.fromResponse(response).build();
+   }
+ 
+   private static Map<String, Object> mapKey(final List<KeyPredicate> keys) throws EdmException {
+     Map<String, Object> keyMap = new HashMap<String, Object>();
+     for (final KeyPredicate key : keys) {
+       final EdmProperty property = key.getProperty();
+       final EdmSimpleType type = (EdmSimpleType) property.getType();
+       keyMap.put(property.getName(), type.valueOfString(key.getLiteral(), EdmLiteralKind.DEFAULT, property.getFacets(),
+           type.getDefaultType()));
+     }
+     return keyMap;
+   }
+ 
+   private static Map<String, Object> mapFunctionParameters(final Map<String, EdmLiteral> functionImportParameters)
+       throws EdmSimpleTypeException {
+     if (functionImportParameters == null) {
+       return Collections.emptyMap();
+     } else {
+       Map<String, Object> parameterMap = new HashMap<String, Object>();
+       for (final String parameterName : functionImportParameters.keySet()) {
+         final EdmLiteral literal = functionImportParameters.get(parameterName);
+         final EdmSimpleType type = literal.getType();
+         parameterMap.put(parameterName, type.valueOfString(literal.getLiteral(), EdmLiteralKind.DEFAULT, null, type
+             .getDefaultType()));
+       }
+       return parameterMap;
+     }
+   }
+ 
+   private Object retrieveData(final EdmEntitySet startEntitySet, final List<KeyPredicate> keyPredicates,
+       final EdmFunctionImport functionImport, final Map<String, Object> functionImportParameters,
+       final List<NavigationSegment> navigationSegments) throws ODataException {
+     Object data;
+     final Map<String, Object> keys = mapKey(keyPredicates);
+ 
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement(getClass().getSimpleName(), "retrieveData");
+ 
+     try {
+       data = functionImport == null ?
+           keys.isEmpty() ?
+               dataSource.readData(startEntitySet) : dataSource.readData(startEntitySet, keys) :
+           dataSource.readData(functionImport, functionImportParameters, keys);
+   
+       EdmEntitySet currentEntitySet =
+           functionImport == null ? startEntitySet : functionImport.getEntitySet();
+       for (NavigationSegment navigationSegment : navigationSegments) {
+         data = dataSource.readRelatedData(
+             currentEntitySet,
+             data,
+             navigationSegment.getEntitySet(),
+             mapKey(navigationSegment.getKeyPredicates()));
+         currentEntitySet = navigationSegment.getEntitySet();
+       }
+     } finally {
+       context.stopRuntimeMeasurement(timingHandle);
+     }
+     return data;
+   }
+ 
+   private <T> String constructETag(final EdmEntitySet entitySet, final T data) throws ODataException {
+     final EdmEntityType entityType = entitySet.getEntityType();
+     String eTag = null;
+     for (final String propertyName : entityType.getPropertyNames()) {
+       final EdmProperty property = (EdmProperty) entityType.getProperty(propertyName);
+       if (property.getFacets() != null && property.getFacets().getConcurrencyMode() == EdmConcurrencyMode.Fixed) {
+         final EdmSimpleType type = (EdmSimpleType) property.getType();
+         final String component = type.valueToString(valueAccess.getPropertyValue(data, property),
+             EdmLiteralKind.DEFAULT, property.getFacets());
+         eTag = eTag == null ? component : eTag + Edm.DELIMITER + component;
+       }
+     }
+     return eTag == null ? null : "W/\"" + eTag + "\"";
+   }
+ 
+   private <T> Map<String, ODataCallback> getCallbacks(final T data, final EdmEntityType entityType)
+       throws EdmException {
+     final List<String> navigationPropertyNames = entityType.getNavigationPropertyNames();
+     if (navigationPropertyNames.isEmpty()) {
+       return null;
+     } else {
+       final WriteCallback callback = new WriteCallback(data);
+       Map<String, ODataCallback> callbacks = new HashMap<String, ODataCallback>();
+       for (final String name : navigationPropertyNames) {
+         callbacks.put(name, callback);
+       }
+       return callbacks;
+     }
+   }
+ 
+   private class WriteCallback implements OnWriteEntryContent, OnWriteFeedContent {
+     private final Object data;
+ 
+     private <T> WriteCallback(final T data) {
+       this.data = data;
+     }
+ 
+     @Override
+     public WriteFeedCallbackResult retrieveFeedResult(final WriteFeedCallbackContext context)
+         throws ODataApplicationException {
+       try {
+         final EdmEntityType entityType =
+             context.getSourceEntitySet().getRelatedEntitySet(context.getNavigationProperty()).getEntityType();
+         List<Map<String, Object>> values = new ArrayList<Map<String, Object>>();
+         Object relatedData = null;
+         try {
+           relatedData = readRelatedData(context);
+           for (final Object entryData : (List<?>) relatedData) {
+             values.add(getStructuralTypeValueMap(entryData, entityType));
+           }
+         } catch (final ODataNotFoundException e) {
+           values.clear();
+         }
+         WriteFeedCallbackResult result = new WriteFeedCallbackResult();
+         result.setFeedData(values);
+         EntityProviderWriteProperties inlineProperties =
+             EntityProviderWriteProperties.serviceRoot(getContext().getPathInfo().getServiceRoot()).callbacks(
+                 getCallbacks(relatedData, entityType)).expandSelectTree(context.getCurrentExpandSelectTreeNode())
+                 .selfLink(context.getSelfLink()).build();
+         result.setInlineProperties(inlineProperties);
+         return result;
+       } catch (final ODataException e) {
+         throw new ODataApplicationException(e.getLocalizedMessage(), Locale.ROOT, e);
+       }
+     }
+ 
+     @Override
+     public WriteEntryCallbackResult retrieveEntryResult(final WriteEntryCallbackContext context)
+         throws ODataApplicationException {
+       try {
+         final EdmEntityType entityType =
+             context.getSourceEntitySet().getRelatedEntitySet(context.getNavigationProperty()).getEntityType();
+         WriteEntryCallbackResult result = new WriteEntryCallbackResult();
+         Object relatedData;
+         try {
+           relatedData = readRelatedData(context);
+         } catch (final ODataNotFoundException e) {
+           relatedData = null;
+         }
+ 
+         if (relatedData == null) {
+           result.setEntryData(Collections.<String, Object> emptyMap());
+         } else {
+           result.setEntryData(getStructuralTypeValueMap(relatedData, entityType));
+ 
+           EntityProviderWriteProperties inlineProperties =
+               EntityProviderWriteProperties.serviceRoot(getContext().getPathInfo().getServiceRoot()).callbacks(
+                   getCallbacks(relatedData, entityType)).expandSelectTree(context.getCurrentExpandSelectTreeNode())
+                   .build();
+           result.setInlineProperties(inlineProperties);
+         }
+         return result;
+       } catch (final ODataException e) {
+         throw new ODataApplicationException(e.getLocalizedMessage(), Locale.ROOT, e);
+       }
+     }
+ 
+     private Object readRelatedData(final WriteCallbackContext context) throws ODataException {
+       final EdmEntitySet entitySet = context.getSourceEntitySet();
+       return dataSource.readRelatedData(
+           entitySet,
+           data instanceof List ? readEntryData((List<?>) data, entitySet.getEntityType(), context
+               .extractKeyFromEntryData()) : data,
+           entitySet.getRelatedEntitySet(context.getNavigationProperty()),
+           Collections.<String, Object> emptyMap());
+     }
+ 
+     private <T> T readEntryData(final List<T> data, final EdmEntityType entityType, final Map<String, Object> key)
+         throws ODataException {
+       for (final T entryData : data) {
+         boolean found = true;
+         for (final EdmProperty keyProperty : entityType.getKeyProperties()) {
+           if (!valueAccess.getPropertyValue(entryData, keyProperty).equals(key.get(keyProperty.getName()))) {
+             found = false;
+             break;
+           }
+         }
+         if (found) {
+           return entryData;
+         }
+       }
+       return null;
+     }
+   }
+ 
+   private <T> ODataResponse writeEntry(final EdmEntitySet entitySet, final ExpandSelectTreeNode expandSelectTree,
+       final T data, final String contentType) throws ODataException, EntityProviderException {
+     final EdmEntityType entityType = entitySet.getEntityType();
+     final Map<String, Object> values = getStructuralTypeValueMap(data, entityType);
+ 
+     ODataContext context = getContext();
+     EntityProviderWriteProperties writeProperties = EntityProviderWriteProperties
+         .serviceRoot(context.getPathInfo().getServiceRoot())
+         .expandSelectTree(expandSelectTree)
+         .callbacks(getCallbacks(data, entityType))
+         .build();
+ 
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "writeEntry");
+ 
+     final ODataResponse response = EntityProvider.writeEntry(contentType, entitySet, values, writeProperties);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return response;
+   }
+ 
+   private ODataEntry parseEntry(final EdmEntitySet entitySet, final InputStream content,
+       final String requestContentType, final EntityProviderReadProperties properties) throws ODataBadRequestException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement("EntityConsumer", "readEntry");
+ 
+     ODataEntry entryValues;
+     try {
+       entryValues = EntityProvider.readEntry(requestContentType, entitySet, content, properties);
+     } catch (final EntityProviderException e) {
+       throw new ODataBadRequestException(ODataBadRequestException.BODY, e);
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return entryValues;
+   }
+ 
+   private Map<String, Object> parseLink(final EdmEntitySet entitySet, final InputStream content,
+       final String contentType) throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement("EntityProvider", "readLink");
+ 
+     final String uriString = EntityProvider.readLink(contentType, entitySet, content);
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     final Map<String, Object> targetKeys = parseLinkUri(entitySet, uriString);
+     if (targetKeys == null) {
+       throw new ODataBadRequestException(ODataBadRequestException.BODY);
+     }
+     return targetKeys;
+   }
+ 
+   private Map<String, Object> parseLinkUri(final EdmEntitySet targetEntitySet, final String uriString)
+       throws EdmException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement("UriParser", "getKeyPredicatesFromEntityLink");
+ 
+     List<KeyPredicate> key = null;
+     try {
+       key = UriParser.getKeyPredicatesFromEntityLink(targetEntitySet, uriString,
+           context.getPathInfo().getServiceRoot());
+     } catch (ODataException e) {
+       // We don't understand the link target. This could also be seen as an error.
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return key == null ? null : mapKey(key);
+   }
+ 
+   private <T> void createInlinedEntities(final EdmEntitySet entitySet, final T data, final ODataEntry entryValues)
+       throws ODataException {
+     final EdmEntityType entityType = entitySet.getEntityType();
+     for (final String navigationPropertyName : entityType.getNavigationPropertyNames()) {
+ 
+       final EdmNavigationProperty navigationProperty =
+           (EdmNavigationProperty) entityType.getProperty(navigationPropertyName);
+       final EdmEntitySet relatedEntitySet = entitySet.getRelatedEntitySet(navigationProperty);
+       final EdmEntityType relatedEntityType = relatedEntitySet.getEntityType();
+ 
+       final Object relatedValue = entryValues.getProperties().get(navigationPropertyName);
+       if (relatedValue == null) {
+         for (final String uriString : entryValues.getMetadata().getAssociationUris(navigationPropertyName)) {
+           final Map<String, Object> key = parseLinkUri(relatedEntitySet, uriString);
+           if (key != null) {
+             dataSource.writeRelation(entitySet, data, relatedEntitySet, key);
+           }
+         }
+ 
+       } else {
+         if (relatedValue instanceof ODataFeed) {
+           ODataFeed feed = (ODataFeed) relatedValue;
+           final List<ODataEntry> relatedValueList = feed.getEntries();
+           for (final ODataEntry relatedValues : relatedValueList) {
+             Object relatedData = dataSource.newDataObject(relatedEntitySet);
+             setStructuralTypeValuesFromMap(relatedData, relatedEntityType, relatedValues.getProperties(), false);
+             dataSource.createData(relatedEntitySet, relatedData);
+             dataSource.writeRelation(entitySet, data, relatedEntitySet, getStructuralTypeValueMap(relatedData,
+                 relatedEntityType));
+             createInlinedEntities(relatedEntitySet, relatedData, relatedValues);
+           }
+         } else if (relatedValue instanceof ODataEntry) {
+           final ODataEntry relatedValueEntry = (ODataEntry) relatedValue;
+           Object relatedData = dataSource.newDataObject(relatedEntitySet);
+           setStructuralTypeValuesFromMap(relatedData, relatedEntityType, relatedValueEntry.getProperties(), false);
+           dataSource.createData(relatedEntitySet, relatedData);
+           dataSource.writeRelation(entitySet, data, relatedEntitySet, getStructuralTypeValueMap(relatedData,
+               relatedEntityType));
+           createInlinedEntities(relatedEntitySet, relatedData, relatedValueEntry);
+         } else {
+           throw new ODataException("Unexpected class for a related value: " + relatedValue.getClass().getSimpleName());
+         }
+ 
+       }
+     }
+   }
+ 
+   private <T> Integer applySystemQueryOptions(final EdmEntitySet entitySet, final List<T> data,
+       final FilterExpression filter, final InlineCount inlineCount, final OrderByExpression orderBy,
+       final String skipToken, final Integer skip, final Integer top) throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement(getClass().getSimpleName(), "applySystemQueryOptions");
+ 
+     if (filter != null) {
+       // Remove all elements the filter does not apply for.
+       // A for-each loop would not work with "remove", see Java documentation.
+       for (Iterator<T> iterator = data.iterator(); iterator.hasNext();) {
+         if (!appliesFilter(iterator.next(), filter)) {
+           iterator.remove();
+         }
+       }
+     }
+ 
+     final Integer count = inlineCount == InlineCount.ALLPAGES ? data.size() : null;
+ 
+     if (orderBy != null) {
+       sort(data, orderBy);
+     } else if (skipToken != null || skip != null || top != null) {
+       sortInDefaultOrder(entitySet, data);
+     }
+ 
+     if (skipToken != null) {
+       while (!data.isEmpty() && !getSkipToken(entitySet, data.get(0)).equals(skipToken)) {
+         data.remove(0);
+       }
+     }
+ 
+     if (skip != null) {
+       if (skip >= data.size()) {
+         data.clear();
+       } else {
+         for (int i = 0; i < skip; i++) {
+           data.remove(0);
+         }
+       }
+     }
+ 
+     if (top != null) {
+       while (data.size() > top) {
+         data.remove(top.intValue());
+       }
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return count;
+   }
+ 
+   private <T> void sort(final List<T> data, final OrderByExpression orderBy) {
+     Collections.sort(data, new Comparator<T>() {
+       @Override
+       public int compare(final T entity1, final T entity2) {
+         try {
+           int result = 0;
+           for (final OrderExpression expression : orderBy.getOrders()) {
+             String first = evaluateExpression(entity1, expression.getExpression());
+             String second = evaluateExpression(entity2, expression.getExpression());
+ 
+             if (first != null && second != null) {
+               result = first.compareTo(second);
+             } else if (first == null && second != null) {
+               result = 1;
+             } else if (first != null && second == null) {
+               result = -1;
+             }
+ 
+             if (expression.getSortOrder() == SortOrder.desc) {
+               result = -result;
+             }
+ 
+             if (result != 0) {
+               break;
+             }
+           }
+           return result;
+         } catch (final ODataException e) {
+           return 0;
+         }
+       }
+     });
+   }
+ 
+   private <T> void sortInDefaultOrder(final EdmEntitySet entitySet, final List<T> data) {
+     Collections.sort(data, new Comparator<T>() {
+       @Override
+       public int compare(final T entity1, final T entity2) {
+         try {
+           return getSkipToken(entitySet, entity1).compareTo(getSkipToken(entitySet, entity2));
+         } catch (final ODataException e) {
+           return 0;
+         }
+       }
+     });
+   }
+ 
+   private <T> boolean appliesFilter(final T data, final FilterExpression filter) throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement(getClass().getSimpleName(), "appliesFilter");
+ 
+     try {
+       return data != null && (filter == null || evaluateExpression(data, filter.getExpression()).equals("true"));
+     } catch (final RuntimeException e) {
+       return false;
+     } finally {
+       context.stopRuntimeMeasurement(timingHandle);
+     }
+   }
+ 
+   private <T> String evaluateExpression(final T data, final CommonExpression expression) throws ODataException {
+     switch (expression.getKind()) {
+     case UNARY:
+       final UnaryExpression unaryExpression = (UnaryExpression) expression;
+       final String operand = evaluateExpression(data, unaryExpression.getOperand());
+ 
+       switch (unaryExpression.getOperator()) {
+       case NOT:
+         return Boolean.toString(!Boolean.parseBoolean(operand));
+       case MINUS:
+         return operand.startsWith("-") ? operand.substring(1) : "-" + operand;
+       default:
+         throw new ODataNotImplementedException();
+       }
+ 
+     case BINARY:
+       final BinaryExpression binaryExpression = (BinaryExpression) expression;
+       final EdmSimpleType type = (EdmSimpleType) binaryExpression.getLeftOperand().getEdmType();
+       final String left = evaluateExpression(data, binaryExpression.getLeftOperand());
+       final String right = evaluateExpression(data, binaryExpression.getRightOperand());
+ 
+       switch (binaryExpression.getOperator()) {
+       case ADD:
+         if (binaryExpression.getEdmType() == EdmSimpleTypeKind.Decimal.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Double.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Single.getEdmSimpleTypeInstance()) {
+           return Double.toString(Double.valueOf(left) + Double.valueOf(right));
+         } else {
+           return Long.toString(Long.valueOf(left) + Long.valueOf(right));
+         }
+       case SUB:
+         if (binaryExpression.getEdmType() == EdmSimpleTypeKind.Decimal.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Double.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Single.getEdmSimpleTypeInstance()) {
+           return Double.toString(Double.valueOf(left) - Double.valueOf(right));
+         } else {
+           return Long.toString(Long.valueOf(left) - Long.valueOf(right));
+         }
+       case MUL:
+         if (binaryExpression.getEdmType() == EdmSimpleTypeKind.Decimal.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Double.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Single.getEdmSimpleTypeInstance()) {
+           return Double.toString(Double.valueOf(left) * Double.valueOf(right));
+         } else {
+           return Long.toString(Long.valueOf(left) * Long.valueOf(right));
+         }
+       case DIV:
+         final String number = Double.toString(Double.valueOf(left) / Double.valueOf(right));
+         return number.endsWith(".0") ? number.replace(".0", "") : number;
+       case MODULO:
+         if (binaryExpression.getEdmType() == EdmSimpleTypeKind.Decimal.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Double.getEdmSimpleTypeInstance()
+             || binaryExpression.getEdmType() == EdmSimpleTypeKind.Single.getEdmSimpleTypeInstance()) {
+           return Double.toString(Double.valueOf(left) % Double.valueOf(right));
+         } else {
+           return Long.toString(Long.valueOf(left) % Long.valueOf(right));
+         }
+       case AND:
+         return Boolean.toString(left.equals("true") && right.equals("true"));
+       case OR:
+         return Boolean.toString(left.equals("true") || right.equals("true"));
+       case EQ:
+         return Boolean.toString(left.equals(right));
+       case NE:
+         return Boolean.toString(!left.equals(right));
+       case LT:
+         if (type == EdmSimpleTypeKind.String.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTime.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTimeOffset.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Guid.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Time.getEdmSimpleTypeInstance()) {
+           return Boolean.toString(left.compareTo(right) < 0);
+         } else {
+           return Boolean.toString(Double.valueOf(left) < Double.valueOf(right));
+         }
+       case LE:
+         if (type == EdmSimpleTypeKind.String.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTime.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTimeOffset.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Guid.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Time.getEdmSimpleTypeInstance()) {
+           return Boolean.toString(left.compareTo(right) <= 0);
+         } else {
+           return Boolean.toString(Double.valueOf(left) <= Double.valueOf(right));
+         }
+       case GT:
+         if (type == EdmSimpleTypeKind.String.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTime.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTimeOffset.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Guid.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Time.getEdmSimpleTypeInstance()) {
+           return Boolean.toString(left.compareTo(right) > 0);
+         } else {
+           return Boolean.toString(Double.valueOf(left) > Double.valueOf(right));
+         }
+       case GE:
+         if (type == EdmSimpleTypeKind.String.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTime.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.DateTimeOffset.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Guid.getEdmSimpleTypeInstance()
+             || type == EdmSimpleTypeKind.Time.getEdmSimpleTypeInstance()) {
+           return Boolean.toString(left.compareTo(right) >= 0);
+         } else {
+           return Boolean.toString(Double.valueOf(left) >= Double.valueOf(right));
+         }
+       case PROPERTY_ACCESS:
+         throw new ODataNotImplementedException();
+       default:
+         throw new ODataNotImplementedException();
+       }
+ 
+     case PROPERTY:
+       final EdmProperty property = (EdmProperty) ((PropertyExpression) expression).getEdmProperty();
+       final EdmSimpleType propertyType = (EdmSimpleType) property.getType();
+       return propertyType.valueToString(valueAccess.getPropertyValue(data, property), EdmLiteralKind.DEFAULT,
+           property.getFacets());
+ 
+     case MEMBER:
+       final MemberExpression memberExpression = (MemberExpression) expression;
+       final PropertyExpression propertyExpression = (PropertyExpression) memberExpression.getProperty();
+       final EdmProperty memberProperty = (EdmProperty) propertyExpression.getEdmProperty();
+       final EdmSimpleType memberType = (EdmSimpleType) memberExpression.getEdmType();
+       List<EdmProperty> propertyPath = new ArrayList<EdmProperty>();
+       CommonExpression currentExpression = memberExpression;
+       while (currentExpression != null) {
+         final PropertyExpression currentPropertyExpression =
+             (PropertyExpression) (currentExpression.getKind() == ExpressionKind.MEMBER ?
+                 ((MemberExpression) currentExpression).getProperty() : currentExpression);
+         final EdmTyped currentProperty = currentPropertyExpression.getEdmProperty();
+         final EdmTypeKind kind = currentProperty.getType().getKind();
+         if (kind == EdmTypeKind.SIMPLE || kind == EdmTypeKind.COMPLEX) {
+           propertyPath.add(0, (EdmProperty) currentProperty);
+         } else {
+           throw new ODataNotImplementedException();
+         }
+         currentExpression =
+             currentExpression.getKind() == ExpressionKind.MEMBER ? ((MemberExpression) currentExpression).getPath()
+                 : null;
+       }
+       return memberType.valueToString(getPropertyValue(data, propertyPath), EdmLiteralKind.DEFAULT, memberProperty
+           .getFacets());
+ 
+     case LITERAL:
+       final LiteralExpression literal = (LiteralExpression) expression;
+       final EdmSimpleType literalType = (EdmSimpleType) literal.getEdmType();
+       return literalType.valueToString(literalType.valueOfString(literal.getUriLiteral(), EdmLiteralKind.URI, null,
+           literalType.getDefaultType()),
+           EdmLiteralKind.DEFAULT, null);
+ 
+     case METHOD:
+       final MethodExpression methodExpression = (MethodExpression) expression;
+       final String first = evaluateExpression(data, methodExpression.getParameters().get(0));
+       final String second = methodExpression.getParameterCount() > 1 ?
+           evaluateExpression(data, methodExpression.getParameters().get(1)) : null;
+       final String third = methodExpression.getParameterCount() > 2 ?
+           evaluateExpression(data, methodExpression.getParameters().get(2)) : null;
+ 
+       switch (methodExpression.getMethod()) {
+       case ENDSWITH:
+         return Boolean.toString(first.endsWith(second));
+       case INDEXOF:
+         return Integer.toString(first.indexOf(second));
+       case STARTSWITH:
+         return Boolean.toString(first.startsWith(second));
+       case TOLOWER:
+         return first.toLowerCase(Locale.ROOT);
+       case TOUPPER:
+         return first.toUpperCase(Locale.ROOT);
+       case TRIM:
+         return first.trim();
+       case SUBSTRING:
+         final int offset = Integer.parseInt(second);
+         return first.substring(offset, offset + Integer.parseInt(third));
+       case SUBSTRINGOF:
+         return Boolean.toString(second.contains(first));
+       case CONCAT:
+         return first + second;
+       case LENGTH:
+         return Integer.toString(first.length());
+       case YEAR:
+         return String.valueOf(Integer.parseInt(first.substring(0, 4)));
+       case MONTH:
+         return String.valueOf(Integer.parseInt(first.substring(5, 7)));
+       case DAY:
+         return String.valueOf(Integer.parseInt(first.substring(8, 10)));
+       case HOUR:
+         return String.valueOf(Integer.parseInt(first.substring(11, 13)));
+       case MINUTE:
+         return String.valueOf(Integer.parseInt(first.substring(14, 16)));
+       case SECOND:
+         return String.valueOf(Integer.parseInt(first.substring(17, 19)));
+       case ROUND:
+         return Long.toString(Math.round(Double.valueOf(first)));
+       case FLOOR:
+         return Long.toString(Math.round(Math.floor(Double.valueOf(first))));
+       case CEILING:
+         return Long.toString(Math.round(Math.ceil(Double.valueOf(first))));
+       default:
+         throw new ODataNotImplementedException();
+       }
+ 
+     default:
+       throw new ODataNotImplementedException();
+     }
+   }
+ 
+   private <T> String getSkipToken(final EdmEntitySet entitySet, final T data) throws ODataException {
+     String skipToken = "";
+     for (final EdmProperty property : entitySet.getEntityType().getKeyProperties()) {
+       final EdmSimpleType type = (EdmSimpleType) property.getType();
+       skipToken = skipToken.concat(type.valueToString(valueAccess.getPropertyValue(data, property),
+           EdmLiteralKind.DEFAULT, property.getFacets()));
+     }
+     return skipToken;
+   }
+ 
+   private <T> Object getPropertyValue(final T data, final List<EdmProperty> propertyPath) throws ODataException {
+     Object dataObject = data;
+     for (final EdmProperty property : propertyPath) {
+       if (dataObject != null) {
+         dataObject = valueAccess.getPropertyValue(dataObject, property);
+       }
+     }
+     return dataObject;
+   }
+ 
+   private void handleMimeType(final Object data, final EdmMapping mapping, final Map<String, Object> valueMap)
+       throws ODataException {
+     final String mimeTypeName = mapping.getMimeType();
+     if (mimeTypeName != null) {
+       Object value = valueAccess.getMappingValue(data, mapping);
+       valueMap.put(mimeTypeName, value);
+       if (mapping.getMediaResourceMimeTypeKey() != null) {
+         valueMap.put(mapping.getMediaResourceMimeTypeKey(), value);
+       }
+     }
+   }
+ 
++
+   private <T> Map<String, Object> getSimpleTypeValueMap(final T data, final List<EdmProperty> propertyPath)
+       throws ODataException {
+     final EdmProperty property = propertyPath.get(propertyPath.size() - 1);
+     Map<String, Object> valueWithMimeType = new HashMap<String, Object>();
+     valueWithMimeType.put(property.getName(), getPropertyValue(data, propertyPath));
+ 
+     handleMimeType(data, property.getMapping(), valueWithMimeType);
 -
+     return valueWithMimeType;
+   }
+ 
+   private <T> Map<String, Object> getStructuralTypeValueMap(final T data, final EdmStructuralType type)
+       throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement(getClass().getSimpleName(), "getStructuralTypeValueMap");
+ 
+     Map<String, Object> valueMap = new HashMap<String, Object>();
+ 
+     EdmMapping mapping = type.getMapping();
+     if (mapping != null) {
+       handleMimeType(data, mapping, valueMap);
+     }
+ 
+     for (final String propertyName : type.getPropertyNames()) {
+       final EdmProperty property = (EdmProperty) type.getProperty(propertyName);
+       final Object value = valueAccess.getPropertyValue(data, property);
+ 
+       if (property.isSimple()) {
+         if (property.getMapping() == null || property.getMapping().getMimeType() == null) {
+           valueMap.put(propertyName, value);
+         } else {
+           // TODO: enable MIME type mapping outside the current subtree
+           valueMap.put(propertyName, getSimpleTypeValueMap(data, Arrays.asList(property)));
+         }
+       } else {
+         valueMap.put(propertyName, getStructuralTypeValueMap(value, (EdmStructuralType) property.getType()));
+       }
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return valueMap;
+   }
+ 
+   private <T> Map<String, Object> getStructuralTypeTypeMap(final T data, final EdmStructuralType type)
+       throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle = context.startRuntimeMeasurement(getClass().getSimpleName(), "getStructuralTypeTypeMap");
+ 
+     Map<String, Object> typeMap = new HashMap<String, Object>();
+     for (final String propertyName : type.getPropertyNames()) {
+       final EdmProperty property = (EdmProperty) type.getProperty(propertyName);
+       if (property.isSimple()) {
+         typeMap.put(propertyName, valueAccess.getPropertyType(data, property));
+       } else {
+         typeMap.put(propertyName, getStructuralTypeTypeMap(valueAccess.getPropertyValue(data, property),
+             (EdmStructuralType) property.getType()));
+       }
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+ 
+     return typeMap;
+   }
+ 
+   private <T> void setStructuralTypeValuesFromMap(final T data, final EdmStructuralType type,
+       final Map<String, Object> valueMap, final boolean merge) throws ODataException {
+     ODataContext context = getContext();
+     final int timingHandle =
+         context.startRuntimeMeasurement(getClass().getSimpleName(), "setStructuralTypeValuesFromMap");
+ 
+     for (final String propertyName : type.getPropertyNames()) {
+       final EdmProperty property = (EdmProperty) type.getProperty(propertyName);
+       if (type instanceof EdmEntityType && ((EdmEntityType) type).getKeyProperties().contains(property)) {
+         Object v = valueAccess.getPropertyValue(data, property);
+         if (v != null) {
+           continue;
+         }
+       }
+ 
+       if (!merge || valueMap != null && valueMap.containsKey(propertyName)) {
+         final Object value = valueMap == null ? null : valueMap.get(propertyName);
+         if (property.isSimple()) {
+           valueAccess.setPropertyValue(data, property, value);
+         } else {
+           @SuppressWarnings("unchecked")
+           final Map<String, Object> values = (Map<String, Object>) value;
+           setStructuralTypeValuesFromMap(valueAccess.getPropertyValue(data, property),
+               (EdmStructuralType) property.getType(), values, merge);
+         }
+       }
+     }
+ 
+     context.stopRuntimeMeasurement(timingHandle);
+   }
+ 
+   @Override
+   public ODataResponse executeBatch(final BatchHandler handler, final String contentType, final InputStream content)
+       throws ODataException {
+     ODataResponse batchResponse;
+     List<BatchResponsePart> batchResponseParts = new ArrayList<BatchResponsePart>();
+     PathInfo pathInfo = getContext().getPathInfo();
+     EntityProviderBatchProperties batchProperties = EntityProviderBatchProperties.init().pathInfo(pathInfo).build();
+     List<BatchRequestPart> batchParts = EntityProvider.parseBatchRequest(contentType, content, batchProperties);
+     for (BatchRequestPart batchPart : batchParts) {
+       batchResponseParts.add(handler.handleBatchPart(batchPart));
+     }
+     batchResponse = EntityProvider.writeBatchResponse(batchResponseParts);
+     return batchResponse;
+   }
+ 
+   @Override
+   public BatchResponsePart executeChangeSet(final BatchHandler handler, final List<ODataRequest> requests)
+       throws ODataException {
+     List<ODataResponse> responses = new ArrayList<ODataResponse>();
+     for (ODataRequest request : requests) {
+       ODataResponse response = handler.handleRequest(request);
+       if (response.getStatus().getStatusCode() >= HttpStatusCodes.BAD_REQUEST.getStatusCode()) {
+         // Rollback
+         List<ODataResponse> errorResponses = new ArrayList<ODataResponse>(1);
+         errorResponses.add(response);
+         return BatchResponsePart.responses(errorResponses).changeSet(false).build();
+       }
+       responses.add(response);
+     }
+     return BatchResponsePart.responses(responses).changeSet(true).build();
+   }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/80379ec1/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java
----------------------------------------------------------------------