/**
   * This method returns any facets that should be applied to the <code>TreeNode (comp)</code>.
   * Useful facets for the sun <code>TreeNode</code> component are: "content" and "image".
   *
   * <p>Facets that already exist on <code>comp</code>, or facets that are directly added to <code>
   * comp</code> do not need to be returned from this method.
   *
   * <p>This implementation directly adds a "content" facet and returns <code>null</code> from this
   * method.
   *
   * @param comp The tree node <code>UIComponent</code>.
   * @param nodeObject The (model) object representing the tree node.
   */
  public Map<String, UIComponent> getFacets(UIComponent comp, Object nodeObject) {
    if (nodeObject == null) {
      return null;
    }
    if (nodeObject.toString().equals(_objectName)) {
      return null;
    }
    Properties props = new Properties();
    LayoutComponent desc = this.getLayoutComponent();

    // Check to see if a childActionListener was added
    // NOTE: This is not needed when a "command" event is used.  In the
    //	 case of a CommandEvent an ActionListener will be
    //	 automatically registered by the ComponentFactoryBase class
    //	 during "setOptions()".  Also, setting a childActionListener
    //	 here should not stop "command" handlers from being invoked.
    setProperty(props, "actionListener", desc.getOption("childActionListener"));

    // Also se the target and text...
    setProperty(props, "target", desc.getOption("childTarget"));
    setProperty(props, "text", comp.getAttributes().get("text"));
    // FIXME: Add support for other hyperlink properties??

    // Create Hyperlink
    // NOTE: Last attribute "content" will be the facet named used.
    UIComponent link =
        ComponentUtil.getChild(
            comp,
            "link",
            "com.sun.jsftemplating.component.factory.sun.HyperlinkFactory",
            props,
            "content");

    // Check to see if we have a childURL, evalute it here (after component
    // is created, before rendered) so we can use the link itself to define
    // the URL.  This has proven to be useful...
    Object val = desc.getOption("childURL");
    if (val != null) {
      link.getAttributes()
          .put("url", desc.resolveValue(FacesContext.getCurrentInstance(), link, val));
    }

    // Set href's handlers...
    // We do it this way rather than earlier b/c the factory will not
    // recognize this as a property, it requires it to be defined in the
    // LayoutComponent as a handler.  So we must do this manually like
    // this.
    List handlers = desc.getHandlers("childCommand");
    if (handlers != null) {
      link.getAttributes().put("command", handlers);
      // This adds the required action listener to proces the commands
      // This is needed here b/c the factory has already executed -- the
      // factory is normally the place where this is added (iff there is
      // at least one command handler).
      ((ActionSource) link).addActionListener(CommandActionListener.getInstance());
    }

    // We already added the facet, return null...
    return 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"));
    } else {
      // This case deals with the children

      // NOTE: All supported options must be handled here,
      // otherwise they'll be ignored
      _childImage = "../.." + _childImageFolder;
      if (nodeObject instanceof NameClassPair) {
        String context = (String) ((NameClassPair) nodeObject).getName();
        setProperty(props, "text", context);
        setProperty(props, "nodeClass", (String) ((NameClassPair) nodeObject).getClassName());
        try {
          ArrayList result =
              (ArrayList)
                  JMXUtil.getMBeanServer()
                      .invoke(
                          new ObjectName(_objectName),
                          "getNames",
                          new String[] {context},
                          new String[] {"java.lang.String"});
        } catch (Exception ex) {
          // Ignore exception since there are no children.
          _childImage = "../.." + _childImageDocument;
        }

      } 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", _childImage);
      setProperty(props, "target", desc.getOption("childTarget"));
      setProperty(props, "action", desc.getOption("childAction"));
      // 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;
  }
  /**
   * 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;
  }