[OLINGO-988] Prevent duplicate ExpandItems in ExpandTreeBuilder

This commit is contained in:
Christian Amend 2016-07-26 10:27:25 +02:00
parent 22a21a28ea
commit 44d6f5a171
7 changed files with 185 additions and 75 deletions

View File

@ -19,13 +19,17 @@
package org.apache.olingo.server.core.deserializer.helper; package org.apache.olingo.server.core.deserializer.helper;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.core.uri.UriInfoImpl; import org.apache.olingo.server.core.uri.UriInfoImpl;
import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl; import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl; import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
public abstract class ExpandTreeBuilder { public abstract class ExpandTreeBuilder {
public abstract ExpandTreeBuilder expand(EdmNavigationProperty edmNavigationProperty); public abstract ExpandTreeBuilder expand(EdmNavigationProperty edmNavigationProperty);
public abstract ExpandOption build();
protected ExpandItemImpl buildExpandItem(final EdmNavigationProperty edmNavigationProperty) { protected ExpandItemImpl buildExpandItem(final EdmNavigationProperty edmNavigationProperty) {
return new ExpandItemImpl() return new ExpandItemImpl()
.setResourcePath(new UriInfoImpl() .setResourcePath(new UriInfoImpl()

View File

@ -18,6 +18,9 @@
*/ */
package org.apache.olingo.server.core.deserializer.helper; package org.apache.olingo.server.core.deserializer.helper;
import java.util.HashMap;
import java.util.Map;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty; import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption; import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl; import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
@ -25,43 +28,41 @@ import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
public class ExpandTreeBuilderImpl extends ExpandTreeBuilder { public class ExpandTreeBuilderImpl extends ExpandTreeBuilder {
private final Map<String, ExpandTreeBuilder> childBuilderCache = new HashMap<String, ExpandTreeBuilder>();
private final ExpandItemImpl parentItem;
private ExpandOptionImpl expandOption = null; private ExpandOptionImpl expandOption = null;
private ExpandTreeBuilderImpl(final ExpandItemImpl parentItem) {
this.parentItem = parentItem;
}
@Override @Override
public ExpandTreeBuilder expand(final EdmNavigationProperty edmNavigationProperty) { public ExpandTreeBuilder expand(final EdmNavigationProperty edmNavigationProperty) {
ExpandItemImpl expandItem = buildExpandItem(edmNavigationProperty);
if (expandOption == null) { if (expandOption == null) {
expandOption = new ExpandOptionImpl(); expandOption = new ExpandOptionImpl();
if(parentItem != null && parentItem.getExpandOption() == null){
parentItem.setSystemQueryOption(expandOption);
} }
}
ExpandTreeBuilder builder = childBuilderCache.get(edmNavigationProperty.getName());
if(builder == null){
ExpandItemImpl expandItem = buildExpandItem(edmNavigationProperty);
expandOption.addExpandItem(expandItem); expandOption.addExpandItem(expandItem);
builder = new ExpandTreeBuilderImpl(expandItem);
return new ExpandTreeBuilderInner(expandItem); childBuilderCache.put(edmNavigationProperty.getName(), builder);
} }
return builder;
}
@Override
public ExpandOption build() { public ExpandOption build() {
return expandOption; return expandOption;
} }
private class ExpandTreeBuilderInner extends ExpandTreeBuilder { public static ExpandTreeBuilder create(){
private ExpandItemImpl parent; return new ExpandTreeBuilderImpl(null);
public ExpandTreeBuilderInner(final ExpandItemImpl expandItem) {
parent = expandItem;
}
@Override
public ExpandTreeBuilder expand(final EdmNavigationProperty edmNavigationProperty) {
if (parent.getExpandOption() == null) {
final ExpandOptionImpl expandOption = new ExpandOptionImpl();
parent.setSystemQueryOption(expandOption);
}
final ExpandItemImpl expandItem = buildExpandItem(edmNavigationProperty);
((ExpandOptionImpl) parent.getExpandOption()).addExpandItem(expandItem);
return new ExpandTreeBuilderInner(expandItem);
}
} }
} }

View File

@ -134,7 +134,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
throw new DeserializerException("Nested Arrays and primitive values are not allowed for an entity value.", throw new DeserializerException("Nested Arrays and primitive values are not allowed for an entity value.",
DeserializerException.MessageKeys.INVALID_ENTITY); DeserializerException.MessageKeys.INVALID_ENTITY);
} }
EdmEntityType derivedEdmEntityType = (EdmEntityType)getDerivedType(edmEntityType, arrayElement); EdmEntityType derivedEdmEntityType = (EdmEntityType) getDerivedType(edmEntityType, arrayElement);
entities.add(consumeEntityNode(derivedEdmEntityType, (ObjectNode) arrayElement, expandBuilder)); entities.add(consumeEntityNode(derivedEdmEntityType, (ObjectNode) arrayElement, expandBuilder));
} }
return entities; return entities;
@ -149,9 +149,9 @@ public class ODataJsonDeserializer implements ODataDeserializer {
throws DeserializerException { throws DeserializerException {
try { try {
final ObjectNode tree = parseJsonTree(stream); final ObjectNode tree = parseJsonTree(stream);
final ExpandTreeBuilderImpl expandBuilder = new ExpandTreeBuilderImpl(); final ExpandTreeBuilder expandBuilder = ExpandTreeBuilderImpl.create();
EdmEntityType derivedEdmEntityType = (EdmEntityType)getDerivedType(edmEntityType, tree); EdmEntityType derivedEdmEntityType = (EdmEntityType) getDerivedType(edmEntityType, tree);
return DeserializerResultImpl.with().entity(consumeEntityNode(derivedEdmEntityType, tree, expandBuilder)) return DeserializerResultImpl.with().entity(consumeEntityNode(derivedEdmEntityType, tree, expandBuilder))
.expandOption(expandBuilder.build()) .expandOption(expandBuilder.build())
@ -384,9 +384,9 @@ public class ODataJsonDeserializer implements ODataDeserializer {
final EdmNavigationProperty edmNavigationProperty) throws DeserializerException { final EdmNavigationProperty edmNavigationProperty) throws DeserializerException {
Link link = new Link(); Link link = new Link();
link.setTitle(navigationPropertyName); link.setTitle(navigationPropertyName);
final ExpandTreeBuilder childExpandBuilder = (expandBuilder != null) ? final ExpandTreeBuilder childExpandBuilder = (expandBuilder != null) ? expandBuilder.expand(edmNavigationProperty)
expandBuilder.expand(edmNavigationProperty) : null; : null;
EdmEntityType derivedEdmEntityType = (EdmEntityType)getDerivedType( EdmEntityType derivedEdmEntityType = (EdmEntityType) getDerivedType(
edmNavigationProperty.getType(), jsonNode); edmNavigationProperty.getType(), jsonNode);
if (jsonNode.isArray() && edmNavigationProperty.isCollection()) { if (jsonNode.isArray() && edmNavigationProperty.isCollection()) {
link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE); link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
@ -398,8 +398,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
&& !edmNavigationProperty.isCollection()) { && !edmNavigationProperty.isCollection()) {
link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE);
if (!jsonNode.isNull()) { if (!jsonNode.isNull()) {
Entity inlineEntity = consumeEntityNode(derivedEdmEntityType, (ObjectNode) jsonNode, Entity inlineEntity = consumeEntityNode(derivedEdmEntityType, (ObjectNode) jsonNode, childExpandBuilder);
childExpandBuilder);
link.setInlineEntity(inlineEntity); link.setInlineEntity(inlineEntity);
} }
} else { } else {
@ -537,9 +536,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
isNullable, maxLength, precision, scale, isUnicode, mapping, arrayElement); isNullable, maxLength, precision, scale, isUnicode, mapping, arrayElement);
valueArray.add(value); valueArray.add(value);
} }
property.setValue(type.getKind() == EdmTypeKind.ENUM ? property.setValue(type.getKind() == EdmTypeKind.ENUM ? ValueType.COLLECTION_ENUM : ValueType.COLLECTION_PRIMITIVE,
ValueType.COLLECTION_ENUM :
ValueType.COLLECTION_PRIMITIVE,
valueArray); valueArray);
break; break;
case COMPLEX: case COMPLEX:
@ -631,14 +628,10 @@ public class ODataJsonDeserializer implements ODataDeserializer {
*/ */
private Class<?> getJavaClassForPrimitiveType(final EdmMapping mapping, final EdmPrimitiveType type) { private Class<?> getJavaClassForPrimitiveType(final EdmMapping mapping, final EdmPrimitiveType type) {
final EdmPrimitiveType edmPrimitiveType = final EdmPrimitiveType edmPrimitiveType =
type.getKind() == EdmTypeKind.ENUM ? type.getKind() == EdmTypeKind.ENUM ? ((EdmEnumType) type).getUnderlyingType() : type
((EdmEnumType) type).getUnderlyingType() : .getKind() == EdmTypeKind.DEFINITION ? ((EdmTypeDefinition) type).getUnderlyingType() : type;
type.getKind() == EdmTypeKind.DEFINITION ? return mapping == null || mapping.getMappedJavaClass() == null ? edmPrimitiveType.getDefaultType() : mapping
((EdmTypeDefinition) type).getUnderlyingType() : .getMappedJavaClass();
type;
return mapping == null || mapping.getMappedJavaClass() == null ?
edmPrimitiveType.getDefaultType() :
mapping.getMappedJavaClass();
} }
/** /**
@ -841,7 +834,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
} }
EdmStructuredType currentEdmType = null; EdmStructuredType currentEdmType = null;
if(edmType instanceof EdmEntityType) { if (edmType instanceof EdmEntityType) {
currentEdmType = serviceMetadata.getEdm() currentEdmType = serviceMetadata.getEdm()
.getEntityType(new FullQualifiedName(odataType)); .getEntityType(new FullQualifiedName(odataType));
} else { } else {

View File

@ -30,11 +30,63 @@ import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.deserializer.DeserializerResult;
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest; import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest;
import org.junit.Test; import org.junit.Test;
public class ODataDeserializerDeepInsertTest extends AbstractODataDeserializerTest { public class ODataDeserializerDeepInsertTest extends AbstractODataDeserializerTest {
@Test
public void unbalancedESAllPrim() throws Exception {
final DeserializerResult result = deserializeWithResult("UnbalancedESAllPrimFeed.json");
ExpandOption root = result.getExpandTree();
assertEquals(1, root.getExpandItems().size());
ExpandItem etTwoPrimManyLevel = root.getExpandItems().get(0);
assertEquals("NavPropertyETTwoPrimMany", etTwoPrimManyLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertEquals(1, etTwoPrimManyLevel.getExpandOption().getExpandItems().size());
ExpandItem etAllPrimOneLevel = etTwoPrimManyLevel.getExpandOption().getExpandItems().get(0);
assertEquals("NavPropertyETAllPrimOne", etAllPrimOneLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertEquals(1, etAllPrimOneLevel.getExpandOption().getExpandItems().size());
ExpandItem etTwoPrimOneLevel = etAllPrimOneLevel.getExpandOption().getExpandItems().get(0);
assertEquals("NavPropertyETTwoPrimOne", etTwoPrimOneLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertNull(etTwoPrimOneLevel.getExpandOption());
}
@Test
public void unbalancedESAllPrim2() throws Exception {
final DeserializerResult result = deserializeWithResult("UnbalancedESAllPrimFeed2.json");
ExpandOption root = result.getExpandTree();
assertEquals(1, root.getExpandItems().size());
ExpandItem etTwoPrimManyLevel = root.getExpandItems().get(0);
assertEquals("NavPropertyETTwoPrimMany", etTwoPrimManyLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertEquals(1, etTwoPrimManyLevel.getExpandOption().getExpandItems().size());
ExpandItem etAllPrimOneLevel = etTwoPrimManyLevel.getExpandOption().getExpandItems().get(0);
assertEquals("NavPropertyETAllPrimOne", etAllPrimOneLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertEquals(2, etAllPrimOneLevel.getExpandOption().getExpandItems().size());
ExpandItem etTwoPrimOneLevel = etAllPrimOneLevel.getExpandOption().getExpandItems().get(0);
assertEquals("NavPropertyETTwoPrimMany", etTwoPrimOneLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertNull(etTwoPrimOneLevel.getExpandOption());
etTwoPrimOneLevel = etAllPrimOneLevel.getExpandOption().getExpandItems().get(1);
assertEquals("NavPropertyETTwoPrimOne", etTwoPrimOneLevel.getResourcePath().getUriResourceParts().get(0)
.getSegmentValue());
assertNull(etTwoPrimOneLevel.getExpandOption());
}
@Test @Test
public void esAllPrimExpandedToOne() throws Exception { public void esAllPrimExpandedToOne() throws Exception {
final Entity entity = deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json"); final Entity entity = deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json");
@ -152,4 +204,10 @@ public class ODataDeserializerDeepInsertTest extends AbstractODataDeserializerTe
return ODataJsonDeserializerEntityTest.deserialize(getFileAsStream(resourceName), return ODataJsonDeserializerEntityTest.deserialize(getFileAsStream(resourceName),
"ETAllPrim", ContentType.JSON); "ETAllPrim", ContentType.JSON);
} }
private DeserializerResult deserializeWithResult(final String resourceName) throws IOException,
DeserializerException {
return ODataJsonDeserializerEntityTest.deserializeWithResult(getFileAsStream(resourceName),
"ETAllPrim", ContentType.JSON);
}
} }

View File

@ -49,6 +49,7 @@ import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; import org.apache.olingo.commons.core.edm.primitivetype.EdmDate;
import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.deserializer.DeserializerResult;
import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.api.deserializer.ODataDeserializer;
import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest; import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest;
import org.junit.Assert; import org.junit.Assert;
@ -1348,6 +1349,12 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe
.getEntity(); .getEntity();
} }
protected static DeserializerResult deserializeWithResult(final InputStream stream, final String entityTypeName,
final ContentType contentType) throws DeserializerException {
return OData.newInstance().createDeserializer(contentType, metadata)
.entity(stream, edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName)));
}
private static Entity deserialize(final String entityString, final String entityTypeName, private static Entity deserialize(final String entityString, final String entityTypeName,
final ContentType contentType) throws DeserializerException { final ContentType contentType) throws DeserializerException {
return deserialize(new ByteArrayInputStream(entityString.getBytes()), entityTypeName, contentType); return deserialize(new ByteArrayInputStream(entityString.getBytes()), entityTypeName, contentType);

View File

@ -0,0 +1,23 @@
{
"@odata.context": "$metadata#ESAllPrim\/$entity",
"@odata.metadataEtag": "W\/\"4efd6576-89c0-487c-8d6c-584e2acbae16\"",
"PropertyInt16": 1,
"NavPropertyETTwoPrimMany": [
{
"PropertyInt16": 2,
"NavPropertyETAllPrimOne": {
"PropertyInt16": 3
}
},
{
"PropertyInt16": 2,
"NavPropertyETAllPrimOne": {
"PropertyInt16": 3,
"NavPropertyETTwoPrimOne": {
"PropertyInt16": 32766,
"PropertyString": "Innermost Entry"
}
}
}
]
}

View File

@ -0,0 +1,24 @@
{
"@odata.context": "$metadata#ESAllPrim\/$entity",
"@odata.metadataEtag": "W\/\"4efd6576-89c0-487c-8d6c-584e2acbae16\"",
"PropertyInt16": 1,
"NavPropertyETTwoPrimMany": [
{
"PropertyInt16": 2,
"NavPropertyETAllPrimOne": {
"PropertyInt16": 3,
"NavPropertyETTwoPrimMany": []
}
},
{
"PropertyInt16": 2,
"NavPropertyETAllPrimOne": {
"PropertyInt16": 3,
"NavPropertyETTwoPrimOne": {
"PropertyInt16": 32766,
"PropertyString": "Innermost Entry"
}
}
}
]
}