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:21 UTC
[16/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/consumer/XmlEntityConsumer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntityConsumer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntityConsumer.java
new file mode 100644
index 0000000..f65938b
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntityConsumer.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * 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.consumer;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.olingo.odata2.api.edm.EdmEntitySet;
+import org.apache.olingo.odata2.api.edm.EdmException;
+import org.apache.olingo.odata2.api.edm.EdmProperty;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
+import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties.EntityProviderReadPropertiesBuilder;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator;
+
+/**
+ * Xml entity (content type dependent) consumer for reading input (from <code>content</code>).
+ *
+ * @author SAP AG
+ */
+public class XmlEntityConsumer {
+
+ /** Default used charset for reader */
+ private static final String DEFAULT_CHARSET = "UTF-8";
+
+ public XmlEntityConsumer() throws EntityProviderException {
+ super();
+ }
+
+ public ODataFeed readFeed(final EdmEntitySet entitySet, final InputStream content, final EntityProviderReadProperties properties) throws EntityProviderException {
+ XMLStreamReader reader = null;
+ EntityProviderException cachedException = null;
+
+ try {
+ reader = createStaxReader(content);
+
+ EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet);
+ XmlFeedConsumer xfc = new XmlFeedConsumer();
+ return xfc.readFeed(reader, eia, properties);
+ } catch (EntityProviderException e) {
+ cachedException = e;
+ throw cachedException;
+ } catch (XMLStreamException e) {
+ cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ throw cachedException;
+ } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ if (cachedException != null) {
+ throw cachedException;
+ } else {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+ }
+ }
+
+ public ODataEntry readEntry(final EdmEntitySet entitySet, final InputStream content, final EntityProviderReadProperties properties) throws EntityProviderException {
+ XMLStreamReader reader = null;
+ EntityProviderException cachedException = null;
+
+ try {
+ XmlEntryConsumer xec = new XmlEntryConsumer();
+ reader = createStaxReader(content);
+
+ EntityInfoAggregator eia = EntityInfoAggregator.create(entitySet);
+ ODataEntry result = xec.readEntry(reader, eia, properties);
+ return result;
+ } catch (EntityProviderException e) {
+ cachedException = e;
+ throw cachedException;
+ } catch (XMLStreamException e) {
+ cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ throw cachedException;
+ } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ if (cachedException != null) {
+ throw cachedException;
+ } else {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+ }
+ }
+
+ public Map<String, Object> readProperty(final EdmProperty edmProperty, final InputStream content, final EntityProviderReadProperties properties) throws EntityProviderException {
+ XMLStreamReader reader = null;
+ EntityProviderException cachedException = null;
+ XmlPropertyConsumer xec = new XmlPropertyConsumer();
+
+ try {
+ reader = createStaxReader(content);
+ Map<String, Object> result = xec.readProperty(reader, edmProperty, properties.getMergeSemantic(), properties.getTypeMappings());
+ return result;
+ } catch (XMLStreamException e) {
+ cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ throw cachedException;
+ } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ if (cachedException != null) {
+ throw cachedException;
+ } else {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+ }
+ }
+
+ public Object readPropertyValue(final EdmProperty edmProperty, final InputStream content) throws EntityProviderException {
+ return readPropertyValue(edmProperty, content, null);
+ }
+
+ public Object readPropertyValue(final EdmProperty edmProperty, final InputStream content, final Class<?> typeMapping) throws EntityProviderException {
+ try {
+ final Map<String, Object> result;
+ EntityProviderReadPropertiesBuilder propertiesBuilder = EntityProviderReadProperties.init().mergeSemantic(false);
+ if (typeMapping == null) {
+ result = readProperty(edmProperty, content, propertiesBuilder.build());
+ } else {
+ Map<String, Object> typeMappings = new HashMap<String, Object>();
+ typeMappings.put(edmProperty.getName(), typeMapping);
+ result = readProperty(edmProperty, content, propertiesBuilder.addTypeMappings(typeMappings).build());
+ }
+ return result.get(edmProperty.getName());
+ } catch (EdmException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+
+ public String readLink(final EdmEntitySet entitySet, final Object content) throws EntityProviderException {
+ XMLStreamReader reader = null;
+ EntityProviderException cachedException = null;
+ XmlLinkConsumer xlc = new XmlLinkConsumer();
+
+ try {
+ reader = createStaxReader(content);
+ return xlc.readLink(reader, entitySet);
+ } catch (XMLStreamException e) {
+ cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ throw cachedException;
+ } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ if (cachedException != null) {
+ throw cachedException;
+ } else {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+ }
+ }
+
+ public List<String> readLinks(final EdmEntitySet entitySet, final Object content) throws EntityProviderException {
+ XMLStreamReader reader = null;
+ EntityProviderException cachedException = null;
+ XmlLinkConsumer xlc = new XmlLinkConsumer();
+
+ try {
+ reader = createStaxReader(content);
+ return xlc.readLinks(reader, entitySet);
+ } catch (XMLStreamException e) {
+ cachedException = new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ throw cachedException;
+ } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ if (cachedException != null) {
+ throw cachedException;
+ } else {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+ }
+ }
+
+ private XMLStreamReader createStaxReader(final Object content) throws XMLStreamException, EntityProviderException {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
+ factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
+
+ if (content == null) {
+ throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT
+ .addContent("Got not supported NULL object as content to de-serialize."));
+ }
+
+ if (content instanceof InputStream) {
+ XMLStreamReader streamReader = factory.createXMLStreamReader((InputStream) content, DEFAULT_CHARSET);
+ // verify charset encoding set in content is supported (if not set UTF-8 is used as defined in 'http://www.w3.org/TR/2008/REC-xml-20081126/')
+ String characterEncodingInContent = streamReader.getCharacterEncodingScheme();
+ if (characterEncodingInContent != null && !DEFAULT_CHARSET.equalsIgnoreCase(characterEncodingInContent)) {
+ throw new EntityProviderException(EntityProviderException.UNSUPPORTED_CHARACTER_ENCODING.addContent(characterEncodingInContent));
+ }
+ return streamReader;
+ }
+ throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT
+ .addContent("Found not supported content of class '" + content.getClass() + "' to de-serialize."));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntryConsumer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntryConsumer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntryConsumer.java
new file mode 100644
index 0000000..881d8c7
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlEntryConsumer.java
@@ -0,0 +1,684 @@
+/*******************************************************************************
+ * 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.consumer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.NamespaceContext;
+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.EdmEntitySet;
+import org.apache.olingo.odata2.api.edm.EdmException;
+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.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
+import org.apache.olingo.odata2.api.ep.callback.OnReadInlineContent;
+import org.apache.olingo.odata2.api.ep.callback.ReadEntryResult;
+import org.apache.olingo.odata2.api.ep.callback.ReadFeedResult;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+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.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.entry.EntryMetadataImpl;
+import org.apache.olingo.odata2.core.ep.entry.MediaMetadataImpl;
+import org.apache.olingo.odata2.core.ep.entry.ODataEntryImpl;
+import org.apache.olingo.odata2.core.ep.feed.FeedMetadataImpl;
+import org.apache.olingo.odata2.core.ep.feed.ODataFeedImpl;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+import org.apache.olingo.odata2.core.uri.ExpandSelectTreeNodeImpl;
+
+/**
+ * Atom/XML format reader/consumer for entries.
+ *
+ * {@link XmlEntryConsumer} instance can be reused for several {@link #readEntry(XMLStreamReader, EntityInfoAggregator, EntityProviderReadProperties)} calls
+ * but be aware that the instance and their <code>readEntry*</code> methods are <b>NOT THREAD SAFE</b>.
+ * @author SAP AG
+ */
+public class XmlEntryConsumer {
+
+ private Map<String, String> foundPrefix2NamespaceUri;
+ private ODataEntryImpl readEntryResult;
+ private Map<String, Object> properties;
+ private MediaMetadataImpl mediaMetadata;
+ private EntryMetadataImpl entryMetadata;
+ private ExpandSelectTreeNodeImpl expandSelectTree;
+ private EntityTypeMapping typeMappings;
+ private String currentHandledStartTagName;
+
+ public ODataEntry readEntry(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties) throws EntityProviderException {
+ try {
+ initialize(readProperties);
+
+ while (reader.hasNext() && !isEntryEndTag(reader)) {
+ reader.nextTag();
+ if (reader.isStartElement()) {
+ handleStartedTag(reader, eia, readProperties);
+ }
+ }
+
+ if (!readProperties.getMergeSemantic()) {
+ validateMandatoryPropertiesAvailable(eia, readEntryResult);
+ }
+
+ return readEntryResult;
+ } 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);
+ }
+ }
+
+ private boolean isEntryEndTag(final XMLStreamReader reader) {
+ return reader.isEndElement()
+ && Edm.NAMESPACE_ATOM_2005.equals(reader.getNamespaceURI())
+ && FormatXml.ATOM_ENTRY.equals(reader.getLocalName());
+ }
+
+ /**
+ * Initialize the {@link XmlEntryConsumer} to be ready for read of an entry.
+ *
+ * @param readProperties
+ * @throws EntityProviderException
+ */
+ private void initialize(final EntityProviderReadProperties readProperties) throws EntityProviderException {
+ properties = new HashMap<String, Object>();
+ mediaMetadata = new MediaMetadataImpl();
+ entryMetadata = new EntryMetadataImpl();
+ expandSelectTree = new ExpandSelectTreeNodeImpl();
+ foundPrefix2NamespaceUri = new HashMap<String, String>();
+
+ readEntryResult = new ODataEntryImpl(properties, mediaMetadata, entryMetadata, expandSelectTree);
+ typeMappings = EntityTypeMapping.create(readProperties.getTypeMappings());
+ foundPrefix2NamespaceUri.putAll(readProperties.getValidatedPrefixNamespaceUris());
+ }
+
+ /**
+ * Validates that all mandatory properties are found and set at the {@link #readEntryResult} entity.
+ * If a mandatory property is missing an {@link EntityProviderException} is thrown.
+ *
+ * @param eia entity info which contains the information which properties are mandatory
+ * @param entry entry for which the mandatory properties are validated
+ * @throws EntityProviderException if a mandatory property is missing
+ */
+ private void validateMandatoryPropertiesAvailable(final EntityInfoAggregator eia, final ODataEntryImpl entry) throws EntityProviderException {
+ Collection<EntityPropertyInfo> propertyInfos = new ArrayList<EntityPropertyInfo>(eia.getPropertyInfos());
+ propertyInfos.removeAll(eia.getKeyPropertyInfos());
+ Map<String, Object> data = entry.getProperties();
+
+ for (EntityPropertyInfo entityPropertyInfo : propertyInfos) {
+ boolean mandatory = entityPropertyInfo.isMandatory();
+ if (mandatory && !data.containsKey(entityPropertyInfo.getName())) {
+ throw new EntityProviderException(EntityProviderException.MISSING_PROPERTY.addContent(entityPropertyInfo.getName()));
+ }
+ }
+ }
+
+ private void handleStartedTag(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties)
+ throws EntityProviderException, XMLStreamException, EdmException {
+
+ currentHandledStartTagName = reader.getLocalName();
+
+ if (FormatXml.ATOM_ID.equals(currentHandledStartTagName)) {
+ readId(reader);
+ } else if (FormatXml.ATOM_ENTRY.equals(currentHandledStartTagName)) {
+ readEntry(reader);
+ } else if (FormatXml.ATOM_LINK.equals(currentHandledStartTagName)) {
+ readLink(reader, eia, readProperties);
+ } else if (FormatXml.ATOM_CONTENT.equals(currentHandledStartTagName)) {
+ readContent(reader, eia);
+ } else if (FormatXml.M_PROPERTIES.equals(currentHandledStartTagName)) {
+ readProperties(reader, eia);
+ } else if (!readProperties.getMergeSemantic()) {
+ readCustomElement(reader, currentHandledStartTagName, eia);
+ } else {
+ skipStartedTag(reader);
+ }
+ }
+
+ private void readCustomElement(final XMLStreamReader reader, final String tagName, final EntityInfoAggregator eia) throws EdmException, EntityProviderException, XMLStreamException {
+ EntityPropertyInfo targetPathInfo = eia.getTargetPathInfo(tagName);
+ NamespaceContext nsctx = reader.getNamespaceContext();
+
+ boolean skipTag = true;
+ if (!Edm.NAMESPACE_ATOM_2005.equals(reader.getName().getNamespaceURI())) {
+
+ if (targetPathInfo != null) {
+ final String customPrefix = targetPathInfo.getCustomMapping().getFcNsPrefix();
+ final String customNamespaceURI = targetPathInfo.getCustomMapping().getFcNsUri();
+
+ if (customPrefix != null && customNamespaceURI != null) {
+ String xmlPrefix = nsctx.getPrefix(customNamespaceURI);
+ String xmlNamespaceUri = reader.getNamespaceURI(customPrefix);
+
+ if (customNamespaceURI.equals(xmlNamespaceUri) && customPrefix.equals(xmlPrefix)) {
+ skipTag = false;
+ reader.require(XMLStreamConstants.START_ELEMENT, customNamespaceURI, tagName);
+ reader.next();
+ reader.require(XMLStreamConstants.CHARACTERS, null, null);
+ final String text = reader.getText();
+ reader.nextTag();
+ reader.require(XMLStreamConstants.END_ELEMENT, customNamespaceURI, tagName);
+
+ final EntityPropertyInfo propertyInfo = getValidatedPropertyInfo(eia, tagName);
+ final Class<?> typeMapping = typeMappings.getMappingClass(propertyInfo.getName());
+ final EdmSimpleType type = (EdmSimpleType) propertyInfo.getType();
+ final Object value = type.valueOfString(text, EdmLiteralKind.DEFAULT, propertyInfo.getFacets(),
+ typeMapping == null ? type.getDefaultType() : typeMapping);
+ properties.put(tagName, value);
+ }
+ }
+ } else {
+ throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(tagName));
+ }
+ }
+
+ if (skipTag) {
+ skipStartedTag(reader);
+ }
+ }
+
+ /**
+ * Skip the tag to which the {@link XMLStreamReader} currently points.
+ * Therefore it is read until an end element tag with current local name is found.
+ *
+ * @param reader
+ * @throws XMLStreamException
+ */
+ private void skipStartedTag(final XMLStreamReader reader) throws XMLStreamException {
+ final String name = reader.getLocalName();
+ int read = 1;
+ while (read > 0 && reader.hasNext()) {
+ reader.next();
+ if (reader.hasName() && name.equals(reader.getLocalName())) {
+ if (reader.isEndElement()) {
+ read--;
+ } else if (reader.isStartElement()) {
+ read++;
+ }
+ }
+ }
+ }
+
+ private void readEntry(final XMLStreamReader reader) throws EntityProviderException, XMLStreamException {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ENTRY);
+
+ extractNamespacesFromTag(reader);
+ final String etag = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_ETAG);
+ entryMetadata.setEtag(etag);
+ }
+
+ private void extractNamespacesFromTag(final XMLStreamReader reader) throws EntityProviderException {
+ // collect namespaces
+ int namespaceCount = reader.getNamespaceCount();
+ for (int i = 0; i < namespaceCount; i++) {
+ String namespacePrefix = reader.getNamespacePrefix(i);
+ String namespaceUri = reader.getNamespaceURI(i);
+
+ foundPrefix2NamespaceUri.put(namespacePrefix, namespaceUri);
+ }
+ }
+
+ private void checkAllMandatoryNamespacesAvailable() throws EntityProviderException {
+ if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_D_2007_08)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_D_2007_08));
+ } else if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_M_2007_08)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_M_2007_08));
+ } else if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_ATOM_2005)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_ATOM_2005));
+ }
+ }
+
+ /**
+ *
+ * @param reader
+ * @param eia
+ * @param readProperties
+ * @throws EntityProviderException
+ * @throws XMLStreamException
+ * @throws EdmException
+ */
+ private void readLink(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties) throws EntityProviderException, XMLStreamException, EdmException {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_LINK);
+
+ final String rel = reader.getAttributeValue(null, FormatXml.ATOM_REL);
+ final String uri = reader.getAttributeValue(null, FormatXml.ATOM_HREF);
+ final String type = reader.getAttributeValue(null, FormatXml.ATOM_TYPE);
+ final String etag = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_ETAG);
+
+ // read to next tag to check if <link> contains any further tags
+ reader.nextTag();
+
+ if (reader.isEndElement()) {
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_LINK);
+
+ if (rel == null || uri == null) {
+ throw new EntityProviderException(EntityProviderException.MISSING_ATTRIBUTE.addContent(
+ FormatXml.ATOM_HREF + "' and/or '" + FormatXml.ATOM_REL).addContent(FormatXml.ATOM_LINK));
+ } else if (rel.startsWith(Edm.NAMESPACE_REL_2007_08)) {
+ final String navigationPropertyName = rel.substring(Edm.NAMESPACE_REL_2007_08.length());
+ entryMetadata.putAssociationUri(navigationPropertyName, uri);
+ } else if (rel.equals(Edm.LINK_REL_EDIT_MEDIA)) {
+ mediaMetadata.setEditLink(uri);
+ mediaMetadata.setEtag(etag);
+ }
+ } else {
+ if (rel != null && rel.startsWith(Edm.NAMESPACE_REL_2007_08)) {
+ readInlineContent(reader, eia, readProperties, type, rel);
+ }
+ }
+ }
+
+ /**
+ * Inline content was found and {@link XMLStreamReader} already points to <m:inline> tag.
+ *
+ * @param reader
+ * @param eia
+ * @param readProperties
+ * @param atomLinkType the atom <code>type</code> attribute value of the <code>link</code> tag
+ * @param atomLinkRel the atom <code>rel</code> attribute value of the <code>link</code> tag
+ * @throws XMLStreamException
+ * @throws EntityProviderException
+ * @throws EdmException
+ */
+ private void readInlineContent(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties,
+ final String atomLinkType, final String atomLinkRel)
+ throws XMLStreamException, EntityProviderException, EdmException {
+
+ //
+ String navigationPropertyName = atomLinkRel.substring(Edm.NAMESPACE_REL_2007_08.length());
+
+ EdmNavigationProperty navigationProperty = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
+ EdmEntitySet entitySet = eia.getEntitySet().getRelatedEntitySet(navigationProperty);
+ EntityInfoAggregator inlineEia = EntityInfoAggregator.create(entitySet);
+
+ final EntityProviderReadProperties inlineProperties = createInlineProperties(readProperties, navigationProperty);
+
+ // validations
+ boolean isFeed = isInlineFeedValidated(reader, eia, atomLinkType, navigationPropertyName);
+
+ List<ODataEntry> inlineEntries = new ArrayList<ODataEntry>();
+
+ while (!(reader.isEndElement() && Edm.NAMESPACE_M_2007_08.equals(reader.getNamespaceURI()) && FormatXml.M_INLINE.equals(reader.getLocalName()))) {
+
+ if (reader.isStartElement() && Edm.NAMESPACE_ATOM_2005.equals(reader.getNamespaceURI()) && FormatXml.ATOM_ENTRY.equals(reader.getLocalName())) {
+ XmlEntryConsumer xec = new XmlEntryConsumer();
+ ODataEntry inlineEntry = xec.readEntry(reader, inlineEia, inlineProperties);
+ inlineEntries.add(inlineEntry);
+ }
+ // next tag
+ reader.next();
+ }
+
+ updateExpandSelectTree(navigationPropertyName, inlineEntries);
+ updateReadProperties(readProperties, navigationPropertyName, navigationProperty, isFeed, inlineEntries);
+
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+ }
+
+ /**
+ * Updates the read properties ({@link #properties}) for this {@link ReadEntryResult} ({@link #readEntryResult}).
+ *
+ * @param readProperties
+ * @param navigationPropertyName
+ * @param navigationProperty
+ * @param isFeed
+ * @param inlineEntries
+ * @throws EntityProviderException
+ */
+ private void updateReadProperties(final EntityProviderReadProperties readProperties, final String navigationPropertyName,
+ final EdmNavigationProperty navigationProperty, final boolean isFeed, final List<ODataEntry> inlineEntries) throws EntityProviderException {
+ Object entry = extractODataEntity(isFeed, inlineEntries);
+ OnReadInlineContent callback = readProperties.getCallback();
+ if (callback == null) {
+ readEntryResult.setContainsInlineEntry(true);
+ properties.put(navigationPropertyName, entry);
+ } else {
+ doCallback(readProperties, navigationProperty, callback, isFeed, entry);
+ }
+ }
+
+ /**
+ * Updates the expand select tree ({@link #expandSelectTree}) for this {@link ReadEntryResult} ({@link #readEntryResult}).
+ *
+ * @param navigationPropertyName
+ * @param inlineEntries
+ * @throws EntityProviderException
+ */
+ private void updateExpandSelectTree(final String navigationPropertyName, final List<ODataEntry> inlineEntries) throws EntityProviderException {
+ expandSelectTree.setExpanded();
+ ExpandSelectTreeNodeImpl subNode = getExpandSelectTreeNode(inlineEntries);
+ expandSelectTree.putLink(navigationPropertyName, subNode);
+ }
+
+ /**
+ * Get the {@link ExpandSelectTreeNodeImpl} from the <code>inlineEntries</code> or if none exists create a new
+ * {@link ExpandSelectTreeNodeImpl}.
+ *
+ * @param inlineEntries entries which are checked for existing {@link ExpandSelectTreeNodeImpl}
+ * @return {@link ExpandSelectTreeNodeImpl} from the <code>inlineEntries</code> or if none exists create a new {@link ExpandSelectTreeNodeImpl}.
+ * @throws EntityProviderException if an unsupported {@link ExpandSelectTreeNode} implementation was found.
+ */
+ private ExpandSelectTreeNodeImpl getExpandSelectTreeNode(final List<ODataEntry> inlineEntries) throws EntityProviderException {
+ if (inlineEntries.isEmpty()) {
+ return new ExpandSelectTreeNodeImpl();
+ } else {
+ ExpandSelectTreeNode inlinedEntryEstNode = inlineEntries.get(0).getExpandSelectTree();
+ if (inlinedEntryEstNode instanceof ExpandSelectTreeNodeImpl) {
+ return (ExpandSelectTreeNodeImpl) inlinedEntryEstNode;
+ } else {
+ throw new EntityProviderException(EntityProviderException.ILLEGAL_ARGUMENT
+ .addContent("Unsupported implementation for " + ExpandSelectTreeNode.class + " found."));
+ }
+ }
+ }
+
+ /**
+ * Get a list of {@link ODataEntry}, an empty list, a single {@link ODataEntry} or <code>NULL</code> based on
+ * <code>isFeed</code> value and <code>inlineEntries</code> content.
+ *
+ * @param isFeed
+ * @param inlineEntries
+ * @return
+ */
+ private Object extractODataEntity(final boolean isFeed, final List<ODataEntry> inlineEntries) {
+ if (isFeed) {
+ //TODO: fill metadata correctly with inline count and inline next link. Both are currently ignored.
+ return new ODataFeedImpl(inlineEntries, new FeedMetadataImpl());
+ } else if (!inlineEntries.isEmpty()) {
+ return inlineEntries.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Do the callback based on given parameters.
+ *
+ * @param readProperties
+ * @param navigationProperty
+ * @param callback
+ * @param isFeed
+ * @param entry
+ * @throws EntityProviderException
+ */
+ private void doCallback(final EntityProviderReadProperties readProperties, final EdmNavigationProperty navigationProperty,
+ final OnReadInlineContent callback, final boolean isFeed, final Object content) throws EntityProviderException {
+
+ try {
+ if (isFeed) {
+ ReadFeedResult callbackInfo = new ReadFeedResult(readProperties, navigationProperty, (ODataFeed) content);
+ callback.handleReadFeed(callbackInfo);
+ } else {
+ ReadEntryResult callbackInfo = new ReadEntryResult(readProperties, navigationProperty, (ODataEntry) content);
+ callback.handleReadEntry(callbackInfo);
+ }
+ } catch (ODataApplicationException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+
+ /**
+ * Create {@link EntityProviderReadProperties} which can be used for reading of inline properties/entrys of navigation links within
+ * this current read entry.
+ *
+ * @param readProperties
+ * @param navigationProperty
+ * @return
+ * @throws EntityProviderException
+ */
+ private EntityProviderReadProperties createInlineProperties(final EntityProviderReadProperties readProperties, final EdmNavigationProperty navigationProperty) throws EntityProviderException {
+ final OnReadInlineContent callback = readProperties.getCallback();
+
+ EntityProviderReadProperties currentReadProperties = EntityProviderReadProperties.initFrom(readProperties).addValidatedPrefixes(foundPrefix2NamespaceUri).build();
+ if (callback == null) {
+ return currentReadProperties;
+ } else {
+ try {
+ return callback.receiveReadProperties(currentReadProperties, navigationProperty);
+ } catch (ODataApplicationException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Inline content was found and {@link XMLStreamReader} already points to <code><m:inline> tag</code>.
+ * <br/>
+ * <b>ATTENTION</b>: If {@link XMLStreamReader} does not point to the <code><m:inline> tag</code> an exception is thrown.
+ * </p>
+ * <p>
+ * Check whether it is an inline <code>Feed</code> or <code>Entry</code> and validate that...
+ * <ul>
+ * <li>...{@link FormatXml#M_INLINE} tag is correctly set.</li>
+ * <li>...based on {@link EdmMultiplicity} of {@link EdmNavigationProperty} all tags are correctly set.</li>
+ * <li>...{@link FormatXml#ATOM_TYPE} tag is correctly set and according to {@link FormatXml#ATOM_ENTRY} or {@link FormatXml#ATOM_FEED} to following tags are available.</li>
+ * </ul>
+ *
+ * For the case that one of above validations fail an {@link EntityProviderException} is thrown.
+ * If validation was successful <code>true</code> is returned for <code>Feed</code> and <code>false</code> for <code>Entry</code>
+ * multiplicity.
+ * </p>
+ *
+ * @param reader xml content reader which already points to <code><m:inline> tag</code>
+ * @param eia all necessary information about the entity
+ * @param type the atom type attribute value of the <code>link</code> tag
+ * @param navigationPropertyName the navigation property name of the entity
+ * @return <code>true</code> for <code>Feed</code> and <code>false</code> for <code>Entry</code>
+ * @throws EntityProviderException is thrown if at least one validation fails.
+ * @throws EdmException if edm access fails
+ */
+ private boolean isInlineFeedValidated(final XMLStreamReader reader, final EntityInfoAggregator eia, final String type, final String navigationPropertyName) throws EntityProviderException, EdmException {
+ boolean isFeed = false;
+ try {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+ //
+ ContentType cType = ContentType.parse(type);
+ if (cType == null) {
+ throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("xml data"));
+ }
+
+ EdmNavigationProperty navigationProperty = (EdmNavigationProperty) eia.getEntityType().getProperty(navigationPropertyName);
+ EdmMultiplicity navigationMultiplicity = navigationProperty.getMultiplicity();
+
+ switch (navigationMultiplicity) {
+ case MANY:
+ validateFeedTags(reader, cType);
+ isFeed = true;
+ break;
+ case ONE:
+ case ZERO_TO_ONE:
+ validateEntryTags(reader, cType);
+ break;
+ }
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("xml data"), e);
+ }
+ return isFeed;
+ }
+
+ private void validateEntryTags(final XMLStreamReader reader, final ContentType cType) throws XMLStreamException, EntityProviderException {
+ if (FormatXml.ATOM_ENTRY.equals(cType.getParameters().get(FormatXml.ATOM_TYPE))) {
+ int next = reader.nextTag();
+ if (XMLStreamConstants.START_ELEMENT == next) {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ENTRY);
+ } else {
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+ }
+ } else {
+ throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("entry"));
+ }
+ }
+
+ private void validateFeedTags(final XMLStreamReader reader, final ContentType cType) throws XMLStreamException, EntityProviderException {
+ if (FormatXml.ATOM_FEED.equals(cType.getParameters().get(FormatXml.ATOM_TYPE))) {
+ int next = reader.nextTag();
+ if (XMLStreamConstants.START_ELEMENT == next) {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_FEED);
+ } else {
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_INLINE);
+ }
+ } else {
+ throw new EntityProviderException(EntityProviderException.INVALID_INLINE_CONTENT.addContent("feed"));
+ }
+ }
+
+ private void readContent(final XMLStreamReader reader, final EntityInfoAggregator eia) throws EntityProviderException, XMLStreamException, EdmException {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_CONTENT);
+
+ extractNamespacesFromTag(reader);
+
+ checkAllMandatoryNamespacesAvailable();
+
+ final String contentType = reader.getAttributeValue(null, FormatXml.ATOM_TYPE);
+ final String sourceLink = reader.getAttributeValue(null, FormatXml.ATOM_SRC);
+
+ reader.nextTag();
+
+ if (reader.isStartElement() && reader.getLocalName().equals(FormatXml.M_PROPERTIES)) {
+ readProperties(reader, eia);
+ } else if (reader.isEndElement()) {
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_CONTENT);
+ } else {
+ throw new EntityProviderException(EntityProviderException.INVALID_STATE
+ .addContent("Expected closing 'content' or starting 'properties' but found '" + reader.getLocalName() + "'."));
+ }
+
+ mediaMetadata.setContentType(contentType);
+ mediaMetadata.setSourceLink(sourceLink);
+ }
+
+ private void readId(final XMLStreamReader reader) throws EntityProviderException, XMLStreamException {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ID);
+ reader.next();
+ if (reader.isCharacters()) {
+ entryMetadata.setId(reader.getText());
+ }
+ reader.nextTag();
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_ID);
+ }
+
+ private void readProperties(final XMLStreamReader reader, final EntityInfoAggregator entitySet) throws XMLStreamException, EdmException, EntityProviderException {
+ // validate namespace
+ checkAllMandatoryNamespacesAvailable();
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_PROPERTIES);
+ if (entitySet.getEntityType().hasStream()) {
+ // external properties
+ checkCurrentHandledStartTag(FormatXml.M_PROPERTIES);
+ } else {
+ // inline properties
+ checkCurrentHandledStartTag(FormatXml.ATOM_CONTENT);
+ }
+
+ EntityPropertyInfo property;
+ XmlPropertyConsumer xpc = new XmlPropertyConsumer();
+
+ String closeTag = null;
+ boolean run = true;
+ reader.next();
+
+ while (run) {
+ if (reader.isStartElement() && closeTag == null) {
+ closeTag = reader.getLocalName();
+ if (isEdmNamespaceProperty(reader)) {
+ if (properties.containsKey(closeTag)) {
+ throw new EntityProviderException(EntityProviderException.DOUBLE_PROPERTY.addContent(closeTag));
+ }
+ property = getValidatedPropertyInfo(entitySet, closeTag);
+ final Object value = xpc.readStartedElement(reader, property, typeMappings);
+ properties.put(closeTag, value);
+ closeTag = null;
+ }
+ } else if (reader.isEndElement()) {
+ if (reader.getLocalName().equals(closeTag)) {
+ closeTag = null;
+ } else if (Edm.NAMESPACE_M_2007_08.equals(reader.getNamespaceURI()) && FormatXml.M_PROPERTIES.equals(reader.getLocalName())) {
+ run = false;
+ }
+ }
+ reader.next();
+ }
+ }
+
+ /**
+ * Check if the {@link #currentHandledStartTagName} is the same as the <code>expectedTagName</code>.
+ * If tag name is not as expected or if {@link #currentHandledStartTagName} is not set an {@link EntityProviderException} is thrown.
+ *
+ * @param expectedTagName expected name for {@link #currentHandledStartTagName}
+ * @throws EntityProviderException if tag name is not as expected or if {@link #currentHandledStartTagName} is <code>NULL</code>.
+ */
+ private void checkCurrentHandledStartTag(final String expectedTagName) throws EntityProviderException {
+ if (currentHandledStartTagName == null) {
+ throw new EntityProviderException(EntityProviderException.INVALID_STATE.addContent("No current handled start tag name set."));
+ } else if (!currentHandledStartTagName.equals(expectedTagName)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_PARENT_TAG.addContent(expectedTagName).addContent(currentHandledStartTagName));
+ }
+ }
+
+ /**
+ * Checks if property of currently read tag in {@link XMLStreamReader} is defined in
+ * <code>edm properties namespace</code> {@value Edm#NAMESPACE_D_2007_08}.
+ *
+ * If no namespace uri definition is found for namespace prefix of property (<code>tag</code>) an exception is thrown.
+ *
+ * @param reader {@link XMLStreamReader} with position at to checked tag
+ * @return <code>true</code> if property is in <code>edm properties namespace</code>, otherwise <code>false</code>.
+ * @throws EntityProviderException If no namespace uri definition is found for namespace prefix of property (<code>tag</code>).
+ */
+ private boolean isEdmNamespaceProperty(final XMLStreamReader reader) throws EntityProviderException {
+ final String nsUri = reader.getNamespaceURI();
+ if (nsUri == null) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(reader.getLocalName()));
+ } else {
+ return Edm.NAMESPACE_D_2007_08.equals(nsUri);
+ }
+ }
+
+ /**
+ * Get validated {@link EntityPropertyInfo} for property with given <code>name</code>.
+ * If validation fails an {@link EntityProviderException} is thrown.
+ *
+ * Currently this is the case if no {@link EntityPropertyInfo} if found for given <code>name</code>.
+ *
+ * @param entitySet
+ * @param name
+ * @return valid {@link EntityPropertyInfo} (which is never <code>NULL</code>).
+ * @throws EntityProviderException
+ */
+ private EntityPropertyInfo getValidatedPropertyInfo(final EntityInfoAggregator entitySet, final String name) throws EntityProviderException {
+ EntityPropertyInfo info = entitySet.getPropertyInfo(name);
+ if (info == null) {
+ throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(name));
+ }
+ return info;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlFeedConsumer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlFeedConsumer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlFeedConsumer.java
new file mode 100644
index 0000000..b15c1f0
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlFeedConsumer.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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.consumer;
+
+import java.util.ArrayList;
+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.ep.EntityProviderException;
+import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+import org.apache.olingo.odata2.core.ep.aggregator.EntityInfoAggregator;
+import org.apache.olingo.odata2.core.ep.feed.FeedMetadataImpl;
+import org.apache.olingo.odata2.core.ep.feed.ODataFeedImpl;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+
+/**
+ * Atom/XML format reader/consumer for feeds.
+ *
+ * {@link XmlFeedConsumer} instance use {@link XmlEntryConsumer#readEntry(XMLStreamReader, EntityInfoAggregator, EntityProviderReadProperties)}
+ * for read/consume of several entries.
+ *
+ * @author SAP AG
+ */
+public class XmlFeedConsumer {
+
+ /**
+ *
+ * @param reader
+ * @param eia
+ * @param readProperties
+ * @return {@link ODataFeed} object
+ * @throws EntityProviderException
+ */
+ public ODataFeed readFeed(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties readProperties) throws EntityProviderException {
+ try {
+ // read xml tag
+ reader.require(XMLStreamConstants.START_DOCUMENT, null, null);
+ reader.nextTag();
+
+ // read feed tag
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_FEED);
+ Map<String, String> foundPrefix2NamespaceUri = extractNamespacesFromTag(reader);
+ foundPrefix2NamespaceUri.putAll(readProperties.getValidatedPrefixNamespaceUris());
+ checkAllMandatoryNamespacesAvailable(foundPrefix2NamespaceUri);
+ EntityProviderReadProperties entryReadProperties =
+ EntityProviderReadProperties.initFrom(readProperties).addValidatedPrefixes(foundPrefix2NamespaceUri).build();
+
+ // read feed data (metadata and entries)
+ return readFeedData(reader, eia, entryReadProperties);
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+
+ /**
+ * Read all feed specific data (like <code>inline count</code> and <code>next link</code>) as well as all feed entries (<code>entry</code>).
+ *
+ * @param reader
+ * @param eia
+ * @param entryReadProperties
+ * @return
+ * @throws XMLStreamException
+ * @throws EntityProviderException
+ */
+ private ODataFeed readFeedData(final XMLStreamReader reader, final EntityInfoAggregator eia, final EntityProviderReadProperties entryReadProperties) throws XMLStreamException, EntityProviderException {
+ FeedMetadataImpl metadata = new FeedMetadataImpl();
+ XmlEntryConsumer xec = new XmlEntryConsumer();
+ List<ODataEntry> results = new ArrayList<ODataEntry>();
+
+ while (reader.hasNext() && !isFeedEndTag(reader)) {
+ if (FormatXml.ATOM_ENTRY.equals(reader.getLocalName())) {
+ ODataEntry entry = xec.readEntry(reader, eia, entryReadProperties);
+ results.add(entry);
+ } else if (FormatXml.M_COUNT.equals(reader.getLocalName())) {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_M_2007_08, FormatXml.M_COUNT);
+
+ reader.next();
+ if (reader.hasText()) {
+ String inlineCountString = reader.getText();
+ try {
+ int inlineCountNumber = Integer.valueOf(inlineCountString);
+ if (inlineCountNumber >= 0) {
+ metadata.setInlineCount(inlineCountNumber);
+ } else {
+ throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID.addContent(inlineCountNumber));
+ }
+ } catch (NumberFormatException e) {
+ throw new EntityProviderException(EntityProviderException.INLINECOUNT_INVALID.addContent(""), e);
+ }
+ }
+ } else if (FormatXml.ATOM_LINK.equals(reader.getLocalName())) {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_ATOM_2005, FormatXml.ATOM_LINK);
+
+ final String rel = reader.getAttributeValue(null, FormatXml.ATOM_REL);
+ if (FormatXml.ATOM_NEXT_LINK.equals(rel)) {
+ final String uri = reader.getAttributeValue(null, FormatXml.ATOM_HREF);
+ metadata.setNextLink(uri);
+ } else if (FormatXml.ATOM_DELTA_LINK.equals(rel)) {
+ final String uri = reader.getAttributeValue(null, FormatXml.ATOM_HREF);
+ metadata.setDeltaLink(uri);
+ }
+
+ reader.next();
+ } else {
+ reader.next();
+ }
+ readTillNextStartTag(reader);
+ }
+ return new ODataFeedImpl(results, metadata);
+ }
+
+ private void readTillNextStartTag(final XMLStreamReader reader) throws XMLStreamException {
+ while (reader.hasNext() && !reader.isStartElement()) {
+ reader.next();
+ }
+ }
+
+ private boolean isFeedEndTag(final XMLStreamReader reader) {
+ return reader.isEndElement()
+ && Edm.NAMESPACE_ATOM_2005.equals(reader.getNamespaceURI())
+ && FormatXml.ATOM_FEED.equals(reader.getLocalName());
+ }
+
+ /**
+ *
+ * @param reader
+ * @return
+ * @throws EntityProviderException
+ */
+ private Map<String, String> extractNamespacesFromTag(final XMLStreamReader reader) throws EntityProviderException {
+ // collect namespaces
+ Map<String, String> foundPrefix2NamespaceUri = new HashMap<String, String>();
+ int namespaceCount = reader.getNamespaceCount();
+ for (int i = 0; i < namespaceCount; i++) {
+ String namespacePrefix = reader.getNamespacePrefix(i);
+ String namespaceUri = reader.getNamespaceURI(i);
+
+ foundPrefix2NamespaceUri.put(namespacePrefix, namespaceUri);
+ }
+ return foundPrefix2NamespaceUri;
+ }
+
+ /**
+ *
+ * @param foundPrefix2NamespaceUri
+ * @throws EntityProviderException
+ */
+ private void checkAllMandatoryNamespacesAvailable(final Map<String, String> foundPrefix2NamespaceUri) throws EntityProviderException {
+ if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_D_2007_08)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_D_2007_08));
+ } else if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_M_2007_08)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_M_2007_08));
+ } else if (!foundPrefix2NamespaceUri.containsValue(Edm.NAMESPACE_ATOM_2005)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_NAMESPACE.addContent(Edm.NAMESPACE_ATOM_2005));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlLinkConsumer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlLinkConsumer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlLinkConsumer.java
new file mode 100644
index 0000000..d419c5b
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlLinkConsumer.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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.consumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.EdmEntitySet;
+import org.apache.olingo.odata2.api.ep.EntityProviderException;
+import org.apache.olingo.odata2.core.ep.util.FormatXml;
+
+/**
+ * @author SAP AG
+ */
+public class XmlLinkConsumer {
+
+ /**
+ * Reads single link with format {@code <uri>http://somelink</uri>}.
+ * @param reader
+ * @param entitySet
+ * @return link as string object
+ * @throws EntityProviderException
+ */
+ public String readLink(final XMLStreamReader reader, final EdmEntitySet entitySet) throws EntityProviderException {
+ try {
+ reader.next();
+ return readLink(reader);
+ } catch (final XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ }
+
+ private String readLink(final XMLStreamReader reader) throws XMLStreamException {
+ return readTag(reader, Edm.NAMESPACE_D_2007_08, FormatXml.D_URI);
+ }
+
+ private String readTag(final XMLStreamReader reader, final String namespaceURI, final String localName) throws XMLStreamException {
+ reader.require(XMLStreamConstants.START_ELEMENT, namespaceURI, localName);
+
+ reader.next();
+ reader.require(XMLStreamConstants.CHARACTERS, null, null);
+ final String result = reader.getText();
+
+ reader.nextTag();
+ reader.require(XMLStreamConstants.END_ELEMENT, namespaceURI, localName);
+
+ return result;
+ }
+
+ /**
+ * Reads multiple links with format
+ * <pre>
+ * {@code
+ * <links>
+ * <uri>http://somelink</uri>
+ * <uri>http://anotherLink</uri>
+ * <uri>http://somelink/yetAnotherLink</uri>
+ * </links>
+ * }
+ * </pre>
+ * @param reader
+ * @param entitySet
+ * @return list of string based links
+ * @throws EntityProviderException
+ */
+ public List<String> readLinks(final XMLStreamReader reader, final EdmEntitySet entitySet) throws EntityProviderException {
+ try {
+ List<String> links = new ArrayList<String>();
+
+ reader.next();
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_D_2007_08, FormatXml.D_LINKS);
+
+ reader.nextTag();
+ while (!reader.isEndElement()) {
+ if (reader.getLocalName().equals(FormatXml.M_COUNT)) {
+ readTag(reader, Edm.NAMESPACE_M_2007_08, FormatXml.M_COUNT);
+ } else {
+ final String link = readLink(reader);
+ links.add(link);
+ }
+ reader.nextTag();
+ }
+
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_D_2007_08, FormatXml.D_LINKS);
+
+ return links;
+ } catch (final XMLStreamException 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/consumer/XmlPropertyConsumer.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlPropertyConsumer.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlPropertyConsumer.java
new file mode 100644
index 0000000..d44c6c5
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/consumer/XmlPropertyConsumer.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * 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.consumer;
+
+import java.util.HashMap;
+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.EdmFacets;
+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.ep.EntityProviderException;
+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;
+
+/**
+ * @author SAP AG
+ */
+public class XmlPropertyConsumer {
+
+ public static final String TRUE = "true";
+
+ public Map<String, Object> readProperty(final XMLStreamReader reader, final EdmProperty property, final boolean merge) throws EntityProviderException {
+ return readProperty(reader, property, merge, null);
+ }
+
+ public Map<String, Object> readProperty(final XMLStreamReader reader, final EdmProperty property, final boolean merge, final Map<String, Object> typeMappings) throws EntityProviderException {
+ EntityPropertyInfo eia = EntityInfoAggregator.create(property);
+
+ try {
+ reader.next();
+
+ Object value = readStartedElement(reader, eia, EntityTypeMapping.create(typeMappings));
+
+ if (eia.isComplex() && merge) {
+ mergeWithDefaultValues(value, eia);
+ }
+
+ Map<String, Object> result = new HashMap<String, Object>();
+ result.put(eia.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);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void mergeWithDefaultValues(final Object value, final EntityPropertyInfo epi) throws EntityProviderException {
+ if (!(value instanceof Map)) {
+ throw new EntityProviderException(EntityProviderException.COMMON);
+ }
+ if (!epi.isComplex()) {
+ throw new EntityProviderException(EntityProviderException.COMMON);
+ }
+
+ mergeComplexWithDefaultValues((Map<String, Object>) value, (EntityComplexPropertyInfo) epi);
+ }
+
+ private void mergeComplexWithDefaultValues(final Map<String, Object> complexValue, final EntityComplexPropertyInfo ecpi) {
+ for (EntityPropertyInfo info : ecpi.getPropertyInfos()) {
+ Object obj = complexValue.get(info.getName());
+ if (obj == null) {
+ if (info.isComplex()) {
+ Map<String, Object> defaultValue = new HashMap<String, Object>();
+ mergeComplexWithDefaultValues(defaultValue, ecpi);
+ complexValue.put(info.getName(), defaultValue);
+ } else {
+ EdmFacets facets = info.getFacets();
+ if (facets != null) {
+ complexValue.put(info.getName(), facets.getDefaultValue());
+ }
+ }
+ }
+ }
+ }
+
+ protected Object readStartedElement(final XMLStreamReader reader, final EntityPropertyInfo propertyInfo, final EntityTypeMapping typeMappings) throws EntityProviderException, EdmException {
+ Map<String, Object> name2Value = new HashMap<String, Object>();
+ Object result = null;
+
+ try {
+ reader.require(XMLStreamConstants.START_ELEMENT, Edm.NAMESPACE_D_2007_08, propertyInfo.getName());
+ final String nullAttribute = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_NULL);
+
+ if (TRUE.equals(nullAttribute)) {
+ reader.nextTag();
+ } else if (propertyInfo.isComplex()) {
+ final String typeAttribute = reader.getAttributeValue(Edm.NAMESPACE_M_2007_08, FormatXml.M_TYPE);
+ String expectedTypeAttributeValue = propertyInfo.getType().getNamespace() + Edm.DELIMITER + propertyInfo.getType().getName();
+ if (typeAttribute != null && !expectedTypeAttributeValue.equals(typeAttribute)) {
+ throw new EntityProviderException(EntityProviderException.INVALID_COMPLEX_TYPE.addContent(expectedTypeAttributeValue).addContent(typeAttribute));
+ }
+
+ reader.nextTag();
+ while (reader.hasNext() && !reader.isEndElement()) {
+ String childName = reader.getLocalName();
+ EntityPropertyInfo childProperty = getChildProperty(childName, propertyInfo);
+ if (childProperty == null) {
+ throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(childName));
+ }
+ Object value = readStartedElement(reader, childProperty, typeMappings.getEntityTypeMapping(propertyInfo.getName()));
+ name2Value.put(childName, value);
+ reader.nextTag();
+ }
+ } else {
+ Class<?> mapping = typeMappings.getMappingClass(propertyInfo.getName());
+ result = convert(propertyInfo, reader.getElementText(), mapping);
+ }
+ reader.require(XMLStreamConstants.END_ELEMENT, Edm.NAMESPACE_D_2007_08, propertyInfo.getName());
+
+ // if reading finished check which result must be returned
+ if (result != null) {
+ return result;
+ } else if (!name2Value.isEmpty()) {
+ return name2Value;
+ }
+ } catch (XMLStreamException e) {
+ throw new EntityProviderException(EntityProviderException.EXCEPTION_OCCURRED.addContent(e.getClass().getSimpleName()), e);
+ }
+ return null;
+ }
+
+ private EntityPropertyInfo getChildProperty(final String childPropertyName, final EntityPropertyInfo property) throws EdmException, EntityProviderException {
+ if (property.isComplex()) {
+ EntityComplexPropertyInfo complex = (EntityComplexPropertyInfo) property;
+ return complex.getPropertyInfo(childPropertyName);
+ }
+ throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(
+ "Expected complex property but found simple for property with name '" + property.getName() + "'"));
+ }
+
+ private Object convert(final EntityPropertyInfo property, final String value, final Class<?> typeMapping) throws EdmException, EntityProviderException {
+ if (!property.isComplex()) {
+ EdmSimpleType type = (EdmSimpleType) property.getType();
+ return type.valueOfString(value, EdmLiteralKind.DEFAULT, property.getFacets(),
+ typeMapping == null ? type.getDefaultType() : typeMapping);
+ }
+ throw new EntityProviderException(EntityProviderException.INVALID_PROPERTY.addContent(
+ "Expected simple property but found complex for property with name '" + property.getName() + "'"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/EntryMetadataImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/EntryMetadataImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/EntryMetadataImpl.java
new file mode 100644
index 0000000..a4b1fbd
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/EntryMetadataImpl.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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.entry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.ep.entry.EntryMetadata;
+
+/**
+ * @author SAP AG
+ */
+public class EntryMetadataImpl implements EntryMetadata {
+ private String id;
+ private String etag;
+ private String uri;
+ private Map<String, List<String>> associationUris = new HashMap<String, List<String>>();
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getEtag() {
+ return etag;
+ }
+
+ public void setEtag(final String etag) {
+ this.etag = etag;
+ }
+
+ @Override
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(final String uri) {
+ this.uri = uri;
+ }
+
+ @Override
+ public List<String> getAssociationUris(final String navigationPropertyName) {
+ final List<String> uris = associationUris.get(navigationPropertyName);
+ if (uris == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.unmodifiableList(uris);
+ }
+ }
+
+ public void putAssociationUri(final String navigationPropertyName, final String uri) {
+ List<String> uris = associationUris.get(navigationPropertyName);
+ if (uris == null) {
+ uris = new ArrayList<String>();
+ }
+ uris.add(uri);
+ associationUris.put(navigationPropertyName, uris);
+ }
+
+ @Override
+ public String toString() {
+ return "EntryMetadataImpl [id=" + id + ", etag=" + etag + ", uri=" + uri + ", associationUris=" + associationUris + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/MediaMetadataImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/MediaMetadataImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/MediaMetadataImpl.java
new file mode 100644
index 0000000..2d8d436
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/MediaMetadataImpl.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.entry;
+
+import org.apache.olingo.odata2.api.ep.entry.MediaMetadata;
+
+/**
+ * @author SAP AG
+ */
+public class MediaMetadataImpl implements MediaMetadata {
+
+ private String sourceLink;
+ private String etag;
+ private String contentType;
+ private String editLink;
+
+ @Override
+ public String getSourceLink() {
+ return sourceLink;
+ }
+
+ @Override
+ public String getEtag() {
+ return etag;
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public String getEditLink() {
+ return editLink;
+ }
+
+ public void setSourceLink(final String sourceLink) {
+ this.sourceLink = sourceLink;
+ }
+
+ public void setEtag(final String etag) {
+ this.etag = etag;
+ }
+
+ public void setContentType(final String contentType) {
+ this.contentType = contentType;
+ }
+
+ public void setEditLink(final String editLink) {
+ this.editLink = editLink;
+ }
+
+ @Override
+ public String toString() {
+ return "MediaMetadataImpl [sourceLink=" + sourceLink + ", etag=" + etag + ", contentType=" + contentType + ", editLink=" + editLink + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/ODataEntryImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/ODataEntryImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/ODataEntryImpl.java
new file mode 100644
index 0000000..2a94a68
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/entry/ODataEntryImpl.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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.entry;
+
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.ep.entry.EntryMetadata;
+import org.apache.olingo.odata2.api.ep.entry.MediaMetadata;
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.uri.ExpandSelectTreeNode;
+import org.apache.olingo.odata2.core.uri.ExpandSelectTreeNodeImpl;
+
+/**
+ * @author SAP AG
+ */
+public class ODataEntryImpl implements ODataEntry {
+
+ private final Map<String, Object> data;
+ private final EntryMetadata entryMetadata;
+ private final MediaMetadata mediaMetadata;
+ private final ExpandSelectTreeNode expandSelectTree;
+ private boolean containsInlineEntry;
+
+ public ODataEntryImpl(final Map<String, Object> data, final MediaMetadata mediaMetadata, final EntryMetadata entryMetadata, final ExpandSelectTreeNodeImpl expandSelectTree) {
+ this(data, mediaMetadata, entryMetadata, expandSelectTree, false);
+ }
+
+ public ODataEntryImpl(final Map<String, Object> data, final MediaMetadata mediaMetadata, final EntryMetadata entryMetadata, final ExpandSelectTreeNode expandSelectTree, final boolean containsInlineEntry) {
+ this.data = data;
+ this.entryMetadata = entryMetadata;
+ this.mediaMetadata = mediaMetadata;
+ this.expandSelectTree = expandSelectTree;
+ this.containsInlineEntry = containsInlineEntry;
+ }
+
+ @Override
+ public Map<String, Object> getProperties() {
+ return data;
+ }
+
+ @Override
+ public MediaMetadata getMediaMetadata() {
+ return mediaMetadata;
+ }
+
+ @Override
+ public EntryMetadata getMetadata() {
+ return entryMetadata;
+ }
+
+ @Override
+ public boolean containsInlineEntry() {
+ return containsInlineEntry;
+ }
+
+ @Override
+ public ExpandSelectTreeNode getExpandSelectTree() {
+ return expandSelectTree;
+ }
+
+ public void setContainsInlineEntry(final boolean containsInlineEntry) {
+ this.containsInlineEntry = containsInlineEntry;
+ }
+
+ @Override
+ public String toString() {
+ return "ODataEntryImpl [data=" + data + ", "
+ + "entryMetadata=" + entryMetadata + ", "
+ + "mediaMetadata=" + mediaMetadata + ", "
+ + "expandSelectTree=" + expandSelectTree + ", "
+ + "containsInlineEntry=" + containsInlineEntry + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/FeedMetadataImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/FeedMetadataImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/FeedMetadataImpl.java
new file mode 100644
index 0000000..3f18298
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/FeedMetadataImpl.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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.feed;
+
+import org.apache.olingo.odata2.api.ep.feed.FeedMetadata;
+
+public class FeedMetadataImpl implements FeedMetadata {
+
+ private Integer inlineCount = null;
+ private String nextLink = null;
+ private String deltaLink;
+
+ public void setInlineCount(final int inlineCount) {
+ this.inlineCount = inlineCount;
+ }
+
+ @Override
+ public Integer getInlineCount() {
+ return inlineCount;
+ }
+
+ public void setNextLink(final String nextLink) {
+ this.nextLink = nextLink;
+ }
+
+ @Override
+ public String getNextLink() {
+ return nextLink;
+ }
+
+ public void setDeltaLink(final String deltaLink) {
+ this.deltaLink = deltaLink;
+ }
+
+ @Override
+ public String getDeltaLink() {
+ return deltaLink;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/ff2b0a0e/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/ODataFeedImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/ODataFeedImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/ODataFeedImpl.java
new file mode 100644
index 0000000..9dd0328
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/feed/ODataFeedImpl.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.feed;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
+import org.apache.olingo.odata2.api.ep.feed.FeedMetadata;
+import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
+
+public class ODataFeedImpl implements ODataFeed {
+
+ private final List<ODataEntry> entries;
+ private final FeedMetadata feedMetadata;
+
+ public ODataFeedImpl(final List<ODataEntry> entries, final FeedMetadata feedMetadata) {
+ this.entries = entries;
+ this.feedMetadata = feedMetadata;
+
+ }
+
+ @Override
+ public List<ODataEntry> getEntries() {
+ return entries;
+ }
+
+ @Override
+ public FeedMetadata getFeedMetadata() {
+ return feedMetadata;
+ }
+
+}