/**
   * Returns a list of resources which contains no linked entries.
   *
   * <p>Links on the same resource entry are deleted from the list of resources. This method has to
   * be used after calling the method CmsObject.getResourcesInTimeRange(String, long, long);
   *
   * @param resources the list of resources which may contain links
   * @return a filtered list of resources
   */
  public static List<CmsResource> filterLinkedResources(List<CmsResource> resources) {

    List<CmsResource> filteredResources = new ArrayList<CmsResource>();
    Set<CmsUUID> addedResources = new HashSet<CmsUUID>();
    long currentTime = System.currentTimeMillis();
    Iterator<CmsResource> i = resources.iterator();

    while (i.hasNext()) {

      CmsResource currentResource = i.next();

      // filter those documents that are folders or outside the release and expire window
      if (currentResource.isFolder()
          || (currentResource.getDateReleased() > currentTime)
          || (currentResource.getDateExpired() < currentTime)) {
        // skip folders and resources outside time range
        continue;
      }

      if (CmsDocumentFactory.isIgnoredDocument(currentResource.getRootPath(), true)) {
        // this resource is ignored, skip it before checking the resource id
        continue;
      }

      CmsUUID resId = currentResource.getResourceId();

      if (!addedResources.contains(resId)) {

        // add resource to return list and ID to set
        addedResources.add(resId);
        filteredResources.add(currentResource);
      }
    }

    // clear objects to release memory
    i = null;
    addedResources = null;
    resources = null;

    return filteredResources;
  }
  /**
   * Creates a list of new resources of the specified folder and filters the unwanted resources.
   *
   * <p>If the parameter categoryFolders is an empty list, all new resources are returned, otherwise
   * only those resources which are in a subfolder specified by the list.
   *
   * <p>
   *
   * @param cms the CmsObject
   * @param startFolder the root folder
   * @param startDate the start date in milliseconds
   * @param endDate the end date in milliseconds
   * @param selectedCategories list with selected categories/folders
   * @param openedCategories list with opened categories/folders
   * @return list of new resources
   */
  private static List<CmsResource> getNewResourceList(
      CmsObject cms,
      String startFolder,
      long startDate,
      long endDate,
      List<String> selectedCategories,
      List<String> openedCategories) {

    List<CmsResource> searchResult = null;
    List<CmsResource> foundResources = null;
    Set<CmsUUID> addedResources = null;

    if (LOG.isDebugEnabled()) {

      StringBuffer buf = new StringBuffer();
      for (int i = 0, n = selectedCategories.size(); i < n; i++) {
        buf.append(selectedCategories.get(i));

        if (i < (n - 1)) {
          buf.append(", ");
        }
      }

      LOG.debug("################ INPUT VALUES FOR NEW DOCUMENTS SEARCH");
      LOG.debug("startDate : " + startDate + " " + new java.util.Date(startDate).toString());
      LOG.debug("endDate : " + endDate + " " + new java.util.Date(endDate).toString());
      LOG.debug("startFolder : " + startFolder);
      LOG.debug("categories : " + buf.toString());
    }

    try {

      // get all resources in the site root which are in the time range
      CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION;
      filter = filter.addRequireLastModifiedAfter(startDate);
      filter = filter.addRequireLastModifiedBefore(endDate);
      foundResources = cms.readResources(startFolder, filter);
    } catch (CmsException e) {

      if (LOG.isErrorEnabled()) {
        LOG.error(
            "Error reading resources in time range "
                + new java.util.Date(startDate).toString()
                + " - "
                + new java.util.Date(endDate).toString()
                + " below folder "
                + startFolder,
            e);
      }
      foundResources = Collections.emptyList();
    }

    if (selectedCategories.size() == 0) {

      // return all found resources with filtered links
      searchResult = filterLinkedResources(foundResources);
    } else {

      addedResources = new HashSet<CmsUUID>();
      searchResult = new ArrayList<CmsResource>();
      long currentTime = System.currentTimeMillis();

      for (int i = 0, n = foundResources.size(); i < n; i++) {

        // analyze each resource if it has to be included in the search result

        CmsResource resource = foundResources.get(i);

        // filter those documents that are folders or outside the release and expire window
        if (resource.isFolder()
            || (resource.getDateReleased() > currentTime)
            || (resource.getDateExpired() < currentTime)) {
          // skip folders and resources outside time range
          continue;
        }

        String resourceName = cms.getRequestContext().removeSiteRoot(resource.getRootPath());
        String parentFolder = CmsResource.getParentFolder(resourceName);
        boolean addToResult = false;

        if (!selectedCategories.contains(parentFolder) && openedCategories.contains(parentFolder)) {

          // skip resources that are inside an opened, but un-selected category/folder
          continue;
        }

        // check if the parent folder of the resource is one of the selected categories/folders
        addToResult = selectedCategories.contains(parentFolder);

        if (!addToResult) {

          // check if the resource is inside a collapsed sub-tree
          // of a selected category

          int openedCategoryCount = 0;

          while (!"/".equals(parentFolder)) {

            if (openedCategories.contains(parentFolder)) {
              openedCategoryCount++;
            }

            if (selectedCategories.contains(parentFolder) && (openedCategoryCount == 0)) {

              // we found a selected parent category,
              // and it's sub-tree is collapsed
              addToResult = true;
              break;
            }

            parentFolder = CmsResource.getParentFolder(parentFolder);
          }
        }

        if (!addToResult) {

          // continue with the next resource
          continue;
        }

        if (CmsDocumentFactory.isIgnoredDocument(resourceName, true)) {
          // this resource is ignored, skip it before checking the resource id
          continue;
        }

        // check if the resource is a sibling that has already been added to the search result
        CmsUUID resourceId = resource.getResourceId();
        if (!addedResources.contains(resourceId)) {

          // add resource to the result
          addedResources.add(resourceId);
          searchResult.add(resource);
        }
      }
    }

    return searchResult;
  }