/**
   * Returns true if this category contains any bases visible by the given {@link
   * arlut.csd.ganymede.server.GanymedeSession session}. If session is null, we're supergash, so of
   * course this category is going to contain a visible base.
   */
  private boolean containsVisibleBase(GanymedeSession session) {
    Vector<CategoryNode> contents;
    boolean result = false;

    /* -- */

    if (session == null) {
      return true; // we're not filtering, return true immediately
    }

    contents = getNodes();

    for (CategoryNode node : contents) {
      if (node instanceof DBObjectBase) {
        DBObjectBase base = (DBObjectBase) node;

        if (session.getPermManager().getPerm(base.getTypeID(), true).isVisible()) {
          result = true;
        }
      } else if (node instanceof DBBaseCategory) {
        DBBaseCategory subCategory = (DBBaseCategory) node;

        result = subCategory.containsVisibleBase(session);
      }
    }

    return result;
  }
  /**
   * This method is used to concatenate this DBBaseCategory's information to the passed-in {@link
   * arlut.csd.ganymede.common.CategoryTransport CategoryTransport} object for serialization to the
   * client.
   *
   * <p>This is kind of icky code, really.. {@link arlut.csd.ganymede.server.DBBaseCategory
   * DBBaseCategory}, {@link arlut.csd.ganymede.server.DBObjectBase DBObjectBase}, {@link
   * arlut.csd.ganymede.server.GanymedeSession GanymedeSession} and {@link
   * arlut.csd.ganymede.common.CategoryTransport CategoryTransport} classes are all getting
   * involved, here.
   *
   * <p>The reason for that is that we want to provide a CategoryTransport object to the client that
   * makes no reference to the server-side GanymedeSession class, and proper generation of a
   * CategoryTransport requires making method calls to a GanymedeSession.
   *
   * <p>The down side of that is that the actual structure of the CategoryTransport is generated
   * across two places.. here and in {@link
   * arlut.csd.ganymede.server.DBObjectBase#addBaseToTransport(arlut.csd.ganymede.common.CategoryTransport,
   * arlut.csd.ganymede.server.GanymedeSession)}.
   */
  private void addCategoryToTransport(
      CategoryTransport transport, GanymedeSession session, boolean hideNonEditables) {
    Vector<CategoryNode> contents;

    /* -- */

    transport.addChunk("cat");
    transport.addChunk(getName());

    contents = getNodes();

    if (contents.size() > 0) {
      transport.addChunk("<");

      for (CategoryNode node : contents) {
        if (node instanceof DBObjectBase) {
          DBObjectBase base = (DBObjectBase) node;

          if (session == null
              || (hideNonEditables
                  && session.getPermManager().getPerm(base.getTypeID(), true).isEditable())
              || (!hideNonEditables
                  && session.getPermManager().getPerm(base.getTypeID(), true).isVisible())) {
            base.addBaseToTransport(transport, session);
          }
        } else if (node instanceof DBBaseCategory) {
          DBBaseCategory subCategory = (DBBaseCategory) node;

          if (session == null
              || (hideNonEditables && subCategory.containsEditableBase(session))
              || (!hideNonEditables && subCategory.containsVisibleBase(session))) {
            subCategory.addCategoryToTransport(transport, session, hideNonEditables);
          }
        }
      }
    }

    // terminate this category record

    transport.addChunk(">");
  }