You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by il...@apache.org on 2014/03/24 10:42:36 UTC

[47/50] [abbrv] git commit: [OLINGO-205, OLINGO-200] provided atom v4 deserialization for entity type/set + entity set request

[OLINGO-205, OLINGO-200] provided atom v4 deserialization for entity type/set + entity set request


Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/commit/9aefb959
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/tree/9aefb959
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/diff/9aefb959

Branch: refs/heads/master
Commit: 9aefb95905edee86a6747c26afa40d6f57451008
Parents: 4780fc5
Author: fmartelli <fa...@gmail.com>
Authored: Mon Mar 24 09:50:23 2014 +0100
Committer: fmartelli <fa...@gmail.com>
Committed: Mon Mar 24 09:50:23 2014 +0100

----------------------------------------------------------------------
 .../org/apache/olingo/fit/AbstractServices.java |   28 +-
 .../olingo/fit/utils/AbstractJSONUtilities.java |  503 +++++++
 .../olingo/fit/utils/AbstractUtilities.java     |    3 +-
 .../olingo/fit/utils/AbstractXMLUtilities.java  | 1302 ++++++++++++++++
 .../org/apache/olingo/fit/utils/Accept.java     |   50 +-
 .../org/apache/olingo/fit/utils/Commons.java    |    6 +-
 .../apache/olingo/fit/utils/JSONUtilities.java  |  503 -------
 .../apache/olingo/fit/utils/XMLUtilities.java   | 1388 ------------------
 .../olingo/fit/utils/v3/JSONUtilities.java      |   28 +
 .../olingo/fit/utils/v3/XMLUtilities.java       |  191 +++
 .../olingo/fit/utils/v4/JSONUtilities.java      |   28 +
 .../olingo/fit/utils/v4/XMLUtilities.java       |  142 ++
 fit/src/main/resources/v4/People/feed.full.json |  332 +++++
 fit/src/main/resources/v4/People/feed.xml       |  218 +++
 fit/src/main/resources/v4/metadata.xml          |  743 +++++-----
 .../request/ODataBasicRequest.java              |    3 +-
 .../request/retrieve/ODataRetrieveRequest.java  |    3 +-
 .../olingo/client/api/domain/ODataLinkType.java |    8 +-
 .../apache/olingo/client/api/format/Format.java |   26 +
 .../olingo/client/api/format/ODataFormat.java   |    8 +-
 .../client/api/format/ODataMediaFormat.java     |    8 +-
 .../client/api/format/ODataPubFormat.java       |   47 +-
 .../client/api/format/ODataValueFormat.java     |    8 +-
 .../request/AbstractODataBasicRequest.java      |  144 +-
 .../communication/request/ODataRequestImpl.java |   12 +-
 .../request/invoke/ODataInvokeRequestImpl.java  |    4 +-
 .../retrieve/AbstractODataRetrieveRequest.java  |    3 +-
 .../request/retrieve/ODataRawRequestImpl.java   |    2 +-
 .../AbstractODataStreamedEntityRequest.java     |    4 +-
 .../core/data/AbstractJsonDeserializer.java     |    5 +-
 .../client/core/data/AtomDeserializer.java      |    2 -
 .../core/data/AtomPropertyDeserializer.java     |   16 +-
 .../core/data/JSONGeoValueDeserializer.java     |   11 +-
 .../client/core/edm/EdmActionImportImpl.java    |    1 -
 .../client/core/edm/EdmOperationImpl.java       |    8 +-
 .../olingo/client/core/edm/EdmTypeInfo.java     |   26 +-
 .../core/op/impl/AbstractODataBinder.java       |    8 +-
 .../core/op/impl/AbstractODataDeserializer.java |    1 -
 .../core/it/v3/EntityRetrieveTestITCase.java    |    2 +-
 .../client/core/it/v3/EntitySetTestITCase.java  |    2 +-
 .../core/it/v3/QueryOptionsTestITCase.java      |    3 +-
 .../client/core/it/v4/AbstractTestITCase.java   |    4 +-
 .../client/core/it/v4/EntitySetTestITCase.java  |  152 ++
 .../org/apache/olingo/commons/api/edm/Edm.java  |  161 +-
 .../olingo/commons/api/edm/EdmActionImport.java |    2 +-
 .../api/edm/constants/ODataServiceVersion.java  |    7 +-
 .../core/edm/AbstractEdmComplexType.java        |   39 +-
 .../core/edm/AbstractEdmEntityContainer.java    |  176 +--
 .../commons/core/edm/AbstractEdmOperation.java  |  131 +-
 .../core/edm/AbstractEdmOperationImport.java    |   65 +-
 .../core/edm/AbstractEdmStructuredType.java     |  159 +-
 .../olingo/commons/core/edm/EdmNamedImpl.java   |   21 +-
 .../olingo/commons/core/edm/EdmTypeImpl.java    |   31 +-
 53 files changed, 3917 insertions(+), 2861 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
index 12032c5..025e4fe 100644
--- a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
+++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
@@ -19,8 +19,8 @@
 package org.apache.olingo.fit;
 
 import org.apache.olingo.fit.utils.Accept;
-import org.apache.olingo.fit.utils.XMLUtilities;
-import org.apache.olingo.fit.utils.JSONUtilities;
+import org.apache.olingo.fit.utils.AbstractXMLUtilities;
+import org.apache.olingo.fit.utils.AbstractJSONUtilities;
 import org.apache.olingo.fit.utils.ODataVersion;
 import org.apache.olingo.fit.utils.FSManager;
 
@@ -70,13 +70,18 @@ public abstract class AbstractServices {
   private static Set<ODataVersion> initialized = EnumSet.noneOf(ODataVersion.class);
 
   protected abstract ODataVersion getVersion();
-  protected final XMLUtilities xml;
+  protected final AbstractXMLUtilities xml;
 
-  protected final JSONUtilities json;
+  protected final AbstractJSONUtilities json;
 
   public AbstractServices() throws Exception {
-    this.xml = new XMLUtilities(getVersion());
-    this.json = new JSONUtilities(getVersion());
+    if (ODataVersion.v3 == getVersion()) {
+      this.xml = new org.apache.olingo.fit.utils.v3.XMLUtilities();
+      this.json = new org.apache.olingo.fit.utils.v3.JSONUtilities();
+    } else {
+      this.xml = new org.apache.olingo.fit.utils.v4.XMLUtilities();
+      this.json = new org.apache.olingo.fit.utils.v4.JSONUtilities();
+    }
 
     if (!initialized.contains(getVersion())) {
       xml.retrieveLinkInfoFromMetadata();
@@ -135,7 +140,7 @@ public abstract class AbstractServices {
       return xml.
               createResponse(FSManager.instance(getVersion()).readFile(filename, Accept.XML), null, Accept.XML);
     } catch (Exception e) {
-      return xml.createFaultResponse(Accept.XML.toString(), e);
+      return xml.createFaultResponse(Accept.XML.toString(getVersion()), e);
     }
   }
 
@@ -547,7 +552,7 @@ public abstract class AbstractServices {
 
       return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
     } catch (Exception e) {
-      return xml.createFaultResponse(Accept.XML.toString(), e);
+      return xml.createFaultResponse(Accept.XML.toString(getVersion()), e);
     }
   }
 
@@ -749,7 +754,7 @@ public abstract class AbstractServices {
 
     } catch (Exception e) {
       LOG.error("Error retrieving entity", e);
-      return xml.createFaultResponse(Accept.JSON.toString(), e);
+      return xml.createFaultResponse(Accept.JSON.toString(getVersion()), e);
     }
   }
 
@@ -775,7 +780,7 @@ public abstract class AbstractServices {
           @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
           final String changes) {
     if (xml.isMediaContent(entitySetName + "/" + path)) {
-      return replaceMediaProperty(prefer, entitySetName, entityId, path, format, changes);
+      return replaceMediaProperty(prefer, entitySetName, entityId, path, changes);
     } else {
       return replaceProperty(accept, prefer, entitySetName, entityId, path, format, changes, false);
     }
@@ -786,7 +791,6 @@ public abstract class AbstractServices {
           final String entitySetName,
           final String entityId,
           final String path,
-          final String format,
           final String value) {
     try {
       final AbstractUtilities utils = getUtilities(null);
@@ -809,7 +813,7 @@ public abstract class AbstractServices {
 
     } catch (Exception e) {
       LOG.error("Error retrieving entity", e);
-      return xml.createFaultResponse(Accept.JSON.toString(), e);
+      return xml.createFaultResponse(Accept.JSON.toString(getVersion()), e);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
new file mode 100644
index 0000000..c62d341
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractJSONUtilities.java
@@ -0,0 +1,503 @@
+/*
+ * 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.fit.utils;
+
+import static org.apache.olingo.fit.utils.Constants.*;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.NotFoundException;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+
+public abstract class AbstractJSONUtilities extends AbstractUtilities {
+
+  public AbstractJSONUtilities(final ODataVersion version) throws Exception {
+    super(version);
+  }
+
+  @Override
+  protected Accept getDefaultFormat() {
+    return Accept.JSON_FULLMETA;
+  }
+
+  @Override
+  protected InputStream addLinks(
+          final String entitySetName, final String entitykey, final InputStream is, final Set<String> links)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
+    IOUtils.closeQuietly(is);
+
+    for (String link : links) {
+      srcNode.set(link + JSON_NAVIGATION_SUFFIX,
+              new TextNode(Commons.getLinksURI(version, entitySetName, entitykey, link)));
+    }
+
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+
+  @Override
+  protected Set<String> retrieveAllLinkNames(InputStream is) throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
+    IOUtils.closeQuietly(is);
+
+    final Set<String> links = new HashSet<String>();
+
+    final Iterator<String> fieldIter = srcNode.fieldNames();
+
+    while (fieldIter.hasNext()) {
+      final String field = fieldIter.next();
+
+      if (field.endsWith(JSON_NAVIGATION_BIND_SUFFIX)
+              || field.endsWith(JSON_NAVIGATION_SUFFIX)
+              || field.endsWith(JSON_MEDIA_SUFFIX)
+              || field.endsWith(JSON_EDITLINK_NAME)) {
+        if (field.indexOf('@') > 0) {
+          links.add(field.substring(0, field.indexOf('@')));
+        } else {
+          links.add(field);
+        }
+      }
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected NavigationLinks retrieveNavigationInfo(
+          final String entitySetName, final InputStream is)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
+    IOUtils.closeQuietly(is);
+
+    final NavigationLinks links = new NavigationLinks();
+
+    final Iterator<Map.Entry<String, JsonNode>> fieldIter = srcNode.fields();
+
+    while (fieldIter.hasNext()) {
+      final Map.Entry<String, JsonNode> field = fieldIter.next();
+      if (field.getKey().endsWith(JSON_NAVIGATION_BIND_SUFFIX)) {
+        final String title = field.getKey().substring(0, field.getKey().indexOf('@'));
+        final List<String> hrefs = new ArrayList<String>();
+        if (field.getValue().isArray()) {
+          for (JsonNode href : ((ArrayNode) field.getValue())) {
+            final String uri = href.asText();
+            hrefs.add(uri.substring(uri.lastIndexOf('/') + 1));
+          }
+        } else {
+          final String uri = field.getValue().asText();
+          hrefs.add(uri.substring(uri.lastIndexOf('/') + 1));
+        }
+
+        links.addLinks(title, hrefs);
+      } else if (Commons.linkInfo.get(version).exists(entitySetName, field.getKey())) {
+        links.addInlines(field.getKey(), IOUtils.toInputStream(field.getValue().toString()));
+      }
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected InputStream normalizeLinks(
+          final String entitySetName, final String entityKey, final InputStream is, final NavigationLinks links)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
+
+    if (links != null) {
+      for (String linkTitle : links.getLinkNames()) {
+        // normalize link
+        srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
+        srcNode.set(
+                linkTitle + JSON_NAVIGATION_SUFFIX,
+                new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
+      }
+
+      for (String linkTitle : links.getInlineNames()) {
+        // normalize link if exist; declare a new one if missing
+        srcNode.remove(linkTitle + JSON_NAVIGATION_BIND_SUFFIX);
+        srcNode.set(
+                linkTitle + JSON_NAVIGATION_SUFFIX,
+                new TextNode(String.format("%s(%s)/%s", entitySetName, entityKey, linkTitle)));
+
+        // remove inline
+        srcNode.remove(linkTitle);
+
+        // remove from links
+        links.removeLink(linkTitle);
+      }
+    }
+
+    srcNode.set(
+            JSON_EDITLINK_NAME,
+            new TextNode(Constants.DEFAULT_SERVICE_URL + entitySetName + "(" + entityKey + ")"));
+
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+
+  @Override
+  public InputStream getPropertyValue(final InputStream src, final List<String> path)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final JsonNode srcNode = mapper.readTree(src);
+    JsonNode node = getProperty(srcNode, path);
+    return IOUtils.toInputStream(node.asText());
+  }
+
+  @Override
+  public InputStream getProperty(
+          final String entitySetName, final String entityId, final List<String> path, final String edmType)
+          throws Exception {
+
+    final InputStream src =
+            fsManager.readFile(Commons.getEntityBasePath(entitySetName, entityId) + ENTITY, Accept.JSON_FULLMETA);
+
+    final ObjectMapper mapper = new ObjectMapper();
+    final JsonNode srcNode = mapper.readTree(src);
+
+    final ObjectNode propertyNode = new ObjectNode(JsonNodeFactory.instance);
+
+    if (StringUtils.isNotBlank(edmType)) {
+      propertyNode.put(JSON_ODATAMETADATA_NAME, ODATA_METADATA_PREFIX + edmType);
+    }
+
+    JsonNode jsonNode = getProperty(srcNode, path);
+
+    if (jsonNode.isArray()) {
+      propertyNode.put("value", (ArrayNode) jsonNode);
+    } else if (jsonNode.isObject()) {
+      propertyNode.putAll((ObjectNode) jsonNode);
+    } else {
+      propertyNode.put("value", jsonNode.asText());
+    }
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    mapper.writeValue(bos, propertyNode);
+
+    final InputStream res = new ByteArrayInputStream(bos.toByteArray());
+    IOUtils.closeQuietly(bos);
+
+    return res;
+  }
+
+  private JsonNode getProperty(final JsonNode node, final List<String> path)
+          throws NotFoundException {
+
+    JsonNode propertyNode = node;
+    for (int i = 0; i < path.size(); i++) {
+      propertyNode = propertyNode.get(path.get(i));
+      if (propertyNode == null) {
+        throw new NotFoundException();
+      }
+    }
+
+    return propertyNode;
+  }
+
+  public InputStream addJsonInlinecount(
+          final InputStream src, final int count, final Accept accept)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final JsonNode srcNode = mapper.readTree(src);
+
+    ((ObjectNode) srcNode).put(ODATA_COUNT_NAME, count);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    mapper.writeValue(bos, srcNode);
+
+    final InputStream res = new ByteArrayInputStream(bos.toByteArray());
+    IOUtils.closeQuietly(bos);
+
+    return res;
+  }
+
+  public InputStream wrapJsonEntities(final InputStream entities) throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final JsonNode node = mapper.readTree(entities);
+
+    final ObjectNode res;
+
+    final JsonNode value = node.get(JSON_VALUE_NAME);
+
+    if (value.isArray()) {
+      res = mapper.createObjectNode();
+      res.set("value", value);
+      final JsonNode next = node.get(JSON_NEXTLINK_NAME);
+      if (next != null) {
+        res.set(JSON_NEXTLINK_NAME, next);
+      }
+    } else {
+      res = (ObjectNode) value;
+    }
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    mapper.writeValue(bos, res);
+
+    final InputStream is = new ByteArrayInputStream(bos.toByteArray());
+    IOUtils.closeQuietly(bos);
+
+    return is;
+  }
+
+  @Override
+  public InputStream selectEntity(final InputStream src, final String[] propertyNames) throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(src);
+
+    final Set<String> retain = new HashSet<String>();
+    retain.add(JSON_ID_NAME);
+    retain.add(JSON_TYPE_NAME);
+    retain.add(JSON_EDITLINK_NAME);
+    retain.add(JSON_NEXTLINK_NAME);
+    retain.add(JSON_ODATAMETADATA_NAME);
+    retain.add(JSON_VALUE_NAME);
+
+    for (String name : propertyNames) {
+      retain.add(name);
+      retain.add(name + JSON_NAVIGATION_SUFFIX);
+      retain.add(name + JSON_MEDIA_SUFFIX);
+      retain.add(name + JSON_TYPE_SUFFIX);
+    }
+
+    srcNode.retain(retain);
+
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+
+  @Override
+  public InputStream readEntities(
+          final List<String> links, final String linkName, final String next, final boolean forceFeed)
+          throws Exception {
+
+    if (links.isEmpty()) {
+      throw new NotFoundException();
+    }
+
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode node = mapper.createObjectNode();
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    if (forceFeed || links.size() > 1) {
+      bos.write("[".getBytes());
+    }
+
+    for (String link : links) {
+      try {
+        final Map.Entry<String, String> uri = Commons.parseEntityURI(link);
+        final Map.Entry<String, InputStream> entity =
+                readEntity(uri.getKey(), uri.getValue(), Accept.JSON_FULLMETA);
+
+        if (bos.size() > 1) {
+          bos.write(",".getBytes());
+        }
+
+        IOUtils.copy(entity.getValue(), bos);
+      } catch (Exception e) {
+        // log and ignore link
+        LOG.warn("Error parsing uri {}", link, e);
+      }
+    }
+
+    if (forceFeed || links.size() > 1) {
+      bos.write("]".getBytes());
+    }
+
+    node.set(JSON_VALUE_NAME, mapper.readTree(new ByteArrayInputStream(bos.toByteArray())));
+
+    if (StringUtils.isNotBlank(next)) {
+      node.set(JSON_NEXTLINK_NAME, new TextNode(next));
+    }
+
+    return IOUtils.toInputStream(node.toString());
+  }
+
+  @Override
+  protected InputStream replaceLink(
+          final InputStream toBeChanged, final String linkName, final InputStream replacement)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+
+    final ObjectNode toBeChangedNode = (ObjectNode) mapper.readTree(toBeChanged);
+    final ObjectNode replacementNode = (ObjectNode) mapper.readTree(replacement);
+
+    if (toBeChangedNode.get(linkName + JSON_NAVIGATION_SUFFIX) == null) {
+      throw new NotFoundException();
+    }
+
+    toBeChangedNode.set(linkName, replacementNode.get(JSON_VALUE_NAME));
+
+    final JsonNode next = replacementNode.get(linkName + JSON_NEXTLINK_NAME);
+    if (next != null) {
+      toBeChangedNode.set(linkName + JSON_NEXTLINK_SUFFIX, next);
+    }
+
+    return IOUtils.toInputStream(toBeChangedNode.toString());
+  }
+
+  @Override
+  protected Map<String, InputStream> getChanges(final InputStream src) throws Exception {
+    final Map<String, InputStream> res = new HashMap<String, InputStream>();
+
+    final ObjectMapper mapper = new ObjectMapper();
+    final JsonNode srcObject = mapper.readTree(src);
+
+    final Iterator<Map.Entry<String, JsonNode>> fields = srcObject.fields();
+    while (fields.hasNext()) {
+      final Map.Entry<String, JsonNode> field = fields.next();
+      res.put(field.getKey(), IOUtils.toInputStream(field.getValue().toString()));
+    }
+
+    return res;
+  }
+
+  @Override
+  protected InputStream setChanges(
+          final InputStream toBeChanged, final Map<String, InputStream> properties) throws Exception {
+
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode toBeChangedObject = (ObjectNode) mapper.readTree(toBeChanged);
+
+    for (Map.Entry<String, InputStream> property : properties.entrySet()) {
+      final JsonNode propertyNode = mapper.readTree(property.getValue());
+      toBeChangedObject.set(property.getKey(), propertyNode);
+    }
+
+    return IOUtils.toInputStream(toBeChangedObject.toString());
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(
+          final String entitySetName, final String entityId, final String linkName)
+          throws Exception {
+    final LinkInfo links = readLinks(entitySetName, entityId, linkName, Accept.JSON_FULLMETA);
+    return extractLinkURIs(links.getLinks());
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(final InputStream is)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(is);
+    IOUtils.closeQuietly(is);
+
+    final List<String> links = new ArrayList<String>();
+
+    JsonNode uris = srcNode.get("value");
+    if (uris == null) {
+      final JsonNode url = srcNode.get("url");
+      if (url != null) {
+        links.add(url.textValue());
+      }
+    } else {
+      final Iterator<JsonNode> iter = ((ArrayNode) uris).iterator();
+      while (iter.hasNext()) {
+        links.add(iter.next().get("url").textValue());
+      }
+    }
+
+    final JsonNode next = srcNode.get(JSON_NEXTLINK_NAME);
+
+    return new SimpleEntry<String, List<String>>(next == null ? null : next.asText(), links);
+  }
+
+  @Override
+  public InputStream addEditLink(
+          final InputStream content, final String title, final String href) throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(content);
+    IOUtils.closeQuietly(content);
+
+    srcNode.set(JSON_EDITLINK_NAME, new TextNode(href));
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+
+  @Override
+  public InputStream replaceProperty(
+          final InputStream src, final InputStream replacement, final List<String> path, final boolean justValue)
+          throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(src);
+    IOUtils.closeQuietly(src);
+
+    final JsonNode replacementNode;
+    if (justValue) {
+      replacementNode = new TextNode(IOUtils.toString(replacement));
+    } else {
+      replacementNode = (ObjectNode) mapper.readTree(replacement);
+    }
+    IOUtils.closeQuietly(replacement);
+
+    JsonNode node = srcNode;
+    for (int i = 0; i < path.size() - 1; i++) {
+      node = node.get(path.get(i));
+      if (node == null) {
+        throw new NotFoundException();
+      }
+    }
+
+    ((ObjectNode) node).set(path.get(path.size() - 1), replacementNode);
+
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+
+  @Override
+  public InputStream deleteProperty(final InputStream src, final List<String> path) throws Exception {
+    final ObjectMapper mapper = new ObjectMapper();
+    final ObjectNode srcNode = (ObjectNode) mapper.readTree(src);
+    IOUtils.closeQuietly(src);
+
+    JsonNode node = srcNode;
+    for (int i = 0; i < path.size() - 1; i++) {
+      node = node.get(path.get(i));
+      if (node == null) {
+        throw new NotFoundException();
+      }
+    }
+
+    ((ObjectNode) node).set(path.get(path.size() - 1), null);
+
+    return IOUtils.toInputStream(srcNode.toString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
index 74653a1..7b74b62 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
@@ -384,7 +384,7 @@ public abstract class AbstractUtilities {
     }
 
     if (accept != null) {
-      builder.header("Content-Type", accept.toString());
+      builder.header("Content-Type", accept.toString(version));
     } else {
       builder.header("Content-Type", "*/*");
     }
@@ -423,7 +423,6 @@ public abstract class AbstractUtilities {
 
   public Response createFaultResponse(final String accept, final Exception e) {
     LOG.debug("Create fault response about .... ", e);
-    e.printStackTrace();
 
     final Response.ResponseBuilder builder = Response.serverError();
     if (version == ODataVersion.v3) {

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
new file mode 100644
index 0000000..d02e828
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractXMLUtilities.java
@@ -0,0 +1,1302 @@
+/*
+ * 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.fit.utils;
+
+import static org.apache.olingo.fit.utils.Constants.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.NotFoundException;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+
+public abstract class AbstractXMLUtilities extends AbstractUtilities {
+
+  protected static XMLInputFactory ifactory = null;
+
+  protected static XMLOutputFactory ofactory = null;
+
+  public AbstractXMLUtilities(final ODataVersion version) throws Exception {
+    super(version);
+  }
+
+  public abstract void retrieveLinkInfoFromMetadata() throws Exception;
+
+  @Override
+  protected Accept getDefaultFormat() {
+    return Accept.ATOM;
+  }
+
+  protected XMLEventReader getEventReader(final InputStream is) throws XMLStreamException {
+    if (ifactory == null) {
+      ifactory = XMLInputFactory.newInstance();
+    }
+    ifactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+    return ifactory.createXMLEventReader(is);
+  }
+
+  protected static XMLEventWriter getEventWriter(final OutputStream os) throws XMLStreamException {
+    if (ofactory == null) {
+      ofactory = XMLOutputFactory.newInstance();
+    }
+
+    return ofactory.createXMLEventWriter(os);
+  }
+
+  private void writeEvent(final XMLEvent event, final XMLEventWriter writer) {
+    if (writer != null) {
+      try {
+        writer.add(event);
+      } catch (XMLStreamException e) {
+        LOG.error("Error writing event {}", event, e);
+      }
+    }
+  }
+
+  private void skipElement(
+          final StartElement start,
+          final XMLEventReader reader,
+          final XMLEventWriter writer,
+          final boolean excludeStart)
+          throws Exception {
+
+    if (!excludeStart) {
+      writeEvent(start, writer);
+    }
+
+    int depth = 1;
+    boolean found = false;
+
+    while (reader.hasNext() && !found) {
+      final XMLEvent event = reader.nextEvent();
+
+      writeEvent(event, writer);
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+        found = depth == 0 && start.getName().equals(event.asEndElement().getName());
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected InputStream addLinks(
+          final String entitySetName, final String entitykey, final InputStream is, final Set<String> links)
+          throws Exception {
+
+    // -----------------------------------------
+    // 0. Build reader and writer
+    // -----------------------------------------
+    final XMLEventReader reader = getEventReader(is);
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+    // -----------------------------------------
+    final Map.Entry<Integer, XmlElement> entry =
+            extractElement(reader, writer, Collections.singletonList("entry"), 0, 1, 1);
+
+    writer.add(entry.getValue().getStart());
+
+    // add for links
+    for (String link : links) {
+      final Set<Attribute> attributes = new HashSet<Attribute>();
+      attributes.add(eventFactory.createAttribute(new QName("title"), link));
+      attributes.add(eventFactory.createAttribute(new QName("href"),
+              Commons.getLinksURI(version, entitySetName, entitykey, link)));
+      attributes.add(eventFactory.createAttribute(new QName("rel"), Constants.ATOM_LINK_REL + link));
+      attributes.add(eventFactory.createAttribute(new QName("type"),
+              Commons.linkInfo.get(version).isFeed(entitySetName, link) ? Constants.ATOM_LINK_FEED
+              : Constants.ATOM_LINK_ENTRY));
+
+      writer.add(eventFactory.createStartElement(new QName(LINK), attributes.iterator(), null));
+      writer.add(eventFactory.createEndElement(new QName(LINK), null));
+    }
+
+    writer.add(entry.getValue().getContentReader());
+    writer.add(entry.getValue().getEnd());
+    writer.add(reader);
+    IOUtils.closeQuietly(is);
+
+    writer.flush();
+    writer.close();
+    reader.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected Set<String> retrieveAllLinkNames(final InputStream is) throws Exception {
+    final Set<String> links = new HashSet<String>();
+
+    final XMLEventReader reader = getEventReader(is);
+
+    try {
+
+      int startDepth = 0;
+
+      while (true) {
+        final Map.Entry<Integer, XmlElement> linkInfo =
+                extractElement(reader, null, Collections.<String>singletonList(LINK), startDepth, 2, 2);
+
+        startDepth = linkInfo.getKey();
+
+        links.add(linkInfo.getValue().getStart().getAttributeByName(new QName("title")).getValue());
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(is);
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected NavigationLinks retrieveNavigationInfo(
+          final String entitySetName, final InputStream is)
+          throws Exception {
+
+    final NavigationLinks links = new NavigationLinks();
+
+    final XMLEventReader reader = getEventReader(is);
+
+    try {
+      final List<Map.Entry<String, String>> filter = new ArrayList<Map.Entry<String, String>>();
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=entry"));
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=feed"));
+
+      int startDepth = 0;
+
+      while (true) {
+        // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
+        final Map.Entry<Integer, XmlElement> linkInfo = extractElement(
+                reader, null, Collections.<String>singletonList(LINK), filter, true, startDepth, 2, 2);
+        final XmlElement link = linkInfo.getValue();
+        startDepth = linkInfo.getKey();
+
+        final String title = link.getStart().getAttributeByName(new QName("title")).getValue();
+
+        final Attribute hrefAttr = link.getStart().getAttributeByName(new QName("href"));
+        final String href = hrefAttr == null ? null : hrefAttr.getValue();
+
+        try {
+          final XmlElement inlineElement =
+                  extractElement(link.getContentReader(), null, Collections.<String>singletonList(INLINE), 0, -1, -1).
+                  getValue();
+          final XMLEventReader inlineReader = inlineElement.getContentReader();
+
+          try {
+            while (true) {
+              final XmlElement entry =
+                      extractElement(inlineReader, null, Collections.<String>singletonList("entry"), 0, -1, -1).
+                      getValue();
+              links.addInlines(title, entry.toStream());
+            }
+          } catch (Exception e) {
+            // Reached the end of document
+          }
+
+          inlineReader.close();
+        } catch (Exception ignore) {
+          // inline element not found (inlines are not mondatory).
+          if (StringUtils.isNotBlank(href) && entityUriPattern.matcher(href).matches()) {
+            links.addLinks(title, href.substring(href.lastIndexOf('/') + 1));
+          }
+        }
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      reader.close();
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected InputStream normalizeLinks(
+          final String entitySetName, final String entityKey, final InputStream is, final NavigationLinks links)
+          throws Exception {
+
+    // -----------------------------------------
+    // 0. Build reader and writer
+    // -----------------------------------------
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(is, bos);
+    is.close();
+
+    final ByteArrayOutputStream tmpBos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(tmpBos);
+
+    final XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 1. Normalize links
+    // -----------------------------------------
+    final Set<String> added = new HashSet<String>();
+
+    try {
+      final List<Map.Entry<String, String>> filter = new ArrayList<Map.Entry<String, String>>();
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=entry"));
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=feed"));
+
+      Map.Entry<Integer, XmlElement> linkInfo = null;
+
+      while (true) {
+        // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
+        linkInfo = extractElement(
+                reader, writer, Collections.<String>singletonList(LINK), filter, true,
+                linkInfo == null ? 0 : linkInfo.getKey(), 2, 2);
+        final XmlElement link = linkInfo.getValue();
+
+        final String title = link.getStart().getAttributeByName(new QName("title")).getValue();
+
+        if (!added.contains(title)) {
+          added.add(title);
+
+          final String normalizedLink = String.format(
+                  "<link href=\"%s(%s)/%s\" rel=\"%s\" title=\"%s\" type=\"%s\"/>",
+                  entitySetName,
+                  entityKey,
+                  title,
+                  link.getStart().getAttributeByName(new QName("rel")).getValue(),
+                  title,
+                  link.getStart().getAttributeByName(new QName("type")).getValue());
+
+          addAtomElement(IOUtils.toInputStream(normalizedLink), writer);
+        }
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      writer.close();
+      reader.close();
+    }
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 2. Add edit link if missing
+    // -----------------------------------------
+    final InputStream content = addEditLink(
+            new ByteArrayInputStream(tmpBos.toByteArray()),
+            entitySetName,
+            Constants.DEFAULT_SERVICE_URL + entitySetName + "(" + entityKey + ")");
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 3. Add content element if missing
+    // -----------------------------------------
+    return addAtomContent(
+            content,
+            entitySetName,
+            Constants.DEFAULT_SERVICE_URL + entitySetName + "(" + entityKey + ")");
+    // -----------------------------------------
+
+  }
+
+  public XmlElement getXmlElement(
+          final StartElement start,
+          final XMLEventReader reader)
+          throws Exception {
+
+    final XmlElement res = new XmlElement();
+    res.setStart(start);
+
+    StringWriter content = new StringWriter();
+
+    int depth = 1;
+
+    while (reader.hasNext() && depth > 0) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+      }
+
+      if (depth == 0) {
+        res.setEnd(event.asEndElement());
+      } else {
+        event.writeAsEncodedUnicode(content);
+      }
+    }
+
+    content.flush();
+    content.close();
+
+    res.setContent(new ByteArrayInputStream(content.toString().getBytes()));
+
+    return res;
+  }
+
+  private void addAtomElement(
+          final InputStream content,
+          final XMLEventWriter writer)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(content);
+
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+    XMLEvent newLine = eventFactory.createSpace("\n");
+
+    try {
+      writer.add(newLine);
+
+      while (reader.hasNext()) {
+        final XMLEvent event = reader.nextEvent();
+
+        if (event.getEventType() != XMLStreamConstants.START_DOCUMENT
+                && event.getEventType() != XMLStreamConstants.END_DOCUMENT
+                && event.getEventType() != XMLStreamConstants.COMMENT) {
+          writer.add(event);
+        }
+      }
+      writer.add(newLine);
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(content);
+    }
+  }
+
+  @Override
+  public InputStream addEditLink(
+          final InputStream content, final String title, final String href)
+          throws Exception {
+
+    final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+    IOUtils.copy(content, copy);
+
+    IOUtils.closeQuietly(content);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    XMLEventWriter writer = getEventWriter(bos);
+
+    final String editLinkElement = String.format("<link rel=\"edit\" title=\"%s\" href=\"%s\" />", title, href);
+
+    try {
+      // check edit link existence
+      extractElement(reader, writer, Collections.<String>singletonList(LINK),
+              Collections.<Map.Entry<String, String>>singletonList(
+              new AbstractMap.SimpleEntry<String, String>("rel", "edit")), false, 0, -1, -1);
+
+      addAtomElement(IOUtils.toInputStream(editLinkElement), writer);
+      writer.add(reader);
+
+    } catch (Exception e) {
+      reader.close();
+      reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+      bos = new ByteArrayOutputStream();
+      writer = getEventWriter(bos);
+
+      final XmlElement entryElement =
+              extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+
+      writer.add(entryElement.getStart());
+
+      addAtomElement(IOUtils.toInputStream(editLinkElement), writer);
+
+      writer.add(entryElement.getContentReader());
+      writer.add(entryElement.getEnd());
+
+      writer.add(reader);
+
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  public InputStream addAtomContent(
+          final InputStream content, final String title, final String href)
+          throws Exception {
+
+    final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+    IOUtils.copy(content, copy);
+
+    IOUtils.closeQuietly(content);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    XMLEventWriter writer = getEventWriter(bos);
+
+    try {
+      // check edit link existence
+      XmlElement contentElement =
+              extractElement(reader, writer, Collections.<String>singletonList("content"), 0, 2, 2).getValue();
+      writer.add(contentElement.getStart());
+      writer.add(contentElement.getContentReader());
+      writer.add(contentElement.getEnd());
+      writer.add(reader);
+    } catch (Exception e) {
+      reader.close();
+      reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+      bos = new ByteArrayOutputStream();
+      writer = getEventWriter(bos);
+
+      if (isMediaContent(title)) {
+        final XmlElement entryElement =
+                extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+
+        writer.add(entryElement.getStart());
+        writer.add(entryElement.getContentReader());
+
+        addAtomElement(
+                IOUtils.toInputStream(String.format("<content type=\"*/*\" src=\"%s/$value\" />", href)),
+                writer);
+
+        writer.add(entryElement.getEnd());
+      } else {
+        try {
+          final XmlElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList(PROPERTIES), 0, 2, 3).getValue();
+
+          addAtomElement(
+                  IOUtils.toInputStream("<content type=\"application/xml\">"),
+                  writer);
+
+          writer.add(entryElement.getStart());
+          writer.add(entryElement.getContentReader());
+          writer.add(entryElement.getEnd());
+
+          addAtomElement(
+                  IOUtils.toInputStream("</content>"),
+                  writer);
+        } catch (Exception nf) {
+          reader.close();
+          reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+          bos = new ByteArrayOutputStream();
+          writer = getEventWriter(bos);
+
+          final XmlElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+          writer.add(entryElement.getStart());
+          writer.add(entryElement.getContentReader());
+
+          addAtomElement(
+                  IOUtils.toInputStream("<content type=\"application/xml\"/>"),
+                  writer);
+
+          writer.add(entryElement.getEnd());
+        }
+      }
+
+      writer.add(reader);
+
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  public int countAllElements(final String entitySetName) throws Exception {
+    final String basePath = entitySetName + File.separatorChar;
+    int count = countFeedElements(fsManager.readFile(basePath + FEED, Accept.XML), "entry");
+
+    final String skipTokenDirPath = fsManager.getAbsolutePath(basePath + SKIP_TOKEN, null);
+
+
+    try {
+      final FileObject skipToken = fsManager.resolve(skipTokenDirPath);
+      final FileObject[] files = fsManager.findByExtension(skipToken, Accept.XML.getExtension().substring(1));
+
+      for (FileObject file : files) {
+        count += countFeedElements(fsManager.readFile(
+                basePath + SKIP_TOKEN + File.separatorChar + file.getName().getBaseName(), null), "entry");
+      }
+    } catch (FileSystemException fse) {
+      LOG.debug("Resource path '{}' not found", skipTokenDirPath);
+    }
+
+
+    return count;
+  }
+
+  private int countFeedElements(final InputStream is, final String elementName) throws XMLStreamException {
+    final XMLEventReader reader = getEventReader(is);
+
+    int count = 0;
+
+    while (reader.hasNext()) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && elementName.equals(event.asStartElement().getName().getLocalPart())) {
+        count++;
+      }
+    }
+
+    reader.close();
+    return count;
+  }
+
+  public Map.Entry<Integer, XmlElement> extractElement(
+          final XMLEventReader reader, final XMLEventWriter writer, final List<String> path,
+          final int startPathPos, final int minPathPos, final int maxPathPos)
+          throws Exception {
+    return extractElement(reader, writer, path, null, false, startPathPos, minPathPos, maxPathPos);
+  }
+
+  public Map.Entry<Integer, XmlElement> extractElement(
+          final XMLEventReader reader, final XMLEventWriter writer, final List<String> path,
+          final Collection<Map.Entry<String, String>> filter,
+          final boolean filterInOr,
+          final int startPathPos, final int minPathPos, final int maxPathPos)
+          throws Exception {
+
+    StartElement start = null;
+    int searchFor = 0;
+    int depth = startPathPos;
+
+    // Current inspected element
+    String current = null;
+
+    // set defaults
+    final List<String> pathElementNames = path == null ? Collections.<String>emptyList() : path;
+    final Collection<Map.Entry<String, String>> filterAttrs =
+            filter == null ? Collections.<Map.Entry<String, String>>emptySet() : filter;
+
+    while (reader.hasNext() && start == null) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+
+        if (current != null || ((minPathPos < 0 || minPathPos <= depth) && (maxPathPos < 0 || depth <= maxPathPos))) {
+          if (pathElementNames.isEmpty()
+                  || pathElementNames.get(searchFor).trim().equals(event.asStartElement().getName().getLocalPart())) {
+
+            if (searchFor < pathElementNames.size() - 1) {
+              // path exploring not completed
+              writeEvent(event, writer);
+              current = pathElementNames.get(searchFor).trim();
+              searchFor++;
+            } else {
+
+              // path exploring completed ... evaluate filter about path element name attribute
+              boolean match = filterAttrs.isEmpty() || !filterInOr;
+
+              for (Map.Entry<String, String> filterAttr : filterAttrs) {
+                final Attribute attr = event.asStartElement().getAttributeByName(new QName(filterAttr.getKey().trim()));
+
+                if (attr == null || !filterAttr.getValue().trim().equals(attr.getValue())) {
+                  match = filterInOr ? match : false;
+                } else {
+                  match = filterInOr ? true : match;
+                }
+              }
+
+              if (match) {
+                // found searched element
+                start = event.asStartElement();
+              } else {
+                skipElement(event.asStartElement(), reader, writer, false);
+                depth--;
+              }
+            }
+          } else if (current == null) {
+            writeEvent(event, writer);
+          } else {
+            // skip element
+            skipElement(event.asStartElement(), reader, writer, false);
+            depth--;
+          }
+        } else {
+          writeEvent(event, writer);
+        }
+
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+
+        writeEvent(event, writer);
+
+        if (event.asEndElement().getName().getLocalPart().equals(current)) {
+          // back step ....
+          searchFor--;
+          current = searchFor > 0 ? pathElementNames.get(searchFor - 1).trim() : null;
+        }
+      } else {
+        writeEvent(event, writer);
+      }
+    }
+
+    if (start == null) {
+      throw new NotFoundException();
+    }
+
+    return new SimpleEntry<Integer, XmlElement>(Integer.valueOf(depth - 1), getXmlElement(start, reader));
+  }
+
+  public InputStream addAtomInlinecount(
+          final InputStream feed, final int count, final Accept accept)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(feed);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    try {
+
+      final XmlElement feedElement =
+              extractElement(reader, writer, Collections.<String>singletonList("feed"), 0, 1, 1).getValue();
+
+      writer.add(feedElement.getStart());
+      addAtomElement(IOUtils.toInputStream(String.format("<m:count>%d</m:count>", count)), writer);
+      writer.add(feedElement.getContentReader());
+      writer.add(feedElement.getEnd());
+
+      while (reader.hasNext()) {
+        writer.add(reader.nextEvent());
+      }
+
+    } finally {
+      writer.flush();
+      writer.close();
+      reader.close();
+      IOUtils.closeQuietly(feed);
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream getPropertyValue(final InputStream is, final List<String> path)
+          throws Exception {
+
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
+
+    final XMLEventReader reader = getEventReader(is);
+    final Map.Entry<Integer, XmlElement> property = extractElement(reader, null, pathElements, 0, 3, 4);
+
+    reader.close();
+    IOUtils.closeQuietly(is);
+
+    return property.getValue().getContent();
+  }
+
+  @Override
+  public InputStream selectEntity(final InputStream entity, final String[] propertyNames) throws Exception {
+    final XMLEventReader reader = getEventReader(entity);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final List<String> found = new ArrayList<String>(Arrays.asList(propertyNames));
+
+    boolean inProperties = false;
+    boolean writeCurrent = true;
+    Boolean writeNext = null;
+    String currentName = null;
+
+    final List<String> fieldToBeSaved = new ArrayList<String>(Arrays.asList(propertyNames));
+
+    while (reader.hasNext()) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && LINK.equals(event.asStartElement().getName().getLocalPart())
+              && !fieldToBeSaved.contains(
+              event.asStartElement().getAttributeByName(new QName("title")).getValue())
+              && !"edit".equals(event.asStartElement().getAttributeByName(new QName("rel")).getValue())) {
+        writeCurrent = false;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+              && LINK.equals(event.asEndElement().getName().getLocalPart())) {
+        writeNext = true;
+      } else if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && (PROPERTIES).equals(event.asStartElement().getName().getLocalPart())) {
+        writeCurrent = true;
+        writeNext = false;
+        inProperties = true;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+              && (PROPERTIES).equals(event.asEndElement().getName().getLocalPart())) {
+        writeCurrent = true;
+      } else if (inProperties) {
+        if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+          final String elementName = event.asStartElement().getName().getLocalPart();
+
+          for (String propertyName : propertyNames) {
+            if ((ATOM_PROPERTY_PREFIX + propertyName.trim()).equals(elementName)) {
+              writeCurrent = true;
+              found.remove(propertyName);
+              currentName = propertyName;
+            }
+          }
+
+        } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+                && StringUtils.isNotBlank(currentName)
+                && (ATOM_PROPERTY_PREFIX + currentName.trim()).equals(
+                event.asEndElement().getName().getLocalPart())) {
+          writeNext = false;
+          currentName = null;
+        }
+
+      }
+
+      if (writeCurrent) {
+        writer.add(event);
+      }
+
+      if (writeNext != null) {
+        writeCurrent = writeNext;
+        writeNext = null;
+      }
+    }
+
+    writer.flush();
+    writer.close();
+    reader.close();
+    IOUtils.closeQuietly(entity);
+
+    // Do not raise any exception in order to support FC properties as well
+    // if (!found.isEmpty()) {
+    //     throw new Exception(String.format("Could not find a properties '%s'", found));
+    // }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream readEntities(
+          final List<String> links, final String linkName, final String next, final boolean forceFeed)
+          throws Exception {
+
+    if (links.isEmpty()) {
+      throw new NotFoundException();
+    }
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    if (forceFeed || links.size() > 1) {
+      // build a feed
+      bos.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>".getBytes());
+
+      bos.write(("<feed xml:base=\"" + DEFAULT_SERVICE_URL + "\" "
+              + "xmlns=\"http://www.w3.org/2005/Atom\" "
+              + "xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\" "
+              + "xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">")
+              .getBytes());
+
+      bos.write(("<id>" + DEFAULT_SERVICE_URL + "entityset(entityid)/" + linkName + "</id>").getBytes());
+
+      bos.write(("<title type=\"text\">" + linkName + "</title>").getBytes());
+      bos.write("<updated>2014-03-03T13:40:49Z</updated>".getBytes());
+      bos.write(("<link rel=\"self\" title=\"" + linkName + "\" href=\"" + linkName + "\" />").getBytes());
+    }
+
+    for (String link : links) {
+      try {
+        final Map.Entry<String, String> uri = Commons.parseEntityURI(link);
+
+        final XmlElement entry =
+                extractElement(
+                getEventReader(readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue()),
+                null,
+                Collections.<String>singletonList("entry"),
+                0, 1, 1).getValue();
+
+        IOUtils.copy(entry.toStream(), bos);
+      } catch (Exception e) {
+        // log and ignore link
+        LOG.warn("Error parsing uri {}", link, e);
+      }
+    }
+
+    if (forceFeed || links.size() > 1) {
+
+      if (StringUtils.isNotBlank(next)) {
+        bos.write(String.format("<link rel=\"next\" href=\"%s\" />", next).getBytes());
+      }
+
+      bos.write("</feed>".getBytes());
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public Map<String, InputStream> getChanges(final InputStream src) throws Exception {
+    final Map<String, InputStream> res = new HashMap<String, InputStream>();
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(src, bos);
+    IOUtils.closeQuietly(src);
+
+    // retrieve properties ...
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+    final Map.Entry<Integer, XmlElement> propertyElement =
+            extractElement(reader, null, Collections.<String>singletonList(PROPERTIES), 0, 2, 3);
+    reader.close();
+
+    reader = propertyElement.getValue().getContentReader();
+
+    try {
+      while (true) {
+        final XmlElement property = extractElement(reader, null, null, 0, -1, -1).getValue();
+        res.put(property.getStart().getName().getLocalPart(), property.toStream());
+      }
+    } catch (Exception ignore) {
+      // end
+    }
+
+    reader.close();
+
+    // retrieve links ...
+    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+    try {
+      int pos = 0;
+      while (true) {
+        final Map.Entry<Integer, XmlElement> linkElement =
+                extractElement(reader, null, Collections.<String>singletonList(LINK), pos, 2, 2);
+
+        res.put("[LINK]" + linkElement.getValue().getStart().getAttributeByName(new QName("title")).getValue(),
+                linkElement.getValue().toStream());
+
+        pos = linkElement.getKey();
+      }
+    } catch (Exception ignore) {
+      // end
+    }
+
+    return res;
+  }
+
+  @Override
+  public InputStream setChanges(
+          final InputStream toBeChanged,
+          final Map<String, InputStream> properties)
+          throws Exception {
+    XMLEventReader reader = getEventReader(toBeChanged);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    XMLEventWriter writer = getEventWriter(bos);
+
+    // ---------------------------------
+    // add property changes
+    // ---------------------------------
+    Map.Entry<Integer, XmlElement> propertyElement =
+            extractElement(reader, writer, Collections.<String>singletonList(PROPERTIES), 0, 2, 3);
+
+    writer.flush();
+
+    ByteArrayOutputStream pbos = new ByteArrayOutputStream();
+    OutputStreamWriter pwriter = new OutputStreamWriter(pbos);
+
+    final XMLEventReader propertyReader = propertyElement.getValue().getContentReader();
+
+    try {
+      while (true) {
+        final XmlElement property = extractElement(propertyReader, null, null, 0, -1, -1).getValue();
+        final String name = property.getStart().getName().getLocalPart();
+
+        if (properties.containsKey(name)) {
+          // replace
+          final InputStream replacement = properties.get(name);
+          properties.remove(property.getStart().getName().getLocalPart());
+          pwriter.append(IOUtils.toString(replacement));
+          IOUtils.closeQuietly(replacement);
+        } else {
+          pwriter.append(IOUtils.toString(property.toStream()));
+        }
+      }
+    } catch (Exception ignore) {
+      // end
+    }
+
+    for (Map.Entry<String, InputStream> remains : properties.entrySet()) {
+      if (!remains.getKey().startsWith("[LINK]")) {
+        pwriter.append(IOUtils.toString(remains.getValue()));
+        IOUtils.closeQuietly(remains.getValue());
+      }
+    }
+
+    pwriter.flush();
+    pwriter.close();
+
+    writer.add(propertyElement.getValue().getStart());
+    writer.add(new XMLEventReaderWrapper(new ByteArrayInputStream(pbos.toByteArray())));
+    writer.add(propertyElement.getValue().getEnd());
+
+    IOUtils.closeQuietly(pbos);
+
+    writer.add(reader);
+    reader.close();
+    writer.flush();
+    writer.close();
+    // ---------------------------------
+
+    // ---------------------------------
+    // add navigationm changes
+    // ---------------------------------
+
+    // remove existent links
+    for (Map.Entry<String, InputStream> remains : properties.entrySet()) {
+
+      if (remains.getKey().startsWith("[LINK]")) {
+        reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+        bos.reset();
+        writer = getEventWriter(bos);
+
+        try {
+          final String linkName = remains.getKey().substring(remains.getKey().indexOf("]") + 1);
+
+          extractElement(reader, writer, Collections.<String>singletonList(LINK),
+                  Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>("title", linkName)),
+                  false, 0, 2, 2);
+
+          writer.add(reader);
+
+        } catch (Exception ignore) {
+          // ignore
+        }
+
+        writer.flush();
+        writer.close();
+      }
+    }
+
+    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+    bos.reset();
+    writer = getEventWriter(bos);
+
+    propertyElement = extractElement(reader, writer, Collections.<String>singletonList(CONTENT), 0, 2, 2);
+    writer.flush();
+
+    pbos.reset();
+    pwriter = new OutputStreamWriter(pbos);
+
+    for (Map.Entry<String, InputStream> remains : properties.entrySet()) {
+      if (remains.getKey().startsWith("[LINK]")) {
+        pwriter.append(IOUtils.toString(remains.getValue()));
+        IOUtils.closeQuietly(remains.getValue());
+      }
+    }
+
+    pwriter.flush();
+    pwriter.close();
+
+    writer.add(new XMLEventReaderWrapper(new ByteArrayInputStream(pbos.toByteArray())));
+    IOUtils.closeQuietly(pbos);
+
+    writer.add(propertyElement.getValue().getStart());
+    writer.add(propertyElement.getValue().getContentReader());
+    writer.add(propertyElement.getValue().getEnd());
+
+    writer.add(reader);
+    reader.close();
+    writer.flush();
+    writer.close();
+    // ---------------------------------
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  protected InputStream replaceLink(
+          final InputStream toBeChanged, final String linkName, final InputStream replacement)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(toBeChanged);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+    XMLEvent newLine = eventFactory.createSpace("\n");
+
+    try {
+      final XmlElement linkElement =
+              extractElement(reader, writer, Collections.<String>singletonList(LINK),
+              Collections.<Map.Entry<String, String>>singletonList(new SimpleEntry<String, String>("title", linkName)),
+              false, 0, -1, -1).getValue();
+      writer.add(linkElement.getStart());
+
+      // ------------------------------------------
+      // write inline ...
+      // ------------------------------------------
+      writer.add(newLine);
+      writer.add(eventFactory.createStartElement("m", null, "inline"));
+
+      addAtomElement(replacement, writer);
+
+      writer.add(eventFactory.createEndElement("m", null, "inline"));
+      writer.add(newLine);
+      // ------------------------------------------
+
+      writer.add(linkElement.getEnd());
+
+      writer.add(reader);
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(toBeChanged);
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  public String getEdmTypeFromAtom(final String entitySetName, final String entityId, final List<String> path)
+          throws Exception {
+    InputStream src = fsManager.readFile(Commons.getEntityBasePath(entitySetName, entityId) + ENTITY, Accept.XML);
+
+    final List<String> atomPathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      atomPathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
+
+    final Map.Entry<Integer, XmlElement> prop = extractElement(getEventReader(src), null, atomPathElements, 0, 3, 4);
+    IOUtils.closeQuietly(src);
+
+    final Attribute type = prop.getValue().getStart().getAttributeByName(new QName(TYPE));
+
+    final String edmType;
+
+    if (type == null) {
+      edmType = Constants.ATOM_DEF_TYPE;
+    } else {
+      edmType = type.getValue();
+    }
+
+    return edmType;
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(
+          final String entitySetName, final String entityId, final String linkName)
+          throws Exception {
+    final LinkInfo links = readLinks(entitySetName, entityId, linkName, Accept.XML);
+    return extractLinkURIs(links.getLinks());
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(final InputStream is)
+          throws Exception {
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(is, bos);
+    IOUtils.closeQuietly(is);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    final List<String> links = new ArrayList<String>();
+    try {
+      while (true) {
+        links.add(IOUtils.toString(extractElement(reader, null, Collections.<String>singletonList("uri"), 0, -1, -1).
+                getValue().getContent()));
+      }
+    } catch (Exception ignore) {
+      // End document reached ...
+    }
+    reader.close();
+
+    String next;
+
+    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    try {
+      next = IOUtils.toString(extractElement(reader, null, Collections.<String>singletonList("next"), 0, -1, -1).
+              getValue().getContent());
+    } catch (Exception ignore) {
+      // next link is not mandatory
+      next = null;
+    }
+    reader.close();
+
+    return new AbstractMap.SimpleEntry<String, List<String>>(next, links);
+  }
+
+  @Override
+  public InputStream getProperty(
+          final String entitySetName, final String entityId, final List<String> path, final String edmType)
+          throws Exception {
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
+
+    final InputStream src =
+            fsManager.readFile(Commons.getEntityBasePath(entitySetName, entityId) + ENTITY, Accept.XML);
+
+    final XMLEventReader reader = getEventReader(src);
+    final XmlElement property = extractElement(reader, null, pathElements, 0, 3, 4).getValue();
+
+    reader.close();
+    IOUtils.closeQuietly(src);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+    writer.add(eventFactory.createStartDocument("UTF-8", "1.0"));
+    writer.add(property.getStart());
+
+    if (property.getStart().getAttributeByName(new QName(ATOM_DATASERVICE_NS)) == null) {
+      writer.add(eventFactory.createNamespace(ATOM_PROPERTY_PREFIX.substring(0, 1), DATASERVICES_NS));
+    }
+    if (property.getStart().getAttributeByName(new QName(ATOM_METADATA_NS)) == null) {
+      writer.add(eventFactory.createNamespace(ATOM_METADATA_PREFIX.substring(0, 1), METADATA_NS));
+    }
+
+    writer.add(property.getContentReader());
+    writer.add(property.getEnd());
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream replaceProperty(
+          final InputStream src, final InputStream replacement, final List<String> path, final boolean justValue)
+          throws Exception {
+
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
+
+    final XMLEventReader reader = getEventReader(src);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final Map.Entry<Integer, XmlElement> element = extractElement(reader, writer, pathElements, 0, 3, 4);
+
+    if (justValue) {
+      writer.add(element.getValue().getStart());
+    }
+
+    final XMLEventReader changesReader = new XMLEventReaderWrapper(replacement);
+
+    writer.add(changesReader);
+    changesReader.close();
+    IOUtils.closeQuietly(replacement);
+
+    if (justValue) {
+      writer.add(element.getValue().getEnd());
+    }
+
+    writer.add(reader);
+
+    reader.close();
+    IOUtils.closeQuietly(src);
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream deleteProperty(final InputStream src, final List<String> path) throws Exception {
+
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
+
+    final XMLEventReader reader = getEventReader(src);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final Map.Entry<Integer, XmlElement> element = extractElement(reader, writer, pathElements, 0, 3, 4);
+
+    final XMLEventReader changesReader = new XMLEventReaderWrapper(
+            IOUtils.toInputStream(String.format("<%s m:null=\"true\" />", path.get(path.size() - 1))));
+
+    writer.add(changesReader);
+    changesReader.close();
+
+    writer.add(reader);
+
+    reader.close();
+    IOUtils.closeQuietly(src);
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java b/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
index 9fdf1da..2371843 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Accept.java
@@ -28,24 +28,35 @@ public enum Accept {
   TEXT(ContentType.TEXT_PLAIN.getMimeType(), ".txt"),
   XML(ContentType.APPLICATION_XML.getMimeType(), ".xml"),
   ATOM(ContentType.APPLICATION_ATOM_XML.getMimeType(), ".xml"),
-  JSON(ContentType.APPLICATION_JSON.getMimeType() + ";odata=minimalmetadata", ".full.json"),
-  JSON_NOMETA(ContentType.APPLICATION_JSON.getMimeType() + ";odata=nometadata", ".full.json"),
-  JSON_FULLMETA(ContentType.APPLICATION_JSON.getMimeType() + ";odata=fullmetadata", ".full.json");
+  JSON(ContentType.APPLICATION_JSON.getMimeType() + ";odata=minimalmetadata",
+  ContentType.APPLICATION_JSON.getMimeType() + ";odata.metadata=minimal", ".full.json"),
+  JSON_NOMETA(ContentType.APPLICATION_JSON.getMimeType() + ";odata=nometadata",
+  ContentType.APPLICATION_JSON.getMimeType() + ";odata.metadata=none", ".full.json"),
+  JSON_FULLMETA(ContentType.APPLICATION_JSON.getMimeType() + ";odata=fullmetadata",
+  ContentType.APPLICATION_JSON.getMimeType() + ";odata.metadata=full", ".full.json");
 
-  private final String contentType;
+  private final String contentTypeV3;
+
+  private final String contentTypeV4;
 
   private final String fileExtension;
 
   private static Pattern allTypesPattern = Pattern.compile("(.*,)?\\*/\\*([,;].*)?");
 
-  Accept(final String contentType, final String fileExtension) {
-    this.contentType = contentType;
+  Accept(final String contentTypeV3, final String fileExtension) {
+    this.contentTypeV3 = contentTypeV3;
+    this.contentTypeV4 = contentTypeV3;
+    this.fileExtension = fileExtension;
+  }
+
+  Accept(final String contentTypeV3, final String contentTypeV4, final String fileExtension) {
+    this.contentTypeV3 = contentTypeV3;
+    this.contentTypeV4 = contentTypeV4;
     this.fileExtension = fileExtension;
   }
 
-  @Override
-  public String toString() {
-    return contentType;
+  public String toString(final ODataVersion version) {
+    return ODataVersion.v3 == version ? contentTypeV3 : contentTypeV4;
   }
 
   public String getExtension() {
@@ -53,23 +64,30 @@ public enum Accept {
   }
 
   public static Accept parse(final String contentType, final ODataVersion version) {
-    return parse(contentType, version, ODataVersion.v3 == version ? ATOM : JSON_NOMETA);
+    final Accept def;
+    if (ODataVersion.v3 == version) {
+      def = ATOM;
+    } else {
+      def = JSON_NOMETA;
+    }
+
+    return parse(contentType, version, def);
   }
 
   public static Accept parse(final String contentType, final ODataVersion version, final Accept def) {
     if (StringUtils.isBlank(contentType) || allTypesPattern.matcher(contentType).matches()) {
       return def;
-    } else if (JSON_NOMETA.toString().equals(contentType)) {
+    } else if (JSON_NOMETA.toString(version).equals(contentType)) {
       return JSON_NOMETA;
-    } else if (JSON.toString().equals(contentType) || "application/json".equals(contentType)) {
+    } else if (JSON.toString(version).equals(contentType) || "application/json".equals(contentType)) {
       return JSON;
-    } else if (JSON_FULLMETA.toString().equals(contentType)) {
+    } else if (JSON_FULLMETA.toString(version).equals(contentType)) {
       return JSON_FULLMETA;
-    } else if (XML.toString().equals(contentType)) {
+    } else if (XML.toString(version).equals(contentType)) {
       return XML;
-    } else if (ATOM.toString().equals(contentType)) {
+    } else if (ATOM.toString(version).equals(contentType)) {
       return ATOM;
-    } else if (TEXT.toString().equals(contentType)) {
+    } else if (TEXT.toString(version).equals(contentType)) {
       return TEXT;
     } else {
       throw new UnsupportedMediaTypeException("Unsupported media type");

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/9aefb959/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
index 233e03d..dcc4807 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Commons.java
@@ -74,6 +74,10 @@ public abstract class Commons {
     mediaContent.put("Car/Photo", null);
   }
 
+  public static Map<ODataVersion, MetadataLinkInfo> getLinkInfo() {
+    return linkInfo;
+  }
+
   public static String getEntityURI(final String entitySetName, final String entityKey) {
     return entitySetName + "(" + entityKey + ")";
   }
@@ -230,7 +234,7 @@ public abstract class Commons {
         break;
 
       default:
-        throw new UnsupportedOperationException(target.toString());
+        throw new UnsupportedOperationException(target.name());
     }
 
     for (String field : toBeRemoved) {