Convert JSON result into an immutable tree of ordinary Java objects (List, Map, 
String, Double, Integer, Boolean). We use sorted maps, so that the tree is 
deterministic.


Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/32fc5271
Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/32fc5271
Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/32fc5271

Branch: refs/heads/master
Commit: 32fc5271dc7e115ccde2d442aa97a1db28e20d59
Parents: 7f294ad
Author: Julian Hyde <[email protected]>
Authored: Fri Feb 15 19:44:05 2013 -0800
Committer: Julian Hyde <[email protected]>
Committed: Fri Feb 15 19:44:05 2013 -0800

----------------------------------------------------------------------
 .../org/apache/drill/optiq/EnumerableDrill.java    |   75 ++++++++++++--
 .../java/org/apache/drill/jdbc/test/JdbcTest.java  |   10 +-
 2 files changed, 69 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/32fc5271/sandbox/prototype/sqlparser/src/main/java/org/apache/drill/optiq/EnumerableDrill.java
----------------------------------------------------------------------
diff --git 
a/sandbox/prototype/sqlparser/src/main/java/org/apache/drill/optiq/EnumerableDrill.java
 
b/sandbox/prototype/sqlparser/src/main/java/org/apache/drill/optiq/EnumerableDrill.java
index c68bf85..15fe06d 100644
--- 
a/sandbox/prototype/sqlparser/src/main/java/org/apache/drill/optiq/EnumerableDrill.java
+++ 
b/sandbox/prototype/sqlparser/src/main/java/org/apache/drill/optiq/EnumerableDrill.java
@@ -19,6 +19,8 @@ package org.apache.drill.optiq;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import net.hydromatic.linq4j.AbstractEnumerable;
 import net.hydromatic.linq4j.Enumerable;
@@ -38,12 +40,14 @@ import java.util.concurrent.*;
  * Runtime helper that executes a Drill query and converts it into an
  * {@link Enumerable}.
  */
-public class EnumerableDrill<E extends JsonNode>
+public class EnumerableDrill<E>
     extends AbstractEnumerable<E>
     implements Enumerable<E> {
   private final LogicalPlan plan;
   final BlockingQueue queue = new ArrayBlockingQueue(100);
 
+  private static final ObjectMapper mapper = createMapper();
+
   /** Creates a DrillEnumerable.
    *
    * @param plan Logical plan
@@ -95,7 +99,7 @@ public class EnumerableDrill<E extends JsonNode>
     // TODO: use a completion service from the container
     final ExecutorCompletionService<Collection<RunOutcome>> service =
         new ExecutorCompletionService<Collection<RunOutcome>>(
-            new ThreadPoolExecutor( 1, 1, 1, TimeUnit.SECONDS,
+            new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
                 new LinkedBlockingDeque<Runnable>(10)));
 
     // Run the plan using an executor. It runs in a different thread, writing
@@ -105,7 +109,6 @@ public class EnumerableDrill<E extends JsonNode>
     final Future<Collection<RunOutcome>> task = runPlan(service);
 
     return new Enumerator<E>() {
-      final ObjectMapper mapper = new ObjectMapper();
       private E current;
 
       @Override
@@ -141,16 +144,66 @@ public class EnumerableDrill<E extends JsonNode>
       public void reset() {
         throw new UnsupportedOperationException();
       }
-
-      JsonNode parseJson(byte[] bytes) {
-        try {
-          return mapper.readTree(bytes);
-        } catch (IOException e) {
-          throw new RuntimeException(e);
-        }
-      }
     };
   }
+
+  private static ObjectMapper createMapper() {
+    return new ObjectMapper();
+  }
+
+  /** Converts a JSON document, represented as an array of bytes, into a Java
+   * object (consisting of Map, List, String, Integer, Double, Boolean). */
+  static Object parseJson(byte[] bytes) {
+    try {
+      return wrapper(mapper.readTree(bytes));
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  /** Converts a JSON node to Java objects ({@link List}, {@link Map},
+   * {@link String}, {@link Integer}, {@link Double}, {@link Boolean}. */
+  static Object wrapper(JsonNode node) {
+    switch (node.asToken()) {
+    case START_OBJECT:
+      return map((ObjectNode) node);
+    case START_ARRAY:
+      return array((ArrayNode) node);
+    case VALUE_STRING:
+      return node.asText();
+    case VALUE_NUMBER_INT:
+      return node.asInt();
+    case VALUE_NUMBER_FLOAT:
+      return node.asDouble();
+    case VALUE_TRUE:
+      return Boolean.TRUE;
+    case VALUE_FALSE:
+      return Boolean.FALSE;
+    case VALUE_NULL:
+      return null;
+    default:
+      throw new AssertionError("unexpected: " + node + ": " + node.asToken());
+    }
+  }
+
+  private static List array(ArrayNode node) {
+    final List<Object> list = new ArrayList<>();
+    for (JsonNode jsonNode : node) {
+      list.add(wrapper(jsonNode));
+    }
+    return Collections.unmodifiableList(list);
+  }
+
+  private static SortedMap<String, Object> map(ObjectNode node) {
+    // TreeMap makes the results deterministic.
+    final TreeMap<String, Object> map = new TreeMap<>();
+    final Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
+    while (fields.hasNext()) {
+      Map.Entry<String, JsonNode> next = fields.next();
+      map.put(next.getKey(), wrapper(next.getValue()));
+    }
+    return Collections.unmodifiableSortedMap(map);
+  }
 }
 
 // End EnumerableDrill.java

http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/32fc5271/sandbox/prototype/sqlparser/src/test/java/org/apache/drill/jdbc/test/JdbcTest.java
----------------------------------------------------------------------
diff --git 
a/sandbox/prototype/sqlparser/src/test/java/org/apache/drill/jdbc/test/JdbcTest.java
 
b/sandbox/prototype/sqlparser/src/test/java/org/apache/drill/jdbc/test/JdbcTest.java
index a6c3bf0..eb0b51a 100644
--- 
a/sandbox/prototype/sqlparser/src/test/java/org/apache/drill/jdbc/test/JdbcTest.java
+++ 
b/sandbox/prototype/sqlparser/src/test/java/org/apache/drill/jdbc/test/JdbcTest.java
@@ -56,11 +56,11 @@ public class JdbcTest extends TestCase {
     final ResultSet resultSet = statement.executeQuery(
         "select * from donuts");
     assertEquals(
-        
"_extra={\"donuts\":{\"type\":\"donut\",\"ppu\":0.55,\"topping\":[{\"type\":\"None\",\"id\":\"5001\"},{\"type\":\"Glazed\",\"id\":\"5002\"},{\"type\":\"Sugar\",\"id\":\"5005\"},{\"type\":\"Powdered
 Sugar\",\"id\":\"5007\"},{\"type\":\"Chocolate with 
Sprinkles\",\"id\":\"5006\"},{\"type\":\"Chocolate\",\"id\":\"5003\"},{\"type\":\"Maple\",\"id\":\"5004\"}],\"name\":\"Cake\",\"sales\":35,\"batters\":{\"batter\":[{\"type\":\"Regular\",\"id\":\"1001\"},{\"type\":\"Chocolate\",\"id\":\"1002\"},{\"type\":\"Blueberry\",\"id\":\"1003\"},{\"type\":\"Devil's
 Food\",\"id\":\"1004\"}]},\"id\":\"0001\"}}\n"
-        + 
"_extra={\"donuts\":{\"type\":\"donut\",\"ppu\":0.69,\"topping\":[{\"type\":\"None\",\"id\":\"5001\"},{\"type\":\"Glazed\",\"id\":\"5002\"},{\"type\":\"Sugar\",\"id\":\"5005\"},{\"type\":\"Chocolate\",\"id\":\"5003\"},{\"type\":\"Maple\",\"id\":\"5004\"}],\"name\":\"Raised\",\"sales\":145,\"batters\":{\"batter\":[{\"type\":\"Regular\",\"id\":\"1001\"}]},\"id\":\"0002\"}}\n"
-        + 
"_extra={\"donuts\":{\"type\":\"donut\",\"ppu\":0.55,\"topping\":[{\"type\":\"None\",\"id\":\"5001\"},{\"type\":\"Glazed\",\"id\":\"5002\"},{\"type\":\"Chocolate\",\"id\":\"5003\"},{\"type\":\"Maple\",\"id\":\"5004\"}],\"name\":\"Old
 
Fashioned\",\"sales\":300,\"batters\":{\"batter\":[{\"type\":\"Regular\",\"id\":\"1001\"},{\"type\":\"Chocolate\",\"id\":\"1002\"}]},\"id\":\"0003\"}}\n"
-        + 
"_extra={\"donuts\":{\"type\":\"donut\",\"ppu\":0.69,\"topping\":[{\"type\":\"None\",\"id\":\"5001\"},{\"type\":\"Glazed\",\"id\":\"5002\"},{\"type\":\"Sugar\",\"id\":\"5005\"},{\"type\":\"Powdered
 Sugar\",\"id\":\"5007\"},{\"type\":\"Chocolate with 
Sprinkles\",\"id\":\"5006\"},{\"type\":\"Chocolate\",\"id\":\"5003\"},{\"type\":\"Maple\",\"id\":\"5004\"}],\"name\":\"Filled\",\"sales\":14,\"batters\":{\"batter\":[{\"type\":\"Regular\",\"id\":\"1001\"},{\"type\":\"Chocolate\",\"id\":\"1002\"},{\"type\":\"Blueberry\",\"id\":\"1003\"},{\"type\":\"Devil's
 
Food\",\"id\":\"1004\"}]},\"id\":\"0004\",\"filling\":[{\"type\":\"None\",\"id\":\"6001\"},{\"type\":\"Raspberry\",\"id\":\"6002\"},{\"type\":\"Lemon\",\"id\":\"6003\"},{\"type\":\"Chocolate\",\"id\":\"6004\"},{\"type\":\"Kreme\",\"id\":\"6005\"}]}}\n"
-        + 
"_extra={\"donuts\":{\"type\":\"donut\",\"ppu\":1.0,\"topping\":[{\"type\":\"Glazed\",\"id\":\"5002\"}],\"name\":\"Apple
 
Fritter\",\"sales\":700,\"batters\":{\"batter\":[{\"type\":\"Regular\",\"id\":\"1001\"}]},\"id\":\"0005\"}}\n",
+        "_extra={donuts={batters={batter=[{id=1001, type=Regular}, {id=1002, 
type=Chocolate}, {id=1003, type=Blueberry}, {id=1004, type=Devil's Food}]}, 
id=0001, name=Cake, ppu=0.55, sales=35, topping=[{id=5001, type=None}, 
{id=5002, type=Glazed}, {id=5005, type=Sugar}, {id=5007, type=Powdered Sugar}, 
{id=5006, type=Chocolate with Sprinkles}, {id=5003, type=Chocolate}, {id=5004, 
type=Maple}], type=donut}}\n"
+        + "_extra={donuts={batters={batter=[{id=1001, type=Regular}]}, 
id=0002, name=Raised, ppu=0.69, sales=145, topping=[{id=5001, type=None}, 
{id=5002, type=Glazed}, {id=5005, type=Sugar}, {id=5003, type=Chocolate}, 
{id=5004, type=Maple}], type=donut}}\n"
+        + "_extra={donuts={batters={batter=[{id=1001, type=Regular}, {id=1002, 
type=Chocolate}]}, id=0003, name=Old Fashioned, ppu=0.55, sales=300, 
topping=[{id=5001, type=None}, {id=5002, type=Glazed}, {id=5003, 
type=Chocolate}, {id=5004, type=Maple}], type=donut}}\n"
+        + "_extra={donuts={batters={batter=[{id=1001, type=Regular}, {id=1002, 
type=Chocolate}, {id=1003, type=Blueberry}, {id=1004, type=Devil's Food}]}, 
filling=[{id=6001, type=None}, {id=6002, type=Raspberry}, {id=6003, 
type=Lemon}, {id=6004, type=Chocolate}, {id=6005, type=Kreme}], id=0004, 
name=Filled, ppu=0.69, sales=14, topping=[{id=5001, type=None}, {id=5002, 
type=Glazed}, {id=5005, type=Sugar}, {id=5007, type=Powdered Sugar}, {id=5006, 
type=Chocolate with Sprinkles}, {id=5003, type=Chocolate}, {id=5004, 
type=Maple}], type=donut}}\n"
+        + "_extra={donuts={batters={batter=[{id=1001, type=Regular}]}, 
id=0005, name=Apple Fritter, ppu=1.0, sales=700, topping=[{id=5002, 
type=Glazed}], type=donut}}\n",
         toString(resultSet));
     resultSet.close();
     statement.close();

Reply via email to