/**
   * Returns a list item created from the resource information, differs between valid resources and
   * invalid resources.
   *
   * <p>
   *
   * @param resource the resource to create the list item from
   * @param list the list
   * @param showPermissions if to show permissions
   * @param showDateLastMod if to show the last modification date
   * @param showUserLastMod if to show the last modification user
   * @param showDateCreate if to show the creation date
   * @param showUserCreate if to show the creation date
   * @param showDateRel if to show the date released
   * @param showDateExp if to show the date expired
   * @param showState if to show the state
   * @param showLockedBy if to show the lock user
   * @param showSite if to show the site
   * @return a list item created from the resource information
   */
  protected CmsListItem createResourceListItem(
      CmsResource resource,
      CmsHtmlList list,
      boolean showPermissions,
      boolean showDateLastMod,
      boolean showUserLastMod,
      boolean showDateCreate,
      boolean showUserCreate,
      boolean showDateRel,
      boolean showDateExp,
      boolean showState,
      boolean showLockedBy,
      boolean showSite) {

    CmsListItem item = list.newItem(resource.getStructureId().toString());
    // get an initialized resource utility
    CmsResourceUtil resUtil = getWp().getResourceUtil();
    resUtil.setResource(resource);
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_NAME, resUtil.getPath());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_ROOT_PATH, resUtil.getFullPath());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TITLE, resUtil.getTitle());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_TYPE, resUtil.getResourceTypeName());
    item.set(A_CmsListExplorerDialog.LIST_COLUMN_SIZE, resUtil.getSizeString());
    if (showPermissions) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_PERMISSIONS, resUtil.getPermissionString());
    }
    if (showDateLastMod) {
      item.set(
          A_CmsListExplorerDialog.LIST_COLUMN_DATELASTMOD,
          new Date(resource.getDateLastModified()));
    }
    if (showUserLastMod) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERLASTMOD, resUtil.getUserLastModified());
    }
    if (showDateCreate) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATECREATE, new Date(resource.getDateCreated()));
    }
    if (showUserCreate) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_USERCREATE, resUtil.getUserCreated());
    }
    if (showDateRel) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEREL, new Date(resource.getDateReleased()));
    }
    if (showDateExp) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_DATEEXP, new Date(resource.getDateExpired()));
    }
    if (showState) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_STATE, resUtil.getStateName());
    }
    if (showLockedBy) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_LOCKEDBY, resUtil.getLockedByName());
    }
    if (showSite) {
      item.set(A_CmsListExplorerDialog.LIST_COLUMN_SITE, resUtil.getSiteTitle());
    }
    setAdditionalColumns(item, resUtil);
    return item;
  }
  /**
   * 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;
  }
  /**
   * Calculates the date to use for comparison of this resource based on the given date identifiers.
   *
   * <p>
   *
   * @param cms the current OpenCms user context
   * @param resource the resource to create the key for
   * @param dateIdentifiers the date identifiers to use for selecting the date
   * @param defaultValue the default value to use in case no value can be calculated
   * @return the calculated date
   * @see CmsDateResourceComparator for a description about how the date identifieres are used
   */
  public static long calculateDate(
      CmsObject cms, CmsResource resource, List<String> dateIdentifiers, long defaultValue) {

    long result = 0;
    List<CmsProperty> properties = null;
    for (int i = 0, size = dateIdentifiers.size(); i < size; i++) {
      // check all configured comparisons
      String date = dateIdentifiers.get(i);
      int pos = DATE_ATTRIBUTES_LIST.indexOf(date);
      switch (pos) {
        case 0: // "dateCreated"
          result = resource.getDateCreated();
          break;
        case 1: // "dateLastModified"
          result = resource.getDateLastModified();
          break;
        case 2: // "dateContent"
          if (resource.isFile()) {
            // date content makes no sense for folders
            result = resource.getDateContent();
          }
          break;
        case 3: // "dateReleased"
          long dr = resource.getDateReleased();
          if (dr != CmsResource.DATE_RELEASED_DEFAULT) {
            // default release date must be ignored
            result = dr;
          }
          break;
        case 4: // "dateExpired"
          long de = resource.getDateExpired();
          if (de != CmsResource.DATE_EXPIRED_DEFAULT) {
            // default expiration date must be ignored
            result = de;
          }
          break;
        default:
          // of this is not an attribute, assume this is a property
          if (properties == null) {
            // we may not have to read the properties since the user may only use attributes,
            // so use lazy initializing here
            try {
              properties = cms.readPropertyObjects(resource, false);
            } catch (CmsException e) {
              // use empty list in case of an error, to avoid further re-read tries
              properties = Collections.emptyList();
            }
          }
          String propValue = CmsProperty.get(date, properties).getValue();
          if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(propValue)) {
            try {
              result = Long.parseLong(propValue.trim());
            } catch (NumberFormatException e) {
              // maybe we have better luck with the next property
            }
          }
          break;
      }
      if (result != 0) {
        // if a date value has been found, terminate the loop
        break;
      }
    }
    if (result == 0) {
      // if nothing else was found, use default
      result = defaultValue;
    }
    return result;
  }
  /**
   * 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;
  }