You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2013/07/26 13:22:20 UTC
[15/51] [partial] initial commit
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomEntryEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomEntryEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomEntryEntityProducer.java
new file mode 100644
index 0000000..1fc2d90
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomEntryEntityProducer.java
@@ -0,0 +1,527 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.olingo.odata2.api.ODataCallback;
+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.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.edm.EdmType;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.ep.callback.OnWriteEntryContent;
+import org.apache.olingo.odata2.api.ep.callback.OnWriteFeedContent;
+import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackContext;
+import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackResult;
+import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackContext;
+import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackResult;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.api.uri.ExpandSelectTreeNode;
+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.aggregator.EntityInfoAggregator;
+import org.apache.olingo.odata2.core.ep.aggregator.EntityPropertyInfo;
+import org.apache.olingo.odata2.core.ep.aggregator.NavigationPropertyInfo;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+
+/**
+ * Serializes an ATOM entry.
+ * @author SAP AG
+ */
+public class AtomEntryEntityProducer {
+
+ private String etag;
+ private String location;
+ private final EntityProviderWriteProperties properties;
+
+ public AtomEntryEntityProducer(final EntityProviderWriteProperties properties) throws EntityProviderException {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data, final boolean isRootElement, final boolean isFeedPart) throws EntityProviderException {
+ try {
+ 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());
+ }
+
+ etag = createETag(eia, data);
+ if (etag != null) {
+ writer.writeAttribute(Edm.NAMESPACE_M_2007_08, FormatXml.M_ETAG, etag);
+ }
+
+ // write all atom infos (mandatory and optional)
+ appendAtomMandatoryParts(writer, eia, data);
+ appendAtomOptionalParts(writer, eia, data);
+
+ if (eia.getEntityType().hasStream()) {
+ // write all links
+ appendAtomEditLink(writer, eia, data);
+ appendAtomContentLink(writer, eia, data, properties.getMediaResourceMimeType());
+ appendAtomNavigationLinks(writer, eia, data);
+ // write properties/content
+ appendCustomProperties(writer, eia, data);
+ appendAtomContentPart(writer, eia, data, properties.getMediaResourceMimeType());
+ appendProperties(writer, eia, data);
+ } else {
+ // write all links
+ appendAtomEditLink(writer, eia, data);
+ appendAtomNavigationLinks(writer, eia, data);
+ // write properties/content
+ appendCustomProperties(writer, eia, data);
+ writer.writeStartElement(FormatXml.ATOM_CONTENT);
+ writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_XML.toString());
+ appendProperties(writer, eia, data);
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+
+ writer.flush();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (URISyntaxException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ 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();
+ XmlPropertyEntityProducer aps = new XmlPropertyEntityProducer();
+ aps.appendCustomProperty(writer, name, info, data.get(name));
+ }
+ }
+
+ protected static String createETag(final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException {
+ try {
+ String etag = null;
+
+ Collection<EntityPropertyInfo> propertyInfos = eia.getETagPropertyInfos();
+ for (EntityPropertyInfo propertyInfo : propertyInfos) {
+ EdmType edmType = propertyInfo.getType();
+ if (edmType instanceof EdmSimpleType) {
+ EdmSimpleType edmSimpleType = (EdmSimpleType) edmType;
+ if (etag == null) {
+ etag = edmSimpleType.valueToString(data.get(propertyInfo.getName()), EdmLiteralKind.DEFAULT, propertyInfo.getFacets());
+ } else {
+ etag = etag + Edm.DELIMITER + edmSimpleType.valueToString(data.get(propertyInfo.getName()), EdmLiteralKind.DEFAULT, propertyInfo.getFacets());
+ }
+ }
+ }
+
+ if (etag != null) {
+ etag = "W/\"" + etag + "\"";
+ }
+
+ return etag;
+ } catch (EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendAtomNavigationLinks(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException, EdmException, URISyntaxException {
+ for (String name : eia.getSelectedNavigationPropertyNames()) {
+ NavigationPropertyInfo info = eia.getNavigationPropertyInfo(name);
+ boolean isFeed = (info.getMultiplicity() == EdmMultiplicity.MANY);
+ String self = createSelfLink(eia, data, info.getName());
+ appendAtomNavigationLink(writer, self, info.getName(), isFeed, eia, data);
+ }
+ }
+
+ private void appendAtomNavigationLink(final XMLStreamWriter writer, final String self, final String navigationPropertyName, final boolean isFeed, final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException, EdmException, URISyntaxException {
+ try {
+ writer.writeStartElement(FormatXml.ATOM_LINK);
+ writer.writeAttribute(FormatXml.ATOM_HREF, self);
+ 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());
+ appendInlineFeed(writer, navigationPropertyName, eia, data, self);
+ } else {
+ writer.writeAttribute(FormatXml.ATOM_TYPE, ContentType.APPLICATION_ATOM_XML_ENTRY.toString());
+ appendInlineEntry(writer, navigationPropertyName, eia, data);
+ }
+
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendInlineFeed(final XMLStreamWriter writer, final String navigationPropertyName, final EntityInfoAggregator eia, final Map<String, Object> data, final String self) throws EntityProviderException, XMLStreamException, EdmException, URISyntaxException {
+
+ if (eia.getExpandedNavigationPropertyNames().contains(navigationPropertyName)) {
+ if (properties.getCallbacks() != null && properties.getCallbacks().containsKey(navigationPropertyName)) {
+ writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+
+ EdmNavigationProperty navProp = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
+ WriteFeedCallbackContext context = new WriteFeedCallbackContext();
+ context.setSourceEntitySet(eia.getEntitySet());
+ context.setNavigationProperty(navProp);
+ context.setEntryData(data);
+ ExpandSelectTreeNode subNode = properties.getExpandSelectTree().getLinks().get(navigationPropertyName);
+ context.setCurrentExpandSelectTreeNode(subNode);
+ context.setSelfLink(new URI(self));
+
+ ODataCallback callback = properties.getCallbacks().get(navigationPropertyName);
+ if (callback == null) {
+ throw new EntityProviderException(EntityProviderException.EXPANDNOTSUPPORTED);
+ }
+ WriteFeedCallbackResult result = null;
+ try {
+ result = ((OnWriteFeedContent) callback).retrieveFeedResult(context);
+ } catch (ODataApplicationException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ List<Map<String, Object>> inlineData = result.getFeedData();
+ if (inlineData == null) {
+ inlineData = new ArrayList<Map<String, Object>>();
+ }
+
+ EntityProviderWriteProperties inlineProperties = result.getInlineProperties();
+ EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navProp);
+ AtomFeedProducer inlineFeedProducer = new AtomFeedProducer(inlineProperties);
+ EntityInfoAggregator inlineEia = EntityInfoAggregator.create(inlineEntitySet, inlineProperties.getExpandSelectTree());
+ inlineFeedProducer.append(writer, inlineEia, inlineData, true);
+
+ writer.writeEndElement();
+ }
+ }
+ }
+
+ private void appendInlineEntry(final XMLStreamWriter writer, final String navigationPropertyName, final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException, XMLStreamException, EdmException {
+
+ if (eia.getExpandedNavigationPropertyNames().contains(navigationPropertyName)) {
+ if (properties.getCallbacks() != null && properties.getCallbacks().containsKey(navigationPropertyName)) {
+ writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+
+ EdmNavigationProperty navProp = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
+ WriteEntryCallbackContext context = new WriteEntryCallbackContext();
+ context.setSourceEntitySet(eia.getEntitySet());
+ context.setNavigationProperty(navProp);
+ context.setEntryData(data);
+ ExpandSelectTreeNode subNode = properties.getExpandSelectTree().getLinks().get(navigationPropertyName);
+ context.setCurrentExpandSelectTreeNode(subNode);
+
+ ODataCallback callback = properties.getCallbacks().get(navigationPropertyName);
+ if (callback == null) {
+ throw new EntityProviderException(EntityProviderException.EXPANDNOTSUPPORTED);
+ }
+ WriteEntryCallbackResult result = null;
+ try {
+ result = ((OnWriteEntryContent) callback).retrieveEntryResult(context);
+ } catch (ODataApplicationException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ Map<String, Object> inlineData = result.getEntryData();
+ if (inlineData != null && !inlineData.isEmpty()) {
+ EntityProviderWriteProperties inlineProperties = result.getInlineProperties();
+ EdmEntitySet inlineEntitySet = eia.getEntitySet().getRelatedEntitySet(navProp);
+ AtomEntryEntityProducer inlineProducer = new AtomEntryEntityProducer(inlineProperties);
+ EntityInfoAggregator inlineEia = EntityInfoAggregator.create(inlineEntitySet, inlineProperties.getExpandSelectTree());
+ inlineProducer.append(writer, inlineEia, inlineData, false, false);
+ }
+
+ writer.writeEndElement();
+ }
+ }
+
+ }
+
+ private void appendAtomEditLink(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException {
+ try {
+ String self = createSelfLink(eia, data, null);
+
+ writer.writeStartElement(FormatXml.ATOM_LINK);
+ writer.writeAttribute(FormatXml.ATOM_HREF, self);
+ writer.writeAttribute(FormatXml.ATOM_REL, Edm.LINK_REL_EDIT);
+ writer.writeAttribute(FormatXml.ATOM_TITLE, eia.getEntityType().getName());
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendAtomContentLink(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data, String mediaResourceMimeType) throws EntityProviderException {
+ try {
+ String self = createSelfLink(eia, data, "$value");
+
+ if (mediaResourceMimeType == null) {
+ mediaResourceMimeType = ContentType.APPLICATION_OCTET_STREAM.toString();
+ }
+
+ writer.writeStartElement(FormatXml.ATOM_LINK);
+ writer.writeAttribute(FormatXml.ATOM_HREF, self);
+ writer.writeAttribute(FormatXml.ATOM_REL, Edm.LINK_REL_EDIT_MEDIA);
+ writer.writeAttribute(FormatXml.ATOM_TYPE, mediaResourceMimeType);
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendAtomContentPart(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data, String mediaResourceMimeType) throws EntityProviderException {
+ try {
+ String self = createSelfLink(eia, data, "$value");
+
+ if (mediaResourceMimeType == null) {
+ 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 EntityProviderException(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);
+ location = properties.getServiceRoot().toASCIIString() + createSelfLink(eia, data, null);
+ writer.writeCharacters(location);
+ 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 = st.valueToString(object, EdmLiteralKind.DEFAULT, titleInfo.getFacets());
+ 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 EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ String getUpdatedString(final EntityInfoAggregator eia, final Map<String, Object> data) throws EdmSimpleTypeException {
+ 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();
+ }
+ String valueToString = EdmDateTimeOffset.getInstance().valueToString(updateDate, EdmLiteralKind.DEFAULT, updateFacets);
+ return valueToString;
+ }
+
+ private String getTargetPathValue(final EntityInfoAggregator eia, final String targetPath, final Map<String, Object> data) throws EntityProviderException {
+ try {
+ EntityPropertyInfo 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 (EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, 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 EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, 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 EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ static String createSelfLink(final EntityInfoAggregator eia, final Map<String, Object> data, final String extension) throws EntityProviderException {
+ StringBuilder sb = new StringBuilder();
+ if (!eia.isDefaultEntityContainer()) {
+ sb.append(Encoder.encode(eia.getEntityContainerName())).append(Edm.DELIMITER);
+ }
+ sb.append(Encoder.encode(eia.getEntitySetName()));
+
+ sb.append("(").append(createEntryKey(eia, data)).append(")").append(extension == null ? "" : ("/" + extension));
+ return sb.toString();
+ }
+
+ private static String createEntryKey(final EntityInfoAggregator entityInfo, final Map<String, Object> data) 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 {
+ keys.append(Encoder.encode(type.valueToString(data.get(name), EdmLiteralKind.URI, keyPropertyInfo.getFacets())));
+ } catch (final EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ return keys.toString();
+ }
+
+ private void appendProperties(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> data) throws EntityProviderException {
+ try {
+ List<String> propertyNames = eia.getSelectedPropertyNames();
+ if (!propertyNames.isEmpty()) {
+ writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_PROPERTIES);
+
+ for (String propertyName : propertyNames) {
+ EntityPropertyInfo propertyInfo = eia.getPropertyInfo(propertyName);
+
+ if (isNotMappedViaCustomMapping(propertyInfo)) {
+ Object value = data.get(propertyName);
+ XmlPropertyEntityProducer aps = new XmlPropertyEntityProducer();
+ aps.append(writer, propertyInfo.getName(), propertyInfo, value);
+ }
+ }
+
+ writer.writeEndElement();
+ }
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private boolean isNotMappedViaCustomMapping(final EntityPropertyInfo propertyInfo) {
+ EdmCustomizableFeedMappings customMapping = propertyInfo.getCustomMapping();
+ if (customMapping != null && customMapping.isFcKeepInContent() != null) {
+ return customMapping.isFcKeepInContent().booleanValue();
+ }
+ return true;
+ }
+
+ public String getETag() {
+ return etag;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomFeedProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomFeedProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomFeedProducer.java
new file mode 100644
index 0000000..9049792
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomFeedProducer.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.net.URI;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.olingo.odata2.api.commons.InlineCount;
+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.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.ep.callback.TombstoneCallback;
+import org.apache.olingo.odata2.api.ep.callback.TombstoneCallbackResult;
+import org.apache.olingo.odata2.core.commons.Encoder;
+import org.apache.olingo.odata2.core.edm.EdmDateTimeOffset;
+import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+
+/**
+ * Serializes an ATOM feed.
+ * @author SAP AG
+ */
+public class AtomFeedProducer {
+
+ private final EntityProviderWriteProperties properties;
+
+ public AtomFeedProducer(final EntityProviderWriteProperties properties) {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final XMLStreamWriter writer, final EntityInfoAggregator eia, final List<Map<String, Object>> data, final boolean isInline) throws EntityProviderException {
+ try {
+ writer.writeStartElement(FormatXml.ATOM_FEED);
+ TombstoneCallback callback = null;
+ 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);
+ callback = getTombstoneCallback();
+ if (callback != null) {
+ writer.writeNamespace(TombstoneCallback.PREFIX_TOMBSTONE, TombstoneCallback.NAMESPACE_TOMBSTONE);
+ }
+ }
+ 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);
+ if (properties.getInlineCountType() == InlineCount.ALLPAGES) {
+ appendInlineCount(writer, properties.getInlineCount());
+ }
+
+ appendEntries(writer, eia, data);
+
+ if (callback != null) {
+ appendDeletedEntries(writer, eia, callback);
+ }
+
+ if (properties.getNextLink() != null) {
+ appendNextLink(writer, properties.getNextLink());
+ }
+
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private TombstoneCallback getTombstoneCallback() {
+ if (properties.getCallbacks() != null && properties.getCallbacks().containsKey(TombstoneCallback.CALLBACK_KEY_TOMBSTONE)) {
+ TombstoneCallback callback = (TombstoneCallback) properties.getCallbacks().get(TombstoneCallback.CALLBACK_KEY_TOMBSTONE);
+ return callback;
+ } else {
+ return null;
+ }
+ }
+
+ private void appendDeletedEntries(final XMLStreamWriter writer, final EntityInfoAggregator eia, final TombstoneCallback callback) throws EntityProviderException {
+ TombstoneCallbackResult callbackResult = callback.getTombstoneCallbackResult();
+ List<Map<String, Object>> tombstoneData = callbackResult.getDeletedEntriesData();
+ if (tombstoneData != null) {
+ TombstoneProducer tombstoneProducer = new TombstoneProducer();
+ tombstoneProducer.appendTombstones(writer, eia, properties, tombstoneData);
+ }
+
+ String deltaLink = callbackResult.getDeltaLink();
+ if (deltaLink != null) {
+ try {
+ writer.writeStartElement(FormatXml.ATOM_LINK);
+ writer.writeAttribute(FormatXml.ATOM_REL, FormatXml.ATOM_DELTA_LINK);
+ writer.writeAttribute(FormatXml.ATOM_HREF, deltaLink);
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+ }
+
+ private void appendNextLink(final XMLStreamWriter writer, final String nextLink) throws EntityProviderException {
+ try {
+ writer.writeStartElement(FormatXml.ATOM_LINK);
+ writer.writeAttribute(FormatXml.ATOM_HREF, nextLink);
+ writer.writeAttribute(FormatXml.ATOM_REL, FormatXml.ATOM_NEXT_LINK);
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendEntries(final XMLStreamWriter writer, final EntityInfoAggregator eia, final List<Map<String, Object>> data) throws EntityProviderException {
+ AtomEntryEntityProducer entryProvider = new AtomEntryEntityProducer(properties);
+ for (Map<String, Object> singleEntryData : data) {
+ entryProvider.append(writer, eia, singleEntryData, false, true);
+ }
+ }
+
+ private void appendInlineCount(final XMLStreamWriter writer, final Integer inlineCount) throws EntityProviderException {
+ if (inlineCount == null || inlineCount < 0) {
+ throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID);
+ }
+ try {
+ writer.writeStartElement(Edm.NAMESPACE_M_2007_08, FormatXml.M_COUNT);
+ writer.writeCharacters(String.valueOf(inlineCount));
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ 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 EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private String createSelfLink(final EntityInfoAggregator eia) throws EntityProviderException {
+ 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 EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private String createAtomId(final EntityInfoAggregator eia) throws EntityProviderException {
+ return properties.getServiceRoot() + createSelfLink(eia);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomServiceDocumentProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomServiceDocumentProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomServiceDocumentProducer.java
new file mode 100644
index 0000000..a38bb2f
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/AtomServiceDocumentProducer.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.Writer;
+import java.util.List;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+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.EdmEntitySetInfo;
+import org.apache.olingo.odata2.api.edm.EdmServiceMetadata;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.core.commons.ContentType;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+
+/**
+ * Writes the OData service document in XML.
+ * @author SAP AG
+ */
+public class AtomServiceDocumentProducer {
+
+ private static final String DEFAULT_CHARSET = ContentType.CHARSET_UTF_8;
+ private static final String XML_VERSION = "1.0";
+ private final Edm edm;
+ private final String serviceRoot;
+
+ public AtomServiceDocumentProducer(final Edm edm, final String serviceRoot) {
+ this.edm = edm;
+ this.serviceRoot = serviceRoot;
+ }
+
+ public void writeServiceDocument(final Writer writer) throws EntityProviderException {
+
+ EdmServiceMetadata serviceMetadata = edm.getServiceMetadata();
+
+ try {
+ XMLStreamWriter xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
+
+ xmlStreamWriter.writeStartDocument(DEFAULT_CHARSET, XML_VERSION);
+ xmlStreamWriter.setPrefix(Edm.PREFIX_XML, Edm.NAMESPACE_XML_1998);
+ xmlStreamWriter.setPrefix(Edm.PREFIX_ATOM, Edm.NAMESPACE_ATOM_2005);
+ xmlStreamWriter.setDefaultNamespace(Edm.NAMESPACE_APP_2007);
+
+ xmlStreamWriter.writeStartElement(FormatXml.APP_SERVICE);
+ xmlStreamWriter.writeAttribute(Edm.PREFIX_XML, Edm.NAMESPACE_XML_1998, FormatXml.XML_BASE, serviceRoot);
+ xmlStreamWriter.writeDefaultNamespace(Edm.NAMESPACE_APP_2007);
+ xmlStreamWriter.writeNamespace(Edm.PREFIX_ATOM, Edm.NAMESPACE_ATOM_2005);
+
+ xmlStreamWriter.writeStartElement(FormatXml.APP_WORKSPACE);
+ xmlStreamWriter.writeStartElement(Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_TITLE);
+ xmlStreamWriter.writeCharacters(FormatXml.ATOM_TITLE_DEFAULT);
+ xmlStreamWriter.writeEndElement();
+
+ List<EdmEntitySetInfo> entitySetInfos = serviceMetadata.getEntitySetInfos();
+ for (EdmEntitySetInfo info : entitySetInfos) {
+ xmlStreamWriter.writeStartElement(FormatXml.APP_COLLECTION);
+ xmlStreamWriter.writeAttribute(FormatXml.ATOM_HREF, info.getEntitySetUri().toASCIIString());
+ xmlStreamWriter.writeStartElement(Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_TITLE);
+ xmlStreamWriter.writeCharacters(info.getEntitySetName());
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeEndElement();
+ }
+
+ // Collection<Schema> schemas = edmProvider.getSchemas();
+ // if (schemas != null) {
+ // for (Schema schema : schemas) {
+ // Collection<EntityContainer> entityContainers = schema.getEntityContainers();
+ // if (entityContainers != null) {
+ // for (EntityContainer entityContainer : entityContainers) {
+ // Collection<EntitySet> entitySets = entityContainer.getEntitySets();
+ // for (EntitySet entitySet : entitySets) {
+ // xmlStreamWriter.writeStartElement(FormatXml.APP_COLLECTION);
+ // if (entityContainer.isDefaultEntityContainer()) {
+ // xmlStreamWriter.writeAttribute(FormatXml.ATOM_HREF, entitySet.getName());
+ // } else {
+ // xmlStreamWriter.writeAttribute(FormatXml.ATOM_HREF, entityContainer.getName() + Edm.DELIMITER + entitySet.getName());
+ // }
+ // xmlStreamWriter.writeStartElement(Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_TITLE);
+ // xmlStreamWriter.writeCharacters(entitySet.getName());
+ // xmlStreamWriter.writeEndElement();
+ // xmlStreamWriter.writeEndElement();
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeEndDocument();
+
+ xmlStreamWriter.flush();
+ } catch (FactoryConfigurationError e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (ODataException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonCollectionEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonCollectionEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonCollectionEntityProducer.java
new file mode 100644
index 0000000..532ea36
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonCollectionEntityProducer.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+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;
+
+/**
+ * Provider for writing a collection of simple-type or complex-type instances in JSON.
+ * @author SAP AG
+ */
+public class JsonCollectionEntityProducer {
+
+ public void append(final Writer writer, final EntityPropertyInfo propertyInfo, final List<?> data) throws EntityProviderException {
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+
+ try {
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.D)
+ .beginObject();
+
+ jsonStreamWriter.name(FormatJson.METADATA)
+ .beginObject()
+ .namedStringValueRaw(FormatJson.TYPE,
+ "Collection(" + propertyInfo.getType().getNamespace() + Edm.DELIMITER + propertyInfo.getType().getName() + ")")
+ .endObject()
+ .separator();
+
+ jsonStreamWriter.name(FormatJson.RESULTS)
+ .beginArray();
+ boolean first = true;
+ for (final Object item : data) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ JsonPropertyEntityProducer.appendPropertyValue(jsonStreamWriter, propertyInfo, item);
+ }
+ jsonStreamWriter.endArray();
+
+ jsonStreamWriter.endObject()
+ .endObject();
+ } catch (final IOException 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);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonEntryEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonEntryEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonEntryEntityProducer.java
new file mode 100644
index 0000000..40a0e33
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonEntryEntityProducer.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.ODataCallback;
+import org.apache.olingo.odata2.api.commons.HttpContentType;
+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.EdmMultiplicity;
+import org.apache.olingo.odata2.api.edm.EdmNavigationProperty;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.ep.callback.OnWriteEntryContent;
+import org.apache.olingo.odata2.api.ep.callback.OnWriteFeedContent;
+import org.apache.olingo.odata2.api.ep.callback.WriteCallbackContext;
+import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackContext;
+import org.apache.olingo.odata2.api.ep.callback.WriteEntryCallbackResult;
+import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackContext;
+import org.apache.olingo.odata2.api.ep.callback.WriteFeedCallbackResult;
+import org.apache.olingo.odata2.api.exception.ODataApplicationException;
+import org.apache.olingo.odata2.core.commons.Encoder;
+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.
+ * @author SAP AG
+ */
+public class JsonEntryEntityProducer {
+
+ private final EntityProviderWriteProperties properties;
+ private String eTag;
+ private String location;
+ private JsonStreamWriter jsonStreamWriter;
+
+ public JsonEntryEntityProducer(final EntityProviderWriteProperties properties) throws EntityProviderException {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final Writer writer, final EntityInfoAggregator entityInfo, final Map<String, Object> data, final boolean isRootElement) throws EntityProviderException {
+ final EdmEntityType type = entityInfo.getEntityType();
+
+ try {
+ jsonStreamWriter = new JsonStreamWriter(writer);
+ if (isRootElement) {
+ jsonStreamWriter.beginObject().name(FormatJson.D);
+ }
+
+ jsonStreamWriter.beginObject();
+
+ jsonStreamWriter.name(FormatJson.METADATA)
+ .beginObject();
+ final String self = AtomEntryEntityProducer.createSelfLink(entityInfo, data, null);
+ location = (properties.getServiceRoot() == null ? "" : properties.getServiceRoot().toASCIIString()) + self;
+ jsonStreamWriter.namedStringValue(FormatJson.ID, location).separator()
+ .namedStringValue(FormatJson.URI, location).separator()
+ .namedStringValueRaw(FormatJson.TYPE,
+ type.getNamespace() + Edm.DELIMITER + type.getName());
+ eTag = AtomEntryEntityProducer.createETag(entityInfo, data);
+ if (eTag != null) {
+ jsonStreamWriter.separator()
+ .namedStringValue(FormatJson.ETAG, eTag);
+ }
+ if (type.hasStream()) {
+ jsonStreamWriter.separator()
+ .namedStringValueRaw(FormatJson.CONTENT_TYPE,
+ properties.getMediaResourceMimeType() == null ?
+ type.getMapping() == null || type.getMapping().getMimeType() == null || data.get(type.getMapping().getMimeType()) == null ?
+ HttpContentType.APPLICATION_OCTET_STREAM : data.get(type.getMapping().getMimeType()).toString() :
+ properties.getMediaResourceMimeType())
+ .separator()
+ .namedStringValue(FormatJson.MEDIA_SRC, self + "/$value").separator()
+ .namedStringValue(FormatJson.EDIT_MEDIA, location + "/$value");
+ }
+ jsonStreamWriter.endObject();
+
+ for (final String propertyName : type.getPropertyNames()) {
+ if (entityInfo.getSelectedPropertyNames().contains(propertyName)) {
+ jsonStreamWriter.separator()
+ .name(propertyName);
+ JsonPropertyEntityProducer.appendPropertyValue(jsonStreamWriter, entityInfo.getPropertyInfo(propertyName), data.get(propertyName));
+ }
+ }
+
+ for (final String navigationPropertyName : type.getNavigationPropertyNames()) {
+ if (entityInfo.getSelectedNavigationPropertyNames().contains(navigationPropertyName)) {
+ jsonStreamWriter.separator()
+ .name(navigationPropertyName);
+ if (entityInfo.getExpandedNavigationPropertyNames().contains(navigationPropertyName)) {
+ if (properties.getCallbacks() != null && properties.getCallbacks().containsKey(navigationPropertyName)) {
+ 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);
+
+ WriteCallbackContext context = isFeed ? new WriteFeedCallbackContext() : new WriteEntryCallbackContext();
+ context.setSourceEntitySet(entitySet);
+ context.setNavigationProperty(navigationProperty);
+ context.setEntryData(data);
+ context.setCurrentExpandSelectTreeNode(properties.getExpandSelectTree().getLinks().get(navigationPropertyName));
+
+ ODataCallback callback = properties.getCallbacks().get(navigationPropertyName);
+ if (callback == null) {
+ throw new EntityProviderException(EntityProviderException.EXPANDNOTSUPPORTED);
+ }
+ try {
+ if (isFeed) {
+ final WriteFeedCallbackResult result = ((OnWriteFeedContent) callback).retrieveFeedResult((WriteFeedCallbackContext) context);
+ List<Map<String, Object>> inlineData = result.getFeedData();
+ if (inlineData == null) {
+ inlineData = new ArrayList<Map<String, Object>>();
+ }
+ final EntityProviderWriteProperties inlineProperties = result.getInlineProperties();
+ final EntityInfoAggregator inlineEntityInfo = EntityInfoAggregator.create(inlineEntitySet, inlineProperties.getExpandSelectTree());
+ new JsonFeedEntityProducer(inlineProperties).append(writer, inlineEntityInfo, inlineData, false);
+
+ } else {
+ final WriteEntryCallbackResult result = ((OnWriteEntryContent) callback).retrieveEntryResult((WriteEntryCallbackContext) context);
+ Map<String, Object> inlineData = result.getEntryData();
+ if (inlineData != null && !inlineData.isEmpty()) {
+ final EntityProviderWriteProperties inlineProperties = result.getInlineProperties();
+ final EntityInfoAggregator inlineEntityInfo = EntityInfoAggregator.create(inlineEntitySet, inlineProperties.getExpandSelectTree());
+ new JsonEntryEntityProducer(inlineProperties).append(writer, inlineEntityInfo, inlineData, false);
+ } else {
+ jsonStreamWriter.unquotedValue("null");
+ }
+ }
+ } catch (final ODataApplicationException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ } else {
+ writeDeferredUri(navigationPropertyName);
+ }
+ } else {
+ writeDeferredUri(navigationPropertyName);
+ }
+ }
+ }
+
+ jsonStreamWriter.endObject();
+
+ if (isRootElement) {
+ jsonStreamWriter.endObject();
+ }
+
+ writer.flush();
+
+ } catch (final IOException 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);
+ }
+ }
+
+ private void writeDeferredUri(final String navigationPropertyName) throws IOException {
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.DEFERRED);
+ JsonLinkEntityProducer.appendUri(jsonStreamWriter, location + "/" + Encoder.encode(navigationPropertyName));
+ jsonStreamWriter.endObject();
+ }
+
+ public String getETag() {
+ return eTag;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonErrorDocumentProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonErrorDocumentProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonErrorDocumentProducer.java
new file mode 100644
index 0000000..a65d997
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonErrorDocumentProducer.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Locale;
+
+import org.apache.olingo.odata2.core.ep.util.FormatJson;
+import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter;
+
+/**
+ * @author SAP AG
+ */
+public class JsonErrorDocumentProducer {
+
+ public void writeErrorDocument(final Writer writer, final String errorCode, final String message, final Locale locale, final String innerError) throws IOException {
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.ERROR)
+ .beginObject()
+ .namedStringValue(FormatJson.CODE, errorCode).separator()
+ .name(FormatJson.MESSAGE)
+ .beginObject()
+ .namedStringValueRaw(FormatJson.LANG,
+ locale == null || locale.getLanguage() == null ? null :
+ locale.getLanguage() + (locale.getCountry() == null || locale.getCountry().isEmpty() ? "" : ("-" + locale.getCountry())))
+ .separator()
+ .namedStringValue(FormatJson.VALUE, message)
+ .endObject();
+ if (innerError != null) {
+ jsonStreamWriter.separator()
+ .namedStringValue(FormatJson.INNER_ERROR, innerError);
+ }
+ jsonStreamWriter.endObject()
+ .endObject();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonFeedEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonFeedEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonFeedEntityProducer.java
new file mode 100644
index 0000000..e406fe2
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonFeedEntityProducer.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.commons.InlineCount;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+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.
+ * @author SAP AG
+ */
+public class JsonFeedEntityProducer {
+
+ private final EntityProviderWriteProperties properties;
+
+ public JsonFeedEntityProducer(final EntityProviderWriteProperties properties) throws EntityProviderException {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final Writer writer, final EntityInfoAggregator entityInfo, final List<Map<String, Object>> data, final boolean isRootElement) throws EntityProviderException {
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+
+ try {
+ jsonStreamWriter.beginObject();
+
+ if (isRootElement) {
+ jsonStreamWriter.name(FormatJson.D)
+ .beginObject();
+ }
+
+ if (properties.getInlineCountType() == InlineCount.ALLPAGES) {
+ final int inlineCount = properties.getInlineCount() == null ? 0 : properties.getInlineCount();
+ jsonStreamWriter.namedStringValueRaw(FormatJson.COUNT, String.valueOf(inlineCount)).separator();
+ }
+
+ jsonStreamWriter.name(FormatJson.RESULTS)
+ .beginArray();
+ JsonEntryEntityProducer entryProducer = new JsonEntryEntityProducer(properties);
+ boolean first = true;
+ for (final Map<String, Object> entryData : data) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ entryProducer.append(writer, entityInfo, entryData, false);
+ }
+ jsonStreamWriter.endArray();
+
+ // Write "next" link.
+ // To be compatible with other implementations out there, the link is
+ // written directly after "__next" and not as "{"uri":"next link"}",
+ // deviating from the OData 2.0 specification.
+ if (properties.getNextLink() != null) {
+ jsonStreamWriter.separator()
+ .namedStringValue(FormatJson.NEXT, properties.getNextLink());
+ }
+
+ if (isRootElement) {
+ jsonStreamWriter.endObject();
+ }
+
+ jsonStreamWriter.endObject();
+ } catch (final IOException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinkEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinkEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinkEntityProducer.java
new file mode 100644
index 0000000..5edbe00
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinkEntityProducer.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+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 a link in JSON.
+ * @author SAP AG
+ */
+public class JsonLinkEntityProducer {
+
+ private final EntityProviderWriteProperties properties;
+
+ public JsonLinkEntityProducer(final EntityProviderWriteProperties properties) throws EntityProviderException {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final Writer writer, final EntityInfoAggregator entityInfo, final Map<String, Object> data) throws EntityProviderException {
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+
+ final String uri = (properties.getServiceRoot() == null ? "" : properties.getServiceRoot().toASCIIString())
+ + AtomEntryEntityProducer.createSelfLink(entityInfo, data, null);
+ try {
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.D);
+ appendUri(jsonStreamWriter, uri);
+ jsonStreamWriter.endObject();
+ } catch (final IOException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+
+ 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/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinksEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinksEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinksEntityProducer.java
new file mode 100644
index 0000000..1c2aa75
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonLinksEntityProducer.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.commons.InlineCount;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
+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 a link collection in JSON.
+ * @author SAP AG
+ */
+public class JsonLinksEntityProducer {
+
+ private final EntityProviderWriteProperties properties;
+
+ public JsonLinksEntityProducer(final EntityProviderWriteProperties properties) throws EntityProviderException {
+ this.properties = properties == null ? EntityProviderWriteProperties.serviceRoot(null).build() : properties;
+ }
+
+ public void append(final Writer writer, final EntityInfoAggregator entityInfo, final List<Map<String, Object>> data) throws EntityProviderException {
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+
+ try {
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.D);
+
+ if (properties.getInlineCountType() == InlineCount.ALLPAGES) {
+ final int inlineCount = properties.getInlineCount() == null ? 0 : properties.getInlineCount();
+ jsonStreamWriter.beginObject()
+ .namedStringValueRaw(FormatJson.COUNT, String.valueOf(inlineCount)).separator()
+ .name(FormatJson.RESULTS);
+ }
+
+ jsonStreamWriter.beginArray();
+ final String serviceRoot = properties.getServiceRoot().toASCIIString();
+ boolean first = true;
+ for (final Map<String, Object> entryData : data) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ JsonLinkEntityProducer.appendUri(jsonStreamWriter,
+ (serviceRoot == null ? "" : serviceRoot) + AtomEntryEntityProducer.createSelfLink(entityInfo, entryData, null));
+ }
+ jsonStreamWriter.endArray();
+
+ if (properties.getInlineCountType() == InlineCount.ALLPAGES) {
+ jsonStreamWriter.endObject();
+ }
+
+ jsonStreamWriter.endObject();
+ } catch (final IOException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonPropertyEntityProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonPropertyEntityProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonPropertyEntityProducer.java
new file mode 100644
index 0000000..81ec29f
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonPropertyEntityProducer.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+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.EdmLiteralKind;
+import org.apache.olingo.odata2.api.edm.EdmSimpleType;
+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.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.
+ * @author SAP AG
+ */
+public class JsonPropertyEntityProducer {
+
+ 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);
+
+ jsonStreamWriter.endObject()
+ .endObject();
+ } catch (final IOException 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 static void appendPropertyValue(final JsonStreamWriter jsonStreamWriter, final EntityPropertyInfo propertyInfo, final Object value) throws IOException, EdmException, EntityProviderException {
+ if (propertyInfo.isComplex()) {
+ if (value == null || value instanceof Map<?, ?>) {
+ jsonStreamWriter.beginObject();
+ appendPropertyMetadata(jsonStreamWriter, propertyInfo.getType());
+ for (final EntityPropertyInfo childPropertyInfo : ((EntityComplexPropertyInfo) propertyInfo).getPropertyInfos()) {
+ jsonStreamWriter.separator();
+ final String name = childPropertyInfo.getName();
+ jsonStreamWriter.name(name);
+ appendPropertyValue(jsonStreamWriter, childPropertyInfo, value == null ? null : ((Map<?, ?>) value).get(name));
+ }
+ jsonStreamWriter.endObject();
+ } else {
+ throw new EntityProviderException(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 Map ? ((Map<?, ?>) value).get(propertyInfo.getName()) : value;
+ final String valueAsString = type.valueToString(contentValue, EdmLiteralKind.JSON, propertyInfo.getFacets());
+ 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/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonServiceDocumentProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonServiceDocumentProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonServiceDocumentProducer.java
new file mode 100644
index 0000000..3dce13c
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/JsonServiceDocumentProducer.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.olingo.odata2.api.edm.Edm;
+import org.apache.olingo.odata2.api.edm.EdmEntitySetInfo;
+import org.apache.olingo.odata2.api.edm.EdmServiceMetadata;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.core.ep.util.FormatJson;
+import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter;
+
+/**
+ * Writes the OData service document in JSON.
+ * @author SAP AG
+ */
+public class JsonServiceDocumentProducer {
+
+ public static void writeServiceDocument(final Writer writer, final Edm edm) throws EntityProviderException {
+ final EdmServiceMetadata serviceMetadata = edm.getServiceMetadata();
+
+ JsonStreamWriter jsonStreamWriter = new JsonStreamWriter(writer);
+ try {
+ jsonStreamWriter.beginObject()
+ .name(FormatJson.D)
+ .beginObject()
+ .name(FormatJson.ENTITY_SETS)
+ .beginArray();
+
+ boolean first = true;
+ for (EdmEntitySetInfo info : serviceMetadata.getEntitySetInfos()) {
+ if (first) {
+ first = false;
+ } else {
+ jsonStreamWriter.separator();
+ }
+ jsonStreamWriter.stringValue(info.getEntitySetUri().toASCIIString());
+ }
+
+ jsonStreamWriter.endArray()
+ .endObject()
+ .endObject();
+ } catch (final IOException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ } catch (final ODataException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/TombstoneProducer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/TombstoneProducer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/TombstoneProducer.java
new file mode 100644
index 0000000..23f7ad7
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/producer/TombstoneProducer.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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.core.ep.producer;
+
+import java.util.Date;
+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.EdmFacets;
+import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
+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.api.ep.EntityProviderWriteProperties;
+import org.apache.olingo.odata2.api.ep.callback.TombstoneCallback;
+import org.apache.olingo.odata2.core.edm.EdmDateTimeOffset;
+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;
+
+/**
+ * @author SAP AG
+ */
+public class TombstoneProducer {
+
+ private String defaultDateString;
+
+ /**
+ * Appends tombstones to an already started feed.
+ * If the list is empty no elements will be appended.
+ * @param writer same as in feed
+ * @param eia same as in feed
+ * @param properties same as in feed
+ * @param deletedEntries data to be appended
+ * @throws EntityProviderException
+ */
+ public void appendTombstones(final XMLStreamWriter writer, final EntityInfoAggregator eia, final EntityProviderWriteProperties properties, final List<Map<String, Object>> deletedEntries) throws EntityProviderException {
+ try {
+ for (Map<String, Object> deletedEntry : deletedEntries) {
+ writer.writeStartElement(TombstoneCallback.NAMESPACE_TOMBSTONE, FormatXml.ATOM_TOMBSTONE_DELETED_ENTRY);
+
+ appendRefAttribute(writer, eia, properties, deletedEntry);
+ appendWhenAttribute(writer, eia, deletedEntry);
+
+ writer.writeEndElement();
+ }
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ } catch (EdmSimpleTypeException e) {
+ throw new EntityProviderException(EntityProviderException.COMMON, e);
+ }
+ }
+
+ private void appendWhenAttribute(final XMLStreamWriter writer, final EntityInfoAggregator eia, final Map<String, Object> deletedEntry) throws XMLStreamException, EdmSimpleTypeException {
+ Object updateDate = null;
+ EntityPropertyInfo updatedInfo = eia.getTargetPathInfo(EdmTargetPath.SYNDICATION_UPDATED);
+ if (updatedInfo != null) {
+ updateDate = deletedEntry.get(updatedInfo.getName());
+ }
+
+ if (updateDate == null) {
+ appendDefaultWhenAttribute(writer);
+ } else {
+ appendCustomWhenAttribute(writer, updateDate, updatedInfo);
+ }
+ }
+
+ private void appendCustomWhenAttribute(final XMLStreamWriter writer, final Object updateDate, final EntityPropertyInfo updatedInfo) throws XMLStreamException, EdmSimpleTypeException {
+ EdmFacets updateFacets = updatedInfo.getFacets();
+ writer.writeAttribute(FormatXml.ATOM_TOMBSTONE_WHEN, EdmDateTimeOffset.getInstance().valueToString(updateDate, EdmLiteralKind.DEFAULT, updateFacets));
+ }
+
+ private void appendRefAttribute(final XMLStreamWriter writer, final EntityInfoAggregator eia, final EntityProviderWriteProperties properties, final Map<String, Object> deletedEntry) throws XMLStreamException, EntityProviderException {
+ String ref = properties.getServiceRoot().toASCIIString() + AtomEntryEntityProducer.createSelfLink(eia, deletedEntry, null);
+ writer.writeAttribute(FormatXml.ATOM_TOMBSTONE_REF, ref);
+ }
+
+ private void appendDefaultWhenAttribute(final XMLStreamWriter writer) throws XMLStreamException, EdmSimpleTypeException {
+ if (defaultDateString == null) {
+ Object defaultDate = new Date();
+ defaultDateString = EdmDateTimeOffset.getInstance().valueToString(defaultDate, EdmLiteralKind.DEFAULT, null);
+ }
+ writer.writeAttribute(FormatXml.ATOM_TOMBSTONE_WHEN, defaultDateString);
+
+ }
+}