Repository: olingo-odata4 Updated Branches: refs/heads/master f2fd03553 -> 120adfea5
OLINGO-1009: adding cycle detection when =max is used during serialization Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/120adfea Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/120adfea Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/120adfea Branch: refs/heads/master Commit: 120adfea500adb4bbf50370a6c4353df23938a28 Parents: f2fd035 Author: Ramesh Reddy <[email protected]> Authored: Thu Sep 8 21:07:18 2016 -0500 Committer: Ramesh Reddy <[email protected]> Committed: Fri Sep 9 14:23:26 2016 -0500 ---------------------------------------------------------------------- .../ExpandWithSystemQueryOptionsITCase.java | 43 +- .../serializer/json/ODataJsonSerializer.java | 143 ++++--- .../core/serializer/xml/ODataXmlSerializer.java | 218 ++++++---- .../olingo/server/tecsvc/data/DataCreator.java | 80 +++- .../tecsvc/provider/ContainerProvider.java | 8 +- .../tecsvc/provider/EntityTypeProvider.java | 11 +- .../tecsvc/provider/PropertyProvider.java | 16 + .../xml/ODataXmlDeserializerTest.java | 1 - .../json/ODataJsonSerializerTest.java | 158 ++++++- .../serializer/xml/ODataXmlSerializerTest.java | 408 ++++++++++++++++++- 10 files changed, 873 insertions(+), 213 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java index 5f44351..e1dd2f5 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/ExpandWithSystemQueryOptionsITCase.java @@ -419,26 +419,9 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas .getEntities() .get(0); - assertShortOrInt(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); - assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); - - assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)); - assertEquals(2, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY) - .asInlineEntitySet() - .getEntitySet() - .getEntities() - .size()); - - final List<ClientEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY) - .asInlineEntitySet() - .getEntitySet() - .getEntities(); - - assertShortOrInt(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); - assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); - - assertShortOrInt(1, fourthLevelEntites.get(1).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); - assertEquals("2", fourthLevelEntites.get(1).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + // cycle happens here + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", entityThirdLevel.getId().toASCIIString()); + assertEquals(0, entityThirdLevel.getProperties().size()); } @Test @@ -495,23 +478,9 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas .getEntities() .get(0); - assertShortOrInt(1, entityThirdLevel.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); - assertEquals("1", entityThirdLevel.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); - - assertNotNull(entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY)); - assertEquals(1, entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY) - .asInlineEntitySet() - .getEntitySet() - .getEntities() - .size()); - - final List<ClientEntity> fourthLevelEntites = entityThirdLevel.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY) - .asInlineEntitySet() - .getEntitySet() - .getEntities(); - - assertShortOrInt(1, fourthLevelEntites.get(0).getProperty(PROPERTY_INT16).getPrimitiveValue().toValue()); - assertEquals("1", fourthLevelEntites.get(0).getProperty(PROPERTY_STRING).getPrimitiveValue().toValue()); + // cycle happens here + assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')", entityThirdLevel.getId().toASCIIString()); + assertEquals(0, entityThirdLevel.getProperties().size()); } @Test http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java index 513df38..f6e6015 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java @@ -166,10 +166,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer { writeOperations(entitySet.getOperations(), json); json.writeFieldName(Constants.VALUE); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, false, json); + writeEntitySet(metadata, entityType, entitySet, null, null, null, false, null, json); } else { writeEntitySet(metadata, entityType, entitySet, - options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), json); + options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), null, json); } writeNextLink(entitySet, json); @@ -212,10 +212,10 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } json.writeFieldName(Constants.VALUE); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, false, json); + writeEntitySet(metadata, entityType, entitySet, null, null, null, false, null, json); } else { writeEntitySet(metadata, entityType, entitySet, - options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), json); + options.getExpand(), null, options.getSelect(), options.getWriteOnlyReferences(), null, json); } // next link not supported by default for streaming results // writeNextLink(entitySet, json); @@ -243,6 +243,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { null, options == null ? null : options.getSelect(), options == null ? false : options.getWriteOnlyReferences(), + null, json); json.close(); @@ -268,7 +269,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, final AbstractEntityCollection entitySet, final ExpandOption expand, Integer toDepth, final SelectOption select, - final boolean onlyReference, final JsonGenerator json) throws IOException, + final boolean onlyReference, final Set<String> ancestors, final JsonGenerator json) throws IOException, SerializerException { json.writeStartArray(); for (final Entity entity : entitySet) { @@ -277,7 +278,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { json.writeStringField(Constants.JSON_ID, getEntityId(entity)); json.writeEndObject(); } else { - writeEntity(metadata, entityType, entity, null, expand, toDepth, select, false, json); + writeEntity(metadata, entityType, entity, null, expand, toDepth, select, false, ancestors, json); } } json.writeEndArray(); @@ -310,59 +311,81 @@ public class ODataJsonSerializer extends AbstractODataSerializer { return true; } - public void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity, + private boolean cycleDetected(final Set<String> ancestors, String child) { + if (ancestors == null) { + return false; + } + return ancestors.contains(child); + } + + protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity, final ContextURL contextURL, final ExpandOption expand, Integer toDepth, - final SelectOption select, final boolean onlyReference, final JsonGenerator json) + final SelectOption select, final boolean onlyReference, Set<String> ancestors, final JsonGenerator json) throws IOException, SerializerException { - json.writeStartObject(); - if (!isODataMetadataNone) { - // top-level entity - if (contextURL != null) { - writeContextURL(contextURL, json); - writeMetadataETag(metadata, json); - } - if (entity.getETag() != null) { - json.writeStringField(Constants.JSON_ETAG, entity.getETag()); + boolean cycle = false; + if (expand != null && cycleDetected(ancestors, getEntityId(entity))) { + cycle = true; + } else { + if (ancestors == null) { + ancestors = new HashSet<String>(); } - if (entityType.hasStream()) { - if (entity.getMediaETag() != null) { - json.writeStringField(Constants.JSON_MEDIA_ETAG, entity.getMediaETag()); - } - if (entity.getMediaContentType() != null) { - json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, entity.getMediaContentType()); + ancestors.add(getEntityId(entity)); + } + try { + json.writeStartObject(); + if (!isODataMetadataNone) { + // top-level entity + if (contextURL != null) { + writeContextURL(contextURL, json); + writeMetadataETag(metadata, json); } - if (entity.getMediaContentSource() != null) { - json.writeStringField(Constants.JSON_MEDIA_READ_LINK, entity.getMediaContentSource().toString()); + if (entity.getETag() != null) { + json.writeStringField(Constants.JSON_ETAG, entity.getETag()); } - if (entity.getMediaEditLinks() != null && !entity.getMediaEditLinks().isEmpty()) { - json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, entity.getMediaEditLinks().get(0).getHref()); + if (entityType.hasStream()) { + if (entity.getMediaETag() != null) { + json.writeStringField(Constants.JSON_MEDIA_ETAG, entity.getMediaETag()); + } + if (entity.getMediaContentType() != null) { + json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, entity.getMediaContentType()); + } + if (entity.getMediaContentSource() != null) { + json.writeStringField(Constants.JSON_MEDIA_READ_LINK, entity.getMediaContentSource().toString()); + } + if (entity.getMediaEditLinks() != null && !entity.getMediaEditLinks().isEmpty()) { + json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, entity.getMediaEditLinks().get(0).getHref()); + } } } - } - if (onlyReference) { - json.writeStringField(Constants.JSON_ID, getEntityId(entity)); - } else { - final EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType()); - if ((!isODataMetadataNone && !resolvedType.equals(entityType)) || isODataMetadataFull) { - json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType()); - } - if ((!isODataMetadataNone && !areKeyPredicateNamesSelected(select, resolvedType)) || isODataMetadataFull) { + if (cycle || onlyReference) { json.writeStringField(Constants.JSON_ID, getEntityId(entity)); - } - - if (isODataMetadataFull) { - if (entity.getSelfLink() != null) { - json.writeStringField(Constants.JSON_READ_LINK, entity.getSelfLink().getHref()); + } else { + final EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType()); + if ((!isODataMetadataNone && !resolvedType.equals(entityType)) || isODataMetadataFull) { + json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType()); + } + if ((!isODataMetadataNone && !areKeyPredicateNamesSelected(select, resolvedType)) || isODataMetadataFull) { + json.writeStringField(Constants.JSON_ID, getEntityId(entity)); } - if (entity.getEditLink() != null) { - json.writeStringField(Constants.JSON_EDIT_LINK, entity.getEditLink().getHref()); + + if (isODataMetadataFull) { + if (entity.getSelfLink() != null) { + json.writeStringField(Constants.JSON_READ_LINK, entity.getSelfLink().getHref()); + } + if (entity.getEditLink() != null) { + json.writeStringField(Constants.JSON_EDIT_LINK, entity.getEditLink().getHref()); + } } + + writeProperties(metadata, resolvedType, entity.getProperties(), select, json); + writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, ancestors, json); + writeOperations(entity.getOperations(), json); } - - writeProperties(metadata, resolvedType, entity.getProperties(), select, json); - writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, json); - writeOperations(entity.getOperations(), json); json.writeEndObject(); + } finally { + if (!cycle && ancestors != null) { + ancestors.remove(getEntityId(entity)); + } } } @@ -446,7 +469,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { protected void writeNavigationProperties(final ServiceMetadata metadata, final EdmStructuredType type, final Linked linked, final ExpandOption expand, final Integer toDepth, - final JsonGenerator json) throws SerializerException, IOException { + final Set<String> ancestors, final JsonGenerator json) throws SerializerException, IOException { if ((toDepth != null && toDepth > 1) || (toDepth == null && ExpandSelectHelper.hasExpand(expand))) { final ExpandItem expandAll = ExpandSelectHelper.getExpandAll(expand); for (final String propertyName : type.getNavigationPropertyNames()) { @@ -460,6 +483,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { innerOptions == null ? null : innerOptions.getCountOption(), innerOptions == null ? false : innerOptions.hasCountPath(), innerOptions == null ? false : innerOptions.isRef(), + ancestors, json); continue; } @@ -493,14 +517,15 @@ public class ODataJsonSerializer extends AbstractODataSerializer { levels = levelsOption.getValue(); } } - + writeExpandedNavigationProperty(metadata, property, navigationLink, - childExpand, levels, - innerOptions == null ? null : innerOptions.getSelectOption(), - innerOptions == null ? null : innerOptions.getCountOption(), - innerOptions == null ? false : innerOptions.hasCountPath(), - innerOptions == null ? false : innerOptions.isRef(), - json); + childExpand, levels, + innerOptions == null ? null : innerOptions.getSelectOption(), + innerOptions == null ? null : innerOptions.getCountOption(), + innerOptions == null ? false : innerOptions.hasCountPath(), + innerOptions == null ? false : innerOptions.isRef(), + ancestors, + json); } } } else if (isODataMetadataFull) { @@ -521,7 +546,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { final ServiceMetadata metadata, final EdmNavigationProperty property, final Link navigationLink, final ExpandOption innerExpand, Integer toDepth, final SelectOption innerSelect, final CountOption innerCount, - final boolean writeOnlyCount, final boolean writeOnlyRef, + final boolean writeOnlyCount, final boolean writeOnlyRef, final Set<String> ancestors, final JsonGenerator json) throws IOException, SerializerException { if (property.isCollection()) { @@ -545,7 +570,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } json.writeFieldName(property.getName()); writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, toDepth, - innerSelect, writeOnlyRef, json); + innerSelect, writeOnlyRef, ancestors, json); } } } else { @@ -554,7 +579,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { json.writeNull(); } else { writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null, - innerExpand, toDepth, innerSelect, writeOnlyRef, json); + innerExpand, toDepth, innerSelect, writeOnlyRef, ancestors, json); } } } @@ -883,7 +908,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { writeProperties(metadata, type, values, options == null ? null : options.getSelect(), json); if (!property.isNull() && property.isComplex()) { writeNavigationProperties(metadata, type, property.asComplex(), - options == null ? null : options.getExpand(), null, json); + options == null ? null : options.getExpand(), null, null, json); } json.writeEndObject(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java index d6de50f..56ee391 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializer.java @@ -259,11 +259,11 @@ public class ODataXmlSerializer extends AbstractODataSerializer { boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences()); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, null, writer, writeOnlyRef); + writeEntitySet(metadata, entityType, entitySet, null, null, null, null, writer, writeOnlyRef, null); } else { writeEntitySet(metadata, entityType, entitySet, options.getExpand(), null, - options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef); + options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef, null); } writer.writeEndElement(); @@ -317,11 +317,11 @@ public class ODataXmlSerializer extends AbstractODataSerializer { boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences()); if (options == null) { - writeEntitySet(metadata, entityType, entitySet, null, null, null, null, writer, writeOnlyRef); + writeEntitySet(metadata, entityType, entitySet, null, null, null, null, writer, writeOnlyRef,null); } else { writeEntitySet(metadata, entityType, entitySet, options.getExpand(), null, - options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef); + options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef,null); } writer.writeEndElement(); @@ -363,7 +363,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { null, options == null ? null : options.getSelect(), options == null ? null : options.xml10InvalidCharReplacement(), - writer, true, false); + writer, true, false, null); writer.writeEndDocument(); writer.flush(); @@ -404,99 +404,135 @@ public class ODataXmlSerializer extends AbstractODataSerializer { protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, final AbstractEntityCollection entitySet, final ExpandOption expand, final Integer toDepth, final SelectOption select, - final String xml10InvalidCharReplacement,final XMLStreamWriter writer, final boolean writeOnlyRef) + final String xml10InvalidCharReplacement,final XMLStreamWriter writer, + final boolean writeOnlyRef,final Set<String> ancestors) throws XMLStreamException, SerializerException { for (final Entity entity : entitySet) { writeEntity(metadata, entityType, entity, null, expand, toDepth, select, - xml10InvalidCharReplacement, writer, false, writeOnlyRef); + xml10InvalidCharReplacement, writer, false, writeOnlyRef, ancestors); } } + + /** + * Get the ascii representation of the entity id + * or thrown an {@link SerializerException} if id is <code>null</code>. + * + * @param entity the entity + * @return ascii representation of the entity id + */ + private String getEntityId(Entity entity) throws SerializerException { + if(entity.getId() == null) { + throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID); + } + return entity.getId().toASCIIString(); + } + private boolean cycleDetected(final Set<String> ancestors, String child) { + if (ancestors == null) { + return false; + } + return ancestors.contains(child); + } + protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity, final ContextURL contextURL, final ExpandOption expand, final Integer toDepth, final SelectOption select, final String xml10InvalidCharReplacement, - final XMLStreamWriter writer, final boolean top, final boolean writeOnlyRef) + final XMLStreamWriter writer, final boolean top, final boolean writeOnlyRef, Set<String> ancestors) throws XMLStreamException, SerializerException { + boolean cycle = false; + if (expand != null && cycleDetected(ancestors, getEntityId(entity))) { + cycle = true; + } else { + if (ancestors == null) { + ancestors = new HashSet<String>(); + } + ancestors.add(getEntityId(entity)); + } - if (writeOnlyRef) { + if (cycle || writeOnlyRef) { writeReference(entity, contextURL, writer, top); return; } - writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM); - if (top) { - writer.writeNamespace(ATOM, NS_ATOM); - writer.writeNamespace(METADATA, NS_METADATA); - writer.writeNamespace(DATA, NS_DATA); - - if (contextURL != null) { // top-level entity - writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT, - ContextURLBuilder.create(contextURL).toASCIIString()); - writeMetadataETag(metadata, writer); + try { + writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM); + if (top) { + writer.writeNamespace(ATOM, NS_ATOM); + writer.writeNamespace(METADATA, NS_METADATA); + writer.writeNamespace(DATA, NS_DATA); + + if (contextURL != null) { // top-level entity + writer.writeAttribute(METADATA, NS_METADATA, Constants.CONTEXT, + ContextURLBuilder.create(contextURL).toASCIIString()); + writeMetadataETag(metadata, writer); + } } - } - if (entity.getETag() != null) { - writer.writeAttribute(METADATA, NS_METADATA, Constants.ATOM_ATTR_ETAG, entity.getETag()); - } - - if (entity.getId() != null) { - writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_ID); - writer.writeCharacters(entity.getId().toASCIIString()); - writer.writeEndElement(); - } - - writerAuthorInfo(entity.getTitle(), writer); - - if (entity.getId() != null) { - writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_LINK); - writer.writeAttribute(Constants.ATTR_REL, Constants.EDIT_LINK_REL); - writer.writeAttribute(Constants.ATTR_HREF, entity.getId().toASCIIString()); - writer.writeEndElement(); - } - - if (entityType.hasStream()) { - writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT); - writer.writeAttribute(Constants.ATTR_TYPE, entity.getMediaContentType()); - if (entity.getMediaContentSource() != null) { - writer.writeAttribute(Constants.ATOM_ATTR_SRC, entity.getMediaContentSource().toString()); - } else { - String id = entity.getId().toASCIIString(); - writer.writeAttribute(Constants.ATOM_ATTR_SRC, - id + (id.endsWith("/") ? "" : "/") + "$value"); + if (entity.getETag() != null) { + writer.writeAttribute(METADATA, NS_METADATA, Constants.ATOM_ATTR_ETAG, entity.getETag()); } + + if (entity.getId() != null) { + writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_ID); + writer.writeCharacters(entity.getId().toASCIIString()); + writer.writeEndElement(); + } + + writerAuthorInfo(entity.getTitle(), writer); + + if (entity.getId() != null) { + writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_LINK); + writer.writeAttribute(Constants.ATTR_REL, Constants.EDIT_LINK_REL); + writer.writeAttribute(Constants.ATTR_HREF, entity.getId().toASCIIString()); + writer.writeEndElement(); + } + + if (entityType.hasStream()) { + writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT); + writer.writeAttribute(Constants.ATTR_TYPE, entity.getMediaContentType()); + if (entity.getMediaContentSource() != null) { + writer.writeAttribute(Constants.ATOM_ATTR_SRC, entity.getMediaContentSource().toString()); + } else { + String id = entity.getId().toASCIIString(); + writer.writeAttribute(Constants.ATOM_ATTR_SRC, + id + (id.endsWith("/") ? "" : "/") + "$value"); + } + writer.writeEndElement(); + } + + // write media links + for (Link link : entity.getMediaEditLinks()) { + writeLink(writer, link); + } + + EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType()); + writeNavigationProperties(metadata, resolvedType, entity, expand, + toDepth, xml10InvalidCharReplacement, ancestors, writer); + + writer.writeStartElement(ATOM, Constants.ATOM_ELEM_CATEGORY, NS_ATOM); + writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, Constants.NS_SCHEME); + writer.writeAttribute(Constants.ATOM_ATTR_TERM, + "#" + resolvedType.getFullQualifiedName().getFullQualifiedNameAsString()); writer.writeEndElement(); + + // In the case media, content is sibiling + if (!entityType.hasStream()) { + writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT); + writer.writeAttribute(Constants.ATTR_TYPE, "application/xml"); + } + + writer.writeStartElement(METADATA, Constants.PROPERTIES, NS_METADATA); + writeProperties(metadata, resolvedType, entity.getProperties(), select, xml10InvalidCharReplacement, writer); + writer.writeEndElement(); // properties + + if (!entityType.hasStream()) { // content + writer.writeEndElement(); + } + + writeOperations(entity.getOperations(), writer); + + writer.writeEndElement(); // entry + } finally { + ancestors.remove(getEntityId(entity)); } - - // write media links - for (Link link : entity.getMediaEditLinks()) { - writeLink(writer, link); - } - - EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType()); - writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, xml10InvalidCharReplacement, writer); - - writer.writeStartElement(ATOM, Constants.ATOM_ELEM_CATEGORY, NS_ATOM); - writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, Constants.NS_SCHEME); - writer.writeAttribute(Constants.ATOM_ATTR_TERM, - "#" + resolvedType.getFullQualifiedName().getFullQualifiedNameAsString()); - writer.writeEndElement(); - - // In the case media, content is sibiling - if (!entityType.hasStream()) { - writer.writeStartElement(NS_ATOM, Constants.ATOM_ELEM_CONTENT); - writer.writeAttribute(Constants.ATTR_TYPE, "application/xml"); - } - - writer.writeStartElement(METADATA, Constants.PROPERTIES, NS_METADATA); - writeProperties(metadata, resolvedType, entity.getProperties(), select, xml10InvalidCharReplacement, writer); - writer.writeEndElement(); // properties - - if (!entityType.hasStream()) { // content - writer.writeEndElement(); - } - - writeOperations(entity.getOperations(), writer); - - writer.writeEndElement(); // entry } private void writeOperations(final List<Operation> operations, @@ -598,7 +634,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { protected void writeNavigationProperties(final ServiceMetadata metadata, final EdmStructuredType type, final Linked linked, final ExpandOption expand, final Integer toDepth, - final String xml10InvalidCharReplacement, final XMLStreamWriter writer) + final String xml10InvalidCharReplacement, final Set<String> ancestors, final XMLStreamWriter writer) throws SerializerException, XMLStreamException { if ((toDepth != null && toDepth > 1) || (toDepth == null && ExpandSelectHelper.hasExpand(expand))) { final ExpandItem expandAll = ExpandSelectHelper.getExpandAll(expand); @@ -615,7 +651,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { innerOptions == null ? null : innerOptions.getCountOption(), innerOptions == null ? false : innerOptions.hasCountPath(), innerOptions == null ? false : innerOptions.isRef(), - xml10InvalidCharReplacement, writer); + xml10InvalidCharReplacement, ancestors, writer); writer.writeEndElement(); writer.writeEndElement(); continue; @@ -654,12 +690,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeLink(writer, navigationLink, false); writer.writeStartElement(METADATA, Constants.ATOM_ELEM_INLINE, NS_METADATA); writeExpandedNavigationProperty(metadata, property, navigationLink, - childExpand, levels, - innerOptions == null ? null : innerOptions.getSelectOption(), - innerOptions == null ? null : innerOptions.getCountOption(), - innerOptions == null ? false : innerOptions.hasCountPath(), - innerOptions == null ? false : innerOptions.isRef(), - xml10InvalidCharReplacement, writer); + childExpand, levels, + innerOptions == null ? null : innerOptions.getSelectOption(), + innerOptions == null ? null : innerOptions.getCountOption(), + innerOptions == null ? false : innerOptions.hasCountPath(), + innerOptions == null ? false : innerOptions.isRef(), + xml10InvalidCharReplacement, ancestors, writer); writer.writeEndElement(); writer.writeEndElement(); } else { @@ -720,6 +756,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer { final ExpandOption innerExpand, final Integer toDepth, final SelectOption innerSelect, final CountOption coutOption, final boolean writeNavigationCount, final boolean writeOnlyRef,final String xml10InvalidCharReplacement, + final Set<String> ancestors, final XMLStreamWriter writer) throws XMLStreamException, SerializerException { if (property.isCollection()) { if (navigationLink != null && navigationLink.getInlineEntitySet() != null) { @@ -731,14 +768,15 @@ public class ODataXmlSerializer extends AbstractODataSerializer { writeCount(navigationLink.getInlineEntitySet(), writer); } writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, toDepth, - innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef); + innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef, ancestors); } writer.writeEndElement(); } } else { if (navigationLink != null && navigationLink.getInlineEntity() != null) { writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null, - innerExpand, toDepth, innerSelect, xml10InvalidCharReplacement, writer, false, writeOnlyRef); + innerExpand, toDepth, innerSelect, xml10InvalidCharReplacement, writer, + false, writeOnlyRef, ancestors); } } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java index 60f0273..dfc1d78 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java @@ -87,11 +87,13 @@ public class DataCreator { data.put("ESMixEnumDefCollComp", createESMixEnumDefCollComp(edm, odata)); data.put("ESStream", createESStream(edm, odata)); data.put("ESWithStream", createESWithStream(edm, odata)); + data.put("ESPeople", createESPeople(edm, odata)); linkESTwoPrim(data); linkESAllPrim(data); linkESKeyNav(data); linkESTwoKeyNav(data); + linkESPeople(data); } private EntityCollection createESMixEnumDefCollComp(Edm edm, OData odata) { @@ -1101,33 +1103,57 @@ public class DataCreator { return entityCollection; } - private EntityCollection createESWithStream(final Edm edm, final OData odata) { - EntityCollection entityCollection = new EntityCollection(); + private EntityCollection createESPeople(final Edm edm, final OData odata) { + EntityCollection entityCollection = new EntityCollection(); - Link readLink = new Link(); - readLink.setRel(Constants.NS_MEDIA_READ_LINK_REL); - readLink.setHref("readLink"); - - entityCollection.getEntities().add(new Entity() - .addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE)) - .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, readLink))); + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("id", 0)) + .addProperty(createPrimitive("name", "A"))); + + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("id", 1)) + .addProperty(createPrimitive("name", "B"))); - Link editLink = new Link(); - editLink.setRel(Constants.NS_MEDIA_EDIT_LINK_REL); - editLink.setHref("http://mediaserver:1234/editLink"); - editLink.setMediaETag("eTag"); - editLink.setType("image/jpeg"); + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("id", 2)) + .addProperty(createPrimitive("name", "C"))); - entityCollection.getEntities().add(new Entity() - .addProperty(createPrimitive("PropertyInt16", (short) 7)) - .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, editLink))); + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("id", 3)) + .addProperty(createPrimitive("name", "D"))); - setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETStream)); - createEntityId(edm, odata, "ESWithStream", entityCollection); - createOperations("ESWithStream", entityCollection, EntityTypeProvider.nameETStream); - return entityCollection; + setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETPeople)); + createEntityId(edm, odata, "ESPeople", entityCollection); + return entityCollection; } + private EntityCollection createESWithStream(final Edm edm, final OData odata) { + EntityCollection entityCollection = new EntityCollection(); + + Link readLink = new Link(); + readLink.setRel(Constants.NS_MEDIA_READ_LINK_REL); + readLink.setHref("readLink"); + + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE)) + .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, readLink))); + + Link editLink = new Link(); + editLink.setRel(Constants.NS_MEDIA_EDIT_LINK_REL); + editLink.setHref("http://mediaserver:1234/editLink"); + editLink.setMediaETag("eTag"); + editLink.setType("image/jpeg"); + + entityCollection.getEntities().add(new Entity() + .addProperty(createPrimitive("PropertyInt16", (short) 7)) + .addProperty(new Property(null, "PropertyStream", ValueType.PRIMITIVE, editLink))); + + setEntityType(entityCollection, edm.getEntityType(EntityTypeProvider.nameETStream)); + createEntityId(edm, odata, "ESWithStream", entityCollection); + createOperations("ESWithStream", entityCollection, EntityTypeProvider.nameETStream); + return entityCollection; + } + @SuppressWarnings("unchecked") private Property createCollPropertyComp() { return createComplexCollection("CollPropertyComp", @@ -1259,6 +1285,18 @@ public class DataCreator { + " <circle cx=\"50\" cy=\"50\" r=\"42\"/>\n" + " </g>\n" + "</svg>\n").getBytes(Charset.forName("UTF-8")); } + private void linkESPeople(final Map<String, EntityCollection> data) { + final EntityCollection entityCollection = data.get("ESPeople"); + final List<Entity> targetEntities = data.get("ESPeople").getEntities(); + + setLinks(entityCollection.getEntities().get(0), "friends", targetEntities.get(1), + targetEntities.get(2)); + setLinks(entityCollection.getEntities().get(1), "friends", targetEntities.get(0), + targetEntities.get(2)); + setLinks(entityCollection.getEntities().get(2), "friends", targetEntities.get(0), + targetEntities.get(3)); + } + private void linkESTwoPrim(final Map<String, EntityCollection> data) { final EntityCollection entityCollection = data.get("ESTwoPrim"); final List<Entity> targetEntities = data.get("ESAllPrim").getEntities(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java index 18fd7a0..cb554f7 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/ContainerProvider.java @@ -103,6 +103,7 @@ public class ContainerProvider { entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESTwoKeyNavCont")); entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESWithStream")); entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, ES_STREAM)); + entitySets.add(prov.getEntitySet(ContainerProvider.nameContainer, "ESPeople")); // Singletons List<CsdlSingleton> singletons = new ArrayList<CsdlSingleton>(); @@ -631,9 +632,14 @@ public class ContainerProvider { .setValue("Entity set with a stream type")), new CsdlAnnotation().setTerm(TermProvider.TERM_DATA.getFullQualifiedNameAsString()).setExpression( new CsdlConstantExpression(CsdlConstantExpression.ConstantExpressionType.Bool, "true")))); + } else if (name.equals("ESPeople")) { + return new CsdlEntitySet() + .setName("ESPeople") + .setType(EntityTypeProvider.nameETPeople) + .setNavigationPropertyBindings(Arrays.asList(new CsdlNavigationPropertyBinding().setPath("friends") + .setTarget("ESPeople"))); } } - return null; } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java index eac9743..6e9a1b7 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/EntityTypeProvider.java @@ -84,6 +84,7 @@ public class EntityTypeProvider { public static final FullQualifiedName nameETStream = new FullQualifiedName(SchemaProvider.NAMESPACE, "ETWithStream"); + public static final FullQualifiedName nameETPeople = new FullQualifiedName(SchemaProvider.NAMESPACE, "ETPeople"); public CsdlEntityType getEntityType(final FullQualifiedName entityTypeName) throws ODataException { if(entityTypeName.equals(nameETAllPrimDefaultValues)){ @@ -372,7 +373,7 @@ public class EntityTypeProvider { PropertyProvider.navPropertyETTwoKeyNavContOneCT_ETTwoKeyNav, PropertyProvider.collectionNavPropertyETTwoKeyNavContMany_CT_ETTwoKeyNav )); -} else if (entityTypeName.equals(nameETTwoKeyNav)) { + } else if (entityTypeName.equals(nameETTwoKeyNav)) { return new CsdlEntityType() .setName("ETTwoKeyNav") .setKey(Arrays.asList( @@ -472,6 +473,14 @@ public class EntityTypeProvider { .setProperties(Arrays.asList( PropertyProvider.propertyInt16_NotNullable, PropertyProvider.propertyStream)); + } else if (entityTypeName.equals(nameETPeople)) { + return new CsdlEntityType() + .setName(nameETPeople.getName()) + .setKey(Arrays.asList(new CsdlPropertyRef().setName("id"))) + .setProperties(Arrays.asList( + PropertyProvider.propertyId, + PropertyProvider.propertyName)) + .setNavigationProperties(Arrays.asList(PropertyProvider.navPropertyFriends)); } return null; } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java ---------------------------------------------------------------------- diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java index 3abe050..c19e99f 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/provider/PropertyProvider.java @@ -927,4 +927,20 @@ public class PropertyProvider { .setName("CollPropertyDefString") .setType(TypeDefinitionProvider.nameTDString) .setCollection(true); + + public static final CsdlProperty propertyId = new CsdlProperty() + .setName("id") + .setType(nameInt32) + .setNullable(false); + + public static final CsdlProperty propertyName = new CsdlProperty() + .setName("name") + .setType(nameString) + .setNullable(true); + + public static final CsdlNavigationProperty navPropertyFriends = new CsdlNavigationProperty() + .setName("friends") + .setType(EntityTypeProvider.nameETPeople) + .setNullable(true) + .setCollection(true); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java index 0daee4d..0c83e40 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializerTest.java @@ -37,7 +37,6 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; -import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index efd0713..82f49aa 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -391,7 +391,9 @@ public class ODataJsonSerializerTest { } @Override public Entity next() { - return new Entity(); + Entity e = new Entity(); + e.setId(URI.create("id")); + return e; } }; CountOption countOption = Mockito.mock(CountOption.class); @@ -421,7 +423,7 @@ public class ODataJsonSerializerTest { ByteArrayOutputStream bout = new ByteArrayOutputStream(); result.write(bout); final String resultString = new String(bout.toByteArray(), "UTF-8"); - Assert.assertEquals(resultString, "ERROR: MISSING_PROPERTY"); + Assert.assertEquals("ERROR: MISSING_PROPERTY", resultString); } @@ -540,6 +542,7 @@ public class ODataJsonSerializerTest { public void nullCollectionButInDataMap() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixEnumDefCollComp"); Entity entity = new Entity(); + entity.setId(URI.create("id")); entity.addProperty(new Property(null, "PropertyEnumString", ValueType.ENUM, 6)); entity.addProperty(new Property(null, "CollPropertyEnumString", ValueType.COLLECTION_ENUM, null)); entity.addProperty(new Property(null, "PropertyDefString", ValueType.PRIMITIVE, "Test")); @@ -572,6 +575,7 @@ public class ODataJsonSerializerTest { public void nullComplexValueButInDataMapAndNullCollectionsNotInDataMap() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixEnumDefCollComp"); Entity entity = new Entity(); + entity.setId(URI.create("id")); entity.addProperty(new Property(null, "PropertyEnumString", ValueType.ENUM, 6)); entity.addProperty(new Property(null, "PropertyDefString", ValueType.PRIMITIVE, "Test")); entity.addProperty(new Property(null, "PropertyCompMixedEnumDef", ValueType.COMPLEX, null)); @@ -594,6 +598,7 @@ public class ODataJsonSerializerTest { public void enumAndTypeDefinition() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixEnumDefCollComp"); Entity entity = new Entity(); + entity.setId(URI.create("id")); entity.addProperty(new Property(null, "PropertyEnumString", ValueType.ENUM, 6)); entity.addProperty(new Property(null, "CollPropertyEnumString", ValueType.COLLECTION_ENUM, Arrays.asList(2, 4, 6))); @@ -1903,4 +1908,153 @@ public class ODataJsonSerializerTest { } Assert.assertEquals(3, count); } + + @Test + public void expandCycle() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( + mockExpandItem)); + InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()).getContent(); + final String resultString = IOUtils.toString(result); + String expected = "{" + + "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"id\":1," + + "\"name\":\"B\"," + + "\"friends\":[" + + "{" + + "\"id\":0," + + "\"name\":\"A\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(1)\"" + + "}," + + "{" + + "\"id\":2," + + "\"name\":\"C\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(0)\"" + + "}," + + "{" + + "\"id\":3," + + "\"name\":\"D\"," + + "\"friends\":[" + + "]" + + "}" + + "]" + + "}" + + "]" + + "}," + + "{" + + "\"id\":2," + + "\"name\":\"C\"," + + "\"friends\":[" + + "{" + + "\"id\":0," + + "\"name\":\"A\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(1)\"" + + "}," + + "{" + + "\"@odata.id\":\"ESPeople(2)\"" + + "}" + + "]" + + "}," + + "{" + + "\"id\":3," + + "\"name\":\"D\"," + + "\"friends\":[" + + "]" + + "}" + + "]" + + "}" + + "]" + + "}"; + Assert.assertEquals(expected, resultString); + } + + @Test + public void expandCycleWith3Level() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE); + Mockito.when(levels.getValue()).thenReturn(3); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( + mockExpandItem)); + InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()).getContent(); + final String resultString = IOUtils.toString(result); + String expected = "{" + + "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"id\":1," + + "\"name\":\"B\"," + + "\"friends\":[" + + "{" + + "\"id\":0," + + "\"name\":\"A\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(1)\"" + + "}," + + "{" + + "\"id\":2," + + "\"name\":\"C\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(0)\"" + + "}," + + "{" + + "\"id\":3," + + "\"name\":\"D\"" + + "}" + + "]" + + "}" + + "]" + + "}," + + "{" + + "\"id\":2," + + "\"name\":\"C\"," + + "\"friends\":[" + + "{" + + "\"id\":0," + + "\"name\":\"A\"," + + "\"friends\":[" + + "{" + + "\"@odata.id\":\"ESPeople(1)\"" + + "}," + + "{" + + "\"@odata.id\":\"ESPeople(2)\"" + + "}" + + "]" + + "}," + + "{" + + "\"id\":3," + + "\"name\":\"D\"," + + "\"friends\":[" + + "]" + + "}" + + "]" + + "}" + + "]" + + "}"; + Assert.assertEquals(expected, resultString); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/120adfea/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java index 64acb48..7eae959 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerTest.java @@ -29,9 +29,9 @@ import java.util.Date; import org.apache.commons.io.IOUtils; import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.ContextURL.Suffix; -import org.apache.olingo.commons.api.data.ComplexValue; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.Property; @@ -59,6 +59,7 @@ import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.api.uri.queryoption.CountOption; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; +import org.apache.olingo.server.api.uri.queryoption.LevelsExpandOption; import org.apache.olingo.server.api.uri.queryoption.SelectItem; import org.apache.olingo.server.api.uri.queryoption.SelectOption; import org.apache.olingo.server.core.ServiceMetadataImpl; @@ -724,6 +725,7 @@ public class ODataXmlSerializerTest { public void enumAndTypeDefinition() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixEnumDefCollComp"); Entity entity = new Entity(); + entity.setId(URI.create("id")); entity.addProperty(new Property(null, "PropertyEnumString", ValueType.ENUM, 6)); entity.addProperty(new Property(null, "CollPropertyEnumString", ValueType.COLLECTION_ENUM, Arrays.asList(2, 4, 6))); @@ -748,9 +750,11 @@ public class ODataXmlSerializerTest { + "<a:entry xmlns:a=\"" + Constants.NS_ATOM + "\"" + " xmlns:m=\"" + Constants.NS_METADATA + "\" xmlns:d=\"" + Constants.NS_DATASERVICES + "\"" + " m:context=\"$metadata#ESMixEnumDefCollComp/$entity\" m:metadata-etag=\"metadataETag\">\n" + + " <a:id>id</a:id>" + " <a:title /> <a:summary />\n" + " <a:updated>" + UPDATED_FORMAT.format(new Date(currentTimeMillis)) + "</a:updated>\n" + " <a:author> <a:name /> </a:author>\n" + + " <a:link rel=\"edit\" href=\"id\" />" + " <a:category scheme=\"" + Constants.NS_SCHEME + "\"\n" + " term=\"#olingo.odata.test1.ETMixEnumDefCollComp\" />\n" + " <a:content type=\"application/xml\">\n" @@ -2305,7 +2309,409 @@ public class ODataXmlSerializerTest { "</a:feed>"; checkXMLEqual(expected, resultString); } + + @Test + public void expandCycle() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( + mockExpandItem)); + long currentTimeMillis = System.currentTimeMillis(); + SerializerResult result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()); + final String resultString = IOUtils.toString(result.getContent()); + + String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<a:entry xmlns:a=\"http://www.w3.org/2005/Atom\" " + + "xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" " + + "xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + + "m:context=\"$metadata#ESPeople/$entity\" m:metadata-etag=\"metadataETag\">\n" + + " <a:id>ESPeople(1)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(1)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(1)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(0)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(0)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(1)\" />\n" + + " <a:entry>\n" + + " <a:id>ESPeople(2)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(2)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(0)\" />\n" + + " <a:entry>\n" + + " <a:id>ESPeople(3)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(3)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(3)/friends\">\n" + + " <m:inline>\n" + + " <a:feed />\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">3</d:id>\n" + + " <d:name>D</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">2</d:id>\n" + + " <d:name>C</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">0</d:id>\n" + + " <d:name>A</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(2)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(2)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(0)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(0)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(1)\" />\n" + + " <m:ref id=\"ESPeople(2)\" />\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">0</d:id>\n" + + " <d:name>A</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(3)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(3)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(3)/friends\">\n" + + " <m:inline>\n" + + " <a:feed />\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">3</d:id>\n" + + " <d:name>D</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">2</d:id>\n" + + " <d:name>C</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">1</d:id>\n" + + " <d:name>B</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + "</a:entry>\n" + + ""; + checkXMLEqual(expected, resultString); + } + @Test + public void expandCycleWith3Level() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE); + Mockito.when(levels.getValue()).thenReturn(3); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( + mockExpandItem)); + long currentTimeMillis = System.currentTimeMillis(); + InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()).getContent(); + final String resultString = IOUtils.toString(result); + String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<a:entry xmlns:a=\"http://www.w3.org/2005/Atom\" " + + "xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" " + + "xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + + "m:context=\"$metadata#ESPeople/$entity\" m:metadata-etag=\"metadataETag\">\n" + + " <a:id>ESPeople(1)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(1)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(1)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(0)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(0)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(1)\" />\n" + + " <a:entry>\n" + + " <a:id>ESPeople(2)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\"" + + " title=\"friends\" href=\"ESPeople(2)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(0)\" />\n" + + " <a:entry>\n" + + " <a:id>ESPeople(3)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(3)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(3)/friends\" />\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">3</d:id>\n" + + " <d:name>D</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">2</d:id>\n" + + " <d:name>C</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">0</d:id>\n" + + " <d:name>A</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(2)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(2)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(2)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(0)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(0)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(0)/friends\">\n" + + " <m:inline>\n" + + " <a:feed>\n" + + " <m:ref id=\"ESPeople(1)\" />\n" + + " <m:ref id=\"ESPeople(2)\" />\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">0</d:id>\n" + + " <d:name>A</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " <a:entry>\n" + + " <a:id>ESPeople(3)</a:id>\n" + + " <a:title />\n" + + " <a:summary />\n" + + " <a:updated>"+UPDATED_FORMAT.format(new Date(currentTimeMillis))+"</a:updated>\n" + + " <a:author>\n" + + " <a:name />\n" + + " </a:author>\n" + + " <a:link rel=\"edit\" href=\"ESPeople(3)\" />\n" + + " <a:link rel=\"http://docs.oasis-open.org/odata/ns/related/friends\" " + + "type=\"application/atom+xml;type=feed\" title=\"friends\" href=\"ESPeople(3)/friends\">\n" + + " <m:inline>\n" + + " <a:feed />\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">3</d:id>\n" + + " <d:name>D</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">2</d:id>\n" + + " <d:name>C</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + " </a:entry>\n" + + " </a:feed>\n" + + " </m:inline>\n" + + " </a:link>\n" + + " <a:category scheme=\"http://docs.oasis-open.org/odata/ns/scheme\" " + + "term=\"#olingo.odata.test1.ETPeople\" />\n" + + " <a:content type=\"application/xml\">\n" + + " <m:properties>\n" + + " <d:id m:type=\"Int32\">1</d:id>\n" + + " <d:name>B</d:name>\n" + + " </m:properties>\n" + + " </a:content>\n" + + "</a:entry>"; + checkXMLEqual(expected, resultString); + } + private void checkXMLEqual(final String expected, final String resultString) throws SAXException, IOException { Diff diff = XMLUnit.compareXML(expected, resultString); diff.overrideDifferenceListener(DIFFERENCE_LISTENER);
