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/06 17:52:45 UTC
[03/50] [abbrv] Poc for ListsDs and Processor
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/ListsProcessor.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/ListsProcessor.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/ListsProcessor.java
new file mode 100644
index 0000000..fcec484
--- /dev/null
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/ListsProcessor.java
@@ -0,0 +1,1625 @@
+/*******************************************************************************
+ * 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.core.annotation.processor;
+
+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.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.data.ListsDataSource;
+import org.apache.olingo.odata2.api.data.ListsDataSource.BinaryData;
+import org.apache.olingo.odata2.api.data.ValueAccess;
+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.processor.ODataSingleProcessor;
+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 ListsDataSource} for the
+ * actual data handling.
+ *
+ */
+public class ListsProcessor extends ODataSingleProcessor {
+
+ // TODO: Paging size should be configurable.
+ private static final int SERVER_PAGING_SIZE = 100;
+
+ private final ListsDataSource dataSource;
+ private final ValueAccess valueAccess;
+
+ public ListsProcessor(final ListsDataSource dataSource, final ValueAccess valueAccess) {
+ this.dataSource = dataSource;
+ this.valueAccess = 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");
+
+ 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();
+ }
+
+ 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;
+ }
+ 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()) {
+ result = evaluateExpression(entity1, expression.getExpression()).compareTo(
+ evaluateExpression(entity2, expression.getExpression()));
+ 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(Object data, EdmMapping mapping, 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)) {
+ 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/3bebf610/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
index 10b8945..7dceba9 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/EdmAnnotationSerializer.java
@@ -25,16 +25,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
-import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
-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.core.annotation.edm.AnnotationHelper;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
index 4a6d9ad..265484d 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonConsumer.java
@@ -18,15 +18,17 @@
******************************************************************************/
package org.apache.olingo.odata2.core.annotation.processor.json;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonToken;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
+
import org.apache.olingo.odata2.core.ep.util.FormatJson;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+
/**
*
*/
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
index b972fed..a277b41 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/main/java/org/apache/olingo/odata2/core/annotation/processor/json/JsonWriter.java
@@ -16,6 +16,7 @@ package org.apache.olingo.odata2.core.annotation.processor.json;
import java.io.IOException;
import java.io.Writer;
+
import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
public class JsonWriter {
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
index 509303c..c6a9219 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/edm/AnnotationEdmProviderTest.java
@@ -15,9 +15,16 @@
*/
package org.apache.olingo.odata2.core.annotation.edm;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+
import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
import org.apache.olingo.odata2.api.edm.FullQualifiedName;
import org.apache.olingo.odata2.api.edm.provider.Association;
@@ -43,11 +50,6 @@ import org.apache.olingo.odata2.core.annotation.model.Photo;
import org.apache.olingo.odata2.core.annotation.model.RefBase;
import org.apache.olingo.odata2.core.annotation.model.Room;
import org.apache.olingo.odata2.core.annotation.model.Team;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import org.junit.Test;
/**
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
index 9889c2e..9427151 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Building.java
@@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
index 6a117db..c063ffe 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/City.java
@@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import org.apache.olingo.odata2.api.annotation.edm.EdmComplexEntity;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+
/**
*
*/
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
index 5247448..07a7dcb 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Employee.java
@@ -20,10 +20,11 @@ package org.apache.olingo.odata2.core.annotation.model;
import java.text.DateFormat;
import java.util.Calendar;
+
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
+import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
-import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
index b8b9725..242b4cf 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Location.java
@@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import org.apache.olingo.odata2.api.annotation.edm.EdmComplexEntity;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
+
/**
*
*/
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
index 1000822..187d4f8 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Manager.java
@@ -20,6 +20,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import java.util.ArrayList;
import java.util.List;
+
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.NavigationEnd;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
index 15a7dcc..c38a506 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/RefBase.java
@@ -17,8 +17,8 @@
package org.apache.olingo.odata2.core.annotation.model;
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
-import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmKey;
+import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
/**
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
index e1c55ed..3628ac8 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Room.java
@@ -17,6 +17,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import java.util.ArrayList;
import java.util.List;
+
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/3bebf610/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
----------------------------------------------------------------------
diff --git a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
index 529ed5a..e3ea0cc 100644
--- a/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
+++ b/odata2-edm-annotation/edm-annotation-core/src/test/java/org/apache/olingo/odata2/core/annotation/model/Team.java
@@ -20,6 +20,7 @@ package org.apache.olingo.odata2.core.annotation.model;
import java.util.ArrayList;
import java.util.List;
+
import org.apache.olingo.odata2.api.annotation.edm.EdmEntityType;
import org.apache.olingo.odata2.api.annotation.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.annotation.edm.EdmProperty;