You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2014/03/18 20:05:53 UTC

[4/4] git commit: [OLINGO-175] Provides (V3) CRUD property + refactoring

[OLINGO-175] Provides (V3) CRUD property + refactoring


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/8f3541c2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/tree/8f3541c2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/diff/8f3541c2

Branch: refs/heads/olingo200
Commit: 8f3541c2dab262c0f72cb70bf3876d300eb2ede3
Parents: a835edd
Author: fmartelli <fa...@gmail.com>
Authored: Tue Mar 18 20:05:28 2014 +0100
Committer: fmartelli <fa...@gmail.com>
Committed: Tue Mar 18 20:05:28 2014 +0100

----------------------------------------------------------------------
 .../org/apache/olingo/fit/AbstractServices.java | 354 +++++++--
 .../olingo/fit/utils/AbstractUtilities.java     | 214 ++----
 .../org/apache/olingo/fit/utils/Constants.java  |   2 +
 .../apache/olingo/fit/utils/JSONUtilities.java  |  93 ++-
 .../olingo/fit/utils/XMLEventReaderWrapper.java |  24 +-
 .../apache/olingo/fit/utils/XMLUtilities.java   | 709 +++++++++++-------
 .../resources/v3/Customer/-7/entity.full.json   | 487 ++++++++++++
 .../resources/v3/Customer/-9/entity.full.json   | 750 +++++++++++++++++++
 .../main/resources/v3/Customer/-9/entity.xml    | 565 ++++++++++++++
 .../main/resources/v3/Order/-8/entity.full.json |  17 +
 fit/src/main/resources/v3/Order/-8/entity.xml   |  43 ++
 .../client/core/it/v3/PropertyTestITCase.java   |  17 -
 12 files changed, 2790 insertions(+), 485 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/8f3541c2/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 27cd0ad..43f47bb 100644
--- a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
+++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
@@ -34,9 +34,12 @@ import org.apache.olingo.fit.utils.LinkInfo;
 import java.io.File;
 import java.io.InputStream;
 import java.util.AbstractMap;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
@@ -64,6 +67,8 @@ public abstract class AbstractServices {
    */
   protected static final Logger LOG = LoggerFactory.getLogger(AbstractServices.class);
 
+  private static Set<ODataVersion> initialized = EnumSet.noneOf(ODataVersion.class);
+
   protected abstract ODataVersion getVersion();
   protected final XMLUtilities xml;
 
@@ -72,6 +77,11 @@ public abstract class AbstractServices {
   public AbstractServices() throws Exception {
     this.xml = new XMLUtilities(getVersion());
     this.json = new JSONUtilities(getVersion());
+
+    if (!initialized.contains(getVersion())) {
+      xml.retrieveLinkInfoFromMetadata();
+      initialized.add(getVersion());
+    }
   }
 
   /**
@@ -279,6 +289,9 @@ public abstract class AbstractServices {
           @PathParam("entitySetName") String entitySetName,
           final String entity) {
 
+    // default
+    AbstractUtilities utils = xml;
+
     try {
       final Accept acceptType = Accept.parse(accept, getVersion());
 
@@ -286,19 +299,15 @@ public abstract class AbstractServices {
         throw new UnsupportedMediaTypeException("Unsupported media type");
       }
 
-      final InputStream res;
-      if (acceptType == Accept.ATOM) {
-        res = xml.addOrReplaceEntity(entitySetName, IOUtils.toInputStream(entity));
-      } else {
-        res = json.addOrReplaceEntity(entitySetName, IOUtils.toInputStream(entity));
-      }
+      utils = getUtilities(acceptType);
+      final InputStream res = utils.addOrReplaceEntity(entitySetName, IOUtils.toInputStream(entity));
 
       final Response response;
       if ("return-no-content".equalsIgnoreCase(prefer)) {
         res.close();
-        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+        response = utils.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
       } else {
-        response = xml.createResponse(res, null, acceptType, Response.Status.CREATED);
+        response = utils.createResponse(res, null, acceptType, Response.Status.CREATED);
       }
 
       if (StringUtils.isNotBlank(prefer)) {
@@ -307,7 +316,7 @@ public abstract class AbstractServices {
 
       return response;
     } catch (Exception e) {
-      return xml.createFaultResponse(accept, e);
+      return utils.createFaultResponse(accept, e);
     }
   }
 
@@ -514,6 +523,259 @@ public abstract class AbstractServices {
     }
   }
 
+  private Response replaceProperty(
+          final String accept,
+          final String prefer,
+          final String entitySetName,
+          final String entityId,
+          final String path,
+          final String format,
+          final String changes,
+          final boolean justValue) {
+    try {
+      Accept acceptType = null;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else if (StringUtils.isNotBlank(accept)) {
+        acceptType = Accept.parse(accept, getVersion(), null);
+      }
+
+      // if the given path is not about any link then search for property
+      LOG.info("Retrieve property {}", path);
+
+      final AbstractUtilities utils = getUtilities(acceptType);
+
+      final InputStream changed = utils.replaceProperty(
+              entitySetName,
+              entityId,
+              IOUtils.toInputStream(changes),
+              Arrays.asList(path.split("/")),
+              acceptType,
+              justValue);
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(changed, null, acceptType, Response.Status.OK);
+      } else {
+        changed.close();
+        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
+      }
+
+      return response;
+
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  private Response deletePropertyValue(
+          final String accept,
+          final String prefer,
+          final String entitySetName,
+          final String entityId,
+          final String path,
+          final String format) {
+    try {
+      Accept acceptType = null;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else if (StringUtils.isNotBlank(accept)) {
+        acceptType = Accept.parse(accept, getVersion(), null);
+      }
+
+      // if the given path is not about any link then search for property
+      LOG.info("Retrieve property {}", path);
+
+      final AbstractUtilities utils = getUtilities(acceptType);
+
+      final InputStream changed = utils.deleteProperty(
+              entitySetName,
+              entityId,
+              Arrays.asList(path.split("/")),
+              acceptType);
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(changed, null, acceptType, Response.Status.OK);
+      } else {
+        changed.close();
+        response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
+      }
+
+      return response;
+
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Replace property value.
+   *
+   * @param accept
+   * @param entitySetName
+   * @param entityId
+   * @param path
+   * @param format
+   * @param changes
+   * @return
+   */
+  @PUT
+  @Path("/{entitySetName}({entityId})/{path:.*}/$value")
+  public Response replacePropertyValue(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          final String changes) {
+    return replaceProperty(accept, prefer, entitySetName, entityId, path, format, changes, true);
+  }
+
+  /**
+   * Replace property.
+   *
+   * @param accept
+   * @param entitySetName
+   * @param entityId
+   * @param path
+   * @param format
+   * @param changes
+   * @return
+   */
+  @MERGE
+  @Path("/{entitySetName}({entityId})/{path:.*}")
+  public Response mergeProperty(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          final String changes) {
+    return replaceProperty(accept, prefer, entitySetName, entityId, path, format, changes, false);
+  }
+
+  /**
+   * Replace property.
+   *
+   * @param accept
+   * @param entitySetName
+   * @param entityId
+   * @param path
+   * @param format
+   * @param changes
+   * @return
+   */
+  @PATCH
+  @Path("/{entitySetName}({entityId})/{path:.*}")
+  public Response patchProperty(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          final String changes) {
+    return replaceProperty(accept, prefer, entitySetName, entityId, path, format, changes, false);
+  }
+
+  /**
+   * Replace property.
+   *
+   * @param accept
+   * @param entitySetName
+   * @param entityId
+   * @param path
+   * @param format
+   * @param changes
+   * @return
+   */
+  @PUT
+  @Path("/{entitySetName}({entityId})/{path:.*}")
+  public Response replaceProperty(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format,
+          final String changes) {
+    return replaceProperty(accept, prefer, entitySetName, entityId, path, format, changes, false);
+  }
+
+  /**
+   * Nullify property value.
+   *
+   * @param accept
+   * @param entitySetName
+   * @param entityId
+   * @param path
+   * @param format
+   * @param changes
+   * @return
+   */
+  @DELETE
+  @Path("/{entitySetName}({entityId})/{path:.*}/$value")
+  public Response deleteProperty(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+    return deletePropertyValue(accept, prefer, entitySetName, entityId, path, format);
+  }
+
+  /**
+   * Retrieve property sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName Entity set name.
+   * @param entityId entity id.
+   * @param path path.
+   * @param format format query option.
+   * @return property.
+   */
+  @GET
+  @Path("/{entitySetName}({entityId})/{path:.*}/$value")
+  public Response getPathValue(
+          @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+          @PathParam("entitySetName") String entitySetName,
+          @PathParam("entityId") String entityId,
+          @PathParam("path") String path,
+          @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
+
+    final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
+    AbstractUtilities utils = null;
+    try {
+      Accept acceptType = null;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else if (StringUtils.isNotBlank(accept)) {
+        acceptType = Accept.parse(accept, getVersion(), null);
+      }
+      utils = getUtilities(acceptType);
+
+      return utils.createResponse(
+              getPath(acceptType, entitySetName, entityId, path, true),
+              Commons.getETag(basePath, getVersion()),
+              acceptType);
+
+    } catch (Exception e) {
+      return (utils == null ? xml : utils).createFaultResponse(accept, e);
+    }
+  }
+
   /**
    * Retrieve property sample.
    *
@@ -534,7 +796,6 @@ public abstract class AbstractServices {
           @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) String format) {
 
     try {
-      boolean searchForValue = path.endsWith("$value");
       Accept acceptType = null;
       if (StringUtils.isNotBlank(format)) {
         acceptType = Accept.valueOf(format.toUpperCase());
@@ -542,14 +803,13 @@ public abstract class AbstractServices {
         acceptType = Accept.parse(accept, getVersion(), null);
       }
 
-      final String basePath =
-              entitySetName + File.separatorChar + Commons.getEntityKey(entityId) + File.separatorChar;
+      final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
 
       InputStream stream;
 
       try {
         final LinkInfo linkInfo = xml.readLinks(entitySetName, entityId, path, Accept.XML);
-        final Map.Entry<String, List<String>> links = XMLUtilities.extractLinkURIs(linkInfo.getLinks());
+        final Map.Entry<String, List<String>> links = xml.extractLinkURIs(linkInfo.getLinks());
 
         switch (acceptType) {
           case JSON:
@@ -564,30 +824,7 @@ public abstract class AbstractServices {
       } catch (NotFoundException e) {
         // if the given path is not about any link then search for property
         LOG.info("Retrieve property {}", path);
-
-        stream = FSManager.instance(getVersion()).readFile(
-                basePath + ENTITY, acceptType == null || acceptType == Accept.TEXT
-                ? Accept.XML : acceptType);
-
-        if (searchForValue) {
-          stream = xml.getAtomPropertyValue(stream, path.split("/"));
-        } else {
-          if (acceptType == null || acceptType == Accept.XML || acceptType == Accept.ATOM) {
-            // retrieve xml
-            stream = xml.getAtomProperty(stream, path.split("/"));
-          } else {
-            // retrieve Edm type from xml
-            final String edmType = xml.getEdmTypeFromXML(
-                    FSManager.instance(getVersion()).readFile(basePath + ENTITY, Accept.XML),
-                    path.split("/"));
-            // retrieve json property
-            stream = json.getJsonProperty(stream, path.split("/"), edmType);
-          }
-        }
-
-        if ((searchForValue && acceptType != null && acceptType != Accept.TEXT) || acceptType == Accept.ATOM) {
-          throw new UnsupportedMediaTypeException("Unsupported media type " + acceptType);
-        }
+        stream = getPath(acceptType, entitySetName, entityId, path, false);
       }
 
       return xml.createResponse(stream, Commons.getETag(basePath, getVersion()), acceptType);
@@ -596,6 +833,37 @@ public abstract class AbstractServices {
     }
   }
 
+  private InputStream getPath(
+          final Accept acceptType,
+          final String entitySetName,
+          final String entityId,
+          final String path,
+          final boolean searchForValue) throws Exception {
+
+    if ((searchForValue && acceptType != null && acceptType != Accept.TEXT) || acceptType == Accept.ATOM) {
+      throw new UnsupportedMediaTypeException("Unsupported media type " + acceptType);
+    }
+
+    final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
+
+    InputStream stream = FSManager.instance(getVersion()).readFile(
+            basePath + ENTITY, acceptType == null || acceptType == Accept.TEXT
+            ? Accept.XML : acceptType);
+
+    final AbstractUtilities utils = getUtilities(acceptType);
+
+    final List<String> pathElements = Arrays.asList(path.split("\\/"));
+
+    if (searchForValue) {
+      stream = utils.getPropertyValue(stream, pathElements);
+    } else {
+      String edmType = xml.getEdmTypeFromAtom(entitySetName, entityId, pathElements);
+      stream = utils.getProperty(entitySetName, entityId, pathElements, edmType);
+    }
+
+    return stream;
+  }
+
   /**
    * Retrieve links sample.
    *
@@ -670,9 +938,9 @@ public abstract class AbstractServices {
 
       final List<String> links;
       if (content == Accept.XML || content == Accept.TEXT || content == Accept.ATOM) {
-        links = XMLUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+        links = xml.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
       } else {
-        links = JSONUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+        links = json.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
       }
 
       utils.putLinksInMemory(
@@ -747,9 +1015,9 @@ public abstract class AbstractServices {
 
       final List<String> links;
       if (content == Accept.XML || content == Accept.TEXT || content == Accept.ATOM) {
-        links = XMLUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+        links = xml.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
       } else {
-        links = JSONUtilities.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
+        links = json.extractLinkURIs(IOUtils.toInputStream(link)).getValue();
       }
 
       utils.putLinksInMemory(
@@ -788,10 +1056,10 @@ public abstract class AbstractServices {
 
       final AbstractUtilities utils = getUtilities(acceptType);
 
-      final Map.Entry<String, List<String>> currents = JSONUtilities.extractLinkURIs(utils.readLinks(
+      final Map.Entry<String, List<String>> currents = json.extractLinkURIs(utils.readLinks(
               entitySetName, entityId, linkName, Accept.JSON_FULLMETA).getLinks());
 
-      final Map.Entry<String, List<String>> toBeRemoved = JSONUtilities.extractLinkURIs(utils.readLinks(
+      final Map.Entry<String, List<String>> toBeRemoved = json.extractLinkURIs(utils.readLinks(
               entitySetName, entityId, linkName + "(" + linkId + ")", Accept.JSON_FULLMETA).getLinks());
 
       final List<String> remains = currents.getValue();

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/8f3541c2/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 9733ce4..b7c5acd 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
@@ -30,7 +30,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.ConcurrentModificationException;
-import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -39,9 +38,6 @@ import java.util.Set;
 import java.util.regex.Pattern;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.Response;
-import javax.xml.namespace.QName;
-import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.events.StartElement;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.vfs2.FileObject;
@@ -56,8 +52,6 @@ public abstract class AbstractUtilities {
    */
   protected static final Logger LOG = LoggerFactory.getLogger(AbstractUtilities.class);
 
-  private static Set<ODataVersion> initialized = EnumSet.noneOf(ODataVersion.class);
-
   protected final ODataVersion version;
 
   protected final FSManager fsManager;
@@ -67,97 +61,6 @@ public abstract class AbstractUtilities {
   public AbstractUtilities(final ODataVersion version) throws Exception {
     this.version = version;
     this.fsManager = FSManager.instance(version);
-    initialize();
-  }
-
-  private void initialize() throws Exception {
-
-    if (!initialized.contains(version)) {
-      final MetadataLinkInfo metadataLinkInfo = new MetadataLinkInfo();
-      Commons.linkInfo.put(version, metadataLinkInfo);
-
-      final InputStream metadata = fsManager.readFile(Constants.METADATA, Accept.XML);
-      final XMLEventReader reader = XMLUtilities.getEventReader(metadata);
-
-      int initialDepth = 0;
-      try {
-        while (true) {
-          Map.Entry<Integer, XmlElement> entityType =
-                  XMLUtilities.getAtomElement(reader, null, "EntityType", null, initialDepth, 4, 4, false);
-          initialDepth = entityType.getKey();
-
-          final String entitySetName =
-                  entityType.getValue().getStart().getAttributeByName(new QName("Name")).getValue();
-
-          final XMLEventReader entityReader = XMLUtilities.getEventReader(entityType.getValue().toStream());
-          int size = 0;
-
-          try {
-            while (true) {
-              final XmlElement navProperty =
-                      XMLUtilities.getAtomElement(entityReader, null, "NavigationProperty");
-
-              final String linkName =
-                      navProperty.getStart().getAttributeByName(new QName("Name")).getValue();
-
-              final Map.Entry<String, Boolean> target = getTargetInfo(navProperty.getStart(), linkName);
-
-              metadataLinkInfo.addLink(
-                      entitySetName,
-                      linkName,
-                      target.getKey(),
-                      target.getValue());
-
-              size++;
-            }
-          } catch (Exception e) {
-          } finally {
-            entityReader.close();
-          }
-
-          if (size == 0) {
-            metadataLinkInfo.addEntitySet(entitySetName);
-          }
-        }
-      } catch (Exception e) {
-      } finally {
-        reader.close();
-        initialized.add(version);
-      }
-    }
-  }
-
-  private Map.Entry<String, Boolean> getTargetInfo(final StartElement element, final String linkName)
-          throws Exception {
-    final InputStream metadata = fsManager.readFile(Constants.METADATA, Accept.XML);
-    XMLEventReader reader = XMLUtilities.getEventReader(metadata);
-
-    final String associationName = element.getAttributeByName(new QName("Relationship")).getValue();
-
-    final Map.Entry<Integer, XmlElement> association = XMLUtilities.getAtomElement(
-            reader, null, "Association",
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>(
-            "Name", associationName.substring(associationName.lastIndexOf(".") + 1))),
-            0, 4, 4, false);
-
-    final InputStream associationContent = association.getValue().toStream();
-    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    IOUtils.copy(associationContent, bos);
-    IOUtils.closeQuietly(associationContent);
-
-    reader.close();
-    reader = XMLUtilities.getEventReader(new ByteArrayInputStream(bos.toByteArray()));
-
-    final Map.Entry<Integer, XmlElement> associationEnd = XMLUtilities.getAtomElement(
-            reader, null, "End",
-            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>("Role", linkName)),
-            0, -1, -1, false);
-
-    final String target = associationEnd.getValue().getStart().getAttributeByName(new QName("Type")).getValue();
-    final boolean feed = associationEnd.getValue().getStart().getAttributeByName(
-            new QName("Multiplicity")).getValue().equals("*");
-
-    return new SimpleEntry<String, Boolean>(target, feed);
   }
 
   public boolean isMediaContent(final String entityName) {
@@ -275,8 +178,7 @@ public abstract class AbstractUtilities {
 
     for (String availableLink : new HashSet<String>(linksToBeMantained)) {
       try {
-        fsManager.resolve(
-                Commons.getLinksPath(version, entitySetName, key, availableLink, Accept.JSON_FULLMETA));
+        fsManager.resolve(Commons.getLinksPath(version, entitySetName, key, availableLink, Accept.JSON_FULLMETA));
       } catch (Exception e) {
         linksToBeMantained.remove(availableLink);
       }
@@ -376,8 +278,7 @@ public abstract class AbstractUtilities {
 
     if (Commons.linkInfo.get(version).isFeed(entitySetName, linkName)) {
       try {
-        final Map.Entry<String, List<String>> currents = JSONUtilities.extractLinkURIs(
-                readLinks(entitySetName, entityKey, linkName, Accept.JSON_FULLMETA).getLinks());
+        final Map.Entry<String, List<String>> currents = extractLinkURIs(entitySetName, entityKey, linkName);
         uris.addAll(currents.getValue());
       } catch (Exception ignore) {
       }
@@ -436,7 +337,6 @@ public abstract class AbstractUtilities {
   }
 
   public Response createFaultResponse(final String accept, final Exception e) {
-    e.printStackTrace();
     LOG.debug("Create fault response about .... ", e);
 
     final Response.ResponseBuilder builder = Response.serverError();
@@ -490,8 +390,7 @@ public abstract class AbstractUtilities {
           propertyNames.add("FromUsername");
 
           final StringBuilder keyBuilder = new StringBuilder();
-          for (Map.Entry<String, InputStream> value
-                  : getPropertyValues(entity, propertyNames, accept).entrySet()) {
+          for (Map.Entry<String, InputStream> value : getPropertyValues(entity, propertyNames).entrySet()) {
 
             if (keyBuilder.length() > 0) {
               keyBuilder.append(",");
@@ -513,7 +412,7 @@ public abstract class AbstractUtilities {
       } else if ("Order".equals(entitySetName)) {
         try {
           final Map<String, InputStream> value =
-                  getPropertyValues(entity, Collections.<String>singletonList("OrderId"), accept);
+                  getPropertyValues(entity, Collections.<String>singletonList("OrderId"));
           res = value.isEmpty() ? null : IOUtils.toString(value.values().iterator().next());
         } catch (Exception e) {
           if (sequence.containsKey(entitySetName)) {
@@ -526,7 +425,7 @@ public abstract class AbstractUtilities {
       } else if ("Customer".equals(entitySetName)) {
         try {
           final Map<String, InputStream> value =
-                  getPropertyValues(entity, Collections.<String>singletonList("CustomerId"), accept);
+                  getPropertyValues(entity, Collections.<String>singletonList("CustomerId"));
           res = value.isEmpty() ? null : IOUtils.toString(value.values().iterator().next());
         } catch (Exception e) {
           if (sequence.containsKey(entitySetName)) {
@@ -539,7 +438,7 @@ public abstract class AbstractUtilities {
       } else if ("ComputerDetail".equals(entitySetName)) {
         try {
           final Map<String, InputStream> value =
-                  getPropertyValues(entity, Collections.<String>singletonList("ComputerDetailId"), accept);
+                  getPropertyValues(entity, Collections.<String>singletonList("ComputerDetailId"));
           res = value.isEmpty() ? null : IOUtils.toString(value.values().iterator().next());
         } catch (Exception e) {
           if (sequence.containsKey(entitySetName)) {
@@ -552,7 +451,7 @@ public abstract class AbstractUtilities {
       } else if ("AllGeoTypesSet".equals(entitySetName)) {
         try {
           final Map<String, InputStream> value =
-                  getPropertyValues(entity, Collections.<String>singletonList("Id"), accept);
+                  getPropertyValues(entity, Collections.<String>singletonList("Id"));
           res = value.isEmpty() ? null : IOUtils.toString(value.values().iterator().next());
         } catch (Exception e) {
           if (sequence.containsKey(entitySetName)) {
@@ -565,7 +464,7 @@ public abstract class AbstractUtilities {
       } else if ("CustomerInfo".equals(entitySetName)) {
         try {
           final Map<String, InputStream> value =
-                  getPropertyValues(entity, Collections.<String>singletonList("CustomerInfoId"), accept);
+                  getPropertyValues(entity, Collections.<String>singletonList("CustomerInfoId"));
           res = value.isEmpty() ? null : IOUtils.toString(value.values().iterator().next());
         } catch (Exception e) {
           if (sequence.containsKey(entitySetName)) {
@@ -587,35 +486,15 @@ public abstract class AbstractUtilities {
     }
   }
 
-  private static Map<String, InputStream> getPropertyValues(
-          final InputStream is, final List<String> propertyNames, final Accept accept)
+  private Map<String, InputStream> getPropertyValues(final InputStream is, final List<String> propertyNames)
           throws Exception {
     final Map<String, InputStream> res = new LinkedHashMap<String, InputStream>();
 
-    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    IOUtils.copy(is, bos);
-    IOUtils.closeQuietly(is);
-
     for (String propertyName : propertyNames) {
-      final InputStream value;
-
-      switch (accept) {
-        case JSON:
-        case JSON_FULLMETA:
-        case JSON_NOMETA:
-          value = JSONUtilities.getJsonPropertyValue(
-                  new ByteArrayInputStream(bos.toByteArray()),
-                  propertyName);
-          break;
-        default:
-          value = XMLUtilities.getAtomPropertyValue(
-                  new ByteArrayInputStream(bos.toByteArray()),
-                  new String[] {propertyName, "$value"});
-      }
-      res.put(propertyName, value);
+      res.put(propertyName, getPropertyValue(is, Collections.<String>singletonList(propertyName)));
     }
 
-    IOUtils.closeQuietly(bos);
+    IOUtils.closeQuietly(is);
 
     return res;
   }
@@ -650,8 +529,7 @@ public abstract class AbstractUtilities {
       throw new UnsupportedMediaTypeException("Unsupported media type");
     }
 
-    final String basePath =
-            entitySetName + File.separatorChar + Commons.getEntityKey(entityId) + File.separatorChar;
+    final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
     return new SimpleEntry<String, InputStream>(basePath, fsManager.readFile(basePath + ENTITY, accept));
   }
 
@@ -666,7 +544,7 @@ public abstract class AbstractUtilities {
     // 0. Retrieve all 'linkName' navigation link uris (NotFoundException if missing) 
     // --------------------------------
     final LinkInfo linkInfo = readLinks(entitySetName, entityId, linkName, Accept.XML);
-    final Map.Entry<String, List<String>> links = XMLUtilities.extractLinkURIs(linkInfo.getLinks());
+    final Map.Entry<String, List<String>> links = extractLinkURIs(entitySetName, entityId, linkName);
     // --------------------------------
 
     // --------------------------------
@@ -704,6 +582,52 @@ public abstract class AbstractUtilities {
     return addOrReplaceEntity(entityId, entitySetName, setChanges(entityInfo.getValue(), replacement));
   }
 
+  public InputStream replaceProperty(
+          final String entitySetName,
+          final String entityId,
+          final InputStream changes,
+          final List<String> path,
+          final Accept accept,
+          final boolean justValue) throws Exception {
+    final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
+
+    final Accept acceptType = accept == null || Accept.TEXT == accept
+            ? Accept.XML : accept.getExtension().equals(Accept.JSON.getExtension()) ? Accept.JSON_FULLMETA : accept;
+
+    // read atom
+    InputStream stream = fsManager.readFile(basePath + ENTITY, acceptType);
+
+    // change atom
+    stream = replaceProperty(stream, changes, path, justValue);
+
+    // save atom
+    fsManager.putInMemory(stream, fsManager.getAbsolutePath(basePath + ENTITY, acceptType));
+
+    return fsManager.readFile(basePath + ENTITY, acceptType);
+  }
+
+  public InputStream deleteProperty(
+          final String entitySetName,
+          final String entityId,
+          final List<String> path,
+          final Accept accept) throws Exception {
+    final String basePath = Commons.getEntityBasePath(entitySetName, entityId);
+
+    final Accept acceptType = accept == null || Accept.TEXT == accept
+            ? Accept.XML : accept.getExtension().equals(Accept.JSON.getExtension()) ? Accept.JSON_FULLMETA : accept;
+
+    // read atom
+    InputStream stream = fsManager.readFile(basePath + ENTITY, acceptType);
+
+    // change atom
+    stream = deleteProperty(stream, path);
+
+    // save atom
+    fsManager.putInMemory(stream, fsManager.getAbsolutePath(basePath + ENTITY, acceptType));
+
+    return fsManager.readFile(basePath + ENTITY, acceptType);
+  }
+
   public abstract InputStream readEntities(
           final List<String> links, final String linkName, final String next, final boolean forceFeed)
           throws Exception;
@@ -722,4 +646,22 @@ public abstract class AbstractUtilities {
 
   public abstract InputStream addEditLink(
           final InputStream content, final String title, final String href) throws Exception;
-}
+
+  protected abstract InputStream replaceProperty(
+          final InputStream src, final InputStream replacement, final List<String> path, final boolean justValue)
+          throws Exception;
+
+  protected abstract InputStream deleteProperty(final InputStream src, final List<String> path) throws Exception;
+
+  public abstract InputStream getProperty(
+          final String entitySetName, final String entityId, final List<String> path, final String edmType)
+          throws Exception;
+
+  public abstract InputStream getPropertyValue(final InputStream is, final List<String> path)
+          throws Exception;
+
+  public abstract Map.Entry<String, List<String>> extractLinkURIs(final InputStream is) throws Exception;
+  
+  public abstract Map.Entry<String, List<String>> extractLinkURIs(
+          final String entitySetName, final String entityId, final String linkName) throws Exception;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/8f3541c2/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
index 4dba9da..59fdc35 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
@@ -28,6 +28,8 @@ public class Constants {
 
   public final static String ODATA_METADATA_PREFIX = DEFAULT_SERVICE_URL + "$metadata#";
 
+  public final static String ATOM_DEF_TYPE = "Edm.String";
+
   public final static String ATOM_PROPERTY_PREFIX = "d:";
 
   public final static String ATOM_METADATA_PREFIX = "m:";

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/8f3541c2/fit/src/main/java/org/apache/olingo/fit/utils/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/JSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/JSONUtilities.java
index f979db7..a58f3d2 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/JSONUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/JSONUtilities.java
@@ -176,35 +176,41 @@ public class JSONUtilities extends AbstractUtilities {
     return IOUtils.toInputStream(srcNode.toString());
   }
 
-  protected static InputStream getJsonPropertyValue(final InputStream src, final String name)
+  @Override
+  public InputStream getPropertyValue(final InputStream src, final List<String> path)
           throws Exception {
-    ObjectMapper mapper = new ObjectMapper();
+    final ObjectMapper mapper = new ObjectMapper();
     final JsonNode srcNode = mapper.readTree(src);
-    JsonNode node = getJsonProperty(srcNode, new String[] {name}, 0);
+    JsonNode node = getProperty(srcNode, path, 0);
     return IOUtils.toInputStream(node.asText());
   }
 
-  public static InputStream getJsonProperty(final InputStream src, final String[] path, final String edmType)
+  @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 property = new ObjectNode(JsonNodeFactory.instance);
+    final ObjectNode propertyNode = new ObjectNode(JsonNodeFactory.instance);
 
     if (StringUtils.isNotBlank(edmType)) {
-      property.put(JSON_ODATAMETADATA_NAME, ODATA_METADATA_PREFIX + edmType);
+      propertyNode.put(JSON_ODATAMETADATA_NAME, ODATA_METADATA_PREFIX + edmType);
     }
 
-    JsonNode jsonNode = getJsonProperty(srcNode, path, 0);
+    JsonNode jsonNode = getProperty(srcNode, path, 0);
     if (jsonNode.isObject()) {
-      property.putAll((ObjectNode) jsonNode);
+      propertyNode.putAll((ObjectNode) jsonNode);
     } else {
-      property.put("value", jsonNode.asText());
+      propertyNode.put("value", jsonNode.asText());
     }
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    mapper.writeValue(bos, property);
+    mapper.writeValue(bos, propertyNode);
 
     final InputStream res = new ByteArrayInputStream(bos.toByteArray());
     IOUtils.closeQuietly(bos);
@@ -212,16 +218,16 @@ public class JSONUtilities extends AbstractUtilities {
     return res;
   }
 
-  private static JsonNode getJsonProperty(final JsonNode node, final String[] path, final int index)
+  private JsonNode getProperty(final JsonNode node, final List<String> path, final int index)
           throws NotFoundException {
     final Iterator<Map.Entry<String, JsonNode>> iter = node.fields();
     while (iter.hasNext()) {
       final Map.Entry<String, JsonNode> entry = iter.next();
-      if (path[index].equals(entry.getKey())) {
-        if (path.length - 1 == index) {
+      if (path.get(index).equals(entry.getKey())) {
+        if (path.size() - 1 == index) {
           return entry.getValue();
         } else {
-          return getJsonProperty(entry.getValue(), path, index + 1);
+          return getProperty(entry.getValue(), path, index + 1);
         }
       }
     }
@@ -400,7 +406,16 @@ public class JSONUtilities extends AbstractUtilities {
     return IOUtils.toInputStream(toBeChangedObject.toString());
   }
 
-  public static Map.Entry<String, List<String>> extractLinkURIs(final InputStream is)
+  @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);
@@ -436,4 +451,52 @@ public class JSONUtilities extends AbstractUtilities {
     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/8f3541c2/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java b/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
index eaf3e8c..6ba0ee9 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/XMLEventReaderWrapper.java
@@ -47,30 +47,22 @@ public class XMLEventReaderWrapper implements XMLEventReader {
     this.wrapped = factory.createXMLEventReader(
             new ByteArrayInputStream(
             (XMLEventReaderWrapper.CONTENT_STAG
-            + IOUtils.toString(stream)
+            + IOUtils.toString(stream).replaceAll("^<\\?xml.*\\?>", "")
             + XMLEventReaderWrapper.CONTENT_ETAG).getBytes()));
 
-    init(wrapped);
+    init();
   }
 
-  public XMLEventReaderWrapper(final XMLEventReader wrapped) {
-    this.wrapped = wrapped;
-    init(wrapped);
-  }
-
-  private void init(final XMLEventReader wrapped) {
+  private void init() {
 
     try {
-      this.nextGivenEvent = this.wrapped.nextEvent();
+      do {
 
-      if (this.nextGivenEvent.isStartDocument()) {
-        this.nextGivenEvent = this.wrapped.nextEvent(); // discard start document    
-      }
+        this.nextGivenEvent = this.wrapped.nextEvent();
 
-      if (this.nextGivenEvent.isStartElement()
-              && CONTENT.equals(this.nextGivenEvent.asStartElement().getName().getLocalPart())) {
-        this.nextGivenEvent = this.wrapped.nextEvent(); // discard content start tag
-      }
+      } while (this.nextGivenEvent.isStartDocument()
+              || (this.nextGivenEvent.isStartElement()
+              && CONTENT.equals(this.nextGivenEvent.asStartElement().getName().getLocalPart())));
 
     } catch (Exception ignore) {
       // ignore

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/8f3541c2/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
index bfe5890..564f1f4 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
@@ -24,6 +24,7 @@ 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;
@@ -50,30 +51,165 @@ 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.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemException;
 
 public class XMLUtilities extends AbstractUtilities {
 
-  protected static XMLInputFactory factory = null;
+  protected static XMLInputFactory ifactory = null;
+
+  protected static XMLOutputFactory ofactory = null;
 
   public XMLUtilities(final ODataVersion version) throws Exception {
     super(version);
   }
 
+  public void retrieveLinkInfoFromMetadata() throws Exception {
+
+    final MetadataLinkInfo metadataLinkInfo = new MetadataLinkInfo();
+    Commons.linkInfo.put(version, metadataLinkInfo);
+
+    final InputStream metadata = fsManager.readFile(Constants.METADATA, Accept.XML);
+    final XMLEventReader reader = getEventReader(metadata);
+
+    int initialDepth = 0;
+    try {
+      while (true) {
+        Map.Entry<Integer, XmlElement> entityType =
+                extractElement(reader, null, Collections.<String>singletonList("EntityType"),
+                null, false, initialDepth, 4, 4);
+        initialDepth = entityType.getKey();
+
+        final String entitySetName =
+                entityType.getValue().getStart().getAttributeByName(new QName("Name")).getValue();
+
+        final XMLEventReader entityReader = getEventReader(entityType.getValue().toStream());
+        int size = 0;
+
+        try {
+          while (true) {
+            final XmlElement navProperty =
+                    extractElement(entityReader, null, Collections.<String>singletonList("NavigationProperty"),
+                    null, false, 0, -1, -1).getValue();
+
+            final String linkName = navProperty.getStart().getAttributeByName(new QName("Name")).getValue();
+            final Map.Entry<String, Boolean> target = getTargetInfo(navProperty.getStart(), linkName);
+
+            metadataLinkInfo.addLink(
+                    entitySetName,
+                    linkName,
+                    target.getKey(),
+                    target.getValue());
+
+            size++;
+          }
+        } catch (Exception e) {
+        } finally {
+          entityReader.close();
+        }
+
+        if (size == 0) {
+          metadataLinkInfo.addEntitySet(entitySetName);
+        }
+      }
+    } catch (Exception e) {
+    } finally {
+      reader.close();
+    }
+  }
+
+  private Map.Entry<String, Boolean> getTargetInfo(final StartElement element, final String linkName)
+          throws Exception {
+    final InputStream metadata = fsManager.readFile(Constants.METADATA, Accept.XML);
+    XMLEventReader reader = getEventReader(metadata);
+
+    final String associationName = element.getAttributeByName(new QName("Relationship")).getValue();
+
+    final Map.Entry<Integer, XmlElement> association = extractElement(
+            reader, null, Collections.<String>singletonList("Association"),
+            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>(
+            "Name", associationName.substring(associationName.lastIndexOf(".") + 1))), false,
+            0, 4, 4);
+
+    reader.close();
+    IOUtils.closeQuietly(metadata);
+
+    final InputStream associationContent = association.getValue().toStream();
+    reader = getEventReader(associationContent);
+
+    final Map.Entry<Integer, XmlElement> associationEnd = extractElement(
+            reader, null, Collections.<String>singletonList("End"),
+            Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>("Role", linkName)),
+            false, 0, -1, -1);
+
+    reader.close();
+    IOUtils.closeQuietly(associationContent);
+
+    final String target = associationEnd.getValue().getStart().getAttributeByName(new QName("Type")).getValue();
+    final boolean feed = associationEnd.getValue().getStart().getAttributeByName(
+            new QName("Multiplicity")).getValue().equals("*");
+
+    return new SimpleEntry<String, Boolean>(target, feed);
+  }
+
   @Override
   protected Accept getDefaultFormat() {
     return Accept.ATOM;
   }
 
-  protected static XMLEventReader getEventReader(final InputStream is) throws XMLStreamException {
-    if (factory == null) {
-      factory = XMLInputFactory.newInstance();
+  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());
+      }
     }
-    factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
-    return factory.createXMLEventReader(is);
   }
 
   /**
@@ -91,12 +227,12 @@ public class XMLUtilities extends AbstractUtilities {
     final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    final XMLEventWriter writer = getEventWriter(bos);
     // -----------------------------------------
+    final Map.Entry<Integer, XmlElement> entry =
+            extractElement(reader, writer, Collections.singletonList("entry"), 0, 1, 1);
 
-    final XmlElement entry = getAtomElement(reader, writer, "entry");
-    writer.add(entry.getStart());
+    writer.add(entry.getValue().getStart());
 
     // add for links
     for (String link : links) {
@@ -113,8 +249,8 @@ public class XMLUtilities extends AbstractUtilities {
       writer.add(eventFactory.createEndElement(new QName(LINK), null));
     }
 
-    writer.add(entry.getContentReader());
-    writer.add(entry.getEnd());
+    writer.add(entry.getValue().getContentReader());
+    writer.add(entry.getValue().getEnd());
     writer.add(reader);
     IOUtils.closeQuietly(is);
 
@@ -140,7 +276,7 @@ public class XMLUtilities extends AbstractUtilities {
 
       while (true) {
         final Map.Entry<Integer, XmlElement> linkInfo =
-                getAtomElement(reader, null, LINK, null, startDepth, 2, 2, true);
+                extractElement(reader, null, Collections.<String>singletonList(LINK), startDepth, 2, 2);
 
         startDepth = linkInfo.getKey();
 
@@ -177,8 +313,8 @@ public class XMLUtilities extends AbstractUtilities {
 
       while (true) {
         // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
-        final Map.Entry<Integer, XmlElement> linkInfo = getAtomElement(
-                reader, null, LINK, filter, startDepth, 2, 2, true);
+        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();
 
@@ -186,12 +322,16 @@ public class XMLUtilities extends AbstractUtilities {
         final String href = link.getStart().getAttributeByName(new QName("href")).getValue();
 
         try {
-          final XmlElement inlineElement = getAtomElement(link.getContentReader(), null, INLINE);
+          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 = getAtomElement(inlineReader, null, "entry");
+              final XmlElement entry =
+                      extractElement(inlineReader, null, Collections.<String>singletonList("entry"), 0, -1, -1).
+                      getValue();
               links.addInlines(title, entry.toStream());
             }
           } catch (Exception e) {
@@ -231,8 +371,7 @@ public class XMLUtilities extends AbstractUtilities {
     is.close();
 
     final ByteArrayOutputStream tmpBos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(tmpBos);
+    final XMLEventWriter writer = getEventWriter(tmpBos);
 
     final XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
     // -----------------------------------------
@@ -251,8 +390,9 @@ public class XMLUtilities extends AbstractUtilities {
 
       while (true) {
         // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
-        linkInfo = getAtomElement(
-                reader, writer, LINK, filter, linkInfo == null ? 0 : linkInfo.getKey(), 2, 2, true);
+        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();
@@ -300,104 +440,7 @@ public class XMLUtilities extends AbstractUtilities {
 
   }
 
-  public XmlElement getAtomElement(
-          final InputStream is,
-          final String name)
-          throws Exception {
-    return getAtomElement(is, name, -1, -1);
-  }
-
-  public static XmlElement getAtomElement(
-          final InputStream is,
-          final String name,
-          final int minDepth,
-          final int maxDepth)
-          throws Exception {
-    final XMLEventReader reader = getEventReader(is);
-    final XmlElement res = getAtomElement(reader, null, name, null, 0, minDepth, maxDepth, false).getValue();
-    reader.close();
-
-    return res;
-  }
-
-  public static XmlElement getAtomElement(
-          final XMLEventReader reader,
-          final XMLEventWriter discarded,
-          final String name)
-          throws Exception {
-    return getAtomElement(reader, discarded, name, null, 0, -1, -1, false).getValue();
-  }
-
-  public XmlElement getAtomElement(
-          final XMLEventReader reader,
-          final XMLEventWriter discarded,
-          final String name,
-          final Collection<Map.Entry<String, String>> filterAttrs)
-          throws Exception {
-    return getAtomElement(reader, discarded, name, filterAttrs, 0, -1, -1, false).getValue();
-  }
-
-  public static Map.Entry<Integer, XmlElement> getAtomElement(
-          final XMLEventReader reader,
-          final XMLEventWriter discarded,
-          final String name,
-          final Collection<Map.Entry<String, String>> filterAttrs,
-          final int initialDepth,
-          final int minDepth,
-          final int maxDepth,
-          final boolean filterInOr)
-          throws Exception {
-
-    int depth = initialDepth;
-    StartElement start = null;
-
-    while (reader.hasNext() && start == null) {
-      final XMLEvent event = reader.nextEvent();
-
-      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
-        depth++;
-
-        if ((StringUtils.isBlank(name) || name.trim().equals(event.asStartElement().getName().getLocalPart()))
-                && (minDepth < 0 || minDepth <= depth) && (maxDepth < 0 || maxDepth >= depth)) {
-
-          boolean match = filterAttrs == null || filterAttrs.isEmpty() || !filterInOr;
-
-          for (Map.Entry<String, String> filterAttr : filterAttrs == null
-                  ? Collections.<Map.Entry<String, String>>emptySet() : 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) {
-            start = event.asStartElement();
-          }
-        }
-
-      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
-        depth--;
-      }
-
-      if (start == null) {
-        if (discarded != null) {
-          discarded.add(event);
-        }
-      }
-    }
-
-    if (start == null) {
-      throw new Exception(String.format("Could not find an element named '%s'", name));
-    }
-
-    return new SimpleEntry<Integer, XmlElement>(Integer.valueOf(depth - 1), getAtomElement(start, reader));
-  }
-
-  public static XmlElement getAtomElement(
+  public XmlElement getXmlElement(
           final StartElement start,
           final XMLEventReader reader)
           throws Exception {
@@ -412,11 +455,9 @@ public class XMLUtilities extends AbstractUtilities {
     while (reader.hasNext() && depth > 0) {
       final XMLEvent event = reader.nextEvent();
 
-      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
-              && start.getName().getLocalPart().equals(event.asStartElement().getName().getLocalPart())) {
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
         depth++;
-      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
-              && start.getName().getLocalPart().equals(event.asEndElement().getName().getLocalPart())) {
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
         depth--;
       }
 
@@ -467,7 +508,6 @@ public class XMLUtilities extends AbstractUtilities {
   public InputStream addEditLink(
           final InputStream content, final String title, final String href)
           throws Exception {
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
 
     final ByteArrayOutputStream copy = new ByteArrayOutputStream();
     IOUtils.copy(content, copy);
@@ -477,15 +517,15 @@ public class XMLUtilities extends AbstractUtilities {
     XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
 
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    XMLEventWriter writer = getEventWriter(bos);
 
     final String editLinkElement = String.format("<link rel=\"edit\" title=\"%s\" href=\"%s\" />", title, href);
 
     try {
       // check edit link existence
-      getAtomElement(reader, writer, LINK,
+      extractElement(reader, writer, Collections.<String>singletonList(LINK),
               Collections.<Map.Entry<String, String>>singletonList(
-              new AbstractMap.SimpleEntry<String, String>("rel", "edit")));
+              new AbstractMap.SimpleEntry<String, String>("rel", "edit")), false, 0, -1, -1);
 
       addAtomElement(IOUtils.toInputStream(editLinkElement), writer);
       writer.add(reader);
@@ -495,9 +535,10 @@ public class XMLUtilities extends AbstractUtilities {
       reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
 
       bos = new ByteArrayOutputStream();
-      writer = xof.createXMLEventWriter(bos);
+      writer = getEventWriter(bos);
 
-      final XmlElement entryElement = getAtomElement(reader, writer, "entry");
+      final XmlElement entryElement =
+              extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
 
       writer.add(entryElement.getStart());
 
@@ -520,7 +561,6 @@ public class XMLUtilities extends AbstractUtilities {
   public InputStream addAtomContent(
           final InputStream content, final String title, final String href)
           throws Exception {
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
 
     final ByteArrayOutputStream copy = new ByteArrayOutputStream();
     IOUtils.copy(content, copy);
@@ -530,11 +570,12 @@ public class XMLUtilities extends AbstractUtilities {
     XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
 
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    XMLEventWriter writer = getEventWriter(bos);
 
     try {
       // check edit link existence
-      XmlElement contentElement = getAtomElement(reader, writer, "content");
+      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());
@@ -544,10 +585,11 @@ public class XMLUtilities extends AbstractUtilities {
       reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
 
       bos = new ByteArrayOutputStream();
-      writer = xof.createXMLEventWriter(bos);
+      writer = getEventWriter(bos);
 
       if (isMediaContent(title)) {
-        final XmlElement entryElement = getAtomElement(reader, writer, "entry");
+        final XmlElement entryElement =
+                extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
 
         writer.add(entryElement.getStart());
         writer.add(entryElement.getContentReader());
@@ -559,7 +601,8 @@ public class XMLUtilities extends AbstractUtilities {
         writer.add(entryElement.getEnd());
       } else {
         try {
-          final XmlElement entryElement = getAtomElement(reader, writer, PROPERTIES);
+          final XmlElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList(PROPERTIES), 0, 2, 3).getValue();
 
           addAtomElement(
                   IOUtils.toInputStream("<content type=\"application/xml\">"),
@@ -577,9 +620,10 @@ public class XMLUtilities extends AbstractUtilities {
           reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
 
           bos = new ByteArrayOutputStream();
-          writer = xof.createXMLEventWriter(bos);
+          writer = getEventWriter(bos);
 
-          final XmlElement entryElement = getAtomElement(reader, writer, "entry");
+          final XmlElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
           writer.add(entryElement.getStart());
           writer.add(entryElement.getContentReader());
 
@@ -643,103 +687,101 @@ public class XMLUtilities extends AbstractUtilities {
     return count;
   }
 
-  public static StartElement getPropertyStartTag(final XMLEventReader propReader, final String[] path)
+  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 {
-    int pos = 0;
-
-    StartElement property = null;
-
-    while (propReader.hasNext() && pos < path.length) {
-      final XMLEvent event = propReader.nextEvent();
-
-      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
-              && (ATOM_PROPERTY_PREFIX + path[pos].trim()).equals(
-              event.asStartElement().getName().getLocalPart())) {
-        pos++;
-        if (path.length == pos) {
-          property = event.asStartElement();
-        }
-      }
-    }
-
-    if (property == null) {
-      throw new NotFoundException();
-    }
-
-    return property;
+    return extractElement(reader, writer, path, null, false, startPathPos, minPathPos, maxPathPos);
   }
 
-  public String getEdmTypeFromXML(final InputStream is, final String[] path)
+  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 {
-    final XMLEventReader reader = getEventReader(is);
 
-    final Attribute type = getPropertyStartTag(reader, path).getAttributeByName(new QName(TYPE));
-
-    reader.close();
-
-    if (type == null) {
-      throw new NotFoundException();
-    }
+    StartElement start = null;
+    int searchFor = 0;
+    int depth = startPathPos;
 
-    return type.getValue();
-  }
+    // Current inspected element
+    String current = null;
 
-  public InputStream getAtomProperty(final InputStream is, final String[] path)
-          throws Exception {
-    final XMLEventReader reader = getEventReader(is);
-
-    final XmlElement props = getAtomElement(reader, null, PROPERTIES);
-    final XMLEventReader propsReader = props.getContentReader();
+    // 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;
 
-    reader.close();
+    while (reader.hasNext() && start == null) {
+      final XMLEvent event = reader.nextEvent();
 
-    final InputStream propertyStream = writeFromStartToEndElement(
-            getPropertyStartTag(propsReader, path),
-            propsReader,
-            true);
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
 
-    if (propertyStream == null) {
-      throw new NotFoundException();
-    }
+        if (current != null || ((minPathPos < 0 || minPathPos <= depth) && (maxPathPos < 0 || depth <= maxPathPos))) {
+          if (pathElementNames.isEmpty()
+                  || pathElementNames.get(searchFor).trim().equals(event.asStartElement().getName().getLocalPart())) {
 
-    return propertyStream;
-  }
+            if (searchFor < pathElementNames.size() - 1) {
+              // path exploring not completed
+              writeEvent(event, writer);
+              current = pathElementNames.get(searchFor).trim();
+              searchFor++;
+            } else {
 
-  private InputStream writeFromStartToEndElement(
-          final StartElement element, final XMLEventReader reader, final boolean document)
-          throws XMLStreamException {
-    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(bos);
+              // 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);
+        }
 
-    final QName name = element.getName();
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
 
-    if (document) {
-      final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
-      writer.add(eventFactory.createStartDocument("UTF-8", "1.0"));
-      writer.add(element);
+        writeEvent(event, writer);
 
-      if (element.getAttributeByName(new QName(ATOM_DATASERVICE_NS)) == null) {
-        writer.add(eventFactory.createNamespace(ATOM_PROPERTY_PREFIX.substring(0, 1), DATASERVICES_NS));
-      }
-      if (element.getAttributeByName(new QName(ATOM_METADATA_NS)) == null) {
-        writer.add(eventFactory.createNamespace(ATOM_METADATA_PREFIX.substring(0, 1), METADATA_NS));
+        if (event.asEndElement().getName().getLocalPart().equals(current)) {
+          // back step ....
+          searchFor--;
+          current = searchFor > 0 ? pathElementNames.get(searchFor - 1).trim() : null;
+        }
+      } else {
+        writeEvent(event, writer);
       }
-    } else {
-      writer.add(element);
     }
 
-    XMLEvent event = element;
-
-    while (reader.hasNext() && !(event.isEndElement() && name.equals(event.asEndElement().getName()))) {
-      event = reader.nextEvent();
-      writer.add(event);
+    if (start == null) {
+      throw new NotFoundException();
     }
 
-    writer.flush();
-    writer.close();
-
-    return new ByteArrayInputStream(bos.toByteArray());
+    return new SimpleEntry<Integer, XmlElement>(Integer.valueOf(depth - 1), getXmlElement(start, reader));
   }
 
   public InputStream addAtomInlinecount(
@@ -748,12 +790,12 @@ public class XMLUtilities extends AbstractUtilities {
     final XMLEventReader reader = getEventReader(feed);
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    final XMLEventWriter writer = getEventWriter(bos);
 
     try {
 
-      final XmlElement feedElement = getAtomElement(reader, writer, "feed");
+      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);
@@ -774,28 +816,23 @@ public class XMLUtilities extends AbstractUtilities {
     return new ByteArrayInputStream(bos.toByteArray());
   }
 
-  public static InputStream getAtomPropertyValue(final InputStream is, final String[] path)
+  @Override
+  public InputStream getPropertyValue(final InputStream is, final List<String> path)
           throws Exception {
-    final XmlElement props = getAtomElement(is, PROPERTIES, 2, 3);
-    final XMLEventReader propsReader = props.getContentReader();
 
-    // search for property start element
-    getPropertyStartTag(propsReader, ArrayUtils.subarray(path, 0, path.length - 1));
+    final List<String> pathElements = new ArrayList<String>();
 
-    final InputStream res;
+    for (String element : path) {
+      pathElements.add(ATOM_PROPERTY_PREFIX + element);
+    }
 
-    XMLEvent event = propsReader.nextEvent();
+    final XMLEventReader reader = getEventReader(is);
+    final Map.Entry<Integer, XmlElement> property = extractElement(reader, null, pathElements, 0, 3, 4);
 
-    // expected text node
-    if (event.isCharacters()) {
-      res = new ByteArrayInputStream(event.asCharacters().getData().getBytes());
-    } else if (event.isEndElement()) {
-      throw new NotFoundException();
-    } else {
-      throw new Exception("The method or operation is not implemented.");
-    }
+    reader.close();
+    IOUtils.closeQuietly(is);
 
-    return res;
+    return property.getValue().getContent();
   }
 
   @Override
@@ -803,8 +840,7 @@ public class XMLUtilities extends AbstractUtilities {
     final XMLEventReader reader = getEventReader(entity);
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    final XMLEventWriter writer = getEventWriter(bos);
 
     final List<String> found = new ArrayList<String>(Arrays.asList(propertyNames));
 
@@ -912,7 +948,11 @@ public class XMLUtilities extends AbstractUtilities {
         final Map.Entry<String, String> uri = Commons.parseEntityURI(link);
 
         final XmlElement entry =
-                getAtomElement(readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue(), "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) {
@@ -945,14 +985,14 @@ public class XMLUtilities extends AbstractUtilities {
     XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
 
     final Map.Entry<Integer, XmlElement> propertyElement =
-            getAtomElement(reader, null, PROPERTIES, null, 0, 2, 3, false);
+            extractElement(reader, null, Collections.<String>singletonList(PROPERTIES), 0, 2, 3);
     reader.close();
 
     reader = propertyElement.getValue().getContentReader();
 
     try {
       while (true) {
-        final XmlElement property = getAtomElement(reader, null, null);
+        final XmlElement property = extractElement(reader, null, null, 0, -1, -1).getValue();
         res.put(property.getStart().getName().getLocalPart(), property.toStream());
       }
     } catch (Exception ignore) {
@@ -968,7 +1008,7 @@ public class XMLUtilities extends AbstractUtilities {
       int pos = 0;
       while (true) {
         final Map.Entry<Integer, XmlElement> linkElement =
-                getAtomElement(reader, null, LINK, null, pos, 2, 2, false);
+                extractElement(reader, null, Collections.<String>singletonList(LINK), pos, 2, 2);
 
         res.put("[LINK]" + linkElement.getValue().getStart().getAttributeByName(new QName("title")).getValue(),
                 linkElement.getValue().toStream());
@@ -990,14 +1030,13 @@ public class XMLUtilities extends AbstractUtilities {
     XMLEventReader reader = getEventReader(toBeChanged);
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    XMLEventWriter writer = getEventWriter(bos);
 
     // ---------------------------------
     // add property changes
     // ---------------------------------
     Map.Entry<Integer, XmlElement> propertyElement =
-            getAtomElement(reader, writer, PROPERTIES, null, 0, 2, 3, false);
+            extractElement(reader, writer, Collections.<String>singletonList(PROPERTIES), 0, 2, 3);
 
     writer.flush();
 
@@ -1008,7 +1047,7 @@ public class XMLUtilities extends AbstractUtilities {
 
     try {
       while (true) {
-        final XmlElement property = getAtomElement(propertyReader, null, null);
+        final XmlElement property = extractElement(propertyReader, null, null, 0, -1, -1).getValue();
         final String name = property.getStart().getName().getLocalPart();
 
         if (properties.containsKey(name)) {
@@ -1058,14 +1097,14 @@ public class XMLUtilities extends AbstractUtilities {
         reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
 
         bos.reset();
-        writer = xof.createXMLEventWriter(bos);
+        writer = getEventWriter(bos);
 
         try {
           final String linkName = remains.getKey().substring(remains.getKey().indexOf("]") + 1);
 
-          getAtomElement(reader, writer, LINK,
-                  Collections.<Map.Entry<String, String>>singleton(new SimpleEntry<String, String>(
-                  "title", linkName)), 0, 2, 2, false);
+          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);
 
@@ -1081,9 +1120,9 @@ public class XMLUtilities extends AbstractUtilities {
     reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
 
     bos.reset();
-    writer = xof.createXMLEventWriter(bos);
+    writer = getEventWriter(bos);
 
-    propertyElement = getAtomElement(reader, writer, CONTENT, null, 0, 2, 2, false);
+    propertyElement = extractElement(reader, writer, Collections.<String>singletonList(CONTENT), 0, 2, 2);
     writer.flush();
 
     pbos.reset();
@@ -1122,17 +1161,16 @@ public class XMLUtilities extends AbstractUtilities {
     final XMLEventReader reader = getEventReader(toBeChanged);
 
     final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-    final XMLOutputFactory xof = XMLOutputFactory.newInstance();
-    final XMLEventWriter writer = xof.createXMLEventWriter(bos);
+    final XMLEventWriter writer = getEventWriter(bos);
 
     final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
     XMLEvent newLine = eventFactory.createSpace("\n");
 
     try {
       final XmlElement linkElement =
-              getAtomElement(reader, writer, LINK,
-              Collections.<Map.Entry<String, String>>singletonList(
-              new SimpleEntry<String, String>("title", linkName)));
+              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());
 
       // ------------------------------------------
@@ -1160,7 +1198,42 @@ public class XMLUtilities extends AbstractUtilities {
     return new ByteArrayInputStream(bos.toByteArray());
   }
 
-  public static Map.Entry<String, List<String>> extractLinkURIs(final InputStream is)
+  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);
@@ -1170,7 +1243,8 @@ public class XMLUtilities extends AbstractUtilities {
     final List<String> links = new ArrayList<String>();
     try {
       while (true) {
-        links.add(IOUtils.toString(getAtomElement(reader, null, "uri").getContent()));
+        links.add(IOUtils.toString(extractElement(reader, null, Collections.<String>singletonList("uri"), 0, -1, -1).
+                getValue().getContent()));
       }
     } catch (Exception ignore) {
       // End document reached ...
@@ -1181,7 +1255,8 @@ public class XMLUtilities extends AbstractUtilities {
 
     reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
     try {
-      next = IOUtils.toString(getAtomElement(reader, null, "next").getContent());
+      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;
@@ -1190,4 +1265,122 @@ public class XMLUtilities extends AbstractUtilities {
 
     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());
+  }
 }