/**
   * Writes html with the list of found agents across the top, with links to tables below for each
   * agent. The tables show each agents relationships to other agents. There are links in those
   * tables too, to the other referenced agents.
   */
  protected void writeResponse(
      XMLable result,
      OutputStream out,
      HttpServletRequest request,
      SimpleServletSupport support,
      int format,
      boolean allRelationships) {
    if ((format == FORMAT_HTML) && allRelationships) {
      HierarchyData data = (HierarchyData) result;
      if (VERBOSE)
        System.out.println(
            "HierarchyWorker.writeResponse - got data for " + data.numOrgs() + " orgs");
      PrintWriter writer = new PrintWriter(out);
      writer.println(
          "<HTML><HEAD>\n<TITLE>"
              + getPrefix()
              + support.getAgentIdentifier()
              + "</TITLE>\n"
              + "</HEAD><BODY>\n"
              + "<H2><CENTER>"
              + getPrefix()
              + support.getAgentIdentifier()
              + "</CENTER></H2><p><pre>\n");
      writer.flush();

      writer.println(
          "<a name=\"top\"/>" + "<TABLE align=center border=0 cellPadding=1 cellSpacing=1>");
      boolean rowEnded = false;
      int k = 0;
      data.sortOrgs();
      for (; k < data.numOrgs(); k++) {
        Organization org = data.getOrgDataAt(k);
        if ((k % AGENTS_IN_ROW) == 0) {
          writer.println("<TR>");
          rowEnded = false;
        }
        writer.println(
            "<TD><a href=\"#" + org.getPrettyName() + "\"/>" + org.getPrettyName() + "</a></TD>");
        if ((k % AGENTS_IN_ROW) == AGENTS_IN_ROW - 1) {
          writer.println("</TR>");
          rowEnded = true;
        }
      }
      for (int i = (k % AGENTS_IN_ROW); i < AGENTS_IN_ROW; i++) writer.print("<TD></TD>");
      if (!rowEnded) writer.println("</TR>");
      writer.println("</TABLE><P/><P/><br/>");

      for (int i = 0; i < data.numOrgs(); i++) {
        Organization org = data.getOrgDataAt(i);
        writer.println(
            "<P>Agent relationships for <B><a name=\""
                + org.getPrettyName()
                + "\" />"
                + org.getPrettyName()
                + "</B>&nbsp;<a href=\"#top\">[top]</a></P>");
        writer.println("<TABLE align=center border=1 cellPadding=1 cellSpacing=1");
        writer.println("width=75% bordercolordark=#660000 bordercolorlight=#cc9966>");
        writer.println("<TR>");
        // writer.println("<TD width=\"25%\"> <FONT color=mediumblue ><B>Agent</FONT></B> </TD>");
        writer.println(
            "<TD width=\"25%\"> <FONT color=mediumblue ><B>Assigned Agent</FONT></B></TD>");
        writer.println("<TD width=\"75%\"> <FONT color=mediumblue ><B>Role </FONT></B></TD>");
        writer.println("</TR>");

        List relations = org.getRelations();
        Collections.sort(relations);
        for (Iterator iter = relations.iterator(); iter.hasNext(); ) {
          Organization.OrgRelation relation = (Organization.OrgRelation) iter.next();
          String relatedOrg = relation.getRelatedOrg();
          writer.println(
              "<TR><TD><a href=\"#"
                  + relatedOrg
                  + "\">"
                  + relatedOrg
                  + "</a></TD><TD>"
                  + relation.getName()
                  + "</TD></TR>");
        }
        writer.println("</TABLE><P>");
      }
      writer.println("\n</BODY></HTML>\n");
      writer.flush();
    } else {
      try {
        writeResponse(result, out, request, support, format);
      } catch (Exception e) {
        System.err.println(
            "Got exception " + e + " writing out response for " + support.getAgentIdentifier());
        e.printStackTrace();
      }
    }
  }
  /** populates a HierarchyData object given the self Org */
  protected HierarchyData getHierarchyData(
      HttpServletRequest request,
      SimpleServletSupport support,
      HasRelationships selfOrg,
      boolean recurse,
      boolean allRelationships,
      Set visitedOrgs) {
    // create a "self" org
    String selfOrgName = ((Asset) selfOrg).getClusterPG().getMessageAddress().toString();
    visitedOrgs.add(selfOrgName);
    // build list of orgs
    HierarchyData hd = new HierarchyData();
    Organization toOrg = new Organization();
    toOrg.setUID(selfOrgName);
    toOrg.setPrettyName(selfOrgName); // where is the pretty name kept?
    hd.setRootOrgID(selfOrgName); // set rootId as self

    MutableTimeSpan mts = new MutableTimeSpan();
    RelationshipSchedule schedule = selfOrg.getRelationshipSchedule();

    Collection subordinates = new HashSet();

    if (!allRelationships) {
      Collection subordinates1 =
          schedule.getMatchingRelationships(SUBORD_ROLE, mts.getStartTime(), mts.getEndTime());
      subordinates.addAll(subordinates1);

      Collection subordinates2 =
          schedule.getMatchingRelationships(
              ADMIN_SUBORD_ROLE, mts.getStartTime(), mts.getEndTime());

      subordinates.addAll(subordinates2);
    } else {
      subordinates.addAll(schedule.getMatchingRelationships(mts));
    }

    // add self org to hierarchy
    hd.addOrgData(toOrg);

    if (VERBOSE && false)
      System.out.println("getHierarchyData - " + selfOrgName + " has these subs " + subordinates);

    if (subordinates.isEmpty()) {
      // no subordinates
      return hd;
    }

    Set recurseSubOrgSet = null;
    if (recurse) {
      recurseSubOrgSet = new HashSet();
    }

    // Safe to iterate over subordinates because getSubordinates() returns
    // a new Collection.
    for (Iterator schedIter = subordinates.iterator(); schedIter.hasNext(); ) {
      Relationship relationship = (Relationship) schedIter.next();
      Asset subOrg = (Asset) schedule.getOther(relationship);
      String role = schedule.getOtherRole(relationship).getName();

      String subOrgName = subOrg.getClusterPG().getMessageAddress().toString();

      // client wants a numerical identifier for the role
      int roleId;
      if (!allRelationships) {
        if (role.equalsIgnoreCase("AdministrativeSubordinate")) {
          // admin_subord
          roleId = org.cougaar.planning.servlet.data.hierarchy.Organization.ADMIN_SUBORDINATE;
        } else {
          // some other subord type
          //   ** add more String.equals cases here **
          roleId = org.cougaar.planning.servlet.data.hierarchy.Organization.SUBORDINATE;
        }
        toOrg.addRelation(subOrgName, roleId);
      } else if (!role.endsWith("Self")) {
        toOrg.addRelation(subOrgName, role);
      }
      if (recurse
          && (!(selfOrgName.equals(subOrgName)))
          && // don't recurse on yourself
          validRole(role)
          && // only on customers, subordinates, etc.
          !visitedOrgs.contains(subOrgName)) { // only ones we haven't visited before
        if (VERBOSE) // so we don't have circular paths
        System.out.println(
              "self "
                  + selfOrgName
                  + " sub "
                  + subOrgName
                  + " role "
                  + role
                  + (validRole(role) ? " VALID " : " invalid"));
        recurseSubOrgSet.add(subOrgName);
      }
    }

    // if we are recursing, recurse on subordinates
    if (recurse) {
      visitedOrgs.addAll(recurseSubOrgSet);
      recurseOnSubords(recurseSubOrgSet, request, support, allRelationships, visitedOrgs, hd);
    }

    // return list
    return hd;
  }