| /* |
| * 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; |
| } |
| } |