[OLINGO-230] Implementation completed

This commit is contained in:
Francesco Chicchiriccò 2014-04-02 15:29:34 +02:00
parent 6ae704d62f
commit 16d3b02886
40 changed files with 1127 additions and 653 deletions

View File

@ -43,4 +43,6 @@ public interface ODataBinder extends CommonODataBinder {
@Override
ODataProperty getODataProperty(Property property);
ODataProperty getODataProperty(Property property, URI base);
}

View File

@ -47,7 +47,6 @@ import org.apache.olingo.client.api.http.HttpClientException;
import org.apache.olingo.client.api.http.HttpMethod;
import org.apache.olingo.client.core.communication.header.ODataHeadersImpl;
import org.apache.olingo.commons.api.domain.ODataError;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.api.format.ODataMediaFormat;
import org.apache.olingo.commons.api.format.ODataPubFormat;
import org.apache.olingo.commons.api.format.ODataValueFormat;
@ -389,8 +388,9 @@ public class ODataRequestImpl<T extends Format> implements ODataRequest {
}
// Add header for KeyAsSegment management
if (odataClient.getServiceVersion() == ODataServiceVersion.V30
if (odataClient.getConfiguration() instanceof Configuration
&& ((Configuration) odataClient.getConfiguration()).isKeyAsSegment()) {
addCustomHeader(
HeaderName.dataServiceUrlConventions.toString(),
new ODataPreferences(odataClient.getServiceVersion()).keyAsSegment());

View File

@ -49,7 +49,7 @@ public abstract class AbstractEdmServiceMetadataImpl implements EdmServiceMetada
public static EdmServiceMetadata getInstance(final ODataServiceVersion version,
final List<? extends Schema> xmlSchemas) {
return version == ODataServiceVersion.V30
return version.compareTo(ODataServiceVersion.V40) < 0
? new org.apache.olingo.client.core.edm.v3.EdmServiceMetadataImpl(xmlSchemas)
: new org.apache.olingo.client.core.edm.v4.EdmServiceMetadataImpl(xmlSchemas);
}

View File

@ -21,6 +21,7 @@ package org.apache.olingo.client.core.op;
import java.io.StringWriter;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.client.api.CommonODataClient;
import org.apache.olingo.client.api.data.ServiceDocument;
@ -31,6 +32,7 @@ import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.Entry;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataCollectionValue;
@ -43,6 +45,7 @@ import org.apache.olingo.commons.api.domain.ODataLink;
import org.apache.olingo.commons.api.domain.ODataOperation;
import org.apache.olingo.commons.api.domain.CommonODataProperty;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.domain.ODataLinked;
import org.apache.olingo.commons.api.domain.ODataServiceDocument;
import org.apache.olingo.commons.api.domain.ODataValue;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
@ -112,6 +115,30 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
return getEntry(entity, reference, true);
}
protected void links(final ODataLinked odataLinked, final Linked linked, final Class<? extends Entry> reference) {
// -------------------------------------------------------------
// Append navigation links (handling inline entry / feed as well)
// -------------------------------------------------------------
// handle navigation links
for (ODataLink link : odataLinked.getNavigationLinks()) {
// append link
LOG.debug("Append navigation link\n{}", link);
linked.getNavigationLinks().add(getLink(link,
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// Append association links
// -------------------------------------------------------------
for (ODataLink link : odataLinked.getAssociationLinks()) {
LOG.debug("Append association link\n{}", link);
linked.getAssociationLinks().add(getLink(link,
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
}
// -------------------------------------------------------------
}
@Override
public Entry getEntry(final CommonODataEntity entity, final Class<? extends Entry> reference, final boolean setType) {
final Entry entry = ResourceFactory.newEntry(reference);
@ -139,17 +166,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// Append navigation links (handling inline entry / feed as well)
// -------------------------------------------------------------
// handle navigation links
for (ODataLink link : entity.getNavigationLinks()) {
// append link
LOG.debug("Append navigation link\n{}", link);
entry.getNavigationLinks().add(getLink(link,
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
}
// -------------------------------------------------------------
links(entity, entry, reference);
// -------------------------------------------------------------
// Append edit-media links
@ -161,16 +178,6 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// Append association links
// -------------------------------------------------------------
for (ODataLink link : entity.getAssociationLinks()) {
LOG.debug("Append association link\n{}", link);
entry.getAssociationLinks().add(getLink(link,
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
}
// -------------------------------------------------------------
if (entity.isMediaEntity()) {
entry.setMediaContentSource(entity.getMediaContentSource());
entry.setMediaContentType(entity.getMediaContentType());
@ -278,6 +285,34 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
return getODataEntity(resource, null);
}
protected void odataLinks(final Linked linked, final ODataLinked odataLinked, final URI base) {
for (Link link : linked.getNavigationLinks()) {
final Entry inlineEntry = link.getInlineEntry();
final Feed inlineFeed = link.getInlineFeed();
if (inlineEntry == null && inlineFeed == null) {
final ODataLinkType linkType = link.getType() == null
? ODataLinkType.ENTITY_NAVIGATION
: ODataLinkType.fromString(client.getServiceVersion(), link.getRel(), link.getType());
odataLinked.addLink(linkType == ODataLinkType.ENTITY_NAVIGATION
? client.getObjectFactory().newEntityNavigationLink(link.getTitle(), base, link.getHref())
: client.getObjectFactory().newEntitySetNavigationLink(link.getTitle(), base, link.getHref()));
} else if (inlineEntry != null) {
odataLinked.addLink(client.getObjectFactory().newInlineEntity(
link.getTitle(), base, link.getHref(),
getODataEntity(inlineEntry,
inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
} else {
odataLinked.addLink(client.getObjectFactory().newInlineEntitySet(
link.getTitle(), base, link.getHref(),
getODataEntitySet(inlineFeed,
inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
}
}
}
protected abstract void copyProperties(List<Property> src, CommonODataEntity dst, final URI base);
@Override
public CommonODataEntity getODataEntity(final Entry resource, final URI defaultBaseURI) {
if (LOG.isDebugEnabled()) {
@ -292,7 +327,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
final CommonODataEntity entity = resource.getSelfLink() == null
? client.getObjectFactory().newEntity(resource.getType())
: client.getObjectFactory().newEntity(resource.getType(),
URIUtils.getURI(base, resource.getSelfLink().getHref()));
URIUtils.getURI(base, resource.getSelfLink().getHref()));
if (StringUtils.isNotBlank(resource.getETag())) {
entity.setETag(resource.getETag());
@ -306,29 +341,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
entity.addLink(client.getObjectFactory().newAssociationLink(link.getTitle(), base, link.getHref()));
}
for (Link link : resource.getNavigationLinks()) {
final Entry inlineEntry = link.getInlineEntry();
final Feed inlineFeed = link.getInlineFeed();
if (inlineEntry == null && inlineFeed == null) {
final ODataLinkType linkType = link.getType() == null
? ODataLinkType.ENTITY_NAVIGATION
: ODataLinkType.fromString(client.getServiceVersion(), link.getRel(), link.getType());
entity.addLink(linkType == ODataLinkType.ENTITY_NAVIGATION
? client.getObjectFactory().newEntityNavigationLink(link.getTitle(), base, link.getHref())
: client.getObjectFactory().newEntitySetNavigationLink(link.getTitle(), base, link.getHref()));
} else if (inlineEntry != null) {
entity.addLink(client.getObjectFactory().newInlineEntity(
link.getTitle(), base, link.getHref(),
getODataEntity(inlineEntry,
inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
} else {
entity.addLink(client.getObjectFactory().newInlineEntitySet(
link.getTitle(), base, link.getHref(),
getODataEntitySet(inlineFeed,
inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
}
}
odataLinks(resource, entity, base);
for (Link link : resource.getMediaEditLinks()) {
entity.addLink(client.getObjectFactory().newMediaEditLink(link.getTitle(), base, link.getHref()));
@ -346,14 +359,12 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
entity.setMediaETag(resource.getMediaETag());
}
for (Property property : resource.getProperties()) {
add(entity, getODataProperty(property));
}
copyProperties(resource.getProperties(), entity, base);
return entity;
}
protected ODataValue getODataValue(final Property resource) {
protected ODataValue getODataValue(final Property resource, final URI base) {
ODataValue value = null;
final EdmTypeInfo typeInfo = resource.getType() == null
@ -363,18 +374,18 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
value = client.getObjectFactory().newPrimitiveValueBuilder().
setText(resource.getValue().asPrimitive().get()).
setType(typeInfo == null
? null
: EdmPrimitiveTypeKind.valueOfFQN(
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
? null
: EdmPrimitiveTypeKind.valueOfFQN(
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
} else if (resource.getValue().isGeospatial()) {
value = client.getObjectFactory().newPrimitiveValueBuilder().
setValue(resource.getValue().asGeospatial().get()).
setType(typeInfo == null
|| EdmPrimitiveTypeKind.Geography.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|| EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
? resource.getValue().asGeospatial().get().getEdmPrimitiveTypeKind()
: EdmPrimitiveTypeKind.valueOfFQN(
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
|| EdmPrimitiveTypeKind.Geography.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|| EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
? resource.getValue().asGeospatial().get().getEdmPrimitiveTypeKind()
: EdmPrimitiveTypeKind.valueOfFQN(
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
} else if (resource.getValue().isComplex()) {
value = client.getObjectFactory().newComplexValue(typeInfo == null
? null : typeInfo.getFullQualifiedName().toString());
@ -389,7 +400,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
for (Value _value : resource.getValue().asCollection().get()) {
final JSONPropertyImpl fake = new JSONPropertyImpl();
fake.setValue(_value);
value.asCollection().add(getODataValue(fake));
value.asCollection().add(getODataValue(fake, base));
}
}

View File

@ -19,6 +19,7 @@
package org.apache.olingo.client.core.op.impl.v3;
import java.net.URI;
import java.util.List;
import org.apache.olingo.commons.api.data.v3.LinkCollection;
import org.apache.olingo.client.api.domain.v3.ODataLinkCollection;
import org.apache.olingo.client.api.op.v3.ODataBinder;
@ -85,6 +86,13 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
return (ODataEntitySet) super.getODataEntitySet(resource, defaultBaseURI);
}
@Override
protected void copyProperties(final List<Property> src, final CommonODataEntity dst, final URI base) {
for (Property property : src) {
add(dst, getODataProperty(property));
}
}
@Override
public ODataEntity getODataEntity(final Entry resource) {
return (ODataEntity) super.getODataEntity(resource);
@ -97,7 +105,7 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
@Override
public ODataProperty getODataProperty(final Property property) {
return new ODataPropertyImpl(property.getName(), getODataValue(property));
return new ODataPropertyImpl(property.getName(), getODataValue(property, null));
}
@Override

View File

@ -19,6 +19,7 @@
package org.apache.olingo.client.core.op.impl.v4;
import java.net.URI;
import java.util.List;
import org.apache.olingo.client.api.data.ServiceDocument;
import org.apache.olingo.client.api.data.ServiceDocumentItem;
import org.apache.olingo.client.api.op.v4.ODataBinder;
@ -27,6 +28,7 @@ import org.apache.olingo.client.core.op.AbstractODataBinder;
import org.apache.olingo.client.core.uri.URIUtils;
import org.apache.olingo.commons.api.data.Entry;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.data.LinkedComplexValue;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.CommonODataEntity;
@ -36,8 +38,10 @@ import org.apache.olingo.commons.api.domain.ODataServiceDocument;
import org.apache.olingo.commons.api.domain.ODataValue;
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
import org.apache.olingo.commons.core.data.EnumValueImpl;
import org.apache.olingo.commons.core.data.LinkedComplexValueImpl;
import org.apache.olingo.commons.core.domain.v4.ODataPropertyImpl;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
import org.apache.olingo.commons.core.op.ResourceFactory;
@ -125,6 +129,19 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
((org.apache.olingo.commons.api.domain.v4.ODataValue) value).asEnum().getValue());
} else {
valueResource = super.getValue(value, reference, setType);
if (value instanceof org.apache.olingo.commons.api.domain.v4.ODataValue
&& ((org.apache.olingo.commons.api.domain.v4.ODataValue) value).isLinkedComplex()) {
final LinkedComplexValue lcValueResource = new LinkedComplexValueImpl();
lcValueResource.get().addAll(valueResource.asComplex().get());
final ODataLinkedComplexValue linked =
((org.apache.olingo.commons.api.domain.v4.ODataValue) value).asLinkedComplex();
links(linked, lcValueResource, reference);
valueResource = lcValueResource;
}
}
return valueResource;
}
@ -139,6 +156,13 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
return (ODataEntitySet) super.getODataEntitySet(resource, defaultBaseURI);
}
@Override
protected void copyProperties(final List<Property> src, final CommonODataEntity dst, final URI base) {
for (Property property : src) {
add(dst, getODataProperty(property, base));
}
}
@Override
public ODataEntity getODataEntity(final Entry resource) {
return (ODataEntity) super.getODataEntity(resource);
@ -153,21 +177,38 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
@Override
public ODataProperty getODataProperty(final Property property) {
return new ODataPropertyImpl(property.getName(), getODataValue(property));
return getODataProperty(property, null);
}
@Override
protected ODataValue getODataValue(final Property resource) {
public ODataProperty getODataProperty(final Property property, final URI base) {
return new ODataPropertyImpl(property.getName(), getODataValue(property, base));
}
@Override
protected ODataValue getODataValue(final Property resource, final URI base) {
final EdmTypeInfo typeInfo = resource.getType() == null
? null
: new EdmTypeInfo.Builder().setTypeExpression(resource.getType()).build();
ODataValue value;
if (resource.getValue().isEnum()) {
final EdmTypeInfo typeInfo = resource.getType() == null
? null
: new EdmTypeInfo.Builder().setTypeExpression(resource.getType()).build();
value = ((ODataClient) client).getObjectFactory().newEnumValue(
typeInfo == null ? null : typeInfo.getFullQualifiedName().toString(),
resource.getValue().asEnum().get());
} else if (resource.getValue().isLinkedComplex()) {
final ODataLinkedComplexValue lcValue = ((ODataClient) client).getObjectFactory().
newLinkedComplexValue(typeInfo == null ? null : typeInfo.getFullQualifiedName().toString());
for (Property property : resource.getValue().asComplex().get()) {
lcValue.add(getODataProperty(property));
}
odataLinks(resource.getValue().asLinkedComplex(), lcValue, base);
value = lcValue;
} else {
value = super.getODataValue(resource);
value = super.getODataValue(resource, base);
}
return value;

View File

@ -161,7 +161,7 @@ public final class URIUtils {
private static String prefix(final ODataServiceVersion version, final EdmPrimitiveTypeKind typeKind) {
String result = StringUtils.EMPTY;
if (version == ODataServiceVersion.V30) {
if (version.compareTo(ODataServiceVersion.V40) < 0) {
switch (typeKind) {
case Guid:
result = "guid'";
@ -188,7 +188,7 @@ public final class URIUtils {
private static String suffix(final ODataServiceVersion version, final EdmPrimitiveTypeKind typeKind) {
String result = StringUtils.EMPTY;
if (version == ODataServiceVersion.V30) {
if (version.compareTo(ODataServiceVersion.V40) < 0) {
switch (typeKind) {
case Guid:
case DateTime:
@ -222,7 +222,7 @@ public final class URIUtils {
private static String timestamp(final ODataServiceVersion version, final Timestamp timestamp)
throws UnsupportedEncodingException, EdmPrimitiveTypeException {
return version == ODataServiceVersion.V30
return version.compareTo(ODataServiceVersion.V40) < 0
? prefix(version, EdmPrimitiveTypeKind.DateTime)
+ URLEncoder.encode(EdmDateTime.getInstance().
valueToString(timestamp, null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null),
@ -238,7 +238,7 @@ public final class URIUtils {
String result;
if (calendar.get(Calendar.ZONE_OFFSET) == 0) {
if (version == ODataServiceVersion.V30) {
if (version.compareTo(ODataServiceVersion.V40) < 0) {
result = prefix(version, EdmPrimitiveTypeKind.DateTime)
+ URLEncoder.encode(EdmDateTime.getInstance().
valueToString(calendar, null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null),
@ -271,7 +271,7 @@ public final class URIUtils {
private static String duration(final ODataServiceVersion version, final Duration duration)
throws UnsupportedEncodingException, EdmPrimitiveTypeException {
return version == ODataServiceVersion.V30
return version.compareTo(ODataServiceVersion.V40) < 0
? EdmTime.getInstance().toUriLiteral(URLEncoder.encode(EdmTime.getInstance().
valueToString(duration, null, null,
Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null), Constants.UTF8))
@ -308,7 +308,7 @@ public final class URIUtils {
try {
if (obj == null) {
value = Constants.ATTR_NULL;
} else if (version == ODataServiceVersion.V40 && obj instanceof Collection) {
} else if (version.compareTo(ODataServiceVersion.V40) >= 0 && obj instanceof Collection) {
final StringBuffer buffer = new StringBuffer("[");
for (@SuppressWarnings("unchecked")
final Iterator<Object> itor = ((Collection<Object>) obj).iterator(); itor.hasNext();) {
@ -320,7 +320,7 @@ public final class URIUtils {
buffer.append(']');
value = buffer.toString();
} else if (version == ODataServiceVersion.V40 && obj instanceof Map) {
} else if (version.compareTo(ODataServiceVersion.V40) >= 0 && obj instanceof Map) {
final StringBuffer buffer = new StringBuffer("{");
for (@SuppressWarnings("unchecked")
final Iterator<Map.Entry<Object, Object>> itor =

View File

@ -31,6 +31,7 @@ import org.apache.olingo.commons.api.domain.ODataInlineEntitySet;
import org.apache.olingo.commons.api.domain.ODataLink;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.api.format.ODataPubFormat;
@ -247,4 +248,31 @@ public class EntityTest extends AbstractTest {
public void jsonRef() {
ref(ODataPubFormat.JSON);
}
private void complexNavigationProperties(final ODataPubFormat format) {
final InputStream input = getClass().getResourceAsStream("entity.withcomplexnavigation." + getSuffix(format));
final ODataEntity entity = getClient().getBinder().getODataEntity(
getClient().getDeserializer().toEntry(input, format).getObject());
assertNotNull(entity);
final ODataLinkedComplexValue addressValue = entity.getProperty("Address").getLinkedComplexValue();
assertNotNull(addressValue);
assertNotNull(addressValue.getNavigationLink("Country"));
// ETag is not serialized
entity.setETag(null);
final ODataEntity written = getClient().getBinder().getODataEntity(getClient().getBinder().
getEntry(entity, ResourceFactory.entryClassForFormat(format == ODataPubFormat.ATOM)));
assertEquals(entity, written);
}
@Test
public void atomComplexNavigationProperties() {
complexNavigationProperties(ODataPubFormat.ATOM);
}
@Test
public void jsonComplexNavigationProperties() {
complexNavigationProperties(ODataPubFormat.JSON);
}
}

View File

@ -58,6 +58,9 @@ public class JSONTest extends AbstractTest {
if (node.has(Constants.JSON_CONTEXT)) {
node.remove(Constants.JSON_CONTEXT);
}
if (node.has(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_ETAG))) {
node.remove(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_ETAG));
}
if (node.has(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_TYPE))) {
node.remove(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_TYPE));
}
@ -140,7 +143,6 @@ public class JSONTest extends AbstractTest {
@Test
public void additionalEntries() throws Exception {
entry("entity.minimal", getODataPubFormat());
// entry("entity.full", getODataPubFormat());
entry("entity.primitive", getODataPubFormat());
entry("entity.complex", getODataPubFormat());
entry("entity.collection.primitive", getODataPubFormat());
@ -153,6 +155,7 @@ public class JSONTest extends AbstractTest {
entry("VipCustomer", getODataPubFormat());
entry("Advertisements_f89dee73-af9f-4cd4-b330-db93c25ff3c7", getODataPubFormat());
entry("entityReference", getODataPubFormat());
entry("entity.withcomplexnavigation", getODataPubFormat());
}
protected void property(final String filename, final ODataFormat format) throws Exception {

View File

@ -27,6 +27,7 @@
<xsl:template match="atom:updated"/>
<xsl:template match="atom:author"/>
<xsl:template match="atom:summary"/>
<xsl:template match="atom:title">
<xsl:if test="string-length(.) &gt; 0">
<title type="{@type}">
@ -38,7 +39,7 @@
<xsl:template match="m:action"/>
<xsl:template match="@*[name() = 'm:etag' or name() = 'm:context']"/>
<xsl:template match="@*[name() = 'm:etag' or name() = 'm:context' or name() = 'm:metadata-etag']"/>
<xsl:template match="node()|@*">
<xsl:copy>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:m="http://docs.oasis-open.org/odata/ns/metadata"
xmlns:d="http://docs.oasis-open.org/odata/ns/data"
xml:base="http://host/service/"
m:context="$metadata#Customers/$entity"
m:metadata-etag="W/&quot;MjAxMy0wNS0xM1QxNDo1NFo=&quot;">
<id>http://host/service/$metadata#Customers('ALFKI')</id>
<title/>
<summary/>
<updated>2012-03-30T07:11:05Z</updated>
<author>
<name/>
</author>
<link rel="edit" title="Customer" href="Customers('ALFKI')"/>
<link rel="http://docs.oasis-open.org/odata/ns/related/Orders"
type="application/atom+xml;type=feed"
title="Orders" href="Customers('ALFKI')/Orders"/>
<link rel="http://docs.oasis-open.org/odata/ns/related/Supplier"
type="application/atom+xml;type=entry"
title="Supplier" href="Customers('ALFKI')/Supplier"/>
<category term="#ODataDemo.Customer"
scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
<content type="application/xml">
<m:properties>
<d:ID>ALFKI</d:ID>
<d:CompanyName>Alfreds Futterkiste</d:CompanyName>
<d:ContactName>Maria Anders</d:ContactName>
<d:ContactTitle>Sales Representative</d:ContactTitle>
<d:Phone>030-0074321</d:Phone>
<d:Fax>030-0076545</d:Fax>
<d:Address>
<d:Street>Obere Str. 57</d:Street>
<d:City>Berlin</d:City>
<d:Region m:null="true"/>
<d:PostalCode>D-12209</d:PostalCode>
<link rel="http://docs.oasis-open.org/odata/ns/related/Country"
type="application/atom+xml;type=entry"
title="Country"
href="Customers('ALFKI')/Address/Country"/>
</d:Address>
</m:properties>
</content>
</entry>

View File

@ -22,7 +22,7 @@ import org.apache.olingo.commons.api.domain.ODataOperation;
import java.net.URI;
import java.util.List;
public interface Entry {
public interface Entry extends Linked {
/**
* Gets ETag.
@ -94,20 +94,6 @@ public interface Entry {
*/
void setEditLink(Link editLink);
/**
* Gets association links.
*
* @return association links.
*/
List<Link> getAssociationLinks();
/**
* Gets navigation links.
*
* @return links.
*/
List<Link> getNavigationLinks();
/**
* Gets media entity links.
*

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.data;
import java.util.List;
public interface Linked {
/**
* Gets association links.
*
* @return association links.
*/
List<Link> getAssociationLinks();
/**
* Gets navigation links.
*
* @return links.
*/
List<Link> getNavigationLinks();
}

View File

@ -0,0 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.data;
public interface LinkedComplexValue extends ComplexValue, Linked {
}

View File

@ -30,6 +30,8 @@ public interface Value {
boolean isComplex();
boolean isLinkedComplex();
boolean isCollection();
Object get();
@ -44,5 +46,7 @@ public interface Value {
ComplexValue asComplex();
LinkedComplexValue asLinkedComplex();
CollectionValue asCollection();
}

View File

@ -24,12 +24,26 @@ import java.util.List;
/**
* OData entity.
*/
public interface CommonODataEntity extends ODataInvokeResult {
public interface CommonODataEntity extends ODataLinked, ODataInvokeResult {
String getName();
URI getLink();
/**
* Returns OData entity edit link.
*
* @return entity edit link.
*/
URI getEditLink();
/**
* Sets OData entity edit link.
*
* @param editLink edit link.
*/
void setEditLink(URI editLink);
/**
* Gets ETag.
*
@ -74,52 +88,6 @@ public interface CommonODataEntity extends ODataInvokeResult {
*/
List<? extends CommonODataProperty> getProperties();
/**
* Puts the given link into one of available lists, based on its type.
*
* @param link to be added
* @return <tt>true</tt> if the given link was added in one of available lists
*/
boolean addLink(ODataLink link);
/**
* Removes the given link from any list (association, navigation, edit-media).
*
* @param link to be removed
* @return <tt>true</tt> if the given link was contained in one of available lists
*/
boolean removeLink(ODataLink link);
/**
* Gets association link with given name, if available, otherwise <tt>null</tt>.
*
* @param name candidate link name
* @return association link with given name, if available, otherwise <tt>null</tt>
*/
ODataLink getAssociationLink(String name);
/**
* Returns all entity association links.
*
* @return OData entity links.
*/
List<ODataLink> getAssociationLinks();
/**
* Gets navigation link with given name, if available, otherwise <tt>null</tt>.
*
* @param name candidate link name
* @return navigation link with given name, if available, otherwise <tt>null</tt>
*/
ODataLink getNavigationLink(String name);
/**
* Returns all entity navigation links (including inline entities / feeds).
*
* @return OData entity links.
*/
List<ODataLink> getNavigationLinks();
/**
* Gets media-edit link with given name, if available, otherwise <tt>null</tt>.
*
@ -135,20 +103,6 @@ public interface CommonODataEntity extends ODataInvokeResult {
*/
List<ODataLink> getEditMediaLinks();
/**
* Returns OData entity edit link.
*
* @return entity edit link.
*/
URI getEditLink();
/**
* Sets OData entity edit link.
*
* @param editLink edit link.
*/
void setEditLink(URI editLink);
/**
* TRUE if read-only entity.
*

View File

@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.domain;
import java.util.List;
public interface ODataLinked {
/**
* Puts the given link into one of available lists, based on its type.
*
* @param link to be added
* @return <tt>true</tt> if the given link was added in one of available lists
*/
boolean addLink(ODataLink link);
/**
* Removes the given link from any list (association, navigation, edit-media).
*
* @param link to be removed
* @return <tt>true</tt> if the given link was contained in one of available lists
*/
boolean removeLink(ODataLink link);
/**
* Gets association link with given name, if available, otherwise <tt>null</tt>.
*
* @param name candidate link name
* @return association link with given name, if available, otherwise <tt>null</tt>
*/
ODataLink getAssociationLink(String name);
/**
* Returns all entity association links.
*
* @return OData entity links.
*/
List<ODataLink> getAssociationLinks();
/**
* Gets navigation link with given name, if available, otherwise <tt>null</tt>.
*
* @param name candidate link name
* @return navigation link with given name, if available, otherwise <tt>null</tt>
*/
ODataLink getNavigationLink(String name);
/**
* Returns all entity navigation links (including inline entities / feeds).
*
* @return OData entity links.
*/
List<ODataLink> getNavigationLinks();
}

View File

@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.domain.v4;
import org.apache.olingo.commons.api.domain.ODataComplexValue;
import org.apache.olingo.commons.api.domain.ODataLinked;
public interface ODataLinkedComplexValue extends ODataValue, ODataLinked, ODataComplexValue<ODataProperty> {
}

View File

@ -44,6 +44,8 @@ public interface ODataObjectFactory extends CommonODataObjectFactory {
@Override
ODataComplexValue<ODataProperty> newComplexValue(String typeName);
ODataLinkedComplexValue newLinkedComplexValue(String typeName);
@Override
ODataCollectionValue<ODataValue> newCollectionValue(String typeName);

View File

@ -37,6 +37,13 @@ public interface ODataProperty extends CommonODataProperty {
* @return complex value if exists; null otherwise.
*/
ODataComplexValue<ODataProperty> getComplexValue();
/**
* Gets complex value with link information (if available).
*
* @return complex value if exists; null otherwise.
*/
ODataLinkedComplexValue getLinkedComplexValue();
/**
* Checks if has enum value.

View File

@ -20,6 +20,20 @@ package org.apache.olingo.commons.api.domain.v4;
public interface ODataValue extends org.apache.olingo.commons.api.domain.ODataValue {
/**
* Check is is a linked complex value.
*
* @return 'TRUE' if linked complex; 'FALSE' otherwise.
*/
boolean isLinkedComplex();
/**
* Casts to complex value with link information (if available).
*
* @return complex value with link information.
*/
ODataLinkedComplexValue asLinkedComplex();
/**
* Check is is an enum value.
*

View File

@ -81,7 +81,7 @@ abstract class AbstractAtomDealer {
new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
this.typeQName = new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ATTR_TYPE);
this.nullQName = new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ATTR_NULL);
this.elementQName = version == ODataServiceVersion.V30
this.elementQName = version.compareTo(ODataServiceVersion.V40) < 0
? new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES), Constants.ELEM_ELEMENT)
: new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ELEM_ELEMENT);
this.countQName =

View File

@ -18,17 +18,26 @@
*/
package org.apache.olingo.commons.core.data;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.Container;
import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.domain.ODataPropertyType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Container<T>> {
@ -42,6 +51,76 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
return geoDeserializer;
}
protected String getTitle(final Map.Entry<String, JsonNode> entry) {
return entry.getKey().substring(0, entry.getKey().indexOf('@'));
}
protected String setInline(final String name, final String suffix, final JsonNode tree,
final ObjectCodec codec, final LinkImpl link) throws IOException {
final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
if (tree.has(entryNamePrefix)) {
final JsonNode inline = tree.path(entryNamePrefix);
if (inline instanceof ObjectNode) {
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
link.setInlineEntry(inline.traverse(codec).<Container<JSONEntryImpl>>readValueAs(
new TypeReference<JSONEntryImpl>() {
}).getObject());
}
if (inline instanceof ArrayNode) {
link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
final JSONFeedImpl feed = new JSONFeedImpl();
final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
while (entries.hasNext()) {
feed.getEntries().add(entries.next().traverse(codec).<Container<JSONEntryImpl>>readValuesAs(
new TypeReference<JSONEntryImpl>() {
}).next().getObject());
}
link.setInlineFeed(feed);
}
}
return entryNamePrefix;
}
protected void links(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove,
final JsonNode tree, final ObjectCodec codec) throws IOException {
if (field.getKey().endsWith(jsonNavigationLink)) {
final LinkImpl link = new LinkImpl();
link.setTitle(getTitle(field));
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL) + getTitle(field));
if (field.getValue().isValueNode()) {
link.setHref(field.getValue().textValue());
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
}
// NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
/* if (field.getValue().isArray()) {
* link.setHref(field.getValue().asText());
* link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
* } */
linked.getNavigationLinks().add(link);
toRemove.add(field.getKey());
toRemove.add(setInline(field.getKey(), jsonNavigationLink, tree, codec, link));
} else if (field.getKey().endsWith(jsonAssociationLink)) {
final LinkImpl link = new LinkImpl();
link.setTitle(getTitle(field));
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL) + getTitle(field));
link.setHref(field.getValue().textValue());
link.setType(ODataLinkType.ASSOCIATION.toString());
linked.getAssociationLinks().add(link);
toRemove.add(field.getKey());
}
}
protected EdmPrimitiveTypeKind getPrimitiveType(final JsonNode node) {
EdmPrimitiveTypeKind result = EdmPrimitiveTypeKind.String;
@ -88,8 +167,20 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
return value;
}
private ComplexValue fromComplex(final JsonNode node) {
final ComplexValue value = new ComplexValueImpl();
private ComplexValue fromComplex(final ObjectNode node, final ObjectCodec codec) throws IOException {
final ComplexValue value = version.compareTo(ODataServiceVersion.V40) < 0
? new ComplexValueImpl()
: new LinkedComplexValueImpl();
if (value.isLinkedComplex()) {
final Set<String> toRemove = new HashSet<String>();
for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
final Map.Entry<String, JsonNode> field = itor.next();
links(field, value.asLinkedComplex(), toRemove, node, codec);
}
node.remove(toRemove);
}
String type = null;
for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
@ -103,7 +194,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
property.setType(type);
type = null;
value(property, field.getValue());
value(property, field.getValue(), codec);
value.get().add(property);
}
}
@ -111,7 +202,9 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
return value;
}
private CollectionValue fromCollection(final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo) {
private CollectionValue fromCollection(final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo,
final ObjectCodec codec) throws IOException {
final CollectionValueImpl value = new CollectionValueImpl();
final EdmTypeInfo type = typeInfo == null
@ -131,14 +224,16 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
if (child.has(jsonType)) {
((ObjectNode) child).remove(jsonType);
}
value.get().add(fromComplex(child));
value.get().add(fromComplex((ObjectNode) child, codec));
}
}
return value;
}
protected void value(final JSONPropertyImpl property, final JsonNode node) {
protected void value(final JSONPropertyImpl property, final JsonNode node, final ObjectCodec codec)
throws IOException {
final EdmTypeInfo typeInfo = StringUtils.isBlank(property.getType())
? null
: new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
@ -155,7 +250,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
switch (propType) {
case COLLECTION:
property.setValue(fromCollection(node.elements(), typeInfo));
property.setValue(fromCollection(node.elements(), typeInfo, codec));
break;
case COMPLEX:
@ -163,7 +258,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
property.setType(node.get(jsonType).asText());
((ObjectNode) node).remove(jsonType);
}
property.setValue(fromComplex(node));
property.setValue(fromComplex((ObjectNode) node, codec));
break;
case ENUM:

View File

@ -20,12 +20,22 @@ package org.apache.olingo.commons.core.data;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.Entry;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Linked;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
@ -39,6 +49,50 @@ abstract class AbstractJsonSerializer<T> extends ODataJacksonSerializer<T> {
private final JSONGeoValueSerializer geoSerializer = new JSONGeoValueSerializer();
protected void links(final Linked linked, final JsonGenerator jgen) throws IOException {
final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
for (Link link : linked.getNavigationLinks()) {
ODataLinkType type = null;
try {
type = ODataLinkType.fromString(version, link.getRel(), link.getType());
} catch (IllegalArgumentException e) {
// ignore
}
if (type == ODataLinkType.ENTITY_SET_NAVIGATION) {
final List<String> uris;
if (entitySetLinks.containsKey(link.getTitle())) {
uris = entitySetLinks.get(link.getTitle());
} else {
uris = new ArrayList<String>();
entitySetLinks.put(link.getTitle(), uris);
}
uris.add(link.getHref());
} else {
if (StringUtils.isNotBlank(link.getHref())) {
jgen.writeStringField(link.getTitle() + Constants.JSON_BIND_LINK_SUFFIX, link.getHref());
}
}
if (link.getInlineEntry() != null) {
jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
} else if (link.getInlineFeed() != null) {
jgen.writeArrayFieldStart(link.getTitle());
for (Entry subEntry : link.getInlineFeed().getEntries()) {
jgen.writeObject(subEntry);
}
jgen.writeEndArray();
}
}
for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
jgen.writeArrayFieldStart(entitySetLink.getKey() + Constants.JSON_BIND_LINK_SUFFIX);
for (String uri : entitySetLink.getValue()) {
jgen.writeString(uri);
}
jgen.writeEndArray();
}
}
private void collection(final JsonGenerator jgen, final String itemType, final CollectionValue value)
throws IOException {
@ -85,6 +139,9 @@ abstract class AbstractJsonSerializer<T> extends ODataJacksonSerializer<T> {
for (Property property : value.asComplex().get()) {
property(jgen, property, property.getName());
}
if (value.isLinkedComplex()) {
links(value.asLinkedComplex(), jgen);
}
jgen.writeEndObject();
}
}

View File

@ -26,6 +26,7 @@ import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.EnumValue;
import org.apache.olingo.commons.api.data.GeospatialValue;
import org.apache.olingo.commons.api.data.LinkedComplexValue;
import org.apache.olingo.commons.api.data.NullValue;
import org.apache.olingo.commons.api.data.PrimitiveValue;
import org.apache.olingo.commons.api.data.Value;
@ -57,6 +58,11 @@ public abstract class AbstractValue implements Value {
return false;
}
@Override
public boolean isLinkedComplex() {
return false;
}
@Override
public boolean isCollection() {
return false;
@ -87,6 +93,11 @@ public abstract class AbstractValue implements Value {
return isComplex() ? (ComplexValue) this : null;
}
@Override
public LinkedComplexValue asLinkedComplex() {
return isLinkedComplex() ? (LinkedComplexValue) this : null;
}
@Override
public CollectionValue asCollection() {
return isCollection() ? (CollectionValue) this : null;

View File

@ -29,27 +29,264 @@ import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataOperation;
import org.apache.olingo.commons.api.domain.ODataPropertyType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.data.v3.XMLLinkCollectionImpl;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
public class AtomDeserializer extends AbstractAtomDealer {
private static final XMLInputFactory FACTORY = XMLInputFactory.newInstance();
private final AtomPropertyDeserializer propDeserializer;
private final AtomGeoValueDeserializer geoDeserializer;
public AtomDeserializer(final ODataServiceVersion version) {
super(version);
this.propDeserializer = new AtomPropertyDeserializer(version);
this.geoDeserializer = new AtomGeoValueDeserializer();
}
private Value fromPrimitive(final XMLEventReader reader, final StartElement start,
final EdmTypeInfo typeInfo) throws XMLStreamException {
Value value = null;
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement() && typeInfo != null && typeInfo.getPrimitiveTypeKind().isGeospatial()) {
final EdmPrimitiveTypeKind geoType = EdmPrimitiveTypeKind.valueOfFQN(
version, typeInfo.getFullQualifiedName().toString());
value = new GeospatialValueImpl(this.geoDeserializer.deserialize(reader, event.asStartElement(), geoType));
}
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
&& (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
value = new PrimitiveValueImpl(event.asCharacters().getData());
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value == null ? new PrimitiveValueImpl(StringUtils.EMPTY) : value;
}
private Value fromComplexOrEnum(final XMLEventReader reader, final StartElement start)
throws XMLStreamException {
Value value = null;
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
if (value == null) {
value = version.compareTo(ODataServiceVersion.V40) < 0
? new ComplexValueImpl()
: new LinkedComplexValueImpl();
}
if (Constants.QNAME_ATOM_ELEM_LINK.equals(event.asStartElement().getName())) {
final LinkImpl link = new LinkImpl();
final Attribute rel = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_REL));
if (rel != null) {
link.setRel(rel.getValue());
}
final Attribute title = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TITLE));
if (title != null) {
link.setTitle(title.getValue());
}
final Attribute href = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_HREF));
if (href != null) {
link.setHref(href.getValue());
}
final Attribute type = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TYPE));
if (type != null) {
link.setType(type.getValue());
}
if (link.getRel().startsWith(
version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL))) {
value.asLinkedComplex().getNavigationLinks().add(link);
inline(reader, event.asStartElement(), link);
} else if (link.getRel().startsWith(
version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL))) {
value.asLinkedComplex().getAssociationLinks().add(link);
}
} else {
value.asComplex().get().add(property(reader, event.asStartElement()));
}
}
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
value = new EnumValueImpl(event.asCharacters().getData());
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value;
}
private CollectionValue fromCollection(final XMLEventReader reader, final StartElement start,
final EdmTypeInfo typeInfo) throws XMLStreamException {
final CollectionValueImpl value = new CollectionValueImpl();
final EdmTypeInfo type = typeInfo == null
? null
: new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
switch (guessPropertyType(reader, typeInfo)) {
case COMPLEX:
case ENUM:
value.get().add(fromComplexOrEnum(reader, event.asStartElement()));
break;
case PRIMITIVE:
value.get().add(fromPrimitive(reader, event.asStartElement(), type));
break;
default:
// do not add null or empty values
}
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value;
}
private ODataPropertyType guessPropertyType(final XMLEventReader reader, final EdmTypeInfo typeInfo)
throws XMLStreamException {
XMLEvent child = null;
while (reader.hasNext() && child == null) {
final XMLEvent event = reader.peek();
if (event.isCharacters() && event.asCharacters().isWhiteSpace()) {
reader.nextEvent();
} else {
child = event;
}
}
final ODataPropertyType type;
if (child == null) {
type = typeInfo == null || typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.ENUM;
} else {
if (child.isStartElement()) {
if (Constants.NS_GML.equals(child.asStartElement().getName().getNamespaceURI())) {
type = ODataPropertyType.PRIMITIVE;
} else if (elementQName.equals(child.asStartElement().getName())) {
type = ODataPropertyType.COLLECTION;
} else {
type = ODataPropertyType.COMPLEX;
}
} else if (child.isCharacters()) {
type = typeInfo == null || typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.ENUM;
} else {
type = ODataPropertyType.EMPTY;
}
}
return type;
}
private AtomPropertyImpl property(final XMLEventReader reader, final StartElement start)
throws XMLStreamException {
final AtomPropertyImpl property = new AtomPropertyImpl();
if (ODataServiceVersion.V40 == version && v4PropertyValueQName.equals(start.getName())) {
// retrieve name from context
final Attribute context = start.getAttributeByName(contextQName);
if (context != null) {
property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
}
} else {
property.setName(start.getName().getLocalPart());
}
final Attribute nullAttr = start.getAttributeByName(this.nullQName);
Value value;
if (nullAttr == null) {
final Attribute typeAttr = start.getAttributeByName(this.typeQName);
final String typeAttrValue = typeAttr == null ? null : typeAttr.getValue();
final EdmTypeInfo typeInfo = StringUtils.isBlank(typeAttrValue)
? null
: new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
if (typeInfo != null) {
property.setType(typeInfo.getTypeExpression());
}
final ODataPropertyType propType = typeInfo == null
? guessPropertyType(reader, typeInfo)
: typeInfo.isCollection()
? ODataPropertyType.COLLECTION
: typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.COMPLEX;
switch (propType) {
case COLLECTION:
value = fromCollection(reader, start, typeInfo);
break;
case COMPLEX:
value = fromComplexOrEnum(reader, start);
break;
case PRIMITIVE:
value = fromPrimitive(reader, start, typeInfo);
break;
case EMPTY:
default:
value = new PrimitiveValueImpl(StringUtils.EMPTY);
}
} else {
value = new NullValueImpl();
}
property.setValue(value);
return property;
}
private Container<AtomPropertyImpl> property(final InputStream input) throws XMLStreamException {
final XMLEventReader reader = FACTORY.createXMLEventReader(input);
final StartElement start = skipBeforeFirstStartElement(reader);
return getContainer(start, propDeserializer.deserialize(reader, start));
return getContainer(start, property(reader, start));
}
private StartElement skipBeforeFirstStartElement(final XMLEventReader reader) throws XMLStreamException {
@ -162,7 +399,7 @@ public class AtomDeserializer extends AbstractAtomDealer {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
entry.getProperties().add(propDeserializer.deserialize(reader, event.asStartElement()));
entry.getProperties().add(property(reader, event.asStartElement()));
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {

View File

@ -1,240 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.core.data;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.domain.ODataPropertyType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
class AtomPropertyDeserializer extends AbstractAtomDealer {
private final AtomGeoValueDeserializer geoDeserializer;
public AtomPropertyDeserializer(final ODataServiceVersion version) {
super(version);
this.geoDeserializer = new AtomGeoValueDeserializer();
}
private Value fromPrimitive(final XMLEventReader reader, final StartElement start,
final EdmTypeInfo typeInfo) throws XMLStreamException {
Value value = null;
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement() && typeInfo != null && typeInfo.getPrimitiveTypeKind().isGeospatial()) {
final EdmPrimitiveTypeKind geoType = EdmPrimitiveTypeKind.valueOfFQN(
version, typeInfo.getFullQualifiedName().toString());
value = new GeospatialValueImpl(this.geoDeserializer.deserialize(reader, event.asStartElement(), geoType));
}
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
&& (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
value = new PrimitiveValueImpl(event.asCharacters().getData());
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value == null ? new PrimitiveValueImpl(StringUtils.EMPTY) : value;
}
private Value fromComplexOrEnum(final XMLEventReader reader, final StartElement start)
throws XMLStreamException {
Value value = null;
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
if (value == null) {
value = new ComplexValueImpl();
}
value.asComplex().get().add(deserialize(reader, event.asStartElement()));
}
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
value = new EnumValueImpl(event.asCharacters().getData());
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value;
}
private CollectionValue fromCollection(final XMLEventReader reader, final StartElement start,
final EdmTypeInfo typeInfo) throws XMLStreamException {
final CollectionValueImpl value = new CollectionValueImpl();
final EdmTypeInfo type = typeInfo == null
? null
: new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
boolean foundEndProperty = false;
while (reader.hasNext() && !foundEndProperty) {
final XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
switch (guessPropertyType(reader, typeInfo)) {
case COMPLEX:
case ENUM:
value.get().add(fromComplexOrEnum(reader, event.asStartElement()));
break;
case PRIMITIVE:
value.get().add(fromPrimitive(reader, event.asStartElement(), type));
break;
default:
// do not add null or empty values
}
}
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
foundEndProperty = true;
}
}
return value;
}
private ODataPropertyType guessPropertyType(final XMLEventReader reader, final EdmTypeInfo typeInfo)
throws XMLStreamException {
XMLEvent child = null;
while (reader.hasNext() && child == null) {
final XMLEvent event = reader.peek();
if (event.isCharacters() && event.asCharacters().isWhiteSpace()) {
reader.nextEvent();
} else {
child = event;
}
}
final ODataPropertyType type;
if (child == null) {
type = typeInfo == null || typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.ENUM;
} else {
if (child.isStartElement()) {
if (Constants.NS_GML.equals(child.asStartElement().getName().getNamespaceURI())) {
type = ODataPropertyType.PRIMITIVE;
} else if (elementQName.equals(child.asStartElement().getName())) {
type = ODataPropertyType.COLLECTION;
} else {
type = ODataPropertyType.COMPLEX;
}
} else if (child.isCharacters()) {
type = typeInfo == null || typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.ENUM;
} else {
type = ODataPropertyType.EMPTY;
}
}
return type;
}
public AtomPropertyImpl deserialize(final XMLEventReader reader, final StartElement start)
throws XMLStreamException {
final AtomPropertyImpl property = new AtomPropertyImpl();
if (ODataServiceVersion.V40 == version && v4PropertyValueQName.equals(start.getName())) {
// retrieve name from context
final Attribute context = start.getAttributeByName(contextQName);
if (context != null) {
property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
}
} else {
property.setName(start.getName().getLocalPart());
}
final Attribute nullAttr = start.getAttributeByName(this.nullQName);
Value value;
if (nullAttr == null) {
final Attribute typeAttr = start.getAttributeByName(this.typeQName);
final String typeAttrValue = typeAttr == null ? null : typeAttr.getValue();
final EdmTypeInfo typeInfo = StringUtils.isBlank(typeAttrValue)
? null
: new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
if (typeInfo != null) {
property.setType(typeInfo.getTypeExpression());
}
final ODataPropertyType propType = typeInfo == null
? guessPropertyType(reader, typeInfo)
: typeInfo.isCollection()
? ODataPropertyType.COLLECTION
: typeInfo.isPrimitiveType()
? ODataPropertyType.PRIMITIVE
: ODataPropertyType.COMPLEX;
switch (propType) {
case COLLECTION:
value = fromCollection(reader, start, typeInfo);
break;
case COMPLEX:
value = fromComplexOrEnum(reader, start);
break;
case PRIMITIVE:
value = fromPrimitive(reader, start, typeInfo);
break;
case EMPTY:
default:
value = new PrimitiveValueImpl(StringUtils.EMPTY);
}
} else {
value = new NullValueImpl();
}
property.setValue(value);
return property;
}
}

View File

@ -1,116 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.core.data;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
class AtomPropertySerializer extends AbstractAtomDealer {
private final AtomGeoValueSerializer geoSerializer;
public AtomPropertySerializer(final ODataServiceVersion version) {
super(version);
this.geoSerializer = new AtomGeoValueSerializer();
}
private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
for (Value item : value.get()) {
if (version == ODataServiceVersion.V30) {
writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
} else {
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
}
value(writer, item);
writer.writeEndElement();
}
}
private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
if (value.isPrimitive()) {
writer.writeCharacters(value.asPrimitive().get());
} else if (value.isEnum()) {
writer.writeCharacters(value.asEnum().get());
} else if (value.isGeospatial()) {
this.geoSerializer.serialize(writer, value.asGeospatial().get());
} else if (value.isCollection()) {
collection(writer, value.asCollection());
} else if (value.isComplex()) {
for (Property property : value.asComplex().get()) {
property(writer, property, false);
}
}
}
public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
throws XMLStreamException {
if (version == ODataServiceVersion.V40 && standalone) {
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
} else {
writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
}
if (standalone) {
namespaces(writer);
}
if (StringUtils.isNotBlank(property.getType())) {
String type = property.getType();
if (version == ODataServiceVersion.V40) {
final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
if (typeInfo.isPrimitiveType()) {
if (typeInfo.isCollection()) {
type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
} else {
type = typeInfo.getFullQualifiedName().getName();
}
} else {
type = "#" + property.getType();
}
}
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
Constants.ATTR_TYPE, type);
}
if (property.getValue().isNull()) {
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
Constants.ATTR_NULL, Boolean.TRUE.toString());
} else {
value(writer, property.getValue());
}
writer.writeEndElement();
}
public void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
property(writer, property, true);
}
}

View File

@ -27,22 +27,106 @@ import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.CollectionValue;
import org.apache.olingo.commons.api.data.Entry;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.Value;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
public class AtomSerializer extends AbstractAtomDealer {
private static final XMLOutputFactory FACTORY = XMLOutputFactory.newInstance();
private final AtomPropertySerializer propSerializer;
private final AtomGeoValueSerializer geoSerializer;
public AtomSerializer(final ODataServiceVersion version) {
super(version);
this.propSerializer = new AtomPropertySerializer(version);
this.geoSerializer = new AtomGeoValueSerializer();
}
private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
for (Value item : value.get()) {
if (version.compareTo(ODataServiceVersion.V40) < 0) {
writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
} else {
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
}
value(writer, item);
writer.writeEndElement();
}
}
private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
if (value.isPrimitive()) {
writer.writeCharacters(value.asPrimitive().get());
} else if (value.isEnum()) {
writer.writeCharacters(value.asEnum().get());
} else if (value.isGeospatial()) {
this.geoSerializer.serialize(writer, value.asGeospatial().get());
} else if (value.isCollection()) {
collection(writer, value.asCollection());
} else if (value.isComplex()) {
for (Property property : value.asComplex().get()) {
property(writer, property, false);
}
}
}
public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
throws XMLStreamException {
if (version.compareTo(ODataServiceVersion.V40) >= 0 && standalone) {
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
} else {
writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
}
if (standalone) {
namespaces(writer);
}
if (StringUtils.isNotBlank(property.getType())) {
String type = property.getType();
if (version.compareTo(ODataServiceVersion.V40) >= 0) {
final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
if (typeInfo.isPrimitiveType()) {
if (typeInfo.isCollection()) {
type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
} else {
type = typeInfo.getFullQualifiedName().getName();
}
} else {
type = "#" + property.getType();
}
}
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
Constants.ATTR_TYPE, type);
}
if (property.getValue().isNull()) {
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
Constants.ATTR_NULL, Boolean.TRUE.toString());
} else {
value(writer, property.getValue());
if (property.getValue().isLinkedComplex()) {
links(writer, property.getValue().asLinkedComplex().getAssociationLinks());
links(writer, property.getValue().asLinkedComplex().getNavigationLinks());
}
}
writer.writeEndElement();
}
private void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
property(writer, property, true);
}
private void startDocument(final XMLStreamWriter writer, final String rootElement) throws XMLStreamException {
@ -59,7 +143,7 @@ public class AtomSerializer extends AbstractAtomDealer {
writer.writeStartDocument();
propSerializer.property(writer, property);
property(writer, property);
writer.writeEndDocument();
writer.flush();
@ -122,7 +206,7 @@ public class AtomSerializer extends AbstractAtomDealer {
private void properties(final XMLStreamWriter writer, final List<Property> properties) throws XMLStreamException {
for (Property property : properties) {
propSerializer.property(writer, property, false);
property(writer, property, false);
}
}

View File

@ -21,11 +21,8 @@ package org.apache.olingo.commons.core.data;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.net.URI;
@ -47,47 +44,11 @@ import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
*/
public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImpl> {
private String getTitle(final Map.Entry<String, JsonNode> entry) {
return entry.getKey().substring(0, entry.getKey().indexOf('@'));
}
private String setInline(final String name, final String suffix, final ObjectNode tree,
final ObjectCodec codec, final LinkImpl link) throws IOException {
final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
if (tree.has(entryNamePrefix)) {
final JsonNode inline = tree.path(entryNamePrefix);
if (inline instanceof ObjectNode) {
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
link.setInlineEntry(inline.traverse(codec).<Container<JSONEntryImpl>>readValueAs(
new TypeReference<JSONEntryImpl>() {
}).getObject());
}
if (inline instanceof ArrayNode) {
link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
final JSONFeedImpl feed = new JSONFeedImpl();
final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
while (entries.hasNext()) {
feed.getEntries().add(entries.next().traverse(codec).<Container<JSONEntryImpl>>readValuesAs(
new TypeReference<JSONEntryImpl>() {
}).next().getObject());
}
link.setInlineFeed(feed);
}
}
return entryNamePrefix;
}
@Override
protected Container<JSONEntryImpl> doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
throws IOException, JsonProcessingException {
final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
final ObjectNode tree = parser.getCodec().readTree(parser);
if (tree.has(Constants.VALUE) && tree.get(Constants.VALUE).isArray()) {
throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
@ -173,35 +134,8 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) {
final Map.Entry<String, JsonNode> field = itor.next();
if (field.getKey().endsWith(jsonNavigationLink)) {
final LinkImpl link = new LinkImpl();
link.setTitle(getTitle(field));
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL) + getTitle(field));
if (field.getValue().isValueNode()) {
link.setHref(field.getValue().textValue());
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
}
// NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
/* if (field.getValue().isArray()) {
* link.setHref(field.getValue().asText());
* link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
* } */
entry.getNavigationLinks().add(link);
toRemove.add(field.getKey());
toRemove.add(setInline(field.getKey(), jsonNavigationLink, tree, parser.getCodec(), link));
} else if (field.getKey().endsWith(jsonAssociationLink)) {
final LinkImpl link = new LinkImpl();
link.setTitle(getTitle(field));
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL) + getTitle(field));
link.setHref(field.getValue().textValue());
link.setType(ODataLinkType.ASSOCIATION.toString());
entry.getAssociationLinks().add(link);
toRemove.add(field.getKey());
} else if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
links(field, entry, toRemove, tree, parser.getCodec());
if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
final LinkImpl link = new LinkImpl();
link.setTitle(getTitle(field));
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL) + getTitle(field));
@ -251,7 +185,7 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
property.setType(type);
type = null;
value(property, field.getValue());
value(property, field.getValue(), parser.getCodec());
entry.getProperties().add(property);
}
}

View File

@ -22,16 +22,9 @@ import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.Entry;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.domain.ODataLinkType;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
/**
@ -49,49 +42,12 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_ID), entry.getId());
}
final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
for (Link link : entry.getNavigationLinks()) {
ODataLinkType type = null;
try {
type = ODataLinkType.fromString(version, link.getRel(), link.getType());
} catch (IllegalArgumentException e) {
// ignore
}
if (type == ODataLinkType.ENTITY_SET_NAVIGATION) {
final List<String> uris;
if (entitySetLinks.containsKey(link.getTitle())) {
uris = entitySetLinks.get(link.getTitle());
} else {
uris = new ArrayList<String>();
entitySetLinks.put(link.getTitle(), uris);
}
uris.add(link.getHref());
} else {
if (StringUtils.isNotBlank(link.getHref())) {
jgen.writeStringField(link.getTitle() + Constants.JSON_BIND_LINK_SUFFIX, link.getHref());
}
}
if (link.getInlineEntry() != null) {
jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
} else if (link.getInlineFeed() != null) {
jgen.writeArrayFieldStart(link.getTitle());
for (Entry subEntry : link.getInlineFeed().getEntries()) {
jgen.writeObject(subEntry);
}
jgen.writeEndArray();
}
}
for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
jgen.writeArrayFieldStart(entitySetLink.getKey() + Constants.JSON_BIND_LINK_SUFFIX);
for (String uri : entitySetLink.getValue()) {
jgen.writeString(uri);
}
jgen.writeEndArray();
for (Property property : entry.getProperties()) {
property(jgen, property, property.getName());
}
links(entry, jgen);
for (Link link : entry.getMediaEditLinks()) {
if (link.getTitle() == null) {
jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_MEDIAEDIT_LINK), link.getHref());
@ -109,10 +65,6 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
}
}
for (Property property : entry.getProperties()) {
property(jgen, property, property.getName());
}
jgen.writeEndObject();
}
}

View File

@ -73,7 +73,7 @@ public class JSONPropertyDeserializer extends AbstractJsonDeserializer<JSONPrope
}
if (property.getValue() == null) {
value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree);
value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree, parser.getCodec());
}
return new Container<JSONPropertyImpl>(contextURL, metadataETag, property);

View File

@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.core.data;
import java.util.ArrayList;
import java.util.List;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.LinkedComplexValue;
public class LinkedComplexValueImpl extends ComplexValueImpl implements LinkedComplexValue {
private final List<Link> associationLinks = new ArrayList<Link>();
private final List<Link> navigationLinks = new ArrayList<Link>();
@Override
public boolean isLinkedComplex() {
return true;
}
@Override
public List<Link> getAssociationLinks() {
return associationLinks;
}
@Override
public List<Link> getNavigationLinks() {
return navigationLinks;
}
}

View File

@ -18,6 +18,7 @@
*/
package org.apache.olingo.commons.core.domain.v4;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.core.domain.AbstractODataCollectionValue;
@ -39,4 +40,14 @@ public class ODataCollectionValueImpl extends AbstractODataCollectionValue<OData
public ODataEnumValue asEnum() {
return null;
}
@Override
public boolean isLinkedComplex() {
return false;
}
@Override
public ODataLinkedComplexValue asLinkedComplex() {
return null;
}
}

View File

@ -18,15 +18,28 @@
*/
package org.apache.olingo.commons.core.domain.v4;
import java.util.ArrayList;
import java.util.List;
import org.apache.olingo.commons.api.domain.ODataLink;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.core.domain.AbstractODataComplexValue;
public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataValue {
public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataLinkedComplexValue {
private static final long serialVersionUID = 1143925901934898802L;
/**
* Navigation links (might contain in-line entities or feeds).
*/
private final List<ODataLink> navigationLinks = new ArrayList<ODataLink>();
/**
* Association links.
*/
private final List<ODataLink> associationLinks = new ArrayList<ODataLink>();
public ODataComplexValueImpl(final String typeName) {
super(typeName);
}
@ -41,4 +54,73 @@ public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProper
return null;
}
@Override
public boolean isLinkedComplex() {
return true;
}
@Override
public ODataLinkedComplexValue asLinkedComplex() {
return this;
}
@Override
public boolean addLink(final ODataLink link) {
boolean result = false;
switch (link.getType()) {
case ASSOCIATION:
result = associationLinks.contains(link) ? false : associationLinks.add(link);
break;
case ENTITY_NAVIGATION:
case ENTITY_SET_NAVIGATION:
result = navigationLinks.contains(link) ? false : navigationLinks.add(link);
break;
case MEDIA_EDIT:
throw new IllegalArgumentException("Complex values cannot have media links!");
default:
}
return result;
}
@Override
public boolean removeLink(final ODataLink link) {
return associationLinks.remove(link) || navigationLinks.remove(link);
}
private ODataLink getLink(final List<ODataLink> links, final String name) {
ODataLink result = null;
for (ODataLink link : links) {
if (name.equals(link.getName())) {
result = link;
}
}
return result;
}
@Override
public ODataLink getNavigationLink(final String name) {
return getLink(navigationLinks, name);
}
@Override
public List<ODataLink> getNavigationLinks() {
return navigationLinks;
}
@Override
public ODataLink getAssociationLink(final String name) {
return getLink(associationLinks, name);
}
@Override
public List<ODataLink> getAssociationLinks() {
return associationLinks;
}
}

View File

@ -19,6 +19,7 @@
package org.apache.olingo.commons.core.domain.v4;
import org.apache.olingo.commons.api.domain.AbstractODataValue;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumValue {
@ -47,4 +48,13 @@ public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumV
return this;
}
@Override
public boolean isLinkedComplex() {
return false;
}
@Override
public ODataLinkedComplexValue asLinkedComplex() {
return null;
}
}

View File

@ -27,6 +27,7 @@ import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
import org.apache.olingo.commons.api.domain.v4.ODataObjectFactory;
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@ -75,6 +76,11 @@ public class ODataObjectFactoryImpl extends AbstractODataObjectFactory implement
return new ODataComplexValueImpl(typeName);
}
@Override
public ODataLinkedComplexValue newLinkedComplexValue(final String typeName) {
return new ODataComplexValueImpl(typeName);
}
@Override
public ODataCollectionValue<ODataValue> newCollectionValue(final String typeName) {
return new ODataCollectionValueImpl(typeName);

View File

@ -18,6 +18,7 @@
*/
package org.apache.olingo.commons.core.domain.v4;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@ -53,4 +54,14 @@ public class ODataPrimitiveValueImpl extends AbstractODataPrimitiveValue impleme
return null;
}
@Override
public boolean isLinkedComplex() {
return false;
}
@Override
public ODataLinkedComplexValue asLinkedComplex() {
return null;
}
}

View File

@ -21,6 +21,7 @@ package org.apache.olingo.commons.core.domain.v4;
import org.apache.olingo.commons.api.domain.ODataCollectionValue;
import org.apache.olingo.commons.api.domain.ODataComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
import org.apache.olingo.commons.api.domain.v4.ODataValue;
import org.apache.olingo.commons.core.domain.AbstractODataProperty;
@ -41,7 +42,9 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
@Override
public ODataEnumValue getEnumValue() {
return hasEnumValue() ? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum() : null;
return hasEnumValue()
? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum()
: null;
}
@Override
@ -49,9 +52,18 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
return hasComplexValue() ? getValue().<ODataProperty>asComplex() : null;
}
@Override
public ODataLinkedComplexValue getLinkedComplexValue() {
return hasComplexValue() && getValue() instanceof org.apache.olingo.commons.api.domain.v4.ODataValue
? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asLinkedComplex()
: null;
}
@Override
public ODataCollectionValue<ODataValue> getCollectionValue() {
return hasCollectionValue() ? getValue().<ODataValue>asCollection() : null;
return hasCollectionValue()
? getValue().<ODataValue>asCollection()
: null;
}
}