[OLINGO-1155]Delta support in Json format

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

Branch: refs/heads/master
Commit: f7e5a5c716821bb376ce1143b8b24e5ae1abd6b3
Parents: f6dd0de
Author: Archana Rai <[email protected]>
Authored: Mon Jul 31 16:13:11 2017 +0530
Committer: Archana Rai <[email protected]>
Committed: Mon Jul 31 16:13:11 2017 +0530

----------------------------------------------------------------------
 .../apache/olingo/commons/api/Constants.java    |   14 +
 .../olingo/commons/api/data/DeletedEntity.java  |    2 +-
 .../api/edm/constants/ODataServiceVersion.java  |   10 +-
 .../commons/api/format/PreferenceName.java      |    1 +
 .../org/apache/olingo/server/api/OData.java     |   10 +
 .../api/serializer/EdmDeltaSerializer.java      |   40 +
 .../api/serializer/SerializerException.java     |    2 +
 .../olingo/server/api/uri/UriInfoAll.java       |    6 +
 .../olingo/server/api/uri/UriInfoCrossjoin.java |    6 +
 .../olingo/server/api/uri/UriInfoResource.java  |    8 +-
 .../api/uri/queryoption/DeltaTokenOption.java   |   28 +
 .../uri/queryoption/SystemQueryOptionKind.java  |    7 +-
 .../server/core/RequestURLHierarchyVisitor.java |   12 +-
 .../olingo/server/core/RequestURLVisitor.java   |    5 +-
 .../apache/olingo/server/core/ODataImpl.java    |   30 +
 .../olingo/server/core/debug/DebugTabUri.java   |    6 +-
 .../server/core/prefer/PreferencesImpl.java     |    3 +-
 .../serializer/json/JsonDeltaSerializer.java    |  555 ++++++++++
 .../JsonDeltaSerializerWithNavigations.java     |  655 ++++++++++++
 .../serializer/json/ODataJsonSerializer.java    |   23 +-
 .../olingo/server/core/uri/UriInfoImpl.java     |    7 +
 .../olingo/server/core/uri/parser/Parser.java   |    9 +
 .../uri/queryoption/DeltaTokenOptionImpl.java   |   39 +
 .../server/core/uri/validator/UriValidator.java |   43 +-
 .../server-core-exceptions-i18n.properties      |    1 +
 .../olingo/server/core/uri/UriInfoImplTest.java |   10 +-
 .../olingo/server/tecsvc/data/DataCreator.java  |   41 +-
 .../olingo/server/tecsvc/data/DataProvider.java |   88 +-
 .../processor/TechnicalEntityProcessor.java     |  109 +-
 .../tecsvc/processor/TechnicalProcessor.java    |   19 +
 .../queryoptions/options/DeltaTokenHandler.java |   54 +
 .../tecsvc/provider/ContainerProvider.java      |   14 +-
 .../tecsvc/provider/EntityTypeProvider.java     |   13 +-
 .../server/tecsvc/provider/SchemaProvider.java  |    1 +
 .../json/JsonDeltaSerializerTest.java           |  731 +++++++++++++
 .../JsonDeltaSerializerWithNavigationsTest.java | 1012 ++++++++++++++++++
 36 files changed, 3567 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
index fb8a6b3..e41abd4 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
@@ -319,4 +319,18 @@ public interface Constants {
   String ASSOCIATION_LINK_TYPE = 
ContentType.APPLICATION_XML.toContentTypeString();
   String ENTITY_COLLECTION_BINDING_LINK_TYPE = 
ContentType.APPLICATION_XML.toContentTypeString();
   String ENTITY_BINDING_LINK_TYPE = 
ContentType.APPLICATION_XML.toContentTypeString();
+  
+ //For v4.01 Delta
+  
+  String LINK = "/$link";
+  String DELETEDLINK = "/$deletedLink";
+  String DELTA = "/$delta";
+  String DELTAVALUE = "delta";
+  String AT = "@";
+  String DELETEDENTITY = "/$deletedEntity";
+  String DELTALINK = "@deltaLink";
+  String HASH = "#";
+  String REMOVED = "removed";
+  String ENTITY = "/$entity";
+  String REASON = "Reason";
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/DeletedEntity.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/DeletedEntity.java
 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/DeletedEntity.java
index a5a6a43..87dc3f9 100644
--- 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/DeletedEntity.java
+++ 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/DeletedEntity.java
@@ -23,7 +23,7 @@ import java.net.URI;
 /**
  * A deleted entity contains the reason for deletion and the id.
  */
-public class DeletedEntity {
+public class DeletedEntity extends Entity{
   
   /**
    * Reason of the removal from the list

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java
 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java
index ef7cb65..c781191 100644
--- 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java
+++ 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java
@@ -40,7 +40,12 @@ public enum ODataServiceVersion {
   /**
    * OData Version 4.0
    */
-  V40("4.0");
+  V40("4.0"),
+  /**
+   * OData Version 4.01
+   */
+  V401("4.01");
+
 
   private static final Pattern DATASERVICEVERSIONPATTERN = 
Pattern.compile("(\\p{Digit}+\\.\\p{Digit}+)(:?;.*)?");
 
@@ -57,7 +62,8 @@ public enum ODataServiceVersion {
       return V10.toString().equals(possibleDataServiceVersion)
           || V20.toString().equals(possibleDataServiceVersion)
           || V30.toString().equals(possibleDataServiceVersion)
-          || V40.toString().equals(possibleDataServiceVersion);
+          || V40.toString().equals(possibleDataServiceVersion)
+          || V401.toString().equals(possibleDataServiceVersion);
     } else {
       throw new IllegalArgumentException(version);
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/PreferenceName.java
----------------------------------------------------------------------
diff --git 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/PreferenceName.java
 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/PreferenceName.java
index f77e615..5ee884c 100644
--- 
a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/PreferenceName.java
+++ 
b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/PreferenceName.java
@@ -29,6 +29,7 @@ public enum PreferenceName {
   INCLUDE_ANNOTATIONS("odata.include-annotations"),
   MAX_PAGE_SIZE("odata.maxpagesize"),
   TRACK_CHANGES("odata.track-changes"),
+  TRACK_CHANGES_PREF("track-changes"),
   RETURN("return"),
   RESPOND_ASYNC("respond-async"),
   WAIT("wait"),

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java
index f72803b..9e00413 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java
@@ -35,6 +35,7 @@ import org.apache.olingo.server.api.etag.ETagHelper;
 import org.apache.olingo.server.api.etag.ServiceMetadataETagSupport;
 import org.apache.olingo.server.api.prefer.Preferences;
 import org.apache.olingo.server.api.serializer.EdmAssistedSerializer;
+import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
 import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
 import org.apache.olingo.server.api.serializer.ODataSerializer;
 import org.apache.olingo.server.api.serializer.SerializerException;
@@ -184,4 +185,13 @@ public abstract class OData {
    */
   public abstract EdmAssistedSerializer createEdmAssistedSerializer(final 
ContentType contentType)
       throws SerializerException;
+  
+  /**
+   * Creates a new serializer object capable of working without EDM information
+   * for rendering delta content in the specified format.
+   * @param contentType a content type supported by Olingo
+   * @param version versions supported by Olingo
+   */
+  public abstract EdmDeltaSerializer createEdmDeltaSerializer(final 
ContentType contentType,
+      final List<String> versions) throws SerializerException;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EdmDeltaSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EdmDeltaSerializer.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EdmDeltaSerializer.java
new file mode 100644
index 0000000..9195294
--- /dev/null
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EdmDeltaSerializer.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.api.serializer;
+
+import org.apache.olingo.commons.api.data.Delta;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.server.api.ServiceMetadata;
+
+public interface EdmDeltaSerializer {
+  
+  /**
+   * Writes collection of delta-response into an InputStream.
+   * Information from the EDM is used in addition to information from the data 
and preferred,
+   * but the serializer works without any EDM information as well.
+   * Linked data is always written as expanded items (so closed reference 
loops have to be avoided).
+   * @param metadata             metadata for the service
+   * @param referencedEntityType the {@link EdmEntityType} or 
<code>null</code> if not available
+   * @param delta     the delta data as entity collection
+   * @param options              options for the serializer
+   */
+  SerializerResult entityCollection(ServiceMetadata metadata, EdmEntityType 
referencedEntityType,
+      Delta delta, EntityCollectionSerializerOptions options) throws 
SerializerException;
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
index be3416b..62a0dd5 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
@@ -41,6 +41,8 @@ public class SerializerException extends 
ODataLibraryException {
     INCONSISTENT_PROPERTY_TYPE,
     /** parameter: property name */
     MISSING_PROPERTY,
+    /** parameter: Delta property name */
+    MISSING_DELTA_PROPERTY,
     /** parameter: - */
     MISSING_ID,
     /** parameters: property name, property value */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoAll.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoAll.java 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoAll.java
index cc63abd..ec58a0d 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoAll.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoAll.java
@@ -22,6 +22,7 @@ import java.util.List;
 
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
 import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
+import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
 import org.apache.olingo.server.api.uri.queryoption.SearchOption;
 import org.apache.olingo.server.api.uri.queryoption.SkipOption;
@@ -68,4 +69,9 @@ public interface UriInfoAll {
    * @return Object containing information of the $top option
    */
   TopOption getTopOption();
+  
+  /**
+   * @return Object containing information of the $deltatoken option
+   */
+  DeltaTokenOption getDeltaTokenOption();
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoCrossjoin.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoCrossjoin.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoCrossjoin.java
index 8fdba84..1718812 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoCrossjoin.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoCrossjoin.java
@@ -21,6 +21,7 @@ package org.apache.olingo.server.api.uri;
 import java.util.List;
 
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
+import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@@ -91,4 +92,9 @@ public interface UriInfoCrossjoin {
    * @return Object containing information of the $top option
    */
   TopOption getTopOption();
+  
+  /**
+   * @return Object containing information of the $deltatoken option
+   */
+  DeltaTokenOption getDeltaTokenOption();
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoResource.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoResource.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoResource.java
index 05e010e..cb5f826 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoResource.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/UriInfoResource.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
 import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
+import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@@ -69,7 +70,12 @@ public interface UriInfoResource {
    * @return Object containing information of the $count option
    */
   CountOption getCountOption();
-
+  
+  /**
+   * @return Object containing information of the $deltatoken option
+   */
+  DeltaTokenOption getDeltaTokenOption();
+  
   /**
    * @return Object containing information of the $orderby option
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/DeltaTokenOption.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/DeltaTokenOption.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/DeltaTokenOption.java
new file mode 100644
index 0000000..51773b7
--- /dev/null
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/DeltaTokenOption.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.api.uri.queryoption;
+
+public interface DeltaTokenOption extends SystemQueryOption {
+  
+  /**
+   * @return Value of $deltatoken
+   */
+  String getValue();
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
index 3cfe47f..76a762d 100644
--- 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/SystemQueryOptionKind.java
@@ -83,7 +83,12 @@ public enum SystemQueryOptionKind {
    * @see LevelsExpandOption
    */
   LEVELS("$levels"),
-
+  
+  /**
+   * @see deltaTokenOption
+   */
+  DELTATOKEN("$deltatoken"),
+  
   /**
    * @see ApplyOption
    */

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
index bc48f71..96be8a4 100644
--- 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
+++ 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
@@ -47,6 +47,7 @@ import org.apache.olingo.server.api.uri.UriResourceRoot;
 import org.apache.olingo.server.api.uri.UriResourceSingleton;
 import org.apache.olingo.server.api.uri.UriResourceValue;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
+import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@@ -259,7 +260,10 @@ public class RequestURLHierarchyVisitor implements 
RequestURLVisitor {
     if (info.getSkipTokenOption() != null) {
       visit(info.getSkipTokenOption());
     }
-
+    
+    if (info.getDeltaTokenOption() != null) {
+      visit(info.getDeltaTokenOption());
+    }
   }
 
   @Override
@@ -313,7 +317,11 @@ public class RequestURLHierarchyVisitor implements 
RequestURLVisitor {
   @Override
   public void visit(UriResourceRef info) {
   }
-
+  
+  @Override
+  public void visit(DeltaTokenOption option) {
+  }
+  
   @Override
   public void visit(UriResourceRoot info) {
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
index f3f4027..5fda0e4 100644
--- 
a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
+++ 
b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
@@ -43,6 +43,7 @@ import org.apache.olingo.server.api.uri.UriResourceRoot;
 import org.apache.olingo.server.api.uri.UriResourceSingleton;
 import org.apache.olingo.server.api.uri.UriResourceValue;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
+import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.FilterOption;
 import org.apache.olingo.server.api.uri.queryoption.FormatOption;
@@ -96,7 +97,9 @@ public interface RequestURLVisitor {
   void visit(TopOption option);
 
   void visit(UriResourceCount option);
-
+  
+  void visit(DeltaTokenOption option);
+  
   void visit(UriResourceRef info);
 
   void visit(UriResourceRoot info);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
index 08693aa..ef42e62 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java
@@ -39,6 +39,7 @@ import org.apache.olingo.server.api.etag.ETagHelper;
 import org.apache.olingo.server.api.etag.ServiceMetadataETagSupport;
 import org.apache.olingo.server.api.prefer.Preferences;
 import org.apache.olingo.server.api.serializer.EdmAssistedSerializer;
+import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
 import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
 import org.apache.olingo.server.api.serializer.ODataSerializer;
 import org.apache.olingo.server.api.serializer.SerializerException;
@@ -53,6 +54,8 @@ import org.apache.olingo.server.core.prefer.PreferencesImpl;
 import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl;
 import org.apache.olingo.server.core.serializer.json.EdmAssistedJsonSerializer;
 import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer;
+import org.apache.olingo.server.core.serializer.json.JsonDeltaSerializer;
+import 
org.apache.olingo.server.core.serializer.json.JsonDeltaSerializerWithNavigations;
 import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializer;
 import org.apache.olingo.server.core.uri.UriHelperImpl;
 
@@ -96,6 +99,33 @@ public class ODataImpl extends OData {
     throw new SerializerException("Unsupported format: " + 
contentType.toContentTypeString(),
         SerializerException.MessageKeys.UNSUPPORTED_FORMAT, 
contentType.toContentTypeString());
   }
+  
+  
+  @Override
+  public EdmDeltaSerializer createEdmDeltaSerializer(final ContentType 
contentType, final List<String> versions)
+      throws SerializerException {
+    if (contentType.isCompatible(ContentType.APPLICATION_JSON)) {
+      if(versions!=null && versions.size()>0){
+       return getMaxVersion(versions)>4 ?  new 
JsonDeltaSerializerWithNavigations(contentType):
+         new JsonDeltaSerializer(contentType);
+      }
+      return new JsonDeltaSerializerWithNavigations(contentType);
+    }
+    throw new SerializerException("Unsupported format: " + 
contentType.toContentTypeString(),
+        SerializerException.MessageKeys.UNSUPPORTED_FORMAT, 
contentType.toContentTypeString());
+  }
+
+  private float getMaxVersion(List<String> versions) {
+    Float versionValue [] = new Float [versions.size()];
+    int i=0;
+    Float max=new Float(0);
+    for(String version:versions){
+     Float ver = Float.valueOf(version);
+     versionValue[i++] = ver;
+     max = max > ver ? max : ver ;
+   }
+    return max;
+  }
 
   @Override
   public ODataHttpHandler createHandler(final ServiceMetadata serviceMetadata) 
{

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java
index 007b7c6..2acbc48 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java
@@ -105,7 +105,11 @@ public class DebugTabUri implements DebugTab {
     } else if (uriInfo.getKind() == UriInfoKind.entityId) {
       appendType(gen, "typeCast", 
uriInfo.asUriInfoEntityId().getEntityTypeCast());
     }
-
+    
+    if (uriInfo.getDeltaTokenOption() != null) {
+      gen.writeStringField("deltatoken", 
uriInfo.getDeltaTokenOption().getValue());
+    }
+    
     if (uriInfo.getFormatOption() != null) {
       gen.writeStringField("format", uriInfo.getFormatOption().getFormat());
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core/src/main/java/org/apache/olingo/server/core/prefer/PreferencesImpl.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/prefer/PreferencesImpl.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/prefer/PreferencesImpl.java
index 7997d57..a757674 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/prefer/PreferencesImpl.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/prefer/PreferencesImpl.java
@@ -78,7 +78,8 @@ public class PreferencesImpl implements Preferences {
 
   @Override
   public boolean hasTrackChanges() {
-    return preferences.containsKey(PreferenceName.TRACK_CHANGES.getName());
+    return (preferences.containsKey(PreferenceName.TRACK_CHANGES.getName())
+        
||preferences.containsKey(PreferenceName.TRACK_CHANGES_PREF.getName()));
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializer.java
new file mode 100644
index 0000000..0708d5d
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializer.java
@@ -0,0 +1,555 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.AbstractEntityCollection;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.DeletedEntity;
+import org.apache.olingo.commons.api.data.Delta;
+import org.apache.olingo.commons.api.data.DeltaLink;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.format.ContentType;
+import 
org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
+import 
org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.serializer.SerializerResult;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
+import org.apache.olingo.server.core.serializer.SerializerResultImpl;
+import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
+import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
+import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
+import org.apache.olingo.server.core.uri.UriHelperImpl;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class JsonDeltaSerializer implements EdmDeltaSerializer {
+
+  private static final String LINK = "/$link";
+  private static final String DELETEDLINK = "/$deletedLink";
+  private static final String DELTA = "/$delta";
+  private static final String HASH = "#";
+  private static final String DELETEDENTITY = "/$deletedEntity";
+  private static final String ENTITY = "/$entity";
+  private static final String REASON = "Reason";
+  private static final String IO_EXCEPTION_TEXT = "An I/O exception occurred.";
+  private final boolean isIEEE754Compatible;
+  private final boolean isODataMetadataNone;
+  private final boolean isODataMetadataFull;
+
+  public JsonDeltaSerializer(final ContentType contentType) {
+    isIEEE754Compatible = 
ContentTypeHelper.isODataIEEE754Compatible(contentType);
+    isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
+    isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
+  }
+
+  @Override
+  public SerializerResult entityCollection(ServiceMetadata metadata, 
EdmEntityType referencedEntityType, Delta delta,
+      EntityCollectionSerializerOptions options) throws SerializerException {
+    OutputStream outputStream = null;
+    SerializerException cachedException = null;
+    try {
+      CircleStreamBuffer buffer = new CircleStreamBuffer();
+      outputStream = buffer.getOutputStream();
+      JsonGenerator json = new JsonFactory().createGenerator(outputStream);
+      boolean pagination = false;
+      json.writeStartObject();
+
+      final ContextURL contextURL = checkContextURL(options == null ? null : 
options.getContextURL());
+      writeContextURL(contextURL, json);
+
+      if (options != null && options.getCount() != null && 
options.getCount().getValue()) {
+        writeInlineCount(delta.getCount(), json);
+      }
+      json.writeFieldName(Constants.VALUE);
+      writeEntitySet(metadata, referencedEntityType, delta, options, json);
+
+      pagination = writeNextLink(delta, json);
+      writeDeltaLink(delta, json, pagination);
+
+      json.close();
+      outputStream.close();
+      return 
SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final IOException e) {
+      cachedException =
+          new SerializerException(IO_EXCEPTION_TEXT, e, 
SerializerException.MessageKeys.IO_EXCEPTION);
+      throw cachedException;
+    } finally {
+      closeCircleStreamBufferOutput(outputStream, cachedException);
+    }
+
+  }
+
+  protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
+      final Delta entitySet, final EntityCollectionSerializerOptions options,
+      final JsonGenerator json) throws IOException,
+      SerializerException {
+    json.writeStartArray();
+    for (final Entity entity : entitySet.getEntities()) {
+      writeAddedUpdatedEntity(metadata, entityType, entity, 
options.getExpand(),
+          options.getSelect(), options.getContextURL(), false, 
options.getContextURL()
+              .getEntitySetOrSingletonOrType(), json);
+    }
+    for (final DeletedEntity deletedEntity : entitySet.getDeletedEntities()) {
+      writeDeletedEntity(deletedEntity, options, json);
+    }
+    for (final DeltaLink addedLink : entitySet.getAddedLinks()) {
+      writeLink(addedLink, options, json, true);
+    }
+    for (final DeltaLink deletedLink : entitySet.getDeletedLinks()) {
+      writeLink(deletedLink, options, json, false);
+    }
+    json.writeEndArray();
+  }
+
+  private void writeLink(DeltaLink link, EntityCollectionSerializerOptions 
options,
+      JsonGenerator json, boolean isAdded) throws IOException, 
SerializerException {
+    try {
+      json.writeStartObject();
+      String entityId = 
options.getContextURL().getEntitySetOrSingletonOrType();
+      String operation = isAdded ? LINK : DELETEDLINK;
+      json.writeStringField(Constants.JSON_CONTEXT, HASH + entityId + 
operation);
+      if (link != null) {
+        if (link.getSource() != null) {
+          json.writeStringField(Constants.ATTR_SOURCE, 
link.getSource().toString());
+        } else {
+          throw new SerializerException("DeltaLink source is null.", 
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Source");
+        }
+        if (link.getRelationship() != null) {
+          json.writeStringField(Constants.ATTR_RELATIONSHIP, 
link.getRelationship().toString());
+        } else {
+          throw new SerializerException("DeltaLink relationship is null.",
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Relationship");
+        }
+        if (link.getTarget() != null) {
+          json.writeStringField(Constants.ERROR_TARGET, 
link.getTarget().toString());
+        } else {
+          throw new SerializerException("DeltaLink target is null.", 
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Target");
+        }
+      } else {
+        throw new SerializerException("DeltaLink is null.", 
+            SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Delta 
Link");
+      }
+      json.writeEndObject();
+    } catch (IOException e) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+  }
+
+  private void writeDeletedEntity(DeletedEntity deletedEntity,
+      EntityCollectionSerializerOptions options, JsonGenerator json) throws 
IOException, SerializerException {
+    if (deletedEntity.getId() == null) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+    if (deletedEntity.getReason() == null) {
+      throw new SerializerException("DeletedEntity reason is null.", 
+          SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, REASON);
+    }
+    json.writeStartObject();
+    json.writeStringField(Constants.JSON_CONTEXT, HASH + 
deletedEntity.getId().toASCIIString() + DELETEDENTITY);
+    json.writeStringField(Constants.JSON_ID, 
deletedEntity.getId().toASCIIString());
+    json.writeStringField(Constants.ELEM_REASON, 
deletedEntity.getReason().name());
+    json.writeEndObject();
+
+  }
+
+  public void writeAddedUpdatedEntity(final ServiceMetadata metadata, final 
EdmEntityType entityType,
+      final Entity entity, final ExpandOption expand, final SelectOption 
select, final ContextURL url,
+      final boolean onlyReference, String name, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartObject();
+    if (entity.getId() != null && url != null) {
+      String entityId = entity.getId().toString();
+      name = url.getEntitySetOrSingletonOrType();
+      if (!entityId.contains(name)) {
+        String entityName = entityId.substring(0, entityId.indexOf("("));
+        if (!entityName.equals(name)) {
+          json.writeStringField(Constants.JSON_CONTEXT, HASH + entityName + 
ENTITY);
+        }
+      }
+    }
+    json.writeStringField(Constants.JSON_ID, getEntityId(entity, entityType, 
name));
+    writeProperties(metadata, entityType, entity.getProperties(), select, 
json);
+    json.writeEndObject();
+
+  }
+
+  private Property findProperty(final String propertyName, final 
List<Property> properties) {
+    for (final Property property : properties) {
+      if (propertyName.equals(property.getName())) {
+        return property;
+      }
+    }
+    return null;
+  }
+
+  protected void writeProperty(final ServiceMetadata metadata,
+      final EdmProperty edmProperty, final Property property,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+    boolean isStreamProperty = isStreamProperty(edmProperty);
+    if (property != null) {
+      if (!isStreamProperty) {
+        json.writeFieldName(edmProperty.getName());
+      }
+      writePropertyValue(metadata, edmProperty, property, selectedPaths, json);
+    }
+
+  }
+
+  private boolean isStreamProperty(EdmProperty edmProperty) {
+    final EdmType type = edmProperty.getType();
+    return (edmProperty.isPrimitive() && type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream));
+  }
+
+  private void writePropertyValue(final ServiceMetadata metadata, final 
EdmProperty edmProperty,
+      final Property property, final Set<List<String>> selectedPaths, final 
JsonGenerator json)
+      throws IOException, SerializerException {
+    final EdmType type = edmProperty.getType();
+    try {
+      if (edmProperty.isPrimitive()
+          || type.getKind() == EdmTypeKind.ENUM || type.getKind() == 
EdmTypeKind.DEFINITION) {
+        if (edmProperty.isCollection()) {
+          writePrimitiveCollection((EdmPrimitiveType) type, property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), 
edmProperty.isUnicode(), json);
+        } else {
+          writePrimitive((EdmPrimitiveType) type, property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), 
edmProperty.isUnicode(), json);
+        }
+      } else if (property.isComplex()) {
+        if (edmProperty.isCollection()) {
+          writeComplexCollection(metadata, (EdmComplexType) type, property, 
selectedPaths, json);
+        } else {
+          writeComplex(metadata, (EdmComplexType) type, property, 
selectedPaths, json);
+        }
+      } else {
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
edmProperty.getName());
+      }
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          edmProperty.getName(), property.getValue().toString());
+    }
+  }
+
+  protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, 
final EdmComplexType baseType,
+      final String derivedTypeName) throws SerializerException {
+
+    String fullQualifiedName = 
baseType.getFullQualifiedName().getFullQualifiedNameAsString();
+    if (derivedTypeName == null ||
+        fullQualifiedName.equals(derivedTypeName)) {
+      return baseType;
+    }
+    EdmComplexType derivedType = metadata.getEdm().getComplexType(new 
FullQualifiedName(derivedTypeName));
+    if (derivedType == null) {
+      throw new SerializerException("Complex Type not found",
+          SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+    }
+    EdmComplexType type = derivedType.getBaseType();
+    while (type != null) {
+      if (type.getFullQualifiedName().equals(baseType.getFullQualifiedName())) 
{
+        return derivedType;
+      }
+      type = type.getBaseType();
+    }
+    throw new SerializerException("Wrong base type",
+        SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName,
+        baseType.getFullQualifiedName().getFullQualifiedNameAsString());
+  }
+
+  private void writeComplex(final ServiceMetadata metadata, final 
EdmComplexType type,
+      final Property property, final Set<List<String>> selectedPaths, final 
JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartObject();
+    String derivedName = property.getType();
+    final EdmComplexType resolvedType = resolveComplexType(metadata, 
(EdmComplexType) type, derivedName);
+    if (!isODataMetadataNone && !resolvedType.equals(type) || 
isODataMetadataFull) {
+      json.writeStringField(Constants.JSON_TYPE, "#" + property.getType());
+    }
+    writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), 
selectedPaths,
+        json);
+    json.writeEndObject();
+  }
+
+  private void writePrimitiveCollection(final EdmPrimitiveType type, final 
Property property,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartArray();
+    for (Object value : property.asCollection()) {
+      switch (property.getValueType()) {
+      case COLLECTION_PRIMITIVE:
+      case COLLECTION_ENUM:
+        try {
+          writePrimitiveValue(property.getName(), type, value, isNullable,
+              maxLength, precision, scale, isUnicode, json);
+        } catch (EdmPrimitiveTypeException e) {
+          throw new SerializerException("Wrong value for property!", e,
+              SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+              property.getName(), property.getValue().toString());
+        }
+        break;
+      case COLLECTION_GEOSPATIAL:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      }
+    }
+    json.writeEndArray();
+  }
+
+  private void writeComplexCollection(final ServiceMetadata metadata, final 
EdmComplexType type,
+      final Property property,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartArray();
+    for (Object value : property.asCollection()) {
+      switch (property.getValueType()) {
+      case COLLECTION_COMPLEX:
+        json.writeStartObject();
+        if (isODataMetadataFull) {
+          json.writeStringField(Constants.JSON_TYPE, "#" +
+              type.getFullQualifiedName().getFullQualifiedNameAsString());
+        }
+        writeComplexValue(metadata, type, ((ComplexValue) value).getValue(), 
selectedPaths, json);
+        json.writeEndObject();
+        break;
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      }
+    }
+    json.writeEndArray();
+  }
+
+  private void writePrimitive(final EdmPrimitiveType type, final Property 
property,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json)
+      throws EdmPrimitiveTypeException, IOException, SerializerException {
+    if (property.isPrimitive()) {
+      writePrimitiveValue(property.getName(), type, property.asPrimitive(),
+          isNullable, maxLength, precision, scale, isUnicode, json);
+    } else if (property.isGeospatial()) {
+      throw new SerializerException("Property type not yet supported!",
+          SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+    } else if (property.isEnum()) {
+      writePrimitiveValue(property.getName(), type, property.asEnum(),
+          isNullable, maxLength, precision, scale, isUnicode, json);
+    } else {
+      throw new SerializerException("Inconsistent property type!",
+          SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, 
property.getName());
+    }
+  }
+
+  protected void writePrimitiveValue(final String name, final EdmPrimitiveType 
type, final Object primitiveValue,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json) throws 
EdmPrimitiveTypeException, IOException {
+    final String value = type.valueToString(primitiveValue,
+        isNullable, maxLength, precision, scale, isUnicode);
+    if (value == null) {
+      json.writeNull();
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
+      json.writeBoolean(Boolean.parseBoolean(value));
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)
+        || (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)
+            || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64))
+            && !isIEEE754Compatible) {
+      json.writeNumber(value);
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) {
+      if (primitiveValue instanceof Link) {
+        Link stream = (Link) primitiveValue;
+        if (!isODataMetadataNone) {
+          if (stream.getMediaETag() != null) {
+            json.writeStringField(name + Constants.JSON_MEDIA_ETAG, 
stream.getMediaETag());
+          }
+          if (stream.getType() != null) {
+            json.writeStringField(name + Constants.JSON_MEDIA_CONTENT_TYPE, 
stream.getType());
+          }
+        }
+        if (isODataMetadataFull) {
+          if (stream.getRel() != null && 
stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {
+            json.writeStringField(name + Constants.JSON_MEDIA_READ_LINK, 
stream.getHref());
+          }
+          if (stream.getRel() == null || 
stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {
+            json.writeStringField(name + Constants.JSON_MEDIA_EDIT_LINK, 
stream.getHref());
+          }
+        }
+      }
+    } else {
+      json.writeString(value);
+    }
+  }
+
+  protected void writeComplexValue(final ServiceMetadata metadata,
+      final EdmComplexType type, final List<Property> properties,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+
+    for (final String propertyName : type.getPropertyNames()) {
+      final Property property = findProperty(propertyName, properties);
+      if (selectedPaths == null || 
ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
+        writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), 
property,
+            selectedPaths == null ? null : 
ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
+            json);
+      }
+    }
+  }
+
+  protected void writeProperties(final ServiceMetadata metadata, final 
EdmStructuredType type,
+      final List<Property> properties,
+      final SelectOption select, final JsonGenerator json)
+      throws IOException, SerializerException {
+    final boolean all = ExpandSelectHelper.isAll(select);
+    final Set<String> selected = all ? new HashSet<String>() : 
ExpandSelectHelper.getSelectedPropertyNames(select
+        .getSelectItems());
+    for (final String propertyName : type.getPropertyNames()) {
+      if (all || selected.contains(propertyName)) {
+        final EdmProperty edmProperty = 
type.getStructuralProperty(propertyName);
+        final Property property = findProperty(propertyName, properties);
+        final Set<List<String>> selectedPaths = all || 
edmProperty.isPrimitive() ? null : ExpandSelectHelper
+            .getSelectedPaths(select.getSelectItems(), propertyName);
+        writeProperty(metadata, edmProperty, property, selectedPaths, json);
+      }
+    }
+  }
+
+  /**
+   * Get the ascii representation of the entity id
+   * or thrown an {@link SerializerException} if id is <code>null</code>.
+   *
+   * @param entity the entity
+   * @return ascii representation of the entity id
+   */
+  private String getEntityId(Entity entity, EdmEntityType entityType, String 
name) throws SerializerException {
+    try {
+      if (entity != null) {
+        if (entity.getId() == null) {
+          if (entityType == null || entityType.getKeyPredicateNames() == null
+              || name == null) {
+            throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+          } else {
+            final UriHelper uriHelper = new UriHelperImpl();
+            entity.setId(URI.create(name + '(' + 
uriHelper.buildKeyPredicate(entityType, entity) + ')'));
+            return entity.getId().toASCIIString();
+          }
+        } else {
+          return entity.getId().toASCIIString();
+        }
+      } 
+      return null;
+    } catch (Exception e) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+  }
+
+  void writeInlineCount(final Integer count, final JsonGenerator json)
+      throws IOException {
+    if (count != null) {
+      String countValue = isIEEE754Compatible ? String.valueOf(count) : 
String.valueOf(count);
+      json.writeStringField(Constants.JSON_COUNT, countValue);
+    }
+  }
+
+  ContextURL checkContextURL(final ContextURL contextURL) throws 
SerializerException {
+    if (isODataMetadataNone) {
+      return null;
+    } else if (contextURL == null) {
+      throw new SerializerException("ContextURL null!", 
SerializerException.MessageKeys.NO_CONTEXT_URL);
+    }
+    return contextURL;
+  }
+
+  void writeContextURL(final ContextURL contextURL, final JsonGenerator json) 
throws IOException {
+    if (!isODataMetadataNone && contextURL != null) {
+      json.writeStringField(Constants.JSON_CONTEXT, 
ContextURLBuilder.create(contextURL).toASCIIString() + DELTA);
+    }
+  }
+
+  boolean writeNextLink(final AbstractEntityCollection entitySet, final 
JsonGenerator json)
+      throws IOException {
+    if (entitySet.getNext() != null) {
+      json.writeStringField(Constants.JSON_NEXT_LINK, 
entitySet.getNext().toASCIIString());
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void writeDeltaLink(final AbstractEntityCollection entitySet, final 
JsonGenerator json, boolean pagination)
+      throws IOException {
+    if (entitySet.getDeltaLink() != null && !pagination) {
+      json.writeStringField(Constants.JSON_DELTA_LINK, 
entitySet.getDeltaLink().toASCIIString());
+    }
+  }
+  
+  protected void closeCircleStreamBufferOutput(final OutputStream outputStream,
+      final SerializerException cachedException)
+      throws SerializerException {
+    if (outputStream != null) {
+      try {
+        outputStream.close();
+      } catch (IOException e) {
+        if (cachedException != null) {
+          throw cachedException;
+        } else {
+          throw new SerializerException(IO_EXCEPTION_TEXT, e,
+              SerializerException.MessageKeys.IO_EXCEPTION);
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/f7e5a5c7/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializerWithNavigations.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializerWithNavigations.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializerWithNavigations.java
new file mode 100644
index 0000000..d2831b7
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/JsonDeltaSerializerWithNavigations.java
@@ -0,0 +1,655 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.AbstractEntityCollection;
+import org.apache.olingo.commons.api.data.Annotation;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.DeletedEntity;
+import org.apache.olingo.commons.api.data.Delta;
+import org.apache.olingo.commons.api.data.DeltaLink;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.format.ContentType;
+import 
org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.EdmDeltaSerializer;
+import 
org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.serializer.SerializerResult;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.api.uri.queryoption.CountOption;
+import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
+import org.apache.olingo.server.core.serializer.SerializerResultImpl;
+import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
+import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
+import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
+import org.apache.olingo.server.core.uri.UriHelperImpl;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class JsonDeltaSerializerWithNavigations implements EdmDeltaSerializer {
+  private static final String IO_EXCEPTION_TEXT = "An I/O exception occurred.";
+  private final boolean isIEEE754Compatible;
+  private final boolean isODataMetadataNone;
+  private final boolean isODataMetadataFull;
+
+  public JsonDeltaSerializerWithNavigations(final ContentType contentType) {
+    isIEEE754Compatible = 
ContentTypeHelper.isODataIEEE754Compatible(contentType);
+    isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
+    isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
+  }
+
+  @Override
+  public SerializerResult entityCollection(ServiceMetadata metadata, 
EdmEntityType referencedEntityType, Delta delta,
+      EntityCollectionSerializerOptions options) throws SerializerException {
+    OutputStream outputStream = null;
+    SerializerException cachedException = null;
+    boolean pagination = false;
+    try {
+      CircleStreamBuffer buffer = new CircleStreamBuffer();
+      outputStream = buffer.getOutputStream();
+      JsonGenerator json = new JsonFactory().createGenerator(outputStream);
+      json.writeStartObject();
+
+      final ContextURL contextURL = checkContextURL(options == null ? null : 
options.getContextURL());
+      writeContextURL(contextURL, json);
+
+      if (options != null && options.getCount() != null && 
options.getCount().getValue()) {
+        writeInlineCount(delta.getCount(), json);
+      }
+      json.writeFieldName(Constants.VALUE);
+      writeEntitySet(metadata, referencedEntityType, delta, options, json);
+
+      pagination = writeNextLink(delta, json);
+      writeDeltaLink(delta, json, pagination);
+
+      json.close();
+      outputStream.close();
+      return 
SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final IOException e) {
+      cachedException =
+          new SerializerException(IO_EXCEPTION_TEXT, e, 
SerializerException.MessageKeys.IO_EXCEPTION);
+      throw cachedException;
+    } finally {
+      closeCircleStreamBufferOutput(outputStream, cachedException);
+    }
+
+  }
+  
+  protected void closeCircleStreamBufferOutput(final OutputStream outputStream,
+      final SerializerException cachedException)
+      throws SerializerException {
+    if (outputStream != null) {
+      try {
+        outputStream.close();
+      } catch (IOException e) {
+        if (cachedException != null) {
+          throw cachedException;
+        } else {
+          throw new SerializerException(IO_EXCEPTION_TEXT, e,
+              SerializerException.MessageKeys.IO_EXCEPTION);
+        }
+      }
+    }
+  }
+  protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
+      final Delta entitySet, final EntityCollectionSerializerOptions options,
+      final JsonGenerator json) throws IOException,
+      SerializerException {
+    json.writeStartArray();
+    for (final Entity entity : entitySet.getEntities()) {
+      writeAddedUpdatedEntity(metadata, entityType, entity, 
options.getExpand(), options.getSelect(),
+          options.getContextURL(), false, 
options.getContextURL().getEntitySetOrSingletonOrType(), json);
+    }
+    for (final DeletedEntity deletedEntity : entitySet.getDeletedEntities()) {
+      writeDeletedEntity(deletedEntity, json);
+    }
+    for (final DeltaLink addedLink : entitySet.getAddedLinks()) {
+      writeLink(addedLink, options, json, true);
+    }
+    for (final DeltaLink deletedLink : entitySet.getDeletedLinks()) {
+      writeLink(deletedLink, options, json, false);
+    }
+    json.writeEndArray();
+  }
+
+  private void writeLink(DeltaLink link, EntityCollectionSerializerOptions 
options,
+      JsonGenerator json, boolean isAdded) throws IOException, 
SerializerException {
+    try {
+      json.writeStartObject();
+      String entityId = 
options.getContextURL().getEntitySetOrSingletonOrType();// throw error if not 
set id
+      String operation = isAdded ? Constants.LINK : Constants.DELETEDLINK;
+      json.writeStringField(Constants.AT + Constants.CONTEXT, Constants.HASH + 
entityId + operation);
+      if (link != null) {
+        if (link.getSource() != null) {
+          json.writeStringField(Constants.ATTR_SOURCE, 
link.getSource().toString());
+        } else {
+          throw new SerializerException("DeltaLink source is null.",
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Source");
+        }
+        if (link.getRelationship() != null) {
+          json.writeStringField(Constants.ATTR_RELATIONSHIP, 
link.getRelationship().toString());
+        } else {
+          throw new SerializerException("DeltaLink relationship is null.",
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Relationship");
+        }
+        if (link.getTarget() != null) {
+          json.writeStringField(Constants.ERROR_TARGET, 
link.getTarget().toString());
+        } else {
+          throw new SerializerException("DeltaLink target is null.",
+              SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
"Target");
+        }
+      } else {
+        throw new SerializerException("DeltaLink is null.",
+            SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, "Delta 
Link");
+      }
+      json.writeEndObject();
+    } catch (IOException e) {
+      throw new SerializerException("Entity id is null.",
+          SerializerException.MessageKeys.MISSING_ID);
+    }
+  }
+
+  private void writeDeletedEntity(Entity deletedEntity,
+      JsonGenerator json) throws IOException, SerializerException {
+    if (deletedEntity.getId() == null) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+    json.writeStartObject();
+    if (isODataMetadataFull) {
+
+      json.writeStringField(Constants.AT + Constants.CONTEXT, Constants.HASH + 
deletedEntity.getId().toASCIIString()
+          + Constants.DELETEDENTITY);
+    }
+    if (((DeletedEntity) deletedEntity).getReason() == null) {
+      throw new SerializerException("DeletedEntity reason is null.",
+          SerializerException.MessageKeys.MISSING_DELTA_PROPERTY, 
Constants.REASON);
+    }
+    json.writeFieldName(Constants.AT + Constants.REMOVED);
+    json.writeStartObject();
+    json.writeStringField(Constants.ELEM_REASON,
+        ((DeletedEntity) deletedEntity).getReason().name());
+    List<Annotation> annotations = deletedEntity.getAnnotations();
+    if (annotations != null && annotations.size() > 0) {
+      for (Annotation annotation : annotations) {
+        json.writeStringField(Constants.AT + annotation.getTerm(), 
annotation.getValue().toString());
+      }
+    }
+    json.writeEndObject();
+    List<Property> properties = deletedEntity.getProperties();
+    if (properties != null && properties.size() > 0) {
+      for (Property property : properties) {
+        json.writeStringField(property.getName(), 
property.getValue().toString());
+      }
+    }
+    json.writeStringField(Constants.AT + Constants.ATOM_ATTR_ID, 
deletedEntity.getId().toASCIIString());
+    json.writeEndObject();
+
+  }
+
+  public void writeAddedUpdatedEntity(final ServiceMetadata metadata, final 
EdmEntityType entityType,
+      final Entity entity, final ExpandOption expand, final SelectOption 
select, final ContextURL url,
+      final boolean onlyReference, String name, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartObject();
+    if (entity.getId() != null && url != null) {
+      name = url.getEntitySetOrSingletonOrType();
+      String entityId = entity.getId().toString();
+      if (!entityId.contains(name)) {
+        String entityName = entityId.substring(0, entityId.indexOf("("));
+        if (!entityName.equals(name)) {
+          json.writeStringField(Constants.AT + Constants.CONTEXT, 
Constants.HASH + entityName
+              + Constants.ENTITY);
+        }
+      }
+    }
+    json.writeStringField(Constants.AT + Constants.ATOM_ATTR_ID, 
getEntityId(entity, entityType, name));
+    writeProperties(metadata, entityType, entity.getProperties(), select, 
json);
+    writeNavigationProperties(metadata, entityType, entity, expand, name, 
json);
+    json.writeEndObject();
+
+  }
+
+  private Property findProperty(final String propertyName, final 
List<Property> properties) {
+    for (final Property property : properties) {
+      if (propertyName.equals(property.getName())) {
+        return property;
+      }
+    }
+    return null;
+  }
+
+  protected void writeProperty(final ServiceMetadata metadata,
+      final EdmProperty edmProperty, final Property property,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+    boolean isStreamProperty = isStreamProperty(edmProperty);
+    if (property != null) {
+      if (!isStreamProperty) {
+        json.writeFieldName(edmProperty.getName());
+      }
+      writePropertyValue(metadata, edmProperty, property, selectedPaths, json);
+    }
+  }
+
+  private boolean isStreamProperty(EdmProperty edmProperty) {
+    final EdmType type = edmProperty.getType();
+    return (edmProperty.isPrimitive() && type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream));
+  }
+
+  private void writePropertyValue(final ServiceMetadata metadata, final 
EdmProperty edmProperty,
+      final Property property, final Set<List<String>> selectedPaths, final 
JsonGenerator json)
+      throws IOException, SerializerException {
+    final EdmType type = edmProperty.getType();
+    try {
+      if (edmProperty.isPrimitive()
+          || type.getKind() == EdmTypeKind.ENUM || type.getKind() == 
EdmTypeKind.DEFINITION) {
+        if (edmProperty.isCollection()) {
+          writePrimitiveCollection((EdmPrimitiveType) type, property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), 
edmProperty.isUnicode(), json);
+        } else {
+          writePrimitive((EdmPrimitiveType) type, property,
+              edmProperty.isNullable(), edmProperty.getMaxLength(),
+              edmProperty.getPrecision(), edmProperty.getScale(), 
edmProperty.isUnicode(), json);
+        }
+      } else if (property.isComplex()) {
+        if (edmProperty.isCollection()) {
+          writeComplexCollection(metadata, (EdmComplexType) type, property, 
selectedPaths, json);
+        } else {
+          writeComplex(metadata, (EdmComplexType) type, property, 
selectedPaths, json);
+        }
+      } else {
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
edmProperty.getName());
+      }
+    } catch (final EdmPrimitiveTypeException e) {
+      throw new SerializerException("Wrong value for property!", e,
+          SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+          edmProperty.getName(), property.getValue().toString());
+    }
+  }
+
+  protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, 
final EdmComplexType baseType,
+      final String derivedTypeName) throws SerializerException {
+
+    String fullQualifiedName = 
baseType.getFullQualifiedName().getFullQualifiedNameAsString();
+    if (derivedTypeName == null ||
+        fullQualifiedName.equals(derivedTypeName)) {
+      return baseType;
+    }
+    EdmComplexType derivedType = metadata.getEdm().getComplexType(new 
FullQualifiedName(derivedTypeName));
+    if (derivedType == null) {
+      throw new SerializerException("Complex Type not found",
+          SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+    }
+    EdmComplexType type = derivedType.getBaseType();
+    while (type != null) {
+      if (type.getFullQualifiedName().equals(baseType.getFullQualifiedName())) 
{
+        return derivedType;
+      }
+      type = type.getBaseType();
+    }
+    throw new SerializerException("Wrong base type",
+        SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName,
+        baseType.getFullQualifiedName().getFullQualifiedNameAsString());
+  }
+
+  private void writeComplex(final ServiceMetadata metadata, final 
EdmComplexType type,
+      final Property property, final Set<List<String>> selectedPaths, final 
JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartObject();
+    String derivedName = property.getType();
+    final EdmComplexType resolvedType = resolveComplexType(metadata, 
(EdmComplexType) type, derivedName);
+    if (!isODataMetadataNone && !resolvedType.equals(type) || 
isODataMetadataFull) {
+      json.writeStringField(Constants.JSON_TYPE, "#" + property.getType());
+    }
+    writeComplexValue(metadata, resolvedType, property.asComplex().getValue(), 
selectedPaths,
+        json);
+    json.writeEndObject();
+  }
+
+  private void writePrimitiveCollection(final EdmPrimitiveType type, final 
Property property,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartArray();
+    for (Object value : property.asCollection()) {
+      switch (property.getValueType()) {
+      case COLLECTION_PRIMITIVE:
+      case COLLECTION_ENUM:
+        try {
+          writePrimitiveValue(property.getName(), type, value, isNullable,
+              maxLength, precision, scale, isUnicode, json);
+        } catch (EdmPrimitiveTypeException e) {
+          throw new SerializerException("Wrong value for property!", e,
+              SerializerException.MessageKeys.WRONG_PROPERTY_VALUE,
+              property.getName(), property.getValue().toString());
+        }
+        break;
+      case COLLECTION_GEOSPATIAL:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      }
+    }
+    json.writeEndArray();
+  }
+
+  private void writeComplexCollection(final ServiceMetadata metadata, final 
EdmComplexType type,
+      final Property property,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+    json.writeStartArray();
+    for (Object value : property.asCollection()) {
+      switch (property.getValueType()) {
+      case COLLECTION_COMPLEX:
+        json.writeStartObject();
+        if (isODataMetadataFull) {
+          json.writeStringField(Constants.JSON_TYPE, "#" +
+              type.getFullQualifiedName().getFullQualifiedNameAsString());
+        }
+        writeComplexValue(metadata, type, ((ComplexValue) value).getValue(), 
selectedPaths, json);
+        json.writeEndObject();
+        break;
+      default:
+        throw new SerializerException("Property type not yet supported!",
+            SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+      }
+    }
+    json.writeEndArray();
+  }
+
+  private void writePrimitive(final EdmPrimitiveType type, final Property 
property,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json)
+      throws EdmPrimitiveTypeException, IOException, SerializerException {
+    if (property.isPrimitive()) {
+      writePrimitiveValue(property.getName(), type, property.asPrimitive(),
+          isNullable, maxLength, precision, scale, isUnicode, json);
+    } else if (property.isGeospatial()) {
+      throw new SerializerException("Property type not yet supported!",
+          SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, 
property.getName());
+    } else if (property.isEnum()) {
+      writePrimitiveValue(property.getName(), type, property.asEnum(),
+          isNullable, maxLength, precision, scale, isUnicode, json);
+    } else {
+      throw new SerializerException("Inconsistent property type!",
+          SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, 
property.getName());
+    }
+  }
+
+  protected void writePrimitiveValue(final String name, final EdmPrimitiveType 
type, final Object primitiveValue,
+      final Boolean isNullable, final Integer maxLength, final Integer 
precision, final Integer scale,
+      final Boolean isUnicode, final JsonGenerator json) throws 
EdmPrimitiveTypeException, IOException {
+    final String value = type.valueToString(primitiveValue,
+        isNullable, maxLength, precision, scale, isUnicode);
+    if (value == null) {
+      json.writeNull();
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
+      json.writeBoolean(Boolean.parseBoolean(value));
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
+        || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)
+        || (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)
+            || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64))
+            && !isIEEE754Compatible) {
+      json.writeNumber(value);
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) {
+      if (primitiveValue instanceof Link) {
+        Link stream = (Link) primitiveValue;
+        if (!isODataMetadataNone) {
+          if (stream.getMediaETag() != null) {
+            json.writeStringField(name + Constants.JSON_MEDIA_ETAG, 
stream.getMediaETag());
+          }
+          if (stream.getType() != null) {
+            json.writeStringField(name + Constants.JSON_MEDIA_CONTENT_TYPE, 
stream.getType());
+          }
+        }
+        if (isODataMetadataFull) {
+          if (stream.getRel() != null && 
stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {
+            json.writeStringField(name + Constants.JSON_MEDIA_READ_LINK, 
stream.getHref());
+          }
+          if (stream.getRel() == null || 
stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {
+            json.writeStringField(name + Constants.JSON_MEDIA_EDIT_LINK, 
stream.getHref());
+          }
+        }
+      }
+    } else {
+      json.writeString(value);
+    }
+  }
+
+  protected void writeComplexValue(final ServiceMetadata metadata,
+      final EdmComplexType type, final List<Property> properties,
+      final Set<List<String>> selectedPaths, final JsonGenerator json)
+      throws IOException, SerializerException {
+
+    for (final String propertyName : type.getPropertyNames()) {
+      final Property property = findProperty(propertyName, properties);
+      if (selectedPaths == null || 
ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
+        writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), 
property,
+            selectedPaths == null ? null : 
ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
+            json);
+      }
+    }
+  }
+
+  protected void writeProperties(final ServiceMetadata metadata, final 
EdmStructuredType type,
+      final List<Property> properties,
+      final SelectOption select, final JsonGenerator json)
+      throws IOException, SerializerException {
+    final boolean all = ExpandSelectHelper.isAll(select);
+    final Set<String> selected = all ? new HashSet<String>() : 
ExpandSelectHelper.getSelectedPropertyNames(select
+        .getSelectItems());
+    for (final String propertyName : type.getPropertyNames()) {
+      if ((all || selected.contains(propertyName)) && properties.size() > 0) {
+        final EdmProperty edmProperty = 
type.getStructuralProperty(propertyName);
+        final Property property = findProperty(propertyName, properties);
+        final Set<List<String>> selectedPaths = all || 
edmProperty.isPrimitive() ? null : ExpandSelectHelper
+            .getSelectedPaths(select.getSelectItems(), propertyName);
+        writeProperty(metadata, edmProperty, property, selectedPaths, json);
+      }
+    }
+  }
+
+  protected void writeNavigationProperties(final ServiceMetadata metadata,
+      final EdmStructuredType type, final Linked linked, final ExpandOption 
expand,
+      final String name, final JsonGenerator json) throws SerializerException, 
IOException {
+    if (ExpandSelectHelper.hasExpand(expand)) {
+      final boolean expandAll = ExpandSelectHelper.getExpandAll(expand) != 
null;
+      final Set<String> expanded = expandAll ? new HashSet<String>() : 
ExpandSelectHelper.getExpandedPropertyNames(
+          expand.getExpandItems());
+      for (final String propertyName : type.getNavigationPropertyNames()) {
+        if (expandAll || expanded.contains(propertyName)) {
+          final EdmNavigationProperty property = 
type.getNavigationProperty(propertyName);
+          final Link navigationLink = 
linked.getNavigationLink(property.getName());
+          final ExpandItem innerOptions = expandAll ? null : 
ExpandSelectHelper.getExpandItem(expand.getExpandItems(),
+              propertyName);
+          if (innerOptions != null && innerOptions.getLevelsOption() != null) {
+            throw new SerializerException("Expand option $levels is not 
supported.",
+                SerializerException.MessageKeys.NOT_IMPLEMENTED);
+          }
+          if (navigationLink != null) {
+            writeExpandedNavigationProperty(metadata, property, navigationLink,
+                innerOptions == null ? null : innerOptions.getExpandOption(),
+                innerOptions == null ? null : innerOptions.getSelectOption(),
+                innerOptions == null ? null : innerOptions.getCountOption(),
+                innerOptions == null ? false : innerOptions.hasCountPath(),
+                innerOptions == null ? false : innerOptions.isRef(),
+                name, json);
+          }
+        }
+      }
+    }
+  }
+
+  protected void writeEntitySet(final ServiceMetadata metadata, final 
EdmEntityType entityType,
+      final AbstractEntityCollection entitySet, final ExpandOption expand, 
final SelectOption select,
+      final boolean onlyReference, String name, final JsonGenerator json) 
throws IOException,
+      SerializerException {
+    json.writeStartArray();
+    for (final Entity entity : entitySet) {
+      if (onlyReference) {
+        json.writeStartObject();
+        json.writeStringField(Constants.JSON_ID, getEntityId(entity, 
entityType, null));
+        json.writeEndObject();
+      } else {
+        if (entity instanceof DeletedEntity) {
+          writeDeletedEntity(entity, json);
+        } else {
+          writeAddedUpdatedEntity(metadata, entityType, entity, expand, 
select, null, false, name, json);
+        }
+
+      }
+    }
+    json.writeEndArray();
+  }
+
+  protected void writeExpandedNavigationProperty(
+      final ServiceMetadata metadata, final EdmNavigationProperty property,
+      final Link navigationLink, final ExpandOption innerExpand,
+      final SelectOption innerSelect, final CountOption innerCount,
+      final boolean writeOnlyCount, final boolean writeOnlyRef, final String 
name,
+      final JsonGenerator json) throws IOException, SerializerException {
+
+    if (property.isCollection()) {
+      if (navigationLink != null && navigationLink.getInlineEntitySet() != 
null) {
+        json.writeFieldName(property.getName() + Constants.AT + 
Constants.DELTAVALUE);
+        writeEntitySet(metadata, property.getType(), 
navigationLink.getInlineEntitySet(), innerExpand,
+            innerSelect, writeOnlyRef, name, json);
+      }
+
+    } else {
+      json.writeFieldName(property.getName()+ Constants.AT + 
Constants.DELTAVALUE);
+      if (navigationLink != null && navigationLink.getInlineEntity() != null) {
+        if (navigationLink.getInlineEntity() instanceof DeletedEntity) {
+          writeDeletedEntity(navigationLink.getInlineEntity(), json);
+        } else {
+          writeAddedUpdatedEntity(metadata, property.getType(), 
navigationLink.getInlineEntity(),
+              innerExpand, innerSelect, null, writeOnlyRef, name, json);
+        }
+      }
+    }
+  }
+
+  /**
+   * Get the ascii representation of the entity id
+   * or thrown an {@link SerializerException} if id is <code>null</code>.
+   *
+   * @param entity the entity
+   * @return ascii representation of the entity id
+   */
+  private String getEntityId(Entity entity, EdmEntityType entityType, String 
name) throws SerializerException {
+    try {
+      if (entity != null) {
+        if (entity.getId() == null) {
+          if (entityType == null || entityType.getKeyPredicateNames() == null
+              || name == null) {
+            throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+          } else {
+            final UriHelper uriHelper = new UriHelperImpl();
+            entity.setId(URI.create(name + '(' + 
uriHelper.buildKeyPredicate(entityType, entity) + ')'));
+            return entity.getId().toASCIIString();
+          }
+        } else {
+          return entity.getId().toASCIIString();
+        }
+      }
+      return null;
+    } catch (Exception e) {
+      throw new SerializerException("Entity id is null.", 
SerializerException.MessageKeys.MISSING_ID);
+    }
+  }
+
+  void writeInlineCount(final Integer count, final JsonGenerator json)
+      throws IOException {
+    if (count != null) {
+      String countValue = isIEEE754Compatible ? String.valueOf(count) : 
String.valueOf(count);
+      json.writeStringField(Constants.AT + Constants.ATOM_ELEM_COUNT, 
countValue);
+    }
+  }
+
+  ContextURL checkContextURL(final ContextURL contextURL) throws 
SerializerException {
+    if (isODataMetadataNone) {
+      return null;
+    } else if (contextURL == null) {
+      throw new SerializerException("ContextURL null!", 
SerializerException.MessageKeys.NO_CONTEXT_URL);
+    }
+    return contextURL;
+  }
+
+  void writeContextURL(final ContextURL contextURL, final JsonGenerator json) 
throws IOException {
+    if (!isODataMetadataNone && contextURL != null) {
+      String context = Constants.AT + Constants.CONTEXT;
+      json.writeStringField(context, 
ContextURLBuilder.create(contextURL).toASCIIString() + Constants.DELTA);
+    }
+  }
+
+  boolean writeNextLink(final AbstractEntityCollection entitySet, final 
JsonGenerator json)
+      throws IOException {
+    if (entitySet.getNext() != null) {
+      json.writeStringField(Constants.JSON_NEXT_LINK, 
entitySet.getNext().toASCIIString());
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void writeDeltaLink(final AbstractEntityCollection entitySet, final 
JsonGenerator json,
+      final boolean pagination)
+      throws IOException {
+    if (entitySet.getDeltaLink() != null && !pagination) {
+      json.writeStringField(Constants.DELTALINK, 
entitySet.getDeltaLink().toASCIIString());
+
+    }
+  }
+}

Reply via email to