You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/01/07 13:48:39 UTC
[1/2] [OLINGO-93] Duplicated ListProcessor for RefScenario
Updated Branches:
refs/heads/master f2db566f2 -> cb9ba5dd4
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ListsProcessor.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ListsProcessor.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ListsProcessor.java
new file mode 100644
index 0000000..fd335b4
--- /dev/null
+++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ListsProcessor.java
@@ -0,0 +1,1646 @@
+/*******************************************************************************
+ * 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.ref.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.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;
+import org.apache.olingo.odata2.ref.processor.ScenarioDataSource.BinaryData;
+
+/**
+ * Implementation of the centralized parts of OData processing,
+ * allowing to use the simplified {@link DataSource} 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 BeanPropertyAccess valueAccess;
+ private final ScenarioDataSource dataSource;
+
+ public ListsProcessor(final ScenarioDataSource dataSource) {
+ this(dataSource, new BeanPropertyAccess());
+ }
+
+ public ListsProcessor(final ScenarioDataSource dataSource, final BeanPropertyAccess 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");
+
+ 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/cb9ba5dd/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java
index bba87b2..cb055ed 100644
--- a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java
+++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java
@@ -27,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.DataSource;
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.edm.EdmException;
@@ -48,7 +47,7 @@ import org.apache.olingo.odata2.ref.model.Team;
* Data for the reference scenario
*
*/
-public class ScenarioDataSource implements DataSource {
+public class ScenarioDataSource {
private static final String ENTITYSET_1_1 = "Employees";
private static final String ENTITYSET_1_2 = "Teams";
@@ -63,7 +62,6 @@ public class ScenarioDataSource implements DataSource {
this.dataContainer = dataContainer;
}
- @Override
public List<?> readData(final EdmEntitySet entitySet) throws ODataNotImplementedException, ODataNotFoundException,
EdmException {
if (ENTITYSET_1_1.equals(entitySet.getName())) {
@@ -83,7 +81,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public Object readData(final EdmEntitySet entitySet, final Map<String, Object> keys)
throws ODataNotImplementedException, ODataNotFoundException, EdmException {
if (ENTITYSET_1_1.equals(entitySet.getName())) {
@@ -139,7 +136,6 @@ public class ScenarioDataSource implements DataSource {
throw new ODataNotImplementedException();
}
- @Override
public Object readRelatedData(final EdmEntitySet sourceEntitySet, final Object sourceData,
final EdmEntitySet targetEntitySet, final Map<String, Object> targetKeys) throws ODataNotImplementedException,
ODataNotFoundException, EdmException {
@@ -218,7 +214,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public Object readData(final EdmFunctionImport function, final Map<String, Object> parameters,
final Map<String, Object> keys) throws ODataNotImplementedException, ODataNotFoundException, EdmException {
if (function.getName().equals("EmployeeSearch")) {
@@ -340,7 +335,6 @@ public class ScenarioDataSource implements DataSource {
return oldestEmployee;
}
- @Override
public BinaryData readBinaryData(final EdmEntitySet entitySet, final Object mediaLinkEntryData)
throws ODataNotImplementedException, ODataNotFoundException, EdmException, ODataApplicationException {
if (mediaLinkEntryData == null) {
@@ -361,7 +355,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public void
writeBinaryData(final EdmEntitySet entitySet, final Object mediaLinkEntryData, final BinaryData binaryData)
throws ODataNotImplementedException, ODataNotFoundException, EdmException, ODataApplicationException {
@@ -382,7 +375,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public Object newDataObject(final EdmEntitySet entitySet) throws ODataNotImplementedException, EdmException {
if (ENTITYSET_1_1.equals(entitySet.getName())) {
Employee employee = dataContainer.createEmployee();
@@ -410,7 +402,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public void deleteData(final EdmEntitySet entitySet, final Map<String, Object> keys)
throws ODataNotImplementedException, ODataNotFoundException, EdmException, ODataApplicationException {
final Object data = readData(entitySet, keys);
@@ -465,7 +456,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public void createData(final EdmEntitySet entitySet, final Object data) throws ODataNotImplementedException,
EdmException, ODataApplicationException {
if (ENTITYSET_1_1.equals(entitySet.getName())) {
@@ -485,7 +475,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public void deleteRelation(final EdmEntitySet sourceEntitySet, final Object sourceData,
final EdmEntitySet targetEntitySet, final Map<String, Object> targetKeys) throws ODataNotImplementedException,
ODataNotFoundException, EdmException, ODataApplicationException {
@@ -547,7 +536,6 @@ public class ScenarioDataSource implements DataSource {
}
}
- @Override
public void writeRelation(final EdmEntitySet sourceEntitySet, final Object sourceData,
final EdmEntitySet targetEntitySet, final Map<String, Object> targetKeys) throws ODataNotImplementedException,
ODataNotFoundException, EdmException, ODataApplicationException {
@@ -617,4 +605,31 @@ public class ScenarioDataSource implements DataSource {
throw new ODataNotImplementedException();
}
}
+
+ /**
+ * Container to store binary data (as byte array) and the associated MIME type.
+ */
+ public static class BinaryData {
+ private final byte[] data;
+ private final String mimeType;
+
+ public BinaryData(final byte[] data, final String mimeType) {
+ this.data = data;
+ this.mimeType = mimeType;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ @Override
+ public String toString() {
+ return "data=" + Arrays.toString(data) + ", mimeType=" + mimeType;
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioServiceFactory.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioServiceFactory.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioServiceFactory.java
index f205aa2..d3075da 100644
--- a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioServiceFactory.java
+++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioServiceFactory.java
@@ -18,8 +18,6 @@
******************************************************************************/
package org.apache.olingo.odata2.ref.processor;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.BeanPropertyAccess;
import org.apache.olingo.odata2.api.ODataCallback;
import org.apache.olingo.odata2.api.ODataDebugCallback;
import org.apache.olingo.odata2.api.ODataService;
@@ -41,7 +39,7 @@ public class ScenarioServiceFactory extends ODataServiceFactory {
return createODataSingleProcessorService(
new ScenarioEdmProvider(),
- new ListsProcessor(new ScenarioDataSource(dataContainer), new BeanPropertyAccess()));
+ new ListsProcessor(new ScenarioDataSource(dataContainer)));
}
@SuppressWarnings("unchecked")
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntitySetTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntitySetTest.java b/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntitySetTest.java
index 0dadc30..ce47662 100644
--- a/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntitySetTest.java
+++ b/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntitySetTest.java
@@ -30,8 +30,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.CharBuffer;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.BeanPropertyAccess;
import org.apache.olingo.odata2.api.edm.EdmEntityContainer;
import org.apache.olingo.odata2.api.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
@@ -42,6 +40,7 @@ import org.apache.olingo.odata2.api.uri.PathInfo;
import org.apache.olingo.odata2.api.uri.UriInfo;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.ref.model.DataContainer;
+import org.apache.olingo.odata2.ref.processor.ListsProcessor;
import org.apache.olingo.odata2.ref.processor.ScenarioDataSource;
import org.apache.olingo.odata2.testutil.fit.BaseTest;
import org.junit.Before;
@@ -62,7 +61,7 @@ public class EntitySetTest extends BaseTest {
dataContainer = new DataContainer();
dataContainer.reset();
dataSource = new ScenarioDataSource(dataContainer);
- processor = new ListsProcessor(dataSource, new BeanPropertyAccess());
+ processor = new ListsProcessor(dataSource);
}
@Before
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntityTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntityTest.java b/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntityTest.java
index 28827d1..4f30696 100644
--- a/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntityTest.java
+++ b/odata2-lib/odata-ref/src/test/java/org/apache/olingo/odata2/ref/read/EntityTest.java
@@ -31,8 +31,6 @@ import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.BeanPropertyAccess;
import org.apache.olingo.odata2.api.edm.EdmEntityContainer;
import org.apache.olingo.odata2.api.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
@@ -47,6 +45,8 @@ import org.apache.olingo.odata2.api.uri.PathInfo;
import org.apache.olingo.odata2.api.uri.UriInfo;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.ref.model.DataContainer;
+import org.apache.olingo.odata2.ref.processor.BeanPropertyAccess;
+import org.apache.olingo.odata2.ref.processor.ListsProcessor;
import org.apache.olingo.odata2.ref.processor.ScenarioDataSource;
import org.apache.olingo.odata2.testutil.fit.BaseTest;
import org.junit.Before;
[2/2] git commit: [OLINGO-93] Duplicated ListProcessor for RefScenario
Posted by mi...@apache.org.
[OLINGO-93] Duplicated ListProcessor for RefScenario
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/cb9ba5dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/tree/cb9ba5dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/diff/cb9ba5dd
Branch: refs/heads/master
Commit: cb9ba5dd4b948857103f6d3524de1323ef96d19e
Parents: f2db566
Author: Michael Bolz <mi...@apache.org>
Authored: Tue Jan 7 13:40:24 2014 +0100
Committer: Michael Bolz <mi...@apache.org>
Committed: Tue Jan 7 13:40:24 2014 +0100
----------------------------------------------------------------------
.../olingo/odata2/fit/ref/AbstractRefTest.java | 6 +-
.../odata2/fit/ref/EntryXmlReadOnlyTest.java | 2 +-
.../AbstractContentNegotiationTest.java | 6 +-
odata2-lib/odata-ref/pom.xml | 5 -
.../ref/processor/BeanPropertyAccess.java | 180 ++
.../odata2/ref/processor/ListsProcessor.java | 1646 ++++++++++++++++++
.../ref/processor/ScenarioDataSource.java | 41 +-
.../ref/processor/ScenarioServiceFactory.java | 4 +-
.../olingo/odata2/ref/read/EntitySetTest.java | 5 +-
.../olingo/odata2/ref/read/EntityTest.java | 4 +-
10 files changed, 1864 insertions(+), 35 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java
index d316b7f..2404a25 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/AbstractRefTest.java
@@ -35,8 +35,6 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.BeanPropertyAccess;
import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
import org.apache.olingo.odata2.api.edm.provider.EdmProvider;
@@ -45,6 +43,7 @@ import org.apache.olingo.odata2.core.processor.ODataSingleProcessorService;
import org.apache.olingo.odata2.ref.edm.ScenarioEdmProvider;
import org.apache.olingo.odata2.ref.model.DataContainer;
import org.apache.olingo.odata2.ref.model.Photo;
+import org.apache.olingo.odata2.ref.processor.ListsProcessor;
import org.apache.olingo.odata2.ref.processor.ScenarioDataSource;
import org.apache.olingo.odata2.testutil.fit.AbstractFitTest;
import org.apache.olingo.odata2.testutil.helper.StringHelper;
@@ -81,8 +80,7 @@ public class AbstractRefTest extends AbstractFitTest {
protected ODataSingleProcessorService createService() {
DataContainer dataContainer = new DataContainer();
dataContainer.reset();
- ODataSingleProcessor processor = new ListsProcessor(new ScenarioDataSource(dataContainer),
- new BeanPropertyAccess());
+ ODataSingleProcessor processor = new ListsProcessor(new ScenarioDataSource(dataContainer));
EdmProvider provider = new ScenarioEdmProvider();
return new ODataSingleProcessorService(provider, processor) {};
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/EntryXmlReadOnlyTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/EntryXmlReadOnlyTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/EntryXmlReadOnlyTest.java
index 79cf938..65c6664 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/EntryXmlReadOnlyTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/EntryXmlReadOnlyTest.java
@@ -28,11 +28,11 @@ import java.lang.reflect.Field;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
import org.apache.olingo.odata2.ref.model.DataContainer;
import org.apache.olingo.odata2.ref.model.Photo;
+import org.apache.olingo.odata2.ref.processor.ListsProcessor;
import org.apache.olingo.odata2.ref.processor.ScenarioDataSource;
import org.custommonkey.xmlunit.XMLAssert;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/contentnegotiation/AbstractContentNegotiationTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/contentnegotiation/AbstractContentNegotiationTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/contentnegotiation/AbstractContentNegotiationTest.java
index 0a19c7f..64c73c3 100644
--- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/contentnegotiation/AbstractContentNegotiationTest.java
+++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/contentnegotiation/AbstractContentNegotiationTest.java
@@ -42,8 +42,6 @@ import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.olingo.odata2.annotation.processor.core.ListsProcessor;
-import org.apache.olingo.odata2.annotation.processor.core.datasource.BeanPropertyAccess;
import org.apache.olingo.odata2.api.ODataService;
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.commons.HttpHeaders;
@@ -56,6 +54,7 @@ import org.apache.olingo.odata2.core.processor.ODataSingleProcessorService;
import org.apache.olingo.odata2.core.uri.UriType;
import org.apache.olingo.odata2.ref.edm.ScenarioEdmProvider;
import org.apache.olingo.odata2.ref.model.DataContainer;
+import org.apache.olingo.odata2.ref.processor.ListsProcessor;
import org.apache.olingo.odata2.ref.processor.ScenarioDataSource;
import org.apache.olingo.odata2.testutil.fit.AbstractFitTest;
import org.apache.olingo.odata2.testutil.helper.StringHelper;
@@ -105,8 +104,7 @@ public abstract class AbstractContentNegotiationTest extends AbstractFitTest {
protected ODataService createService() throws ODataException {
DataContainer dataContainer = new DataContainer();
dataContainer.init();
- final ODataSingleProcessor processor = new ListsProcessor(new ScenarioDataSource(dataContainer),
- new BeanPropertyAccess());
+ final ODataSingleProcessor processor = new ListsProcessor(new ScenarioDataSource(dataContainer));
final EdmProvider provider = new ScenarioEdmProvider();
return new ODataSingleProcessorService(provider, processor) {};
}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/pom.xml
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/pom.xml b/odata2-lib/odata-ref/pom.xml
index 27cc47c..fbf419f 100644
--- a/odata2-lib/odata-ref/pom.xml
+++ b/odata2-lib/odata-ref/pom.xml
@@ -91,11 +91,6 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.apache.olingo</groupId>
- <artifactId>olingo-odata2-annotation-processor-core-incubating</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/cb9ba5dd/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/BeanPropertyAccess.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/BeanPropertyAccess.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/BeanPropertyAccess.java
new file mode 100644
index 0000000..8898439
--- /dev/null
+++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/BeanPropertyAccess.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * 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.ref.processor;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.edm.EdmMapping;
+import org.apache.olingo.odata2.api.edm.EdmProperty;
+import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.exception.ODataHttpException;
+import org.apache.olingo.odata2.api.exception.ODataNotFoundException;
+
+/**
+ * Data access.
+ */
+public class BeanPropertyAccess {
+
+ public <T> Object getPropertyValue(final T data, final EdmProperty property) throws ODataException {
+ return getValue(data, getGetterMethodName(property));
+ }
+
+ public <T, V> void setPropertyValue(final T data, final EdmProperty property, final V value) throws ODataException {
+ final String methodName = getSetterMethodName(getGetterMethodName(property));
+ if (methodName != null) {
+ setValue(data, methodName, value);
+ }
+ }
+
+ public <T> Class<?> getPropertyType(final T data, final EdmProperty property) throws ODataException {
+ return getType(data, getGetterMethodName(property));
+ }
+
+ public <T> Object getMappingValue(final T data, final EdmMapping mapping) throws ODataException {
+ if (mapping != null && mapping.getMimeType() != null) {
+ return getValue(data, mapping.getMimeType());
+ }
+ return null;
+ }
+
+ public <T, V> void setMappingValue(final T data, final EdmMapping mapping, final V value) throws ODataException {
+ if (mapping != null && mapping.getMimeType() != null) {
+ setValue(data, getSetterMethodName(mapping.getMimeType()), value);
+ }
+ }
+
+ private String getGetterMethodName(final EdmProperty property) throws EdmException {
+ final String prefix = isBooleanProperty(property) ? "is" : "get";
+ final String defaultMethodName = prefix + property.getName();
+ return property.getMapping() == null || property.getMapping().getInternalName() == null ?
+ defaultMethodName : property.getMapping().getInternalName();
+ }
+
+ private boolean isBooleanProperty(final EdmProperty property) throws EdmException {
+ return property.isSimple()
+ && property.getType() == EdmSimpleTypeKind.Boolean.getEdmSimpleTypeInstance();
+ }
+
+ private String getSetterMethodName(final String getterMethodName) {
+ return getterMethodName.contains(".") ?
+ null : getterMethodName.replaceFirst("^is", "set").replaceFirst("^get", "set");
+ }
+
+ private <T> Object getValue(final T data, final String methodName) throws ODataNotFoundException {
+ Object dataObject = data;
+
+ for (final String method : methodName.split("\\.", -1)) {
+ if (dataObject != null) {
+ try {
+ dataObject = dataObject.getClass().getMethod(method).invoke(dataObject);
+ } catch (SecurityException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ } catch (NoSuchMethodException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ } catch (IllegalArgumentException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ } catch (IllegalAccessException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ } catch (InvocationTargetException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ }
+ }
+ }
+
+ return dataObject;
+ }
+
+ private <T, V> void setValue(final T data, final String methodName, final V value)
+ throws ODataNotFoundException {
+ try {
+ boolean found = false;
+ for (final Method method : Arrays.asList(data.getClass().getMethods())) {
+ if (method.getName().equals(methodName)) {
+ found = true;
+ final Class<?> type = method.getParameterTypes()[0];
+ if (value == null) {
+ if (type.equals(byte.class) || type.equals(short.class) || type.equals(int.class)
+ || type.equals(long.class) || type.equals(char.class)) {
+ method.invoke(data, 0);
+ } else if (type.equals(float.class) || type.equals(double.class)) {
+ method.invoke(data, 0.0);
+ } else if (type.equals(boolean.class)) {
+ method.invoke(data, false);
+ } else {
+ method.invoke(data, value);
+ }
+ } else {
+ method.invoke(data, value);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ throw new ODataNotFoundException(null);
+ }
+ } catch (SecurityException e) {
+ throw new ODataNotFoundException(null, e);
+ } catch (IllegalArgumentException e) {
+ throw new ODataNotFoundException(null, e);
+ } catch (IllegalAccessException e) {
+ throw new ODataNotFoundException(null, e);
+ } catch (InvocationTargetException e) {
+ throw new ODataNotFoundException(null, e);
+ }
+ }
+
+ private <T> Class<?> getType(final T data, final String methodName) throws ODataNotFoundException {
+ if (data == null) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON);
+ }
+
+ Class<?> type = data.getClass();
+ for (final String method : methodName.split("\\.", -1)) {
+ try {
+ type = type.getMethod(method).getReturnType();
+ if (type.isPrimitive()) {
+ if (type == boolean.class) {
+ type = Boolean.class;
+ } else if (type == byte.class) {
+ type = Byte.class;
+ } else if (type == short.class) {
+ type = Short.class;
+ } else if (type == int.class) {
+ type = Integer.class;
+ } else if (type == long.class) {
+ type = Long.class;
+ } else if (type == float.class) {
+ type = Float.class;
+ } else if (type == double.class) {
+ type = Double.class;
+ }
+ }
+ } catch (final SecurityException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ } catch (final NoSuchMethodException e) {
+ throw new ODataNotFoundException(ODataHttpException.COMMON, e);
+ }
+ }
+ return type;
+ }
+}