http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlPropertyDeserializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlPropertyDeserializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlPropertyDeserializer.java new file mode 100644 index 0000000..53370bc --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/deserializer/XmlPropertyDeserializer.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.deserializer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmProperty; +import org.apache.olingo.odata2.api.edm.EdmSimpleType; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.DeserializerProperties; +import org.apache.olingo.odata2.core.ep.aggregator.EntityComplexPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.EntityTypeMapping; +import org.apache.olingo.odata2.core.ep.util.FormatXml; + +/** + * XML property consumer. + */ +public class XmlPropertyDeserializer { + + protected static final String TRUE = "true"; + protected static final String FALSE = "false"; + + /** + * Read property of every entry in a payload + * @param reader + * @param property + * @param readProperties + * @return Map<String, Object> + * @throws EntityProviderException + */ + public Map<String, Object> readProperty(final XMLStreamReader reader, final EdmProperty property, + final DeserializerProperties readProperties) throws EntityProviderException { + return readProperty(reader, EntityInfoAggregator.create(property), readProperties); + } + + /** + * Read property of every entry in a payload + * @param reader + * @param propertyInfo + * @param readProperties + * @return Map<String, Object> + * @throws EntityProviderException + */ + public Map<String, Object> readProperty(final XMLStreamReader reader, final EntityPropertyInfo propertyInfo, + final DeserializerProperties readProperties) throws EntityProviderException { + final EntityTypeMapping typeMappings = + EntityTypeMapping.create(readProperties == null ? Collections.<String, Object> emptyMap() : + readProperties.getTypeMappings()); + try { + reader.next(); + + Object value = readStartedElement(reader, propertyInfo.getName(), propertyInfo, typeMappings, readProperties); + + Map<String, Object> result = new HashMap<String, Object>(); + result.put(propertyInfo.getName(), value); + return result; + } catch (XMLStreamException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } catch (EdmException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + + /** + * Deserializes a collection of entities + * @param reader + * @param info + * @param properties + * @return List<Object> + * @throws EntityProviderException + */ + public List<Object> readCollection(XMLStreamReader reader, final EntityPropertyInfo info, + final DeserializerProperties properties) throws EntityProviderException { + final String collectionName = info.getName(); + final EntityTypeMapping typeMappings = EntityTypeMapping.create( + properties == null || !properties.getTypeMappings().containsKey(collectionName) ? + Collections.<String, Object> emptyMap() : + Collections.<String, Object> singletonMap(FormatXml.D_ELEMENT, + properties.getTypeMappings().get(collectionName))); + List<Object> result = new ArrayList<Object>(); + try { + reader.nextTag(); + reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_D_2007_08, collectionName); + reader.nextTag(); + while (reader.isStartElement()) { + result.add(readStartedElement(reader, FormatXml.D_ELEMENT, info, typeMappings, properties)); + reader.nextTag(); + } + reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_D_2007_08, collectionName); + reader.next(); + reader.require(XMLStreamConstants.END_DOCUMENT, null, null); + return result; + } catch (final XMLStreamException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } catch (final EdmException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED + .addContent(e.getClass().getSimpleName()), e); + } + } + + protected Object readStartedElement(XMLStreamReader reader, final String name, //NOSONAR + final EntityPropertyInfo propertyInfo, + final EntityTypeMapping typeMappings, final DeserializerProperties readProperties) + throws EntityProviderException, EdmException { + Object result = null; + + try { + reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_D_2007_08, name); + final String nullAttribute = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_NULL); + + if (!(nullAttribute == null || TRUE.equals(nullAttribute) || FALSE.equals(nullAttribute))) { + throw new EntityProviderException(EntityProviderException.COMMON); + } + + if (TRUE.equals(nullAttribute)) { + if ((readProperties == null || readProperties.isValidatingFacets()) && propertyInfo.isMandatory()) { + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY_VALUE.addContent(name)); + } + reader.nextTag(); + } else if (propertyInfo.isComplex()) { + final String typeAttribute = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_TYPE); + if (typeAttribute != null) { + final String expectedTypeAttributeValue = + propertyInfo.getType().getNamespace() + Edm.DELIMITER + propertyInfo.getType().getName(); + if (!expectedTypeAttributeValue.equals(typeAttribute)) { //NOSONAR + throw new EntityProviderException(EntityProviderException.INVALID_COMPLEX_TYPE.addContent( + expectedTypeAttributeValue).addContent(typeAttribute)); + } + } + + reader.nextTag(); + Map<String, Object> name2Value = new HashMap<String, Object>(); + while (reader.hasNext() && !reader.isEndElement()) { + final String childName = reader.getLocalName(); + final EntityPropertyInfo childProperty = + ((EntityComplexPropertyInfo) propertyInfo).getPropertyInfo(childName); + if (childProperty == null) { //NOSONAR + throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(childName)); + } + final Object value = readStartedElement(reader, childName, childProperty, + typeMappings.getEntityTypeMapping(name), readProperties); + name2Value.put(childName, value); + reader.nextTag(); + } + result = name2Value; + } else { + result = convert(propertyInfo, reader.getElementText(), typeMappings.getMappingClass(name), readProperties); + } + reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_D_2007_08, name); + + return result; + } catch (XMLStreamException e) { + throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + private Object convert(final EntityPropertyInfo property, final String value, final Class<?> typeMapping, + final DeserializerProperties readProperties) throws EdmSimpleTypeException { + final EdmSimpleType type = (EdmSimpleType) property.getType(); + return type.valueOfString(value, EdmLiteralKind.DEFAULT, + readProperties == null || readProperties.isValidatingFacets() ? property.getFacets() : null, + typeMapping == null ? type.getDefaultType() : typeMapping); + } +}
http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomEntryEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomEntryEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomEntryEntitySerializer.java new file mode 100644 index 0000000..5881813 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomEntryEntitySerializer.java @@ -0,0 +1,669 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmCustomizableFeedMappings; +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmFacets; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmMapping; +import org.apache.olingo.odata2.api.edm.EdmMultiplicity; +import org.apache.olingo.odata2.api.edm.EdmNavigationProperty; +import org.apache.olingo.odata2.api.edm.EdmSimpleType; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.edm.EdmTargetPath; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.client.api.ep.EntityCollection; +import org.apache.olingo.odata2.client.api.ep.EntityCollectionSerializerProperties; +import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties; +import org.apache.olingo.odata2.core.commons.ContentType; +import org.apache.olingo.odata2.core.commons.Encoder; +import org.apache.olingo.odata2.core.edm.EdmDateTimeOffset; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.util.FormatXml; + +/** + * Serializes an ATOM entry. + * + */ +public class AtomEntryEntitySerializer { + private final EntitySerializerProperties properties; + private static final String VALUE = "/$value"; + + /** + * + * @param properties + */ + public AtomEntryEntitySerializer(final EntitySerializerProperties properties) { + this.properties = properties == null ? EntitySerializerProperties.serviceRoot(null).build() : properties; + } + + /** + * This serializes the xml payload entry + * @param writer + * @param eia + * @param data + * @param isRootElement + * @param isFeedPart + * @throws EntityProviderException + */ + public void append(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Entity data, + final boolean isRootElement, final boolean isFeedPart) throws EntityProviderException { + + try { + if (properties.getServiceRoot() == null) { + throw new EntityProviderProducerException(EntityProviderException.MANDATORY_WRITE_PROPERTY); + } + writer.writeStartElement(FormatXml.ATOM_ENTRY); + + if (isRootElement) { + writer.writeDefaultNamespace(Edm.NAMESPACE_ATOM_2005); + writer.writeNamespace(Edm.PREFIX_M, Edm.NAMESPACE_M_2007_08); + writer.writeNamespace(Edm.PREFIX_D, Edm.NAMESPACE_D_2007_08); + } + if (!isFeedPart) { + writer.writeAttribute(Edm.PREFIX_XML, Edm.NAMESPACE_XML_1998, FormatXml.XML_BASE, properties.getServiceRoot() + .toASCIIString()); + } + + String selfLink = null; + if (properties.isIncludeMetadata()) { + // write all atom infos (mandatory and optional) + selfLink = createSelfLink(eia, data.getProperties(), null, properties.isKeyAutoGenerated(), false); + appendAtomMandatoryParts(writer, eia, data.getProperties()); + appendAtomOptionalParts(writer, eia, data.getProperties()); + appendAtomEditLink(writer, eia, selfLink); + if (eia.getEntityType().hasStream()) { + appendAtomContentLink(writer, eia, data.getProperties(), selfLink); + } + } + + appendNavigationLinks(writer, eia, data); + appendCustomProperties(writer, eia, data.getProperties()); + + if (eia.getEntityType().hasStream()) { + if (properties.isIncludeMetadata()) { + appendAtomContentPart(writer, eia, data.getProperties(), selfLink); + } + appendProperties(writer, eia, data.getProperties()); + } else { + writer.writeStartElement(FormatXml.ATOM_CONTENT); + writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_XML.toString()); + appendProperties(writer, eia, data.getProperties()); + writer.writeEndElement(); + } + writer.writeEndElement(); + writer.flush(); + + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } catch (URISyntaxException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + @SuppressWarnings("unchecked") + private void appendNavigationLinks(final XMLStreamWriter writer, //NOSONAR + final EntityInfoAggregator eia, + final Entity data) + throws EntityProviderException, EdmException, URISyntaxException, XMLStreamException { + + for (Entry<String, Object> entry : data.getNavigations().entrySet()) { + final EntityInfoAggregator targetEntityInfo = EntityInfoAggregator.create( + eia.getEntitySet().getRelatedEntitySet( + (EdmNavigationProperty) eia.getEntityType().getProperty(entry.getKey()))); + final boolean isFeed = + (eia.getNavigationPropertyInfo(entry.getKey()).getMultiplicity() == EdmMultiplicity.MANY); + if (entry.getValue() == null) { + throw new EntityProviderProducerException(EntityProviderProducerException.NULL_VALUE); + } else if (entry.getValue() instanceof Map) { + Map<String, Object> navigationKeyMap = (Map<String, Object>) entry.getValue(); + if (navigationKeyMap != null && !navigationKeyMap.isEmpty()) { + appendAtomNavigationLink(writer, createSelfLink(targetEntityInfo, navigationKeyMap, null, + properties.isKeyAutoGenerated(), false), entry.getKey(), isFeed); + writer.writeEndElement(); + } + } else if (entry.getValue() instanceof Entity) { + Entity navigationEntity = (Entity) entry.getValue(); + Map<String, Object> navigationKeyMap = navigationEntity.getProperties(); + if (navigationKeyMap != null && !navigationKeyMap.isEmpty()) { + String navigationPropertyName = entry.getKey(); + String selfLink = createSelfLink(eia, data.getProperties(), navigationPropertyName, + properties.isKeyAutoGenerated(), false); + appendNavigationLink(writer, selfLink, navigationPropertyName); + + writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_ATOM_XML_ENTRY.toString()); + appendInlineEntry(writer, navigationPropertyName, eia, data); + writer.writeEndElement(); + + } + } else if (entry.getValue() instanceof EntityCollection) { + String navigationPropertyName = entry.getKey(); + String selfLink = createSelfLink(eia, data.getProperties(), navigationPropertyName, + properties.isKeyAutoGenerated(), false); + if (!((EntityCollection) entry.getValue()).getEntities().isEmpty()) { + appendNavigationLink(writer, selfLink, navigationPropertyName); + + writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_ATOM_XML_FEED.toString()); + appendInlineFeed(writer, navigationPropertyName, eia, data); + writer.writeEndElement(); + } + + } else{ + throw new EntityProviderProducerException(EntityProviderProducerException.INCORRECT_NAVIGATION_TYPE); + + } + } + } + + private void appendNavigationLink(XMLStreamWriter writer, String selfLink, String navigationPropertyName) + throws XMLStreamException { + + writer.writeStartElement(FormatXml.ATOM_LINK); + writer.writeAttribute(FormatXml.ATOM_HREF, selfLink); + writer.writeAttribute(FormatXml.ATOM_REL, Edm.NAMESPACE_REL_2007_08 + navigationPropertyName); + writer.writeAttribute(FormatXml.ATOM_TITLE, navigationPropertyName); + } + + private void appendCustomProperties(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data) throws EntityProviderException { + List<String> noneSyndicationTargetPaths = eia.getNoneSyndicationTargetPathNames(); + for (String tpName : noneSyndicationTargetPaths) { + EntityPropertyInfo info = eia.getTargetPathInfo(tpName); + final String name = info.getName(); + XmlPropertyEntitySerializer aps = new XmlPropertyEntitySerializer(properties); + aps.appendCustomProperty(writer, name, info, data.get(name)); + } + } + + private void appendAtomNavigationLink(final XMLStreamWriter writer, final String target, + final String navigationPropertyName, final boolean isFeed) + throws EntityProviderException, EdmException, URISyntaxException { //NOSONAR + try { + writer.writeStartElement(FormatXml.ATOM_LINK); + writer.writeAttribute(FormatXml.ATOM_HREF, target); + writer.writeAttribute(FormatXml.ATOM_REL, Edm.NAMESPACE_REL_2007_08 + navigationPropertyName); + writer.writeAttribute(FormatXml.ATOM_TITLE, navigationPropertyName); + if (isFeed) { + writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_ATOM_XML_FEED.toString()); + } else { + writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_ATOM_XML_ENTRY.toString()); + } + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + private void appendInlineFeed(final XMLStreamWriter writer, final String navigationPropertyName, + final EntityInfoAggregator eia, final Entity data) + throws EntityProviderException, XMLStreamException, EdmException { + + if (eia.getNavigationPropertyNames().contains(navigationPropertyName) && + data != null && data.getNavigations().containsKey(navigationPropertyName)) { + + EdmNavigationProperty navProp = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName); + + if (navProp == null) { + throw new EntityProviderProducerException(EntityProviderException.EXPANDNOTSUPPORTED); + } + EntityCollection inlineData; + inlineData = (EntityCollection) data.getNavigation(navigationPropertyName); + if (inlineData == null) { + inlineData = new EntityCollection(); + } + + if (inlineData.getEntities().isEmpty()) { + return; + } + writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); + + EntityCollectionSerializerProperties inlineProperties = inlineData.getCollectionProperties() == null + ? EntityCollectionSerializerProperties.serviceRoot(data.getWriteProperties().getServiceRoot()).build() + : inlineData.getCollectionProperties(); + EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navProp); + AtomFeedSerializer inlineFeedProducer = new AtomFeedSerializer(inlineProperties); + inlineData.setCollectionProperties(inlineProperties); + EntityInfoAggregator inlineEia = + EntityInfoAggregator.create(inlineEntitySet, null); + inlineFeedProducer.append(writer, inlineEia, inlineData, true); + + writer.writeEndElement(); + + } + } + + private void appendInlineEntry(final XMLStreamWriter writer, final String navigationPropertyName, + final EntityInfoAggregator eia, final Entity data) throws EntityProviderException, + XMLStreamException, EdmException { + + if (data.getNavigations() != null && data.getNavigations().containsKey(navigationPropertyName)) { + + EdmNavigationProperty navProp = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName); + + Entity inlineData = (Entity) data.getNavigation(navigationPropertyName); + + if ((inlineData == null) || inlineData.getProperties().size() == 0) { + return; + } + + writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE); + if (inlineData != null && !(inlineData.getProperties().isEmpty())) { + inlineData.setWriteProperties(inlineData.getWriteProperties() == null ? data.getWriteProperties() : inlineData + .getWriteProperties()); + EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navProp); + AtomEntryEntitySerializer inlineProducer = new AtomEntryEntitySerializer(inlineData.getWriteProperties()); + EntityInfoAggregator inlineEia = + EntityInfoAggregator.create(inlineEntitySet, null); + inlineProducer.append(writer, inlineEia, inlineData, false, false); + } + + writer.writeEndElement(); + } + } + + + private void appendAtomEditLink(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final String selfLink) throws EntityProviderException { + try { + writer.writeStartElement(FormatXml.ATOM_LINK); + writer.writeAttribute(FormatXml.ATOM_HREF, selfLink); + writer.writeAttribute(FormatXml.ATOM_REL, Edm.LINK_REL_EDIT); + writer.writeAttribute(FormatXml.ATOM_TITLE, eia.getEntityType().getName()); + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + private void appendAtomContentLink(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data, final String selfLink) throws EntityProviderException, EdmException { + try { + String mediaResourceMimeType = null; + EdmMapping entityTypeMapping = eia.getEntityType().getMapping(); + if (entityTypeMapping != null) { + String mediaResourceMimeTypeKey = entityTypeMapping.getMediaResourceMimeTypeKey(); + if (mediaResourceMimeTypeKey != null) { + mediaResourceMimeType = (String) data.get(mediaResourceMimeTypeKey); + } + } + if (mediaResourceMimeType == null) { + mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString(); + } + + writer.writeStartElement(FormatXml.ATOM_LINK); + writer.writeAttribute(FormatXml.ATOM_HREF, selfLink + VALUE); + writer.writeAttribute(FormatXml.ATOM_REL, Edm.LINK_REL_EDIT_MEDIA); + writer.writeAttribute(FormatXml.ATOM_TYPE, mediaResourceMimeType); + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + private void appendAtomContentPart(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data, final String selfLink) throws EntityProviderException, EdmException { + try { + + EdmMapping entityTypeMapping = eia.getEntityType().getMapping(); + String self = null; + String mediaResourceMimeType = null; + + if (entityTypeMapping != null) { + String mediaResourceSourceKey = entityTypeMapping.getMediaResourceSourceKey(); + if (mediaResourceSourceKey != null) { + self = (String) data.get(mediaResourceSourceKey); + } + if (self == null) { + self = selfLink + VALUE; + } + String mediaResourceMimeTypeKey = entityTypeMapping.getMediaResourceMimeTypeKey(); + if (mediaResourceMimeTypeKey != null) { + mediaResourceMimeType = (String) data.get(mediaResourceMimeTypeKey); + } + if (mediaResourceMimeType == null) { + mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString(); + } + } else { + self = selfLink + VALUE; + mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString(); + } + + writer.writeStartElement(FormatXml.ATOM_CONTENT); + writer.writeAttribute(FormatXml.ATOM_TYPE, mediaResourceMimeType); + writer.writeAttribute(FormatXml.ATOM_SRC, self); + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + private void appendAtomMandatoryParts(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data) throws EntityProviderException { + try { + writer.writeStartElement(FormatXml.ATOM_ID); + String idlocation = properties.getServiceRoot().toASCIIString() + createSelfLink( + eia, data, null, properties.isKeyAutoGenerated(), true);; + writer.writeCharacters(idlocation); + writer.writeEndElement(); + + writer.writeStartElement(FormatXml.ATOM_TITLE); + writer.writeAttribute(FormatXml.ATOM_TYPE, FormatXml.ATOM_TEXT); + EntityPropertyInfo titleInfo = eia.getTargetPathInfo(EdmTargetPath.SYNDICATION_TITLE); + if (titleInfo != null) { + EdmSimpleType st = (EdmSimpleType) titleInfo.getType(); + Object object = data.get(titleInfo.getName()); + String title = null; + try { //NOSONAR + title = st.valueToString(object, EdmLiteralKind.DEFAULT, titleInfo.getFacets()); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderProducerException(EdmSimpleTypeException.getMessageReference( + e.getMessageReference()).updateContent(e.getMessageReference().getContent(), titleInfo.getName()), e); + } + if (title != null) { + writer.writeCharacters(title); + } + } else { + writer.writeCharacters(eia.getEntitySetName()); + } + writer.writeEndElement(); + + writer.writeStartElement(FormatXml.ATOM_UPDATED); + + writer.writeCharacters(getUpdatedString(eia, data)); + + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + String getUpdatedString(final EntityInfoAggregator eia, final Map<String, Object> data) + throws EdmSimpleTypeException, EntityProviderProducerException { + Object updateDate = null; + EdmFacets updateFacets = null; + EntityPropertyInfo updatedInfo = eia.getTargetPathInfo(EdmTargetPath.SYNDICATION_UPDATED); + if (updatedInfo != null) { + updateDate = data.get(updatedInfo.getName()); + if (updateDate != null) { + updateFacets = updatedInfo.getFacets(); + } + } + if (updateDate == null) { + updateDate = new Date(); + } + try { + return EdmDateTimeOffset.getInstance().valueToString(updateDate, EdmLiteralKind.DEFAULT, updateFacets); + } catch (final EdmSimpleTypeException e) { + throw new EntityProviderProducerException( + EdmSimpleTypeException.getMessageReference(e.getMessageReference()). + updateContent(e.getMessageReference().getContent(), + updatedInfo == null ? null : updatedInfo.getName()), e); + } + } + + private String getTargetPathValue(final EntityInfoAggregator eia, final String targetPath, + final Map<String, Object> data) throws EntityProviderException { + EntityPropertyInfo info = null; + try { + info = eia.getTargetPathInfo(targetPath); + if (info != null) { + EdmSimpleType type = (EdmSimpleType) info.getType(); + Object value = data.get(info.getName()); + return type.valueToString(value, EdmLiteralKind.DEFAULT, info.getFacets()); + } + return null; + } catch (final EdmSimpleTypeException e) { + throw new EntityProviderProducerException( + EdmSimpleTypeException.getMessageReference(e.getMessageReference()). + updateContent(e.getMessageReference().getContent(), info.getName()), e); + } + } + + private void appendAtomOptionalParts(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data) throws EntityProviderException { + try { + String authorEmail = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_AUTHOREMAIL, data); + String authorName = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_AUTHORNAME, data); + String authorUri = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_AUTHORURI, data); + if (authorEmail != null || authorName != null || authorUri != null) { + writer.writeStartElement(FormatXml.ATOM_AUTHOR); + appendAtomOptionalPart(writer, FormatXml.ATOM_AUTHOR_NAME, authorName, false); + appendAtomOptionalPart(writer, FormatXml.ATOM_AUTHOR_EMAIL, authorEmail, false); + appendAtomOptionalPart(writer, FormatXml.ATOM_AUTHOR_URI, authorUri, false); + writer.writeEndElement(); + } + + String summary = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_SUMMARY, data); + appendAtomOptionalPart(writer, FormatXml.ATOM_SUMMARY, summary, true); + + String contributorName = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_CONTRIBUTORNAME, data); + String contributorEmail = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_CONTRIBUTOREMAIL, data); + String contributorUri = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_CONTRIBUTORURI, data); + if (contributorEmail != null || contributorName != null || contributorUri != null) { + writer.writeStartElement(FormatXml.ATOM_CONTRIBUTOR); + appendAtomOptionalPart(writer, FormatXml.ATOM_CONTRIBUTOR_NAME, contributorName, false); + appendAtomOptionalPart(writer, FormatXml.ATOM_CONTRIBUTOR_EMAIL, contributorEmail, false); + appendAtomOptionalPart(writer, FormatXml.ATOM_CONTRIBUTOR_URI, contributorUri, false); + writer.writeEndElement(); + } + + String rights = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_RIGHTS, data); + appendAtomOptionalPart(writer, FormatXml.ATOM_RIGHTS, rights, true); + String published = getTargetPathValue(eia, EdmTargetPath.SYNDICATION_PUBLISHED, data); + appendAtomOptionalPart(writer, FormatXml.ATOM_PUBLISHED, published, false); + + String term = eia.getEntityType().getNamespace() + Edm.DELIMITER + eia.getEntityType().getName(); + writer.writeStartElement(FormatXml.ATOM_CATEGORY); + writer.writeAttribute(FormatXml.ATOM_CATEGORY_TERM, term); + writer.writeAttribute(FormatXml.ATOM_CATEGORY_SCHEME, Edm.NAMESPACE_SCHEME_2007_08); + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + private void appendAtomOptionalPart(final XMLStreamWriter writer, final String name, final String value, + final boolean writeType) throws EntityProviderException { + try { + if (value != null) { + writer.writeStartElement(name); + if (writeType) { + writer.writeAttribute(FormatXml.ATOM_TYPE, FormatXml.ATOM_TEXT); + } + writer.writeCharacters(value); + writer.writeEndElement(); + } + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + static String createSelfLink(final EntityInfoAggregator eia, final Map<String, Object> data, final String extension, + boolean isKeyAutoGenerated, boolean isIdTag) + throws EntityProviderException { + StringBuilder sb = new StringBuilder(); + if (!eia.isDefaultEntityContainer()) { + sb.append(Encoder.encode(eia.getEntityContainerName())).append(Edm.DELIMITER); + } + sb.append(Encoder.encode(eia.getEntitySetName())); + + String keyValue = createEntryKey(eia, data, isKeyAutoGenerated, isIdTag); + if (isIdTag && isKeyAutoGenerated && "".equals(keyValue) && keyValue.length() == 0) { + sb.append(extension == null ? "" : ("/" + extension)); + } else { + sb.append("(").append(keyValue).append(")"). + append(extension == null ? "" : ("/" + extension)); + } + return sb.toString(); + } + + private static String createEntryKey(final EntityInfoAggregator entityInfo, final Map<String, Object> data, + boolean isKeyAutoGenerated, boolean isIdTag) + throws EntityProviderException { + final List<EntityPropertyInfo> keyPropertyInfos = entityInfo.getKeyPropertyInfos(); + + StringBuilder keys = new StringBuilder(); + for (final EntityPropertyInfo keyPropertyInfo : keyPropertyInfos) { + if (keys.length() > 0) { + keys.append(','); + } + + final String name = keyPropertyInfo.getName(); + if (keyPropertyInfos.size() > 1) { + keys.append(Encoder.encode(name)).append('='); + } + + final EdmSimpleType type = (EdmSimpleType) keyPropertyInfo.getType(); + try { + String keyValue = null; + if (isKeyAutoGenerated && data.get(name) == null) { + if (isIdTag) { + keyValue = ""; + } else { + Object value = fetchDefaultValue(type.getDefaultType()); + keyValue = Encoder.encode(type.valueToString(value, EdmLiteralKind.URI, + keyPropertyInfo.getFacets())); + } + } else { + keyValue = Encoder.encode(type.valueToString(data.get(name), EdmLiteralKind.URI, + keyPropertyInfo.getFacets())); + } + keys.append(keyValue); + } catch (final EdmSimpleTypeException e) { + throw new EntityProviderProducerException( + EdmSimpleTypeException.getMessageReference(e.getMessageReference()). + updateContent(e.getMessageReference().getContent(), name), e); + } + } + + return keys.toString(); + } + + private static Object fetchDefaultValue(Class<?> edmType) throws EntityProviderProducerException { + if (edmType == boolean.class || edmType == Boolean.class) { + return false; + } else if (edmType == String.class) { + return "A"; + } else if (edmType == byte.class || edmType == Byte.class) { + return new Byte(Byte.parseByte("0")); + } else if (edmType == char.class) { + return new Character('A'); + } else if (edmType == short.class || edmType == Short.class) { + return new Short(Short.parseShort("0")); + } else if (edmType == int.class || edmType == Integer.class) { + return new Integer(0); + } else if (edmType == long.class || edmType == Long.class) { + return new Long(0L); + } else if (edmType == float.class || edmType == Float.class) { + return new Float(0.0f); + } else if (edmType == double.class || edmType == Double.class) { + return new Double(0.0d); + } else if (edmType == BigDecimal.class) { + return BigDecimal.valueOf(0.0); + } else if (edmType == UUID.class) { + return UUID.fromString("0"); + } else if (edmType == Timestamp.class) { + return new Timestamp(Calendar.getInstance().getTimeInMillis()); + } else if (edmType == Calendar.class) { + return Calendar.getInstance(); + } else if (edmType == byte[].class || edmType == Byte[].class) { + return new byte[0]; + } else if (edmType == Date.class) { + return new Date(); + } else if (edmType == BigInteger.class) { + return new BigInteger("0"); + } else if (edmType == Time.class) { + return new Time(0L); + } else { + throw new EntityProviderProducerException(EdmSimpleTypeException.VALUE_TYPE_NOT_SUPPORTED); + } + } + + private void appendProperties(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data) throws EntityProviderException { + try { + { + if (!data.isEmpty() && data.size() > 0) { + writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_PROPERTIES); + for (String propertyName : eia.getPropertyNames()) { + if (data.containsKey(propertyName)) { + appendPropertyNameValue(writer, eia, data, propertyName); + } + } + writer.writeEndElement(); + } + } + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + /** + * @param writer + * @param eia + * @param data + * @param propertyName + * @throws EntityProviderException + */ + private void appendPropertyNameValue(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final Map<String, Object> data, String propertyName) throws EntityProviderException { + EntityPropertyInfo propertyInfo = eia.getPropertyInfo(propertyName); + if (isNotMappedViaCustomMapping(propertyInfo)) { + Object value = data.get(propertyName); + XmlPropertyEntitySerializer aps = new XmlPropertyEntitySerializer(properties); + aps.append(writer, propertyInfo.getName(), propertyInfo, value); + } + } + + private boolean isNotMappedViaCustomMapping(final EntityPropertyInfo propertyInfo) { + EdmCustomizableFeedMappings customMapping = propertyInfo.getCustomMapping(); + if (customMapping != null && customMapping.isFcKeepInContent() != null) { + return customMapping.isFcKeepInContent(); + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomFeedSerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomFeedSerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomFeedSerializer.java new file mode 100644 index 0000000..8e4e615 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/AtomFeedSerializer.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.net.URI; +import java.util.Date; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmFacets; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.client.api.ep.EntityCollection; +import org.apache.olingo.odata2.client.api.ep.EntityCollectionSerializerProperties; +import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties; +import org.apache.olingo.odata2.core.commons.Encoder; +import org.apache.olingo.odata2.core.edm.EdmDateTimeOffset; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.util.FormatXml; + +/** + * Serializes an ATOM feed. + * + */ +public class AtomFeedSerializer { + + private final EntityCollectionSerializerProperties properties; + + /** + * + * @param properties + */ + public AtomFeedSerializer(final EntityCollectionSerializerProperties properties) { + this.properties = properties == null ? + EntityCollectionSerializerProperties.serviceRoot(null).build() : + properties; + } + + /** + * This serializes the xml payload feed + * @param writer + * @param eia + * @param data + * @param isInline + * @throws EntityProviderException + */ + public void append(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final EntityCollection data, final boolean isInline) throws EntityProviderException { + try { + if (properties.getServiceRoot() == null) { + throw new EntityProviderProducerException(EntityProviderException.MANDATORY_WRITE_PROPERTY); + } + + writer.writeStartElement(FormatXml.ATOM_FEED); + if (!isInline) { + writer.writeDefaultNamespace(Edm.NAMESPACE_ATOM_2005); + writer.writeNamespace(Edm.PREFIX_M, Edm.NAMESPACE_M_2007_08); + writer.writeNamespace(Edm.PREFIX_D, Edm.NAMESPACE_D_2007_08); + + } + writer.writeAttribute(Edm.PREFIX_XML, Edm.NAMESPACE_XML_1998, FormatXml.XML_BASE, properties.getServiceRoot() + .toASCIIString()); + + // write all atom infos (mandatory and optional) + appendAtomMandatoryParts(writer, eia); + appendAtomSelfLink(writer, eia); + + + appendEntries(writer, eia, data); + + + + + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + private void appendEntries(final XMLStreamWriter writer, final EntityInfoAggregator eia, + final EntityCollection data) throws EntityProviderException { + AtomEntryEntitySerializer entryProvider; + for (Entity singleEntryData : data.getEntities()) { + entryProvider = singleEntryData.getWriteProperties() == null? data.getGlobalEntityProperties() == null ? + new AtomEntryEntitySerializer + (EntitySerializerProperties.serviceRoot(data.getCollectionProperties().getServiceRoot()).build()) : + new AtomEntryEntitySerializer(data.getGlobalEntityProperties()): + new AtomEntryEntitySerializer(singleEntryData.getWriteProperties()); + entryProvider.append(writer, eia, singleEntryData, false, true); + } + } + + + private void appendAtomSelfLink(final XMLStreamWriter writer, final EntityInfoAggregator eia) + throws EntityProviderException { + + URI self = properties.getSelfLink(); + String selfLink = ""; + if (self == null) { + selfLink = createSelfLink(eia); + } else { + selfLink = self.toASCIIString(); + } + try { + writer.writeStartElement(FormatXml.ATOM_LINK); + writer.writeAttribute(FormatXml.ATOM_HREF, selfLink); + writer.writeAttribute(FormatXml.ATOM_REL, Edm.LINK_REL_SELF); + writer.writeAttribute(FormatXml.ATOM_TITLE, eia.getEntitySetName()); + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } + } + + private String createSelfLink(final EntityInfoAggregator eia) { + StringBuilder sb = new StringBuilder(); + if (!eia.isDefaultEntityContainer()) { + final String entityContainerName = Encoder.encode(eia.getEntityContainerName()); + sb.append(entityContainerName).append(Edm.DELIMITER); + } + final String entitySetName = Encoder.encode(eia.getEntitySetName()); + sb.append(entitySetName); + return sb.toString(); + } + + private void appendAtomMandatoryParts(final XMLStreamWriter writer, final EntityInfoAggregator eia) + throws EntityProviderException { + try { + writer.writeStartElement(FormatXml.ATOM_ID); + writer.writeCharacters(createAtomId(eia)); + writer.writeEndElement(); + + writer.writeStartElement(FormatXml.ATOM_TITLE); + writer.writeAttribute(FormatXml.M_TYPE, FormatXml.ATOM_TEXT); + writer.writeCharacters(eia.getEntitySetName()); + writer.writeEndElement(); + + writer.writeStartElement(FormatXml.ATOM_UPDATED); + + Object updateDate = null; + EdmFacets updateFacets = null; + updateDate = new Date(); + writer.writeCharacters(EdmDateTimeOffset.getInstance().valueToString(updateDate, EdmLiteralKind.DEFAULT, + updateFacets)); + writer.writeEndElement(); + + writer.writeStartElement(FormatXml.ATOM_AUTHOR); + writer.writeStartElement(FormatXml.ATOM_AUTHOR_NAME); + writer.writeEndElement(); + writer.writeEndElement(); + + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + private String createAtomId(final EntityInfoAggregator eia) throws EntityProviderException { + return properties.getServiceRoot() + createSelfLink(eia); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonEntryEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonEntryEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonEntryEntitySerializer.java new file mode 100644 index 0000000..b727538 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonEntryEntitySerializer.java @@ -0,0 +1,309 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmEntitySet; +import org.apache.olingo.odata2.api.edm.EdmEntityType; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmMapping; +import org.apache.olingo.odata2.api.edm.EdmMultiplicity; +import org.apache.olingo.odata2.api.edm.EdmNavigationProperty; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.client.api.ep.EntityCollection; +import org.apache.olingo.odata2.client.api.ep.EntityCollectionSerializerProperties; +import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties; +import org.apache.olingo.odata2.core.commons.ContentType; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.util.FormatJson; +import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter; + +/** + * Producer for writing an entity in JSON, also usable for function imports + * returning a single instance of an entity type. + * + */ +public class JsonEntryEntitySerializer { + + private final EntitySerializerProperties properties; + private String location; + private String idlocation; + private JsonStreamWriter jsonStreamWriter; + private static final String VALUE = "/$value"; + + /** + * + * @param properties + * @throws EntityProviderException + */ + public JsonEntryEntitySerializer(final EntitySerializerProperties properties) { + this.properties = properties == null ? EntitySerializerProperties.serviceRoot(null).build() : properties; + } + + /** + * This serializes the json payload entry + * @param writer + * @param entityInfo + * @param data + * @param isRootElement + * @throws EntityProviderException + */ + public void append(final Writer writer, final EntityInfoAggregator entityInfo, final Entity data) + throws EntityProviderException { + if (data == null) { + throw new EntityProviderException(EntityProviderException.NULL_VALUE); + } + final EdmEntityType type = entityInfo.getEntityType(); + + try { + jsonStreamWriter = new JsonStreamWriter(writer); + + jsonStreamWriter.beginObject(); + + boolean containsMetadata = false; + if (properties.isIncludeMetadata()) { + writeMetadata(entityInfo, data.getProperties(), type); + containsMetadata = true; + } + writeProperties(entityInfo, data.getProperties(), type, containsMetadata); + + writeNavigationProperties(writer, entityInfo, data.getNavigations(), type); + jsonStreamWriter.endObject(); + + writer.flush(); + + } catch (final IOException e) { + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } catch (final EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + private void writeNavigationProperties(final Writer writer, final EntityInfoAggregator entityInfo, + final Map<String, Object> data, + final EdmEntityType type) throws EdmException, EntityProviderException, IOException { + for (final String navigationPropertyName : type.getNavigationPropertyNames()) { + if (data.containsKey(navigationPropertyName)) { + if (data.get(navigationPropertyName) == null) { + throw new EntityProviderException(EntityProviderException.NULL_VALUE); + } + if (data.get(navigationPropertyName) instanceof Entity || + data.get(navigationPropertyName) instanceof EntityCollection) { + jsonStreamWriter.separator(); + jsonStreamWriter.name(navigationPropertyName); + writeExpandedNavigationProperty(writer, entityInfo, data, type, navigationPropertyName); + } else if (data.get(navigationPropertyName) instanceof Map<?,?>){ + writeNavigationLinks(entityInfo, data); + } else { + throw new EntityProviderException(EntityProviderException.INCORRECT_NAVIGATION_TYPE); + } + } + } + } + + private void writeExpandedNavigationProperty(final Writer writer, final EntityInfoAggregator entityInfo, + final Map<String, Object> data, + final EdmEntityType type, final String navigationPropertyName) throws EdmException, EntityProviderException, + IOException { + + final EdmNavigationProperty navigationProperty = (EdmNavigationProperty) type.getProperty(navigationPropertyName); + final boolean isFeed = navigationProperty.getMultiplicity() == EdmMultiplicity.MANY; + final EdmEntitySet entitySet = entityInfo.getEntitySet(); + final EdmEntitySet inlineEntitySet = entitySet.getRelatedEntitySet(navigationProperty); + + if (isFeed) { + EntityCollection inlineData = (EntityCollection) data.get(navigationProperty.getName()); + + if(inlineData == null){ + throw new EntityProviderException(EntityProviderException.NULL_VALUE); + } + final EntityCollectionSerializerProperties inlineProperties = inlineData.getCollectionProperties() == null ? + EntityCollectionSerializerProperties. + serviceRoot(properties.getServiceRoot()).build() : + inlineData.getCollectionProperties(); + JsonFeedEntitySerializer jsonFeedEntityProducer = new JsonFeedEntitySerializer(inlineProperties); + final EntityInfoAggregator inlineEntityInfo = + EntityInfoAggregator.create(inlineEntitySet, null); + jsonFeedEntityProducer.appendAsArray(writer, inlineEntityInfo, inlineData); + } else { + Entity inlineData = (Entity) data.get(navigationProperty.getName()); + //This statement is used for the client use case. Flag should never be set on server side + if(inlineData == null){ + throw new EntityProviderException(EntityProviderException.NULL_VALUE); + } + if (inlineData != null && inlineData.getProperties() != null && + !inlineData.getProperties().isEmpty()) { + final EntitySerializerProperties inlineProperties = inlineData.getWriteProperties() == null ? + EntitySerializerProperties. + serviceRoot(properties.getServiceRoot()).build() : inlineData.getWriteProperties(); + final EntityInfoAggregator inlineEntityInfo = + EntityInfoAggregator.create(inlineEntitySet, null); + new JsonEntryEntitySerializer(inlineProperties).append(writer, inlineEntityInfo, inlineData); + } else { + jsonStreamWriter.beginObject(); + jsonStreamWriter.endObject(); + } + } + } + + private void writeProperties(final EntityInfoAggregator entityInfo, final Map<String, Object> data, + final EdmEntityType type, boolean containsMetadata) throws EdmException, EntityProviderException, IOException { + // if the payload contains metadata we must not omit the first comm as it separates the _metadata object form the + // properties + boolean omitComma = !containsMetadata; + + List<String> propertyNames = type.getPropertyNames(); + for (final String propertyName : propertyNames) { + if (data.containsKey(propertyName)) { + omitComma = appendPropertyNameValue(entityInfo, data, omitComma, propertyName); + } + } + } + + /** + * @param entityInfo + * @param data + * @param omitComma + * @param propertyName + * @return + * @throws IOException + * @throws EdmException + * @throws EntityProviderException + */ + private boolean appendPropertyNameValue(final EntityInfoAggregator entityInfo, final Map<String, Object> data, + boolean omitComma, String propertyName) throws IOException, EdmException, EntityProviderException { + if (omitComma) { + omitComma = false; //NOSONAR + } else { + jsonStreamWriter.separator(); + } + jsonStreamWriter.name(propertyName); + + JsonPropertyEntitySerializer.appendPropertyValue(jsonStreamWriter, + entityInfo.getPropertyInfo(propertyName), + data.get(propertyName), + properties.isValidatingFacets()); + return omitComma; + } + + private void writeMetadata(final EntityInfoAggregator entityInfo, final Map<String, Object> data, + final EdmEntityType type) throws IOException, EntityProviderException, EdmException { + if (properties.getServiceRoot() == null) { + location = ""; + idlocation = ""; + } else { + location = properties.getServiceRoot().toASCIIString() + + AtomEntryEntitySerializer.createSelfLink(entityInfo, data, null, properties.isKeyAutoGenerated(), false); + idlocation = properties.getServiceRoot().toASCIIString() + + AtomEntryEntitySerializer.createSelfLink(entityInfo, data, null, properties.isKeyAutoGenerated(), true); + } + + jsonStreamWriter.name(FormatJson.METADATA); + jsonStreamWriter.beginObject(); + jsonStreamWriter.namedStringValue(FormatJson.ID, idlocation); + jsonStreamWriter.separator(); + jsonStreamWriter.namedStringValue(FormatJson.URI, location); + jsonStreamWriter.separator(); + jsonStreamWriter.namedStringValueRaw(FormatJson.TYPE, type.getNamespace() + Edm.DELIMITER + type.getName()); + + if (type.hasStream()) { + jsonStreamWriter.separator(); + + EdmMapping entityTypeMapping = entityInfo.getEntityType().getMapping(); + String mediaResourceMimeType = null; + String mediaSrc = null; + + if (entityTypeMapping != null) { + String mediaResourceSourceKey = entityTypeMapping.getMediaResourceSourceKey(); + if (mediaResourceSourceKey != null) { + mediaSrc = (String) data.get(mediaResourceSourceKey); + } + if (mediaSrc == null) { + mediaSrc = location + VALUE; + } + String mediaResourceMimeTypeKey = entityTypeMapping.getMediaResourceMimeTypeKey(); + if (mediaResourceMimeTypeKey != null) { + mediaResourceMimeType = (String) data.get(mediaResourceMimeTypeKey); + } + if (mediaResourceMimeType == null) { + mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString(); + } + } else { + mediaSrc = location + VALUE; + mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString(); + } + + jsonStreamWriter.namedStringValueRaw(FormatJson.CONTENT_TYPE, mediaResourceMimeType); + jsonStreamWriter.separator(); + + jsonStreamWriter.namedStringValue(FormatJson.MEDIA_SRC, mediaSrc); + jsonStreamWriter.separator(); + jsonStreamWriter.namedStringValue(FormatJson.EDIT_MEDIA, location + VALUE); + } + jsonStreamWriter.endObject(); + } + + private String createCustomTargetLink(final EntityInfoAggregator entityInfo, final String navigationPropertyName, + final Map<String, Object> key) throws EntityProviderException, EdmException { + String target; + final EntityInfoAggregator targetEntityInfo = EntityInfoAggregator.create( + entityInfo.getEntitySet().getRelatedEntitySet( + (EdmNavigationProperty) entityInfo.getEntityType().getProperty(navigationPropertyName))); + target = (properties.getServiceRoot() == null ? "" : properties.getServiceRoot().toASCIIString()) + + AtomEntryEntitySerializer.createSelfLink(targetEntityInfo, key, null, properties.isKeyAutoGenerated(), false); + return target; + } + + private void writeNavigationLinks(final EntityInfoAggregator entityInfo, + Map<String, Object> navigationLinks) throws IOException, + EntityProviderException, EdmException { + if (navigationLinks != null && !navigationLinks.isEmpty()) { + for (Entry<String, Object> entry : navigationLinks.entrySet()) { + String target = null; + if (entry.getValue() instanceof HashMap<?, ?>) { + @SuppressWarnings("unchecked") + Map<String, Object> navigationKeyMap = (Map<String, Object>) entry.getValue(); + if (navigationKeyMap != null && !navigationKeyMap.isEmpty()) { //NOSONAR + target = createCustomTargetLink(entityInfo, entry.getKey(), navigationKeyMap); + jsonStreamWriter.separator(); + jsonStreamWriter.name(entry.getKey()); + jsonStreamWriter.beginObject() + .name(FormatJson.DEFERRED); + JsonLinkEntitySerializer.appendUri(jsonStreamWriter, target); + jsonStreamWriter.endObject(); + } + } + } + } + } + + public String getLocation() { + return location; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonFeedEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonFeedEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonFeedEntitySerializer.java new file mode 100644 index 0000000..e86b6c4 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonFeedEntitySerializer.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.client.api.ep.EntityCollection; +import org.apache.olingo.odata2.client.api.ep.EntityCollectionSerializerProperties; +import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator; +import org.apache.olingo.odata2.core.ep.util.FormatJson; +import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter; + +/** + * Producer for writing an entity collection (a feed) in JSON. + * + */ +public class JsonFeedEntitySerializer { + + private final EntityCollectionSerializerProperties properties; + + /** + * + * @param properties + * @throws EntityProviderException + */ + public JsonFeedEntitySerializer(final EntityCollectionSerializerProperties properties) { + this.properties = properties == null ? EntityCollectionSerializerProperties. + serviceRoot(null).build() : properties; + } + + /** + * This serializes the json payload feed + * @param writer + * @param entityInfo + * @param data + * @throws EntityProviderException + */ + public void appendAsArray(final Writer writer, final EntityInfoAggregator entityInfo, + final EntityCollection data) throws EntityProviderException { + + JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer); + try { + jsonStreamWriter.beginArray(); + appendEntries(writer, entityInfo, data, jsonStreamWriter); + jsonStreamWriter.endArray(); + } catch (final IOException e) { + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + /** + * This serializes the json payload feed + * @param writer + * @param entityInfo + * @param data + * @param isRootElement + * @throws EntityProviderException + */ + public void appendAsObject(final Writer writer, final EntityInfoAggregator entityInfo, + final EntityCollection data) throws EntityProviderException { + if (data == null) { + throw new EntityProviderException(EntityProviderException.NULL_VALUE); + } + JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer); + + try { + jsonStreamWriter.beginObject(); + + jsonStreamWriter.name(FormatJson.RESULTS) + .beginArray(); + + appendEntries(writer, entityInfo, data, jsonStreamWriter); + + jsonStreamWriter.endArray(); + + jsonStreamWriter.endObject(); + } catch (final IOException e) { + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } + } + + private void appendEntries(final Writer writer, final EntityInfoAggregator entityInfo, + final EntityCollection data, JsonStreamWriter jsonStreamWriter) throws EntityProviderException, + IOException { + boolean first = true; + for (Entity entryData : data.getEntities()) { + if (first) { + first = false; + } else { + jsonStreamWriter.separator(); + } + EntitySerializerProperties entryProperties = entryData == null || + entryData.getWriteProperties() == null ? data.getGlobalEntityProperties() != null? + data.getGlobalEntityProperties(): EntitySerializerProperties. + serviceRoot(properties.getServiceRoot()). + build() : entryData.getWriteProperties(); + + JsonEntryEntitySerializer entryProducer = new JsonEntryEntitySerializer(entryProperties); + entryProducer.append(writer, entityInfo, entryData); + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonLinkEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonLinkEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonLinkEntitySerializer.java new file mode 100644 index 0000000..cd756ce --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonLinkEntitySerializer.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.io.IOException; + +import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties; +import org.apache.olingo.odata2.core.ep.util.FormatJson; +import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter; + +/** + * Producer for writing a link in JSON. + * + */ +public class JsonLinkEntitySerializer { + + private final EntityProviderWriteProperties properties; //NOSONAR + + /** + * + * @param properties + * @throws EntityProviderException + */ + public JsonLinkEntitySerializer(final EntityProviderWriteProperties properties) { + this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties; + } + + protected static void appendUri(final JsonStreamWriter jsonStreamWriter, final String uri) throws IOException { + jsonStreamWriter.beginObject() + .namedStringValue(FormatJson.URI, uri) + .endObject(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonPropertyEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonPropertyEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonPropertyEntitySerializer.java new file mode 100644 index 0000000..f759389 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/JsonPropertyEntitySerializer.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmFacets; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmSimpleType; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeKind; +import org.apache.olingo.odata2.api.edm.EdmType; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityComplexPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.util.FormatJson; +import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter; + +/** + * Producer for writing a single simple or complex property in JSON, also usable + * for function imports returning a single instance of a simple or complex type. + * + */ +public class JsonPropertyEntitySerializer { + + /** + * Serializes a property of every entry in json payload + * @param writer + * @param propertyInfo + * @param value + * @throws EntityProviderException + */ + public void append(final Writer writer, final EntityPropertyInfo propertyInfo, final Object value) + throws EntityProviderException { + JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer); + + try { + jsonStreamWriter.beginObject() + .name(FormatJson.D) + .beginObject(); + + jsonStreamWriter.name(propertyInfo.getName()); + appendPropertyValue(jsonStreamWriter, propertyInfo.isComplex() ? (EntityComplexPropertyInfo) propertyInfo + : propertyInfo, value, true); + + jsonStreamWriter.endObject() + .endObject(); + } catch (final IOException e) { + throw new EntityProviderProducerException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass() + .getSimpleName()), e); + } catch (final EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + protected static void appendPropertyValue(final JsonStreamWriter jsonStreamWriter, + final EntityPropertyInfo propertyInfo, final Object value, + boolean validatingFacets) throws IOException, EdmException, + EntityProviderException { //NOSONAR + if (propertyInfo.isComplex()) { + if (value == null || value instanceof Map<?,?>) { + jsonStreamWriter.beginObject(); + appendPropertyMetadata(jsonStreamWriter, propertyInfo.getType()); + if (value == null) { + jsonStreamWriter.endObject(); + return; + } + for (final EntityPropertyInfo childPropertyInfo : ((EntityComplexPropertyInfo) propertyInfo) + .getPropertyInfos()) { + final String name = childPropertyInfo.getName(); + if ( !((Map<?,?>)value).containsKey(name)) { //NOSONAR + continue; + } + jsonStreamWriter.separator(); + jsonStreamWriter.name(name); + appendPropertyValue(jsonStreamWriter, childPropertyInfo, + value == null ? null : ((Map<?,?>) value).get(name), validatingFacets); //NOSONAR + } + jsonStreamWriter.endObject(); + } else { + throw new EntityProviderProducerException(EntityProviderException.ILLEGAL_ARGUMENT + .addContent("A complex property must have a Map as data")); + } + } else { + final EdmSimpleType type = (EdmSimpleType) propertyInfo.getType(); + final Object contentValue = value instanceof Entity ? ((Entity) value). + getProperties().get(propertyInfo.getName()) : value; + final EdmFacets facets = validatingFacets ? propertyInfo.getFacets(): null; + String valueAsString = null; + try { + valueAsString = type.valueToString(contentValue, EdmLiteralKind.JSON, facets); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderProducerException(EdmSimpleTypeException.getMessageReference( + e.getMessageReference()).updateContent(e.getMessageReference().getContent(), + propertyInfo.getName()), e); + } + switch (EdmSimpleTypeKind.valueOf(type.getName())) { + case String: + jsonStreamWriter.stringValue(valueAsString); + break; + case Boolean: + case Byte: + case SByte: + case Int16: + case Int32: + jsonStreamWriter.unquotedValue(valueAsString); + break; + case DateTime: + case DateTimeOffset: + // Although JSON escaping is (and should be) done in the JSON + // serializer, we backslash-escape the forward slash here explicitly + // because it is not required to escape it in JSON but in OData. + jsonStreamWriter.stringValueRaw(valueAsString == null ? null : valueAsString.replace("/", "\\/")); + break; + default: + jsonStreamWriter.stringValueRaw(valueAsString); + break; + } + } + } + + protected static void appendPropertyMetadata(final JsonStreamWriter jsonStreamWriter, final EdmType type) + throws IOException, EdmException { + jsonStreamWriter.name(FormatJson.METADATA) + .beginObject() + .namedStringValueRaw(FormatJson.TYPE, type.getNamespace() + Edm.DELIMITER + type.getName()) + .endObject(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/9e949e40/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/XmlPropertyEntitySerializer.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/XmlPropertyEntitySerializer.java b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/XmlPropertyEntitySerializer.java new file mode 100644 index 0000000..24bfc17 --- /dev/null +++ b/odata2-lib/odata-client-core/src/main/java/org/apache/olingo/odata2/client/core/ep/serializer/XmlPropertyEntitySerializer.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * 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.odata2.client.core.ep.serializer; + +import java.util.List; +import java.util.Map; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.olingo.odata2.api.edm.Edm; +import org.apache.olingo.odata2.api.edm.EdmCustomizableFeedMappings; +import org.apache.olingo.odata2.api.edm.EdmException; +import org.apache.olingo.odata2.api.edm.EdmFacets; +import org.apache.olingo.odata2.api.edm.EdmLiteralKind; +import org.apache.olingo.odata2.api.edm.EdmSimpleType; +import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; +import org.apache.olingo.odata2.api.ep.EntityProviderException; +import org.apache.olingo.odata2.client.api.ep.Entity; +import org.apache.olingo.odata2.client.api.ep.EntitySerializerProperties; +import org.apache.olingo.odata2.core.ep.EntityProviderProducerException; +import org.apache.olingo.odata2.core.ep.aggregator.EntityComplexPropertyInfo; +import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo; +import org.apache.olingo.odata2.core.ep.util.FormatXml; + +/** + * Internal EntityProvider for simple and complex EDM properties which are pre-analyzed as {@link EntityPropertyInfo}. + * + */ +public class XmlPropertyEntitySerializer { + + private final boolean validateFacets; + + /** + * + * @param writeProperties + */ + public XmlPropertyEntitySerializer(final EntitySerializerProperties writeProperties) { + this(writeProperties.isValidatingFacets()); + } + + /** + * + * @param validateFacets + */ + public XmlPropertyEntitySerializer( final boolean validateFacets) { + this.validateFacets = validateFacets; + } + + /** + * Append {@link Object} <code>value</code> based on {@link EntityPropertyInfo} to {@link XMLStreamWriter} in an + * already existing XML structure inside the d namespace. + * + * @param writer + * @param name Name of the outer XML tag + * @param propertyInfo + * @param value + * @throws EntityProviderException + */ + public void append(final XMLStreamWriter writer, final String name, final EntityPropertyInfo propertyInfo, + final Object value) throws EntityProviderException { + try { + writer.writeStartElement(Edm.NAMESPACE_D_2007_08, name); + + if (propertyInfo.isComplex()) { + appendProperty(writer, (EntityComplexPropertyInfo) propertyInfo, value); + } else { + appendProperty(writer, propertyInfo, value); + } + + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + /** + * Serializes custom properties + * @param writer + * @param name + * @param propertyInfo + * @param value + * @throws EntityProviderException + */ + public void appendCustomProperty(final XMLStreamWriter writer, final String name, + final EntityPropertyInfo propertyInfo, final Object value) throws EntityProviderException { + try { + if (!propertyInfo.isComplex()) { + writeStartElementWithCustomNamespace(writer, propertyInfo, name); + appendProperty(writer, propertyInfo, value); + writer.writeEndElement(); + } + } catch (XMLStreamException e) { + throw new EntityProviderProducerException(EntityProviderException.COMMON, e); + } catch (EdmException e) { + throw new EntityProviderProducerException(e.getMessageReference(), e); + } + } + + + /** + * + * @param writer + * @param propertyInfo + * @param value + * @throws XMLStreamException + * @throws EdmException + * @throws EntityProviderException + */ + private void appendProperty(final XMLStreamWriter writer, final EntityComplexPropertyInfo propertyInfo, + final Object value) throws XMLStreamException, EdmException, EntityProviderException { + + if (value == null) { + writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_NULL, FormatXml.ATOM_VALUE_TRUE); + } else { + writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_TYPE, getFqnTypeName(propertyInfo)); + List<EntityPropertyInfo> propertyInfos = propertyInfo.getPropertyInfos(); + for (EntityPropertyInfo childPropertyInfo : propertyInfos) { + if ( value instanceof Map && !((Map<?,?>)value).containsKey(childPropertyInfo.getName())|| + (value instanceof Entity && (((Entity)value).getProperty(childPropertyInfo.getName()))==null)) { + continue; + } + Object childValue = extractChildValue(value, childPropertyInfo.getName()); + append(writer, childPropertyInfo.getName(), childPropertyInfo, childValue); + } + } + } + + /** + * Returns full qualified name of a type of a given PropertyInfo. + * @return Full qualified name + */ + private String getFqnTypeName(final EntityComplexPropertyInfo propertyInfo) throws EdmException { + return propertyInfo.getType().getNamespace() + Edm.DELIMITER + propertyInfo.getType().getName(); + } + + /** + * If <code>value</code> is a {@link Map} the element with given <code>name</code> as key is returned. + * If <code>value</code> is NOT a {@link Map} its {@link String#valueOf(Object)} result is returned. + * + * @param value + * @param name + * @return name or result (see above) + */ + private Object extractChildValue(final Object value, final String name) { + if (value instanceof Map) { + Map<?, ?> map = (Map<?, ?>) value; + return map.get(name); + }else if (value instanceof Entity) { + Map<?, ?> map = ((Entity) value).getProperties(); + return map.get(name); + } + return String.valueOf(value); + } + + /** + * Appends a simple-property value to the XML stream. + * @param writer the XML stream writer + * @param prop property informations + * @param value the value of the property + * @throws XMLStreamException + * @throws EdmException + * @throws EntityProviderProducerException + */ + private void appendProperty(final XMLStreamWriter writer, final EntityPropertyInfo prop, final Object value) + throws XMLStreamException, EdmException, EntityProviderProducerException { + Object contentValue = value; + String mimeType = null; + if (prop.getMimeType() != null) { + mimeType = prop.getMimeType(); + } else if (prop.getMapping() != null && prop.getMapping().getMediaResourceMimeTypeKey() != null) { + mimeType = (String) extractChildValue(value, prop.getMapping().getMediaResourceMimeTypeKey()); + contentValue = extractChildValue(value, prop.getName()); + } + + if (mimeType != null) { + writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.M_MIME_TYPE, mimeType); + } + + final EdmSimpleType type = (EdmSimpleType) prop.getType(); + final EdmFacets facets = validateFacets ? prop.getFacets() : null; + String valueAsString = null; + try { + valueAsString = type.valueToString(contentValue, EdmLiteralKind.DEFAULT, facets); + } catch (EdmSimpleTypeException e) { + throw new EntityProviderProducerException(EdmSimpleTypeException.getMessageReference( + e.getMessageReference()).updateContent( + e.getMessageReference().getContent(), prop.getName()), e); + } + if (valueAsString == null) { + writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.ATOM_NULL, FormatXml.ATOM_VALUE_TRUE); + } else { + writer.writeCharacters(valueAsString); + } + } + + /** + * + * @param writer + * @param prop + * @param name + * @throws XMLStreamException + * @throws EntityProviderException + */ + private void writeStartElementWithCustomNamespace(final XMLStreamWriter writer, final EntityPropertyInfo prop, + final String name) throws XMLStreamException, EntityProviderException { + EdmCustomizableFeedMappings mapping = prop.getCustomMapping(); + String nsPrefix = mapping.getFcNsPrefix(); + String nsUri = mapping.getFcNsUri(); + if (nsUri == null || nsPrefix == null) { + throw new EntityProviderProducerException(EntityProviderException.INVALID_NAMESPACE.addContent(name)); + } + writer.writeStartElement(nsPrefix, name, nsUri); + writer.writeNamespace(nsPrefix, nsUri); + } +}