/**
   * Construct a new MockCrawlableDataset and add it as a child of this MockCrawlableDataset. (If a
   * child with the same name already exists, it will be replaced.)
   *
   * @param childName the name of the new child.
   * @param isCollection whether the new child is a collection
   * @return the new child MockCrawlableDataset
   */
  public MockCrawlableDataset addChild(String childName, boolean isCollection) {
    String[] pathSegments = CrawlableDatasetUtils.getPathSegments(childName);
    if (!CrawlableDatasetUtils.isValidRelativePath(pathSegments) || pathSegments.length > 1)
      throw new IllegalArgumentException(String.format("Child name [%s] is not valid.", childName));

    MockCrawlableDataset childCrDs =
        new MockCrawlableDataset(this.path + "/" + childName, isCollection);
    childCrDs.setParent(this);
    if (this.childrenMap == null) {
      this.childrenMap = new HashMap<String, CrawlableDataset>();
      this.childrenList = new ArrayList<CrawlableDataset>();
    }
    CrawlableDataset prevCrDs = this.childrenMap.put(childCrDs.getName(), childCrDs);
    if (prevCrDs != null) this.childrenList.remove(prevCrDs);
    this.childrenList.add(childCrDs);
    return childCrDs;
  }
  public CrawlableDataset getDescendant(String relativePath) {
    if (!this.isCollection) throw new IllegalStateException("Dataset not a collection.");
    String[] pathSegments = CrawlableDatasetUtils.getPathSegments(relativePath);
    if (!CrawlableDatasetUtils.isValidRelativePath(pathSegments))
      throw new IllegalArgumentException(String.format("Ill-formed relative path [%s]", path));

    boolean singleLevelPath = pathSegments.length == 1;

    CrawlableDataset curCrDs = this.childrenMap.get(pathSegments[0]);
    if (curCrDs != null) {
      if (singleLevelPath) return curCrDs;
      return curCrDs.getDescendant(CrawlableDatasetUtils.stepDownRelativePath(pathSegments));
    } else {
      curCrDs = new MockCrawlableDataset(this.getPath() + "/" + pathSegments[0], singleLevelPath);
      ((MockCrawlableDataset) curCrDs).setExists(false);
      if (singleLevelPath) return curCrDs;
      return curCrDs.getDescendant(CrawlableDatasetUtils.stepDownRelativePath(pathSegments));
    }
  }