olingo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rare...@apache.org
Subject [5/8] olingo-odata4 git commit: OLINGO-640: Adding support for ATOM+XML Serializer and De-Serializer
Date Wed, 15 Jul 2015 02:36:54 GMT
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
index df88d27..e75d74a 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomDeserializer.java
@@ -20,8 +20,10 @@ package org.apache.olingo.client.core.serialization;
 
 import java.io.InputStream;
 import java.net.URI;
+import java.sql.Timestamp;
 import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -61,6 +63,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.edm.geo.Geospatial;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
 
 import com.fasterxml.aalto.stax.InputFactoryImpl;
 
@@ -95,12 +98,17 @@ public class AtomDeserializer extends AbstractAtomDealer implements ODataDeseria
 
       if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
           && (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
-
         final String stringValue = event.asCharacters().getData();
-        value = typeInfo == null ? stringValue : // TODO: add facets
-          ((EdmPrimitiveType) typeInfo.getType()).valueOfString(stringValue, true, null,
+        if (typeInfo == null) {
+          value = stringValue;
+        } else {
+          final EdmPrimitiveType primitiveType = (EdmPrimitiveType) typeInfo.getType();
+          final Class<?> returnType = primitiveType.getDefaultType().isAssignableFrom(Calendar.class)
+              ? Timestamp.class : primitiveType.getDefaultType();
+          value = ((EdmPrimitiveType) typeInfo.getType()).valueOfString(stringValue, true, null,
               Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true,
-              ((EdmPrimitiveType) typeInfo.getType()).getDefaultType());
+              returnType);
+        }
       }
 
       if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
@@ -634,11 +642,9 @@ public class AtomDeserializer extends AbstractAtomDealer implements ODataDeseria
                 entity.setMediaETag(mediaETag.getValue());
               }
             } else if (link.getRel().startsWith(Constants.NS_NAVIGATION_LINK_REL)) {
-
               entity.getNavigationLinks().add(link);
               inline(reader, event.asStartElement(), link);
             } else if (link.getRel().startsWith(Constants.NS_ASSOCIATION_LINK_REL)) {
-
               entity.getAssociationLinks().add(link);
             } else if (link.getRel().startsWith(Constants.NS_MEDIA_EDIT_LINK_REL)) {
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomSerializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomSerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomSerializer.java
index bc56e48..3c12f5a 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomSerializer.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/AtomSerializer.java
@@ -20,8 +20,11 @@ package org.apache.olingo.client.core.serialization;
 
 import java.io.Writer;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.xml.XMLConstants;
 import javax.xml.stream.XMLOutputFactory;
@@ -83,7 +86,7 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
   private void value(final XMLStreamWriter writer,
       final ValueType valueType, final EdmPrimitiveTypeKind kind, final Object value)
           throws XMLStreamException, EdmPrimitiveTypeException {
-    if (value == null) {
+    if (value == null || (valueType == ValueType.COMPLEX && ((ComplexValue)value).getValue().isEmpty())) {
       writer.writeAttribute(Constants.PREFIX_METADATA, namespaceMetadata,
           Constants.ATTR_NULL, Boolean.TRUE.toString());
       return;
@@ -140,7 +143,11 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
         property.getValue());
     if (!property.isNull() && property.isComplex() && !property.isCollection()) {
       links(writer, property.asComplex().getAssociationLinks());
-      links(writer, property.asComplex().getNavigationLinks());
+      if (serverMode) {
+        links(writer, property.asComplex().getNavigationLinks());
+      } else {
+        writeNavigationLinks(writer, property.asComplex().getNavigationLinks());
+      }
     }
 
     writer.writeEndElement();
@@ -158,7 +165,6 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
   private void startDocument(final XMLStreamWriter writer, final String rootElement) throws XMLStreamException {
     writer.writeStartDocument();
     writer.setDefaultNamespace(Constants.NS_ATOM);
-
     writer.writeStartElement(rootElement);
 
     namespaces(writer);
@@ -175,49 +181,147 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
     writer.writeEndDocument();
     writer.flush();
   }
-
-  private void links(final XMLStreamWriter writer, final List<Link> links)
+  
+  private boolean isEntitySetNavigation(final Link link) {
+    return Constants.ENTITY_SET_NAVIGATION_LINK_TYPE.equals(link.getType());
+  }
+  
+  private void writeNavigationLinks(final XMLStreamWriter writer, final List<Link> links)
       throws XMLStreamException, EdmPrimitiveTypeException {
-    for (Link link : links) {
-      writer.writeStartElement(Constants.ATOM_ELEM_LINK);
-
-      if (StringUtils.isNotBlank(link.getRel())) {
-        writer.writeAttribute(Constants.ATTR_REL, link.getRel());
-      }
-      if (StringUtils.isNotBlank(link.getTitle())) {
-        writer.writeAttribute(Constants.ATTR_TITLE, link.getTitle());
-      }
-      if (StringUtils.isNotBlank(link.getHref())) {
-        writer.writeAttribute(Constants.ATTR_HREF, link.getHref());
-      }
-      if (StringUtils.isNotBlank(link.getType())) {
-        writer.writeAttribute(Constants.ATTR_TYPE, link.getType());
-      }
+    final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
 
+    for (Link link : links) {
+    
       if (link.getInlineEntity() != null || link.getInlineEntitySet() != null) {
-        writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ATOM_ELEM_INLINE, namespaceMetadata);
-
-        if (link.getInlineEntity() != null) {
-          writer.writeStartElement(Constants.ATOM_ELEM_ENTRY);
-          entity(writer, link.getInlineEntity());
-          writer.writeEndElement();
-        }
-        if (link.getInlineEntitySet() != null) {
-          writer.writeStartElement(Constants.ATOM_ELEM_FEED);
-          entitySet(writer, link.getInlineEntitySet());
-          writer.writeEndElement();
+        writeLink(writer, link, new ExtraContent() {
+          @Override
+          public void write(XMLStreamWriter writer, Link link) throws XMLStreamException, EdmPrimitiveTypeException {
+            writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ATOM_ELEM_INLINE, namespaceMetadata);        
+            if (link.getInlineEntity() != null) {
+              writer.writeStartElement(namespaceAtom, Constants.ATOM_ELEM_ENTRY);
+              entity(writer, link.getInlineEntity());
+              writer.writeEndElement();
+            }
+            if (link.getInlineEntitySet() != null) {
+              writer.writeStartElement(namespaceAtom, Constants.ATOM_ELEM_FEED);
+              entitySet(writer, link.getInlineEntitySet());
+              writer.writeEndElement();
+            }
+            writer.writeEndElement(); // inline    
+          }
+        });
+        
+      } else if (link.getBindingLink() != null) {
+        writeLink(writer, link, new ExtraContent() {
+          @Override
+          public void write(XMLStreamWriter writer, Link link) throws XMLStreamException, EdmPrimitiveTypeException {
+            writer.writeAttribute(Constants.ATTR_HREF, link.getBindingLink());
+          }
+        });
+      } else if (link.getBindingLinks() != null && !link.getBindingLinks().isEmpty()) {
+        writeLink(writer, link, new ExtraContent() {
+          @Override
+          public void write(XMLStreamWriter writer, Link link) throws XMLStreamException, EdmPrimitiveTypeException {
+            writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ATOM_ELEM_INLINE, namespaceMetadata);
+            writer.writeStartElement(namespaceAtom, Constants.ATOM_ELEM_FEED);
+            for (String binding:link.getBindingLinks()) {            
+              Entity entity = new Entity();
+              entity.setId(URI.create(binding));
+              inlineEntityRef(writer, entity);                      
+            }
+            writer.writeEndElement(); //feed            
+            writer.writeEndElement(); //inline
+          }
+        });
+      } else {
+        if (isEntitySetNavigation(link)) {
+          final List<String> uris;
+          if (entitySetLinks.containsKey(link.getTitle())) {
+            uris = entitySetLinks.get(link.getTitle());
+          } else {
+            uris = new ArrayList<String>();
+            entitySetLinks.put(link.getTitle(), uris);
+          }
+          if (StringUtils.isNotBlank(link.getHref())) {
+            uris.add(link.getHref());
+          }
+        } else {      
+          writeLink(writer, link, new ExtraContent() {
+            @Override
+            public void write(XMLStreamWriter writer, Link link) 
+                throws XMLStreamException, EdmPrimitiveTypeException {
+            }
+          });
         }
-
-        writer.writeEndElement();
       }
-
-      for (Annotation annotation : link.getAnnotations()) {
-        annotation(writer, annotation, null);
+    }
+    for (String title : entitySetLinks.keySet()) {
+      final List<String>entitySetLink = entitySetLinks.get(title);
+      if (!entitySetLink.isEmpty()) {
+        Link link = new Link();
+        link.setTitle(title);
+        link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
+        link.setRel(Constants.NS_NAVIGATION_LINK_REL+title);
+
+        writeLink(writer, link, new ExtraContent() {
+          @Override
+          public void write(XMLStreamWriter writer, Link link) throws XMLStreamException, EdmPrimitiveTypeException {
+            writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ATOM_ELEM_INLINE, namespaceMetadata);
+            writer.writeStartElement(namespaceAtom, Constants.ATOM_ELEM_FEED);
+            for (String binding:entitySetLink) {            
+              Entity entity = new Entity();
+              entity.setId(URI.create(binding));
+              inlineEntityRef(writer, entity);                      
+            }
+            writer.writeEndElement();    
+            writer.writeEndElement();
+          }
+        });                
       }
-
-      writer.writeEndElement();
+    }    
+  }
+  
+  private void links(final XMLStreamWriter writer, final List<Link> links)
+      throws XMLStreamException, EdmPrimitiveTypeException {
+    for (Link link : links) {
+      writeLink(writer, link, new ExtraContent() {
+        @Override
+        public void write(XMLStreamWriter writer, Link link) 
+            throws XMLStreamException, EdmPrimitiveTypeException {
+        }
+      });
     }
   }
+  
+  interface ExtraContent {
+    void write(final XMLStreamWriter writer, final Link link) 
+        throws XMLStreamException, EdmPrimitiveTypeException;
+  }
+  
+  private void writeLink(final XMLStreamWriter writer, final Link link, final ExtraContent content)
+      throws XMLStreamException, EdmPrimitiveTypeException {
+    writer.writeStartElement(Constants.ATOM_ELEM_LINK);
+
+    if (StringUtils.isNotBlank(link.getRel())) {
+      writer.writeAttribute(Constants.ATTR_REL, link.getRel());
+    }
+    if (StringUtils.isNotBlank(link.getTitle())) {
+      writer.writeAttribute(Constants.ATTR_TITLE, link.getTitle());
+    }
+    if (StringUtils.isNotBlank(link.getHref())) {
+      writer.writeAttribute(Constants.ATTR_HREF, link.getHref());
+    }
+    if (StringUtils.isNotBlank(link.getType())) {
+      writer.writeAttribute(Constants.ATTR_TYPE, link.getType());
+    }
+    
+    content.write(writer, link);
+    
+    for (Annotation annotation : link.getAnnotations()) {
+      annotation(writer, annotation, null);
+    }
+    writer.writeEndElement();    
+  }  
 
   private void common(final XMLStreamWriter writer, final AbstractODataObject object) throws XMLStreamException {
     if (StringUtils.isNotBlank(object.getTitle())) {
@@ -298,7 +402,12 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
     }
 
     links(writer, entity.getAssociationLinks());
-    links(writer, entity.getNavigationLinks());
+    if (serverMode) {
+      links(writer, entity.getNavigationLinks());
+    } else {
+      writeNavigationLinks(writer, entity.getNavigationLinks());
+      writeNavigationLinks(writer, entity.getNavigationBindings());
+    }
     links(writer, entity.getMediaEditLinks());
 
     if (serverMode) {
@@ -336,15 +445,22 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
     }
   }
 
+  private void inlineEntityRef(final XMLStreamWriter writer, final Entity entity) throws XMLStreamException {
+    writer.writeStartElement(namespaceMetadata, Constants.ATOM_ELEM_ENTRY_REF);
+    writer.writeAttribute(Constants.ATOM_ATTR_ID, entity.getId().toASCIIString());
+    writer.writeEndElement();
+  }
+  
   private void entityRef(final XMLStreamWriter writer, final Entity entity) throws XMLStreamException {
     writer.writeStartElement(Constants.ATOM_ELEM_ENTRY_REF);
-    writer.writeNamespace(StringUtils.EMPTY, namespaceMetadata);
+    writer.writeDefaultNamespace(namespaceMetadata);
     writer.writeAttribute(Constants.ATOM_ATTR_ID, entity.getId().toASCIIString());
+    writer.writeEndElement();
   }
 
   private void entityRef(final XMLStreamWriter writer, final ResWrap<Entity> container) throws XMLStreamException {
     writer.writeStartElement(Constants.ATOM_ELEM_ENTRY_REF);
-    writer.writeNamespace(StringUtils.EMPTY, namespaceMetadata);
+    writer.writeDefaultNamespace(namespaceMetadata);
     addContextInfo(writer, container);
     writer.writeAttribute(Constants.ATOM_ATTR_ID, container.getPayload().getId().toASCIIString());
   }
@@ -356,15 +472,11 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
     if (entity.getType() == null && entity.getProperties().isEmpty()) {
       writer.writeStartDocument();
       writer.setDefaultNamespace(namespaceMetadata);
-
       entityRef(writer, entity);
     } else {
       startDocument(writer, Constants.ATOM_ELEM_ENTRY);
-
       entity(writer, entity);
     }
-
-    writer.writeEndElement();
     writer.writeEndDocument();
     writer.flush();
   }
@@ -416,7 +528,6 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
     for (Entity entity : entitySet.getEntities()) {
       if (entity.getType() == null && entity.getProperties().isEmpty()) {
         entityRef(writer, entity);
-        writer.writeEndElement();
       } else {
         writer.writeStartElement(Constants.ATOM_ELEM_ENTRY);
         entity(writer, entity);
@@ -512,9 +623,10 @@ public class AtomSerializer extends AbstractAtomDealer implements ODataSerialize
 
     writer.writeStartDocument();
 
-    writer.writeStartElement(Constants.ATTR_METADATA, Constants.ATTR_REF);
+    writer.writeStartElement(Constants.ATTR_METADATA, Constants.ATTR_REF, Constants.NS_METADATA);
     writer.writeNamespace(Constants.ATTR_METADATA, Constants.NS_METADATA);
-    writer.writeAttribute(Constants.ATTR_METADATA, Constants.CONTEXT, container.getContextURL().toASCIIString());
+    writer.writeAttribute(Constants.ATTR_METADATA, Constants.NS_METADATA, Constants.CONTEXT, 
+        container.getContextURL().toASCIIString());
     writer.writeAttribute(Constants.ATOM_ATTR_ID, container.getPayload().toASCIIString());
     writer.writeEndElement();
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java
index 926f74c..1f42d9b 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java
@@ -456,7 +456,7 @@ public class ODataBinderImpl implements ODataBinder {
         odataLinked.addLink(createODataInlineEntity(inlineEntity,
             URIUtils.getURI(base, href), title, metadataETag));
       } else {
-        odataLinked.addLink(createODataInlineEntitySet(inlineEntitySet,
+        odataLinked.addLink(createODataInlineEntitySet(inlineEntitySet, href == null?null:
             URIUtils.getURI(base, href), title, metadataETag));
       }
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
index 87edec1..3577455 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/JSONTest.java
@@ -146,7 +146,7 @@ public class JSONTest extends AbstractTest {
     final StringWriter writer = new StringWriter();
     getClient().getSerializer(contentType).write(writer, getClient().getDeserializer(contentType).toEntity(
         getClass().getResourceAsStream(filename + "." + getSuffix(contentType))).getPayload());
-    
+    System.out.println(writer.toString());
     assertSimilar(filename + "." + getSuffix(contentType), writer.toString());
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Customers.xml
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Customers.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Customers.xml
index aa416d2..a0adedd 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Customers.xml
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Customers.xml
@@ -35,7 +35,9 @@
     <category term="#Microsoft.Test.OData.Services.ODataWCFService.Customer" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
     <link rel="edit" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)"/>
     <link rel="http://docs.oasis-open.org/odata/ns/related/Parent" type="application/atom+xml;type=entry" title="Parent" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Parent"/>
-    <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" title="Orders" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders"/>
+    <link rel="http://docs.oasis-open.org/odata/ns/related/Orders"
+	   type="application/atom+xml;type=feed" title="Orders">
+	   <m:inline><feed><m:ref id="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders"/></feed></m:inline></link>
     <link rel="http://docs.oasis-open.org/odata/ns/related/Company" type="application/atom+xml;type=entry" title="Company" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Company"/>
     <title/>
     <updated>2014-03-31T09:35:14Z</updated>
@@ -76,7 +78,10 @@
     <category term="#Microsoft.Test.OData.Services.ODataWCFService.Customer" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
     <link rel="edit" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=2)"/>
     <link rel="http://docs.oasis-open.org/odata/ns/related/Parent" type="application/atom+xml;type=entry" title="Parent" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=2)/Parent"/>
-    <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" title="Orders" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=2)/Orders"/>
+    <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" 
+      title="Orders">
+      <m:inline><feed><m:ref id="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=2)/Orders"/></feed></m:inline>
+      </link>
     <link rel="http://docs.oasis-open.org/odata/ns/related/Company" type="application/atom+xml;type=entry" title="Company" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=2)/Company"/>
     <title/>
     <updated>2014-03-31T09:35:14Z</updated>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Products_5.xml
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Products_5.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Products_5.xml
index 9109f2d..409799e 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Products_5.xml
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/Products_5.xml
@@ -23,7 +23,7 @@
   <id>http://odatae2etest.azurewebsites.net/javatest/DefaultService/Products(5)</id>
   <category term="#Microsoft.Test.OData.Services.ODataWCFService.Product" scheme="http://docs.oasis-open.org/odata/ns/scheme" />
   <link rel="edit" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Products(5)" />
-  <link rel="http://docs.oasis-open.org/odata/ns/related/Details" type="application/atom+xml;type=feed" title="Details" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Products(5)/Details" />
+  <link rel="http://docs.oasis-open.org/odata/ns/related/Details" type="application/atom+xml;type=feed" title="Details"><m:inline><feed><ref xmlns="http://docs.oasis-open.org/odata/ns/metadata" id="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Products(5)/Details"/></feed></m:inline></link>
   <title />
   <updated>2014-03-28T11:24:49Z</updated>
   <author>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/VipCustomer.xml
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/VipCustomer.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/VipCustomer.xml
index d233ce9..cc5c04d 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/VipCustomer.xml
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/VipCustomer.xml
@@ -30,7 +30,7 @@
   <category term="#Microsoft.Test.OData.Services.ODataWCFService.Customer" scheme="http://docs.oasis-open.org/odata/ns/scheme" />
   <link rel="edit" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/VipCustomer" />
   <link rel="http://docs.oasis-open.org/odata/ns/related/Parent" type="application/atom+xml;type=entry" title="Parent" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/VipCustomer/Parent" />
-  <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" title="Orders" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/VipCustomer/Orders" />
+  <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" title="Orders"><m:inline><feed><ref xmlns="http://docs.oasis-open.org/odata/ns/metadata" id="http://odatae2etest.azurewebsites.net/javatest/DefaultService/VipCustomer/Orders"/></feed></m:inline></link>
   <link rel="http://docs.oasis-open.org/odata/ns/related/Company" type="application/atom+xml;type=entry" title="Company" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/VipCustomer/Company" />
   <title />
   <updated>2014-03-28T13:03:32Z</updated>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
index 851f960..a3b06a9 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/annotated.xml
@@ -24,7 +24,9 @@
   <category term="#Microsoft.Test.OData.Services.ODataWCFService.Customer" scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
   <link rel="edit" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)"/>
   <link rel="http://docs.oasis-open.org/odata/ns/related/Parent" type="application/atom+xml;type=entry" title="Parent" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Parent"/>
-  <link rel="http://docs.oasis-open.org/odata/ns/related/Orders" type="application/atom+xml;type=feed" title="Orders" href="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders">
+	<link rel="http://docs.oasis-open.org/odata/ns/related/Orders"
+		type="application/atom+xml;type=feed" title="Orders">
+		<m:inline><feed><m:ref id="http://odatae2etest.azurewebsites.net/javatest/DefaultService/Customers(PersonID=1)/Orders"/></feed></m:inline>
     <m:annotation term="com.contoso.display.style" m:type="#com.contoso.display.styleType">
       <d:order m:type="Int32">2</d:order>
     </m:annotation>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/entity.withcomplexnavigation.xml
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/entity.withcomplexnavigation.xml b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/entity.withcomplexnavigation.xml
index 1a4e0cc..94cb766 100644
--- a/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/entity.withcomplexnavigation.xml
+++ b/lib/client-core/src/test/resources/org/apache/olingo/client/core/v4/entity.withcomplexnavigation.xml
@@ -33,7 +33,8 @@ under the License.
   <link rel="edit" title="Customer" href="Customers('ALFKI')"/>
   <link rel="http://docs.oasis-open.org/odata/ns/related/Orders"
         type="application/atom+xml;type=feed"
-        title="Orders" href="Customers('ALFKI')/Orders"/>
+        title="Orders">
+        <m:inline><feed><m:ref id="Customers('ALFKI')/Orders"/></feed></m:inline> </link>        
   <link rel="http://docs.oasis-open.org/odata/ns/related/Supplier"
         type="application/atom+xml;type=entry"
         title="Supplier" href="Customers('ALFKI')/Supplier"/>

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
index d81530f..0eab699 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
@@ -31,7 +31,8 @@ public class EntityCollectionSerializerOptions {
   private ExpandOption expand;
   private SelectOption select;
   private boolean onlyReferences;
-  
+  private String id;
+
   /** Gets the {@link ContextURL}. */
   public ContextURL getContextURL() {
     return contextURL;
@@ -56,6 +57,11 @@ public class EntityCollectionSerializerOptions {
   public boolean onlyReferences() {
     return onlyReferences;
   }
+  
+  /** Gets the id of the entity collection */
+  public String getId() {
+    return id;
+  }
 
   /** Initializes the options builder. */
   public static Builder with() {
@@ -101,6 +107,12 @@ public class EntityCollectionSerializerOptions {
       return this;
     }
     
+    /** Sets id of the collection */
+    public Builder setId(final String id) {
+      options.id = id;
+      return this;
+    }
+
     /** Builds the OData serializer options. */
     public EntityCollectionSerializerOptions build() {
       return options;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
index 76e8d5f..7f3f234 100644
--- a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
@@ -152,7 +152,8 @@ public abstract class ServiceRequest {
       return (T) EntityCollectionSerializerOptions.with()
           .contextURL(isODataMetadataNone(getResponseContentType()) ? null : contextUrl)
           .count(uriInfo.getCountOption()).expand(uriInfo.getExpandOption())
-          .select(uriInfo.getSelectOption()).setWriteOnlyReferences(references).build();
+          .select(uriInfo.getSelectOption()).setWriteOnlyReferences(references)
+          .setId(getODataRequest().getRawBaseUri()+getODataRequest().getRawODataPath()).build();
     } else if (serilizerOptions.isAssignableFrom(ComplexSerializerOptions.class)) {
       return (T) ComplexSerializerOptions.with().contextURL(contextUrl)
           .expand(this.uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption()).build();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
index f35c277..2eed18f 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
@@ -53,7 +53,9 @@ public class ContentNegotiator {
       return Arrays.asList(
           ContentType.JSON,
           ContentType.JSON_NO_METADATA,
-          ContentType.APPLICATION_JSON);
+          ContentType.APPLICATION_JSON,
+          ContentType.APPLICATION_ATOM_XML,
+          ContentType.APPLICATION_XML);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
index dac1642..2dbac81 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
@@ -44,11 +44,12 @@ import org.apache.olingo.server.api.uri.UriHelper;
 import org.apache.olingo.server.core.debug.DebugResponseHelperImpl;
 import org.apache.olingo.server.core.deserializer.FixedFormatDeserializerImpl;
 import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer;
+import org.apache.olingo.server.core.deserializer.xml.ODataXmlDeserializer;
 import org.apache.olingo.server.core.etag.ETagHelperImpl;
 import org.apache.olingo.server.core.prefer.PreferencesImpl;
 import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl;
 import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
-import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializerImpl;
+import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
 import org.apache.olingo.server.core.uri.UriHelperImpl;
 
 public class ODataImpl extends OData {
@@ -64,8 +65,9 @@ public class ODataImpl extends OData {
           || ContentType.VALUE_ODATA_METADATA_NONE.equals(metadata)) {
         serializer = new ODataJsonSerializer(contentType);
       }
-    } else if (contentType.isCompatible(ContentType.APPLICATION_XML)) {
-      serializer = new ODataXmlSerializerImpl();
+    } else if (contentType.isCompatible(ContentType.APPLICATION_XML)
+        || contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) {
+      serializer = new ODataXmlSerializer();
     }
 
     if (serializer == null) {
@@ -114,15 +116,13 @@ public class ODataImpl extends OData {
 
     if (contentType.isCompatible(ContentType.JSON)) {
       deserializer = new ODataJsonDeserializer(contentType);
-      // } else if(contentType.isCompatible(ContentType.APPLICATION_XML))
-      // We do not support XML deserialization right now so this must lead
-      // to an error.
-      // {
+    } else if (contentType.isCompatible(ContentType.APPLICATION_XML)
+        || contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)) {
+      deserializer = new ODataXmlDeserializer();      
     } else {
       throw new DeserializerException("Unsupported format: " + contentType.toContentTypeString(),
           DeserializerException.MessageKeys.UNSUPPORTED_FORMAT, contentType.toContentTypeString());
     }
-
     return deserializer;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5b99eb7b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
new file mode 100644
index 0000000..c130ebb
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/xml/ODataXmlDeserializer.java
@@ -0,0 +1,702 @@
+/*
+ * 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.server.core.deserializer.xml;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.AbstractODataObject;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntityCollection;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.Valuable;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmAction;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmEnumType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import org.apache.olingo.commons.core.edm.primitivetype.AbstractGeospatialType;
+import org.apache.olingo.commons.core.edm.primitivetype.SingletonPrimitiveType;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.deserializer.DeserializerResult;
+import org.apache.olingo.server.api.deserializer.ODataDeserializer;
+import org.apache.olingo.server.core.deserializer.DeserializerResultImpl;
+
+import com.fasterxml.aalto.stax.InputFactoryImpl;
+
+public class ODataXmlDeserializer implements ODataDeserializer {
+
+  protected static final XMLInputFactory FACTORY = new InputFactoryImpl();
+  private static final String ATOM = "a";
+  private static final String NS_ATOM = "http://www.w3.org/2005/Atom";  
+  protected QName REF_ELEMENT = new QName("http://docs.oasis-open.org/odata/ns/metadata", "ref");
+  protected QName FEED_ELEMENT = new QName("http://www.w3.org/2005/Atom", "feed");
+  protected QName ID_ATTR = new QName(NS_ATOM, ATOM);
+
+  protected final QName propertiesQName = new QName(Constants.NS_METADATA, Constants.PROPERTIES);
+  protected final QName propertyValueQName = new QName(Constants.NS_METADATA, Constants.VALUE);
+  protected final QName contextQName = new QName(Constants.NS_METADATA, Constants.CONTEXT);
+  protected final QName nullQName = new QName(Constants.NS_METADATA, Constants.ATTR_NULL);
+  protected final QName inlineQName = new QName(Constants.NS_METADATA, Constants.ATOM_ELEM_INLINE);
+  protected final QName entryRefQName = new QName(Constants.NS_METADATA, Constants.ATOM_ELEM_ENTRY_REF);
+  protected final QName etagQName = new QName(Constants.NS_METADATA, Constants.ATOM_ATTR_ETAG); 
+  protected final QName countQName = new QName(Constants.NS_METADATA, Constants.ATOM_ELEM_COUNT);
+  
+  protected void namespaces(final XMLStreamWriter writer) throws XMLStreamException {
+    writer.writeNamespace(StringUtils.EMPTY, Constants.NS_ATOM);
+    writer.writeNamespace(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
+    writer.writeNamespace(Constants.PREFIX_METADATA, Constants.NS_METADATA);
+    writer.writeNamespace(Constants.PREFIX_DATASERVICES, Constants.NS_DATASERVICES);
+    writer.writeNamespace(Constants.PREFIX_GML, Constants.NS_GML);
+    writer.writeNamespace(Constants.PREFIX_GEORSS, Constants.NS_GEORSS);
+  }
+  
+  protected XMLEventReader getReader(final InputStream input) throws XMLStreamException {
+    return FACTORY.createXMLEventReader(input);
+  }
+
+  private Object primitive(final XMLEventReader reader, final StartElement start,
+      final EdmProperty edmProperty) throws XMLStreamException, EdmPrimitiveTypeException, 
+      DeserializerException {
+
+    Object value = null;
+
+    boolean foundEndProperty = false;
+    while (reader.hasNext() && !foundEndProperty) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
+        if (edmProperty.getType() instanceof AbstractGeospatialType<?>) {
+          throw new DeserializerException("geo types support not implemented",
+              DeserializerException.MessageKeys.NOT_IMPLEMENTED);
+        }
+        final String stringValue = event.asCharacters().getData();
+        value = ((EdmPrimitiveType)edmProperty.getType()).valueOfString(stringValue, 
+            edmProperty.isNullable(), 
+            edmProperty.getMaxLength(), 
+            edmProperty.getPrecision(), 
+            edmProperty.getScale(), 
+            edmProperty.isUnicode(), 
+            ((EdmPrimitiveType)edmProperty.getType()).getDefaultType());
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndProperty = true;
+      }
+    }
+    return value;
+  }
+
+  private Object complex(final XMLEventReader reader, final StartElement start, EdmComplexType edmComplex, 
+      boolean validateType)
+      throws XMLStreamException, EdmPrimitiveTypeException, DeserializerException {
+
+    ComplexValue value = new ComplexValue();
+    boolean foundEndProperty = false;
+    while (reader.hasNext() && !foundEndProperty) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isStartElement()) {
+        StartElement se = event.asStartElement();
+        value.getValue().add(property(reader, se, (EdmProperty)edmComplex.getProperty(se.getName().getLocalPart())));
+      }
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndProperty = true;
+      }
+    }
+    return value;
+  }
+
+  private void collection(final Valuable valuable, final XMLEventReader reader, final StartElement start,
+      final EdmProperty edmProperty) throws XMLStreamException, EdmPrimitiveTypeException, DeserializerException {
+
+    List<Object> values = new ArrayList<Object>();
+    EdmType edmType = edmProperty.getType();
+
+    boolean foundEndProperty = false;
+    while (reader.hasNext() && !foundEndProperty) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement()) {        
+        if (edmType instanceof SingletonPrimitiveType) {
+          values.add(primitive(reader, event.asStartElement(), edmProperty));          
+        } else if (edmType instanceof EdmComplexType) {
+          values.add(complex(reader, event.asStartElement(), (EdmComplexType)edmType, false));                    
+        } else if (edmType instanceof EdmEnumType) {
+          values.add(readEnum(reader, event.asStartElement(), edmProperty));          
+        } else {
+          // do not add null or empty values
+        }
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndProperty = true;
+      }
+    }
+    valuable.setValue(getValueType(edmType, true), values);
+  }
+
+  private Object readEnum(XMLEventReader reader, StartElement start, EdmProperty edmProperty) 
+      throws XMLStreamException {
+    boolean foundEndProperty = false;
+    Object value = null;
+    while (reader.hasNext() && !foundEndProperty) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
+        value = event.asCharacters().getData();
+      }
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndProperty = true;
+      }
+    }
+    return value;
+  }
+
+  private Property property(final XMLEventReader reader, final StartElement start, final EdmProperty edmProperty)
+      throws XMLStreamException, EdmPrimitiveTypeException, DeserializerException {
+
+    final Property property = new Property();
+
+    if (propertyValueQName.equals(start.getName())) {
+      // retrieve name from context
+      final Attribute context = start.getAttributeByName(contextQName);
+      if (context != null) {
+        property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
+      }
+    } else {
+      property.setName(start.getName().getLocalPart());
+    }
+    valuable(property, reader, start, edmProperty);
+    return property;
+  }
+
+  private ValueType getValueType(EdmType edmType, boolean isCollection) {
+    if (edmType instanceof SingletonPrimitiveType) {
+      return isCollection? ValueType.COLLECTION_PRIMITIVE:ValueType.PRIMITIVE;
+    } else if (edmType instanceof EdmComplexType) {
+      return isCollection? ValueType.COLLECTION_COMPLEX:ValueType.COMPLEX;
+    } else if (edmType instanceof EdmEnumType) {
+      return isCollection?ValueType.COLLECTION_ENUM:ValueType.ENUM;
+    } else {
+      return ValueType.PRIMITIVE;
+    }
+  }
+  
+  private void valuable(final Valuable valuable, final XMLEventReader reader, final StartElement start,
+      EdmProperty edmProperty) throws XMLStreamException, EdmPrimitiveTypeException,
+      DeserializerException {
+
+    final Attribute nullAttr = start.getAttributeByName(nullQName);
+    if (nullAttr != null) {
+      //found null
+      boolean foundEndProperty = false;
+      while (reader.hasNext() && !foundEndProperty) {
+        final XMLEvent event = reader.nextEvent();
+        if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+          foundEndProperty = true;
+        }
+      }
+      valuable.setValue(getValueType(edmProperty.getType(), false), null);  
+      return;
+    }
+
+    EdmType edmType = edmProperty.getType();
+    if (edmProperty.isCollection()) {
+      collection(valuable, reader, start, edmProperty);
+      valuable.setType("Collection("+edmType.getFullQualifiedName().getFullQualifiedNameAsString()+")");
+    } else if (edmType instanceof SingletonPrimitiveType) {
+      valuable.setType(edmType.getFullQualifiedName().getFullQualifiedNameAsString());
+      valuable.setValue(ValueType.PRIMITIVE, primitive(reader, start, edmProperty));          
+    } else if (edmType instanceof EdmComplexType) {
+      valuable.setValue(ValueType.COMPLEX, complex(reader, start, (EdmComplexType)edmType, true));
+      valuable.setType(edmType.getFullQualifiedName().getFullQualifiedNameAsString());
+    } else if (edmType instanceof EdmEnumType) {
+      valuable.setValue(ValueType.ENUM, readEnum(reader, start, edmProperty));
+      valuable.setType(edmType.getFullQualifiedName().getFullQualifiedNameAsString());
+    } else {
+      // do not add null or empty values
+    }
+  }
+
+  @Override
+  public DeserializerResult property(InputStream input, EdmProperty edmProperty) 
+      throws DeserializerException {
+    try {
+      final XMLEventReader reader = getReader(input);
+      final StartElement start = skipBeforeFirstStartElement(reader);
+      Property property = property(reader, start, edmProperty);
+      return DeserializerResultImpl.with().property(property)
+          .build();
+    } catch (XMLStreamException e) {
+      throw new DeserializerException(e.getMessage(), e, DeserializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new DeserializerException(e.getMessage(), e, 
+          DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY);    
+    }
+  }
+
+  private StartElement skipBeforeFirstStartElement(final XMLEventReader reader) throws XMLStreamException {
+    StartElement startEvent = null;
+    while (reader.hasNext() && startEvent == null) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isStartElement()) {
+        startEvent = event.asStartElement();
+      }
+    }
+    if (startEvent == null) {
+      throw new IllegalArgumentException("Cannot find any XML start element");
+    }
+
+    return startEvent;
+  }
+
+  private void common(final XMLEventReader reader, final StartElement start,
+      final AbstractODataObject object, final String key) throws XMLStreamException {
+
+    boolean foundEndElement = false;
+    while (reader.hasNext() && !foundEndElement) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
+        try {
+          object.setCommonProperty(key, event.asCharacters().getData());
+        } catch (ParseException e) {
+          throw new XMLStreamException("While parsing Atom entry or feed common elements", e);
+        }
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndElement = true;
+      }
+    }
+  }
+
+  private void inline(final XMLEventReader reader, final StartElement start, final Link link, 
+      final EdmEntityType edmEntityType) throws XMLStreamException, EdmPrimitiveTypeException, 
+      DeserializerException {
+
+    boolean foundEndElement = false;
+    EdmNavigationProperty navigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
+    while (reader.hasNext() && !foundEndElement) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement()) {
+        if (inlineQName.equals(event.asStartElement().getName())) {
+          StartElement inline = getStartElement(reader);
+          if (inline != null) {
+            if (Constants.QNAME_ATOM_ELEM_ENTRY.equals(inline.getName())) {
+              if (navigationProperty.isCollection()) {
+                throw new DeserializerException( "Navigation Property "+ link.getTitle() + 
+                    " must be collection entities",
+                    DeserializerException.MessageKeys.INVALID_ANNOTATION_TYPE, link.getTitle());            
+              }
+              
+              link.setInlineEntity(entity(reader, inline, navigationProperty.getType()));
+            }
+            if (Constants.QNAME_ATOM_ELEM_FEED.equals(inline.getName())) {
+              if (!navigationProperty.isCollection()) {
+                throw new DeserializerException("Navigation Property " + link.getTitle() + 
+                    " must be single entity",
+                    DeserializerException.MessageKeys.INVALID_ANNOTATION_TYPE, link.getTitle());            
+              }                                          
+              link.setInlineEntitySet(entitySet(reader, inline, navigationProperty.getType()));
+            }
+          }
+        } else if (REF_ELEMENT.equals(event.asStartElement().getName())) {
+          if (navigationProperty.isCollection()) {
+            throw new DeserializerException("Binding annotation: " + link.getTitle() + 
+                " must be collection of entity refercences",
+                DeserializerException.MessageKeys.INVALID_ANNOTATION_TYPE, link.getTitle());            
+          }          
+          link.setBindingLink(entityRef(reader, event.asStartElement()));
+          link.setType(Constants.ENTITY_BINDING_LINK_TYPE);
+        } else if (Constants.QNAME_ATOM_ELEM_FEED.equals(event.asStartElement().getName())) {
+          if (navigationProperty.isCollection()) {
+            throw new DeserializerException("Binding annotation: " + link.getTitle() + 
+                " must be single entity refercences",
+                DeserializerException.MessageKeys.INVALID_ANNOTATION_TYPE, link.getTitle());            
+          }
+          link.setBindingLinks(entityRefCollection(reader, event.asStartElement()));
+          link.setType(Constants.ENTITY_COLLECTION_BINDING_LINK_TYPE);
+        }
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndElement = true;
+      }
+    }
+  }
+
+  private List<String> entityRefCollection(XMLEventReader reader, StartElement start) 
+      throws XMLStreamException {
+    boolean foundEndElement = false;
+    ArrayList<String> references = new ArrayList<String>();
+    while (reader.hasNext() && !foundEndElement) {
+      final XMLEvent event = reader.nextEvent();
+      
+      if (event.isStartElement()) {
+        if (REF_ELEMENT.equals(event.asStartElement().getName())) {
+          references.add(entityRef(reader, event.asStartElement()));
+        }
+      }
+      
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndElement = true;
+      }
+    }
+    return references;
+  }
+
+  private String entityRef(XMLEventReader reader, StartElement start) throws XMLStreamException {
+    boolean foundEndElement = false;
+    final Attribute entityRefId = start.getAttributeByName(Constants.QNAME_ATOM_ATTR_ID);
+    while (reader.hasNext() && !foundEndElement) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndElement = true;
+      }
+    }
+    return entityRefId.getValue();
+  }
+
+  private StartElement getStartElement(final XMLEventReader reader) throws XMLStreamException {
+    while (reader.hasNext()) {
+      final XMLEvent innerEvent = reader.peek();
+      if (innerEvent.isCharacters() && innerEvent.asCharacters().isWhiteSpace()) {
+        reader.nextEvent();
+      } else if (innerEvent.isStartElement()) {
+        return innerEvent.asStartElement();
+      } else if (innerEvent.isEndElement() && inlineQName.equals(innerEvent.asEndElement().getName())) {
+        return null;
+      }
+    }
+    return null;
+  }
+
+  private void properties(final XMLEventReader reader, final StartElement start, final Entity entity, 
+      final EdmEntityType edmEntityType)
+      throws XMLStreamException, EdmPrimitiveTypeException, DeserializerException {
+
+    boolean foundEndProperties = false;
+    while (reader.hasNext() && !foundEndProperties) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement()) {
+        EdmProperty edmProperty = (EdmProperty)edmEntityType
+            .getProperty(event.asStartElement().getName().getLocalPart());
+        entity.getProperties().add(property(reader, event.asStartElement(), edmProperty));
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndProperties = true;
+      }
+    }
+  }
+
+  private Entity entityRef(final StartElement start) throws XMLStreamException {
+    final Entity entity = new Entity();
+
+    final Attribute entityRefId = start.getAttributeByName(Constants.QNAME_ATOM_ATTR_ID);
+    if (entityRefId != null) {
+      entity.setId(URI.create(entityRefId.getValue()));
+    }
+
+    return entity;
+  }
+
+  private Entity entity(final XMLEventReader reader, final StartElement start, final EdmEntityType edmEntityType)
+      throws XMLStreamException, EdmPrimitiveTypeException, DeserializerException {
+    Entity entity = null;
+    if (entryRefQName.equals(start.getName())) {
+      entity = entityRef(start);
+    } else if (Constants.QNAME_ATOM_ELEM_ENTRY.equals(start.getName())) {
+      entity = new Entity();
+      final Attribute xmlBase = start.getAttributeByName(Constants.QNAME_ATTR_XML_BASE);
+      if (xmlBase != null) {
+        entity.setBaseURI(URI.create(xmlBase.getValue()));
+      }
+
+      final Attribute etag = start.getAttributeByName(etagQName);
+      if (etag != null) {
+        entity.setETag(etag.getValue());
+      }
+
+      boolean foundEndEntry = false;
+      while (reader.hasNext() && !foundEndEntry) {
+        final XMLEvent event = reader.nextEvent();
+
+        if (event.isStartElement()) {
+          if (Constants.QNAME_ATOM_ELEM_ID.equals(event.asStartElement().getName())) {
+            common(reader, event.asStartElement(), entity, "id");
+          } else if (Constants.QNAME_ATOM_ELEM_CATEGORY.equals(event.asStartElement().getName())) {
+            final Attribute term = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATOM_ATTR_TERM));
+            if (term != null) {
+              entity.setType(new EdmTypeInfo.Builder().setTypeExpression(term.getValue()).build().internal());
+            }
+          } else if (Constants.QNAME_ATOM_ELEM_LINK.equals(event.asStartElement().getName())) {
+            final Link link = new Link();
+            final Attribute rel = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_REL));
+            if (rel != null) {
+              link.setRel(rel.getValue());
+            }
+            final Attribute title = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TITLE));
+            if (title != null) {
+              link.setTitle(title.getValue());
+            }
+            final Attribute href = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_HREF));
+            if (href != null) {
+              link.setBindingLink(href.getValue());
+            }
+            final Attribute linktype = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TYPE));
+            if (linktype != null) {
+              link.setType(linktype.getValue());
+            }
+            if (Constants.SELF_LINK_REL.equals(link.getRel())) {
+              entity.setSelfLink(link);
+            } else if (Constants.EDIT_LINK_REL.equals(link.getRel())) {
+              entity.setEditLink(link);
+            } else if (Constants.EDITMEDIA_LINK_REL.equals(link.getRel())) {
+              final Attribute mediaETag = event.asStartElement().getAttributeByName(etagQName);
+              if (mediaETag != null) {
+                entity.setMediaETag(mediaETag.getValue());
+              }
+            } else if (link.getRel().startsWith(Constants.NS_NAVIGATION_LINK_REL)) {              
+              inline(reader, event.asStartElement(), link, edmEntityType);
+              if (link.getInlineEntity() == null && link.getInlineEntitySet() == null) {
+                entity.getNavigationBindings().add(link);
+              } else {
+                if (link.getInlineEntitySet() != null) {
+                  List<String> bindings = new ArrayList<String>();
+                  List<Entity> enities = link.getInlineEntitySet().getEntities();
+                  
+                  for (Entity inlineEntity:enities) {
+                    // check if this is reference
+                    if (inlineEntity.getId() != null && inlineEntity.getProperties().isEmpty()) {
+                      bindings.add(inlineEntity.getId().toASCIIString());
+                    }
+                  }
+                  if (!bindings.isEmpty()) {
+                    link.setInlineEntitySet(null);
+                    link.setBindingLinks(bindings);
+                    entity.getNavigationBindings().add(link);
+                  } else {
+                    entity.getNavigationLinks().add(link);
+                  }                  
+                } else {
+                  // add link
+                  entity.getNavigationLinks().add(link);                  
+                }                
+              }
+            } else if (link.getRel().startsWith(Constants.NS_ASSOCIATION_LINK_REL)) {
+              entity.getAssociationLinks().add(link);
+            } else if (link.getRel().startsWith(Constants.NS_MEDIA_EDIT_LINK_REL)) {
+              final Attribute metag = event.asStartElement().getAttributeByName(etagQName);
+              if (metag != null) {
+                link.setMediaETag(metag.getValue());
+              }
+              entity.getMediaEditLinks().add(link);
+            }
+          } else if (Constants.QNAME_ATOM_ELEM_CONTENT.equals(event.asStartElement().getName())) {
+            final Attribute contenttype = event.asStartElement()
+                .getAttributeByName(QName.valueOf(Constants.ATTR_TYPE));
+            if (contenttype == null || ContentType.APPLICATION_XML.toContentTypeString()
+                .equals(contenttype.getValue())) {
+              properties(reader, skipBeforeFirstStartElement(reader), entity, edmEntityType);
+            } else {
+              entity.setMediaContentType(contenttype.getValue());
+              final Attribute src = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATOM_ATTR_SRC));
+              if (src != null) {
+                entity.setMediaContentSource(URI.create(src.getValue()));
+              }
+            }
+          } else if (propertiesQName.equals(event.asStartElement().getName())) {
+            properties(reader, event.asStartElement(), entity, edmEntityType);
+          }
+        }
+
+        if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+          foundEndEntry = true;
+        }
+      }
+    }
+    return entity;
+  }
+
+  @Override
+  public DeserializerResult entity(InputStream input, EdmEntityType edmEntityType) 
+      throws DeserializerException {
+    try {
+      final XMLEventReader reader = getReader(input);
+      final StartElement start = skipBeforeFirstStartElement(reader);
+      final Entity entity = entity(reader, start, edmEntityType);
+      if (entity == null) {
+        throw new DeserializerException("No entity found!", DeserializerException.MessageKeys.INVALID_ENTITY);
+      } 
+      return DeserializerResultImpl.with().entity(entity)
+          .build();      
+    } catch (XMLStreamException e) {
+      throw new DeserializerException(e.getMessage(), e, 
+          DeserializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new DeserializerException(e.getMessage(), e, 
+          DeserializerException.MessageKeys.INVALID_ENTITY);
+    }
+  }
+
+  private void count(final XMLEventReader reader, final StartElement start, final EntityCollection entitySet)
+      throws XMLStreamException {
+
+    boolean foundEndElement = false;
+    while (reader.hasNext() && !foundEndElement) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
+        entitySet.setCount(Integer.valueOf(event.asCharacters().getData()));
+      }
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndElement = true;
+      }
+    }
+  }
+
+  private EntityCollection entitySet(final XMLEventReader reader, final StartElement start,
+      final EdmEntityType edmEntityType) throws XMLStreamException, EdmPrimitiveTypeException, 
+      DeserializerException {
+    if (!Constants.QNAME_ATOM_ELEM_FEED.equals(start.getName())) {
+      return null;
+    }
+    final EntityCollection entitySet = new EntityCollection();
+    final Attribute xmlBase = start.getAttributeByName(Constants.QNAME_ATTR_XML_BASE);
+    if (xmlBase != null) {
+      entitySet.setBaseURI(URI.create(xmlBase.getValue()));
+    }
+
+    boolean foundEndFeed = false;
+    while (reader.hasNext() && !foundEndFeed) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.isStartElement()) {
+        if (countQName.equals(event.asStartElement().getName())) {
+          count(reader, event.asStartElement(), entitySet);
+        } else if (Constants.QNAME_ATOM_ELEM_ID.equals(event.asStartElement().getName())) {
+          common(reader, event.asStartElement(), entitySet, "id");
+        } else if (Constants.QNAME_ATOM_ELEM_LINK.equals(event.asStartElement().getName())) {
+          final Attribute rel = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_REL));
+          if (rel != null) {
+            if (Constants.NEXT_LINK_REL.equals(rel.getValue())) {
+              final Attribute href = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_HREF));
+              if (href != null) {
+                entitySet.setNext(URI.create(href.getValue()));
+              }
+            }
+            if (Constants.NS_DELTA_LINK_REL.equals(rel.getValue())) {
+              final Attribute href = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_HREF));
+              if (href != null) {
+                entitySet.setDeltaLink(URI.create(href.getValue()));
+              }
+            }
+          }
+        } else if (Constants.QNAME_ATOM_ELEM_ENTRY.equals(event.asStartElement().getName())) {
+          entitySet.getEntities().add(entity(reader, event.asStartElement(), edmEntityType));
+        } else if (entryRefQName.equals(event.asStartElement().getName())) {
+          entitySet.getEntities().add(entityRef(event.asStartElement()));
+        }
+      }
+
+      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
+        foundEndFeed = true;
+      }
+    }
+    return entitySet;
+  }
+
+  @Override
+  public DeserializerResult entityCollection(InputStream input, EdmEntityType edmEntityType)
+      throws DeserializerException {
+    try {
+      final XMLEventReader reader = getReader(input);
+      final StartElement start = skipBeforeFirstStartElement(reader);
+      EntityCollection entityCollection = entitySet(reader, start, edmEntityType);
+      for (Entity entity:entityCollection.getEntities()) {
+        entity.setType(edmEntityType.getFullQualifiedName().getFullQualifiedNameAsString());
+      }
+      return DeserializerResultImpl.with().entityCollection(entityCollection)
+          .build();      
+    } catch (XMLStreamException e) {
+      throw new DeserializerException(e.getMessage(), e, DeserializerException.MessageKeys.IO_EXCEPTION);
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new DeserializerException(e.getMessage(), e, 
+          DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY);
+    }
+  }
+  
+  @Override
+  public DeserializerResult entityReferences(InputStream stream) throws DeserializerException {
+    try {
+      XMLEventReader reader = getReader(stream);
+      ArrayList<URI> references = new ArrayList<URI>();
+
+      while (reader.hasNext()) {
+        final XMLEvent event = reader.nextEvent();
+        if (event.isStartElement()) {
+          StartElement start = event.asStartElement();
+          if (REF_ELEMENT.equals(start.getName())) {
+            Attribute context = start.getAttributeByName(ID_ATTR);
+            if (context == null) {
+              context = start.getAttributeByName(new QName("id"));
+            }
+            URI uri = URI.create(context.getValue());
+            references.add(uri);
+          }
+        }
+      }
+      return DeserializerResultImpl.with().entityReferences(references).build();
+    } catch (XMLStreamException e) {
+      throw new DeserializerException("An IOException occurred", e.getCause(),
+          DeserializerException.MessageKeys.IO_EXCEPTION);
+    }
+  }
+
+  @Override
+  public DeserializerResult actionParameters(InputStream stream, EdmAction edmAction) 
+      throws DeserializerException {
+    throw new DeserializerException("Not implemented", DeserializerException.MessageKeys.NOT_IMPLEMENTED);
+  } 
+}


Mime
View raw message