/**
  * Set the refs collection to the entity reference strings from the entities collection (and
  * nothing more)
  *
  * @param refs The collection (String) of entity references to modify.
  * @param entities The collection (Entity) of entity objects to use.
  */
 public static void setEntityRefsFromEntities(Collection refs, Collection entities) {
   refs.clear();
   for (Iterator i = entities.iterator(); i.hasNext(); ) {
     Entity entity = (Entity) i.next();
     refs.add(entity.getReference());
   }
 }
  /**
   * Test a collection of Entity object for the specified entity reference
   *
   * @param entities The collection (Entity) of entities
   * @param entityRef The string entity reference to find.
   * @return true if found, false if not.
   */
  public static boolean entityCollectionContainsRefString(Collection entities, String entityRef) {
    for (Iterator i = entities.iterator(); i.hasNext(); ) {
      Entity entity = (Entity) i.next();
      if (entity.getReference().equals(entityRef)) return true;
    }

    return false;
  }
  @WebMethod
  @Path("/getSubmissionsForAssignment")
  @Produces("text/plain")
  @GET
  public String getSubmissionsForAssignment(
      @WebParam(name = "sessionId", partName = "sessionId") @QueryParam("sessionId")
          String sessionId,
      @WebParam(name = "assignmentId", partName = "assignmentId") @QueryParam("assignmentId")
          String assignmentId) {
    try {

      Session s = establishSession(sessionId);
      Assignment assign = assignmentService.getAssignment(assignmentId);
      List subs = assignmentService.getSubmissions(assign);

      // build the xml
      LOG.debug("about to start building xml doc");
      Document dom = Xml.createDocument();
      Node all = dom.createElement("submissions");
      dom.appendChild(all);

      for (int i = 0; i < subs.size(); i++) {

        AssignmentSubmission thisSub = (AssignmentSubmission) subs.get(i);
        LOG.debug("got submission" + thisSub);
        Element uElement = dom.createElement("submission");
        uElement.setAttribute("feedback-comment", thisSub.getFeedbackComment());
        uElement.setAttribute("feedback-text", thisSub.getFeedbackText());
        uElement.setAttribute("grade", thisSub.getGrade());
        uElement.setAttribute("status", thisSub.getStatus());
        uElement.setAttribute("submitted-text", thisSub.getSubmittedText());
        List submitters = thisSub.getSubmitterIds();
        for (int q = 0; q < submitters.size(); q++) {
          uElement.setAttribute("submitter-id", (String) submitters.get(q));
        }

        List submissions = thisSub.getSubmittedAttachments();
        // Element attachments = dom.createElement("attachment");
        for (int q = 0; q < submissions.size(); q++) {
          // Element attachments = dom.createElement("attachment");
          Reference ref = (Reference) submissions.get(q);
          Entity ent = ref.getEntity();
          uElement.setAttribute("attachment-url", ent.getUrl());
          // all.appendChild();
        }

        all.appendChild(uElement);
      }
      String retVal = Xml.writeDocumentToString(dom);
      return retVal;
    } catch (Exception e) {
      LOG.error(
          "WS getSubmissionsForAssignment(): " + e.getClass().getName() + " : " + e.getMessage());
    }

    return "<submissions />";
  }
  /**
   * Test a collection of Entity reference Strings for the specified Entity
   *
   * @param refs The collection (String) of entity refs
   * @param entity The Entity to find.
   * @return true if found, false if not.
   */
  public static boolean refCollectionContainsEntity(Collection refs, Entity entity) {
    String targetRef = entity.getReference();
    for (Iterator i = refs.iterator(); i.hasNext(); ) {
      String entityRef = (String) i.next();
      if (entityRef.equals(targetRef)) return true;
    }

    return false;
  }
  /**
   * Fill in the two collections of Entity reference strings - those added in newEntities that were
   * not in oldEntityRefs, and those removed, i.e. in oldEntityRefs not in newEntities.
   */
  public static void computeAddedRemovedEntityRefsFromNewEntitiesOldRefs(
      Collection addedEntities,
      Collection removedEntities,
      Collection newEntities,
      Collection oldEntityRefs) {
    // added
    for (Iterator i = newEntities.iterator(); i.hasNext(); ) {
      Entity entity = (Entity) i.next();
      if (!refCollectionContainsEntity(oldEntityRefs, entity)) {
        addedEntities.add(entity.getReference());
      }
    }

    // removed
    for (Iterator i = oldEntityRefs.iterator(); i.hasNext(); ) {
      String entityRef = (String) i.next();
      if (!entityCollectionContainsRefString(newEntities, entityRef)) {
        removedEntities.add(entityRef);
      }
    }
  }
  /**
   * Compare these objects based on my property and ascending settings. Collections sort lower than
   * Resources.
   *
   * @param o1 The first object, ContentCollection or ContentResource
   * @param o2 The second object, ContentCollection or ContentResource
   * @return The compare result: -1 if o1 < o2, 0 if they are equal, and 1 if o1 > o2
   */
  public int compare(Object o1, Object o2) {
    String property = m_property;

    // PROP_CONTENT_PRIORITY is special because it allows
    // intermixing folders and files.
    if (property.equals(ResourceProperties.PROP_CONTENT_PRIORITY)) {
      Entity entity1 = (Entity) o1;
      Entity entity2 = (Entity) o2;
      String str1 = entity1.getProperties().getProperty(property);
      String str2 = entity2.getProperties().getProperty(property);

      if (str1 == null || str2 == null) {
        // ignore -- default to a different sort
      } else {
        try {
          Integer rank1 = new Integer(str1);
          Integer rank2 = new Integer(str2);
          return m_ascending ? rank1.compareTo(rank2) : rank2.compareTo(rank1);
        } catch (NumberFormatException e) {
          // ignore -- default to a different sort
        }
      }
      // if unable to do a priority sort, sort by title
      property = ResourceProperties.PROP_DISPLAY_NAME;
    }

    // collections sort lower than resources
    if ((o1 instanceof ContentCollection) && (o2 instanceof ContentResource)) {
      return (m_ascending ? -1 : 1);
    }
    if ((o1 instanceof ContentResource) && (o2 instanceof ContentCollection)) {
      return (m_ascending ? 1 : -1);
    }

    if (property.equals(ResourceProperties.PROP_CONTENT_LENGTH)
        && o1 instanceof ContentCollection) {
      int size1 = ((ContentCollection) o1).getMemberCount();
      int size2 = ((ContentCollection) o2).getMemberCount();
      int rv = ((size1 < size2) ? -1 : ((size1 > size2) ? 1 : 0));
      if (!m_ascending) rv = -rv;
      return rv;
    }

    // ok, they are both the same: resources or collections

    // try a numeric interpretation
    try {
      long l1 = ((Entity) o1).getProperties().getLongProperty(property);
      long l2 = ((Entity) o2).getProperties().getLongProperty(property);
      int rv = ((l1 < l2) ? -1 : ((l1 > l2) ? 1 : 0));
      if (!m_ascending) rv = -rv;
      return rv;
    } catch (Exception ignore) {
    }

    // try a Time interpretation
    try {
      Time t1 = ((Entity) o1).getProperties().getTimeProperty(property);
      Time t2 = ((Entity) o2).getProperties().getTimeProperty(property);
      int rv = t1.compareTo(t2);
      if (!m_ascending) rv = -rv;
      return rv;
    } catch (Exception ignore) {
    }

    // do a formatted interpretation - case insensitive
    if (o1 == null) return -1;
    if (o2 == null) return +1;

    int rv = 0;
    if (m_smart_sort) {
      rv =
          compareLikeMacFinder(
              ((Entity) o1).getProperties().getPropertyFormatted(property),
              ((Entity) o2).getProperties().getPropertyFormatted(property));
    } else {
      rv =
          ((Entity) o1)
              .getProperties()
              .getPropertyFormatted(property)
              .compareTo(((Entity) o2).getProperties().getPropertyFormatted(property));
    }
    return m_ascending ? rv : -rv;
  } // compare
  /**
   * Prepare the current page of messages to display.
   *
   * @return List of MailArchiveMessage to display on this page.
   */
  protected List prepPage(SessionState state) {
    List rv = new Vector();

    // access the page size
    int pageSize = ((Integer) state.getAttribute(STATE_PAGESIZE)).intValue();

    // cleanup prior prep
    state.removeAttribute(STATE_NUM_MESSAGES);

    // are we going next or prev, first or last page?
    boolean goNextPage = state.getAttribute(STATE_GO_NEXT_PAGE) != null;
    boolean goPrevPage = state.getAttribute(STATE_GO_PREV_PAGE) != null;
    boolean goFirstPage = state.getAttribute(STATE_GO_FIRST_PAGE) != null;
    boolean goLastPage = state.getAttribute(STATE_GO_LAST_PAGE) != null;
    state.removeAttribute(STATE_GO_NEXT_PAGE);
    state.removeAttribute(STATE_GO_PREV_PAGE);
    state.removeAttribute(STATE_GO_FIRST_PAGE);
    state.removeAttribute(STATE_GO_LAST_PAGE);

    // are we going next or prev message?
    boolean goNext = state.getAttribute(STATE_GO_NEXT) != null;
    boolean goPrev = state.getAttribute(STATE_GO_PREV) != null;
    state.removeAttribute(STATE_GO_NEXT);
    state.removeAttribute(STATE_GO_PREV);

    // read all channel messages
    List allMessages = readAllResources(state);

    if (allMessages == null) {
      return rv;
    }

    // if we have no prev page and do have a top message, then we will stay "pined" to the top
    boolean pinToTop =
        ((state.getAttribute(STATE_TOP_PAGE_MESSAGE) != null)
            && (state.getAttribute(STATE_PREV_PAGE_EXISTS) == null)
            && !goNextPage
            && !goPrevPage
            && !goNext
            && !goPrev
            && !goFirstPage
            && !goLastPage);

    // if we have no next page and do have a top message, then we will stay "pined" to the bottom
    boolean pinToBottom =
        ((state.getAttribute(STATE_TOP_PAGE_MESSAGE) != null)
            && (state.getAttribute(STATE_NEXT_PAGE_EXISTS) == null)
            && !goNextPage
            && !goPrevPage
            && !goNext
            && !goPrev
            && !goFirstPage
            && !goLastPage);

    // how many messages, total
    int numMessages = allMessages.size();

    if (numMessages == 0) {
      return rv;
    }

    // save the number of messges
    state.setAttribute(STATE_NUM_MESSAGES, new Integer(numMessages));

    // find the position of the message that is the top first on the page
    int posStart = 0;
    String messageIdAtTheTopOfThePage = (String) state.getAttribute(STATE_TOP_PAGE_MESSAGE);
    if (messageIdAtTheTopOfThePage != null) {
      // find the next page
      posStart = findResourceInList(allMessages, messageIdAtTheTopOfThePage);

      // if missing, start at the top
      if (posStart == -1) {
        posStart = 0;
      }
    }

    // if going to the next page, adjust
    if (goNextPage) {
      posStart += pageSize;
    }

    // if going to the prev page, adjust
    else if (goPrevPage) {
      posStart -= pageSize;
      if (posStart < 0) posStart = 0;
    }

    // if going to the first page, adjust
    else if (goFirstPage) {
      posStart = 0;
    }

    // if going to the last page, adjust
    else if (goLastPage) {
      posStart = numMessages - pageSize;
      if (posStart < 0) posStart = 0;
    }

    // pinning
    if (pinToTop) {
      posStart = 0;
    } else if (pinToBottom) {
      posStart = numMessages - pageSize;
      if (posStart < 0) posStart = 0;
    }

    // get the last page fully displayed
    if (posStart + pageSize > numMessages) {
      posStart = numMessages - pageSize;
      if (posStart < 0) posStart = 0;
    }

    // compute the end to a page size, adjusted for the number of messages available
    int posEnd = posStart + (pageSize - 1);
    if (posEnd >= numMessages) posEnd = numMessages - 1;
    int numMessagesOnThisPage = (posEnd - posStart) + 1;

    // select the messages on this page
    for (int i = posStart; i <= posEnd; i++) {
      rv.add(allMessages.get(i));
    }

    // save which message is at the top of the page
    Entity messageAtTheTopOfThePage = (Entity) allMessages.get(posStart);
    state.setAttribute(STATE_TOP_PAGE_MESSAGE, messageAtTheTopOfThePage.getId());

    // which message starts the next page (if any)
    int next = posStart + pageSize;
    if (next < numMessages) {
      state.setAttribute(STATE_NEXT_PAGE_EXISTS, "");
    } else {
      state.removeAttribute(STATE_NEXT_PAGE_EXISTS);
    }

    // which message ends the prior page (if any)
    int prev = posStart - 1;
    if (prev >= 0) {
      state.setAttribute(STATE_PREV_PAGE_EXISTS, "");
    } else {
      state.removeAttribute(STATE_PREV_PAGE_EXISTS);
    }

    if (state.getAttribute(STATE_VIEW_ID) != null) {
      int viewPos = findResourceInList(allMessages, (String) state.getAttribute(STATE_VIEW_ID));

      // are we moving to the next message
      if (goNext) {
        // advance
        viewPos++;
        if (viewPos >= numMessages) viewPos = numMessages - 1;
      }

      // are we moving to the prev message
      if (goPrev) {
        // retreat
        viewPos--;
        if (viewPos < 0) viewPos = 0;
      }

      // update the view message
      state.setAttribute(STATE_VIEW_ID, ((Entity) allMessages.get(viewPos)).getId());

      // if the view message is no longer on the current page, adjust the page
      // Note: next time through this will get processed
      if (viewPos < posStart) {
        state.setAttribute(STATE_GO_PREV_PAGE, "");
      } else if (viewPos > posEnd) {
        state.setAttribute(STATE_GO_NEXT_PAGE, "");
      }

      if (viewPos > 0) {
        state.setAttribute(STATE_PREV_EXISTS, "");
      } else {
        state.removeAttribute(STATE_PREV_EXISTS);
      }

      if (viewPos < numMessages - 1) {
        state.setAttribute(STATE_NEXT_EXISTS, "");
      } else {
        state.removeAttribute(STATE_NEXT_EXISTS);
      }
    }

    return rv;
  } // prepPage