@Override
  protected Iterable<DataNodeWithPath> queryWithPath(
      SchemaNode schemaNode,
      LocationPathExpression queryPath,
      boolean expandTrailingList,
      boolean includeEmptyContainers)
      throws BigDBException {
    // The path must always contain the step for this node, so it's an
    // error if it's called with an empty path.
    if (queryPath.size() == 0) {
      throw new BigDBException("Query path argument cannot be empty");
    }

    // We're at a leaf so we should be at the end of the query path
    if (queryPath.size() > 1) {
      throw new BigDBException("Invalid path argument: " + queryPath);
    }

    Step step = queryPath.getStep(0);
    if (!step.getPredicates().isEmpty()) {
      throw new BigDBException("Leaf nodes cannot have predicates; path: " + queryPath);
    }

    DataNodeWithPath dataNodeWithPath = new DataNodeWithPathImpl(queryPath, this);
    return Collections.<DataNodeWithPath>singletonList(dataNodeWithPath);
  }
  @Override
  protected Iterable<DataNodeWithPath> queryWithPath(
      SchemaNode schemaNode,
      LocationPathExpression queryPath,
      boolean expandTrailingList,
      boolean includeEmptyContainers)
      throws BigDBException {
    // The path must always contain the step for this node, so it's an
    // error if it's called with an empty path.
    if (queryPath.size() == 0) {
      throw new BigDBException("Query path argument cannot be empty");
    }

    return Collections.<DataNodeWithPath>emptyList();
  }
  @Override
  protected Iterable<DataNodeWithPath> queryWithPath(
      SchemaNode schemaNode,
      LocationPathExpression queryPath,
      boolean expandTrailingList,
      boolean includeEmptyContainers)
      throws BigDBException {

    // The path must always contain the step for this node, so it's an
    // error if it's called with an empty path.
    if (queryPath.size() == 0) {
      throw new BigDBException("Query path argument cannot be empty");
    }

    ListSchemaNode listSchemaNode = (ListSchemaNode) schemaNode;
    SchemaNode listElementSchemaNode = listSchemaNode.getListElementSchemaNode();
    LocationPathExpression remainingQueryPath = queryPath.subpath(1);

    Step listStep = queryPath.getStep(0);
    String listName = listStep.getName();

    List<DataNodeWithPath> result = new ArrayList<DataNodeWithPath>();

    if (expandTrailingList || (queryPath.size() > 1)) {
      // Currently we only support querying the entire unkeyed list or else
      // a single index list element, so check to see if there's a
      // non-negative integer index predicate.
      int index = listStep.getIndexPredicate();
      Iterable<DataNode> listElementDataNodes;
      boolean singleListElement = (index >= 0);
      if (singleListElement) {
        DataNode elementDataNode = getChild(index);
        listElementDataNodes = Collections.singletonList(elementDataNode);
      } else {
        listElementDataNodes = this;
        index = 0;
      }

      // Iterate over the range of list elements set above
      for (DataNode listElementDataNode : listElementDataNodes) {
        Step listElementStep = DataNodeUtilities.getListElementStep(listName, index++);
        if (singleListElement
            || matchesPredicates(
                listElementSchemaNode, listElementDataNode, listElementStep, listStep)) {
          LocationPathExpression listElementPath = LocationPathExpression.ofStep(listElementStep);
          if (queryPath.size() > 1) {
            // There are more steps in the query path, so we need to
            // call
            // query recursively
            LocationPathExpression listElementQueryPath =
                LocationPathExpression.ofPaths(listElementPath, remainingQueryPath);
            Iterable<DataNodeWithPath> listElementResult =
                listElementDataNode.queryWithPath(
                    listElementSchemaNode, listElementQueryPath, expandTrailingList);
            // Add the results to the results for the overall list
            for (DataNodeWithPath dataNodeWithPath : listElementResult) {
              result.add(dataNodeWithPath);
            }
          } else {
            // No more query path to evaluate, so just make a
            // DataNodeWithPath
            // for the list element data node.
            DataNodeWithPath dataNodeWithPath =
                new DataNodeWithPathImpl(listElementPath, listElementDataNode);
            result.add(dataNodeWithPath);
          }
        }
      }
    } else {
      // Return the list node itself rather than expanding to the
      // matching list elements.
      DataNodeWithPath dataNodeWithPath =
          new DataNodeWithPathImpl(LocationPathExpression.ofStep(Step.of(listName)), this);
      result.add(dataNodeWithPath);
    }
    return result;
  }