private List<Object> explainInputs(List<RelNode> inputs) {
   final List<Object> list = jsonBuilder.list();
   for (RelNode input : inputs) {
     String id = relIdMap.get(input);
     if (id == null) {
       input.explain(this);
       id = previousId;
     }
     list.add(id);
   }
   return list;
 }
  protected void explain_(RelNode rel, List<Pair<String, Object>> values) {
    final Map<String, Object> map = jsonBuilder.map();

    map.put("id", null); // ensure that id is the first attribute
    map.put("relOp", relJson.classToTypeName(rel.getClass()));
    for (Pair<String, Object> value : values) {
      if (value.right instanceof RelNode) {
        continue;
      }
      put(map, value.left, value.right);
    }
    // omit 'inputs: ["3"]' if "3" is the preceding rel
    final List<Object> list = explainInputs(rel.getInputs());
    if (list.size() != 1 || !list.get(0).equals(previousId)) {
      map.put("inputs", list);
    }

    final String id = Integer.toString(relIdMap.size());
    relIdMap.put(rel, id);
    map.put("id", id);

    relList.add(map);
    previousId = id;
  }
 public RelJsonWriter() {
   jsonBuilder = new JsonBuilder();
   relList = jsonBuilder.list();
   relJson = new RelJson(jsonBuilder);
 }
 /** Returns a JSON string describing the relational expressions that were just explained. */
 public String asString() {
   final Map<String, Object> map = jsonBuilder.map();
   map.put("rels", relList);
   return jsonBuilder.toJsonString(map);
 }