Java tutorial
/* * 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.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.HashMap; import java.util.List; import java.util.Map; 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.ResWrap; import org.apache.olingo.client.api.data.ServiceDocument; import org.apache.olingo.client.api.data.ServiceDocumentItem; import org.apache.olingo.client.api.domain.ClientAnnotatable; import org.apache.olingo.client.api.domain.ClientAnnotation; import org.apache.olingo.client.api.domain.ClientCollectionValue; import org.apache.olingo.client.api.domain.ClientComplexValue; import org.apache.olingo.client.api.domain.ClientDeletedEntity.Reason; import org.apache.olingo.client.api.domain.ClientDelta; import org.apache.olingo.client.api.domain.ClientDeltaLink; import org.apache.olingo.client.api.domain.ClientEntity; import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.domain.ClientInlineEntity; import org.apache.olingo.client.api.domain.ClientInlineEntitySet; import org.apache.olingo.client.api.domain.ClientLink; import org.apache.olingo.client.api.domain.ClientLinkType; import org.apache.olingo.client.api.domain.ClientLinked; import org.apache.olingo.client.api.domain.ClientOperation; import org.apache.olingo.client.api.domain.ClientProperty; import org.apache.olingo.client.api.domain.ClientServiceDocument; import org.apache.olingo.client.api.domain.ClientValuable; import org.apache.olingo.client.api.domain.ClientValue; import org.apache.olingo.client.api.serialization.ODataBinder; import org.apache.olingo.client.api.serialization.ODataSerializerException; import org.apache.olingo.client.core.domain.ClientAnnotationImpl; import org.apache.olingo.client.core.domain.ClientDeletedEntityImpl; import org.apache.olingo.client.core.domain.ClientDeltaLinkImpl; import org.apache.olingo.client.core.domain.ClientPropertyImpl; 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.ComplexValue; 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; import org.apache.olingo.commons.api.data.Entity; import org.apache.olingo.commons.api.data.EntityCollection; import org.apache.olingo.commons.api.data.Link; import org.apache.olingo.commons.api.data.Linked; import org.apache.olingo.commons.api.data.Operation; import org.apache.olingo.commons.api.data.Property; import org.apache.olingo.commons.api.data.Valuable; import org.apache.olingo.commons.api.data.ValueType; 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.ContentType; import org.apache.olingo.commons.core.edm.EdmTypeInfo; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ODataBinderImpl implements ODataBinder { /** * Logger. */ protected static final Logger LOG = LoggerFactory.getLogger(ODataBinderImpl.class); protected final ODataClient client; public ODataBinderImpl(final ODataClient client) { this.client = client; } @Override public boolean add(final ClientEntity entity, final ClientProperty property) { return entity.getProperties().add(property); } protected boolean add(final ClientEntitySet entitySet, final ClientEntity entity) { return entitySet.getEntities().add(entity); } @Override public ClientServiceDocument getODataServiceDocument(final ServiceDocument resource) { final ClientServiceDocument serviceDocument = new ClientServiceDocument(); 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())); } for (ServiceDocumentItem singleton : resource.getSingletons()) { serviceDocument.getSingletons().put( 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())); } return serviceDocument; } private void updateValuable(final Valuable propertyResource, final ClientValuable odataValuable) { final Object propertyValue = getValue(odataValuable.getValue()); if (odataValuable.hasPrimitiveValue()) { propertyResource.setType(odataValuable.getPrimitiveValue().getTypeName()); propertyResource.setValue( 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(ValueType.COMPLEX, propertyValue); } else if (odataValuable.hasCollectionValue()) { final ClientCollectionValue<ClientValue> collectionValue = odataValuable.getCollectionValue(); propertyResource.setType(collectionValue.getTypeName()); final ClientValue value = 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; } else if (value.isEnum()) { valueType = ValueType.COLLECTION_ENUM; } else if (value.isComplex()) { valueType = ValueType.COLLECTION_COMPLEX; } propertyResource.setValue(valueType, propertyValue); } } private void annotations(final ClientAnnotatable odataAnnotatable, final Annotatable annotatable) { for (ClientAnnotation odataAnnotation : odataAnnotatable.getAnnotations()) { final Annotation annotation = new Annotation(); annotation.setTerm(odataAnnotation.getTerm()); annotation.setType(odataAnnotation.getValue().getTypeName()); updateValuable(annotation, odataAnnotation); annotatable.getAnnotations().add(annotation); } } @Override public EntityCollection getEntitySet(final ClientEntitySet odataEntitySet) { final EntityCollection entitySet = new EntityCollection(); entitySet.setCount(odataEntitySet.getCount()); final URI next = odataEntitySet.getNext(); if (next != null) { entitySet.setNext(next); } for (ClientEntity entity : odataEntitySet.getEntities()) { entitySet.getEntities().add(getEntity(entity)); } entitySet.setDeltaLink(odataEntitySet.getDeltaLink()); annotations(odataEntitySet, entitySet); return entitySet; } protected void links(final ClientLinked odataLinked, final Linked linked) { // ------------------------------------------------------------- // Append navigation links (handling inline entity / entity set as well) // ------------------------------------------------------------- // handle navigation links for (ClientLink link : odataLinked.getNavigationLinks()) { // append link LOG.debug("Append navigation link\n{}", link); linked.getNavigationLinks().add(getLink(link)); } // ------------------------------------------------------------- // ------------------------------------------------------------- // Append association links // ------------------------------------------------------------- for (ClientLink link : odataLinked.getAssociationLinks()) { LOG.debug("Append association link\n{}", link); linked.getAssociationLinks().add(getLink(link)); } // ------------------------------------------------------------- for (Link link : linked.getNavigationLinks()) { final ClientLink odataLink = odataLinked.getNavigationLink(link.getTitle()); if (!(odataLink instanceof ClientInlineEntity) && !(odataLink instanceof ClientInlineEntitySet)) { annotations(odataLink, link); } } } @Override public Entity getEntity(final ClientEntity odataEntity) { final Entity entity = new Entity(); entity.setType(odataEntity.getTypeName() == null ? null : odataEntity.getTypeName().toString()); // ------------------------------------------------------------- // Add edit and self link // ------------------------------------------------------------- final URI odataEditLink = odataEntity.getEditLink(); if (odataEditLink != null) { final Link editLink = new Link(); editLink.setTitle(entity.getType()); editLink.setHref(odataEditLink.toASCIIString()); editLink.setRel(Constants.EDIT_LINK_REL); entity.setEditLink(editLink); } if (odataEntity.isReadOnly()) { final Link selfLink = new Link(); 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 (ClientLink 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 (ClientProperty property : odataEntity.getProperties()) { entity.getProperties().add(getProperty(property)); } entity.setId(odataEntity.getId()); annotations(odataEntity, entity); return entity; } @Override public Link getLink(final ClientLink link) { final Link linkResource = new Link(); 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 ClientInlineEntity) { // append inline entity final ClientEntity inlineEntity = ((ClientInlineEntity) link).getEntity(); LOG.debug("Append in-line entity\n{}", inlineEntity); linkResource.setInlineEntity(getEntity(inlineEntity)); } else if (link instanceof ClientInlineEntitySet) { // append inline entity set final ClientEntitySet InlineEntitySet = ((ClientInlineEntitySet) link).getEntitySet(); LOG.debug("Append in-line entity set\n{}", InlineEntitySet); linkResource.setInlineEntitySet(getEntitySet(InlineEntitySet)); } return linkResource; } @Override public Property getProperty(final ClientProperty property) { final Property propertyResource = new Property(); propertyResource.setName(property.getName()); updateValuable(propertyResource, property); annotations(property, propertyResource); return propertyResource; } protected Object getValue(final ClientValue value) { Object valueResource = null; if (value == null) { return null; } if (value.isEnum()) { valueResource = value.asEnum().getValue(); } else if (value.isPrimitive()) { valueResource = value.asPrimitive().toValue(); } else if (value.isComplex()) { List<Property> complexProperties = new ArrayList<Property>(); for (final ClientProperty propertyValue : value.asComplex()) { complexProperties.add(getProperty(propertyValue)); } final ComplexValue lcValueResource = new ComplexValue(); lcValueResource.getValue().addAll(complexProperties); annotations(value.asComplex(), lcValueResource); links(value.asComplex(), lcValueResource); valueResource = lcValueResource; } else if (value.isCollection()) { final ClientCollectionValue<? extends ClientValue> _value = value.asCollection(); ArrayList<Object> lcValueResource = new ArrayList<Object>(); for (final ClientValue collectionValue : _value) { lcValueResource.add(getValue(collectionValue)); } valueResource = lcValueResource; } return valueResource; } private void odataAnnotations(final Annotatable annotatable, final ClientAnnotatable odataAnnotatable) { for (Annotation annotation : annotatable.getAnnotations()) { FullQualifiedName fqn = null; if (client instanceof EdmEnabledODataClient) { final EdmTerm term = ((EdmEnabledODataClient) client).getCachedEdm() .getTerm(new FullQualifiedName(annotation.getTerm())); if (term != null) { fqn = term.getType().getFullQualifiedName(); } } if (fqn == null && annotation.getType() != null) { final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(annotation.getType()) .build(); if (typeInfo.isPrimitiveType()) { fqn = typeInfo.getPrimitiveTypeKind().getFullQualifiedName(); } } final ClientAnnotation odataAnnotation = new ClientAnnotationImpl(annotation.getTerm(), getODataValue(fqn, annotation, null, null)); odataAnnotatable.getAnnotations().add(odataAnnotation); } } @Override public ClientEntitySet getODataEntitySet(final ResWrap<EntityCollection> resource) { if (LOG.isDebugEnabled()) { final StringWriter writer = new StringWriter(); try { client.getSerializer(ContentType.JSON).write(writer, resource.getPayload()); } catch (final ODataSerializerException e) { LOG.debug("EntitySet -> ODataEntitySet:\n{}", writer.toString()); } 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 ClientEntitySet 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 (Operation op : resource.getPayload().getOperations()) { ClientOperation operation = new ClientOperation(); operation.setTarget(URIUtils.getURI(base, op.getTarget())); operation.setTitle(op.getTitle()); operation.setMetadataAnchor(op.getMetadataAnchor()); entitySet.getOperations().add(operation); } for (Entity entityResource : resource.getPayload().getEntities()) { add(entitySet, getODataEntity( new ResWrap<Entity>(resource.getContextURL(), resource.getMetadataETag(), entityResource))); } if (resource.getPayload().getDeltaLink() != null) { entitySet.setDeltaLink(URIUtils.getURI(base, resource.getPayload().getDeltaLink())); } odataAnnotations(resource.getPayload(), entitySet); return entitySet; } protected void odataNavigationLinks(final EdmType edmType, final Linked linked, final ClientLinked 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 EntityCollection inlineEntitySet = link.getInlineEntitySet(); if (inlineEntity == null && inlineEntitySet == null) { ClientLinkType linkType = null; if (edmType instanceof EdmStructuredType) { final EdmNavigationProperty navProp = ((EdmStructuredType) edmType) .getNavigationProperty(title); if (navProp != null) { linkType = navProp.isCollection() ? ClientLinkType.ENTITY_SET_NAVIGATION : ClientLinkType.ENTITY_NAVIGATION; } } if (linkType == null) { linkType = link.getType() == null ? ClientLinkType.ENTITY_NAVIGATION : ClientLinkType.fromString(link.getRel(), link.getType()); } odataLinked.addLink(linkType == ClientLinkType.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, href == null ? null : URIUtils.getURI(base, href), title, metadataETag)); } } for (ClientLink link : odataLinked.getNavigationLinks()) { if (!(link instanceof ClientInlineEntity) && !(link instanceof ClientInlineEntitySet)) { odataAnnotations(linked.getNavigationLink(link.getName()), link); } } } private ClientInlineEntity createODataInlineEntity(final Entity inlineEntity, final URI uri, final String title, final String metadataETag) { return new ClientInlineEntity(uri, ClientLinkType.ENTITY_NAVIGATION, title, getODataEntity( new ResWrap<Entity>(inlineEntity.getBaseURI() == null ? null : inlineEntity.getBaseURI(), metadataETag, inlineEntity))); } private ClientInlineEntitySet createODataInlineEntitySet(final EntityCollection inlineEntitySet, final URI uri, final String title, final String metadataETag) { return new ClientInlineEntitySet(uri, ClientLinkType.ENTITY_SET_NAVIGATION, title, getODataEntitySet(new ResWrap<EntityCollection>( 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 ClientLink createLinkFromNavigationProperty(final Property property, final String propertyTypeName, final Integer count) { if (property.isCollection()) { EntityCollection inlineEntitySet = new EntityCollection(); for (final Object inlined : property.asCollection()) { Entity inlineEntity = new Entity(); inlineEntity.setType(propertyTypeName); inlineEntity.getProperties().addAll(((ComplexValue) inlined).getValue()); copyAnnotations(inlineEntity, (ComplexValue) inlined); inlineEntitySet.getEntities().add(inlineEntity); } if (count != null) { inlineEntitySet.setCount(count); } return createODataInlineEntitySet(inlineEntitySet, null, property.getName(), null); } else { Entity inlineEntity = new Entity(); inlineEntity.setType(propertyTypeName); inlineEntity.getProperties().addAll(property.asComplex().getValue()); copyAnnotations(inlineEntity, property.asComplex()); return createODataInlineEntity(inlineEntity, null, property.getName(), null); } } private void copyAnnotations(Entity inlineEntity, ComplexValue complex) { for (Annotation annotation : complex.getAnnotations()) { if (annotation.getTerm().equals(Constants.JSON_TYPE.substring(1))) { inlineEntity.setType((String) annotation.asPrimitive()); } else if (annotation.getTerm().equals(Constants.JSON_ID.substring(1))) { inlineEntity.setId(URI.create((String) annotation.asPrimitive())); } else if (annotation.getTerm().equals(Constants.JSON_ETAG.substring(1))) { inlineEntity.setETag((String) annotation.asPrimitive()); } } } private ClientLink createLinkFromEmptyNavigationProperty(final String propertyName, final Integer count) { EntityCollection inlineEntitySet = new EntityCollection(); if (count != null) { inlineEntitySet.setCount(count); } return createODataInlineEntitySet(inlineEntitySet, null, propertyName, null); } @Override public ClientEntity getODataEntity(final ResWrap<Entity> resource) { if (LOG.isDebugEnabled()) { final StringWriter writer = new StringWriter(); try { client.getSerializer(ContentType.JSON).write(writer, resource.getPayload()); } catch (final ODataSerializerException e) { LOG.debug("EntityResource -> ODataEntity:\n{}", writer.toString()); } 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 ClientEntity 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()) { if (link.getRel().startsWith(Constants.NS_MEDIA_READ_LINK_REL)) { entity.addLink(client.getObjectFactory().newMediaReadLink(link.getTitle(), URIUtils.getURI(base, link.getHref()), link.getType(), link.getMediaETag())); } else { entity.addLink(client.getObjectFactory().newMediaEditLink(link.getTitle(), URIUtils.getURI(base, link.getHref()), link.getType(), link.getMediaETag())); } } for (Operation op : resource.getPayload().getOperations()) { ClientOperation operation = new ClientOperation(); operation.setTarget(URIUtils.getURI(base, op.getTarget())); operation.setTitle(op.getTitle()); operation.setMetadataAnchor(op.getMetadataAnchor()); 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()); } Map<String, Integer> countMap = new HashMap<String, Integer>(); for (final Property property : resource.getPayload().getProperties()) { EdmType propertyType = null; if (edmType instanceof EdmEntityType) { EdmElement edmProperty = ((EdmEntityType) edmType).getProperty(property.getName()); if (edmProperty != null) { propertyType = edmProperty.getType(); if (edmProperty instanceof EdmNavigationProperty && !property.isNull()) { final String propertyTypeName = propertyType.getFullQualifiedName() .getFullQualifiedNameAsString(); entity.addLink(createLinkFromNavigationProperty(property, propertyTypeName, countMap.remove(property.getName()))); continue; } } else { int idx = property.getName().indexOf(Constants.JSON_COUNT); if (idx != -1) { String navigationName = property.getName().substring(0, idx); edmProperty = ((EdmEntityType) edmType).getProperty(navigationName); if (edmProperty != null) { if (edmProperty instanceof EdmNavigationProperty) { ClientLink link = entity.getNavigationLink(navigationName); if (link == null) { countMap.put(navigationName, (Integer) property.getValue()); } else { link.asInlineEntitySet().getEntitySet().setCount((Integer) property.getValue()); } } } } } } add(entity, getODataProperty(propertyType, property)); } if (!countMap.isEmpty()) { for (String name : countMap.keySet()) { entity.addLink(createLinkFromEmptyNavigationProperty(name, countMap.get(name))); } } entity.setId(resource.getPayload().getId()); odataAnnotations(resource.getPayload(), entity); return entity; } @Override public ClientProperty getODataProperty(final ResWrap<Property> resource) { final Property payload = resource.getPayload(); final EdmTypeInfo typeInfo = buildTypeInfo(ContextURLParser.parse(resource.getContextURL()), resource.getMetadataETag(), payload.getName(), payload.getType()); final ClientProperty property = new ClientPropertyImpl(payload.getName(), getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), payload, resource.getContextURL(), resource.getMetadataETag())); odataAnnotations(payload, property); for (Operation op : resource.getPayload().getOperations()) { ClientOperation operation = new ClientOperation(); operation.setTarget(op.getTarget()); operation.setTitle(op.getTitle()); operation.setMetadataAnchor(op.getMetadataAnchor()); property.getOperations().add(operation); } return property; } 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 if (isPrimiteveType(typeName)) { // Inheritance is not allowed for primitive types, so we use the type given by the EDM. typeInfo = new EdmTypeInfo.Builder().setTypeExpression(typeName.toString()).build(); } else { typeInfo = new EdmTypeInfo.Builder().setTypeExpression(propertyType).build(); } } return typeInfo; } private boolean isPrimiteveType(final FullQualifiedName typeName) { try { return EdmPrimitiveTypeKind.valueOfFQN(typeName) != null; } catch (IllegalArgumentException e) { return false; } } protected ClientProperty getODataProperty(final EdmType type, final Property resource) { final EdmTypeInfo typeInfo = buildTypeInfo(type == null ? null : type.getFullQualifiedName(), resource.getType()); final ClientProperty property = new ClientPropertyImpl(resource.getName(), getODataValue(typeInfo == null ? null : typeInfo.getFullQualifiedName(), resource, null, null)); odataAnnotations(resource, property); return property; } protected ClientValue getODataValue(final FullQualifiedName type, 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) { final EdmEnumType edmType = ((EdmEnabledODataClient) client).getEdm(metadataETag).getEnumType(type); if (!valuable.isCollection() && valuable.isPrimitive() && edmType != null) { valuable.setValue(ValueType.ENUM, valuable.asPrimitive()); } } ClientValue value = null; if (valuable.isCollection()) { value = client.getObjectFactory() .newCollectionValue(type == null ? null : "Collection(" + type.toString() + ")"); for (Object _value : valuable.asCollection()) { final Property fake = new Property(); fake.setValue(valuable.getValueType().getBaseType(), _value); value.asCollection().add(getODataValue(type, fake, contextURL, metadataETag)); } } else if (valuable.isEnum()) { value = client.getObjectFactory().newEnumValue(type == null ? null : type.toString(), valuable.asEnum().toString()); } else if (valuable.isComplex()) { final ClientComplexValue lcValue = client.getObjectFactory() .newComplexValue(type == null ? null : type.toString()); EdmComplexType edmType = null; if (client instanceof EdmEnabledODataClient && type != null) { edmType = ((EdmEnabledODataClient) client).getEdm(metadataETag).getComplexType(type); } for (Property property : valuable.asComplex().getValue()) { EdmType edmPropertyType = null; if (edmType != null) { final EdmElement edmProp = edmType.getProperty(property.getName()); if (edmProp != null) { edmPropertyType = edmProp.getType(); } } lcValue.add(getODataProperty(edmPropertyType, property)); } odataNavigationLinks(edmType, valuable.asComplex(), lcValue, metadataETag, contextURL); odataAnnotations(valuable.asComplex(), lcValue); value = lcValue; } else { 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(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(type.toString())) .build(); } else if (valuable.isComplex()) { final ClientComplexValue cValue = 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().getValue()) { 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; } } return value; } @Override public ClientDelta getODataDelta(final ResWrap<Delta> resource) { final URI base = resource.getContextURL() == null ? resource.getPayload().getBaseURI() : ContextURLParser.parse(resource.getContextURL()).getServiceRoot(); final URI next = resource.getPayload().getNext(); final ClientDelta delta = next == null ? client.getObjectFactory().newDelta() : client.getObjectFactory().newDelta(URIUtils.getURI(base, next.toASCIIString())); if (resource.getPayload().getCount() != null) { delta.setCount(resource.getPayload().getCount()); } if (resource.getPayload().getDeltaLink() != null) { delta.setDeltaLink(URIUtils.getURI(base, resource.getPayload().getDeltaLink())); } for (Entity entityResource : resource.getPayload().getEntities()) { add(delta, getODataEntity( new ResWrap<Entity>(resource.getContextURL(), resource.getMetadataETag(), entityResource))); } for (DeletedEntity deletedEntity : resource.getPayload().getDeletedEntities()) { final ClientDeletedEntityImpl impl = new ClientDeletedEntityImpl(); impl.setId(URIUtils.getURI(base, deletedEntity.getId())); impl.setReason(Reason.valueOf(deletedEntity.getReason().name())); delta.getDeletedEntities().add(impl); } odataAnnotations(resource.getPayload(), delta); for (DeltaLink link : resource.getPayload().getAddedLinks()) { final ClientDeltaLink impl = new ClientDeltaLinkImpl(); impl.setRelationship(link.getRelationship()); impl.setSource(URIUtils.getURI(base, link.getSource())); impl.setTarget(URIUtils.getURI(base, link.getTarget())); odataAnnotations(link, impl); delta.getAddedLinks().add(impl); } for (DeltaLink link : resource.getPayload().getDeletedLinks()) { final ClientDeltaLink impl = new ClientDeltaLinkImpl(); impl.setRelationship(link.getRelationship()); impl.setSource(URIUtils.getURI(base, link.getSource())); impl.setTarget(URIUtils.getURI(base, link.getTarget())); odataAnnotations(link, impl); delta.getDeletedLinks().add(impl); } return delta; } }