incubator-adffaces-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From awi...@apache.org
Subject svn commit: r427701 [1/2] - in /incubator/adffaces/trunk/trinidad: trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/
Date Tue, 01 Aug 2006 19:59:31 GMT
Author: awiner
Date: Tue Aug  1 12:59:30 2006
New Revision: 427701

URL: http://svn.apache.org/viewvc?rev=427701&view=rev
Log:
Add initial revision of XMLMenuModel

Added:
    incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/XMLMenuModel.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/GroupNode.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/ItemNode.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuConstants.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuContentHandlerImpl.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuNode.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuUtils.java

Added: incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/XMLMenuModel.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/XMLMenuModel.java?rev=427701&view=auto
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/XMLMenuModel.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-api/src/main/java/org/apache/myfaces/trinidad/model/XMLMenuModel.java Tue Aug  1 12:59:30 2006
@@ -0,0 +1,687 @@
+/*
+ * @(#)XMLMenuModel.java
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.myfaces.trinidad.model;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+
+import javax.faces.el.PropertyResolver;
+
+import javax.faces.el.ValueBinding;
+import javax.faces.webapp.UIComponentTag;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
+
+/**
+ * Creates a Menu Model from a TreeModel where nodes in the treeModel
+ * contain viewId information.
+ * <p>
+ * Each node must have either a bean getter method or a Map property
+ * that returns a viewId. There are several restrictions on the data:
+ * <ul>
+ * o The nodes in the tree must either be all beans or all maps,
+ * but not a mix of beans and maps.
+ * o The viewId of a node can be null, but if set it must be unique.
+ * o The tree cannot be mutable.
+ * </ul> 
+ * <p>
+ * The getFocusRowKey method
+ * <ul>
+ * o gets the current viewId by calling
+ * FacesContext.getCurrentInstance().getViewRoot().getViewId()
+ * o compares the current viewId with the viewId's in the viewIdFocusPathMap
+ * that was built by traversing the tree when the model was created.
+ * o returns the focus path to the node with the current viewId or null if the 
+ * current viewId can't be found.
+ * o in the case where a viewId has multiple focus paths, the currently 
+ * selected node is used as a key into the nodeFocusPathMap to return the 
+ * correct focus path.
+ * </ul>
+ * <p>
+ * The Model is created by specifying it in the faces-config.xml file
+ * as follows
+ * <pre>
+ *   &lt;managed-bean&gt;
+ *    &lt;managed-bean-name&gt;hr_menu&lt;/managed-bean-name&gt;
+ *    &lt;managed-bean-class&gt;
+ *      org.apache.myfaces.trinidad.model.XMLMenuModel
+ *    &lt;/managed-bean-class&gt;
+ *    &lt;managed-bean-scope&gt;request&lt;/managed-bean-scope&gt;
+ *    &lt;managed-property&gt;
+ *      &lt;property-name&gt;source&lt;/property-name&gt;
+ *      &lt;property-class&gt;java.lang.String&lt;/property-class&gt;
+ *      &lt;value&gt;/WEB-INF/hr-menu.xml&lt;/value&gt;
+ *    &lt;/managed-property&gt;
+ *  &lt;/managed-bean&gt;
+ * </pre>
+ *
+ * @author Gary Kind
+ * 
+ */
+
+/*
+ * Three hashmaps are also created in order to be able to resolve cases where
+ * multiple menu items cause navigation to the same viewId.  All 3 of these maps
+ * are created after the metadata is parsed and the tree is built, in the 
+ * MenuContentHandlerImpl. 
+ * 
+ * o The first hashMap is called the viewIdFocusPathMap and is built by 
+ * traversing the tree when the model is created.  Each node's focusViewId is 
+ * obtained and used as the key to an entry in the viewIdHashMap.  An ArrayList 
+ * is used as the entry's value and each item in the ArrayList is a node's 
+ * rowkey from the tree. This allows us to have duplicate rowkeys for a single 
+ * focusViewId which translates to a menu that contains multiple items pointing 
+ * to the same page. In general, each entry will have an ArrayList of rowkeys 
+ * with only 1 rowkey, AKA focus path.
+ * o The second hashMap is called the nodeFocusPathMap and is built at the 
+ * same time the viewIdHashMap is built. Each entry's key is the actual node and 
+ * the value is the row key.  Since the model keeps track of the currently
+ * selected menu node, this hashmap can be used to resolve viewId's with 
+ * multiple focus paths.  Since we have the currently selected node, we just
+ * use this hashMap to get its focus path.
+ * o The third hashMap is called idNodeMap and is built at the same time as the
+ * previous maps.  This map is populated by having each entry contain the node's
+ * id as the key and the actual node as the value.  In order to keep track of 
+ * the currently selected node in the case of a GET, the node's id is appended 
+ * to the request URL as a parameter.  The currently selected node's id is 
+ * picked up and this map is used to get the actual node that is currently 
+ * selected.
+ *
+ * Keeping track of the currently selected menu item/node.  
+ * 
+ * If an itemNode in the metadata uses its "action" attribute, a POST is done
+ * and the node's "doAction" method is called when the menu item is clicked. At
+ * that time, the model is notified through its setCurrentlyPostedNode() method, 
+ * where the current node is set and the request method is set to POST.
+ * 
+ * If an itemNode in the metadata uses its "destination" attribute, a GET is
+ * done.  Nothing is called on the model when the menu item is clicked.  However
+ * at the time the page is rendered the "getDestination" method for all nodes
+ * using the "destination" attribute is called.  At this point
+ * we append the node's id to the value of the destination attribute URL, as
+ * a parameter, and return it. So when getFocusRowKey() is called, we get the 
+ * request the node's parameter matching the currently selected node's id.  
+ * Using the node id, we find the matching node in the idNodeMap and voila, we
+ * have the currently selected node!
+ */
+public class XMLMenuModel extends BaseMenuModel
+                          implements Serializable
+{
+  public XMLMenuModel()
+  {
+    super();
+  }
+  
+  /**
+   * setSource - specifies the XML metadata and creates
+   * the XML Menu Model.
+   * 
+   * @param menuMetadataUri - String URI to the XML metadata.
+   */
+  public void setSource(String menuMetadataUri)
+  {
+    if (menuMetadataUri == null || "".equals(menuMetadataUri))
+      return;
+      
+    _mdSource = menuMetadataUri;
+    _createModel();
+  }
+
+  /**
+   * Makes the TreeModel part of the menu model.  Also creates the
+   * _viewIdFocusPathMap, _nodeFocusPathMap, and idNodeMaps.
+   * 
+   * @param data.  The Tree Model instance
+   */
+  
+  public void setWrappedData(Object data)
+  {
+    super.setWrappedData(data);
+    
+    // The only thing the child menu models are needed for are their
+    // menuLists, which get incorporated into the Root Model's tree.
+    // There is no need to create the hashmaps or anything
+    // on the child menu models.  A lot of overhead (performance and
+    // memory) would be wasted.
+    if (_mdSource.equals(_getRootUri()))
+    {
+      _viewIdFocusPathMap = _contentHandler.getViewIdFocusPathMap();
+      _nodeFocusPathMap   = _contentHandler.getNodeFocusPathMap();
+      _idNodeMap          = _contentHandler.getIdNodeMap();
+    }
+  }
+  
+  /**
+   * Returns the rowKey to the current viewId, or in the case of where the 
+   * model has nodes with duplicate viewId's and one is encountered, we 
+   * return the rowKey of the currently selected node.
+   * <p>
+   *
+   * The getFocusRowKey method
+   * <ul>
+   * <li>gets the current viewId by calling
+   * FacesContext.getCurrentInstance().getViewRoot().getViewId()
+   * <li>compares the current viewId with the viewId's in the viewIdFocusPathMap
+   * that was built by traversing the tree when the model was created.
+   * <li>returns the focus path to the node with the current viewId or null if 
+   * the current viewId can't be found.
+   * <li>in the case where a viewId has multiple focus paths, the currently 
+   * selected node is used as a key into the nodeFocusPathMap to return the 
+   * correct focus path.
+   * </ul>
+   * 
+   * @return  the rowKey to the node with the current viewId or null if the 
+   * current viewId can't be found. 
+   */
+  
+  public Object getFocusRowKey()
+  {
+    Object focusPath        = null;
+    String currentViewId    = _getCurrentViewId();
+    FacesContext context    = FacesContext.getCurrentInstance();
+    boolean beginNewRequest = (_begunRequest == false);
+
+    _begunRequest = true;    
+
+    
+    if (beginNewRequest)
+    {
+        // Initializations
+        _prevFocusPath = null;
+      
+      // How did we get to this page?
+      // 1) Clicked on a menu item with its action attribute set.  This does
+      //    a POST.
+      // 2) Clicked on a menu item with its destination attribute set.  This
+      //    does a GET.
+      // 3) Navigation to a viewId within our model but done from outside the
+      //    model.  Examples, button, text link, etc.
+      //
+      
+      // Case 1: POST method.  Current Node has already been set and so has the
+      // request method.  The doAction() method of the clicked node calls
+      // the setCurrentlyPostedNode() method of this model, which sets both. So
+      // we have nothing to do in this case.
+      
+      if (_getRequestMethod() != _METHOD_POST)
+      {
+        // Case 2: GET method.  We have hung the selected node's id off the 
+        // requests URL, which enables us to get the selected node and also 
+        // to know that the request method is GET.
+        Map paramMap = context.getExternalContext().getRequestParameterMap();
+        String nodeId = (String) paramMap.get(_NODE_ID_PROPERTY);
+        
+        if (nodeId != null)
+        {
+          _setCurrentlySelectedNode(getNode(nodeId));
+          _setRequestMethod(_METHOD_GET);
+        }
+      }
+      
+      // Case 3: Navigation to a page within the model from an outside
+      // method, e.g. button, link text, etc.  In this case we set the
+      // currently selected node to null.  This tells us to get the 0th
+      // element of the ArrayList returned from the viewId hashMap.  This
+      // should be a focus path match to the node whose "defaultFocusPath"
+      // attribute was set to 'true'.
+      if (_getRequestMethod() == _METHOD_NONE)
+      {
+        _setCurrentlySelectedNode(null);
+      }
+  
+      // Get the matching focus path ArrayList for the currentViewId.
+      // This is an ArrayList because our map allows nodes with the same
+      // viewId, that is, different focus paths to the same viewId.
+      ArrayList fpArrayList = (ArrayList) _viewIdFocusPathMap.get(currentViewId);
+  
+      if (fpArrayList != null)
+      {
+        // Get the currently selected node
+        Object currentNode = _getCurrentlySelectedNode();
+        
+        if (fpArrayList.size() == 1  || currentNode == null)
+        {
+          // For fpArrayLists with multiple focusPaths,
+          // the 0th entry in the fpArrayList carries the 
+          // focusPath of the node with its defaultFocusPath
+          // attribute set to "true", if there is one.  If
+          // not, the 0th element is the default.
+          focusPath = fpArrayList.get(0);
+        }
+        else
+        {
+          focusPath = _nodeFocusPathMap.get(currentNode);
+        }
+      }
+      
+      // Save all pertinent information
+      _prevFocusPath = focusPath;
+      
+      _setRequestMethod(_METHOD_NONE);
+    }
+    else
+    {
+      // Not at the beginning of a new Request.
+      // Return the previous focus path.
+      // This optimization is here because, for each menu 
+      // item selected, getFocusRowKey gets called multiple times.
+      return _prevFocusPath;
+    }
+
+    return focusPath;
+  }
+
+
+  /**
+   * Gets the URI to the XML menu metadata.
+   * 
+   * @return String URI to the XML menu metadata.
+   */
+  public String getSource()
+  {
+    return _mdSource;
+  }
+
+  /**
+   * Sets the boolean value that determines whether or not to create
+   * nodes whose rendered attribute value is false.  The default
+   * value is false.
+   * 
+   * This is set through a managed property of the XMLMenuModel 
+   * managed bean -- typically in the faces-config.xml file for
+   * a faces application.
+   */
+  public void setCreateHiddenNodes(boolean createHiddenNodes)
+  {
+    _createHiddenNodes = createHiddenNodes;
+  }
+  
+  /**
+   * Gets the boolean value that determines whether or not to create
+   * nodes whose rendered attribute value is false.  The default
+   * value is false.
+   * 
+   * This is called by the contentHandler when parsing the XML metadata
+   * for each node.
+   * 
+   * @return the boolean value that determines whether or not to create
+   * nodes whose rendered attribute value is false.
+   */
+  public boolean getCreateHiddenNodes()
+  {
+    return _createHiddenNodes;
+  }
+  
+
+  /**
+   * Maps the focusPath returned when the viewId is newViewId 
+   * to the focusPath returned when the viewId is aliasedViewId.
+   * This allows view id's not in the treeModel to be mapped
+   * to a focusPath.
+   * 
+   * @param newViewId the view id to add a focus path for.
+   * @param aliasedViewId the view id to use to get the focusPath to use 
+   *        for newViewId.
+   */
+  public void addViewId(String newViewId, String aliasedViewId)
+  { 
+    Object focusPath = _viewIdFocusPathMap.get(aliasedViewId);
+    if (focusPath != null)
+    {
+      _viewIdFocusPathMap.put(newViewId, focusPath);
+    }
+  }
+
+  /**
+   * Sets the currently selected node and the request method.  
+   * This is called by a selected node's doAction method.  This
+   * menu node must have had its "action" attribute set, thus the
+   * method is POST.
+   * 
+   * @param currentNode  The currently selected node in the menu
+   */
+  public void setCurrentlyPostedNode(Object currentNode)  
+  {
+    _setCurrentlySelectedNode(currentNode);
+    _setRequestMethod(_METHOD_POST);
+  }
+
+  /**
+   * Get a the MenuNode corresponding to the key "id" from the 
+   * node id hashmap.
+   * 
+   * @param id - String node id key for the hashmap entry.
+   * @return The MenuNode that corresponds to id.
+   */
+  public Object getNode (String id)
+  {
+    // This needs to be public because the nodes call into this map
+    return _idNodeMap.get(id);
+  }
+
+  /**
+   * Gets the list of custom properties from the node 
+   * and returns the value of propName.
+   * 
+   * @param node Object used to get its list of custom properties
+   * @param propName String name of the property whose value is desired
+   * 
+   * @return Object value of propName for Object node.
+   */
+  public Object getCustomProperty(Object node, String propName)
+  {
+    if (node == null)
+      return null;
+      
+    FacesContext context = FacesContext.getCurrentInstance();
+    PropertyResolver resolver = context.getApplication().getPropertyResolver();
+    
+    // =-=AEW Attributes?  A Map<String, String> would be more appropriate
+    Attributes propList = 
+      (Attributes) resolver.getValue(node, _CUSTOM_ATTR_LIST);
+   
+    if (propList == null)
+      return null;
+      
+    String value = propList.getValue(propName);
+    
+    // If it is an El expression, we must evaluate it
+    // and return its value
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       )
+     {
+       Object elValue = null;
+       
+       try
+       {
+         FacesContext ctx     = FacesContext.getCurrentInstance();
+         ValueBinding binding = ctx.getApplication().createValueBinding(value);
+         elValue              = binding.getValue(ctx);
+       }
+       catch (Exception ex)
+       {
+         _LOG.warning("EL Expression " + value + 
+                      " is invalid or returned a bad value", ex);
+         return null;
+       }
+       return elValue;
+     }
+     
+    return value;
+  }
+  
+  /* ====================================================================
+   * Private Methods
+   * ==================================================================== */
+   
+  /**
+    * Creates a menu model based on the menu metadata Uri.
+    * This is accomplished by:
+    * <ol>
+    * <li> Get the MenuContentHandlerImpl through the Services API.
+    * <li> Set the root model and current model on the content handler, which, 
+    * in turn, sets the models on each of the nodes.
+    * <li> Parse the metadata.  This calls into the MenuContentHandler's 
+    * startElement and endElement methods, where a List of nodes and a TreeModel
+    * are created, along with the 3 hashMaps needed by the Model.</li>
+    * <li> Use the TreeModel to create the XMLMenuModel.</li>
+    * </ol>
+    */
+  private void _createModel()
+  {
+    try
+    {
+      if (_contentHandler == null)
+      {        
+        List services = 
+          ClassLoaderUtils.getServices(_MENUCONTENTHANDLER_SERVICE);
+         
+        if (services.isEmpty())
+          throw new IllegalStateException("No MenuContentHandler was registered.");
+        
+        _contentHandler = (MenuContentHandler) services.get(0);
+        if (_contentHandler == null)
+        {
+          throw new NullPointerException();
+        }
+      }
+      
+      // Set the root, top-level menu model's URI on the contentHandler.
+      // In this model, the menu content handler and nodes need to have
+      // access to the model's data structures and to notify the model
+      // of the currently selected node (in the case of a POST).
+      _setRootModelUri(_contentHandler);
+
+      // Set the local model (model created by a sharedNode) on the
+      // contentHandler so that nodes can get back to their local model
+      // if necessary.
+      _setModelUri(_contentHandler);
+      
+      TreeModel treeModel = _contentHandler.getTreeModel(_mdSource);
+      setWrappedData(treeModel);
+    }
+    catch (Exception ex)
+    {
+      _LOG.severe(  "Exception creating menu model " 
+                  + _mdSource, ex);
+      return;
+    }
+  }
+
+  /**
+   * _setRootModelUri - sets the top-level, menu model's Uri on the 
+   * menu content handler. This is so nodes will only operate
+   * on the top-level, root model. 
+   * 
+   */
+  private void _setRootModelUri(MenuContentHandler contentHandler)
+  {
+    if (_rootUri == null)
+    {
+      _rootUri = _mdSource;
+      
+      // Put the root model on the Application Map so that it
+      // Can be picked up by the nodes to call back into the 
+      // root model
+      FacesContext facesContext = FacesContext.getCurrentInstance();
+      Map requestMap = facesContext.getExternalContext().getRequestMap();
+      
+      requestMap.put(_rootUri, this);
+
+      // Set the key (_rootUri) to the root model on the content
+      // handler so that it can then be set on each of the nodes
+      contentHandler.setRootModelUri(_rootUri);
+    }
+  }
+  
+  /**
+   * Returns the root menu model's Uri.
+   * 
+   * @return the root menu model's Uri.
+   */
+  private String _getRootUri()
+  {
+    return _rootUri;
+  }
+  
+  /**
+   * _setModelUri - sets the local, menu model's Uri on the 
+   * menu content handler. 
+   * 
+   */
+  private void _setModelUri(MenuContentHandler contentHandler)
+  {
+    String localUri = _mdSource;
+    
+    // Put the local model on the Request Map so that it
+    // Can be picked up by the nodes to call back into the 
+    // local model
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    Map requestMap = facesContext.getExternalContext().getRequestMap();
+    
+    requestMap.put(localUri, this);
+    
+    // Set the key (_rootUri) to the root model on the content
+    // handler so that it can then be set on each of the nodes
+    contentHandler.setModelUri(localUri);
+  }
+  
+  /**
+   * Returns the current viewId.
+   * 
+   * @return  the current viewId or null if the current viewId can't be found 
+   */
+  
+  private String _getCurrentViewId()
+  {    
+    String currentViewId = 
+        FacesContext.getCurrentInstance().getViewRoot().getViewId();  
+                   
+    return currentViewId;
+  }  
+
+  /**
+   * Gets the currently selected node in the menu
+   */
+  private Object _getCurrentlySelectedNode()
+  {
+    return _currentNode;
+  }
+  
+  /**
+   * Sets the currently selected node.
+   * 
+   * @param currentNode.  The currently selected node in the menu.
+   */
+  private void _setCurrentlySelectedNode(Object currentNode)
+  {
+    _currentNode = currentNode;
+  }
+
+  /**
+   * Sets the request method
+   * 
+   * @param method
+   */
+  private void _setRequestMethod(String method)
+  {
+    _requestMethod = method;
+  }
+  
+  /**
+   * Get the request method
+   */
+  private String _getRequestMethod()
+  {
+    return _requestMethod;
+  }
+
+  /* ================================================================
+   * Public inner interface for the menu content handler
+   * implementation
+   * ================================================================ */
+   
+  /*
+   * Interface corresponding to the MenuContentHandlerImpl
+   * inorg.apache.myfaces.trinidadinternal.menu.   This is used to achieve 
+   * separation between the api (trinidad) and the implementation (trinidadinternal).
+   * It is only used by the XMLMenuModel, thus it is an internal interface.
+   */
+  public interface MenuContentHandler 
+  {
+    /**
+      * Get the TreeModel built while parsing metadata.
+      * 
+      * @param uri String mapkey to a (possibly) treeModel cached on
+      *        the MenuContentHandlerImpl.
+      * @return TreeModel.
+      */
+    public TreeModel getTreeModel(String uri);
+
+    /**
+      * Sets the root Uri on the ContentHandler so that the nodes
+      * can get back to the root model of the application menu tree
+      * through the request map.
+      */
+    public void setRootModelUri(String uri);
+    
+    /**
+      * Sets the local, sharedNode model's Uri on the ContentHandler so that
+      * the local model can be gotte too if necessary.
+      */
+    public void setModelUri(String uri);
+
+    /**
+     * Get the Model's idNodeMap
+     * 
+     * @return the Model's idNodeMap
+     */
+    public Map getIdNodeMap();
+
+    /**
+     * Get the Model's nodeFocusPathMap
+     * 
+     * @return the Model's nodeFocusPathMap
+     */
+    public Map getNodeFocusPathMap();
+
+    /**
+     * Get the Model's viewIdFocusPathMap
+     * 
+     * @return the Model's viewIdFocusPathMap
+     */
+    public Map getViewIdFocusPathMap();
+  }
+     
+  private Object  _currentNode       = null;
+  private Object  _prevFocusPath     = null;
+  private String  _requestMethod     = _METHOD_NONE;
+  private String  _mdSource          = null;
+  private boolean _createHiddenNodes = false;
+  private boolean _begunRequest      = false;
+
+  private Map _viewIdFocusPathMap;
+  private Map _nodeFocusPathMap;
+  private Map _idNodeMap;
+
+  static private String _rootUri                    = null;  
+  static private MenuContentHandler _contentHandler = null;
+  
+  static private final String _NODE_ID_PROPERTY     = "nodeId";
+  static private final String _METHOD_GET           = "get";
+  static private final String _METHOD_POST          = "post";
+  static private final String _METHOD_NONE          = "none";
+  static private final String _CUSTOM_ATTR_LIST     = "customPropList";
+  static private final String _MENUCONTENTHANDLER_SERVICE =
+            "org.apache.myfaces.trinidad.model.XMLMenuModel$MenuContentHandler";
+            
+  static private final TrinidadLogger _LOG = 
+         TrinidadLogger.createTrinidadLogger(XMLMenuModel.class);
+}

Added: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/GroupNode.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/GroupNode.java?rev=427701&view=auto
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/GroupNode.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/GroupNode.java Tue Aug  1 12:59:30 2006
@@ -0,0 +1,199 @@
+/*
+ * @(#)GroupNode.java
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.myfaces.trinidadinternal.menu;
+
+import java.lang.reflect.Array;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+
+/**
+ * Code specific to a Menu Model's GroupNode.
+ *     
+ * @author Gary Kind 
+ */
+
+public class GroupNode extends MenuNode
+{
+  /**
+    * Constructs a GroupNode
+    */
+  public GroupNode()
+  {
+    super();
+  }
+  
+  /**
+    * Called by the Default ActionListener 
+    * when a menu node is clicked/selected.
+    * 
+    * @return String outcome or viewId used
+    *         during a POST for navigation.
+    */
+  public String doAction()
+  {
+    // Call the doAction method of my idref node
+    return getRefNode().doAction();
+  }
+  
+  /**
+    * Get the Destination URL of a page for a
+    * GET.
+    * 
+    * @return String URL of a page.
+    */
+  public String getDestination()
+  {
+    // Call the getDestination method of my idref node
+    return getRefNode().getDestination();
+  }
+  
+  /**
+    * Sets the idref of the node.  
+    * 
+    * The value of this attribute is an "id" of another node
+    * This tells the pointing node where to obtain its viewId and 
+    * takes precedence (and will replace) the pointing nodes viewId,
+    * if one exists.  This should point to a node of the same style,
+    * e.g. actionNode points to actionNode.  
+    * 
+    * @param idref - String name pointing to the "id" of another node
+    */
+  public void setIdRef(String idref)
+  {
+    _idref = idref;
+    
+    // Create a list of idref's for easier access
+    if (_idref != null)
+      _makeIdRefList (idref);
+  }
+  
+  /**
+    * Get the node whose id matches this node's
+    * idref attribute value.
+    * 
+    * @return the MenuNode whose id matches this
+    *         node's idref attribute value.
+    */
+  public MenuNode getRefNode()
+  {
+    MenuNode refNode = null;
+    
+    // create one if it does not exist
+    // should not happen, but can't hurt
+    if (_idrefList == null)
+    {
+      String idref = getIdRef();
+      _makeIdRefList(idref);       
+    }
+    
+    // Get idrefList
+    String[] idrefList = _getIdRefList();
+    
+    // Traverse the list. Do the following:
+    //    o get Node from Model's hashMap of nodes and ids
+    //    o check attributes (rendered, disabled, readOnly)
+    //    o if they are ok, return the node    
+    for (int i=0; i < Array.getLength(idrefList); i++)
+    {
+      String refNodeId = idrefList[i];
+      
+      refNode = (MenuNode) getRootModel().getNode(refNodeId);
+      
+      // if nothing found, move on to the next idref
+      if (refNode == null)
+       continue;
+       
+      // Check the attributes of the found node
+      // IMPORTANT NOTE: nodes whose rendered attribute
+      // is set to false never get created, so the first
+      // test should never return true.  But just in 
+      // case the creation ever changes, we will leave
+      // this test.
+      if (  !refNode.getRendered()
+          || refNode.getDisabled()
+          || refNode.getReadOnly()
+         )
+      {
+       refNode = null;
+       continue;
+      }
+       
+      // Ok, we have a valid RefNode
+      break;
+    }
+    
+    // If no valid node is found,
+    // log an error
+    if (refNode == null)
+    {
+        _LOG.severe("GroupNode " + getLabel() + "refers to no valid node.\n");
+        return null;
+    }    
+    
+    return refNode;
+  }
+
+  /**
+    * Get the id of the node referred to by 
+    * the idref attribute of this node.
+    * 
+    * @return String id of the node referred 
+    *         to by the idref attribure of
+    *         this node.
+    */
+  public String getIdRef()
+  {
+    return _idref;
+  }
+
+  /* =============================================================
+   * Private methods
+   * =============================================================*/
+   
+  /**
+    * _getIdRefList. gets the list of idrefs for this node.
+    * 
+    * @return String[] list of idrefs for this node.
+    */
+  private String[] _getIdRefList()
+  {
+    return _idrefList;
+  }
+  
+  /**
+    * Make a list of idref entries from the nodes String
+    * of idref's.
+    * 
+    * This should only be called from the node's setIdRef
+    * method.  So if it is called more than once (highly 
+    * unlikely), simply empty out the previous contents.
+    * 
+    * @param entries - String of String entries
+    * 
+    */
+  private void _makeIdRefList (String entries)
+  {    
+    _idrefList = entries.trim().split("\\s+");
+  }
+
+  private String   _idref     = null;
+  private String[] _idrefList = null;
+
+  private final static TrinidadLogger _LOG = 
+       TrinidadLogger.createTrinidadLogger(GroupNode.class);
+}

Added: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/ItemNode.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/ItemNode.java?rev=427701&view=auto
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/ItemNode.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/ItemNode.java Tue Aug  1 12:59:30 2006
@@ -0,0 +1,424 @@
+/*
+* @(#)ItemNode.java
+*
+* Copyright 2006 The Apache Software Foundation.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.apache.myfaces.trinidadinternal.menu;
+
+import javax.faces.webapp.UIComponentTag;
+
+/**
+ * Code specific to a Menu Model's ItemNode.
+ *     
+ * @author Gary Kind
+ */
+
+public class ItemNode extends MenuNode
+{
+  /**
+    * Constructs an ItemNode
+    */
+  public ItemNode()
+  {
+    super();
+  }
+  
+  /**
+    * Sets the action of the node.  This is obtained from the menu
+    * metadata file and is the string value of the "action" 
+    * property.
+    * 
+    * @param action - the string value of the ItemNode's "action" property.
+    */
+
+  public void setAction(String action)
+  {
+    _action = action;
+  }  
+  
+  /**
+    * Gets the value of the node's action property.  The action attr value
+    * could be one of 2 things:
+    * 1) An EL expression
+    * 2) An outcome referencing a navigation rule in the faces_config file.
+    * 
+    * Since this method is called only when an ItemNode is clicked, the model 
+    * is notified that this node is the currently selected node.
+    * 
+    * @return String value of the ItemNode's "action" property.
+    */
+  public String doAction()
+  {
+    String value = _action;
+
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       ) 
+    {
+      // Value of action is EL method binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+    }
+
+    // Post me as the selected Node for the request
+    postSelectedNode(this);
+
+    return value;
+  }
+
+  /**
+    * setActionListener - sets the value of the Menu Node's actionListener
+    * atribute.
+    * 
+    * @param actionListener - El expression method reference to an 
+    * action listener
+    */
+  public void setActionListener(String actionListener)
+  {
+    _actionListener = actionListener;
+  }  
+  
+  /**
+    * getActionListener - gets the value of the Menu Node's actionListener
+    * attribute.
+    * 
+    * @return String  - method reference to an 
+    * action listener
+    */
+  public String getActionListener()
+  {
+    String value = _actionListener;
+
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       )
+    {
+      // Value of action is EL method binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+      setActionListener(value);
+    }
+
+    return value;
+  }
+  
+  /**
+    * setLaunchListener - sets the value of the Menu Node's launchListener
+    * atribute.
+    * 
+    * @param launchListener - El expression method reference to a 
+    * launch listener
+    */
+  public void setLaunchListener(String launchListener)
+  {
+    _launchListener = launchListener;
+  }  
+  
+  /**
+    * getLaunchListener - gets the value of the Menu Node's launchListener
+    * attribute.
+    * 
+    * @return String  - method reference to an 
+    * launch listener
+    */
+  public String getLaunchListener()
+  {
+    String value = _launchListener;
+
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       )
+    {
+      // Value of action is EL method binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+      setLaunchListener(value);
+    }
+
+    return value;
+  }
+  
+  /**
+    * setReturnListener - sets the value of the Menu Node's returnListener
+    * atribute.
+    * 
+    * @param returnListener - El expression method reference to a 
+    * return listener
+    */
+  public void setReturnListener(String returnListener)
+  {
+    _returnListener = returnListener;
+  }  
+  
+  /**
+    * getReturnListener - gets the value of the Menu Node's returnListener
+    * attribute.
+    * 
+    * @return String  - method reference to an 
+    * return listener
+    */
+  public String getReturnListener()
+  {
+    String value = _returnListener;
+
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       )
+    {
+      // Value of action is EL method binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+      setReturnListener(value);
+    }
+
+    return value;
+  }
+  
+  /**
+    * Sets the immediate attribute of the menu item.  
+    *  
+    * @param immediate - boolean indicating whether or not data validation - 
+    * client-side or server-side - should take place when 
+    * events are generated by this component. 
+    */
+  public void setImmediate(boolean immediate)
+  {
+    _immediateStr = immediate ? "true" : "false";
+  }
+
+  /**
+    * Sets the immediate attribute of the menu item.  
+    * 
+    * @param immediateStr - string representing a boolean value
+    * or an EL expression
+    */
+  public void setImmediate(String immediateStr)
+  {
+    _immediateStr = immediateStr;
+  }
+      
+  /**
+    * Gets the immediate attribute of the menu item.  
+    *
+    * @return boolean - indicating whether or not data validation - 
+    * client-side or server-side - should take place when events 
+    * are generated by this component. 
+    */
+  public boolean getImmediate()
+  {
+    boolean immediate = MenuUtils.evalBoolean(_immediateStr, false);
+    return immediate;
+  }
+      
+  /**
+    * Sets the useWindow attribute of the menu item.  
+    *  
+    * @param useWindow - boolean indicating whether
+    * or not to use a new window when launching dialogs. 
+    */
+  public void setUseWindow(boolean useWindow)
+  {
+    _useWindowStr = useWindow ? "true" : "false";
+  }
+
+  /**
+    * Sets the useWindow attribute of the menu item.  
+    * 
+    * @param useWindowStr - string representing a boolean value or
+    * an EL Expression
+    */
+  public void setUseWindow(String useWindowStr)
+  {
+    _useWindowStr = useWindowStr;
+  }
+      
+  /**
+    * Gets the useWindow attribute of the menu item.  
+    *
+    * @return boolean - indicating whether
+    * or not to use a new window when launching dialogs. 
+    */
+  public boolean getUseWindow()
+  {
+    boolean useWindow = MenuUtils.evalBoolean(_useWindowStr, false);
+    return useWindow;
+  }
+      
+  /**
+    * Sets the windowHeight attribute of the menu item.  
+    *  
+    * @param windowHeight - int height of the window, if 
+    * this command is used to launch a window.
+    */
+  public void setWindowHeight(int windowHeight)
+  {
+    _windowHeightStr = Integer.toString(windowHeight);
+  }
+
+  /**
+    * Sets the windowHeight attribute of the menu item.  
+    *  
+    * @param windowHeightStr - String Height of the window, if 
+    * this command is used to launch a window. Could be an
+    * EL expression
+    */
+  public void setWindowHeight(String windowHeightStr)
+  {
+    _windowHeightStr = windowHeightStr;
+  }
+
+  /**
+    * Gets the windowHeight attribute of the menu item.  
+    *
+    * @return int height of the window, if 
+    * this command is used to launch a window. 
+    */
+  public int getWindowHeight()
+  {
+    int windowHeight = MenuUtils.evalInt(_windowHeightStr);
+    return windowHeight;
+  }
+      
+  /**
+    * Sets the windowWidth attribute of the menu item.  
+    *  
+    * @param windowWidth - int width of the window, if 
+    * this command is used to launch a window.
+    */
+  public void setWindowWidth(int windowWidth)
+  {
+    _windowWidthStr = Integer.toString(windowWidth);
+  }
+
+  /**
+    * Sets the windowWidth attribute of the menu item.  
+    *  
+    * @param windowWidthStr - String width of the window, if 
+    * this command is used to launch a window. Could be an
+    * EL expression
+    */
+  public void setWindowWidth(String windowWidthStr)
+  {
+    _windowWidthStr = windowWidthStr;
+  }
+
+  /**
+    * Gets the windowWidth attribute of the menu item.  
+    *
+    * @return int width of the window, if 
+    * this command is used to launch a window. 
+    */
+  public int getWindowWidth()
+  {
+    int windowWidth = MenuUtils.evalInt(_windowWidthStr);
+    return windowWidth;
+  }
+
+  /**
+    * Sets the destination of the node.  
+    * 
+    * This is obtained from the metadata file and is the string
+    * value of the "destination" property.
+    *
+    * @param destination - either a URI or an EL method binding expression.
+    */
+  public void setDestination(String destination)
+  {
+    _destination = destination;
+  }  
+  
+  /**
+    * Gets the value of the node's destination property.
+    * The destination attr value could be one of 2 things:
+    * 1) a uri
+    * 2) An EL expression
+    * 
+    * So that the model can identify this node as the currently selected
+    * node, the node's id is appended to the destination as a parameter
+    * that is picked up when the getFocusRowKey() method of the model 
+    * is called to get the focus path.
+    * 
+    * @return destination - the String value of the destinationNode's
+    *                       "destination" property.
+    */
+  public String getDestination()
+  {
+    String value = _destination;
+      
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       ) 
+    {
+      // Value of action is EL method binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+    }
+
+    // Appending nodeId to URL so that we can identify the node
+    // when getFocusRowKey() is called on the model.
+    return value != null ? value + "?nodeId=" + getId() : value;
+  }
+  
+  /**
+   * setTargetFrame - sets the value of the Destination Node's
+   * targetFrame attribute
+   * 
+   * @param targetFrame - the target frame for the goCommandMenuItem.
+   */
+  public void setTargetFrame(String targetFrame) 
+  {
+    _targetFrame = targetFrame; 
+  }
+
+  /**
+   * getTargetFrame - gets the value of the Destination Node's
+   * targetFrame attribute
+   * 
+   * @return the target frame for the goCommandMenuItem.
+   */
+  public String getTargetFrame()
+  {
+    String value = _targetFrame;
+    
+    // Could be EL expression
+    if (   value != null
+        && UIComponentTag.isValueReference(value)
+       )
+    {
+      // Value of destination is EL value binding, so we 
+      // need to evaluate it
+      value = (String)MenuUtils.getBoundValue(value);
+      setTargetFrame(value);
+    }
+     
+    return value;
+  }
+
+  private String _destination     = null;
+  private String _targetFrame     = null;   
+  private String _action          = null;
+  private String _actionListener  = null;
+  private String _launchListener  = null;
+  private String _returnListener  = null;
+  private String _immediateStr    = null;
+  private String _useWindowStr    = null;
+  private String _windowHeightStr = null;
+  private String _windowWidthStr  = null;
+}

Added: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuConstants.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuConstants.java?rev=427701&view=auto
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuConstants.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuConstants.java Tue Aug  1 12:59:30 2006
@@ -0,0 +1,25 @@
+/*
+ * @(#)MenuConstants.java
+ *
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.myfaces.trinidadinternal.menu;
+
+public interface MenuConstants
+{
+  public final static String NODE_STYLE_ITEM  = "item";
+  public final static String NODE_STYLE_GROUP = "group";
+}

Added: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuContentHandlerImpl.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuContentHandlerImpl.java?rev=427701&view=auto
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuContentHandlerImpl.java (added)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/menu/MenuContentHandlerImpl.java Tue Aug  1 12:59:30 2006
@@ -0,0 +1,957 @@
+/* @(#)MenuContentHandlerImpl.java
+ *
+ *
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.myfaces.trinidadinternal.menu;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.net.URL;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import java.util.Map;
+
+import java.util.Stack;
+
+import javax.faces.context.FacesContext;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.model.ChildPropertyTreeModel;
+import org.apache.myfaces.trinidad.model.TreeModel;
+import org.apache.myfaces.trinidad.model.XMLMenuModel;
+import org.apache.myfaces.trinidad.model.XMLMenuModel.MenuContentHandler;
+
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Handler called by the SAXParser when parsing menu metadata
+ * as part of the XML Menu Model of Trinidad Faces.
+ * <p>
+ * This is called through the Services API (See XMLMenuModel.java) to 
+ * keep the separation between API's and internal modules.
+ * <p>
+ * startElement() and endElement() are called as one would expect,
+ * at the start of parsing an element in the menu metadata file and
+ * at the end of parsing an element in the menu metadata file.
+ * 
+ * The menu model is created as a List of itemNodes and groupNodes
+ * which is available to and used by the XMLMenuModel to create the
+ * TreeModel and internal Maps.
+ * 
+ * @author Kris McQueen and Gary Kind
+ */
+ /* 
+  * IMPORTANT NOTE: Much of the work and data structures used by the 
+  * XMLMenuModel are created (and kept) in this class.  This is necessarily the 
+  * case because the scope of the XMLMenuModel is request.  The 
+  * MenuContentHandlerImpl is shared so it does not get rebuilt upon each 
+  * request as the XMLMenuModel does. So the XMLMenuModel can get its data
+  * each time it is rebuilt (on each request) without having to reparse and
+  * recreate all of its data structures.  It simply gets them from here.
+  * 
+  * As well as the tree, three hashmaps are created in order to be able to 
+  * resolve cases where multiple menu items cause navigation to the same viewId.  
+  * All 3 of these maps are created after the metadata is parsed and the tree is
+  * built, in the _addToMaps method. 
+  * 
+  * o The first hashMap is called the viewIdFocusPathMap and is built by 
+  * traversing the tree after it is built (see endDocument()).  
+  * Each node's focusViewId is 
+  * obtained and used as the key to an entry in the viewIdHashMap.  An ArrayList 
+  * is used as the entry's value and each item in the ArrayList is a node's 
+  * rowkey from the tree. This allows us to have duplicate rowkeys for a single 
+  * focusViewId which translates to a menu that contains multiple items pointing 
+  * to the same page. In general, each entry will have an ArrayList of rowkeys 
+  * with only 1 rowkey, AKA focus path.
+  * o The second hashMap is called the nodeFocusPathMap and is built at the 
+  * same time the viewIdHashMap is built. Each entry's key is the actual node 
+  * and the value is the row key.  Since the model keeps track of the currently
+  * selected menu node, this hashmap can be used to resolve viewId's with 
+  * multiple focus paths.  Since we have the currently selected node, we just
+  * use this hashMap to get its focus path.
+  * o The third hashMap is called idNodeMap and is built at the same time as the
+  * previous maps.  This map is populated by having each entry contain the node's
+  * id as the key and the actual node as the value.  In order to keep track of 
+  * the currently selected node in the case of a GET, the node's id is appended 
+  * to the request URL as a parameter.  The currently selected node's id is 
+  * picked up and this map is used to get the actual node that is currently 
+  * selected.
+  */ 
+public class MenuContentHandlerImpl extends DefaultHandler
+                                    implements MenuContentHandler
+{
+  /**
+    * Constructs a Menu Content Handler.
+    */
+  public MenuContentHandlerImpl()
+  {
+    super();
+    
+    // Init the menu list map
+    if (_treeModelMap == null)
+      _treeModelMap = new HashMap<String, TreeModel>();
+  }
+  
+  /**
+   * Called by the SAX Parser at the start of parsing a document.
+   */
+  public void startDocument()
+  {
+    _nodeDepth = 0;
+    _menuNodes = new ArrayList();
+    _menuList  = null;
+   
+    // Handler Id will have to change also to be unique 
+    _handlerId = Integer.toString(System.identityHashCode((Object) _menuNodes));
+  }
+  
+  /**
+    * Start the parsing of an node element entry in the menu metadata file.
+    * <p>
+    * If the entry is for an itemNode or a destinationNode, create the node
+    * and it to the List.  If the entry is for a sharedNode, a new submenu
+    * model is created.
+    * 
+    * @param nameSpaceUri - only used when passed to super class.
+    * @param localElemName - only used when passed to super class.
+    * @param qualifiedElemName - String designating the node type of the entry.
+    * @param attrList - List of attributes in the menudata entry.
+    * @throws SAXException
+    */
+  public void startElement(String nameSpaceUri, String localElemName, 
+                           String qualifiedElemName, Attributes attrList)
+    throws SAXException
+  {
+    super.startElement(nameSpaceUri, localElemName, qualifiedElemName, 
+                       attrList);
+                       
+    if (_ROOT_NODE.equals(qualifiedElemName))
+    {
+      // Unless both of these are specified, don't attempt to load
+      // the resource bundle.
+      String resBundle    = attrList.getValue(_RES_BUNDLE_ATTR);
+      String resBundleKey = attrList.getValue(_VAR_ATTR);
+      
+      if (   (resBundle != null    && !"".equals(resBundle))
+          && (resBundleKey != null && !"".equals(resBundleKey))
+         )
+      {        
+        // Load the resource Bundle.
+        // Ensure the bundle key is unique by appending the 
+        // handler Id.
+        MenuUtils.loadBundle(resBundle, resBundleKey + getHandlerId());
+        _resBundleKey  = resBundleKey;
+        _resBundleName = resBundle;
+      }
+    }
+    else
+    {
+      // Either itemNode, destinationNode, or groupNode
+      boolean isNonSharedNode = (   _ITEM_NODE.equals(qualifiedElemName)
+                                 || _GROUP_NODE.equals(qualifiedElemName)
+                                );
+               
+      if (isNonSharedNode) 
+      {
+        _currentNodeStyle = (  _ITEM_NODE.equals(qualifiedElemName) 
+                             ? MenuConstants.NODE_STYLE_ITEM
+                             : MenuConstants.NODE_STYLE_GROUP
+                            );
+        _nodeDepth++;
+        
+        if ((_skipDepth >= 0) && (_nodeDepth > _skipDepth))
+        {
+          // This sub-tree is being skipped, so just return
+          return;
+        }
+        
+        if (_menuNodes.size() < _nodeDepth) 
+        {
+          ArrayList list = new ArrayList();
+          _menuNodes.add(list);
+        }
+
+        _attrList = new AttributesImpl(attrList);
+          
+        // Create either an itemNode or groupNode.
+        MenuNode menuNode = _createMenuNode();
+        
+        if (menuNode == null)
+        {
+          // No menu item is created, so note that we are 
+          // now skipping the subtree
+          _skipDepth = _nodeDepth;
+        }
+        else
+        {
+          if (   (_resBundleName != null && !"".equals(_resBundleName))
+              && (_resBundleKey  != null && !"".equals(_resBundleKey))
+             )
+          {
+            menuNode.setResBundleKey(_resBundleKey);
+            menuNode.setResBundleName(_resBundleName);
+          }
+          
+          // Set the node's MenuContentHandlerImpl id so that when
+          // the node's getLabel() method is called, we can
+          // use the handlerId to insert into the label
+          // if it is an EL expression.
+          menuNode.setHandlerId(getHandlerId());
+          
+          // Set the root model on the node so we can call into
+          // the root model from the node to populate its
+          // idNodeMap (See CombinationMenuModel.java)
+          menuNode.setRootModelUri(getRootModelUri());
+          
+          // Set the local model (created when parsing a sharedNode)
+          // on the node in case the node needs to get back to its
+          // local model.
+          // menuNode.setModel(getModel());
+          
+          List list = (List)_menuNodes.get(_nodeDepth-1);
+          list.add(menuNode);
+        }
+      }
+      else if (_SHARED_NODE.equals(qualifiedElemName))
+      {
+        _nodeDepth++;
+  
+        // SharedNode's "ref" property points to another submenu's metadata,
+        // and thus a new model, which we build here.  Note: this will
+        // recursively call into this MenuContentHandlerImpl when parsing the
+        // submenu's metadata.
+        String expr            = attrList.getValue(_REF_ATTR);
+        
+        // Need to push several items onto the stack now as we recurse
+        // into another menu model.
+        _saveModelData();        
+
+        XMLMenuModel menuModel = (XMLMenuModel)MenuUtils.getBoundValue(expr);
+        
+        // Now must pop the values cause we are back to the parent
+        // model.
+        _restoreModelData();
+        
+        Object subMenuObj      = menuModel.getWrappedData();
+        List subMenuList       = null;
+        
+        if (subMenuObj instanceof ChildPropertyTreeModel)
+        {
+          subMenuList =  
+            (List)((ChildPropertyTreeModel)subMenuObj).getWrappedData();
+        }
+  
+        // SharedNode could be the first child
+        // So we need a new list for the children
+        if (_menuNodes.size() < _nodeDepth) 
+        {
+          ArrayList list = new ArrayList();
+          _menuNodes.add(list);
+        }
+        
+        List list = (List)_menuNodes.get(_nodeDepth-1);
+        list.addAll(subMenuList);
+      }
+    }
+  }
+  
+  /**
+   * Processing done at the end of parsing a node enty element from the 
+   * menu metadata file.  This manages the node depth properly so that
+   * method startElement works correctly to build the List.
+   * 
+   * @param nameSpaceUri - not used.
+   * @param localElemName - not used.
+   * @param qualifiedElemName - String designating the node type of the entry.
+   */
+  public void endElement(String nameSpaceUri, String localElemName, String qualifiedElemName)
+  {
+    if (   _ITEM_NODE.equals(qualifiedElemName) 
+        || _GROUP_NODE.equals(qualifiedElemName)
+       ) 
+    {
+      _nodeDepth--;
+
+      if (_skipDepth >= 0)
+      {
+        if (_nodeDepth < _skipDepth)
+        {
+          _skipDepth = -1;
+        }
+      }
+      else
+      {
+        if (_nodeDepth > 0)
+        {
+          // The parent menu item is the last menu item at the previous depth
+          List      parentList = (List)_menuNodes.get(_nodeDepth-1);
+          MenuNode  parentNode = (MenuNode)parentList.get(parentList.size()-1);
+          
+          parentNode.setChildren((List)_menuNodes.get(_nodeDepth));
+        }
+
+        // If we have dropped back two levels, then we are done adding
+        // the parent's sub tree, remove the list of menu items at that level
+        // so they don't get added twice.
+        if (_nodeDepth == (_menuNodes.size() - 2)) 
+        {
+          _menuNodes.remove(_nodeDepth+1);
+        }
+      }
+    }
+    else if (_SHARED_NODE.equals(qualifiedElemName))
+    {
+      _nodeDepth--;
+
+      if (_nodeDepth > 0)
+      {
+        // The parent menu item is the last menu item at the previous depth
+        List      parentList = (List)_menuNodes.get(_nodeDepth-1);
+        MenuNode  parentNode = (MenuNode)parentList.get(parentList.size()-1);
+        
+        parentNode.setChildren((List)_menuNodes.get(_nodeDepth));
+      }
+    }
+  }
+
+  /**
+   * Called by the SAX Parser at the end of parsing a document. 
+   * Here, the menuList is put on the menuList map.
+   */
+  public void endDocument()
+  {
+    _menuList = (List)_menuNodes.get(0);
+    
+    // Create the treeModel
+    ChildPropertyTreeModel treeModel = 
+                  new ChildPropertyTreeModel((Object)_menuList, "children");
+                  
+    // Put it in the map
+    _treeModelMap.put(_currentTreeModelMapKey, treeModel);
+    
+    // If Model is the Root, then build Model's hashmaps 
+    // and set them on the Root Model.
+    if (getRootModelUri().equals(getModelUri()))
+    {
+      _viewIdFocusPathMap = new HashMap<String, ArrayList>();
+      _nodeFocusPathMap   = new HashMap<Object, ArrayList>();
+      _idNodeMap          = new HashMap<String, Object>();
+      Object oldPath      = treeModel.getRowKey(); 
+
+      treeModel.setRowKey(null);
+
+      // Populate the maps
+      _addToMaps(treeModel, _viewIdFocusPathMap, _nodeFocusPathMap, _idNodeMap);
+
+      treeModel.setRowKey(oldPath);
+    }
+  }
+  
+  /**
+   * Get the Model's viewIdFocusPathMap
+   * 
+   * @return the Model's viewIdFocusPathMap
+   */
+  public Map getViewIdFocusPathMap()
+  {
+    return _viewIdFocusPathMap;
+  }
+  
+  /**
+   * Get the Model's nodeFocusPathMap
+   * 
+   * @return the Model's nodeFocusPathMap
+   */
+  public Map getNodeFocusPathMap()
+  {
+    return _nodeFocusPathMap;
+  }
+
+  /**
+   * Get the Model's idNodeMap
+   * 
+   * @return the Model's idNodeMap
+   */
+  public Map getIdNodeMap()
+  {
+    return _idNodeMap;
+  }
+
+  /**
+    * Get the treeModel built during parsing
+    * 
+    * @return List of menu nodes.
+    */
+  public TreeModel getTreeModel(String uri)
+  {
+    TreeModel model = _treeModelMap.get(uri);
+    if (model == null)
+    {
+      _currentTreeModelMapKey = uri;
+
+      try
+      {
+        // Get a parser.  NOTE: we are using the jdk's 1.5 SAXParserFactory
+        // and SAXParser here.
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+        SAXParser parser = factory.newSAXParser();
+        
+        
+        // Parse the metadata
+        InputStream inStream = _getStream(uri);
+        parser.parse(inStream, this);
+      }
+      catch (SAXException saxex)
+      {
+        _LOG.severe ( "Exception creating model " + uri, saxex);
+      }
+      catch (IOException ioe)
+      {
+        _LOG.severe ( "Exception creating model " + uri, ioe);
+      }
+      catch (ParserConfigurationException pce)
+      {
+        _LOG.severe ( "Exception creating model " + uri, pce);
+      }
+    }
+
+    return _treeModelMap.get(uri);
+  }
+
+  /**
+   * Get the top-level, root menu model, which contains
+   * the entire menu tree.
+   * 
+   * @return root, top-level XMLMenuModel
+   */
+  public XMLMenuModel getRootModel()
+  {
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    Map requestMap = facesContext.getExternalContext().getRequestMap();
+    
+    return (XMLMenuModel) requestMap.get(getRootModelUri());
+  }
+  
+  /**
+   * Get the top-level, root menu model's Uri.
+   * 
+   * @return root, top-level XMLMenuModel's Uri
+   */
+  public String getRootModelUri()
+  {
+    return _rootModelUri;
+  }
+  
+  /**
+   * Sets the root menu Model's Uri.
+   * <p>
+   * This is always only the top-level, root model's Uri.
+   * We do this because the MenuContentHandlerImpl and nodes need to be able 
+   * to call into the root model to:
+   * <ul>
+   * <li>notify them root menu model of the currently selected node on a POST
+   * </ul>
+   * 
+   * @param rootModelUri - String the root, top-level menu model's Uri.
+   */
+  public void setRootModelUri(String rootModelUri)
+  {
+    _rootModelUri = rootModelUri;
+  }
+
+  /**
+   * Get the local (sharedNode) menu model.
+   * 
+   * @return sharedNode's XMLMenuModel
+   */
+  public XMLMenuModel getModel()
+  {
+    FacesContext facesContext = FacesContext.getCurrentInstance();
+    Map requestMap = facesContext.getExternalContext().getRequestMap();
+    
+    return (XMLMenuModel) requestMap.get(getModelUri());
+  }
+  
+  /**
+   * Get the local (sharedNode) menu model's Uri.
+   * 
+   * @return sharedNode's XMLMenuModel Uri
+   */
+  public String getModelUri()
+  {
+    return _localModelUri;
+  }
+  
+  /**
+   * Sets the local (sharedNode's) menu Model's Uri.
+   * 
+   * @param localModelUri - String the root, top-level menu model's Uri.
+   */
+  public void setModelUri(String localModelUri)
+  {
+    _localModelUri = localModelUri;
+  }
+
+  /**
+   * Sets the treeModel map key used to get a cached treeModel 
+   * from the MenuContentHandlerImpl.
+   * 
+   * Note: this is set from the XMLMenuModel BEFORE parsing begins
+   * 
+   * @param uri String path to the menu model's metadata
+   */
+  public void setTreeModelKey(String uri)
+  {
+    _currentTreeModelMapKey = uri;
+  }
+  
+  //=======================================================================
+  // Package Private Methods
+  //=======================================================================
+  /**
+   * Gets the MenuContentHandlerImpl's id.
+   * 
+   * This is set in the MenuContentHandlerImpl's Constructor
+   * and is used to ensure that the all resource bundle keys
+   * and node ids are unique.
+   * 
+   * @return String handler id.
+   */
+  String getHandlerId()
+  {
+    return _handlerId;
+  }
+    
+  /**
+   * Returns the hashmap key for a resource bundle.
+   * 
+   * This the value of the "var" attribute for the menu root node
+   * from the menu's metadata
+   * 
+   * @return String hashmap key.
+   */
+  String getBundleKey()
+  {
+    return _resBundleKey;
+  }
+  
+  //=======================================================================
+  // Private Methods
+  //=======================================================================
+  
+  /**
+   * Creates a MenuNode from attribute list.
+   *
+   * @return MenuNode used in the Menu List.
+   */
+  private MenuNode _createMenuNode ()
+  {
+    // Get generic attributes
+    
+    // If the node has rendered = false, do not create it.
+    // This is a security risk and cannot be allowed
+    String renderedStr = _getAndRemoveAttrValue(_RENDERED_ATTR);
+    
+    // We do not create nodes whose rendered attr is false
+    // and if the Root model or the local model's (sharedNode 
+    // model) says that nodes whose rendered attribute is false
+    // should not be created, then we don't either.
+    //
+    // This default value of false (don't create nodes whose
+    // rendered attr is false) can be overridden by the 
+    // XMLMenuModel's managed property, createHiddenNodes.  
+    // Typically this is done in faces-config.xml
+    //
+    if (   "false".equals(renderedStr)
+        && (   !getRootModel().getCreateHiddenNodes()
+            || !getModel().getCreateHiddenNodes()
+           )
+       )
+    {
+      return null;
+    }
+      
+    String label       = _getAndRemoveAttrValue(_LABEL_ATTR);
+    String icon        = _getAndRemoveAttrValue(_ICON_ATTR);
+    String disabledStr = _getAndRemoveAttrValue(_DISABLED_ATTR);
+    String readOnlyStr = _getAndRemoveAttrValue(_READONLY_ATTR);
+    String accessKey   = _getAndRemoveAttrValue(_ACCESSKEY_ATTR);
+    String labelAndAccessKey   = _getAndRemoveAttrValue(_LABEL_AND_ACCESSKEY_ATTR);
+    String id          = _getAndRemoveAttrValue(_ID_ATTR);
+    String visibleStr  = _getAndRemoveAttrValue(_VISIBLE_ATTR);
+    
+    MenuNode menuNode = 
+        (  _currentNodeStyle == MenuConstants.NODE_STYLE_ITEM
+         ? _createItemNode()
+         : _createGroupNode()
+        );
+  
+    // Set the generic attributes
+    menuNode.setLabel(label);
+    menuNode.setIcon(icon);
+    menuNode.setDisabled(disabledStr);
+    menuNode.setRendered(renderedStr);
+    menuNode.setReadOnly(readOnlyStr);
+    menuNode.setAccessKey(accessKey);
+    menuNode.setId(id);
+    menuNode.setVisible(visibleStr);
+    
+    if (labelAndAccessKey != null)
+      menuNode.setLabelAndAccessKey(labelAndAccessKey);
+
+    // Set the Any Attributes Attrlist
+    if (_attrList.getLength() > 0)
+    {
+      menuNode.setCustomPropList(_attrList);
+    }
+    
+    return menuNode;
+  }
+  
+  /**
+    * Creates an itemNode from attribute list obtained by parsing an 
+    * itemNode menu metadata entry.
+    * 
+    * @return Node of type ItemNode.
+    */
+  private ItemNode _createItemNode()
+  {
+    // Create the itemNode  
+    ItemNode itemNode = new ItemNode();
+
+    String action         = _getAndRemoveAttrValue(_ACTION_ATTR);
+    String actionListener = _getAndRemoveAttrValue(_ACTIONLISTENER_ATTR);
+    String launchListener = _getAndRemoveAttrValue(_LAUNCHLISTENER_ATTR);
+    String returnListener = _getAndRemoveAttrValue(_RETURNLISTENER_ATTR);
+    String immediate      = _getAndRemoveAttrValue(_IMMEDIATE_ATTR);
+    String useWindow      = _getAndRemoveAttrValue(_USEWINDOW_ATTR);
+    String windowHeight   = _getAndRemoveAttrValue(_WINDOWHEIGHT_ATTR);
+    String windowWidth    = _getAndRemoveAttrValue(_WINDOWWIDTH_ATTR);
+    String defaultFocusPathStr = _getAndRemoveAttrValue(_DEFAULT_FOCUS_PATH_ATTR);
+    String focusViewId    = _getAndRemoveAttrValue(_FOCUS_VIEWID_ATTR);
+
+    // Former Destination node attrs
+    String destination = _getAndRemoveAttrValue(_DESTINATION_ATTR);
+    String targetFrame = _getAndRemoveAttrValue(_TARGETFRAME_ATTR);
+    
+    // An item node with one of two(2) possible values:
+    // 1) outcome
+    // 2) EL method binding  (which can return either a URI or 
+    //    an outcome
+
+    // Set its properties - null is ok.
+    itemNode.setAction(action);
+    itemNode.setActionListener(actionListener);
+    itemNode.setLaunchListener(launchListener);
+    itemNode.setReturnListener(returnListener);
+    itemNode.setImmediate(immediate);
+    itemNode.setUseWindow(useWindow);
+    itemNode.setWindowHeight(windowHeight);
+    itemNode.setWindowWidth(windowWidth);
+    itemNode.setFocusViewId(focusViewId);
+    itemNode.setDefaultFocusPath(defaultFocusPathStr);
+
+    // Former destination node attrs
+    itemNode.setDestination(destination);
+    itemNode.setTargetFrame(targetFrame);
+    
+    return itemNode;
+  }
+
+  /**
+    * Creates a GroupNode from attribute list passed obtained by parsing
+    * a GroupNode menu metadata entry.
+    * 
+    * @return Node of type GroupNode
+    */
+  private GroupNode _createGroupNode()
+  {
+    // Create the GroupNode    
+    GroupNode groupNode = new GroupNode();
+    String idRef = _getAndRemoveAttrValue(_IDREF_ATTR);
+      
+    // Set its attributes - null is ok
+    groupNode.setIdRef(idRef);
+    
+    return groupNode;
+  }
+
+  /**
+   * Saves all information needed for parsing and building model data 
+   * before recursing into the new model of a sharedNode.
+   * 
+   * Note: if you add a new push in this method, you must also add
+   * a corresponding pop in _restoreModelData() below in the correct order.
+   */
+  private void _saveModelData()
+  {
+    if (_saveDataStack == null)
+    {
+      _saveDataStack = new Stack();
+    }
+
+    // DO NOT CHANGE THE ORDER HERE.  IT MUST MATCH
+    // "pushes" DONE BELOW in _restoreModelData.
+    int nodeDepthSave       = _nodeDepth;
+    ArrayList menuNodesSave = new ArrayList(_menuNodes);
+    
+    
+    ArrayList menuListSave  = (  _menuList != null
+                               ? new ArrayList(_menuList)
+                               : null
+                              );
+                              
+    String mapTreeKeySave   = _currentTreeModelMapKey;
+    String localModelUriSave   = _localModelUri;
+    String handlerId = _handlerId;
+    String resBundleName = _resBundleName;
+    String resBundleKey = _resBundleKey;
+    _saveDataStack.push(nodeDepthSave);
+    _saveDataStack.push(menuNodesSave);
+    _saveDataStack.push(menuListSave);
+    _saveDataStack.push(mapTreeKeySave);        
+    _saveDataStack.push(localModelUriSave);        
+    _saveDataStack.push(handlerId);        
+    _saveDataStack.push(resBundleName);        
+    _saveDataStack.push(resBundleKey);            
+  }
+
+  /**
+   * Restores data needed for parsing and building model data
+   * as execution returns from creating a sharedNode child menu model.
+   *
+   * Note: if you add a new pop in this method, you must also add
+   * a corresponding push in _saveModelData() above in the correct order.
+   */
+  private void _restoreModelData()
+  {
+    // DO NOT CHANGE THE ORDER HERE.  IT MUST MATCH
+    // "pushes" DONE ABOVE in _saveModelData.
+    _resBundleKey           = (String) _saveDataStack.pop();
+    _resBundleName          = (String) _saveDataStack.pop();
+    _handlerId              = (String) _saveDataStack.pop();
+    _localModelUri          = (String) _saveDataStack.pop();
+    _currentTreeModelMapKey = (String) _saveDataStack.pop();
+    _menuList               = (ArrayList) _saveDataStack.pop();
+    _menuNodes              = (ArrayList) _saveDataStack.pop();
+    _nodeDepth              = ((Integer)_saveDataStack.pop()).intValue();    
+  }
+  
+  /**
+   * Gets the specified attribute's value from the Attributes List
+   * passed in by the parser.  Also removes this attribute so that 
+   * once we are finished processing and removing all the known 
+   * attributes, those left are custom attributes.
+   * 
+   * @param attrName
+   * @return String value of the attribute in the Attributes List.
+   */
+  private String _getAndRemoveAttrValue(String attrName)  
+  {
+    int idx = _attrList.getIndex(attrName);
+    
+    if (idx == -1)
+      return null;
+      
+    String attrValue = _attrList.getValue(idx);
+    _attrList.removeAttribute(idx);
+    return attrValue;
+  }
+
+  /*=========================================================================
+   * Menu Model Data Structure section.
+   * ======================================================================*/
+  /**
+   * Traverses the tree and builds the model's viewIdFocusPathMap, 
+   * nodeFocusPathMap, and _idNodeMap
+   * 
+   * @param tree
+   */
+  private void _addToMaps(
+    TreeModel tree,
+    Map viewIdFocusPathMap,
+    Map nodeFocusPathMap,
+    Map idNodeMap)
+  {
+    for ( int i = 0; i < tree.getRowCount(); i++)
+    {
+      tree.setRowIndex(i);
+
+      // Get the node
+      MenuNode node = (MenuNode) tree.getRowData();
+
+      // Get its focus path
+      ArrayList focusPath = (ArrayList)tree.getRowKey();
+      
+      // Get the focusViewId of the node
+      Object viewIdObject = node.getFocusViewId(); 
+      
+      if (viewIdObject != null)
+      {          
+        // Put this entry in the nodeFocusPathMap
+        nodeFocusPathMap.put(node, (Object)focusPath);
+
+        // Does this viewId already exist in the _viewIdFocusPathMap?
+        ArrayList existingFpArrayList = 
+            (ArrayList) viewIdFocusPathMap.get(viewIdObject);
+        
+        if (existingFpArrayList == null)
+        {
+          // This is a node with a unique focusViewId.  Simply create
+          // and Arraylist and add the focusPath as the single entry
+          // to the focus path ArrayList.  Then put the focusPath 
+          // ArrayList in the focusPath HashMap.
+          ArrayList<ArrayList> fpArrayList = new ArrayList<ArrayList>();
+          fpArrayList.add(focusPath);
+          viewIdFocusPathMap.put(viewIdObject, (Object)fpArrayList);
+        }
+        else
+        {
+          // This is a node that points to the same viewId as at least one 
+          // other node.
+          
+          // If the node's defaultFocusPath is set to true, we move it to
+          // the head of the ArrayList. The 0th element of the list is 
+          // always returned when navigation to a viewId occurs from outside 
+          // the menu model (that is _currentNode is null)
+          boolean defFocusPath = node.getDefaultFocusPath();
+          
+          if (defFocusPath)
+          {
+            existingFpArrayList.add(0, (Object)focusPath);
+          }
+          else
+          {
+            existingFpArrayList.add((Object)focusPath);
+          }              
+        }
+      }
+      
+      // Get the Id of the node
+      String idProp = node.getId(); 
+      
+      if (idProp != null)
+      {
+        idNodeMap.put(idProp, node);
+      }
+      
+      if (tree.isContainer() && !tree.isContainerEmpty())
+      {
+        tree.enterContainer();
+        _addToMaps(tree, viewIdFocusPathMap, nodeFocusPathMap, idNodeMap);
+        tree.exitContainer();
+      }
+    }
+  }  
+  
+
+  /**
+   * getStream - Opens an InputStream to the provided URI.
+   * 
+   * @param uri - String uri to a data source.
+   * @return InputStream to the data source.
+   */
+  private InputStream _getStream(String uri)
+  {
+    try
+    {
+      // Open the metadata  
+      FacesContext context = FacesContext.getCurrentInstance();
+      URL url = context.getExternalContext().getResource(uri);
+      return url.openStream();
+    }
+    catch (Exception ex)
+    {
+      _LOG.severe("Exception opening URI " + uri, ex);
+      return null;
+    }    
+  }
+
+
+  //========================================================================
+  // Private variables
+  //========================================================================
+  
+  private List   _menuNodes;
+  private List   _menuList;
+  private String _currentTreeModelMapKey;
+  private int    _nodeDepth;
+  private int    _skipDepth = -1;
+  private String _currentNodeStyle;
+  private String _handlerId;
+  private String _resBundleKey;
+  private String _resBundleName;
+  private AttributesImpl _attrList;
+  private Map<String, TreeModel> _treeModelMap;
+  private Stack  _saveDataStack;
+  private Map    _viewIdFocusPathMap;
+  private Map    _nodeFocusPathMap;
+  private Map    _idNodeMap;
+
+  
+  // Menu model Uri's
+  private String _rootModelUri  = null;
+  private String _localModelUri = null;
+
+  // Nodes
+  private final static String _GROUP_NODE        = "groupNode";
+  private final static String _ITEM_NODE         = "itemNode";
+  private final static String _SHARED_NODE       = "sharedNode";
+  private final static String _ROOT_NODE         = "menu";
+  
+  // Attributes    
+  private final static String _LABEL_ATTR        = "label";
+  private final static String _RENDERED_ATTR     = "rendered";
+  private final static String _ID_ATTR           = "id";
+  private final static String _IDREF_ATTR        = "idref";
+  private final static String _ICON_ATTR         = "icon";
+  private final static String _DISABLED_ATTR     = "disabled";
+  private final static String _DESTINATION_ATTR  = "destination";
+  private final static String _ACTION_ATTR       = "action";
+  private final static String _REF_ATTR          = "ref";    
+  private final static String _READONLY_ATTR     = "readOnly";
+  private final static String _VAR_ATTR          = "var";
+  private final static String _RES_BUNDLE_ATTR   = "resourceBundle";
+  private final static String _FOCUS_VIEWID_ATTR = "focusViewId";
+  private final static String _ACCESSKEY_ATTR    = "accessKey";
+  private final static String _LABEL_AND_ACCESSKEY_ATTR = "labelAndAccessKey";
+  private final static String _TARGETFRAME_ATTR  = "targetframe";
+  private final static String _ACTIONLISTENER_ATTR = "actionListener";
+  private final static String _LAUNCHLISTENER_ATTR = "launchListener";
+  private final static String _RETURNLISTENER_ATTR = "returnListener";
+  private final static String _IMMEDIATE_ATTR      = "immediate";
+  private final static String _USEWINDOW_ATTR      = "useWindow";
+  private final static String _WINDOWHEIGHT_ATTR   = "windowHeight";
+  private final static String _WINDOWWIDTH_ATTR    = "windowWidth";
+  private final static String _DEFAULT_FOCUS_PATH_ATTR  = "defaultFocusPath";
+  private final static String _VISIBLE_ATTR        = "visible";
+    
+  private final static TrinidadLogger _LOG = 
+                        TrinidadLogger.createTrinidadLogger(MenuContentHandlerImpl.class);
+  
+} // endclass MenuContentHandlerImpl



Mime
View raw message