@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);
  }
  @BigDBQuery
  public static Object getDeviceOracle(@BigDBParam("query") Query query) throws BigDBException {
    String entityClassName = null;

    assert query.getSteps() != null;
    assert query.getSteps().size() == 1;
    assert query.getStep(0).getName().equals(CURRENT_STEP_NAME);

    Step currentStep = query.getStep(0);

    final String id = currentStep.getExactMatchPredicateString(ID_FIELD_NAME);
    String macStr = currentStep.getExactMatchPredicateString(MAC_FIELD_NAME);
    Long vlanObj = (Long) currentStep.getExactMatchPredicateValue(VLAN_FIELD_NAME);
    entityClassName = currentStep.getExactMatchPredicateString(ENTITY_CLASS_FIELD_NAME);
    String dpidStr = currentStep.getExactMatchPredicateString(SWITCH_DPID_FIELD_NAME);
    Short portShort = (Short) currentStep.getExactMatchPredicateValue(SWITCH_PORT_FIELD_NAME);
    String ipStr = currentStep.getExactMatchPredicateString(IP_ADDRESS_FIELD_NAME);

    if (id != null && (macStr != null || vlanObj != null || entityClassName != null)) {
      throw new BigDBException(
          "This query only accepts a device id or "
              + "a combination of mac, vlan and entity-class-name, "
              + "but not both.");
    }
    if (id != null) {
      // convert from device-id to individual components
      try {
        IndexedEntity.EntityWithClassName e = IndexedEntity.getNamedEntityFromKeyString(id);
        DeviceOracle d =
            new DeviceOracle(
                id,
                e.getEntity().getMacAddress(),
                e.getEntity().getVlan(),
                e.getEntityClassName(),
                e.getEntity().getIpv4Address(),
                e.getEntity().getSwitchDPID(),
                e.getEntity().getSwitchPort());
        ArrayList<DeviceOracle> dd = new ArrayList<DeviceOracle>();
        dd.add(d);
        return dd;
      } catch (Exception e) {
        throw new BigDBException("Invalid device id " + id);
      }
    } else if (macStr != null && entityClassName != null) {
      DeviceOracle d =
          createDeviceOracle(macStr, vlanObj, entityClassName, ipStr, dpidStr, portShort);

      ArrayList<DeviceOracle> dd = new ArrayList<DeviceOracle>();
      dd.add(d);
      return dd;
    } else {
      throw new BigDBException(
          "This query requires a device id or a "
              + "combination of mac, vlan and entity-class-name");
    }
  }
 @Override
 public DataNode getChild(String name) throws BigDBException {
   return getChild(Step.of(name));
 }
  @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;
  }