You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/09/17 06:32:41 UTC

git commit: [OLINGO-422] More support for expand and select in ContextURL

Repository: olingo-odata4
Updated Branches:
  refs/heads/OLINGO-422-SelectExpandSupport 461cdbbe2 -> a1d9f747b


[OLINGO-422] More support for expand and select in ContextURL


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

Branch: refs/heads/OLINGO-422-SelectExpandSupport
Commit: a1d9f747b8a44c1993f49b772eff149aab0c8db9
Parents: 461cdbb
Author: mibo <mi...@apache.org>
Authored: Wed Sep 17 06:27:19 2014 +0200
Committer: mibo <mi...@apache.org>
Committed: Wed Sep 17 06:32:28 2014 +0200

----------------------------------------------------------------------
 .../server/api/serializer/ODataSerializer.java  |  13 ++
 .../core/serializer/ODataXmlSerializerImpl.java |   8 +
 .../serializer/json/ODataJsonSerializer.java    |  32 +---
 .../serializer/utils/ContextURLBuilder.java     |   4 +-
 .../core/serializer/utils/ContextURLHelper.java | 144 ++++++++++++++++++
 .../serializer/utils/ExpandSelectHelper.java    |  35 ++++-
 .../serializer/utils/ContextURLBuilderTest.java |  15 +-
 .../tecsvc/processor/TechnicalProcessor.java    |  24 ++-
 .../json/ODataJsonSerializerTest.java           | 148 +++++++++---------
 .../serializer/utils/ContextURLHelperTest.java  | 151 +++++++++++++++++++
 10 files changed, 452 insertions(+), 122 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
index cc13485..91542aa 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
@@ -25,6 +25,8 @@ import org.apache.olingo.commons.api.data.EntitySet;
 import org.apache.olingo.commons.api.edm.Edm;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
 import org.apache.olingo.server.api.ODataServerError;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 
 public interface ODataSerializer {
 
@@ -47,4 +49,15 @@ public interface ODataSerializer {
    * @throws ODataSerializerException 
    */
   InputStream error(ODataServerError error) throws ODataSerializerException;
+
+  /**
+   * Builds the select-list part of a {@link org.apache.olingo.commons.api.data.ContextURL ContextURL}.
+   * @param edmEntitySet the Entity Set
+   * @param expand       the $expand option
+   * @param select       the $select option
+   * @return a String with the select list
+   * @throws ODataSerializerException
+   */
+  String buildContextURLSelectList(EdmEntitySet edmEntitySet, ExpandOption expand, SelectOption select)
+      throws ODataSerializerException;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/ODataXmlSerializerImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/ODataXmlSerializerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/ODataXmlSerializerImpl.java
index 65b0b3a..580833a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/ODataXmlSerializerImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/ODataXmlSerializerImpl.java
@@ -32,7 +32,10 @@ import org.apache.olingo.server.api.ODataServerError;
 import org.apache.olingo.server.api.serializer.ODataSerializer;
 import org.apache.olingo.server.api.serializer.ODataSerializerException;
 import org.apache.olingo.server.api.serializer.ODataSerializerOptions;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+import org.apache.olingo.server.core.serializer.utils.ContextURLHelper;
 import org.apache.olingo.server.core.serializer.xml.MetadataDocumentXmlSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -98,4 +101,9 @@ public class ODataXmlSerializerImpl implements ODataSerializer {
         ODataSerializerException.MessageKeys.NOT_IMPLEMENTED);
   }
 
+  @Override
+  public String buildContextURLSelectList(final EdmEntitySet edmEntitySet,
+      final ExpandOption expand, final SelectOption select) throws ODataSerializerException {
+    return ContextURLHelper.buildSelectList(edmEntitySet.getEntityType(), expand, select);
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index 6ab22b5..094b72e 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -20,7 +20,6 @@ package org.apache.olingo.server.core.serializer.json;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -51,6 +50,7 @@ import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
 import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
 import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
+import org.apache.olingo.server.core.serializer.utils.ContextURLHelper;
 import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -382,9 +382,9 @@ public class ODataJsonSerializer implements ODataSerializer {
     json.writeStartObject();
     for (final String propertyName : type.getPropertyNames()) {
       final Property property = findProperty(propertyName, properties);
-      if (selectedPaths == null || isSelected(selectedPaths, propertyName)) {
+      if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) {
         writeProperty((EdmProperty) type.getProperty(propertyName), property,
-            selectedPaths == null ? null : getReducedSelectedPaths(selectedPaths, propertyName),
+            selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName),
             json);
       }
     }
@@ -400,27 +400,9 @@ public class ODataJsonSerializer implements ODataSerializer {
     return null;
   }
 
-  private static boolean isSelected(final Set<List<String>> selectedPaths, final String propertyName) {
-    for (final List<String> path : selectedPaths) {
-      if (propertyName.equals(path.get(0))) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private static Set<List<String>> getReducedSelectedPaths(final Set<List<String>> selectedPaths,
-      final String propertyName) {
-    Set<List<String>> reducedPaths = new HashSet<List<String>>();
-    for (final List<String> path : selectedPaths) {
-      if (path.size() > 1) {
-        if (propertyName.equals(path.get(0))) {
-          reducedPaths.add(path.subList(1, path.size()));
-        }
-      } else {
-        return null;
-      }
-    }
-    return reducedPaths.isEmpty() ? null : reducedPaths;
+  @Override
+  public String buildContextURLSelectList(final EdmEntitySet edmEntitySet,
+      final ExpandOption expand, final SelectOption select) throws ODataSerializerException {
+    return ContextURLHelper.buildSelectList(edmEntitySet.getEntityType(), expand, select);
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
index faeb571..a9c1315 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
@@ -19,7 +19,6 @@
 package org.apache.olingo.server.core.serializer.utils;
 
 import java.net.URI;
-
 import org.apache.olingo.commons.api.Constants;
 import org.apache.olingo.commons.api.data.ContextURL;
 import org.apache.olingo.commons.core.Encoder;
@@ -41,6 +40,9 @@ public final class ContextURLBuilder {
       }
       result.append('/').append(Encoder.encode(contextURL.getDerivedEntity()));
     }
+    if (contextURL.getSelectList() != null) {
+      result.append('(').append(contextURL.getSelectList()).append(')');
+    }
     if (contextURL.isReference()) {
       if (contextURL.getEntitySetOrSingletonOrType() != null) {
         throw new IllegalArgumentException("ContextURL: $ref with Entity Set");

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelper.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelper.java
new file mode 100644
index 0000000..49aa3c3
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.utils;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.core.Encoder;
+import org.apache.olingo.server.api.serializer.ODataSerializerException;
+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.SelectItem;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
+
+public final class ContextURLHelper {
+
+  /** Builds a list of selected Properties for the ContextURL,
+   *  taking care to preserve the order as defined in the EDM;
+   *  returns NULL if no selection has taken place.
+   * @param entityType the Entity Type
+   * @param expand     the Expand option (from the URL's $expand query option)
+   * @param select     the Select option (from the URL's $select query option)
+   * @return a select-list String
+   * @throws ODataSerializerException if an unsupported feature is used
+   */
+  public static String buildSelectList(final EdmEntityType entityType,
+      final ExpandOption expand, final SelectOption select) throws ODataSerializerException {
+    StringBuilder result = new StringBuilder();
+    boolean isSelected = select != null && select.getSelectItems() != null && !select.getSelectItems().isEmpty();
+    if(isSelected) {
+      handleSelect(entityType, select, result);
+    }
+
+    if (ExpandSelectHelper.hasExpand(expand) && !ExpandSelectHelper.isExpandAll(expand)) {
+      handleExpand(entityType, expand, result);
+    }
+    return result.length() == 0 ? null : result.toString();
+  }
+
+  private static void handleSelect(EdmEntityType entityType, SelectOption select, StringBuilder result) {
+    if (ExpandSelectHelper.isAll(select)) {
+      result.append('*');
+    } else {
+      final List<SelectItem> selectItems = select.getSelectItems();
+      final Set<String> selectedPropertyNames = ExpandSelectHelper.getSelectedPropertyNames(selectItems);
+      for (final String propertyName : entityType.getPropertyNames()) {
+        if (selectedPropertyNames.contains(propertyName)) {
+          if (result.length() > 0) {
+            result.append(',');
+          }
+          final EdmProperty edmProperty = (EdmProperty) entityType.getProperty(propertyName);
+          final Set<List<String>> selectedPaths = ExpandSelectHelper.getSelectedPaths(selectItems, propertyName);
+          if (selectedPaths == null) {
+            result.append(Encoder.encode(propertyName));
+          } else {
+            final List<List<String>> complexSelectedPaths = getComplexSelectedPaths(edmProperty, selectedPaths);
+            boolean first = true;
+            for (final List<String> path : complexSelectedPaths) {
+              if (first) {
+                first = false;
+              } else {
+                result.append(',');
+              }
+              boolean innerFirst = true;
+              for (final String name : path) {
+                if (innerFirst) {
+                  innerFirst = false;
+                } else {
+                  result.append('/');
+                }
+                result.append(Encoder.encode(name));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private static void handleExpand(EdmEntityType entityType, ExpandOption expand, StringBuilder result)
+      throws ODataSerializerException {
+    final Set<String> expandedPropertyNames = ExpandSelectHelper.getExpandedPropertyNames(expand.getExpandItems());
+    for (final String propertyName : entityType.getNavigationPropertyNames()) {
+      if (expandedPropertyNames.contains(propertyName)) {
+        final ExpandItem expandItem = ExpandSelectHelper.getExpandItem(expand.getExpandItems(), propertyName);
+        if (ExpandSelectHelper.hasExpand(expandItem.getExpandOption())
+            && !ExpandSelectHelper.isExpandAll(expandItem.getExpandOption())
+            || ExpandSelectHelper.hasSelect(expandItem.getSelectOption())) {
+          final String innerSelectList = buildSelectList(entityType.getNavigationProperty(propertyName).getType(),
+              expandItem.getExpandOption(), expandItem.getSelectOption());
+          if (result.length() > 0) {
+            result.append(',');
+          }
+          result.append(Encoder.encode(propertyName)).append('(').append(innerSelectList).append(')');
+        }
+      }
+    }
+  }
+
+  private static List<List<String>> getComplexSelectedPaths(final EdmProperty edmProperty,
+      final Set<List<String>> selectedPaths) {
+    List<List<String>> result = new ArrayList<List<String>>();
+    if (selectedPaths == null) {
+      List<String> path = new LinkedList<String>();
+      path.add(edmProperty.getName());
+      result.add(path);
+    } else {
+      final EdmComplexType type = (EdmComplexType) edmProperty.getType();
+      for (final String complexPropertyName : type.getPropertyNames()) {
+        if (ExpandSelectHelper.isSelected(selectedPaths, complexPropertyName)) {
+          List<List<String>> complexSelectedPaths = getComplexSelectedPaths(
+              (EdmProperty) type.getProperty(complexPropertyName),
+              ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, complexPropertyName));
+          for (final List<String> path : complexSelectedPaths) {
+            path.add(0, edmProperty.getName());
+            result.add(path);
+          }
+        }
+      }
+    }
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java
index c5dcbbe..37dbbac 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ExpandSelectHelper.java
@@ -34,16 +34,20 @@ import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 
 public abstract class ExpandSelectHelper {
 
+  public static boolean hasSelect(final SelectOption select) {
+    return select != null && select.getSelectItems() != null && !select.getSelectItems().isEmpty();
+  }
+
   public static boolean isAll(final SelectOption select) {
-    if (select == null || select.getSelectItems() == null || select.getSelectItems().isEmpty()) {
-      return true;
-    } else {
+    if (hasSelect(select)) {
       for (final SelectItem item : select.getSelectItems()) {
         if (item.isStar()) {
           return true;
         }
       }
       return false;
+    } else {
+      return true;
     }
   }
 
@@ -81,6 +85,31 @@ public abstract class ExpandSelectHelper {
     return selectedPaths.isEmpty() ? null : selectedPaths;
   }
 
+
+  public static boolean isSelected(final Set<List<String>> selectedPaths, final String propertyName) {
+    for (final List<String> path : selectedPaths) {
+      if (propertyName.equals(path.get(0))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static Set<List<String>> getReducedSelectedPaths(final Set<List<String>> selectedPaths,
+      final String propertyName) {
+    Set<List<String>> reducedPaths = new HashSet<List<String>>();
+    for (final List<String> path : selectedPaths) {
+      if (propertyName.equals(path.get(0))) {
+        if (path.size() > 1) {
+          reducedPaths.add(path.subList(1, path.size()));
+        } else {
+          return null;
+        }
+      }
+    }
+    return reducedPaths.isEmpty() ? null : reducedPaths;
+  }
+
   public static boolean hasExpand(final ExpandOption expand) {
     return expand != null && expand.getExpandItems() != null && !expand.getExpandItems().isEmpty();
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilderTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilderTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilderTest.java
index 7a01643..bc136f4 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilderTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilderTest.java
@@ -21,7 +21,6 @@ package org.apache.olingo.server.core.serializer.utils;
 import static org.junit.Assert.assertEquals;
 
 import java.net.URI;
-
 import org.apache.olingo.commons.api.data.ContextURL;
 import org.apache.olingo.commons.api.data.ContextURL.Suffix;
 import org.apache.olingo.commons.api.edm.EdmEntitySet;
@@ -34,14 +33,14 @@ public class ContextURLBuilderTest {
 
   @Test
   public void buildServiceDocument() {
-    ContextURL contextURL = ContextURL.with()
+    final ContextURL contextURL = ContextURL.with()
         .serviceRoot(URI.create("http://host/service/")).build();
     assertEquals("http://host/service/$metadata", ContextURLBuilder.create(contextURL).toASCIIString());
   }
 
   @Test
   public void buildRelative() {
-    ContextURL contextURL = ContextURL.with().build();
+    final ContextURL contextURL = ContextURL.with().build();
     assertEquals("$metadata", ContextURLBuilder.create(contextURL).toASCIIString());
   }
 
@@ -49,7 +48,7 @@ public class ContextURLBuilderTest {
   public void buildEntitySet() {
     EdmEntitySet entitySet = Mockito.mock(EdmEntitySet.class);
     Mockito.when(entitySet.getName()).thenReturn("Customers");
-    ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
+    final ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
         .entitySet(entitySet)
         .build();
     assertEquals("http://host/service/$metadata#Customers", ContextURLBuilder.create(contextURL).toASCIIString());
@@ -61,7 +60,7 @@ public class ContextURLBuilderTest {
     Mockito.when(entitySet.getName()).thenReturn("Customers");
     EdmEntityType derivedType = Mockito.mock(EdmEntityType.class);
     Mockito.when(derivedType.getFullQualifiedName()).thenReturn(new FullQualifiedName("Model", "VipCustomer"));
-    ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
+    final ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
         .entitySet(entitySet)
         .derived(derivedType)
         .build();
@@ -82,7 +81,7 @@ public class ContextURLBuilderTest {
     Mockito.when(entitySet.getName()).thenReturn("Customers");
     EdmEntityType derivedType = Mockito.mock(EdmEntityType.class);
     Mockito.when(derivedType.getFullQualifiedName()).thenReturn(new FullQualifiedName("Model", "VipCustomer"));
-    ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
+    final ContextURL contextURL = ContextURL.with().serviceRoot(URI.create("http://host/service/"))
         .entitySet(entitySet)
         .derived(derivedType)
         .suffix(Suffix.ENTITY)
@@ -98,7 +97,7 @@ public class ContextURLBuilderTest {
 
   @Test
   public void buildReference() {
-    ContextURL contextURL = ContextURL.with().suffix(Suffix.REFERENCE).build();
+    final ContextURL contextURL = ContextURL.with().suffix(Suffix.REFERENCE).build();
     assertEquals("$metadata#$ref", ContextURLBuilder.create(contextURL).toASCIIString());
   }
 
@@ -116,7 +115,7 @@ public class ContextURLBuilderTest {
     EdmEntityType derivedType = Mockito.mock(EdmEntityType.class);
     Mockito.when(derivedType.getFullQualifiedName()).thenReturn(
         new FullQualifiedName("Namensräumchen", "UnüblicherName"));
-    ContextURL contextURL = ContextURL.with().entitySet(entitySet).derived(derivedType).build();
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet).derived(derivedType).build();
     assertEquals("$metadata#Entit%C3%A4ten/Namensr%C3%A4umchen.Un%C3%BCblicherName",
         ContextURLBuilder.create(contextURL).toString());
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
index 950ce95..926353f 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalProcessor.java
@@ -36,11 +36,14 @@ import org.apache.olingo.server.api.ODataTranslatedException;
 import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
 import org.apache.olingo.server.api.processor.EntityProcessor;
 import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.ODataSerializerException;
 import org.apache.olingo.server.api.serializer.ODataSerializerOptions;
 import org.apache.olingo.server.api.uri.UriInfo;
 import org.apache.olingo.server.api.uri.UriInfoResource;
 import org.apache.olingo.server.api.uri.UriResource;
 import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
 import org.apache.olingo.server.tecsvc.data.DataProvider;
 
 import java.util.List;
@@ -74,11 +77,13 @@ public class TechnicalProcessor implements EntityCollectionProcessor, EntityProc
         response.setStatusCode(HttpStatusCode.NOT_FOUND.getStatusCode());
       } else {
         ODataSerializer serializer = odata.createSerializer(ODataFormat.fromContentType(requestedContentType));
+        final ExpandOption expand = uriInfo.getExpandOption();
+        final SelectOption select = uriInfo.getSelectOption();
         response.setContent(serializer.entitySet(edmEntitySet, entitySet,
             ODataSerializerOptions.with()
-                .contextURL(getContextUrl(edmEntitySet, false))
+                .contextURL(getContextUrl(serializer, edmEntitySet, false, expand, select))
                 .count(uriInfo.getCountOption())
-                .expand(uriInfo.getExpandOption()).select(uriInfo.getSelectOption())
+                .expand(expand).select(select)
                 .build()));
         response.setStatusCode(HttpStatusCode.OK.getStatusCode());
         response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
@@ -106,12 +111,13 @@ public class TechnicalProcessor implements EntityCollectionProcessor, EntityProc
         response.setStatusCode(HttpStatusCode.NOT_FOUND.getStatusCode());
       } else {
         ODataSerializer serializer = odata.createSerializer(ODataFormat.fromContentType(requestedContentType));
+        final ExpandOption expand = uriInfo.getExpandOption();
+        final SelectOption select = uriInfo.getSelectOption();
         response.setContent(serializer.entity(edmEntitySet, entity,
             ODataSerializerOptions.with()
-                .contextURL(getContextUrl(edmEntitySet, true))
+                .contextURL(getContextUrl(serializer, edmEntitySet, true, expand, select))
                 .count(uriInfo.getCountOption())
-                .expand(uriInfo.getExpandOption())
-                .select(uriInfo.getSelectOption())
+                .expand(uriInfo.getExpandOption()).select(uriInfo.getSelectOption())
                 .build()));
         response.setStatusCode(HttpStatusCode.OK.getStatusCode());
         response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
@@ -167,7 +173,11 @@ public class TechnicalProcessor implements EntityCollectionProcessor, EntityProc
     return uriResource.getEntitySet();
   }
 
-  private ContextURL getContextUrl(final EdmEntitySet entitySet, final boolean isSingleEntity) {
-    return ContextURL.with().entitySet(entitySet).suffix(isSingleEntity ? Suffix.ENTITY : null).build();
+  private ContextURL getContextUrl(final ODataSerializer serializer,
+      final EdmEntitySet entitySet, final boolean isSingleEntity,
+      final ExpandOption expand, final SelectOption select) throws ODataSerializerException {
+    return ContextURL.with().entitySet(entitySet)
+        .selectList(serializer.buildContextURLSelectList(entitySet, expand, select))
+        .suffix(isSingleEntity ? Suffix.ENTITY : null).build();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
index 213c26d..c22f677 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
@@ -350,18 +350,17 @@ public class ODataJsonSerializerTest {
     final SelectItem selectItem1 = mockSelectItem(edmEntitySet, "PropertyDate");
     final SelectItem selectItem2 = mockSelectItem(edmEntitySet, "PropertyBoolean");
     final SelectOption select = mockSelectOption(Arrays.asList(selectItem1, selectItem2, selectItem2));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entity(edmEntitySet, entity,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                // ContextURL.Builder.with().entitySet(edmEntitySet).selectList("PropertyBoolean,PropertyDate")
-                //     .suffix(Suffix.ENTITY).build(),
-                    .select(select)
-                    .build());
+    InputStream result = serializer
+        .entity(edmEntitySet, entity,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, null, select))
+                    .suffix(Suffix.ENTITY).build())
+                .select(select)
+                .build());
     final String resultString = IOUtils.toString(result);
     final String expectedResult = "{"
-        // + "\"@odata.context\":\"$metadata#ESAllPrim(PropertyBoolean,PropertyDate)/$entity\","
+        + "\"@odata.context\":\"$metadata#ESAllPrim(PropertyBoolean,PropertyDate)/$entity\","
         + "\"PropertyBoolean\":true,\"PropertyDate\":\"2012-12-03\"}";
     Assert.assertEquals(expectedResult, resultString);
   }
@@ -391,18 +390,17 @@ public class ODataJsonSerializerTest {
     final EntitySet entitySet = data.readAll(edmEntitySet);
     final SelectOption select = mockSelectOption(Arrays.asList(
         mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp", "PropertyString")));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entitySet(edmEntitySet, entitySet,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet)
-                    //     .selectList("PropertyComp/PropertyComp/PropertyString").build(),
-                    .select(select)
-                    .build());
+    InputStream result = serializer
+        .entitySet(edmEntitySet, entitySet,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, null, select))
+                    .build())
+                .select(select)
+                .build());
     final String resultString = IOUtils.toString(result);
     Assert.assertEquals("{"
-        // + "\"@odata.context\":\"$metadata#ESCompComp(PropertyComp/PropertyComp/PropertyString)\","
+        + "\"@odata.context\":\"$metadata#ESCompComp(PropertyComp/PropertyComp/PropertyString)\","
         + "\"value\":["
         + "{\"PropertyComp\":{\"PropertyComp\":{\"PropertyString\":\"String 1\"}}},"
         + "{\"PropertyComp\":{\"PropertyComp\":{\"PropertyString\":\"String 2\"}}}]}",
@@ -416,18 +414,16 @@ public class ODataJsonSerializerTest {
     final SelectOption select = mockSelectOption(Arrays.asList(
         mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp", "PropertyString"),
         mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp")));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entitySet(edmEntitySet, entitySet,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet)
-                    //     .selectList("PropertyComp/PropertyComp").build()
-                    .select(select)
-                    .build());
-    final String resultString = IOUtils.toString(result);
+    final String resultString = IOUtils.toString(serializer
+        .entitySet(edmEntitySet, entitySet,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, null, select))
+                    .build())
+                .select(select)
+                .build()));
     Assert.assertEquals("{"
-    //    + "\"@odata.context\":\"$metadata#ESCompComp(PropertyComp/PropertyComp)\","
+        + "\"@odata.context\":\"$metadata#ESCompComp(PropertyComp/PropertyComp)\","
         + "\"value\":["
         + "{\"PropertyComp\":{\"PropertyComp\":{\"PropertyInt16\":123,\"PropertyString\":\"String 1\"}}},"
         + "{\"PropertyComp\":{\"PropertyComp\":{\"PropertyInt16\":987,\"PropertyString\":\"String 2\"}}}]}",
@@ -477,17 +473,16 @@ public class ODataJsonSerializerTest {
     ExpandItem expandItem = mockExpandItem(edmEntitySet, "NavPropertyETAllPrimOne");
     Mockito.when(expandItem.getSelectOption()).thenReturn(select);
     final ExpandOption expand = mockExpandOption(Arrays.asList(expandItem));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entity(edmEntitySet, entity,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()
-                    .expand(expand)
-                    .build());
-    final String resultString = IOUtils.toString(result);
+    final String resultString = IOUtils.toString(serializer
+        .entity(edmEntitySet, entity,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, expand, select))
+                    .suffix(Suffix.ENTITY).build())
+                .expand(expand)
+                .build()));
     Assert.assertEquals("{"
-        // + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimOne(PropertyDate))/$entity\","
+        + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimOne(PropertyDate))/$entity\","
         + "\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\","
         + "\"NavPropertyETAllPrimOne\":{\"PropertyDate\":\"2012-12-03\"}}",
         resultString);
@@ -502,18 +497,17 @@ public class ODataJsonSerializerTest {
     Mockito.when(expandItemAll.isStar()).thenReturn(true);
     final ExpandOption expand = mockExpandOption(Arrays.asList(expandItem, expandItem, expandItemAll));
     final SelectOption select = mockSelectOption(Arrays.asList(mockSelectItem(edmEntitySet, "PropertySByte")));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entity(edmEntitySet, entity,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()
-                    .expand(expand)
-                    .select(select)
-                    .build());
-    final String resultString = IOUtils.toString(result);
+    final String resultString = IOUtils.toString(serializer
+        .entity(edmEntitySet, entity,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, expand, select))
+                    .suffix(Suffix.ENTITY).build())
+                .expand(expand)
+                .select(select)
+                .build()));
     Assert.assertEquals("{"
-        // + "\"@odata.context\":\"$metadata#ESAllPrim(PropertySByte)/$entity\","
+        + "\"@odata.context\":\"$metadata#ESAllPrim(PropertySByte)/$entity\","
         + "\"PropertySByte\":127,"
         + "\"NavPropertyETTwoPrimOne\":{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"},"
         + "\"NavPropertyETTwoPrimMany\":[{\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\"}]}",
@@ -528,18 +522,17 @@ public class ODataJsonSerializerTest {
     Mockito.when(expandItemAll.isStar()).thenReturn(true);
     final ExpandOption expand = mockExpandOption(Arrays.asList(expandItemAll));
     final SelectOption select = mockSelectOption(Arrays.asList(mockSelectItem(edmEntitySet, "PropertyTimeOfDay")));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entity(edmEntitySet, entity,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()
-                    .expand(expand)
-                    .select(select)
-                    .build());
-    final String resultString = IOUtils.toString(result);
+    final String resultString = IOUtils.toString(serializer
+        .entity(edmEntitySet, entity,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, expand, select))
+                    .suffix(Suffix.ENTITY).build())
+                .expand(expand)
+                .select(select)
+                .build()));
     Assert.assertEquals("{"
-        // + "\"@odata.context\":\"$metadata#ESAllPrim(PropertyTimeOfDay)/$entity\","
+        + "\"@odata.context\":\"$metadata#ESAllPrim(PropertyTimeOfDay)/$entity\","
         + "\"PropertyTimeOfDay\":\"23:49:14\","
         + "\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]}",
         resultString);
@@ -559,17 +552,16 @@ public class ODataJsonSerializerTest {
         mockSelectItem(innerEntitySet, "PropertyInt32")));
     Mockito.when(expandItemFirst.getSelectOption()).thenReturn(select);
     final ExpandOption expand = mockExpandOption(Arrays.asList(expandItemFirst));
-    InputStream result =
-        new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA) // serializer
-            .entity(edmEntitySet, entity,
-                ODataSerializerOptions.with()
-                    .contextURL(null)
-                    // ContextURL.Builder.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()
-                    .expand(expand)
-                    .build());
-    final String resultString = IOUtils.toString(result);
+    final String resultString = IOUtils.toString(serializer
+        .entity(edmEntitySet, entity,
+            ODataSerializerOptions.with()
+                .contextURL(ContextURL.with().entitySet(edmEntitySet)
+                    .selectList(serializer.buildContextURLSelectList(edmEntitySet, expand, select))
+                    .suffix(Suffix.ENTITY).build())
+                .expand(expand)
+                .build()));
     Assert.assertEquals("{"
-        // + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimMany(PropertyInt32))/$entity\","
+        + "\"@odata.context\":\"$metadata#ESTwoPrim(NavPropertyETAllPrimMany(PropertyInt32))/$entity\","
         + "\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\","
         + "\"NavPropertyETAllPrimMany\":["
         + "{\"PropertyInt32\":-2147483648,\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]},"
@@ -581,7 +573,7 @@ public class ODataJsonSerializerTest {
         resultString);
   }
 
-  private UriInfoResource mockResource(final EdmEntitySet edmEntitySet, final String... names) {
+  private static UriInfoResource mockResource(final EdmEntitySet edmEntitySet, final String... names) {
     EdmStructuredType type = edmEntitySet.getEntityType();
     List<UriResource> elements = new ArrayList<UriResource>();
     for (final String name : Arrays.asList(names)) {
@@ -603,27 +595,27 @@ public class ODataJsonSerializerTest {
     return resource;
   }
 
-  private SelectItem mockSelectItem(final EdmEntitySet edmEntitySet, final String... names) {
+  public static SelectItem mockSelectItem(final EdmEntitySet edmEntitySet, final String... names) {
     final UriInfoResource resource = mockResource(edmEntitySet, names);
     SelectItem selectItem = Mockito.mock(SelectItem.class);
     Mockito.when(selectItem.getResourcePath()).thenReturn(resource);
     return selectItem;
   }
 
-  private SelectOption mockSelectOption(final List<SelectItem> selectItems) {
+  public static SelectOption mockSelectOption(final List<SelectItem> selectItems) {
     SelectOption select = Mockito.mock(SelectOption.class);
     Mockito.when(select.getSelectItems()).thenReturn(selectItems);
     return select;
   }
 
-  private ExpandItem mockExpandItem(final EdmEntitySet edmEntitySet, final String... names) {
+  public static ExpandItem mockExpandItem(final EdmEntitySet edmEntitySet, final String... names) {
     final UriInfoResource resource = mockResource(edmEntitySet, names);
     ExpandItem expandItem = Mockito.mock(ExpandItem.class);
     Mockito.when(expandItem.getResourcePath()).thenReturn(resource);
     return expandItem;
   }
 
-  private ExpandOption mockExpandOption(final List<ExpandItem> expandItems) {
+  public static ExpandOption mockExpandOption(final List<ExpandItem> expandItems) {
     ExpandOption expand = Mockito.mock(ExpandOption.class);
     Mockito.when(expand.getExpandItems()).thenReturn(expandItems);
     return expand;

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/a1d9f747/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelperTest.java
----------------------------------------------------------------------
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelperTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelperTest.java
new file mode 100644
index 0000000..96f8817
--- /dev/null
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/utils/ContextURLHelperTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.server.api.OData;
+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.SelectItem;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
+import org.apache.olingo.server.core.serializer.json.ODataJsonSerializerTest;
+import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class ContextURLHelperTest {
+
+  private static final Edm edm = OData.newInstance().createEdm(new EdmTechProvider());
+  private static final EdmEntityContainer entityContainer = edm.getEntityContainer(
+      new FullQualifiedName("olingo.odata.test1", "Container"));
+
+  @Test
+  public void buildSelect() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESAllPrim");
+    final SelectItem selectItem1 = ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyString");
+    final SelectItem selectItem2 = ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyInt16");
+    final SelectOption select = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(
+        selectItem1, selectItem2, selectItem2));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), null, select)).build();
+    assertEquals("$metadata#ESAllPrim(PropertyInt16,PropertyString)",
+        ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildSelectAll() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESAllPrim");
+    final SelectItem selectItem1 = ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyGuid");
+    SelectItem selectItem2 = Mockito.mock(SelectItem.class);
+    Mockito.when(selectItem2.isStar()).thenReturn(true);
+    final SelectOption select = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(selectItem1, selectItem2));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), null, select)).build();
+    assertEquals("$metadata#ESAllPrim(*)", ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildSelectComplex() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESCompMixPrimCollComp");
+    final SelectOption select = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(
+        ODataJsonSerializerTest.mockSelectItem(entitySet,
+            "PropertyMixedPrimCollComp", "PropertyComp", "PropertyString"),
+        ODataJsonSerializerTest.mockSelectItem(entitySet,
+            "PropertyMixedPrimCollComp", "PropertyComp", "PropertyInt16"),
+        ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyMixedPrimCollComp", "CollPropertyString"),
+        ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyMixedPrimCollComp", "CollPropertyComp"),
+        ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyInt16")));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), null, select)).build();
+    assertEquals("$metadata#ESCompMixPrimCollComp("
+        + "PropertyInt16,"
+        + "PropertyMixedPrimCollComp/CollPropertyString,"
+        + "PropertyMixedPrimCollComp/PropertyComp/PropertyInt16,"
+        + "PropertyMixedPrimCollComp/PropertyComp/PropertyString,"
+        + "PropertyMixedPrimCollComp/CollPropertyComp)",
+        ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildExpandAll() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    ExpandItem expandItem = Mockito.mock(ExpandItem.class);
+    Mockito.when(expandItem.isStar()).thenReturn(true);
+    final ExpandOption expand = ODataJsonSerializerTest.mockExpandOption(Arrays.asList(expandItem));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), expand, null)).build();
+    assertEquals("$metadata#ESTwoPrim", ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildExpandNoSelect() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    final ExpandOption expand = ODataJsonSerializerTest.mockExpandOption(Arrays.asList(
+        ODataJsonSerializerTest.mockExpandItem(entitySet, "NavPropertyETAllPrimOne")));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), expand, null)).build();
+    assertEquals("$metadata#ESTwoPrim", ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildExpandSelect() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    final ExpandItem expandItem1 = ODataJsonSerializerTest.mockExpandItem(entitySet, "NavPropertyETAllPrimOne");
+    final EdmEntitySet innerEntitySet = entityContainer.getEntitySet("ESAllPrim");
+    ExpandItem expandItem2 = ODataJsonSerializerTest.mockExpandItem(entitySet, "NavPropertyETAllPrimMany");
+    final SelectOption innerSelect = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(
+        ODataJsonSerializerTest.mockSelectItem(innerEntitySet, "PropertyInt32")));
+    Mockito.when(expandItem2.getSelectOption()).thenReturn(innerSelect);
+    final ExpandOption expand = ODataJsonSerializerTest.mockExpandOption(Arrays.asList(
+        expandItem1, expandItem2));
+    final SelectItem selectItem = ODataJsonSerializerTest.mockSelectItem(entitySet, "PropertyString");
+    final SelectOption select = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(selectItem));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), expand, select)).build();
+    assertEquals("$metadata#ESTwoPrim(PropertyString,NavPropertyETAllPrimMany(PropertyInt32))",
+        ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+
+  @Test
+  public void buildExpandSelectTwoLevels() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    final EdmEntitySet innerEntitySet = entityContainer.getEntitySet("ESAllPrim");
+    ExpandItem expandItemInner = ODataJsonSerializerTest.mockExpandItem(innerEntitySet, "NavPropertyETTwoPrimOne");
+    SelectItem innerSelectItem = Mockito.mock(SelectItem.class);
+    Mockito.when(innerSelectItem.isStar()).thenReturn(true);
+    final SelectOption innerSelect = ODataJsonSerializerTest.mockSelectOption(Arrays.asList(innerSelectItem));
+    Mockito.when(expandItemInner.getSelectOption()).thenReturn(innerSelect);
+    final ExpandOption innerExpand = ODataJsonSerializerTest.mockExpandOption(Arrays.asList(expandItemInner));
+    ExpandItem expandItem = ODataJsonSerializerTest.mockExpandItem(entitySet, "NavPropertyETAllPrimOne");
+    Mockito.when(expandItem.getExpandOption()).thenReturn(innerExpand);
+    final ExpandOption expand = ODataJsonSerializerTest.mockExpandOption(Arrays.asList(expandItem));
+    final ContextURL contextURL = ContextURL.with().entitySet(entitySet)
+        .selectList(ContextURLHelper.buildSelectList(entitySet.getEntityType(), expand, null)).build();
+    assertEquals("$metadata#ESTwoPrim(NavPropertyETAllPrimOne(NavPropertyETTwoPrimOne(*)))",
+        ContextURLBuilder.create(contextURL).toASCIIString());
+  }
+}