/**
   * Method to generate a new query using the passed query as basis.
   *
   * @param language The query language
   * @param ec ExecutionContext
   * @param query The query filter (String) or a previous Query
   * @return The Query
   */
  public Query newQuery(String language, ExecutionContext ec, Object query) {
    if (language == null) {
      return null;
    }

    String languageImpl = language;

    // Find the query support for this language and this datastore
    if (query == null) {
      // TODO We don't have candidate so don't know the StoreManager to use
      throw new NucleusException("Not yet supported for queries with unknown candidate");
    }

    if (query instanceof String) {
      // Single-string query
      String queryString = (String) query;
      String candidateName = null;
      if (languageImpl.equalsIgnoreCase("JDOQL")
          && queryString.toUpperCase().indexOf(" FROM ") > 0) {
        int candidateStart = queryString.toUpperCase().indexOf(" FROM ") + 6;
        int candidateEnd = queryString.indexOf(" ", candidateStart + 1);
        candidateName = queryString.substring(candidateStart, candidateEnd);
      }

      if (candidateName != null) {
        ClassLoaderResolver clr = nucleusCtx.getClassLoaderResolver(null);
        AbstractClassMetaData cmd =
            nucleusCtx.getMetaDataManager().getMetaDataForClass(candidateName, clr);
        StoreManager classStoreMgr =
            ((FederatedStoreManager) storeMgr).getStoreManagerForClass(cmd);
        return classStoreMgr.getQueryManager().newQuery(languageImpl, ec, query);
      }
      // TODO Extract the candidate for this query
      // TODO Find StoreManager for the candidate
      throw new NucleusException("Not yet supported for single-string queries");
    } else if (query instanceof Query) {
      // Based on previous query
      StoreManager storeMgr = ((Query) query).getStoreManager();
      return storeMgr.getQueryManager().newQuery(languageImpl, ec, query);
    } else {
      if (query instanceof Class) {
        // Find StoreManager for the candidate
        Class cls = (Class) query;
        ClassLoaderResolver clr = nucleusCtx.getClassLoaderResolver(cls.getClassLoader());
        AbstractClassMetaData cmd = nucleusCtx.getMetaDataManager().getMetaDataForClass(cls, clr);
        StoreManager classStoreMgr =
            ((FederatedStoreManager) storeMgr).getStoreManagerForClass(cmd);
        return classStoreMgr.getQueryManager().newQuery(languageImpl, ec, query);
      }
      throw new NucleusException(
          "Not yet supported for queries taking in object of type " + query.getClass());
    }
  }
  /**
   * Convenience method to get all objects of the specified type.
   *
   * @param ec Execution Context
   * @param mconn Managed Connection
   * @param cmd Metadata for the type to return
   * @param ignoreCache Whether to ignore the cache
   * @param fp Fetch Plan
   * @param filter Optional filter for the candidates
   * @param storeMgr StoreManager in use
   * @return List of objects of the candidate type
   */
  private static List getObjectsOfType(
      final ExecutionContext ec,
      final HBaseManagedConnection mconn,
      final AbstractClassMetaData cmd,
      boolean ignoreCache,
      FetchPlan fp,
      final Filter filter,
      final StoreManager storeMgr) {
    List results = new ArrayList();

    if (!storeMgr.managesClass(cmd.getFullClassName())) {
      storeMgr.manageClasses(ec.getClassLoaderResolver(), cmd.getFullClassName());
    }
    final Table table = storeMgr.getStoreDataForClass(cmd.getFullClassName()).getTable();
    final String tableName = table.getName();
    final int[] fpMembers = fp.getFetchPlanForClass(cmd).getMemberNumbers();
    try {
      final ClassLoaderResolver clr = ec.getClassLoaderResolver();

      Iterator<Result> it =
          (Iterator<Result>)
              AccessController.doPrivileged(
                  new PrivilegedExceptionAction() {
                    public Object run() throws Exception {
                      Scan scan = new Scan();
                      if (filter != null) {
                        scan.setFilter(filter);
                      }

                      // Retrieve all fetch-plan fields
                      for (int i = 0; i < fpMembers.length; i++) {
                        AbstractMemberMetaData mmd =
                            cmd.getMetaDataForManagedMemberAtAbsolutePosition(fpMembers[i]);
                        RelationType relationType = mmd.getRelationType(clr);
                        if (relationType != RelationType.NONE
                            && MetaDataUtils.getInstance()
                                .isMemberEmbedded(
                                    ec.getMetaDataManager(), clr, mmd, relationType, null)) {
                          if (RelationType.isRelationSingleValued(relationType)) {
                            // 1-1 embedded
                            List<AbstractMemberMetaData> embMmds =
                                new ArrayList<AbstractMemberMetaData>();
                            embMmds.add(mmd);
                            addColumnsToScanForEmbeddedMember(scan, embMmds, table, ec);
                          }
                        } else {
                          Column col =
                              table
                                  .getMemberColumnMappingForMember(mmd)
                                  .getColumn(0); // TODO Support multicol mapping
                          byte[] familyName = HBaseUtils.getFamilyNameForColumn(col).getBytes();
                          byte[] qualifName = HBaseUtils.getQualifierNameForColumn(col).getBytes();
                          scan.addColumn(familyName, qualifName);
                        }
                      }

                      VersionMetaData vermd = cmd.getVersionMetaDataForClass();
                      if (cmd.isVersioned() && vermd.getFieldName() == null) {
                        // Add version column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getVersionColumn()).getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getVersionColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }
                      if (cmd.hasDiscriminatorStrategy()) {
                        // Add discriminator column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getDiscriminatorColumn())
                                .getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getDiscriminatorColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }
                      if (cmd.getIdentityType() == IdentityType.DATASTORE) {
                        // Add datastore identity column
                        byte[] familyName =
                            HBaseUtils.getFamilyNameForColumn(table.getDatastoreIdColumn())
                                .getBytes();
                        byte[] qualifName =
                            HBaseUtils.getQualifierNameForColumn(table.getDatastoreIdColumn())
                                .getBytes();
                        scan.addColumn(familyName, qualifName);
                      }

                      HTableInterface htable = mconn.getHTable(tableName);
                      ResultScanner scanner = htable.getScanner(scan);
                      if (ec.getStatistics() != null) {
                        // Add to statistics
                        ec.getStatistics().incrementNumReads();
                      }
                      Iterator<Result> it = scanner.iterator();
                      return it;
                    }
                  });

      // Instantiate the objects
      if (cmd.getIdentityType() == IdentityType.APPLICATION) {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingApplicationIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      } else if (cmd.getIdentityType() == IdentityType.DATASTORE) {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingDatastoreIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      } else {
        while (it.hasNext()) {
          final Result result = it.next();
          Object obj =
              getObjectUsingNondurableIdForResult(
                  result, cmd, ec, ignoreCache, fpMembers, tableName, storeMgr, table);
          if (obj != null) {
            results.add(obj);
          }
        }
      }
    } catch (PrivilegedActionException e) {
      throw new NucleusDataStoreException(e.getMessage(), e.getCause());
    }
    return results;
  }