You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2016/10/07 12:57:32 UTC

[1/2] olingo-odata4 git commit: [OLINGO-935] $apply as option for $expand should be possible

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 72fcaa1a5 -> 9e67d0e3e


[OLINGO-935] $apply as option for $expand should be possible

Signed-off-by: Christian Amend <ch...@sap.com>


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

Branch: refs/heads/master
Commit: 1600684d4f7dab8db570d02212f4bd12bb73a1a0
Parents: 72fcaa1
Author: Klaus Straubinger <kl...@sap.com>
Authored: Fri Oct 7 14:36:51 2016 +0200
Committer: Christian Amend <ch...@sap.com>
Committed: Fri Oct 7 14:51:57 2016 +0200

----------------------------------------------------------------------
 .../server/api/uri/queryoption/ExpandItem.java  |   5 +
 .../server/core/uri/parser/ExpandParser.java    |   7 +
 .../core/uri/queryoption/ExpandItemImpl.java    |  12 +-
 .../core/uri/queryoption/ExpandOptionImpl.java  |   3 +-
 .../core/uri/queryoption/OrderByItemImpl.java   |   4 +-
 .../core/uri/queryoption/SearchOptionImpl.java  |   3 +-
 .../olingo/server/core/uri/UriInfoImplTest.java |   6 +-
 .../core/uri/parser/ExpandParserTest.java       | 491 ++++++++-----------
 .../server/core/uri/parser/UriParserTest.java   |  24 +-
 .../core/uri/testutil/ExpandValidator.java      |  37 +-
 .../core/uri/testutil/FilterTreeToText.java     |  12 +-
 .../core/uri/testutil/TestUriValidator.java     |  28 +-
 12 files changed, 284 insertions(+), 348 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
index 4e71157..8ae8898 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/uri/queryoption/ExpandItem.java
@@ -73,6 +73,11 @@ public interface ExpandItem {
   ExpandOption getExpandOption();
 
   /**
+   * @return Information on the option $apply when used within $expand
+   */
+  ApplyOption getApplyOption();
+
+  /**
    * @return A {@link UriInfoResource} object containing the resource path segments to be expanded
    */
   UriInfoResource getResourcePath();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
index 8a493ff..53a14d7 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/parser/ExpandParser.java
@@ -50,6 +50,7 @@ import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.apply.DynamicStructuredType;
 import org.apache.olingo.server.core.uri.validator.UriValidationException;
 
 public class ExpandParser {
@@ -276,6 +277,12 @@ public class ExpandParser {
           topOption.setValue(value);
           systemQueryOption = topOption;
 
+        } else if (!forRef && !forCount && tokenizer.next(TokenKind.APPLY)) {
+          ParserHelper.requireNext(tokenizer, TokenKind.EQ);
+          systemQueryOption = new ApplyParser(edm, odata).parse(tokenizer,
+              // Data aggregation may change the structure of the result, so we create a new dynamic type.
+              new DynamicStructuredType(referencedType), null, aliases);
+
         } else {
           throw new UriParserSyntaxException("Allowed query option expected.",
               UriParserSyntaxException.MessageKeys.SYNTAX);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java
index c21aa26..a1dc64b 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandItemImpl.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.ex.ODataRuntimeException;
 import org.apache.olingo.server.api.uri.UriInfoResource;
+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.ExpandItem;
 import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
@@ -45,6 +46,7 @@ public class ExpandItemImpl implements ExpandItem {
   private CountOption inlineCountOption;
   private SelectOption selectOption;
   private ExpandOption expandOption;
+  private ApplyOption applyOption;
 
   private UriInfoResource resourceInfo;
 
@@ -56,7 +58,10 @@ public class ExpandItemImpl implements ExpandItem {
 
   public ExpandItemImpl setSystemQueryOption(final SystemQueryOption sysItem) {
 
-    if (sysItem instanceof ExpandOption) {
+    if (sysItem instanceof ApplyOption) {
+      validateDoubleSystemQueryOption(applyOption, sysItem);
+      applyOption = (ApplyOption) sysItem;
+    } else if (sysItem instanceof ExpandOption) {
       validateDoubleSystemQueryOption(expandOption, sysItem);
       expandOption = (ExpandOption) sysItem;
     } else if (sysItem instanceof FilterOption) {
@@ -148,6 +153,11 @@ public class ExpandItemImpl implements ExpandItem {
     return expandOption;
   }
 
+  @Override
+  public ApplyOption getApplyOption() {
+    return applyOption;
+  }
+
   public ExpandItemImpl setResourcePath(final UriInfoResource resourceInfo) {
     this.resourceInfo = resourceInfo;
     return this;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandOptionImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandOptionImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandOptionImpl.java
index 87e2b6c..bda2957 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandOptionImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/ExpandOptionImpl.java
@@ -34,8 +34,9 @@ public class ExpandOptionImpl extends SystemQueryOptionImpl implements ExpandOpt
     setKind(SystemQueryOptionKind.EXPAND);
   }
 
-  public void addExpandItem(final ExpandItem expandItem) {
+  public ExpandOptionImpl addExpandItem(final ExpandItem expandItem) {
     expandItems.add(expandItem);
+    return this;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/OrderByItemImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/OrderByItemImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/OrderByItemImpl.java
index ad57d6b..ae3d66c 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/OrderByItemImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/OrderByItemImpl.java
@@ -32,7 +32,7 @@ public class OrderByItemImpl implements OrderByItem {
     return descending;
   }
 
-  public OrderByItem setDescending(final boolean descending) {
+  public OrderByItemImpl setDescending(final boolean descending) {
     this.descending = descending;
     return this;
   }
@@ -42,7 +42,7 @@ public class OrderByItemImpl implements OrderByItem {
     return expression;
   }
 
-  public OrderByItem setExpression(final Expression expression) {
+  public OrderByItemImpl setExpression(final Expression expression) {
     this.expression = expression;
     return this;
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
index 45a0cd4..813798e 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/queryoption/SearchOptionImpl.java
@@ -35,7 +35,8 @@ public class SearchOptionImpl extends SystemQueryOptionImpl implements SearchOpt
     return searchExpression;
   }
 
-  public void setSearchExpression(final SearchExpression searchExpression) {
+  public SearchOptionImpl setSearchExpression(final SearchExpression searchExpression) {
     this.searchExpression = searchExpression;
+    return this;
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java
index bdc2986..ac35292 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/uri/UriInfoImplTest.java
@@ -33,6 +33,7 @@ import org.apache.olingo.server.api.uri.UriResourceEntitySet;
 import org.apache.olingo.server.api.uri.queryoption.AliasQueryOption;
 import org.apache.olingo.server.api.uri.queryoption.QueryOption;
 import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.ApplyOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
 import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
@@ -107,6 +108,7 @@ public class UriInfoImplTest {
 
   @Test
   public void customQueryOption() {
+    final QueryOption apply = new ApplyOptionImpl().setName("");
     final QueryOption expand = new ExpandOptionImpl().setName("");
     final QueryOption filter = new FilterOptionImpl().setName("");
     final QueryOption format = new FormatOptionImpl().setName("");
@@ -128,6 +130,7 @@ public class UriInfoImplTest {
     final QueryOption alias = new AliasQueryOptionImpl().setName("alias").setText("C");
 
     final UriInfo uriInfo = new UriInfoImpl()
+        .setQueryOption(apply)
         .setQueryOption(expand)
         .setQueryOption(filter)
         .setQueryOption(format)
@@ -145,7 +148,8 @@ public class UriInfoImplTest {
         .setQueryOption(initialQueryOption)
         .setQueryOption(alias);
 
-    assertEquals(12, uriInfo.getSystemQueryOptions().size());
+    assertEquals(13, uriInfo.getSystemQueryOptions().size());
+    assertEquals(apply, uriInfo.getApplyOption());
     assertEquals(expand, uriInfo.getExpandOption());
     assertEquals(filter, uriInfo.getFilterOption());
     assertEquals(format, uriInfo.getFormatOption());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
index ebc4cc6..e2f129d 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/ExpandParserTest.java
@@ -23,172 +23,131 @@ import java.util.Collections;
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edmx.EdmxReference;
 import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataLibraryException;
+import org.apache.olingo.server.api.uri.UriInfo;
 import org.apache.olingo.server.api.uri.UriInfoKind;
+import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
+import org.apache.olingo.server.api.uri.queryoption.apply.Aggregate;
+import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression;
 import org.apache.olingo.server.core.uri.parser.UriParserSemanticException.MessageKeys;
+import org.apache.olingo.server.core.uri.testutil.ExpandValidator;
 import org.apache.olingo.server.core.uri.testutil.TestUriValidator;
 import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
 import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
 import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
 import org.apache.olingo.server.tecsvc.provider.PropertyProvider;
+import org.junit.Assert;
 import org.junit.Test;
 
 /** Tests of the parts of the URI parser that parse the sytem query option $expand. */
 public class ExpandParserTest {
 
-  private static final Edm edm = OData.newInstance().createServiceMetadata(
+  private static final OData oData = OData.newInstance();
+  private static final Edm edm = oData.createServiceMetadata(
       new EdmTechProvider(), Collections.<EdmxReference> emptyList()).getEdm();
 
-  private final TestUriValidator testUri = new TestUriValidator().setEdm(edm);
-
   @Test
   public void expandStar() throws Exception {
-    testUri.run("ESKeyNav(1)", "$expand=*")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .isSegmentStar();
-
-    testUri.run("ESKeyNav(1)", "$expand=*/$ref")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
+    runOnETKeyNav("*").isSegmentStar();
+
+    runOnETKeyNav("*/$ref")
         .isSegmentStar()
         .isSegmentRef();
 
-    testUri.run("ESKeyNav(1)", "$expand=*/$ref,NavPropertyETKeyNavMany")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
+    runOnETKeyNav("*/$ref,NavPropertyETKeyNavMany")
         .isSegmentStar().isSegmentRef()
         .next()
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
 
-    testUri.run("ESKeyNav(1)", "$expand=*($levels=3)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
+    runOnETKeyNav("*($levels=3)")
         .isSegmentStar()
-        .isLevelText("3");
+        .isLevels(3);
 
-    testUri.run("ESKeyNav(1)", "$expand=*($levels=max)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
+    runOnETKeyNav("*($levels=max)")
         .isSegmentStar()
-        .isLevelText("max");
+        .isLevelsMax();
   }
 
   @Test
   public void expandNavigationRef() throws Exception {
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef();
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne/$ref")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne/$ref")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav, false)
         .n().isRef();
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($filter=PropertyInt16 eq 1)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($filter=PropertyInt16 eq 1)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator().goFilter().is("<<PropertyInt16> eq <1>>");
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($orderby=PropertyInt16)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($orderby=PropertyInt16)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
         .isSortOrder(0, false)
         .goOrder(0).goPath().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($skip=1)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($skip=1)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
-        .isSkipText("1");
+        .isSkip(1);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($top=2)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($top=2)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
-        .isTopText("2");
+        .isTop(2);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($count=true)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($count=true)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
-        .isInlineCountText("true");
+        .isInlineCount(true);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($skip=1;$top=3)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($skip=1;$top=3)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
-        .isSkipText("1")
-        .isTopText("3");
+        .isSkip(1)
+        .isTop(3);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref($skip=1%3b$top=3)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref($skip=1%3b$top=3)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
-        .isSkipText("1")
-        .isTopText("3");
+        .isSkip(1)
+        .isTop(3);
   }
 
   @Test
   public void expandNavigationCount() throws Exception {
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$count")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$count")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isCount();
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne/$count")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne/$count")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav, false)
         .n().isCount();
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$count($filter=PropertyInt16 gt 1)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$count($filter=PropertyInt16 gt 1)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .n().isCount()
         .goUpExpandValidator()
         .goFilter().is("<<PropertyInt16> gt <1>>");
@@ -196,366 +155,322 @@ public class ExpandParserTest {
 
   @Test
   public void expandNavigationOptions() throws Exception {
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($filter=PropertyInt16 eq 1)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETTwoKeyNav("NavPropertyETKeyNavMany")
+        .goPath()
+        .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
+
+    runOnETKeyNav("NavPropertyETKeyNavMany($filter=PropertyInt16 eq 1)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator().goFilter().is("<<PropertyInt16> eq <1>>");
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($orderby=PropertyInt16)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($orderby=PropertyInt16)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .isSortOrder(0, false)
         .goOrder(0).goPath().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($skip=1)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($skip=1)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isSkipText("1");
+        .isSkip(1);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($top=2)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($top=2)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isTopText("2");
+        .isTop(2);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($count=true)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($count=true)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isInlineCountText("true");
+        .isInlineCount(true);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($select=PropertyString)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($select=PropertyString)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .goSelectItem(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($expand=NavPropertyETTwoKeyNavOne)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($expand=NavPropertyETTwoKeyNavOne)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .goExpand()
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($expand=NavPropertyETKeyNavMany)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($expand=NavPropertyETKeyNavMany)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .goExpand()
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne($levels=5)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne($levels=5)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav, false)
         .goUpExpandValidator()
-        .isLevelText("5");
+        .isLevels(5);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($select=PropertyString)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($select=PropertyString)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .goSelectItem(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavOne($levels=max)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne($levels=max)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav, false)
         .goUpExpandValidator()
-        .isLevelText("max");
+        .isLevelsMax();
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($skip=1;$top=2)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($skip=1;$top=2)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isSkipText("1")
-        .isTopText("2");
+        .isSkip(1)
+        .isTop(2);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($skip=1%3b$top=2)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany($skip=1%3b$top=2)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
-        .isSkipText("1")
-        .isTopText("2");
+        .isSkip(1)
+        .isTop(2);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany($search=Country AND Western)")
-        .isKind(UriInfoKind.resource).goExpand()
-        .first().goPath().first().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
+    runOnETKeyNav("NavPropertyETKeyNavMany($search=Country AND Western)")
+        .goPath().isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
         .goUpExpandValidator()
         .isSearchSerialized("{'Country' AND 'Western'}");
+  }
 
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='Hugo')", "$expand=NavPropertyETKeyNavMany")
-        .isKind(UriInfoKind.resource).goPath()
-        .first()
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'Hugo'")
-        .goUpUriValidator().goExpand()
-        .first()
-        .goPath().first()
-        .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav, true);
+  @Test
+  public void expandNavigationApplyOption() throws Exception {
+    UriInfo uriInfo = new Parser(edm, oData).parseUri("ESTwoKeyNav",
+        "$expand=NavPropertyETKeyNavMany($apply=identity),NavPropertyETKeyNavOne", null);
+    Assert.assertEquals(ApplyItem.Kind.IDENTITY,
+        uriInfo.getExpandOption().getExpandItems().get(0).getApplyOption().getApplyItems().get(0).getKind());
+    Assert.assertEquals("NavPropertyETKeyNavOne",
+        uriInfo.getExpandOption().getExpandItems().get(1)
+            .getResourcePath().getUriResourceParts().get(0).getSegmentValue());
+
+    uriInfo = new Parser(edm, oData).parseUri("ESTwoKeyNav",
+        "$expand=NavPropertyETKeyNavMany($apply=aggregate(PropertyInt16 with sum as s))", null);
+    final ApplyItem applyItem =
+        uriInfo.getExpandOption().getExpandItems().get(0).getApplyOption().getApplyItems().get(0);
+    Assert.assertEquals(ApplyItem.Kind.AGGREGATE, applyItem.getKind());
+    Assert.assertEquals(AggregateExpression.StandardMethod.SUM,
+        ((Aggregate) applyItem).getExpressions().get(0).getStandardMethod());
   }
 
   @Test
   public void expandTypeCasts() throws Exception {
-    testUri.run("ESTwoKeyNav", "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
 
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='Hugo')",
-        "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
-        .isKind(UriInfoKind.resource).goPath().first()
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'Hugo'")
-        .goUpUriValidator().goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETKeyNavMany")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
-        .isType(EntityTypeProvider.nameETKeyNav)
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true);
 
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
-        "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany")
-        .isKind(UriInfoKind.resource).goPath().first()
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'2'")
-        .goUpUriValidator().goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
-        .isType(EntityTypeProvider.nameETTwoKeyNav)
+        .goPath()
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true);
 
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')",
-        "$expand=olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav")
-        .isKind(UriInfoKind.resource).goPath().first()
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'2'")
-        .goUpUriValidator().goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany"
+        + "/olingo.odata.test1.ETTwoBaseTwoKeyNav")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBaseTwoKeyNav);
 
-    testUri.run("ESTwoKeyNav", "$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/NavPropertyETTwoKeyNavOne")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/NavPropertyETTwoKeyNavOne")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
+        .goPath()
         .isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false)
         .n()
         .isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false);
 
-    testUri.run("ESTwoKeyNav", "$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/*")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav/*")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
         .isSegmentStar()
-        .goPath().first().isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false);
+        .goPath().isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false);
 
-    testUri.run("ESTwoKeyNav", "$expand=olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav"
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/PropertyCompNav"
         + "/olingo.odata.test1.CTTwoBasePrimCompNav/NavPropertyETTwoKeyNavOne")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
+        .goPath()
         .isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTBasePrimCompNav, false)
         .isTypeFilter(ComplexTypeProvider.nameCTTwoBasePrimCompNav)
         .n()
         .isNavProperty("NavPropertyETTwoKeyNavOne", EntityTypeProvider.nameETTwoKeyNav, false);
 
-    testUri.run("ESKeyNav", "$expand=NavPropertyETTwoKeyNavMany/Namespace1_Alias.ETBaseTwoKeyNav"
+    runOnETKeyNav("NavPropertyETTwoKeyNavMany/Namespace1_Alias.ETBaseTwoKeyNav"
         + "($expand=NavPropertyETBaseTwoKeyNavOne)")
-        .isKind(UriInfoKind.resource)
-        .goExpand().goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
-        .isType(EntityTypeProvider.nameETTwoKeyNav, true)
         .isTypeFilterOnCollection(EntityTypeProvider.nameETBaseTwoKeyNav)
         .goUpExpandValidator()
         // go to the expand options of the current expand
         .goExpand()
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETBaseTwoKeyNavOne", EntityTypeProvider.nameETBaseTwoKeyNav, false);
 
-    testUri.run("ESKeyNav(1)", "$expand=NavPropertyETKeyNavMany/$ref,NavPropertyETTwoKeyNavMany($skip=2;$top=1)")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
+    runOnETKeyNav("NavPropertyETKeyNavMany/$ref,NavPropertyETTwoKeyNavMany($skip=2;$top=1)")
         .goPath()
-        .first()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
         .n().isRef()
         .goUpExpandValidator()
         .next()
         .goPath()
-        .first().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .goUpExpandValidator()
-        .isSkipText("2")
-        .isTopText("1");
+        .isSkip(2)
+        .isTop(1);
 
-    testUri.run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", "$expand=olingo.odata.test1.ETBaseTwoKeyNav"
-        + "/NavPropertyETTwoKeyNavMany/olingo.odata.test1.ETTwoBaseTwoKeyNav($select=PropertyString)")
-        .isKind(UriInfoKind.resource).goPath()
-        .first()
-        .isKeyPredicate(0, "PropertyInt16", "1")
-        .isKeyPredicate(1, "PropertyString", "'2'")
-        .goUpUriValidator().goExpand().first()
+    runOnETTwoKeyNav("olingo.odata.test1.ETBaseTwoKeyNav/NavPropertyETTwoKeyNavMany"
+        + "/olingo.odata.test1.ETTwoBaseTwoKeyNav($select=PropertyString)")
         .isExpandStartType(EntityTypeProvider.nameETBaseTwoKeyNav)
-        .goPath().first()
+        .goPath()
         .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
-        .isType(EntityTypeProvider.nameETTwoKeyNav)
         .isTypeFilterOnCollection(EntityTypeProvider.nameETTwoBaseTwoKeyNav)
         .goUpExpandValidator()
         .goSelectItem(0).isPrimitiveProperty("PropertyString", PropertyProvider.nameString, false);
 
-    testUri.run("ESKeyNav", "$expand=NavPropertyETKeyNavOne($expand=NavPropertyETKeyNavMany("
-        + "$expand=NavPropertyETKeyNavOne))")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne($expand=NavPropertyETKeyNavMany($expand=NavPropertyETKeyNavOne))")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
-        .goExpand().first()
-        .goPath().first()
+        .goExpand()
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavMany", EntityTypeProvider.nameETKeyNav, true)
-        .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
-        .goExpand().first()
-        .goPath().first()
-        .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav);
+        .goExpand()
+        .goPath()
+        .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false);
 
-    testUri.run("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyInt16)")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne($select=PropertyInt16)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
         .goSelectItem(0).isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
-    testUri.run("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyCompNav/PropertyInt16)")
-        .isKind(UriInfoKind.resource)
-        .goExpand().first()
-        .goPath().first()
+    runOnETKeyNav("NavPropertyETKeyNavOne($select=PropertyCompNav/PropertyInt16)")
+        .goPath()
         .isNavProperty("NavPropertyETKeyNavOne", EntityTypeProvider.nameETKeyNav, false)
-        .isType(EntityTypeProvider.nameETKeyNav)
         .goUpExpandValidator()
         .goSelectItem(0)
-        .first().isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTNavFiveProp, false)
+        .isComplexProperty("PropertyCompNav", ComplexTypeProvider.nameCTNavFiveProp, false)
         .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
 
-    testUri.runEx("ESKeyNav", "$expand=undefined")
-        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
-    testUri.runEx("ESTwoKeyNav", "$expand=PropertyCompNav/undefined")
-        .isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
-    testUri.runEx("ESTwoKeyNav", "$expand=PropertyCompNav/*+")
+    runOnETKeyNavEx("undefined").isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    runOnETTwoKeyNavEx("PropertyCompNav/undefined").isExSemantic(MessageKeys.EXPRESSION_PROPERTY_NOT_IN_TYPE);
+    runOnETTwoKeyNavEx("PropertyCompNav/*+")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
   }
 
   @Test
   public void duplicatedSystemQueryOptionsInExpand() throws Exception {
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($select=PropertyInt16;$select=PropertyInt16)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($select=PropertyInt16;$select=PropertyInt16)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($filter=true;$filter=true)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($filter=true;$filter=true)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($orderby=PropertyInt16;$orderby=PropertyInt16)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($orderby=PropertyInt16;$orderby=PropertyInt16)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($levels=2;$levels=3)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($levels=2;$levels=3)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($expand=*;$expand=*)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($expand=*;$expand=*)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($count=true;$count=true)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($count=true;$count=true)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($top=1;$top=1)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($top=1;$top=1)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($skip=2;$skip=2)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($skip=2;$skip=2)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
 
-    testUri.runEx("ESKeyNav", "$expand=NavPropertyETKeyNavOne($search=Test;$search=Test)")
+    runOnETKeyNavEx("NavPropertyETKeyNavOne($search=Test;$search=Test)")
         .isExSyntax(UriParserSyntaxException.MessageKeys.DOUBLE_SYSTEM_QUERY_OPTION);
   }
 
   @Test
   public void simpleKeyInExpandSystemQueryOption() throws Exception {
-    testUri.runEx("ESAllPrim(0)", "$expand=NavPropertyETTwoPrimMany(-365)($filter=PropertyString eq 'Test String1')")
+    runOnETTwoKeyNavEx("NavPropertyETKeyNavMany(-365)($filter=PropertyString eq 'Test String1')")
         .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
   @Test
   public void compoundKeyInExpandSystemQueryOption() throws Exception {
-    testUri.runEx("ESAllPrim(0)", "$expand=NavPropertyETTwoPrimMany(PropertyInt16=1,PropertyString=2)"
+    runOnETKeyNavEx("NavPropertyETTwoKeyNavMany(PropertyInt16=1,PropertyString=2)"
         + "($filter=PropertyString eq 'Test String1')")
         .isExSyntax(UriParserSyntaxException.MessageKeys.SYNTAX);
   }
 
   @Test
   public void keyPredicatesInExpandFilter() throws Exception {
-    testUri.run("ESKeyNav(0)", "$expand=NavPropertyETTwoKeyNavMany($filter=NavPropertyETTwoKeyNavMany"
-        + "(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 1)").goExpand()
-        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+    runOnETKeyNav("NavPropertyETTwoKeyNavMany($filter=NavPropertyETTwoKeyNavMany"
+        + "(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 1)")
+        .goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .goUpExpandValidator().goFilter()
-        .is("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+        .left().goPath()
+        .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, false)
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
   }
 
   @Test
   public void keyPredicatesInDoubleExpandedFilter() throws Exception {
-    testUri.run("ESKeyNav(0)", "$expand=NavPropertyETTwoKeyNavMany($expand=NavPropertyETTwoKeyNavMany"
+    runOnETKeyNav("NavPropertyETTwoKeyNavMany($expand=NavPropertyETTwoKeyNavMany"
         + "($filter=NavPropertyETTwoKeyNavMany(PropertyInt16=1,PropertyString='2')/PropertyInt16 eq 1))")
-        .goExpand()
-        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .goUpExpandValidator().goExpand()
-        .first().goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
+        .goPath().isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, true)
         .goUpExpandValidator().goFilter()
-        .is("<<NavPropertyETTwoKeyNavMany/PropertyInt16> eq <1>>");
+        .left().goPath()
+        .isNavProperty("NavPropertyETTwoKeyNavMany", EntityTypeProvider.nameETTwoKeyNav, false)
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .n().isPrimitiveProperty("PropertyInt16", PropertyProvider.nameInt16, false);
+  }
+
+  private ExpandValidator runOnETKeyNav(final String expand) throws ODataLibraryException {
+    return new TestUriValidator().setEdm(edm).run("ESKeyNav(1)", "$expand=" + expand)
+        .isKind(UriInfoKind.resource).goPath().isType(EntityTypeProvider.nameETKeyNav, false)
+        .goUpUriValidator().goExpand();
+  }
+
+  private ExpandValidator runOnETTwoKeyNav(final String expand) throws ODataLibraryException {
+    return new TestUriValidator().setEdm(edm)
+        .run("ESTwoKeyNav(PropertyInt16=1,PropertyString='2')", "$expand=" + expand)
+        .isKind(UriInfoKind.resource)
+        .goPath()
+        .isType(EntityTypeProvider.nameETTwoKeyNav, false)
+        .isKeyPredicate(0, "PropertyInt16", "1")
+        .isKeyPredicate(1, "PropertyString", "'2'")
+        .goUpUriValidator()
+        .goExpand();
+  }
+
+  private TestUriValidator runOnETKeyNavEx(final String expand) throws ODataLibraryException {
+    return new TestUriValidator().setEdm(edm).runEx("ESKeyNav(1)", "$expand=" + expand);
+  }
+
+  private TestUriValidator runOnETTwoKeyNavEx(final String expand) throws ODataLibraryException {
+    return new TestUriValidator().setEdm(edm).runEx("ESTwoKeyNav", "$expand=" + expand);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/UriParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/UriParserTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/UriParserTest.java
index 87a702c..693f5b6 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/UriParserTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/parser/UriParserTest.java
@@ -439,12 +439,12 @@ public class UriParserTest {
     testUri.run("ESKeyNav", "$top=1")
         .isKind(UriInfoKind.resource)
         .goPath().isEntitySet("ESKeyNav")
-        .goUpUriValidator().isTopText("1");
+        .goUpUriValidator().isTop(1);
 
     testUri.run("ESKeyNav", "$top=0")
         .isKind(UriInfoKind.resource)
         .goPath().isEntitySet("ESKeyNav")
-        .goUpUriValidator().isTopText("0");
+        .goUpUriValidator().isTop(0);
 
     testUri.runEx("ESKeyNav", "$top=undefined")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
@@ -490,10 +490,10 @@ public class UriParserTest {
   public void count() throws Exception {
     testUri.run("ESAllPrim", "$count=true")
         .isKind(UriInfoKind.resource)
-        .isInlineCountText("true");
+        .isInlineCount(true);
     testUri.run("ESAllPrim", "$count=false")
         .isKind(UriInfoKind.resource)
-        .isInlineCountText("false");
+        .isInlineCount(false);
     testUri.runEx("ESAllPrim", "$count=undefined")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
     testUri.runEx("ESAllPrim", "$count=")
@@ -504,10 +504,10 @@ public class UriParserTest {
   public void skip() throws Exception {
     testUri.run("ESAllPrim", "$skip=3")
         .isKind(UriInfoKind.resource)
-        .isSkipText("3");
+        .isSkip(3);
     testUri.run("ESAllPrim", "$skip=0")
         .isKind(UriInfoKind.resource)
-        .isSkipText("0");
+        .isSkip(0);
 
     testUri.runEx("ESAllPrim", "$skip=F")
         .isExSyntax(UriParserSyntaxException.MessageKeys.WRONG_VALUE_FOR_SYSTEM_QUERY_OPTION);
@@ -795,11 +795,11 @@ public class UriParserTest {
   public void allowedSystemQueryOptionsOnAll() throws Exception {
     testUri.run("$all", "$count=true&$format=json&$search=abc&$skip=5&$top=5&$skiptoken=abc")
         .isKind(UriInfoKind.all)
-        .isInlineCountText("true")
+        .isInlineCount(true)
         .isFormatText("json")
         .isSearchSerialized("'abc'")
-        .isSkipText("5")
-        .isTopText("5")
+        .isSkip(5)
+        .isTop(5)
         .isSkipTokenText("abc");
   }
 
@@ -809,7 +809,7 @@ public class UriParserTest {
         + "&$filter=ESAllPrim/PropertyInt16 eq 2&$format=json&$orderby=ESAllPrim/PropertyInt16"
         + "&$search=abc&$skip=5&$top=5&$skiptoken=abc")
         .isKind(UriInfoKind.crossjoin)
-        .isInlineCountText("true")
+        .isInlineCount(true)
         .goExpand().goPath().isEntitySet("ESAllPrim")
         .goUpExpandValidator().goUpToUriValidator()
         .goFilter().left().goPath().first().isEntitySet("ESAllPrim")
@@ -817,8 +817,8 @@ public class UriParserTest {
         .goUpFilterValidator().goUpToUriValidator()
         .isFormatText("json")
         .isSearchSerialized("'abc'")
-        .isSkipText("5")
-        .isTopText("5")
+        .isSkip(5)
+        .isTop(5)
         .isSkipTokenText("abc");
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
index 0c60ee2..9a08b97 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/ExpandValidator.java
@@ -27,7 +27,6 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
 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.OrderByOption;
-import org.apache.olingo.server.api.uri.queryoption.QueryOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectItem;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 
@@ -127,47 +126,43 @@ public class ExpandValidator implements TestValidator {
     return this;
   }
 
-  public ExpandValidator isLevelText(final String text) {
-    final QueryOption option = (QueryOption) expandItem.getLevelsOption();
-    assertEquals(text, option.getText());
+  public ExpandValidator isLevels(final int levels) {
+    assertEquals(levels, expandItem.getLevelsOption().getValue());
     return this;
   }
 
-  public ExpandValidator isSkipText(final String text) {
-    final QueryOption option = expandItem.getSkipOption();
-    assertEquals(text, option.getText());
+  public ExpandValidator isLevelsMax() {
+    assertTrue(expandItem.getLevelsOption().isMax());
     return this;
   }
 
-  public ExpandValidator isTopText(final String text) {
-    final QueryOption option = expandItem.getTopOption();
-    assertEquals(text, option.getText());
+  public ExpandValidator isSkip(final int skip) {
+    assertEquals(skip, expandItem.getSkipOption().getValue());
     return this;
   }
 
-  public ExpandValidator isInlineCountText(final String text) {
-    final QueryOption option = expandItem.getCountOption();
-    assertEquals(text, option.getText());
+  public ExpandValidator isTop(final int top) {
+    assertEquals(top, expandItem.getTopOption().getValue());
+    return this;
+  }
+
+  public ExpandValidator isInlineCount(final boolean value) {
+    assertEquals(value, expandItem.getCountOption().getValue());
     return this;
   }
 
   public ExpandValidator isSelectItemStar(final int index) {
-    SelectOption select = expandItem.getSelectOption();
-    SelectItem item = select.getSelectItems().get(index);
-    assertTrue(item.isStar());
+    assertTrue(expandItem.getSelectOption().getSelectItems().get(index).isStar());
     return this;
   }
 
   public ExpandValidator isSelectItemAllOperations(final int index, final FullQualifiedName fqn) {
-    SelectOption select = expandItem.getSelectOption();
-    SelectItem item = select.getSelectItems().get(index);
-    assertEquals(fqn.toString(), item.getAllOperationsInSchemaNameSpace().toString());
+    assertEquals(fqn, expandItem.getSelectOption().getSelectItems().get(index).getAllOperationsInSchemaNameSpace());
     return this;
   }
 
   public ExpandValidator isSortOrder(final int index, final boolean descending) {
-    OrderByOption orderBy = expandItem.getOrderByOption();
-    assertEquals(descending, orderBy.getOrders().get(index).isDescending());
+    assertEquals(descending, expandItem.getOrderByOption().getOrders().get(index).isDescending());
     return this;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java
index 610b666..2103dd1 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/FilterTreeToText.java
@@ -71,13 +71,13 @@ public class FilterTreeToText implements ExpressionVisitor<String> {
       throws ExpressionVisitException {
 
     String text = "<" + methodCall + "(";
-    int i = 0;
-    while (i < parameters.size()) {
-      if (i > 0) {
+    boolean first = true;
+    for (final String parameter : parameters) {
+      if (!first) {
         text += ",";
       }
-      text += parameters.get(i);
-      i++;
+      text += parameter;
+      first = false;
     }
     return text + ")>";
   }
@@ -104,7 +104,7 @@ public class FilterTreeToText implements ExpressionVisitor<String> {
         tmp = typed.toString(true);
       }
 
-      if (ret.length() != 0) {
+      if (ret.length() > 0) {
         ret += "/";
       }
       ret += tmp;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/1600684d/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
index d869cb1..3f1b913 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/testutil/TestUriValidator.java
@@ -26,7 +26,6 @@ import static org.junit.Assert.fail;
 import java.util.List;
 
 import org.apache.olingo.commons.api.edm.Edm;
-import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.edm.FullQualifiedName;
 import org.apache.olingo.commons.api.http.HttpMethod;
 import org.apache.olingo.server.api.OData;
@@ -121,7 +120,7 @@ public class TestUriValidator implements TestValidator {
   public ResourceValidator goSelectItemPath(final int index) {
     final SelectOption select = uriInfo.getSelectOption();
     assertNotNull("no select found", select);
-    SelectItem item = select.getSelectItems().get(index);
+    final SelectItem item = select.getSelectItems().get(index);
     return new ResourceValidator()
         .setUpValidator(this)
         .setEdm(edm)
@@ -131,16 +130,15 @@ public class TestUriValidator implements TestValidator {
   public TestUriValidator isSelectStartType(final int index, final FullQualifiedName fullName) {
     final SelectOption select = uriInfo.getSelectOption();
     assertNotNull("no select found", select);
-    SelectItem item = select.getSelectItems().get(index);
-    EdmType actualType = item.getStartTypeFilter();
-    assertEquals(fullName, actualType.getFullQualifiedName());
+    final SelectItem item = select.getSelectItems().get(index);
+    assertEquals(fullName, item.getStartTypeFilter().getFullQualifiedName());
     return this;
   }
 
   public TestUriValidator isSelectItemStar(final int index) {
     final SelectOption select = uriInfo.getSelectOption();
     assertNotNull("no select found", select);
-    SelectItem item = select.getSelectItems().get(index);
+    final SelectItem item = select.getSelectItems().get(index);
     assertTrue(item.isStar());
     return this;
   }
@@ -148,7 +146,7 @@ public class TestUriValidator implements TestValidator {
   public TestUriValidator isSelectItemAllOp(final int index, final FullQualifiedName fqn) {
     final SelectOption select = uriInfo.getSelectOption();
     assertNotNull("no select found", select);
-    SelectItem item = select.getSelectItems().get(index);
+    final SelectItem item = select.getSelectItems().get(index);
     assertEquals(fqn, item.getAllOperationsInSchemaNameSpace());
     return this;
   }
@@ -162,27 +160,27 @@ public class TestUriValidator implements TestValidator {
   }
 
   public TestUriValidator isFormatText(final String text) {
-    assertEquals(text, uriInfo.getFormatOption().getText());
+    assertEquals(text, uriInfo.getFormatOption().getFormat());
     return this;
   }
 
-  public TestUriValidator isTopText(final String topText) {
-    assertEquals(topText, uriInfo.getTopOption().getText());
+  public TestUriValidator isSkip(final int skip) {
+    assertEquals(skip, uriInfo.getSkipOption().getValue());
     return this;
   }
 
-  public TestUriValidator isInlineCountText(final String inlineCountText) {
-    assertEquals(inlineCountText, uriInfo.getCountOption().getText());
+  public TestUriValidator isTop(final int top) {
+    assertEquals(top, uriInfo.getTopOption().getValue());
     return this;
   }
 
-  public TestUriValidator isSkipText(final String skipText) {
-    assertEquals(skipText, uriInfo.getSkipOption().getText());
+  public TestUriValidator isInlineCount(final boolean value) {
+    assertEquals(value, uriInfo.getCountOption().getValue());
     return this;
   }
 
   public TestUriValidator isSkipTokenText(final String skipTokenText) {
-    assertEquals(skipTokenText, uriInfo.getSkipTokenOption().getText());
+    assertEquals(skipTokenText, uriInfo.getSkipTokenOption().getValue());
     return this;
   }
 


[2/2] olingo-odata4 git commit: [OLINGO-935] improved debug support

Posted by ch...@apache.org.
[OLINGO-935] improved debug support

including more unit tests

Signed-off-by: Christian Amend <ch...@sap.com>


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

Branch: refs/heads/master
Commit: 9e67d0e3e8ae71ea15098ba24fd22bf74c2faaa4
Parents: 1600684
Author: Klaus Straubinger <kl...@sap.com>
Authored: Fri Oct 7 14:41:17 2016 +0200
Committer: Christian Amend <ch...@sap.com>
Committed: Fri Oct 7 14:52:41 2016 +0200

----------------------------------------------------------------------
 .../olingo/server/core/debug/DebugTabBody.java  |  43 +-
 .../server/core/debug/DebugTabRuntime.java      |   8 +-
 .../server/core/debug/DebugTabStacktrace.java   |   2 +-
 .../olingo/server/core/debug/DebugTabUri.java   |  49 ++-
 .../core/debug/ExpressionJsonVisitor.java       |  78 +++-
 .../server/core/debug/AbstractDebugTabTest.java |  28 +-
 .../server/core/debug/DebugTabBodyTest.java     |  67 +++
 .../server/core/debug/DebugTabRuntimeTest.java  |  73 ++++
 .../core/debug/DebugTabStacktraceTest.java      | 107 +++++
 .../server/core/debug/DebugTabUriTest.java      | 405 +++++++++++++++++++
 .../core/debug/ServerCoreDebuggerTest.java      |  37 +-
 11 files changed, 832 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java
index b0c5314..9bca8eb 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java
@@ -23,7 +23,6 @@ import java.io.InputStream;
 import java.io.Writer;
 
 import org.apache.commons.codec.binary.Base64;
-import org.apache.olingo.commons.api.ex.ODataRuntimeException;
 import org.apache.olingo.commons.api.http.HttpHeader;
 import org.apache.olingo.server.api.ODataResponse;
 import org.apache.olingo.server.api.deserializer.DeserializerException;
@@ -70,33 +69,12 @@ public class DebugTabBody implements DebugTab {
     return "Body";
   }
 
-  //
   @Override
-  public void appendJson(final JsonGenerator gen) throws IOException {
+  public void appendJson(final JsonGenerator json) throws IOException {
     if (response == null || response.getContent() == null) {
-      gen.writeNull();
+      json.writeNull();
     } else {
-      gen.writeString(getContentString());
-    }
-  }
-
-  private String getContentString() {
-    try {
-      String contentString;
-      switch (responseContent) {
-      case IMAGE:
-        contentString = Base64.encodeBase64String(streamToBytes(response.getContent()));
-        break;
-      case JSON:
-      case XML:
-      case TEXT:
-      default:
-        contentString = new String(streamToBytes(response.getContent()), "UTF-8");
-        break;
-      }
-      return contentString;
-    } catch (IOException e) {
-      return "Could not parse Body for Debug Output";
+      json.writeString(getContentString());
     }
   }
 
@@ -130,12 +108,23 @@ public class DebugTabBody implements DebugTab {
     }
   }
 
-  private byte[] streamToBytes(final InputStream input) {
+  private String getContentString() {
+    try {
+      final byte[] content = streamToBytes(response.getContent());
+      return responseContent == ResponseContent.IMAGE ?
+          Base64.encodeBase64String(content) :
+          new String(content, "UTF-8");
+    } catch (final IOException e) {
+      return "Could not parse Body for Debug Output";
+    }
+  }
+
+  private byte[] streamToBytes(final InputStream input) throws IOException {
     if (input != null) {
       try {
         return new FixedFormatDeserializerImpl().binary(input);
       } catch (final DeserializerException e) {
-        throw new ODataRuntimeException("Error on reading request content", e);
+        throw new IOException("Error on reading request content", e);
       }
     }
     return null;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java
index 4214050..7416594 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java
@@ -65,12 +65,12 @@ public class DebugTabRuntime implements DebugTab {
   private void appendJsonNode(final JsonGenerator gen, final RuntimeNode node) throws IOException {
     gen.writeStartObject();
     gen.writeStringField("class", node.className);
-    gen.writeStringField("method ", node.methodName);
+    gen.writeStringField("method", node.methodName);
 
     if (node.timeStopped == 0) {
       gen.writeNullField("duration");
     } else {
-      gen.writeStringField("duration", Long.toString((node.timeStopped - node.timeStarted) / TO_MILLIS_DIVISOR));
+      gen.writeNumberField("duration", (node.timeStopped - node.timeStarted) / TO_MILLIS_DIVISOR);
       gen.writeStringField("unit", "�s");
     }
 
@@ -140,7 +140,9 @@ public class DebugTabRuntime implements DebugTab {
 
     protected boolean add(final RuntimeMeasurement runtimeMeasurement) {
       if (timeStarted <= runtimeMeasurement.getTimeStarted()
-          && timeStopped != 0 && timeStopped >= runtimeMeasurement.getTimeStopped()) {
+          && timeStopped != 0
+          && timeStopped > runtimeMeasurement.getTimeStarted() // in case the stop time has not been set
+          && timeStopped >= runtimeMeasurement.getTimeStopped()) {
         for (RuntimeNode candidate : children) {
           if (candidate.add(runtimeMeasurement)) {
             return true;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabStacktrace.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabStacktrace.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabStacktrace.java
index 3cc94b6..310ec3a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabStacktrace.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabStacktrace.java
@@ -90,7 +90,7 @@ public class DebugTabStacktrace implements DebugTab {
     gen.writeStartObject();
     gen.writeStringField("class", element.getClassName());
     gen.writeStringField("method", element.getMethodName());
-    gen.writeStringField("line", Integer.toString(element.getLineNumber()));
+    gen.writeNumberField("line", element.getLineNumber());
     gen.writeEndObject();
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/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 05ac2aa..007b7c6 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
@@ -25,15 +25,18 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.commons.api.ex.ODataException;
 import org.apache.olingo.server.api.uri.UriInfo;
 import org.apache.olingo.server.api.uri.UriInfoKind;
 import org.apache.olingo.server.api.uri.UriParameter;
 import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
 import org.apache.olingo.server.api.uri.UriResourceEntitySet;
 import org.apache.olingo.server.api.uri.UriResourceFunction;
 import org.apache.olingo.server.api.uri.UriResourceNavigation;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
 import org.apache.olingo.server.api.uri.queryoption.ApplyItem;
 import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
 import org.apache.olingo.server.api.uri.queryoption.CountOption;
@@ -92,6 +95,15 @@ public class DebugTabUri implements DebugTab {
     if (uriInfo.getKind() == UriInfoKind.resource) {
       gen.writeFieldName("uriResourceParts");
       appendURIResourceParts(gen, uriInfo.getUriResourceParts());
+    } else if (uriInfo.getKind() == UriInfoKind.crossjoin) {
+      gen.writeFieldName("entitySetNames");
+      gen.writeStartArray();
+      for (final String name : uriInfo.asUriInfoCrossjoin().getEntitySetNames()) {
+        gen.writeString(name);
+      }
+      gen.writeEndArray();
+    } else if (uriInfo.getKind() == UriInfoKind.entityId) {
+      appendType(gen, "typeCast", uriInfo.asUriInfoEntityId().getEntityTypeCast());
     }
 
     if (uriInfo.getFormatOption() != null) {
@@ -183,23 +195,39 @@ public class DebugTabUri implements DebugTab {
       gen.writeStartObject();
       gen.writeStringField("uriResourceKind", resource.getKind().toString());
       gen.writeStringField("segment", resource.toString());
-      if (resource instanceof UriResourcePartTyped && ((UriResourcePartTyped) resource).getType() != null) {
-        gen.writeStringField("type",
-            ((UriResourcePartTyped) resource).getType().getFullQualifiedName().getFullQualifiedNameAsString());
+      if (resource instanceof UriResourcePartTyped) {
+        appendType(gen, "type", ((UriResourcePartTyped) resource).getType());
+        gen.writeBooleanField("isCollection", ((UriResourcePartTyped) resource).isCollection());
       }
       if (resource instanceof UriResourceEntitySet) {
         appendParameters(gen, "keys", ((UriResourceEntitySet) resource).getKeyPredicates());
+        appendType(gen, "typeFilterOnCollection", ((UriResourceEntitySet) resource).getTypeFilterOnCollection());
+        appendType(gen, "typeFilterOnEntry", ((UriResourceEntitySet) resource).getTypeFilterOnEntry());
       } else if (resource instanceof UriResourceNavigation) {
         appendParameters(gen, "keys", ((UriResourceNavigation) resource).getKeyPredicates());
+        appendType(gen, "typeFilterOnCollection", ((UriResourceNavigation) resource).getTypeFilterOnCollection());
+        appendType(gen, "typeFilterOnEntry", ((UriResourceNavigation) resource).getTypeFilterOnEntry());
       } else if (resource instanceof UriResourceFunction) {
         appendParameters(gen, "parameters", ((UriResourceFunction) resource).getParameters());
         appendParameters(gen, "keys", ((UriResourceFunction) resource).getKeyPredicates());
+        appendType(gen, "typeFilterOnCollection", ((UriResourceFunction) resource).getTypeFilterOnCollection());
+        appendType(gen, "typeFilterOnEntry", ((UriResourceFunction) resource).getTypeFilterOnEntry());
+      } else if (resource instanceof UriResourceSingleton) {
+        appendType(gen, "typeFilter", ((UriResourceSingleton) resource).getEntityTypeFilter());
+      } else if (resource instanceof UriResourceComplexProperty) {
+        appendType(gen, "typeFilter", ((UriResourceComplexProperty) resource).getComplexTypeFilter());
       }
       gen.writeEndObject();
     }
     gen.writeEndArray();
   }
 
+  private void appendType(JsonGenerator json, final String name, final EdmType type) throws IOException {
+    if (type != null) {
+      json.writeStringField(name, type.getFullQualifiedName().getFullQualifiedNameAsString());
+    }
+  }
+
   private void appendParameters(final JsonGenerator gen, final String name, final List<UriParameter> parameters)
       throws IOException {
     if (!parameters.isEmpty()) {
@@ -260,7 +288,7 @@ public class DebugTabUri implements DebugTab {
 
     appendCommonJsonObjects(gen, item.getCountOption(), item.getSkipOption(), item.getTopOption(),
         item.getFilterOption(), item.getOrderByOption(), item.getSelectOption(), item.getExpandOption(),
-        item.getSearchOption(), null); // TODO: item.getApplyOption()
+        item.getSearchOption(), item.getApplyOption());
 
     gen.writeEndObject();
   }
@@ -476,8 +504,21 @@ public class DebugTabUri implements DebugTab {
       appendURIResourceParts(json, uriInfo.getUriResourceParts());
       json.close();
       writer.append("\n</li>\n</ul>\n");
+    } else if (uriInfo.getKind() == UriInfoKind.crossjoin) {
+      writer.append("<h2>Crossjoin EntitySet Names</h2>\n")
+          .append("<ul>\n");
+      for (final String name : uriInfo.asUriInfoCrossjoin().getEntitySetNames()) {
+        writer.append("<li>").append(name).append("</li>\n");
+      }
+      writer.append("</ul>\n");
     } else {
       writer.append("<h2>Kind</h2>\n<p>").append(uriInfo.getKind().name()).append("</p>\n");
+      if (uriInfo.getKind() == UriInfoKind.entityId && uriInfo.asUriInfoEntityId().getEntityTypeCast() != null) {
+        writer.append("<h2>Type Cast</h2>\n<p>")
+            .append(uriInfo.asUriInfoEntityId().getEntityTypeCast().getFullQualifiedName()
+                .getFullQualifiedNameAsString())
+            .append("</p>\n");
+      }
     }
 
     if (uriInfo.getSearchOption() != null) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java
index cff320f..4f12882 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ExpressionJsonVisitor.java
@@ -18,17 +18,22 @@
  */
 package org.apache.olingo.server.core.debug;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.util.List;
+
 import org.apache.olingo.commons.api.edm.EdmEnumType;
 import org.apache.olingo.commons.api.edm.EdmType;
 import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.uri.UriParameter;
 import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.UriResourceFunction;
+import org.apache.olingo.server.api.uri.UriResourceIt;
 import org.apache.olingo.server.api.uri.UriResourceLambdaAll;
 import org.apache.olingo.server.api.uri.UriResourceLambdaAny;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
 import org.apache.olingo.server.api.uri.UriResourcePartTyped;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
 import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
 import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
@@ -38,7 +43,10 @@ import org.apache.olingo.server.api.uri.queryoption.expression.Member;
 import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
 import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
 
-import java.util.List;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
  * A custom expression visitor which converts the tree into a {@link JsonNode} tree.
@@ -76,6 +84,10 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
   private static final String TYPE_NAME = "type";
   private static final String OPERATOR_NAME = "operator";
   private static final String NODE_TYPE_NAME = "nodeType";
+  private static final String KEYS_NAME = "keys";
+  private static final String TYPE_FILTER_NAME = "typeFilter";
+  private static final String TYPE_FILTER_ON_COLLECTION_NAME = "typeFilterOnCollection";
+  private static final String TYPE_FILTER_ON_ENTRY_NAME = "typeFilterOnEntry";
 
   private final JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
 
@@ -140,6 +152,7 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
     ObjectNode result = nodeFactory.objectNode()
         .put(NODE_TYPE_NAME, MEMBER_NAME)
         .put(TYPE_NAME, getType(lastSegment));
+    putType(result, TYPE_FILTER_NAME, member.getStartTypeFilter());
     ArrayNode segments = result.putArray(RESOURCE_SEGMENTS_NAME);
     for (final UriResource segment : uriResourceParts) {
       if (segment instanceof UriResourceLambdaAll) {
@@ -149,10 +162,32 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
         final UriResourceLambdaAny any = (UriResourceLambdaAny) segment;
         segments.add(visitLambdaExpression(ANY_NAME, any.getLambdaVariable(), any.getExpression()));
       } else if (segment instanceof UriResourcePartTyped) {
-        segments.add(nodeFactory.objectNode()
+        ObjectNode node = nodeFactory.objectNode()
             .put(NODE_TYPE_NAME, segment.getKind().toString())
             .put(NAME_NAME, segment.toString())
-            .put(TYPE_NAME, getType(segment)));
+            .put(TYPE_NAME, getType(segment));
+        if (segment instanceof UriResourceEntitySet) {
+          putParameters(node, KEYS_NAME, ((UriResourceEntitySet) segment).getKeyPredicates());
+          putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceEntitySet) segment).getTypeFilterOnCollection());
+          putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceEntitySet) segment).getTypeFilterOnEntry());
+        } else if (segment instanceof UriResourceNavigation) {
+          putParameters(node, KEYS_NAME, ((UriResourceNavigation) segment).getKeyPredicates());
+          putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceNavigation) segment).getTypeFilterOnCollection());
+          putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceNavigation) segment).getTypeFilterOnEntry());
+        } else if (segment instanceof UriResourceFunction) {
+          putParameters(node, PARAMETERS_NAME, ((UriResourceFunction) segment).getParameters());
+          putParameters(node, KEYS_NAME, ((UriResourceFunction) segment).getKeyPredicates());
+          putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceFunction) segment).getTypeFilterOnCollection());
+          putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceFunction) segment).getTypeFilterOnEntry());
+        } else if (segment instanceof UriResourceIt) {
+          putType(node, TYPE_FILTER_ON_COLLECTION_NAME, ((UriResourceIt) segment).getTypeFilterOnCollection());
+          putType(node, TYPE_FILTER_ON_ENTRY_NAME, ((UriResourceIt) segment).getTypeFilterOnEntry());
+        } else if (segment instanceof UriResourceSingleton) {
+          putType(node, TYPE_FILTER_NAME, ((UriResourceSingleton) segment).getEntityTypeFilter());
+        } else if (segment instanceof UriResourceComplexProperty) {
+          putType(node, TYPE_FILTER_NAME, ((UriResourceComplexProperty) segment).getComplexTypeFilter());
+        }
+        segments.add(node);
       } else {
         segments.add(nodeFactory.objectNode()
             .put(NODE_TYPE_NAME, segment.getKind().toString())
@@ -163,6 +198,22 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
     return result;
   }
 
+  private void putType(ObjectNode node, final String name, final EdmType type) {
+    if (type != null) {
+      node.put(name, type.getFullQualifiedName().getFullQualifiedNameAsString());
+    }
+  }
+
+  private void putParameters(ObjectNode node, final String name, final List<UriParameter> parameters) {
+    if (!parameters.isEmpty()) {
+      ObjectNode parametersNode = node.putObject(name);
+      for (final UriParameter parameter : parameters) {
+        parametersNode.put(parameter.getName(),
+            parameter.getText() == null ? parameter.getAlias() : parameter.getText());
+      }
+    }
+  }
+
   @Override
   public JsonNode visitAlias(final String aliasName) throws ExpressionVisitException, ODataApplicationException {
     return nodeFactory.objectNode()
@@ -186,8 +237,8 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
   }
 
   @Override
-  public JsonNode visitEnum(final EdmEnumType type, final List<String> enumValues) throws ExpressionVisitException,
-  ODataApplicationException {
+  public JsonNode visitEnum(final EdmEnumType type, final List<String> enumValues)
+      throws ExpressionVisitException, ODataApplicationException {
     ObjectNode result = nodeFactory.objectNode()
         .put(NODE_TYPE_NAME, ENUM_NAME)
         .put(TYPE_NAME, getTypeString(type));
@@ -206,9 +257,8 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
       return NUMBER_NAME;
     case NOT:
       return BOOLEAN_NAME;
-    default:
-      return UNKNOWN_NAME;
     }
+    return UNKNOWN_NAME;
   }
 
   private String getType(final MethodKind methodCall) {
@@ -257,9 +307,9 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
       return DATETIMEOFFSET_NAME;
 
     case CAST:
-    default:
       return UNKNOWN_NAME;
     }
+    return UNKNOWN_NAME;
   }
 
   private String getType(final BinaryOperatorKind operator) {
@@ -281,10 +331,8 @@ public class ExpressionJsonVisitor implements ExpressionVisitor<JsonNode> {
     case AND:
     case OR:
       return BOOLEAN_NAME;
-
-    default:
-      return UNKNOWN_NAME;
     }
+    return UNKNOWN_NAME;
   }
 
   private String getTypeString(final EdmType type) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/AbstractDebugTabTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/AbstractDebugTabTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/AbstractDebugTabTest.java
index 95a3ba1..bbebe20 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/AbstractDebugTabTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/AbstractDebugTabTest.java
@@ -21,24 +21,30 @@ package org.apache.olingo.server.core.debug;
 import java.io.IOException;
 import java.io.StringWriter;
 
-import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 public abstract class AbstractDebugTabTest {
 
-  protected String createHtml(final DebugTab tab) throws IOException {
-    StringWriter writer = new StringWriter();
-    tab.appendHtml(writer);
-    writer.flush();
-    return writer.toString();
+  protected String createHtml(DebugTab tab) throws IOException {
+    return create(tab, true);
+  }
+
+  protected String createJson(DebugTab tab) throws IOException {
+    return create(tab, false);
   }
 
-  protected String createJson(final DebugTab tab) throws IOException {
+  private String create(DebugTab tab, final boolean html) throws IOException {
     StringWriter writer = new StringWriter();
-    JsonGenerator gen = new JsonFactory().createGenerator(writer);
-    tab.appendJson(gen);
-    gen.flush();
-    gen.close();
+    if (html) {
+      tab.appendHtml(writer);
+    } else {
+      // Create JSON generator (the object mapper is necessary to write expression trees).
+      JsonGenerator json = new ObjectMapper().getFactory().createGenerator(writer);
+      tab.appendJson(json);
+      json.flush();
+      json.close();
+    }
     writer.flush();
     return writer.toString();
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabBodyTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabBodyTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabBodyTest.java
index 088e398..6365f16 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabBodyTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabBodyTest.java
@@ -20,6 +20,14 @@ package org.apache.olingo.server.core.debug;
 
 import static org.junit.Assert.assertEquals;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.server.api.ODataResponse;
 import org.junit.Test;
 
 public class DebugTabBodyTest extends AbstractDebugTabTest {
@@ -31,4 +39,63 @@ public class DebugTabBodyTest extends AbstractDebugTabTest {
     assertEquals("null", createJson(tab));
     assertEquals("<pre class=\"code\">\nODataLibrary: No body.\n</pre>\n", createHtml(tab));
   }
+
+  @Test
+  public void json() throws Exception {
+    ODataResponse response = new ODataResponse();
+    response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.JSON_NO_METADATA.toContentTypeString());
+    response.setContent(IOUtils.toInputStream("{\"property\": true}"));
+    assertEquals("\"{\\\"property\\\": true}\"", createJson(new DebugTabBody(response)));
+
+    response.setContent(IOUtils.toInputStream("{\"property\": false}"));
+    assertEquals("<pre class=\"code json\">\n{\"property\": false}\n</pre>\n", createHtml(new DebugTabBody(response)));
+  }
+
+  @Test
+  public void xml() throws Exception {
+    ODataResponse response = new ODataResponse();
+    response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.APPLICATION_XML.toContentTypeString());
+    response.setContent(IOUtils.toInputStream("<?xml version='1.1'?>\n<a xmlns=\"b\" />\n"));
+    assertEquals("\"<?xml version='1.1'?>\\n<a xmlns=\\\"b\\\" />\\n\"", createJson(new DebugTabBody(response)));
+
+    response.setContent(IOUtils.toInputStream("<?xml version='1.1'?>\n<c xmlns=\"d\" />\n"));
+    assertEquals("<pre class=\"code xml\">\n&lt;?xml version='1.1'?&gt;\n&lt;c xmlns=\"d\" /&gt;\n\n</pre>\n",
+        createHtml(new DebugTabBody(response)));
+  }
+
+  @Test
+  public void text() throws Exception {
+    ODataResponse response = new ODataResponse();
+    response.setContent(IOUtils.toInputStream("testText\n12"));
+    assertEquals("\"testText\\n12\"", createJson(new DebugTabBody(response)));
+
+    response.setContent(IOUtils.toInputStream("testText\n34"));
+    assertEquals("<pre class=\"code\">\ntestText\n34\n</pre>\n", createHtml(new DebugTabBody(response)));
+  }
+
+  @Test
+  public void image() throws Exception {
+    ODataResponse response = new ODataResponse();
+    response.setHeader(HttpHeader.CONTENT_TYPE, "image/png");
+    response.setContent(new ByteArrayInputStream(new byte[] { -1, -2, -3, -4 }));
+    assertEquals("\"//79/A==\"", createJson(new DebugTabBody(response)));
+
+    response.setContent(new ByteArrayInputStream(new byte[] { -5, -6, -7, -8 }));
+    assertEquals("<img src=\"data:image/png;base64,+/r5+A==\" />\n", createHtml(new DebugTabBody(response)));
+  }
+
+  @Test
+  public void streamError() throws Exception {
+    ODataResponse response = new ODataResponse();
+    InputStream input = new InputStream() {
+      @Override
+      public int read() throws IOException {
+        throw new IOException("test");
+      }
+    };
+    response.setContent(input);
+    assertEquals("\"Could not parse Body for Debug Output\"", createJson(new DebugTabBody(response)));
+    assertEquals("<pre class=\"code\">\nCould not parse Body for Debug Output\n</pre>\n",
+        createHtml(new DebugTabBody(response)));
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabRuntimeTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabRuntimeTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabRuntimeTest.java
new file mode 100644
index 0000000..7f4f062
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabRuntimeTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.debug;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.apache.olingo.server.api.debug.RuntimeMeasurement;
+import org.junit.Test;
+
+public class DebugTabRuntimeTest extends AbstractDebugTabTest {
+
+  @Test
+  public void runtime() throws Exception {
+    final DebugTabRuntime tab = new DebugTabRuntime(Arrays.asList(
+        createMeasurement("class1", "method1", 0, 42),
+        createMeasurement("class2", "method2", 12, 23),
+        createMeasurement("class2", "method2", 24, 26),
+        createMeasurement("class3", "method3", 98, 0)));
+    assertEquals("[{\"class\":\"class1\",\"method\":\"method1\",\"duration\":42,\"unit\":\"�s\",\"children\":["
+        + "{\"class\":\"class2\",\"method\":\"method2\",\"duration\":13,\"unit\":\"�s\"}]},"
+        + "{\"class\":\"class3\",\"method\":\"method3\",\"duration\":null}]",
+        createJson(tab));
+
+    assertEquals("<ol class=\"tree\">\n"
+        + "<li>\n"
+        + "<span class=\"code\"><span class=\"draw\">&#x251C;&#x2500;&nbsp;</span>"
+        + "<span class=\"class\">class1</span>.<span class=\"method\">method1(&hellip;)</span>"
+        + "</span><span class=\"numeric\" title=\"Gross duration\">42&nbsp;&micro;s</span>\n"
+        + "<ol class=\"tree\">\n"
+        + "<li>\n"
+        + "<span class=\"code\"><span class=\"draw\">&#x2502;&nbsp;&nbsp;&#x2514;&#x2500;&nbsp;</span>"
+        + "<span class=\"class\">class2</span>.<span class=\"method\">method2(&hellip;)</span>"
+        + "</span><span class=\"numeric\" title=\"Gross duration\">13&nbsp;&micro;s</span>\n"
+        + "</li>\n"
+        + "</ol>\n"
+        + "</li>\n"
+        + "<li>\n"
+        + "<span class=\"code\"><span class=\"draw\">&#x2514;&#x2500;&nbsp;</span>"
+        + "<span class=\"class\">class3</span>.<span class=\"method\">method3(&hellip;)</span>"
+        + "</span><span class=\"null\" title=\"Stop time missing\">unfinished</span>\n"
+        + "</li>\n"
+        + "</ol>\n",
+        createHtml(tab));
+  }
+
+  private RuntimeMeasurement createMeasurement(final String className, final String methodName,
+      final int startMilliseconds, final int stopMilliseconds) {
+    RuntimeMeasurement measurement = new RuntimeMeasurement();
+    measurement.setClassName(className);
+    measurement.setMethodName(methodName);
+    measurement.setTimeStarted(startMilliseconds * 1000);
+    measurement.setTimeStopped(stopMilliseconds * 1000);
+    return measurement;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabStacktraceTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabStacktraceTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabStacktraceTest.java
new file mode 100644
index 0000000..35687da
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabStacktraceTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.debug;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class DebugTabStacktraceTest extends AbstractDebugTabTest {
+
+  @Test
+  public void stacktrace() throws Exception {
+    Exception cause = new Exception("innerError");
+    cause.setStackTrace(new StackTraceElement[] {
+        new StackTraceElement("inner.class", "inner.method", "inner/filename", 99) });
+    Exception exception = new Exception("error", cause);
+    exception.setStackTrace(new StackTraceElement[] {
+        new StackTraceElement("some.class", "some.method", "filename", 42),
+        cause.getStackTrace()[0] });
+    final DebugTabStacktrace tab = new DebugTabStacktrace(exception);
+    assertEquals("{\"exceptions\":["
+        + "{\"class\":\"java.lang.Exception\",\"message\":\"error\","
+        + "\"invocation\":{\"class\":\"some.class\",\"method\":\"some.method\",\"line\":42}},"
+        + "{\"class\":\"java.lang.Exception\",\"message\":\"innerError\","
+        + "\"invocation\":{\"class\":\"inner.class\",\"method\":\"inner.method\",\"line\":99}}],"
+        + "\"stacktrace\":["
+        + "{\"class\":\"some.class\",\"method\":\"some.method\",\"line\":42},"
+        + "{\"class\":\"inner.class\",\"method\":\"inner.method\",\"line\":99}]}",
+        createJson(tab));
+
+    assertEquals("<h2>java.lang.Exception</h2>\n"
+        + "<p>innerError</p>\n"
+        + "<table>\n"
+        + "<thead>\n"
+        + "<tr>\n"
+        + "<th class=\"name\">Class</th>\n"
+        + "<th class=\"name\">Method</th>\n"
+        + "<th class=\"value\">Line number in class</th>\n"
+        + "</tr>\n"
+        + "</thead>\n"
+        + "<tbody>\n"
+        + "<tr>\n"
+        + "<td class=\"name\">inner.class</td>\n"
+        + "<td class=\"name\">inner.method</td>\n"
+        + "<td class=\"value\">99</td>\n"
+        + "</tr>\n"
+        + "</tbody>\n"
+        + "</table>\n"
+        + "<h2>java.lang.Exception</h2>\n"
+        + "<p>error</p>\n"
+        + "<table>\n"
+        + "<thead>\n"
+        + "<tr>\n"
+        + "<th class=\"name\">Class</th>\n"
+        + "<th class=\"name\">Method</th>\n"
+        + "<th class=\"value\">Line number in class</th>\n"
+        + "</tr>\n"
+        + "</thead>\n"
+        + "<tbody>\n"
+        + "<tr>\n"
+        + "<td class=\"name\">some.class</td>\n"
+        + "<td class=\"name\">some.method</td>\n"
+        + "<td class=\"value\">42</td>\n"
+        + "</tr>\n"
+        + "</tbody>\n"
+        + "</table>\n"
+        + "<h2>Stacktrace</h2>\n"
+        + "<table>\n"
+        + "<thead>\n"
+        + "<tr>\n"
+        + "<th class=\"name\">Class</th>\n"
+        + "<th class=\"name\">Method</th>\n"
+        + "<th class=\"value\">Line number in class</th>\n"
+        + "</tr>\n"
+        + "</thead>\n"
+        + "<tbody>\n"
+        + "<tr>\n"
+        + "<td class=\"name\">some.class</td>\n"
+        + "<td class=\"name\">some.method</td>\n"
+        + "<td class=\"value\">42</td>\n"
+        + "</tr>\n"
+        + "<tr>\n"
+        + "<td class=\"name\">inner.class</td>\n"
+        + "<td class=\"name\">inner.method</td>\n"
+        + "<td class=\"value\">99</td>\n"
+        + "</tr>\n"
+        + "</tbody>\n"
+        + "</table>\n",
+        createHtml(tab));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabUriTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabUriTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabUriTest.java
new file mode 100644
index 0000000..26b7db1
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/DebugTabUriTest.java
@@ -0,0 +1,405 @@
+/*
+ * 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.debug;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmFunction;
+import org.apache.olingo.commons.api.edm.EdmFunctionImport;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmReturnType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.server.api.uri.UriInfoKind;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.queryoption.SelectItem;
+import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression.StandardMethod;
+import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
+import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
+import org.apache.olingo.server.core.uri.UriInfoImpl;
+import org.apache.olingo.server.core.uri.UriParameterImpl;
+import org.apache.olingo.server.core.uri.UriResourceEntitySetImpl;
+import org.apache.olingo.server.core.uri.UriResourceFunctionImpl;
+import org.apache.olingo.server.core.uri.UriResourceNavigationPropertyImpl;
+import org.apache.olingo.server.core.uri.UriResourcePrimitivePropertyImpl;
+import org.apache.olingo.server.core.uri.parser.search.SearchTermImpl;
+import org.apache.olingo.server.core.uri.queryoption.AliasQueryOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.ApplyOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.CountOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.CustomQueryOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.ExpandOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.FilterOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.FormatOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.IdOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.LevelsOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.OrderByItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.OrderByOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SearchOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SkipOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.SkipTokenOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.TopOptionImpl;
+import org.apache.olingo.server.core.uri.queryoption.apply.AggregateExpressionImpl;
+import org.apache.olingo.server.core.uri.queryoption.apply.AggregateImpl;
+import org.apache.olingo.server.core.uri.queryoption.apply.GroupByImpl;
+import org.apache.olingo.server.core.uri.queryoption.apply.GroupByItemImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.BinaryImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.LiteralImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.MemberImpl;
+import org.apache.olingo.server.core.uri.queryoption.expression.MethodImpl;
+import org.junit.Test;
+
+public class DebugTabUriTest extends AbstractDebugTabTest {
+
+  @Test
+  public void resourceEntitySet() throws Exception {
+    EdmEntitySet edmEntitySet = mock(EdmEntitySet.class);
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.resource)
+        .addResourcePart(new UriResourceEntitySetImpl(edmEntitySet)));
+
+    assertEquals("{\"kind\":\"resource\",\"uriResourceParts\":["
+        + "{\"uriResourceKind\":\"entitySet\",\"segment\":null,\"isCollection\":true}]}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Resource Path</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("uriResourceKind"), containsString("entitySet"),
+        containsString("segment"), containsString("null")));
+    assertThat(html, allOf(
+        containsString("isCollection"), containsString("true"),
+        endsWith("</li>\n</ul>\n")));
+  }
+
+  @Test
+  public void resourceFunction() throws Exception {
+    EdmReturnType returnType = mock(EdmReturnType.class);
+    EdmFunction edmFunction = mock(EdmFunction.class);
+    when(edmFunction.getReturnType()).thenReturn(returnType);
+    EdmFunctionImport edmFunctionImport = mock(EdmFunctionImport.class);
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.resource)
+        .addResourcePart(new UriResourceFunctionImpl(edmFunctionImport, edmFunction,
+            Arrays.asList((UriParameter) new UriParameterImpl().setName("parameter1")))));
+
+    assertEquals("{\"kind\":\"resource\",\"uriResourceParts\":["
+        + "{\"uriResourceKind\":\"function\",\"segment\":null,\"isCollection\":false,"
+        + "\"parameters\":{\"parameter1\":null}}]}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Resource Path</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("uriResourceKind"), containsString("function"),
+        containsString("segment"), containsString("null")));
+    assertThat(html, allOf(
+        containsString("isCollection"), containsString("false"),
+        containsString("parameters"), containsString("parameter1"),
+        endsWith("</li>\n</ul>\n")));
+  }
+
+  @Test
+  public void crossjoin() throws Exception {
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.crossjoin)
+        .addEntitySetName("ES1").addEntitySetName("ES2"));
+
+    assertEquals("{\"kind\":\"crossjoin\",\"entitySetNames\":[\"ES1\",\"ES2\"]}", createJson(tab));
+
+    assertEquals("<h2>Crossjoin EntitySet Names</h2>\n"
+        + "<ul>\n"
+        + "<li>ES1</li>\n"
+        + "<li>ES2</li>\n"
+        + "</ul>\n",
+        createHtml(tab));
+  }
+
+  @Test
+  public void entityId() throws Exception {
+    EdmEntityType edmEntityType = mock(EdmEntityType.class);
+    when(edmEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("ns", "entityType"));
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.entityId)
+        .setEntityTypeCast(edmEntityType));
+
+    assertEquals("{\"kind\":\"entityId\",\"typeCast\":\"ns.entityType\"}", createJson(tab));
+
+    assertEquals("<h2>Kind</h2>\n"
+        + "<p>entityId</p>\n"
+        + "<h2>Type Cast</h2>\n"
+        + "<p>ns.entityType</p>\n",
+        createHtml(tab));
+  }
+
+  @Test
+  public void simpleQueryOptions() throws Exception {
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setQueryOption(new FormatOptionImpl().setFormat("json"))
+        .setQueryOption(new IdOptionImpl().setValue("ES(42)").setText("ES(42)"))
+        .setQueryOption(new SkipOptionImpl().setValue(123).setText("123"))
+        .setQueryOption(new TopOptionImpl().setValue(456).setText("456"))
+        .setQueryOption(new SkipTokenOptionImpl().setValue("xyz123"))
+        .setQueryOption(new CountOptionImpl().setValue(false).setText("false"))
+        .setQueryOption(new SearchOptionImpl().setSearchExpression(new SearchTermImpl("searchTest")))
+        .setQueryOption(new CustomQueryOptionImpl().setName("customQuery").setText("customValue"))
+        .setQueryOption(new AliasQueryOptionImpl().setAliasValue(null).setName("@alias")));
+
+    assertEquals("{\"kind\":\"all\","
+        + "\"format\":\"json\","
+        + "\"id\":\"ES(42)\","
+        + "\"skiptoken\":\"xyz123\","
+        + "\"isCount\":false,"
+        + "\"skip\":123,"
+        + "\"top\":456,"
+        + "\"search\":{\"nodeType\":\"searchTerm\",\"searchTerm\":\"searchTest\"},"
+        + "\"aliases\":{\"@alias\":null},"
+        + "\"customQueryOptions\":{\"customQuery\":\"customValue\"}"
+        + "}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Kind</h2>\n"
+            + "<p>all</p>\n"),
+        containsString("<h2>Search Option</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("searchTerm"), containsString("searchTest"),
+        containsString("<h2>Unstructured System Query Options</h2>\n"
+            + "<table>\n"
+            + "<thead>\n"
+            + "<tr><th class=\"name\">Name</th><th class=\"value\">Value</th></tr>\n"
+            + "</thead>\n"
+            + "<tbody>\n"
+            + "<tr><td class=\"name\">$count</td><td class=\"value\">false</td></tr>\n"
+            + "<tr><td class=\"name\">$skip</td><td class=\"value\">123</td></tr>\n"
+            + "<tr><td class=\"name\">$skiptoken</td><td class=\"value\">xyz123</td></tr>\n"
+            + "<tr><td class=\"name\">$top</td><td class=\"value\">456</td></tr>\n"
+            + "<tr><td class=\"name\">$format</td><td class=\"value\">json</td></tr>\n"
+            + "<tr><td class=\"name\">$id</td><td class=\"value\">ES(42)</td></tr>\n"
+            + "</tbody>\n"
+            + "</table>\n")));
+    assertThat(html, allOf(
+        containsString("<h2>Aliases</h2>\n"
+            + "<table>\n"
+            + "<thead>\n"
+            + "<tr><th class=\"name\">Name</th><th class=\"value\">Value</th></tr>\n"
+            + "</thead>\n"
+            + "<tbody>\n"
+            + "<tr><td class=\"name\">@alias</td><td class=\"value\">null</td></tr>\n"
+            + "</tbody>\n"
+            + "</table>\n"),
+        endsWith("<h2>Custom Query Options</h2>\n"
+            + "<table>\n"
+            + "<thead>\n"
+            + "<tr><th class=\"name\">Name</th><th class=\"value\">Value</th></tr>\n"
+            + "</thead>\n"
+            + "<tbody>\n"
+            + "<tr><td class=\"name\">customQuery</td><td class=\"value\">customValue</td></tr>\n"
+            + "</tbody>\n"
+            + "</table>\n")));
+  }
+
+  @Test
+  public void select() throws Exception {
+    EdmProperty edmProperty = mock(EdmProperty.class);
+    when(edmProperty.getName()).thenReturn("property");
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setSystemQueryOption(new SelectOptionImpl().setSelectItems(Arrays.asList(
+            (SelectItem) new SelectItemImpl().setStar(true),
+            new SelectItemImpl().setResourcePath(
+                new UriInfoImpl().setKind(UriInfoKind.resource)
+                    .addResourcePart(new UriResourcePrimitivePropertyImpl(edmProperty)))))));
+    assertEquals("{\"kind\":\"all\",\"select\":[\"*\",\"property\"]}", createJson(tab));
+
+    assertEquals("<h2>Kind</h2>\n"
+        + "<p>all</p>\n"
+        + "<h2>Selected Properties</h2>\n"
+        + "<ul>\n"
+        + "<li>*</li>\n"
+        + "<li>property</li>\n"
+        + "</ul>\n",
+        createHtml(tab));
+  }
+
+  @Test
+  public void expand() throws Exception {
+    EdmNavigationProperty edmProperty = mock(EdmNavigationProperty.class);
+    when(edmProperty.getName()).thenReturn("property");
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setSystemQueryOption(new ExpandOptionImpl().addExpandItem(
+            new ExpandItemImpl().setResourcePath(
+                new UriInfoImpl().setKind(UriInfoKind.resource)
+                    .addResourcePart(new UriResourceNavigationPropertyImpl(edmProperty)))
+                .setSystemQueryOption(new LevelsOptionImpl().setValue(1)))));
+    assertEquals("{\"kind\":\"all\",\"expand\":[{\"expandPath\":["
+        + "{\"uriResourceKind\":\"navigationProperty\",\"segment\":\"property\",\"isCollection\":false}],"
+        + "\"levels\":1}]}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Kind</h2>\n"
+            + "<p>all</p>\n"
+            + "<h2>Expand Option</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("navigationProperty"), containsString("property"),
+        containsString("isCollection"), containsString("false")));
+    assertThat(html, allOf(containsString("levels"), endsWith("</li>\n</ul>\n")));
+  }
+
+  @Test
+  public void filter() throws Exception {
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setSystemQueryOption(new FilterOptionImpl().setExpression(
+            new BinaryImpl(new LiteralImpl("1", EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64)),
+                BinaryOperatorKind.GT,
+                new LiteralImpl("2", EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)),
+                EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)))));
+    assertEquals("{\"kind\":\"all\",\"filter\":{"
+        + "\"nodeType\":\"binary\",\"operator\":\"gt\",\"type\":\"Boolean\","
+        + "\"left\":{\"nodeType\":\"literal\",\"type\":\"Edm.Int64\",\"value\":\"1\"},"
+        + "\"right\":{\"nodeType\":\"literal\",\"type\":\"Edm.SByte\",\"value\":\"2\"}}}",
+        createJson(tab));
+
+    assertThat(createHtml(tab), allOf(
+        startsWith("<h2>Kind</h2>\n"
+            + "<p>all</p>\n"
+            + "<h2>Filter Option</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("nodeType"), containsString("binary"),
+        containsString("operator"), containsString("gt")));
+    assertThat(createHtml(tab), allOf(
+        containsString("literal"), containsString("Int64"),
+        containsString("SByte"), containsString("2"),
+        endsWith("</li>\n</ul>\n")));
+
+    assertEquals("{\"kind\":\"all\",\"filter\":{"
+        + "\"nodeType\":\"method\",\"operator\":\"ceiling\",\"type\":\"Number\","
+        + "\"parameters\":[{\"nodeType\":\"literal\",\"type\":\"Edm.Decimal\",\"value\":\"1.5\"}]}}",
+        createJson(new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+            .setSystemQueryOption(new FilterOptionImpl().setExpression(
+                new MethodImpl(MethodKind.CEILING, Arrays.asList(
+                    (Expression) new LiteralImpl("1.5",
+                        EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)))))))));
+
+    EdmEntityType edmEntityType = mock(EdmEntityType.class);
+    when(edmEntityType.getFullQualifiedName()).thenReturn(new FullQualifiedName("ns", "entityType"));
+    EdmProperty edmProperty = mock(EdmProperty.class);
+    when(edmProperty.getName()).thenReturn("property");
+    assertEquals("{\"kind\":\"all\",\"filter\":{"
+        + "\"nodeType\":\"member\",\"type\":\"unknown\",\"typeFilter\":\"ns.entityType\","
+        + "\"resourceSegments\":[{\"nodeType\":\"primitiveProperty\",\"name\":\"property\",\"type\":\"unknown\"}]}}",
+        createJson(new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+            .setSystemQueryOption(new FilterOptionImpl().setExpression(
+                new MemberImpl(new UriInfoImpl().setKind(UriInfoKind.resource)
+                    .addResourcePart(new UriResourcePrimitivePropertyImpl(edmProperty)),
+                    edmEntityType))))));
+  }
+
+  @Test
+  public void orderby() throws Exception {
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setSystemQueryOption(new OrderByOptionImpl().addOrder(
+            new OrderByItemImpl().setExpression(
+                new LiteralImpl("false", EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)))
+                .setDescending(true))));
+
+    assertEquals("{\"kind\":\"all\",\"orderby\":{\"nodeType\":\"orderCollection\",\"orders\":["
+        + "{\"nodeType\":\"order\",\"sortorder\":\"desc\",\"expression\":"
+        + "{\"nodeType\":\"literal\",\"type\":\"Edm.Boolean\",\"value\":\"false\"}}]}}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Kind</h2>\n"
+            + "<p>all</p>\n"
+            + "<h2>OrderBy Option</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("nodeType"), containsString("order"),
+        containsString("sortorder"), containsString("desc")));
+    assertThat(html, allOf(
+        containsString("expression"), containsString("literal"),
+        containsString("Edm.Boolean"), containsString("false"),
+        endsWith("</li>\n</ul>\n")));
+  }
+
+  @Test
+  public void apply() throws Exception {
+    EdmProperty edmProperty = mock(EdmProperty.class);
+    when(edmProperty.getName()).thenReturn("property");
+    final DebugTabUri tab = new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+        .setSystemQueryOption(new ApplyOptionImpl().add(new AggregateImpl().addExpression(
+            new AggregateExpressionImpl()
+                .setPath(new UriInfoImpl().setKind(UriInfoKind.resource).addResourcePart(
+                    new UriResourcePrimitivePropertyImpl(edmProperty)))
+                .setStandardMethod(StandardMethod.AVERAGE)
+                .setAlias("average")))));
+
+    assertEquals("{\"kind\":\"all\",\"apply\":[{\"kind\":\"AGGREGATE\",\"aggregate\":[{"
+        + "\"path\":[{\"uriResourceKind\":\"primitiveProperty\",\"segment\":\"property\",\"isCollection\":false}],"
+        + "\"standardMethod\":\"AVERAGE\",\"as\":\"average\"}]}]}",
+        createJson(tab));
+
+    final String html = createHtml(tab);
+    assertThat(html, allOf(
+        startsWith("<h2>Kind</h2>\n"
+            + "<p>all</p>\n"
+            + "<h2>Apply Option</h2>\n"
+            + "<ul>\n"
+            + "<li class=\"json\">"),
+        containsString("kind"), containsString("AGGREGATE"),
+        containsString("aggregate"), containsString("path")));
+    assertThat(html, allOf(
+        containsString("primitiveProperty"), containsString("property"),
+        containsString("standardMethod"), containsString("AVERAGE")));
+    assertThat(html, allOf(
+        containsString("as"), containsString("average"),
+        endsWith("</li>\n</ul>\n")));
+
+    assertEquals("{\"kind\":\"all\",\"apply\":[{\"kind\":\"GROUP_BY\",\"groupBy\":[{"
+        + "\"path\":[{\"uriResourceKind\":\"primitiveProperty\",\"segment\":\"property\",\"isCollection\":false}],"
+        + "\"isRollupAll\":true}]}]}",
+        createJson(new DebugTabUri(new UriInfoImpl().setKind(UriInfoKind.all)
+            .setSystemQueryOption(new ApplyOptionImpl().add(new GroupByImpl().addGroupByItem(
+                new GroupByItemImpl().setIsRollupAll()
+                    .setPath(new UriInfoImpl().setKind(UriInfoKind.resource).addResourcePart(
+                        new UriResourcePrimitivePropertyImpl(edmProperty)))))))));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/9e67d0e3/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/ServerCoreDebuggerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/ServerCoreDebuggerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/ServerCoreDebuggerTest.java
index 280ff38..9772061 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/ServerCoreDebuggerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/debug/ServerCoreDebuggerTest.java
@@ -18,8 +18,11 @@
  */
 package org.apache.olingo.server.core.debug;
 
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -36,20 +39,22 @@ import org.apache.olingo.server.api.OData;
 import org.apache.olingo.server.api.ODataResponse;
 import org.apache.olingo.server.api.debug.DebugInformation;
 import org.apache.olingo.server.api.debug.DebugSupport;
+import org.apache.olingo.server.api.debug.DefaultDebugSupport;
 import org.junit.Before;
 import org.junit.Test;
 
 public class ServerCoreDebuggerTest {
 
+  private final OData odata = OData.newInstance();
   private ServerCoreDebugger debugger;
 
   @Before
   public void setupDebugger() {
-    debugger = new ServerCoreDebugger(OData.newInstance());
+    debugger = new ServerCoreDebugger(odata);
     DebugSupport processor = mock(DebugSupport.class);
     when(processor.isUserAuthorized()).thenReturn(true);
     when(processor.createDebugResponse(anyString(), any(DebugInformation.class)))
-    .thenThrow(new ODataRuntimeException("Test"));
+        .thenThrow(new ODataRuntimeException("Test"));
     debugger.setDebugSupportProcessor(processor);
   }
 
@@ -63,7 +68,7 @@ public class ServerCoreDebuggerTest {
     HttpServletRequest request = mock(HttpServletRequest.class);
     when(request.getParameter(DebugSupport.ODATA_DEBUG_QUERY_PARAMETER)).thenReturn(DebugSupport.ODATA_DEBUG_JSON);
 
-    ServerCoreDebugger localDebugger = new ServerCoreDebugger(OData.newInstance());
+    ServerCoreDebugger localDebugger = new ServerCoreDebugger(odata);
     localDebugger.resolveDebugMode(request);
     assertFalse(debugger.isDebugMode());
   }
@@ -84,7 +89,7 @@ public class ServerCoreDebuggerTest {
     DebugSupport debugSupportMock = mock(DebugSupport.class);
     when(debugSupportMock.isUserAuthorized()).thenReturn(false);
 
-    ServerCoreDebugger localDebugger = new ServerCoreDebugger(OData.newInstance());
+    ServerCoreDebugger localDebugger = new ServerCoreDebugger(odata);
     localDebugger.setDebugSupportProcessor(debugSupportMock);
 
     localDebugger.resolveDebugMode(request);
@@ -108,4 +113,28 @@ public class ServerCoreDebuggerTest {
 
     assertEquals(odResponse, debugResponse);
   }
+
+  @Test
+  public void runtimeMeasurement() throws Exception {
+    ServerCoreDebugger defaultDebugger = new ServerCoreDebugger(odata);
+    defaultDebugger.setDebugSupportProcessor(new DefaultDebugSupport());
+    HttpServletRequest request = mock(HttpServletRequest.class);
+    when(request.getParameter(DebugSupport.ODATA_DEBUG_QUERY_PARAMETER)).thenReturn(DebugSupport.ODATA_DEBUG_JSON);
+    defaultDebugger.resolveDebugMode(request);
+
+    final int handle = defaultDebugger.startRuntimeMeasurement("someClass", "someMethod");
+    defaultDebugger.stopRuntimeMeasurement(handle);
+    assertEquals(0, handle);
+
+    assertThat(IOUtils.toString(defaultDebugger.createDebugResponse(null, null, null, null, null).getContent()),
+        allOf(containsString("\"runtime\""), containsString("\"someClass\""), containsString("\"someMethod\""),
+            containsString("]}}")));
+
+    request = mock(HttpServletRequest.class);
+    when(request.getParameter(DebugSupport.ODATA_DEBUG_QUERY_PARAMETER)).thenReturn(DebugSupport.ODATA_DEBUG_HTML);
+    defaultDebugger.resolveDebugMode(request);
+    assertThat(IOUtils.toString(defaultDebugger.createDebugResponse(null, null, null, null, null).getContent()),
+        allOf(containsString(">Runtime<"), containsString(">someClass<"), containsString(">someMethod("),
+            containsString("</html>")));
+  }
 }