/** Returns child <code>TreeNode</code>s for the given <code>TreeNode</code> model Object. */
  public List getChildTreeNodeObjects(Object nodeObject) {
    if (nodeObject == null) {
      return null;
    }
    if (nodeObject.toString().equals(_objectName)) {
      // In this implementation _objectName represents the top-level,
      // we need to find its children here
      if (_children != null) {
        return Arrays.asList((Object[]) _children);
      }
      _children =
          (Object[])
              JMXUtil.invoke(
                  _objectName, _methodName,
                  _paramsArray, _paramTypesArray);

      // Ok, we got the result, provide an event in case we want to
      // do some filtering
      FacesContext ctx = FacesContext.getCurrentInstance();
      Object retVal =
          getLayoutComponent()
              .dispatchHandlers(
                  ctx,
                  FilterTreeEvent.EVENT_TYPE,
                  new FilterTreeEvent(getParentUIComponent(), _children));
      if ((retVal != null) && (retVal instanceof Object[])) {
        // We have a return value, use it instead of the original list
        _children = (Object[]) retVal;
      }
    } else {
      // Currently multiple levels are not implemented
      return null;
    }

    return _children != null ? Arrays.asList((Object[]) _children) : null;
  }
  /**
   * This method returns the "options" that should be supplied to the factory that creates the
   * <code>TreeNode</code> for the given tree node model object.
   *
   * <p>Some useful options for the standard <code>TreeNode</code> component include:
   *
   * <p>
   *
   * <ul>
   *   <li>text
   *   <li>url
   *   <li>imageURL
   *   <li>target
   *   <li>action
   *   <li>
   *   <li>actionListener
   *   <li>expanded
   * </ul>
   *
   * <p>See Tree / TreeNode component documentation for more details.
   */
  public Map<String, Object> getFactoryOptions(Object nodeObject) {
    if (nodeObject == null) {
      return null;
    }

    LayoutComponent desc = getLayoutComponent();
    Map<String, Object> props = new HashMap<String, Object>();
    if (nodeObject.toString().equals(_objectName)) {
      // This case deals with the top node.

      // NOTE: All supported options must be handled here,
      //		otherwise they'll be ignored.
      // NOTE: Options will be evaluated later, do not eval here.
      setProperty(props, "text", desc.getOption("text"));
      setProperty(props, "url", desc.getOption("url"));
      setProperty(props, "imageURL", desc.getOption("imageURL"));
      setProperty(props, "target", desc.getOption("target"));
      setProperty(props, "action", desc.getOption("action"));

      // NOTE: Although actionListener is supported, LH currently
      //	     implements this to be the ActionListener of the "turner"
      //	     which is inconsistent with "action".  We should make use
      //	     of the "Handler" feature which provides a "toggle"
      //	     CommandEvent.
      setProperty(props, "actionListener", desc.getOption("actionListener"));
      setProperty(props, "expanded", desc.getOption("expanded"));
      setProperty(props, "rendered", desc.getOption("rendered"));
    } else {
      // This case deals with the children

      // NOTE: All supported options must be handled here,
      // otherwise they'll be ignored

      // FIXME: There was a check near here (in DynamicTreeNode.updateKids(...) for
      // FIXME: isChildValid... figure out how we want to expose an "exludes" list
      // FIXME: or filter to make this more generalized.
      if (nodeObject instanceof ObjectName) {
        if (_nameAtt != null) {
          setProperty(
              props, "text", (String) JMXUtil.getAttribute((ObjectName) nodeObject, _nameAtt));
        } else if (_nameMethod != null) {
          // This is for monitoring MBeans, they don't have a name
          // attr, but perhaps a getName() method.
          setProperty(
              props,
              "text",
              (String) JMXUtil.invoke((ObjectName) nodeObject, _nameMethod, null, null));
        }
        if (!props.containsKey("text")) {
          // fallback to the object name
          setProperty(props, "text", nodeObject.toString());
        }
      } else if (nodeObject instanceof String) {
        setProperty(props, "text", (String) nodeObject);
        /*
            FIXME: This is from line #108 - 109 of DynamicTreeNode.java:
            node.setAttribute("webServiceKey", webServiceKeyMap.get(name));
            node.setAttribute("webServiceName", name);
        */
      } else {
        throw new RuntimeException(
            "'"
                + nodeObject
                + "' Illegal type ("
                + nodeObject.getClass().getName()
                + ") for tree processing");
      }

      // Finish setting the child properties
      setProperty(props, "url", desc.getOption("childURL"));
      setProperty(props, "imageURL", desc.getOption("childImageURL"));
      setProperty(props, "target", desc.getOption("childTarget"));
      setProperty(props, "action", desc.getOption("childAction"));
      String tt = (String) desc.getOption("targetConfigName");
      if (!GuiUtil.isEmpty(tt)) {
        setProperty(props, "targetConfigName", tt);
      }
      String check = (String) desc.getOption("checkAdminServer");
      if (!GuiUtil.isEmpty(check)) {
        String serverName = (String) props.get("text");
        if (serverName.equals("server")) {
          setProperty(props, "text", "server (Admin Server)");
          setProperty(props, "serverName", "server");
        } else setProperty(props, "serverName", serverName);
      }
      // We are using "childActionListener" for the hyperlink, not the TreeNode
      //	    setProperty(props, "actionListener", desc.getOption("childActionListener"));
      setProperty(props, "expanded", desc.getOption("childExpanded"));
    }

    // Return the options
    return props;
  }