Repository: olingo-odata4 Updated Branches: refs/heads/master d088866e9 -> 68b14ffe4
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68b14ffe/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java index e291782..b23878b 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java @@ -1,34 +1,41 @@ /* * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file + * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file + * 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 - * + * 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 + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.olingo.client.core.serialization; +import java.io.StringWriter; import java.net.URI; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.apache.olingo.client.api.EdmEnabledODataClient; import org.apache.olingo.client.api.ODataClient; import org.apache.olingo.client.api.data.ServiceDocument; import org.apache.olingo.client.api.data.ServiceDocumentItem; import org.apache.olingo.client.api.serialization.ODataBinder; import org.apache.olingo.client.core.uri.URIUtils; +import org.apache.olingo.commons.api.Constants; import org.apache.olingo.commons.api.data.Annotatable; import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.ContextURL; import org.apache.olingo.commons.api.data.DeletedEntity; import org.apache.olingo.commons.api.data.Delta; import org.apache.olingo.commons.api.data.DeltaLink; @@ -41,31 +48,50 @@ import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.ResWrap; import org.apache.olingo.commons.api.data.Valuable; import org.apache.olingo.commons.api.data.ValueType; -import org.apache.olingo.commons.api.domain.ODataEntity; -import org.apache.olingo.commons.api.domain.ODataEntitySet; -import org.apache.olingo.commons.api.domain.ODataProperty; import org.apache.olingo.commons.api.domain.ODataAnnotatable; import org.apache.olingo.commons.api.domain.ODataAnnotation; import org.apache.olingo.commons.api.domain.ODataCollectionValue; +import org.apache.olingo.commons.api.domain.ODataComplexValue; +import org.apache.olingo.commons.api.domain.ODataDeletedEntity.Reason; import org.apache.olingo.commons.api.domain.ODataDelta; import org.apache.olingo.commons.api.domain.ODataDeltaLink; +import org.apache.olingo.commons.api.domain.ODataEntity; +import org.apache.olingo.commons.api.domain.ODataEntitySet; import org.apache.olingo.commons.api.domain.ODataInlineEntity; 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.ODataLinked; import org.apache.olingo.commons.api.domain.ODataLinkedComplexValue; +import org.apache.olingo.commons.api.domain.ODataOperation; +import org.apache.olingo.commons.api.domain.ODataProperty; import org.apache.olingo.commons.api.domain.ODataServiceDocument; import org.apache.olingo.commons.api.domain.ODataValuable; import org.apache.olingo.commons.api.domain.ODataValue; -import org.apache.olingo.commons.api.domain.ODataDeletedEntity.Reason; +import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.edm.EdmBindingTarget; import org.apache.olingo.commons.api.edm.EdmComplexType; import org.apache.olingo.commons.api.edm.EdmElement; +import org.apache.olingo.commons.api.edm.EdmEntityContainer; +import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmEnumType; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.edm.EdmProperty; +import org.apache.olingo.commons.api.edm.EdmSchema; +import org.apache.olingo.commons.api.edm.EdmStructuredType; import org.apache.olingo.commons.api.edm.EdmTerm; import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.geo.Geospatial; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.api.serialization.ODataSerializerException; import org.apache.olingo.commons.core.data.AnnotationImpl; +import org.apache.olingo.commons.core.data.EntityImpl; +import org.apache.olingo.commons.core.data.EntitySetImpl; +import org.apache.olingo.commons.core.data.LinkImpl; import org.apache.olingo.commons.core.data.LinkedComplexValueImpl; import org.apache.olingo.commons.core.data.PropertyImpl; import org.apache.olingo.commons.core.domain.ODataAnnotationImpl; @@ -73,12 +99,22 @@ import org.apache.olingo.commons.core.domain.ODataDeletedEntityImpl; import org.apache.olingo.commons.core.domain.ODataDeltaLinkImpl; import org.apache.olingo.commons.core.domain.ODataPropertyImpl; import org.apache.olingo.commons.core.edm.EdmTypeInfo; +import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.commons.core.serialization.ContextURLParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ODataBinderImpl implements ODataBinder { + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(ODataBinderImpl.class); -public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder { + protected final ODataClient client; public ODataBinderImpl(final ODataClient client) { - super(client); + this.client = client; } @Override @@ -86,29 +122,32 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder return ((ODataEntity) entity).getProperties().add((ODataProperty) property); } - @Override protected boolean add(final ODataEntitySet entitySet, final ODataEntity entity) { return ((ODataEntitySet) entitySet).getEntities().add((ODataEntity) entity); } @Override public ODataServiceDocument getODataServiceDocument(final ServiceDocument resource) { - final ODataServiceDocument serviceDocument = super.getODataServiceDocument(resource); + final ODataServiceDocument serviceDocument = new ODataServiceDocument(); + for (ServiceDocumentItem entitySet : resource.getEntitySets()) { + serviceDocument.getEntitySets(). + put(entitySet.getName(), URIUtils.getURI(resource.getBaseURI(), entitySet.getUrl())); + } for (ServiceDocumentItem functionImport : resource.getFunctionImports()) { serviceDocument.getFunctionImports().put( - functionImport.getName() == null ? functionImport.getUrl() : functionImport.getName(), - URIUtils.getURI(resource.getBaseURI(), functionImport.getUrl())); + functionImport.getName() == null ? functionImport.getUrl() : functionImport.getName(), + URIUtils.getURI(resource.getBaseURI(), functionImport.getUrl())); } for (ServiceDocumentItem singleton : resource.getSingletons()) { serviceDocument.getSingletons().put( - singleton.getName() == null ? singleton.getUrl() : singleton.getName(), - URIUtils.getURI(resource.getBaseURI(), singleton.getUrl())); + singleton.getName() == null ? singleton.getUrl() : singleton.getName(), + URIUtils.getURI(resource.getBaseURI(), singleton.getUrl())); } for (ServiceDocumentItem sdoc : resource.getRelatedServiceDocuments()) { serviceDocument.getRelatedServiceDocuments().put( - sdoc.getName() == null ? sdoc.getUrl() : sdoc.getName(), - URIUtils.getURI(resource.getBaseURI(), sdoc.getUrl())); + sdoc.getName() == null ? sdoc.getUrl() : sdoc.getName(), + URIUtils.getURI(resource.getBaseURI(), sdoc.getUrl())); } return serviceDocument; @@ -119,28 +158,28 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder if (odataValuable.hasPrimitiveValue()) { propertyResource.setType(odataValuable.getPrimitiveValue().getTypeName()); propertyResource.setValue( - propertyValue instanceof Geospatial ? ValueType.GEOSPATIAL : ValueType.PRIMITIVE, - propertyValue); + propertyValue instanceof Geospatial ? ValueType.GEOSPATIAL : ValueType.PRIMITIVE, + propertyValue); } else if (odataValuable.hasEnumValue()) { propertyResource.setType(odataValuable.getEnumValue().getTypeName()); propertyResource.setValue(ValueType.ENUM, propertyValue); } else if (odataValuable.hasComplexValue()) { propertyResource.setType(odataValuable.getComplexValue().getTypeName()); propertyResource.setValue( - propertyValue instanceof LinkedComplexValue ? ValueType.LINKED_COMPLEX : ValueType.COMPLEX, - propertyValue); + propertyValue instanceof LinkedComplexValue ? ValueType.LINKED_COMPLEX : ValueType.COMPLEX, + propertyValue); } else if (odataValuable.hasCollectionValue()) { final ODataCollectionValue<org.apache.olingo.commons.api.domain.ODataValue> collectionValue = - odataValuable.getCollectionValue(); + odataValuable.getCollectionValue(); propertyResource.setType(collectionValue.getTypeName()); final org.apache.olingo.commons.api.domain.ODataValue value = - collectionValue.iterator().hasNext() ? collectionValue.iterator().next() : null; + collectionValue.iterator().hasNext() ? collectionValue.iterator().next() : null; ValueType valueType = ValueType.COLLECTION_PRIMITIVE; if (value == null) { valueType = ValueType.COLLECTION_PRIMITIVE; } else if (value.isPrimitive()) { valueType = value.asPrimitive().toValue() instanceof Geospatial - ? ValueType.COLLECTION_GEOSPATIAL : ValueType.COLLECTION_PRIMITIVE; + ? ValueType.COLLECTION_GEOSPATIAL : ValueType.COLLECTION_PRIMITIVE; } else if (value.isEnum()) { valueType = ValueType.COLLECTION_ENUM; } else if (value.isLinkedComplex()) { @@ -166,18 +205,47 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder @Override public EntitySet getEntitySet(final ODataEntitySet odataEntitySet) { - final EntitySet entitySet = super.getEntitySet(odataEntitySet); + final EntitySet entitySet = new EntitySetImpl(); + + entitySet.setCount(odataEntitySet.getCount()); + + final URI next = odataEntitySet.getNext(); + if (next != null) { + entitySet.setNext(next); + } + + for (ODataEntity entity : odataEntitySet.getEntities()) { + entitySet.getEntities().add(getEntity(entity)); + } + entitySet.setDeltaLink(((ODataEntitySet) odataEntitySet).getDeltaLink()); annotations((ODataEntitySet) odataEntitySet, entitySet); return entitySet; } - @Override protected void links(final ODataLinked odataLinked, final Linked linked) { - super.links(odataLinked, linked); + // ------------------------------------------------------------- + // Append navigation links (handling inline entity / entity set as well) + // ------------------------------------------------------------- + // handle navigation links + for (ODataLink link : odataLinked.getNavigationLinks()) { + // append link + LOG.debug("Append navigation link\n{}", link); + linked.getNavigationLinks().add(getLink(link)); + } + // ------------------------------------------------------------- + + // ------------------------------------------------------------- + // Append association links + // ------------------------------------------------------------- + for (ODataLink link : odataLinked.getAssociationLinks()) { + LOG.debug("Append association link\n{}", link); + linked.getAssociationLinks().add(getLink(link)); + } + // ------------------------------------------------------------- for (Link link : linked.getNavigationLinks()) { - final org.apache.olingo.commons.api.domain.ODataLink odataLink = odataLinked.getNavigationLink(link.getTitle()); + final ODataLink odataLink = odataLinked.getNavigationLink(link.getTitle()); if (!(odataLink instanceof ODataInlineEntity) && !(odataLink instanceof ODataInlineEntitySet)) { annotations((ODataLink) odataLink, link); } @@ -186,13 +254,84 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder @Override public Entity getEntity(final ODataEntity odataEntity) { - final Entity entity = super.getEntity(odataEntity); + final Entity entity = new EntityImpl(); + + entity.setType(odataEntity.getTypeName() == null ? null : odataEntity.getTypeName().toString()); + + // ------------------------------------------------------------- + // Add edit and self link + // ------------------------------------------------------------- + final URI odataEditLink = odataEntity.getEditLink(); + if (odataEditLink != null) { + final LinkImpl editLink = new LinkImpl(); + editLink.setTitle(entity.getType()); + editLink.setHref(odataEditLink.toASCIIString()); + editLink.setRel(Constants.EDIT_LINK_REL); + entity.setEditLink(editLink); + } + + if (odataEntity.isReadOnly()) { + final LinkImpl selfLink = new LinkImpl(); + selfLink.setTitle(entity.getType()); + selfLink.setHref(odataEntity.getLink().toASCIIString()); + selfLink.setRel(Constants.SELF_LINK_REL); + entity.setSelfLink(selfLink); + } + // ------------------------------------------------------------- + + links(odataEntity, entity); + + // ------------------------------------------------------------- + // Append edit-media links + // ------------------------------------------------------------- + for (ODataLink link : odataEntity.getMediaEditLinks()) { + LOG.debug("Append edit-media link\n{}", link); + entity.getMediaEditLinks().add(getLink(link)); + } + // ------------------------------------------------------------- + + if (odataEntity.isMediaEntity()) { + entity.setMediaContentSource(odataEntity.getMediaContentSource()); + entity.setMediaContentType(odataEntity.getMediaContentType()); + entity.setMediaETag(odataEntity.getMediaETag()); + } + + for (ODataProperty property : odataEntity.getProperties()) { + entity.getProperties().add(getProperty(property)); + } + entity.setId(((ODataEntity) odataEntity).getId()); annotations((ODataEntity) odataEntity, entity); return entity; } @Override + public Link getLink(final ODataLink link) { + final Link linkResource = new LinkImpl(); + linkResource.setRel(link.getRel()); + linkResource.setTitle(link.getName()); + linkResource.setHref(link.getLink() == null ? null : link.getLink().toASCIIString()); + linkResource.setType(link.getType().toString()); + linkResource.setMediaETag(link.getMediaETag()); + + if (link instanceof ODataInlineEntity) { + // append inline entity + final ODataEntity inlineEntity = ((ODataInlineEntity) link).getEntity(); + LOG.debug("Append in-line entity\n{}", inlineEntity); + + linkResource.setInlineEntity(getEntity(inlineEntity)); + } else if (link instanceof ODataInlineEntitySet) { + // append inline entity set + final ODataEntitySet InlineEntitySet = ((ODataInlineEntitySet) link).getEntitySet(); + LOG.debug("Append in-line entity set\n{}", InlineEntitySet); + + linkResource.setInlineEntitySet(getEntitySet(InlineEntitySet)); + } + + return linkResource; + } + + @Override public Property getProperty(final ODataProperty property) { final ODataProperty _property = (ODataProperty) property; @@ -204,26 +343,26 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder return propertyResource; } - @Override + // TODO: Refactor away the v3 code @SuppressWarnings("unchecked") protected Object getValue(final ODataValue value) { Object valueResource; if (value instanceof org.apache.olingo.commons.api.domain.ODataValue - && ((org.apache.olingo.commons.api.domain.ODataValue) value).isEnum()) { + && ((org.apache.olingo.commons.api.domain.ODataValue) value).isEnum()) { valueResource = - ((org.apache.olingo.commons.api.domain.ODataValue) value).asEnum().getValue(); + ((org.apache.olingo.commons.api.domain.ODataValue) value).asEnum().getValue(); } else { - valueResource = super.getValue(value); + valueResource = getValueInternal(value); if (value instanceof org.apache.olingo.commons.api.domain.ODataValue - && ((org.apache.olingo.commons.api.domain.ODataValue) value).isLinkedComplex()) { + && ((org.apache.olingo.commons.api.domain.ODataValue) value).isLinkedComplex()) { final LinkedComplexValue lcValueResource = new LinkedComplexValueImpl(); lcValueResource.getValue().addAll((List<Property>) valueResource); final ODataLinkedComplexValue linked = - ((org.apache.olingo.commons.api.domain.ODataValue) value).asLinkedComplex(); + ((org.apache.olingo.commons.api.domain.ODataValue) value).asLinkedComplex(); annotations(linked, lcValueResource); links(linked, lcValueResource); @@ -233,12 +372,37 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder return valueResource; } + protected Object getValueInternal(final ODataValue value) { + if (value == null) { + return null; + } else if (value.isPrimitive()) { + return value.asPrimitive().toValue(); + } else if (value.isComplex()) { + final ODataComplexValue<? extends ODataProperty> _value = value.asComplex(); + List<Property> valueResource = new ArrayList<Property>(); + + for (final ODataProperty propertyValue : _value) { + valueResource.add(getProperty(propertyValue)); + } + return valueResource; + } else if (value.isCollection()) { + final ODataCollectionValue<? extends ODataValue> _value = value.asCollection(); + ArrayList<Object> valueResource = new ArrayList<Object>(); + + for (final ODataValue collectionValue : _value) { + valueResource.add(getValue(collectionValue)); + } + return valueResource; + } + return null; + } + private void odataAnnotations(final Annotatable annotatable, final ODataAnnotatable odataAnnotatable) { for (Annotation annotation : annotatable.getAnnotations()) { FullQualifiedName fqn = null; if (client instanceof EdmEnabledODataClient) { final EdmTerm term = ((EdmEnabledODataClient) client).getCachedEdm(). - getTerm(new FullQualifiedName(annotation.getTerm())); + getTerm(new FullQualifiedName(annotation.getTerm())); if (term != null) { fqn = term.getType().getFullQualifiedName(); } @@ -252,19 +416,42 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder } final ODataAnnotation odataAnnotation = new ODataAnnotationImpl(annotation.getTerm(), - (org.apache.olingo.commons.api.domain.ODataValue) getODataValue(fqn, annotation, null, null)); + (org.apache.olingo.commons.api.domain.ODataValue) getODataValue(fqn, annotation, null, null)); odataAnnotatable.getAnnotations().add(odataAnnotation); } } @Override public ODataEntitySet getODataEntitySet(final ResWrap<EntitySet> resource) { - final ODataEntitySet entitySet = (ODataEntitySet) super.getODataEntitySet(resource); + if (LOG.isDebugEnabled()) { + final StringWriter writer = new StringWriter(); + try { + client.getSerializer(ODataFormat.JSON).write(writer, resource.getPayload()); + } catch (final ODataSerializerException e) {} + writer.flush(); + LOG.debug("EntitySet -> ODataEntitySet:\n{}", writer.toString()); + } + + final URI base = resource.getContextURL() == null + ? resource.getPayload().getBaseURI() + : ContextURLParser.parse(resource.getContextURL()).getServiceRoot(); + + final URI next = resource.getPayload().getNext(); + + final ODataEntitySet entitySet = next == null + ? client.getObjectFactory().newEntitySet() + : client.getObjectFactory().newEntitySet(URIUtils.getURI(base, next.toASCIIString())); + + if (resource.getPayload().getCount() != null) { + entitySet.setCount(resource.getPayload().getCount()); + } + + for (Entity entityResource : resource.getPayload().getEntities()) { + add(entitySet, getODataEntity( + new ResWrap<Entity>(resource.getContextURL(), resource.getMetadataETag(), entityResource))); + } if (resource.getPayload().getDeltaLink() != null) { - final URI base = resource.getContextURL() == null - ? resource.getPayload().getBaseURI() - : ContextURLParser.parse(resource.getContextURL()).getServiceRoot(); entitySet.setDeltaLink(URIUtils.getURI(base, resource.getPayload().getDeltaLink())); } odataAnnotations(resource.getPayload(), entitySet); @@ -272,11 +459,41 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder return entitySet; } - @Override protected void odataNavigationLinks(final EdmType edmType, - final Linked linked, final ODataLinked odataLinked, final String metadataETag, final URI base) { + final Linked linked, final ODataLinked odataLinked, final String metadataETag, final URI base) { + for (Link link : linked.getNavigationLinks()) { + final String href = link.getHref(); + final String title = link.getTitle(); + final Entity inlineEntity = link.getInlineEntity(); + final EntitySet inlineEntitySet = link.getInlineEntitySet(); + if (inlineEntity == null && inlineEntitySet == null) { + ODataLinkType linkType = null; + if (edmType instanceof EdmStructuredType) { + final EdmNavigationProperty navProp = ((EdmStructuredType) edmType).getNavigationProperty(title); + if (navProp != null) { + linkType = navProp.isCollection() ? + ODataLinkType.ENTITY_SET_NAVIGATION : + ODataLinkType.ENTITY_NAVIGATION; + } + } + if (linkType == null) { + linkType = link.getType() == null ? + ODataLinkType.ENTITY_NAVIGATION : + ODataLinkType.fromString(client.getServiceVersion(), link.getRel(), link.getType()); + } + + odataLinked.addLink(linkType == ODataLinkType.ENTITY_NAVIGATION ? + client.getObjectFactory().newEntityNavigationLink(title, URIUtils.getURI(base, href)) : + client.getObjectFactory().newEntitySetNavigationLink(title, URIUtils.getURI(base, href))); + } else if (inlineEntity != null) { + odataLinked.addLink(createODataInlineEntity(inlineEntity, + URIUtils.getURI(base, href), title, metadataETag)); + } else { + odataLinked.addLink(createODataInlineEntitySet(inlineEntitySet, + URIUtils.getURI(base, href), title, metadataETag)); + } + } - super.odataNavigationLinks(edmType, linked, odataLinked, metadataETag, base); for (org.apache.olingo.commons.api.domain.ODataLink link : odataLinked.getNavigationLinks()) { if (!(link instanceof ODataInlineEntity) && !(link instanceof ODataInlineEntitySet)) { odataAnnotations(linked.getNavigationLink(link.getName()), (ODataAnnotatable) link); @@ -284,9 +501,201 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder } } + private ODataInlineEntity createODataInlineEntity(final Entity inlineEntity, + final URI uri, final String title, final String metadataETag) { + return new ODataInlineEntity(client.getServiceVersion(), uri, ODataLinkType.ENTITY_NAVIGATION, title, + getODataEntity(new ResWrap<Entity>( + inlineEntity.getBaseURI() == null ? null : inlineEntity.getBaseURI(), metadataETag, + inlineEntity))); + } + + private ODataInlineEntitySet createODataInlineEntitySet(final EntitySet inlineEntitySet, + final URI uri, final String title, final String metadataETag) { + return new ODataInlineEntitySet(client.getServiceVersion(), uri, ODataLinkType.ENTITY_SET_NAVIGATION, title, + getODataEntitySet(new ResWrap<EntitySet>( + inlineEntitySet.getBaseURI() == null ? null : inlineEntitySet.getBaseURI(), metadataETag, + inlineEntitySet))); + } + + private EdmEntityType findEntityType( + final String entitySetOrSingletonOrType, final EdmEntityContainer container) { + + EdmEntityType type = null; + + final String firstToken = StringUtils.substringBefore(entitySetOrSingletonOrType, "/"); + EdmBindingTarget bindingTarget = container.getEntitySet(firstToken); + if (bindingTarget == null) { + bindingTarget = container.getSingleton(firstToken); + } + if (bindingTarget != null) { + type = bindingTarget.getEntityType(); + } + + if (entitySetOrSingletonOrType.indexOf('/') != -1) { + final String[] splitted = entitySetOrSingletonOrType.split("/"); + if (splitted.length > 1) { + for (int i = 1; i < splitted.length && type != null; i++) { + final EdmNavigationProperty navProp = type.getNavigationProperty(splitted[i]); + if (navProp == null) { + type = null; + } else { + type = navProp.getType(); + } + } + } + } + + return type; + } + + /** + * Infer type name from various sources of information including Edm and context URL, if available. + * + * @param candidateTypeName type name as provided by the service + * @param contextURL context URL + * @param metadataETag metadata ETag + * @return Edm type information + */ + private EdmType findType(final String candidateTypeName, final ContextURL contextURL, final String metadataETag) { + EdmType type = null; + + if (client instanceof EdmEnabledODataClient) { + final Edm edm = ((EdmEnabledODataClient) client).getEdm(metadataETag); + if (StringUtils.isNotBlank(candidateTypeName)) { + type = edm.getEntityType(new FullQualifiedName(candidateTypeName)); + } + if (type == null && contextURL != null) { + if (contextURL.getDerivedEntity() == null) { + for (EdmSchema schema : edm.getSchemas()) { + final EdmEntityContainer container = schema.getEntityContainer(); + if (container != null) { + final EdmEntityType entityType = findEntityType(contextURL.getEntitySetOrSingletonOrType(), container); + + if (entityType != null) { + if (contextURL.getNavOrPropertyPath() == null) { + type = entityType; + } else { + final EdmNavigationProperty navProp = + entityType.getNavigationProperty(contextURL.getNavOrPropertyPath()); + + type = navProp == null + ? entityType + : navProp.getType(); + } + } + } + } + if (type == null) { + type = new EdmTypeInfo.Builder().setEdm(edm). + setTypeExpression(contextURL.getEntitySetOrSingletonOrType()).build().getType(); + } + } else { + type = edm.getEntityType(new FullQualifiedName(contextURL.getDerivedEntity())); + } + } + } + + return type; + } + + private ODataLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName) { + if (property.isCollection()) { + EntitySet inlineEntitySet = new EntitySetImpl(); + for (final Object inlined : property.asCollection()) { + Entity inlineEntity = new EntityImpl(); + inlineEntity.setType(propertyTypeName); + inlineEntity.getProperties().addAll( + inlined instanceof LinkedComplexValue ? ((LinkedComplexValue) inlined).getValue() : + inlined instanceof Property ? ((Property) inlined).asComplex() : null); + inlineEntitySet.getEntities().add(inlineEntity); + } + return createODataInlineEntitySet(inlineEntitySet, null, property.getName(), null); + } else { + Entity inlineEntity = new EntityImpl(); + inlineEntity.setType(propertyTypeName); + inlineEntity.getProperties().addAll( + property.isLinkedComplex() ? property.asLinkedComplex().getValue() : property.asComplex()); + return createODataInlineEntity(inlineEntity, null, property.getName(), null); + } + } + @Override public ODataEntity getODataEntity(final ResWrap<Entity> resource) { - final ODataEntity entity = (ODataEntity) super.getODataEntity(resource); + if (LOG.isDebugEnabled()) { + final StringWriter writer = new StringWriter(); + try { + client.getSerializer(ODataFormat.JSON).write(writer, resource.getPayload()); + } catch (final ODataSerializerException e) {} + writer.flush(); + LOG.debug("EntityResource -> ODataEntity:\n{}", writer.toString()); + } + + final ContextURL contextURL = ContextURLParser.parse(resource.getContextURL()); + final URI base = resource.getContextURL() == null + ? resource.getPayload().getBaseURI() + : contextURL.getServiceRoot(); + final EdmType edmType = findType(resource.getPayload().getType(), contextURL, resource.getMetadataETag()); + FullQualifiedName typeName = null; + if (resource.getPayload().getType() == null) { + if (edmType != null) { + typeName = edmType.getFullQualifiedName(); + } + } else { + typeName = new FullQualifiedName(resource.getPayload().getType()); + } + + final ODataEntity entity = resource.getPayload().getSelfLink() == null + ? client.getObjectFactory().newEntity(typeName) + : client.getObjectFactory().newEntity(typeName, + URIUtils.getURI(base, resource.getPayload().getSelfLink().getHref())); + + if (StringUtils.isNotBlank(resource.getPayload().getETag())) { + entity.setETag(resource.getPayload().getETag()); + } + + if (resource.getPayload().getEditLink() != null) { + entity.setEditLink(URIUtils.getURI(base, resource.getPayload().getEditLink().getHref())); + } + + for (Link link : resource.getPayload().getAssociationLinks()) { + entity.addLink(client.getObjectFactory(). + newAssociationLink(link.getTitle(), URIUtils.getURI(base, link.getHref()))); + } + + odataNavigationLinks(edmType, resource.getPayload(), entity, resource.getMetadataETag(), base); + + for (Link link : resource.getPayload().getMediaEditLinks()) { + entity.addLink(client.getObjectFactory(). + newMediaEditLink(link.getTitle(), URIUtils.getURI(base, link.getHref()))); + } + + for (ODataOperation operation : resource.getPayload().getOperations()) { + operation.setTarget(URIUtils.getURI(base, operation.getTarget())); + entity.getOperations().add(operation); + } + + if (resource.getPayload().isMediaEntity()) { + entity.setMediaEntity(true); + entity.setMediaContentSource(URIUtils.getURI(base, resource.getPayload().getMediaContentSource())); + entity.setMediaContentType(resource.getPayload().getMediaContentType()); + entity.setMediaETag(resource.getPayload().getMediaETag()); + } + + for (final Property property : resource.getPayload().getProperties()) { + EdmType propertyType = null; + if (edmType instanceof EdmEntityType) { + final EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName()); + if (edmProperty != null) { + propertyType = edmProperty.getType(); + if (edmProperty instanceof EdmNavigationProperty) { + final String propertyTypeName = propertyType.getFullQualifiedName().getFullQualifiedNameAsString(); + entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName)); + break; + } + } + } + add(entity, getODataProperty(propertyType, property)); + } entity.setId(resource.getPayload().getId()); odataAnnotations(resource.getPayload(), entity); @@ -298,31 +707,63 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder public ODataProperty getODataProperty(final ResWrap<Property> resource) { final Property payload = resource.getPayload(); final EdmTypeInfo typeInfo = buildTypeInfo(ContextURLParser.parse(resource.getContextURL()), - resource.getMetadataETag(), payload.getName(), payload.getType()); + resource.getMetadataETag(), payload.getName(), payload.getType()); final ODataProperty property = new ODataPropertyImpl(payload.getName(), - getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), - payload, resource.getContextURL(), resource.getMetadataETag())); + getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), + payload, resource.getContextURL(), resource.getMetadataETag())); odataAnnotations(payload, property); return property; } - @Override + private EdmTypeInfo buildTypeInfo(final ContextURL contextURL, final String metadataETag, + final String propertyName, final String propertyType) { + + FullQualifiedName typeName = null; + final EdmType type = findType(null, contextURL, metadataETag); + if (type instanceof EdmStructuredType) { + final EdmProperty edmProperty = ((EdmStructuredType) type).getStructuralProperty(propertyName); + if (edmProperty != null) { + typeName = edmProperty.getType().getFullQualifiedName(); + } + } + if (typeName == null && type != null) { + typeName = type.getFullQualifiedName(); + } + + return buildTypeInfo(typeName, propertyType); + } + + private EdmTypeInfo buildTypeInfo(final FullQualifiedName typeName, final String propertyType) { + EdmTypeInfo typeInfo = null; + if (typeName == null) { + if (propertyType != null) { + typeInfo = new EdmTypeInfo.Builder().setTypeExpression(propertyType).build(); + } + } else { + if (propertyType == null || propertyType.equals(EdmPrimitiveTypeKind.String.getFullQualifiedName().toString())) { + typeInfo = new EdmTypeInfo.Builder().setTypeExpression(typeName.toString()).build(); + } else { + typeInfo = new EdmTypeInfo.Builder().setTypeExpression(propertyType).build(); + } + } + return typeInfo; + } + protected ODataProperty getODataProperty(final EdmType type, final Property resource) { final EdmTypeInfo typeInfo = buildTypeInfo(type == null ? null : type.getFullQualifiedName(), resource.getType()); final ODataProperty property = new ODataPropertyImpl(resource.getName(), - getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), - resource, null, null)); + getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), + resource, null, null)); odataAnnotations(resource, property); return property; } - @Override protected ODataValue getODataValue(final FullQualifiedName type, - final Valuable valuable, final URI contextURL, final String metadataETag) { + final Valuable valuable, final URI contextURL, final String metadataETag) { // fixes enum values treated as primitive when no type information is available if (client instanceof EdmEnabledODataClient && type != null) { @@ -332,13 +773,13 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder } } - ODataValue value; + ODataValue value = null; if (valuable.isEnum()) { value = ((ODataClient) client).getObjectFactory().newEnumValue(type == null ? null : type.toString(), - valuable.asEnum().toString()); + valuable.asEnum().toString()); } else if (valuable.isLinkedComplex()) { final ODataLinkedComplexValue lcValue = - ((ODataClient) client).getObjectFactory().newLinkedComplexValue(type == null ? null : type.toString()); + ((ODataClient) client).getObjectFactory().newLinkedComplexValue(type == null ? null : type.toString()); EdmComplexType edmType = null; if (client instanceof EdmEnabledODataClient && type != null) { @@ -361,7 +802,76 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder value = lcValue; } else { - value = super.getODataValue(type, valuable, contextURL, metadataETag); + if (valuable.isGeospatial()) { + value = client.getObjectFactory().newPrimitiveValueBuilder(). + setValue(valuable.asGeospatial()). + setType(type == null + || EdmPrimitiveTypeKind.Geography.getFullQualifiedName().equals(type) + || EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().equals(type) + ? valuable.asGeospatial().getEdmPrimitiveTypeKind() + : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), type.toString())). + build(); + } else if (valuable.isPrimitive() || valuable.getValueType() == null) { + // fixes non-string values treated as string when no type information is available at de-serialization level + if (type != null && !EdmPrimitiveTypeKind.String.getFullQualifiedName().equals(type) + && EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) + && valuable.asPrimitive() instanceof String) { + + final EdmPrimitiveType primitiveType = + EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.valueOf(type.getName())); + final Class<?> returnType = primitiveType.getDefaultType().isAssignableFrom(Calendar.class) + ? Timestamp.class : primitiveType.getDefaultType(); + try { + valuable.setValue(valuable.getValueType(), + primitiveType.valueOfString(valuable.asPrimitive().toString(), + null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null, + returnType)); + } catch (EdmPrimitiveTypeException e) { + throw new IllegalArgumentException(e); + } + } + + value = client.getObjectFactory().newPrimitiveValueBuilder(). + setValue(valuable.asPrimitive()). + setType(type == null || !EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) + ? null + : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), type.toString())). + build(); + } else if (valuable.isComplex()) { + final ODataComplexValue<ODataProperty> cValue = + (ODataComplexValue<ODataProperty>) client.getObjectFactory(). + newComplexValue(type == null ? null : type.toString()); + + if (!valuable.isNull()) { + EdmComplexType edmType = null; + if (client instanceof EdmEnabledODataClient && type != null) { + edmType = ((EdmEnabledODataClient) client).getEdm(metadataETag).getComplexType(type); + } + + for (Property property : valuable.asComplex()) { + EdmType edmPropertyType = null; + if (edmType != null) { + final EdmElement edmProp = edmType.getProperty(property.getName()); + if (edmProp != null) { + edmPropertyType = edmProp.getType(); + } + } + + cValue.add(getODataProperty(edmPropertyType, property)); + } + } + + value = cValue; + } else if (valuable.isCollection()) { + value = + client.getObjectFactory().newCollectionValue(type == null ? null : "Collection(" + type.toString() + ")"); + + for (Object _value : valuable.asCollection()) { + final Property fake = new PropertyImpl(); + fake.setValue(valuable.getValueType().getBaseType(), _value); + value.asCollection().add(getODataValue(type, fake, contextURL, metadataETag)); + } + } } return value; @@ -370,14 +880,14 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder @Override public ODataDelta getODataDelta(final ResWrap<Delta> resource) { final URI base = resource.getContextURL() == null - ? resource.getPayload().getBaseURI() - : ContextURLParser.parse(resource.getContextURL()).getServiceRoot(); + ? resource.getPayload().getBaseURI() + : ContextURLParser.parse(resource.getContextURL()).getServiceRoot(); final URI next = resource.getPayload().getNext(); final ODataDelta delta = next == null - ? ((ODataClient) client).getObjectFactory().newDelta() - : ((ODataClient) client).getObjectFactory().newDelta(URIUtils.getURI(base, next.toASCIIString())); + ? ((ODataClient) client).getObjectFactory().newDelta() + : ((ODataClient) client).getObjectFactory().newDelta(URIUtils.getURI(base, next.toASCIIString())); if (resource.getPayload().getCount() != null) { delta.setCount(resource.getPayload().getCount()); @@ -389,7 +899,7 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder for (Entity entityResource : resource.getPayload().getEntities()) { add(delta, getODataEntity( - new ResWrap<Entity>(resource.getContextURL(), resource.getMetadataETag(), entityResource))); + new ResWrap<Entity>(resource.getContextURL(), resource.getMetadataETag(), entityResource))); } for (DeletedEntity deletedEntity : resource.getPayload().getDeletedEntities()) { final ODataDeletedEntityImpl impl = new ODataDeletedEntityImpl(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68b14ffe/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataReaderImpl.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataReaderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataReaderImpl.java index f9a7893..22b1798 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataReaderImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataReaderImpl.java @@ -19,22 +19,143 @@ package org.apache.olingo.client.core.serialization; import java.io.InputStream; +import java.net.URI; +import java.util.Map; +import org.apache.commons.io.IOUtils; +import org.apache.olingo.client.api.CommonODataClient; import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.data.ServiceDocument; +import org.apache.olingo.client.api.domain.ODataEntitySetIterator; +import org.apache.olingo.client.api.edm.xml.Schema; +import org.apache.olingo.client.api.edm.xml.XMLMetadata; import org.apache.olingo.client.api.serialization.ODataReader; +import org.apache.olingo.client.core.edm.EdmClientImpl; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ResWrap; import org.apache.olingo.commons.api.domain.ODataEntity; import org.apache.olingo.commons.api.domain.ODataEntitySet; +import org.apache.olingo.commons.api.domain.ODataError; import org.apache.olingo.commons.api.domain.ODataProperty; +import org.apache.olingo.commons.api.domain.ODataServiceDocument; +import org.apache.olingo.commons.api.domain.ODataValue; +import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.commons.api.serialization.ODataDeserializerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class ODataReaderImpl extends AbstractODataReader implements ODataReader { +public class ODataReaderImpl implements ODataReader { - public ODataReaderImpl(final ODataClient client) { - super(client); + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(ODataReaderImpl.class); + + protected final CommonODataClient<?> client; + + public ODataReaderImpl(final CommonODataClient<?> client) { + this.client = client; + } + + @Override + public Edm readMetadata(final InputStream input) { + return readMetadata(client.getDeserializer(ODataFormat.XML).toMetadata(input).getSchemaByNsOrAlias()); } @Override + public Edm readMetadata(final Map<String, Schema> xmlSchemas) { + return new EdmClientImpl(client.getServiceVersion(), xmlSchemas); + } + + @Override + public ODataServiceDocument readServiceDocument(final InputStream input, final ODataFormat format) + throws ODataDeserializerException { + return client.getBinder().getODataServiceDocument( + client.getDeserializer(format).toServiceDocument(input).getPayload()); + } + + @Override + public ODataError readError(final InputStream inputStream, final ODataFormat format ) + throws ODataDeserializerException { + return client.getDeserializer(format).toError(inputStream); + } + + @Override + public <T> ResWrap<T> read(final InputStream src, final String format, final Class<T> reference) + throws ODataDeserializerException { + ResWrap<T> res; + + try { + if (ODataEntitySetIterator.class.isAssignableFrom(reference)) { + res = new ResWrap<T>( + (URI) null, + null, + reference.cast(new ODataEntitySetIterator<ODataEntitySet, ODataEntity>( + client, src, ODataFormat.fromString(format)))); + } else if (ODataEntitySet.class.isAssignableFrom(reference)) { + final ResWrap<EntitySet> resource = client.getDeserializer(ODataFormat.fromString(format)) + .toEntitySet(src); + res = new ResWrap<T>( + resource.getContextURL(), + resource.getMetadataETag(), + reference.cast(client.getBinder().getODataEntitySet(resource))); + } else if (ODataEntity.class.isAssignableFrom(reference)) { + final ResWrap<Entity> container = client.getDeserializer(ODataFormat.fromString(format)).toEntity(src); + res = new ResWrap<T>( + container.getContextURL(), + container.getMetadataETag(), + reference.cast(client.getBinder().getODataEntity(container))); + } else if (ODataProperty.class.isAssignableFrom(reference)) { + final ResWrap<Property> container = client.getDeserializer(ODataFormat.fromString(format)).toProperty(src); + res = new ResWrap<T>( + container.getContextURL(), + container.getMetadataETag(), + reference.cast(client.getBinder().getODataProperty(container))); + } else if (ODataValue.class.isAssignableFrom(reference)) { + res = new ResWrap<T>( + (URI) null, + null, + reference.cast(client.getObjectFactory().newPrimitiveValueBuilder(). + setType(ODataFormat.fromString(format) == ODataFormat.TEXT_PLAIN + ? EdmPrimitiveTypeKind.String : EdmPrimitiveTypeKind.Stream). + setValue(IOUtils.toString(src)) // TODO: set correct value + .build())); + } else if (XMLMetadata.class.isAssignableFrom(reference)) { + res = new ResWrap<T>( + (URI) null, + null, + reference.cast(readMetadata(src))); + } else if (ODataServiceDocument.class.isAssignableFrom(reference)) { + final ResWrap<ServiceDocument> resource = + client.getDeserializer(ODataFormat.fromString(format)).toServiceDocument(src); + res = new ResWrap<T>( + resource.getContextURL(), + resource.getMetadataETag(), + reference.cast(client.getBinder().getODataServiceDocument(resource.getPayload()))); + } else if (ODataError.class.isAssignableFrom(reference)) { + res = new ResWrap<T>( + (URI) null, + null, + reference.cast(readError(src, ODataFormat.fromString(format)))); + } else { + throw new IllegalArgumentException("Invalid reference type " + reference); + } + } catch (Exception e) { + LOG.warn("Cast error", e); + res = null; + } finally { + if (!ODataEntitySetIterator.class.isAssignableFrom(reference)) { + IOUtils.closeQuietly(src); + } + } + + return res; + } + @Override public ODataEntitySet readEntitySet(final InputStream input, final ODataFormat format) throws ODataDeserializerException { return ((ODataClient) client).getBinder().getODataEntitySet(client.getDeserializer(format).toEntitySet(input));
