blob: 5db1d112cd46bd61e4d679382554536a82e00824 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.core.uri.validator;
import java.util.HashMap;
import java.util.List;
import org.apache.olingo.commons.api.edm.EdmAction;
import org.apache.olingo.commons.api.edm.EdmActionImport;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmFunctionImport;
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmReturnType;
import org.apache.olingo.commons.api.edm.EdmSingleton;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceAction;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceFunction;
import org.apache.olingo.server.api.uri.UriResourceKind;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
import org.apache.olingo.server.api.uri.UriResourceProperty;
import org.apache.olingo.server.api.uri.UriResourceSingleton;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOption;
import org.apache.olingo.server.api.uri.queryoption.SystemQueryOptionKind;
public class UriValidator {
//@formatter:off (Eclipse formatter)
//CHECKSTYLE:OFF (Maven checkstyle)
private boolean[][] decisionMatrix =
{
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-LEVELS 11-TOP */
/* all 0 */ { true , true , true , false, true , true , true , true , true , true , true , false },
/* batch 1 */ { false, false, false, false, false, false, false, false, false, false, false, false },
/* crossjoin 2 */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entityId 3 */ { false, true , true , true , false, false, false, true , false, false, true , false },
/* metadata 4 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* resource 5 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* service 6 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* entitySet 7 */ { true , true , true , false, true , true , true , true , true , true , true , true },
/* entitySetCount 8 */ { true, false, false, false, false, false, true, false, false, false, false, false },
/* entity 9 */ { false, true , true , false, false, false, false, true , false, false, true , false },
/* mediaStream 10 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* references 11 */ { true , true , false, false, false, true , true , false, true , true , false, true },
/* reference 12 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyComplex 13 */ { false, true , true , false, false, false, false, true , false, false, true , false },
/* propertyComplexCollection 14 */ { true , true , true , false, true , true , false, false, true , true , true , true },
/* propertyComplexCollectionCount 15 */ { true, false, false, false, false, false, true, false, false, false, false, false },
/* propertyPrimitive 16 */ { false, true , false, false, false, false, false, false, false, false, false, false },
/* propertyPrimitiveCollection 17 */ { true , true , false, false, false, true , false, false, true , true , false, true },
/* propertyPrimitiveCollectionCount 18 */ { true, false, false, false, false, false, true, false, false, false, false, false },
/* propertyPrimitiveValue 19 */ { false, true , false, false, false, false, false, false, false, false, false, false },
};
private boolean[][] decisionMatrixForHttpMethod =
{
/* 0-FILTER 1-FORMAT 2-EXPAND 3-ID 4-COUNT 5-ORDERBY 6-SEARCH 7-SELECT 8-SKIP 9-SKIPTOKEN 10-LEVELS 11-TOP */
/* GET 0 */ { true , true , true , true, true , true , true , true , true , true , true , true },
/* POST 0 */ { true , false , true , false, false , true , false , true , false , false , true , false },
/* PUT 0 */ { false , false , false , false, false , false , false , false , false , false , false , false },
/* DELETE 0 */ { false , false , false , false, false , false, false , false, false , false , false, false },
/* PATCH 0 */ { false , false , false , false, false , false , false , false , false , false , false , false },
};
//CHECKSTYLE:ON
//@formatter:on
private enum RowIndexForUriType {
all(0),
batch(1),
crossjoin(2),
entityId(3),
metadata(4),
resource(5),
service(6),
entitySet(7),
entitySetCount(8),
entity(9),
mediaStream(10),
references(11),
reference(12),
propertyComplex(13),
propertyComplexCollection(14),
propertyComplexCollectionCount(15),
propertyPrimitive(16),
propertyPrimitiveCollection(17),
propertyPrimitiveCollectionCount(18),
propertyPrimitiveValue(19);
private int idx;
RowIndexForUriType(final int i) {
idx = i;
}
public int getIndex() {
return idx;
}
}
private enum ColumnIndex {
filter(0),
format(1),
expand(2),
id(3),
count(4),
orderby(5),
search(6),
select(7),
skip(8),
skiptoken(9),
levels(10),
top(11);
private int idx;
ColumnIndex(final int i) {
idx = i;
}
public int getIndex() {
return idx;
}
}
private enum RowIndexForHttpMethod {
GET(0),
POST(1),
PUT(2),
DELETE(3),
PATCH(4);
private int idx;
RowIndexForHttpMethod(final int i) {
idx = i;
}
public int getIndex() {
return idx;
}
}
public UriValidator() {
super();
}
public void validate(final UriInfo uriInfo, final HttpMethod httpMethod) throws UriValidationException {
validateForHttpMethod(uriInfo, httpMethod);
validateQueryOptions(uriInfo);
validateKeyPredicates(uriInfo);
validatePropertyOperations(uriInfo, httpMethod);
}
private ColumnIndex colIndex(final SystemQueryOptionKind queryOptionKind) throws UriValidationException {
ColumnIndex idx;
switch (queryOptionKind) {
case FILTER:
idx = ColumnIndex.filter;
break;
case FORMAT:
idx = ColumnIndex.format;
break;
case EXPAND:
idx = ColumnIndex.expand;
break;
case ID:
idx = ColumnIndex.id;
break;
case COUNT:
idx = ColumnIndex.count;
break;
case ORDERBY:
idx = ColumnIndex.orderby;
break;
case SEARCH:
idx = ColumnIndex.search;
break;
case SELECT:
idx = ColumnIndex.select;
break;
case SKIP:
idx = ColumnIndex.skip;
break;
case SKIPTOKEN:
idx = ColumnIndex.skiptoken;
break;
case LEVELS:
idx = ColumnIndex.levels;
break;
case TOP:
idx = ColumnIndex.top;
break;
default:
throw new UriValidationException("Unsupported option: " + queryOptionKind.toString(),
UriValidationException.MessageKeys.UNSUPPORTED_QUERY_OPTION, queryOptionKind.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForUriType(final UriInfo uriInfo) throws UriValidationException {
RowIndexForUriType idx;
switch (uriInfo.getKind()) {
case all:
idx = RowIndexForUriType.all;
break;
case batch:
idx = RowIndexForUriType.batch;
break;
case crossjoin:
idx = RowIndexForUriType.crossjoin;
break;
case entityId:
idx = RowIndexForUriType.entityId;
break;
case metadata:
idx = RowIndexForUriType.metadata;
break;
case resource:
idx = rowIndexForResourceKind(uriInfo);
break;
case service:
idx = RowIndexForUriType.service;
break;
default:
throw new UriValidationException("Unsupported uriInfo kind: " + uriInfo.getKind(),
UriValidationException.MessageKeys.UNSUPPORTED_URI_KIND, uriInfo.getKind().toString());
}
return idx;
}
private RowIndexForUriType rowIndexForResourceKind(final UriInfo uriInfo) throws UriValidationException {
RowIndexForUriType idx;
int lastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 1;
UriResource lastPathSegment = uriInfo.getUriResourceParts().get(lastPathSegmentIndex);
switch (lastPathSegment.getKind()) {
case count:
idx = rowIndexForCount(uriInfo);
break;
case action:
idx = rowIndexForAction(lastPathSegment);
break;
case complexProperty:
idx = rowIndexForComplexProperty(lastPathSegment);
break;
case entitySet:
idx = rowIndexForEntitySet(lastPathSegment);
break;
case function:
idx = rowIndexForFunction(lastPathSegment);
break;
case navigationProperty:
idx =
((UriResourceNavigation) lastPathSegment).isCollection() ? RowIndexForUriType.entitySet
: RowIndexForUriType.entity;
break;
case primitiveProperty:
idx = rowIndexForPrimitiveProperty(lastPathSegment);
break;
case ref:
idx = rowIndexForRef(uriInfo, lastPathSegment);
break;
case root:
idx = RowIndexForUriType.service;
break;
case singleton:
idx = RowIndexForUriType.entity;
break;
case value:
idx = rowIndexForValue(uriInfo);
break;
default:
throw new UriValidationException("Unsupported uriResource kind: " + lastPathSegment.getKind(),
UriValidationException.MessageKeys.UNSUPPORTED_URI_RESOURCE_KIND, lastPathSegment.getKind().toString());
}
return idx;
}
private RowIndexForUriType rowIndexForValue(final UriInfo uriInfo) throws UriValidationException {
RowIndexForUriType idx;
int secondLastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 2;
UriResource secondLastPathSegment = uriInfo.getUriResourceParts().get(secondLastPathSegmentIndex);
switch (secondLastPathSegment.getKind()) {
case primitiveProperty:
idx = RowIndexForUriType.propertyPrimitiveValue;
break;
case entitySet:
idx = RowIndexForUriType.mediaStream;
break;
case function:
UriResourceFunction uriFunction = (UriResourceFunction) secondLastPathSegment;
EdmFunction function;
EdmFunctionImport functionImport = uriFunction.getFunctionImport();
if (functionImport != null) {
List<EdmFunction> functions = functionImport.getUnboundFunctions();
function = functions.get(0);
} else {
function = uriFunction.getFunction();
}
EdmTypeKind functionReturnTypeKind = function.getReturnType().getType().getKind();
boolean isFunctionCollection = function.getReturnType().isCollection();
idx = determineReturnType(functionReturnTypeKind, isFunctionCollection);
break;
case action:
UriResourceAction uriAction = (UriResourceAction) secondLastPathSegment;
EdmActionImport actionImport = uriAction.getActionImport();
EdmAction action;
if (actionImport != null) {
action = actionImport.getUnboundAction();
} else {
action = uriAction.getAction();
}
EdmTypeKind actionReturnTypeKind = action.getReturnType().getType().getKind();
boolean isActionCollection = action.getReturnType().isCollection();
idx = determineReturnType(actionReturnTypeKind, isActionCollection);
break;
case navigationProperty:
UriResourceNavigation uriNavigation = (UriResourceNavigation) secondLastPathSegment;
if (uriNavigation.isCollection()) {
idx = RowIndexForUriType.entitySet;
} else {
idx = RowIndexForUriType.entity;
}
break;
case singleton:
UriResourceSingleton uriSingleton = (UriResourceSingleton) secondLastPathSegment;
EdmSingleton singleton = uriSingleton.getSingleton();
EdmTypeKind singletonReturnTypeKind = singleton.getEntityType().getKind();
idx = determineReturnType(singletonReturnTypeKind, false);
break;
default:
throw new UriValidationException("Unexpected kind in path segment before $value: "
+ secondLastPathSegment.getKind(), UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_VALUE,
secondLastPathSegment.toString());
}
return idx;
}
private RowIndexForUriType determineReturnType(final EdmTypeKind functionReturnTypeKind,
final boolean isCollection) throws UriValidationException {
RowIndexForUriType idx;
switch (functionReturnTypeKind) {
case COMPLEX:
idx = isCollection ? RowIndexForUriType.propertyComplexCollection : RowIndexForUriType.propertyComplex;
break;
case ENTITY:
idx = isCollection ? RowIndexForUriType.entitySet : RowIndexForUriType.entity;
break;
case PRIMITIVE:
case ENUM:
case DEFINITION:
idx = isCollection ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive;
break;
default:
throw new UriValidationException("Unsupported function return type: " + functionReturnTypeKind,
UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE, functionReturnTypeKind.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForRef(final UriInfo uriInfo, final UriResource lastPathSegment)
throws UriValidationException {
RowIndexForUriType idx;
int secondLastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 2;
UriResource secondLastPathSegment = uriInfo.getUriResourceParts().get(secondLastPathSegmentIndex);
if (secondLastPathSegment instanceof UriResourcePartTyped) {
idx =
((UriResourcePartTyped) secondLastPathSegment).isCollection() ? RowIndexForUriType.references
: RowIndexForUriType.reference;
} else {
throw new UriValidationException("secondLastPathSegment not a class of UriResourcePartTyped: "
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment
.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForPrimitiveProperty(final UriResource lastPathSegment)
throws UriValidationException {
RowIndexForUriType idx;
if (lastPathSegment instanceof UriResourcePartTyped) {
idx =
((UriResourcePartTyped) lastPathSegment).isCollection() ? RowIndexForUriType.propertyPrimitiveCollection
: RowIndexForUriType.propertyPrimitive;
} else {
throw new UriValidationException("lastPathSegment not a class of UriResourcePartTyped: "
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment
.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForFunction(final UriResource lastPathSegment) throws UriValidationException {
RowIndexForUriType idx;
UriResourceFunction urf = (UriResourceFunction) lastPathSegment;
EdmReturnType rt = urf.getFunction().getReturnType();
switch (rt.getType().getKind()) {
case ENTITY:
if (((EdmEntityType) rt.getType()).hasStream()) {
idx = RowIndexForUriType.mediaStream;
} else {
idx = rt.isCollection() ? RowIndexForUriType.entitySet : RowIndexForUriType.entity;
}
break;
case PRIMITIVE:
idx = rt.isCollection() ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive;
break;
case COMPLEX:
idx = rt.isCollection() ? RowIndexForUriType.propertyComplexCollection : RowIndexForUriType.propertyComplex;
break;
default:
throw new UriValidationException("Unsupported function return type: " + rt.getType().getKind(),
UriValidationException.MessageKeys.UNSUPPORTED_FUNCTION_RETURN_TYPE,
rt.getType().getKind().toString());
}
return idx;
}
private RowIndexForUriType rowIndexForEntitySet(final UriResource lastPathSegment) throws UriValidationException {
RowIndexForUriType idx;
if (lastPathSegment instanceof UriResourcePartTyped) {
idx =
((UriResourcePartTyped) lastPathSegment).isCollection() ? RowIndexForUriType.entitySet
: RowIndexForUriType.entity;
} else {
throw new UriValidationException("lastPathSegment not a class of UriResourcePartTyped: "
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment
.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForComplexProperty(final UriResource lastPathSegment)
throws UriValidationException {
RowIndexForUriType idx;
if (lastPathSegment instanceof UriResourcePartTyped) {
idx =
((UriResourcePartTyped) lastPathSegment).isCollection() ? RowIndexForUriType.propertyComplexCollection
: RowIndexForUriType.propertyComplex;
} else {
throw new UriValidationException("lastPathSegment not a class of UriResourcePartTyped: "
+ lastPathSegment.getClass(), UriValidationException.MessageKeys.LAST_SEGMENT_NOT_TYPED, lastPathSegment
.toString());
}
return idx;
}
private RowIndexForUriType rowIndexForAction(final UriResource lastPathSegment) throws UriValidationException {
RowIndexForUriType idx;
UriResourceAction ura = (UriResourceAction) lastPathSegment;
EdmReturnType rt = ura.getAction().getReturnType();
switch (rt.getType().getKind()) {
case ENTITY:
if (((EdmEntityType) rt.getType()).hasStream()) {
idx = RowIndexForUriType.mediaStream;
} else {
idx = rt.isCollection() ? RowIndexForUriType.entitySet : RowIndexForUriType.entity;
}
break;
case PRIMITIVE:
idx = rt.isCollection() ? RowIndexForUriType.propertyPrimitiveCollection : RowIndexForUriType.propertyPrimitive;
break;
case COMPLEX:
idx = rt.isCollection() ? RowIndexForUriType.propertyComplexCollection : RowIndexForUriType.propertyComplex;
break;
default:
throw new UriValidationException("Unsupported action return type: " + rt.getType().getKind(),
UriValidationException.MessageKeys.UNSUPPORTED_ACTION_RETURN_TYPE, rt.getType().getKind().toString());
}
return idx;
}
private RowIndexForUriType rowIndexForCount(final UriInfo uriInfo) throws UriValidationException {
RowIndexForUriType idx;
int secondLastPathSegmentIndex = uriInfo.getUriResourceParts().size() - 2;
UriResource secondLastPathSegment = uriInfo.getUriResourceParts().get(secondLastPathSegmentIndex);
switch (secondLastPathSegment.getKind()) {
case entitySet:
case navigationProperty:
idx = RowIndexForUriType.entitySetCount;
break;
case complexProperty:
idx = RowIndexForUriType.propertyComplexCollectionCount;
break;
case primitiveProperty:
idx = RowIndexForUriType.propertyPrimitiveCollectionCount;
break;
case function:
UriResourceFunction uriFunction = (UriResourceFunction) secondLastPathSegment;
EdmFunction function;
List<EdmFunction> functions;
EdmFunctionImport functionImport = uriFunction.getFunctionImport();
if (functionImport != null) {
functions = functionImport.getUnboundFunctions();
function = functions.get(0);
} else {
function = uriFunction.getFunction();
}
EdmTypeKind functionReturnTypeKind = function.getReturnType().getType().getKind();
boolean isCollection = function.getReturnType().isCollection();
idx = determineReturnType(functionReturnTypeKind, isCollection);
break;
default:
throw new UriValidationException("Illegal path part kind before $count: " + secondLastPathSegment.getKind(),
UriValidationException.MessageKeys.UNALLOWED_KIND_BEFORE_COUNT, secondLastPathSegment.toString());
}
return idx;
}
private void validateQueryOptions(final UriInfo uriInfo) throws UriValidationException {
RowIndexForUriType row = rowIndexForUriType(uriInfo);
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
ColumnIndex col = colIndex(option.getKind());
if (!decisionMatrix[row.getIndex()][col.getIndex()]) {
throw new UriValidationException("System query option not allowed: " + option.getName(),
UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED, option.getName());
}
}
}
private void validateForHttpMethod(final UriInfo uriInfo, final HttpMethod httpMethod) throws UriValidationException {
RowIndexForHttpMethod row = rowIndexForHttpMethod(httpMethod);
for (SystemQueryOption option : uriInfo.getSystemQueryOptions()) {
ColumnIndex col = colIndex(option.getKind());
if (!decisionMatrixForHttpMethod[row.getIndex()][col.getIndex()]) {
throw new UriValidationException("System query option " + option.getName() + " not allowed for method "
+ httpMethod, UriValidationException.MessageKeys.SYSTEM_QUERY_OPTION_NOT_ALLOWED_FOR_HTTP_METHOD, option
.getName(), httpMethod.toString());
}
}
}
private RowIndexForHttpMethod rowIndexForHttpMethod(final HttpMethod httpMethod) throws UriValidationException {
RowIndexForHttpMethod idx;
switch (httpMethod) {
case GET:
idx = RowIndexForHttpMethod.GET;
break;
case POST:
idx = RowIndexForHttpMethod.POST;
break;
case PUT:
idx = RowIndexForHttpMethod.PUT;
break;
case DELETE:
idx = RowIndexForHttpMethod.DELETE;
break;
case PATCH:
idx = RowIndexForHttpMethod.PATCH;
break;
default:
throw new UriValidationException("HTTP method not supported: " + httpMethod,
UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, httpMethod.toString());
}
return idx;
}
private void validateKeyPredicates(final UriInfo uriInfo) throws UriValidationException {
for (UriResource pathSegment : uriInfo.getUriResourceParts()) {
if (pathSegment.getKind() == UriResourceKind.entitySet) {
UriResourceEntitySet pathEntitySet = (UriResourceEntitySet) pathSegment;
List<UriParameter> keyPredicates = pathEntitySet.getKeyPredicates();
if (keyPredicates != null) {
final List<String> keyPredicateNames = pathEntitySet.getEntityType().getKeyPredicateNames();
HashMap<String, EdmKeyPropertyRef> edmKeys = new HashMap<String, EdmKeyPropertyRef>();
for (EdmKeyPropertyRef key : pathEntitySet.getEntityType().getKeyPropertyRefs()) {
edmKeys.put(key.getKeyPropertyName(), key);
final String alias = key.getAlias();
if (alias != null) {
edmKeys.put(alias, key);
}
}
for (UriParameter keyPredicate : keyPredicates) {
final String name = keyPredicate.getName();
final String alias = keyPredicate.getAlias();
final String value = alias == null ?
keyPredicate.getText() :
uriInfo.getValueForAlias(alias);
EdmKeyPropertyRef edmKey = edmKeys.get(name);
if (edmKey == null) {
if (keyPredicateNames.contains(name)) {
throw new UriValidationException("Double key property: " + name,
UriValidationException.MessageKeys.DOUBLE_KEY_PROPERTY, name);
} else {
throw new UriValidationException("Unknown key property: " + name,
UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name);
}
}
final EdmProperty property = edmKey.getProperty();
final EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) property.getType();
try {
if (!edmPrimitiveType.validate(edmPrimitiveType.fromUriLiteral(value),
property.isNullable(), property.getMaxLength(),
property.getPrecision(), property.getScale(), property.isUnicode())) {
// TODO: Check exception here
throw new UriValidationException("PrimitiveTypeException",
UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name);
}
} catch (EdmPrimitiveTypeException e) {
// TODO: Check exception here
throw new UriValidationException("PrimitiveTypeException", e,
UriValidationException.MessageKeys.INVALID_KEY_PROPERTY, name);
}
edmKeys.remove(name);
edmKeys.remove(alias);
}
}
}
}
}
private void validatePropertyOperations(final UriInfo uriInfo, final HttpMethod method)
throws UriValidationException {
final List<UriResource> parts = uriInfo.getUriResourceParts();
final UriResource last = parts.size() > 0 ? parts.get(parts.size() - 1) : null;
final UriResource previous = parts.size() > 1 ? parts.get(parts.size() - 2) : null;
if (last != null
&& (last.getKind() == UriResourceKind.primitiveProperty
|| last.getKind() == UriResourceKind.complexProperty
|| last.getKind() == UriResourceKind.value && previous.getKind() == UriResourceKind.primitiveProperty)) {
final EdmProperty property = ((UriResourceProperty)
(last.getKind() == UriResourceKind.value ? previous : last)).getProperty();
if (method == HttpMethod.PATCH && property.isCollection()) {
throw new UriValidationException("Attempt to patch collection property.",
UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, method.toString());
}
if (method == HttpMethod.DELETE && property.isNullable() != null && !property.isNullable()) {
throw new UriValidationException("Attempt to delete non-nullable property.",
UriValidationException.MessageKeys.UNSUPPORTED_HTTP_METHOD, method.toString());
}
}
}
}