OLINGO-811: implementing the =nav/ and =nav/

This commit is contained in:
Ramesh Reddy 2016-03-22 18:22:27 -05:00
parent 3786699f01
commit 5dee97f762
13 changed files with 318 additions and 70 deletions

View File

@ -37,7 +37,6 @@ import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.uri.QueryOption;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.junit.Ignore;
import org.junit.Test;
public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCase {
@ -209,8 +208,7 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas
}
@Test
@Ignore("Server do not support navigation property count annotations")
public void count() {
public void count() throws Exception{
final ODataClient client = getEdmEnabledClient();
Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class);
options.put(QueryOption.SELECT, "PropertyInt16");
@ -220,6 +218,42 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas
client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions(
NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, options).addQueryOption(QueryOption.SELECT,
"PropertyInt16,PropertyString").build();
final ODataRetrieveResponse<ClientEntitySet> response =
client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute();
final List<ClientEntity> entities = response.getBody().getEntities();
assertEquals(4, entities.size());
for (final ClientEntity entity : entities) {
final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue();
final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue();
final ClientEntitySet entitySet =
entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet();
if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) {
assertEquals(Integer.valueOf(2), entitySet.getCount());
} else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) {
assertEquals(Integer.valueOf(1), entitySet.getCount());
} else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) {
assertEquals(Integer.valueOf(1), entitySet.getCount());
} else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) {
assertEquals(Integer.valueOf(0), entitySet.getCount());
} else {
fail();
}
}
}
@Test
public void countOnly() throws Exception {
final ODataClient client = getEdmEnabledClient();
Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class);
final URI uri =
client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions(
NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, false, true, options).addQueryOption(QueryOption.SELECT,
"PropertyInt16,PropertyString").build();
final ODataRetrieveResponse<ClientEntitySet> response =
client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute();
@ -232,20 +266,63 @@ public class ExpandWithSystemQueryOptionsITCase extends AbstractParamTecSvcITCas
final ClientEntitySet entitySet =
entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet();
if (propInt16.equals(1) && propString.equals("1")) {
if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) {
assertEquals(Integer.valueOf(2), entitySet.getCount());
} else if (propInt16.equals(1) && propString.equals("2")) {
assertEquals(Integer.valueOf(2), entitySet.getCount());
} else if (propInt16.equals(2) && propString.equals("1")) {
assertEquals(Integer.valueOf(2), entitySet.getCount());
} else if (propInt16.equals(3) && propString.equals("1")) {
} else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) {
assertEquals(Integer.valueOf(1), entitySet.getCount());
} else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) {
assertEquals(Integer.valueOf(1), entitySet.getCount());
} else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) {
assertEquals(Integer.valueOf(0), entitySet.getCount());
} else {
fail();
}
}
}
}
@Test
public void reference() throws Exception {
final ODataClient client = getEdmEnabledClient();
Map<QueryOption, Object> options = new EnumMap<QueryOption, Object>(QueryOption.class);
final URI uri =
client.newURIBuilder(SERVICE_URI).appendEntitySetSegment(ES_TWO_KEY_NAV).expandWithOptions(
NAV_PROPERTY_ET_TWO_KEY_NAV_MANY, true, false, options).addQueryOption(QueryOption.SELECT,
"PropertyInt16,PropertyString").build();
final ODataRetrieveResponse<ClientEntitySet> response =
client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute();
final List<ClientEntity> entities = response.getBody().getEntities();
assertEquals(4, entities.size());
for (final ClientEntity entity : entities) {
final Object propInt16 = entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue();
final Object propString = entity.getProperty(PROPERTY_STRING).getPrimitiveValue().toValue();
final ClientEntitySet entitySet =
entity.getNavigationLink(NAV_PROPERTY_ET_TWO_KEY_NAV_MANY).asInlineEntitySet().getEntitySet();
if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("1")) {
assertEquals(2, entitySet.getEntities().size());
assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')",
entitySet.getEntities().get(0).getId().toString());
assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
entitySet.getEntities().get(1).getId().toString());
} else if ((propInt16.equals(1) ||propInt16.equals((short)1)) && propString.equals("2")) {
assertEquals(1, entitySet.getEntities().size());
assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='1')",
entitySet.getEntities().get(0).getId().toString());
} else if ((propInt16.equals(2) ||propInt16.equals((short)2)) && propString.equals("1")) {
assertEquals(1, entitySet.getEntities().size());
assertEquals("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
entitySet.getEntities().get(0).getId().toString());
} else if ((propInt16.equals(3) ||propInt16.equals((short)3)) && propString.equals("1")) {
assertEquals(0, entitySet.getEntities().size());
} else {
fail();
}
}
}
@Test
public void singleEntityWithExpand() {
/* A single entity request will be dispatched to a different processor method than entity set request */

View File

@ -350,6 +350,21 @@ public interface URIBuilder {
*/
URIBuilder expandWithOptions(String expandItem, Map<QueryOption, Object> options);
/**
* The set of expanded entities can be refined through the application of expand options, expressed as a
* semicolon-separated list of system query options, enclosed in parentheses, see [OData-URL].
*
* @param expandItem item to be expanded.
* @param pathRef include the /$ref at the end of the $expand item's path;if true pathCount MUST be false
* @param pathCount include /$count at the end of the $expand item's path;if true pathRef MUST be false
* @param options System query options. Allowed query options are: $filter, $select, $orderby, $skip, $top, $count,
* $search, $expand, and $levels.
* @return current URIBuilder instance.
* @see org.apache.olingo.client.api.uri.QueryOption#EXPAND
*/
URIBuilder expandWithOptions(String expandItem, boolean pathRef,
boolean pathCount, Map<QueryOption, Object> options);
/**
* Properties of related entities can be specified by including the $select query option within the $expand.
* <br />

View File

@ -94,6 +94,12 @@ public class JsonDeserializer implements ODataDeserializer {
final ObjectCodec codec, final Link link) throws IOException {
final String entityNamePrefix = name.substring(0, name.indexOf(suffix));
Integer count = null;
if (tree.hasNonNull(entityNamePrefix+Constants.JSON_COUNT)) {
count = tree.get(entityNamePrefix+Constants.JSON_COUNT).asInt();
}
if (tree.has(entityNamePrefix)) {
final JsonNode inline = tree.path(entityNamePrefix);
JsonEntityDeserializer entityDeserializer = new JsonEntityDeserializer(serverMode);
@ -106,6 +112,9 @@ public class JsonDeserializer implements ODataDeserializer {
link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
final EntityCollection entitySet = new EntityCollection();
if (count != null) {
entitySet.setCount(count);
}
for (final Iterator<JsonNode> entries = inline.elements(); entries.hasNext();) {
entitySet.getEntities().add(entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload());
}
@ -247,6 +256,11 @@ public class JsonDeserializer implements ODataDeserializer {
}
} else if (type == null && field.getKey().endsWith(getJSONAnnotation(Constants.JSON_TYPE))) {
type = field.getValue().asText();
} else if (field.getKey().endsWith(getJSONAnnotation(Constants.JSON_COUNT))) {
final Property property = new Property();
property.setName(field.getKey());
property.setValue(ValueType.PRIMITIVE, Integer.parseInt(field.getValue().asText()));
properties.add(property);
} else if (annotation == null && customAnnotation.matches() && !"odata".equals(customAnnotation.group(2))) {
annotation = new Annotation();
annotation.setTerm(customAnnotation.group(2) + "." + customAnnotation.group(3));

View File

@ -23,7 +23,9 @@ import java.net.URI;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.client.api.EdmEnabledODataClient;
@ -565,23 +567,50 @@ public class ODataBinderImpl implements ODataBinder {
return type;
}
private ClientLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName) {
private ClientLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName,
final Integer count) {
if (property.isCollection()) {
EntityCollection inlineEntitySet = new EntityCollection();
for (final Object inlined : property.asCollection()) {
Entity inlineEntity = new Entity();
inlineEntity.setType(propertyTypeName);
inlineEntity.getProperties().addAll(((ComplexValue) inlined).getValue());
copyAnnotations(inlineEntity, (ComplexValue) inlined);
inlineEntitySet.getEntities().add(inlineEntity);
}
if (count != null) {
inlineEntitySet.setCount(count);
}
return createODataInlineEntitySet(inlineEntitySet, null, property.getName(), null);
} else {
Entity inlineEntity = new Entity();
inlineEntity.setType(propertyTypeName);
inlineEntity.getProperties().addAll(property.asComplex().getValue());
copyAnnotations(inlineEntity, property.asComplex());
return createODataInlineEntity(inlineEntity, null, property.getName(), null);
}
}
private void copyAnnotations(Entity inlineEntity, ComplexValue complex) {
for (Annotation annotation:complex.getAnnotations()) {
if (annotation.getTerm().equals(Constants.JSON_TYPE.substring(1))){
inlineEntity.setType((String)annotation.asPrimitive());
} else if (annotation.getTerm().equals(Constants.JSON_ID.substring(1))){
inlineEntity.setId(URI.create((String)annotation.asPrimitive()));
} else if (annotation.getTerm().equals(Constants.JSON_ETAG.substring(1))){
inlineEntity.setETag((String)annotation.asPrimitive());
}
}
}
private ClientLink createLinkFromEmptyNavigationProperty(final String propertyName,
final Integer count) {
EntityCollection inlineEntitySet = new EntityCollection();
if (count != null) {
inlineEntitySet.setCount(count);
}
return createODataInlineEntitySet(inlineEntitySet, null, propertyName, null);
}
@Override
public ClientEntity getODataEntity(final ResWrap<Entity> resource) {
@ -650,21 +679,45 @@ public class ODataBinderImpl implements ODataBinder {
entity.setMediaETag(resource.getPayload().getMediaETag());
}
Map<String, Integer> countMap = new HashMap<String, Integer>();
for (final Property property : resource.getPayload().getProperties()) {
EdmType propertyType = null;
if (edmType instanceof EdmEntityType) {
final EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName());
EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName());
if (edmProperty != null) {
propertyType = edmProperty.getType();
if (edmProperty instanceof EdmNavigationProperty && !property.isNull()) {
final String propertyTypeName = propertyType.getFullQualifiedName().getFullQualifiedNameAsString();
entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName));
entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName,
countMap.remove(property.getName())));
continue;
}
} else {
int idx = property.getName().indexOf(Constants.JSON_COUNT);
if (idx != -1) {
String navigationName = property.getName().substring(0, idx);
edmProperty = ((EdmEntityType) edmType).getProperty(navigationName);
if (edmProperty != null) {
if (edmProperty instanceof EdmNavigationProperty) {
ClientLink link = entity.getNavigationLink(navigationName);
if (link == null) {
countMap.put(navigationName, (Integer)property.getValue());
} else {
link.asInlineEntitySet().getEntitySet().setCount((Integer)property.getValue());
}
}
}
}
}
}
add(entity, getODataProperty(propertyType, property));
}
if (!countMap.isEmpty()) {
for (String name:countMap.keySet()) {
entity.addLink(createLinkFromEmptyNavigationProperty(name, countMap.get(name)));
}
}
entity.setId(resource.getPayload().getId());
odataAnnotations(resource.getPayload(), entity);

View File

@ -446,13 +446,20 @@ public class URIBuilderImpl implements URIBuilder {
@Override
public URIBuilder expandWithOptions(final String expandItem, final Map<QueryOption, Object> options) {
return expandWithOptions(expandItem, false, false, options);
}
@Override
public URIBuilder expandWithOptions(String expandItem, boolean pathRef,
boolean pathCount, Map<QueryOption, Object> options) {
final Map<String, Object> _options = new LinkedHashMap<String, Object>();
for (Map.Entry<QueryOption, Object> entry : options.entrySet()) {
_options.put("$" + entry.getKey().toString(), entry.getValue());
}
return expand(expandItem + buildMultiKeySegment(_options, false, ';'));
String path = pathRef?"/$ref":pathCount?"/$count":StringUtils.EMPTY;
return expand(expandItem + buildMultiKeySegment(_options, false, ';')+path);
}
@Override
public URIBuilder expandWithSelect(final String expandItem, final String... selectItems) {
return expand(expandItem + "($select=" + StringUtils.join(selectItems, ",") + ")");

View File

@ -87,6 +87,21 @@ public class URIBuilderTest extends AbstractTest {
assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)").
addParameter("$expand", "ProductDetails($expand=ProductInfo;$select=Price),Orders,Customers").build(), uri);
}
@Test
public void expandWithOptionsCount() throws URISyntaxException {
final URI uri = client.newURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(5).
expandWithOptions("ProductDetails", false, true, new LinkedHashMap<QueryOption, Object>() {
private static final long serialVersionUID = 3109256773218160485L;
{
put(QueryOption.EXPAND, "ProductInfo");
put(QueryOption.SELECT, "Price");
}
}).expand("Orders", "Customers").build();
assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)").
addParameter("$expand", "ProductDetails($expand=ProductInfo;$select=Price)/$count,Orders,Customers")
.build(), uri);
}
public void expandWithLevels() throws URISyntaxException {
final URI uri = client.newURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(1).

View File

@ -85,10 +85,16 @@ public interface ExpandItem {
/**
* @return A $ref is used within $expand.
* For example: ...?$expand=$ref
* For example: ...?$expand=navigation/$ref
*/
boolean isRef();
/**
* @return A $count is used within $expand.
* For example: ...?$expand=navigation/$count
*/
boolean hasCountPath();
/**
* @return Before resource path segments which should be expanded a type filter may be used.
* For example: ...persons?$expand=namespace.managertype/team

View File

@ -26,10 +26,10 @@ import java.util.List;
import java.util.Set;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.AbstractEntityCollection;
import org.apache.olingo.commons.api.data.EntityIterator;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked;
@ -60,11 +60,12 @@ import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
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.SelectOption;
import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
import org.apache.olingo.server.core.ODataWritableContent;
import org.apache.olingo.server.core.serializer.AbstractODataSerializer;
import org.apache.olingo.server.core.serializer.SerializerResultImpl;
import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
@ -402,30 +403,54 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
writeExpandedNavigationProperty(metadata, property, navigationLink,
innerOptions == null ? null : innerOptions.getExpandOption(),
innerOptions == null ? null : innerOptions.getSelectOption(), json);
innerOptions == null ? null : innerOptions.getSelectOption(),
innerOptions == null ? null : innerOptions.getCountOption(),
innerOptions == null ? false : innerOptions.hasCountPath(),
innerOptions == null ? false : innerOptions.isRef(),
json);
}
}
}
}
protected void writeExpandedNavigationProperty(final ServiceMetadata metadata, final EdmNavigationProperty property,
final Link navigationLink, final ExpandOption innerExpand, final SelectOption innerSelect,
protected void writeExpandedNavigationProperty(
final ServiceMetadata metadata, final EdmNavigationProperty property,
final Link navigationLink, final ExpandOption innerExpand,
final SelectOption innerSelect, final CountOption innerCount,
final boolean writeOnlyCount, final boolean writeOnlyRef,
final JsonGenerator json) throws IOException, SerializerException {
json.writeFieldName(property.getName());
if (property.isCollection()) {
if (navigationLink == null || navigationLink.getInlineEntitySet() == null) {
json.writeStartArray();
json.writeEndArray();
} else {
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
innerSelect, false, json);
if (writeOnlyCount) {
if (navigationLink == null || navigationLink.getInlineEntitySet() == null) {
writeInlineCount(property.getName(), 0, json);
} else {
writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json);
}
} else {
if (navigationLink == null || navigationLink.getInlineEntitySet() == null) {
if (innerCount != null && innerCount.getValue()) {
writeInlineCount(property.getName(), 0, json);
}
json.writeFieldName(property.getName());
json.writeStartArray();
json.writeEndArray();
} else {
if (innerCount != null && innerCount.getValue()) {
writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json);
}
json.writeFieldName(property.getName());
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
innerSelect, writeOnlyRef, json);
}
}
} else {
json.writeFieldName(property.getName());
if (navigationLink == null || navigationLink.getInlineEntity() == null) {
json.writeNull();
} else {
writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null,
innerExpand, innerSelect, false, json);
innerExpand, innerSelect, writeOnlyRef, json);
}
}
}
@ -852,6 +877,18 @@ public class ODataJsonSerializer extends AbstractODataSerializer {
}
}
void writeInlineCount(final String propertyName,
final Integer count, final JsonGenerator json)
throws IOException {
if (count != null) {
if (isIEEE754Compatible) {
json.writeStringField(propertyName+Constants.JSON_COUNT, String.valueOf(count));
} else {
json.writeNumberField(propertyName+Constants.JSON_COUNT, count);
}
}
}
void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json) throws IOException {
if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());

View File

@ -66,6 +66,7 @@ import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.serializer.SerializerResult;
import org.apache.olingo.server.api.serializer.SerializerStreamResult;
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.SelectOption;
@ -254,11 +255,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
writeNextLink(entitySet, writer);
}
boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences());
if (options == null) {
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer);
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer, writeOnlyRef);
} else {
writeEntitySet(metadata, entityType, entitySet,
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer);
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef);
}
writer.writeEndElement();
@ -310,11 +312,12 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
writeCount(entitySet, writer);
}
boolean writeOnlyRef = (options != null && options.getWriteOnlyReferences());
if (options == null) {
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer);
writeEntitySet(metadata, entityType, entitySet, null, null, null, writer, writeOnlyRef);
} else {
writeEntitySet(metadata, entityType, entitySet,
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer);
options.getExpand(), options.getSelect(), options.xml10InvalidCharReplacement(), writer, writeOnlyRef);
}
writer.writeEndElement();
@ -355,7 +358,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
options == null ? null : options.getExpand(),
options == null ? null : options.getSelect(),
options == null ? null : options.xml10InvalidCharReplacement(),
writer, true);
writer, true, false);
writer.writeEndDocument();
writer.flush();
@ -395,19 +398,24 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select,
final String xml10InvalidCharReplacement,final XMLStreamWriter writer)
final String xml10InvalidCharReplacement,final XMLStreamWriter writer, final boolean writeOnlyRef)
throws XMLStreamException, SerializerException {
for (final Entity entity : entitySet) {
writeEntity(metadata, entityType, entity, null, expand, select, xml10InvalidCharReplacement, writer, false);
writeEntity(metadata, entityType, entity, null, expand, select,
xml10InvalidCharReplacement, writer, false, writeOnlyRef);
}
}
protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
final Entity entity, final ContextURL contextURL, final ExpandOption expand,
final SelectOption select, final String xml10InvalidCharReplacement,
final XMLStreamWriter writer, final boolean top)
final XMLStreamWriter writer, final boolean top, final boolean writeOnlyRef)
throws XMLStreamException, SerializerException {
if (writeOnlyRef) {
writeReference(entity, contextURL, writer, top);
return;
}
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_ENTRY, NS_ATOM);
if (top) {
writer.writeNamespace(ATOM, NS_ATOM);
@ -591,7 +599,10 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
writeExpandedNavigationProperty(metadata, property, navigationLink,
innerOptions == null ? null : innerOptions.getExpandOption(),
innerOptions == null ? null : innerOptions.getSelectOption(),
xml10InvalidCharReplacement, writer);
innerOptions == null ? null : innerOptions.getCountOption(),
innerOptions == null ? false : innerOptions.hasCountPath(),
innerOptions == null ? false : innerOptions.isRef(),
xml10InvalidCharReplacement, writer);
writer.writeEndElement();
writer.writeEndElement();
}
@ -650,19 +661,27 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
protected void writeExpandedNavigationProperty(final ServiceMetadata metadata,
final EdmNavigationProperty property, final Link navigationLink,
final ExpandOption innerExpand, final SelectOption innerSelect, final String xml10InvalidCharReplacement,
final ExpandOption innerExpand, final SelectOption innerSelect, final CountOption coutOption,
final boolean writeNavigationCount, final boolean writeOnlyRef,final String xml10InvalidCharReplacement,
final XMLStreamWriter writer) throws XMLStreamException, SerializerException {
if (property.isCollection()) {
if (navigationLink != null && navigationLink.getInlineEntitySet() != null) {
writer.writeStartElement(ATOM, Constants.ATOM_ELEM_FEED, NS_ATOM);
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
innerSelect, xml10InvalidCharReplacement, writer);
if (writeNavigationCount) {
writeCount(navigationLink.getInlineEntitySet(), writer);
} else {
if (coutOption != null && coutOption.getValue()) {
writeCount(navigationLink.getInlineEntitySet(), writer);
}
writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
innerSelect, xml10InvalidCharReplacement, writer, writeOnlyRef);
}
writer.writeEndElement();
}
} else {
if (navigationLink != null && navigationLink.getInlineEntity() != null) {
writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null,
innerExpand, innerSelect, xml10InvalidCharReplacement, writer, false);
innerExpand, innerSelect, xml10InvalidCharReplacement, writer, false, writeOnlyRef);
}
}
}
@ -1173,7 +1192,7 @@ public class ODataXmlSerializer extends AbstractODataSerializer {
private void writeCount(final AbstractEntityCollection entitySet, final XMLStreamWriter writer)
throws XMLStreamException {
writer.writeStartElement(METADATA, Constants.ATOM_ELEM_COUNT, NS_METADATA);
writer.writeCharacters(String.valueOf(entitySet.getCount()));
writer.writeCharacters(String.valueOf(entitySet.getCount()==null?0:entitySet.getCount()));
writer.writeEndElement();
}

View File

@ -160,10 +160,12 @@ public class ExpandParser {
if (hasSlash || tokenizer.next(TokenKind.SLASH)) {
if (tokenizer.next(TokenKind.REF)) {
resource.addResourcePart(new UriResourceRefImpl());
item.setIsRef(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, true, false);
} else {
ParserHelper.requireNext(tokenizer, TokenKind.COUNT);
resource.addResourcePart(new UriResourceCountImpl());
item.setCountPath(true);
parseOptions(tokenizer, newReferencedType, newReferencedIsCollection, item, false, true);
}
} else {

View File

@ -51,6 +51,7 @@ public class ExpandItemImpl implements ExpandItem {
private boolean isStar;
private boolean isRef;
private boolean hasCountPath;
private EdmType startTypeFilter;
public ExpandItemImpl setSystemQueryOption(final SystemQueryOption sysItem) {
@ -187,4 +188,13 @@ public class ExpandItemImpl implements ExpandItem {
this.startTypeFilter = startTypeFilter;
return this;
}
@Override
public boolean hasCountPath() {
return this.hasCountPath;
}
public void setCountPath(boolean value) {
this.hasCountPath = value;
}
}

View File

@ -439,7 +439,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final SelectOption select = uriInfo.getSelectOption();
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand);
final Entity entitySerialization = expandHandler.transformEntityGraphToTree(entity, edmEntitySet, expand, null);
expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand, uriInfo,
serviceMetadata.getEdm());
@ -515,7 +515,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
final EntityCollection entitySetSerialization = expandHandler.transformEntitySetGraphToTree(entitySet,
edmEntitySet,
expand);
expand, null);
expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand, uriInfo,
serviceMetadata.getEdm());
final CountOption countOption = uriInfo.getCountOption();

View File

@ -96,11 +96,8 @@ public class ExpandSystemQueryOptionHandler {
}
} else {
final List<UriResource> uriResourceParts = item.getResourcePath().getUriResourceParts();
if (uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourceNavigation) {
if (uriResourceParts.get(0) instanceof UriResourceNavigation) {
navigationProperties.add(((UriResourceNavigation) uriResourceParts.get(0)).getProperty());
} else {
throw new ODataApplicationException("Not supported resource part in expand system query option",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
}
@ -146,19 +143,22 @@ public class ExpandSystemQueryOptionHandler {
}
public EntityCollection transformEntitySetGraphToTree(final EntityCollection entitySet,
final EdmBindingTarget edmBindingTarget, final ExpandOption expand) throws ODataApplicationException {
final EdmBindingTarget edmBindingTarget, final ExpandOption expand,
final ExpandItem expandItem) throws ODataApplicationException {
final EntityCollection newEntitySet = newEntitySet(entitySet);
for (final Entity entity : entitySet.getEntities()) {
newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand));
newEntitySet.getEntities().add(transformEntityGraphToTree(entity, edmBindingTarget, expand, expandItem));
}
if (expandItem != null && expandItem.hasCountPath()) {
newEntitySet.setCount(entitySet.getEntities().size());
}
return newEntitySet;
}
public Entity transformEntityGraphToTree(final Entity entity, final EdmBindingTarget edmEntitySet,
final ExpandOption expand) throws ODataApplicationException {
final ExpandOption expand, final ExpandItem parentExpandItem) throws ODataApplicationException {
final Entity newEntity = newEntity(entity);
if (hasExpandItems(expand)) {
final boolean expandAll = expandAll(expand);
@ -173,16 +173,14 @@ public class ExpandSystemQueryOptionHandler {
final EdmBindingTarget edmBindingTarget = edmEntitySet.getRelatedBindingTarget(propertyName);
final Link newLink = newLink(link);
newEntity.getNavigationLinks().add(newLink);
final ExpandOption innerExpandOption = getInnerExpandOption(expand, propertyName);
final ExpandItem expandItem = getInnerExpandItem(expand, propertyName);
if (edmNavigationProperty.isCollection()) {
newLink.setInlineEntitySet(transformEntitySetGraphToTree(link.getInlineEntitySet(),
edmBindingTarget,
innerExpandOption));
edmBindingTarget, expandItem.getExpandOption(), expandItem));
} else {
newLink.setInlineEntity(transformEntityGraphToTree(link.getInlineEntity(),
edmBindingTarget,
innerExpandOption));
edmBindingTarget,expandItem.getExpandOption(), expandItem));
}
}
}
@ -250,29 +248,24 @@ public class ExpandSystemQueryOptionHandler {
Set<String> expanded = new HashSet<String>();
for (final ExpandItem item : expandItems) {
final List<UriResource> resourceParts = item.getResourcePath().getUriResourceParts();
if (resourceParts.size() == 1) {
final UriResource resource = resourceParts.get(0);
if (resource instanceof UriResourceNavigation) {
expanded.add(((UriResourceNavigation) resource).getProperty().getName());
}
} else {
throw new ODataApplicationException("Expand is not supported within complex properties.",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
final UriResource resource = resourceParts.get(0);
if (resource instanceof UriResourceNavigation) {
expanded.add(((UriResourceNavigation) resource).getProperty().getName());
}
}
return expanded;
}
private ExpandOption getInnerExpandOption(final ExpandOption expand, final String propertyName) {
private ExpandItem getInnerExpandItem(final ExpandOption expand, final String propertyName) {
for (final ExpandItem item : expand.getExpandItems()) {
if(item.isStar()) {
return item.getExpandOption();
return item;
}
final UriResource resource = item.getResourcePath().getUriResourceParts().get(0);
if (resource instanceof UriResourceNavigation
&& propertyName.equals(((UriResourceNavigation) resource).getProperty().getName())) {
return item.getExpandOption();
return item;
}
}