private Iterable<Integer> queryDocuments(BSONObject query) throws MongoServerException {
    synchronized (indexes) {
      for (Index index : indexes) {
        if (index.canHandle(query)) {
          return matchDocuments(query, index.getPositions(query));
        }
      }
    }

    return matchDocuments(query);
  }
  public synchronized void addDocument(BSONObject document) throws MongoServerException {

    Integer pos = emptyPositions.poll();
    if (pos == null) pos = Integer.valueOf(documents.size());

    for (Index index : indexes) {
      index.checkAdd(document);
    }
    for (Index index : indexes) {
      index.add(document, pos);
    }
    dataSize.addAndGet(Utils.calculateSize(document));
    if (pos == documents.size()) {
      documents.add(document);
    } else {
      documents.set(pos.intValue(), document);
    }
  }
  public BSONObject validate() {
    BSONObject response = new BasicBSONObject("ns", getFullName());
    response.put("extentCount", Integer.valueOf(0));
    response.put("datasize", Long.valueOf(dataSize.get()));
    response.put("nrecords", Integer.valueOf(documents.size()));
    response.put("padding", Integer.valueOf(1));
    response.put("deletedCount", Integer.valueOf(emptyPositions.size()));
    response.put("deletedSize", Integer.valueOf(0));

    response.put("nIndexes", Integer.valueOf(indexes.size()));
    BSONObject keysPerIndex = new BasicBSONObject();
    for (Index index : indexes) {
      keysPerIndex.put(index.getName(), Long.valueOf(index.getCount()));
    }

    response.put("keysPerIndex", keysPerIndex);
    response.put("valid", Boolean.TRUE);
    response.put("errors", Arrays.asList());
    Utils.markOkay(response);
    return response;
  }
  public synchronized void removeDocument(BSONObject document) throws MongoServerException {
    Integer pos = null;

    if (!indexes.isEmpty()) {
      for (Index index : indexes) {
        pos = index.remove(document);
      }
    } else {
      int idx = documents.indexOf(document);
      if (idx >= 0) {
        pos = Integer.valueOf(idx);
      }
    }
    if (pos == null) {
      // not found
      return;
    }
    dataSize.addAndGet(-Utils.calculateSize(document));
    documents.set(pos.intValue(), null);
    emptyPositions.add(pos);
  }
  private BSONObject updateDocument(BSONObject document, BSONObject updateQuery, Integer matchPos)
      throws MongoServerException {
    synchronized (document) {
      // copy document
      BSONObject oldDocument = new BasicBSONObject();
      cloneInto(oldDocument, document);

      BSONObject newDocument = calculateUpdateDocument(document, updateQuery, matchPos, false);

      if (!newDocument.equals(oldDocument)) {
        for (Index index : indexes) {
          index.checkUpdate(oldDocument, newDocument);
        }
        for (Index index : indexes) {
          index.updateInPlace(oldDocument, newDocument);
        }

        long oldSize = Utils.calculateSize(oldDocument);
        long newSize = Utils.calculateSize(newDocument);
        dataSize.addAndGet(newSize - oldSize);

        // only keep fields that are also in the updated document
        Set<String> fields = new HashSet<String>(document.keySet());
        fields.removeAll(newDocument.keySet());
        for (String key : fields) {
          document.removeField(key);
        }

        // update the fields
        for (String key : newDocument.keySet()) {
          if (key.contains(".")) {
            throw new MongoServerException(
                "illegal field name. must not happen as it must be catched by the driver");
          }
          document.put(key, newDocument.get(key));
        }
      }
      return oldDocument;
    }
  }
  public BSONObject getStats() {
    BSONObject response = new BasicBSONObject("ns", getFullName());
    response.put("count", Integer.valueOf(documents.size()));
    response.put("size", Long.valueOf(dataSize.get()));

    double averageSize = 0;
    if (!documents.isEmpty()) {
      averageSize = dataSize.get() / (double) documents.size();
    }
    response.put("avgObjSize", Double.valueOf(averageSize));
    response.put("storageSize", Integer.valueOf(0));
    response.put("numExtents", Integer.valueOf(0));
    response.put("nindexes", Integer.valueOf(indexes.size()));
    BSONObject indexSizes = new BasicBSONObject();
    for (Index index : indexes) {
      indexSizes.put(index.getName(), Long.valueOf(index.getDataSize()));
    }

    response.put("indexSize", indexSizes);
    Utils.markOkay(response);
    return response;
  }