You are viewing a plain text version of this content. The canonical link for it is here.
Posted to adffaces-commits@incubator.apache.org by sl...@apache.org on 2006/10/01 17:26:57 UTC

svn commit: r451772 [1/8] - in /incubator/adffaces/trunk/trinidad: src/site/xdoc/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xht...

Author: slessard
Date: Sun Oct  1 10:26:55 2006
New Revision: 451772

URL: http://svn.apache.org/viewvc?view=rev&rev=451772
Log:
Applied patch for ADFFACES-60 with some small changes to compile with the new public rendering API.

Modified:
    incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinSelectors.java
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/resources/META-INF/adf/styles/base-desktop.xss
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/resources/META-INF/adf/styles/minimal-desktop.xss
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimal-golden.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimalIE-golden.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimalIERtl-golden.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimalInacc-golden.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimalSaf-golden.xml
    incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimalScrRdr-golden.xml

Modified: incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml?view=diff&rev=451772&r1=451771&r2=451772
==============================================================================
--- incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml (original)
+++ incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml Sun Oct  1 10:26:55 2006
@@ -2954,7 +2954,52 @@
           </tr>
         </table>
       </subsection>
-      <subsection name="tr:processTrain Component">
+      <subsection name="tr:train Component">
+        <table>
+          <tr>
+            <th colspan="2">
+              <i>Properties</i>
+            </th>
+          </tr>
+          <tr>
+            <th>Name</th>
+            <th>Description</th>
+          </tr>
+          <tr>
+            <td>-ora-render-parent-train</td>
+            <td>Boolean value that specifies if parent train icons should be 
+                rendered if this train is a sub-train. A sub-train is a process
+                not located at the root of its TreeModel.</td>
+          </tr>
+          <tr>
+            <td>-ora-visible-stop-count</td>
+            <td>Strictly positive integer value specifying the maximum amount of 
+                stops visible at a time.</td>
+          </tr>
+        </table>
+        <table>
+          <tr>
+            <th colspan="2">
+              <i>Aliases</i>
+            </th>
+          </tr>
+          <tr>
+            <th>Name</th>
+            <th>Description</th>
+          </tr>
+          <tr>
+            <td>.AFTrainContent:alias</td>
+            <td>Styles all train station content.</td>
+          </tr>
+          <tr>
+            <td>.AFTrainIconCell:alias</td>
+            <td>Styles all train station icons.</td>
+          </tr>
+          <tr>
+            <td>.AFTrainJoin:alias</td>
+            <td>Styles all train station joins.</td>
+          </tr>
+        </table>
         <table>
           <tr>
             <th colspan="2">
@@ -2966,33 +3011,215 @@
             <th>Description</th>
           </tr>
           <tr>
-            <td>af|processTrain::step-active</td>
-            <td>Styles the active train station.</td>
+            <td>af|train</td>
+            <td>Styles the whole train.</td>
+          </tr>
+          <tr>
+            <td>af|train::stop</td>
+            <td>Styles a train stop. A stop include both the icon and its content.
+                This selector is often combined with one or more station pseudo-classes like 
+                :selected, :unvisited, :disabled, :read-only and .p_AFVisited and 
+                used in descendant selector, e.g. "af|train::stop:unvisited af|train::link" 
+                would style the link generated for an unvisited stop.</td>
+          </tr>
+          <tr>
+            <td>af|train::stop-icon-cell</td>
+            <td>Styles the icon section of the stop. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainIconCell:alias.
+                </td>
           </tr>
           <tr>
-            <td>af|processTrain::step-visited</td>
-            <td>Styles the visited train stations.</td>
+            <td>af|train::stop-content</td>
+            <td>Styles the content section of the stop. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainContent:alias.
+                </td>
           </tr>
           <tr>
-            <td>af|processTrain::step-disabled</td>
-            <td>Styles the disabled train stations.</td>
+            <td>af|train::join</td>
+            <td>Styles the join between stop icons. This selector is combinable with state 
+                pseudo-classes, except :selected. This selector includes .AFTrainJoin:alias.
+                </td>
           </tr>
           <tr>
-            <td>af|processTrain::step-unvisited</td>
-            <td>Styles the unvisited train stations.</td>
+            <td>af|train::overflow-start</td>
+            <td>Styles a train start overflow. A start overflow is generated when the amount 
+                of steps in the process is higher than the amount of visibled stop defined by
+                the -ora-visible-stop-count property. In LTR mode, this selector represents 
+                the left side overflow (previous step group). This selector includes both the 
+                icon and its content. This selector is combinable with state pseudo-classes.</td>
           </tr>
           <tr>
-            <td>.AFTrainStation:alias</td>
-            <td>Style that is included by all of the af|processTrain* style
-                classes.</td>
+            <td>af|train::overflow-start-icon-cell</td>
+            <td>Styles the icon section of the overflow-start. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainIconCell:alias.
+                </td>
           </tr>
           <tr>
-            <td>af|processTrain::step-visited af|processTrain:: link</td>
-            <td>Styles the visited train stations links.</td>
+            <td>af|train::overflow-start-content</td>
+            <td>Styles the content section of the overflow-start. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainContent:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::overflow-end</td>
+            <td>Styles a train end overflow. A end overflow is generated when the amount 
+                of steps in the process is higher than the amount of visibled stop defined by
+                the -ora-visible-stop-count property. In LTR mode, this selector represents 
+                the right side overflow (next step group). This selector includes both the 
+                icon and its content. This selector is combinable with state pseudo-classes.</td>
+          </tr>
+          <tr>
+            <td>af|train::overflow-end-icon-cell</td>
+            <td>Styles the icon section of the overflow-end. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainIconCell:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::overflow-end-content</td>
+            <td>Styles the content section of the overflow-end. This selector is combinable
+                with state pseudo-classes. This selector includes .AFTrainContent:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::join-overflow</td>
+            <td>Styles the join between overflows and stop icons. This selector is combinable
+                with state pseudo-classes, except :selected. This selector includes
+                .AFTrainJoin:alias.</td>
+          </tr>
+          <tr>
+            <td>af|train::parent-start</td>
+            <td>Styles a train start parent. A parent is generated when the current train is
+                not located at the root of its TreeModel and that -ora-render-parent-train 
+                property is set to true. In LTR mode this will be rendered to the left of the 
+                first step's stop. This selector includes both the icon and its content. 
+                This selector is NOT combinable with state pseudo-classes.</td>
+          </tr>
+          <tr>
+            <td>af|train::parent-start-icon-cell</td>
+            <td>Styles the icon section of the parent-start. This selector is NOT combinable
+                with state pseudo-classes. This selector includes .AFTrainIconCell:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::parent-start-content</td>
+            <td>Styles the content section of the parent-start. This selector is NOT combinable
+                with state pseudo-classes. This selector includes .AFTrainContent:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::parent-end</td>
+            <td>Styles a train end parent. A parent is generated when the current train is
+                not located at the root of its TreeModel and that -ora-render-parent-train 
+                property is set to true. In LTR mode this will be rendered to the right of 
+                the last step's stop. This selector includes both the icon and its content. 
+                This selector is NOT combinable with state pseudo-classes.</td>
+          </tr>
+          <tr>
+            <td>af|train::parent-end-icon-cell</td>
+            <td>Styles the icon section of the parent-end. This selector is NOT combinable
+                with state pseudo-classes. This selector includes .AFTrainIconCell:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::parent-end-content</td>
+            <td>Styles the content section of the parent-end. This selector is NOT combinable
+                with state pseudo-classes. This selector includes .AFTrainContent:alias.
+                </td>
+          </tr>
+          <tr>
+            <td>af|train::join-parent</td>
+            <td>Styles the join between parent and stop icons. This selector is NOT combinable
+                with state pseudo-classes. This selector includes .AFTrainJoin:alias.</td>
+          </tr>
+          <tr>
+            <td>af|train::link</td>
+            <td>Styles the links generated within the stop's content. This selector is
+                NOT combinable with state pseudo-classes</td>
+          </tr>
+        </table>
+        <table>
+          <tr>
+            <th colspan="2">
+              <i>Icons</i>
+            </th>
+          </tr>
+          <tr>
+            <th>Name</th>
+            <th>Description</th>
           </tr>
           <tr>
-            <td>af|processTrain::step-unvisited af|processTrain::link</td>
-            <td>Styles the unvisited train stations links.</td>
+            <td colspan="2">
+              Stop and overflow icons are combinable with one or more state pseudo-classes. 
+              For example, it's possible to build icons for a very specific situations like 
+              a read-only visited stop being different from a read-only unvisited stop. The 
+              valid classes are:
+              <ul>
+                <li>:visited for steps that were already completed (assumed to be all steps 
+                    before the current one, this might change in the future);</li>
+                <li>:selected for the current step. This state is not available to overflows;
+                    </li>
+                <li>:unvisited for steps that were not completed by the user (assumed to be 
+                    all steps after the current one, this might change in the future);</li>
+                <li>:read-only for steps that cannot be accessed directly by the user by 
+                    clicking on the link (clicking on the icon is not implemented at this 
+                    time). This state is combinable with any of the previous three states, 
+                    but must be placed after them, e.g. :selected:read-only-icon is valid, 
+                    while :read-only:selected is not;</li>
+                <li>:disabled for steps that cannot be accessed by the user using the link
+                    and should theorically not be accessible at all until some condition is 
+                    met. This state has absolute priority and is not combinable with any other
+                    state.</li>
+              </ul>
+            </td>
+          </tr>
+          <tr>
+            <td>af|train::stop&lt;states&gt;-icon</td>
+            <td>Define the icons for stops. At least one state must be specified. 
+                For example:
+                <ul>
+                  <li>VALID: af|train::stop:selected-icon;</li>
+                  <li>INVALID: af|train::stop-icon;</li>
+                </ul></td>
+          </tr>
+          <tr>
+            <td>af|train::overflow-start&lt;states&gt;-icon</td>
+            <td>Define the icons for overflows toward the previous step group. At least one 
+                state must be specified. 
+                For example:
+                <ul>
+                  <li>VALID: af|train::overflow-start:read-only-icon;</li>
+                  <li>INVALID: af|train::overflow-start-icon;</li>
+                </ul></td>
+          </tr>
+          <tr>
+            <td>af|train::overflow-end&lt;states&gt;-icon</td>
+            <td>Define the icons for overflows toward the next step group. At least one 
+                state must be specified. 
+                For example:
+                <ul>
+                  <li>VALID: af|train::overflow-end:read-only-icon;</li>
+                  <li>INVALID: af|train::overflow-end-icon;</li>
+                </ul></td>
+          </tr>
+          <tr>
+            <td>af|train::parent-start&lt;states&gt;-icon</td>
+            <td>Define the icons for the parent train that include this train.
+                It appears before the first stop's icon. This icon is NOT 
+                combinable with state pseudo-classes. For example: 
+                <ul>
+                  <li>VALID: af|train::parent-start-icon;</li>
+                  <li>INVALID: af|train::parent-start:read-only-icon;</li>
+                </ul></td>
+          </tr>
+          <tr>
+            <td>af|train::parent-end&lt;states&gt;-icon</td>
+            <td>Define the icons for the parent train that follow the parent train
+                including this train. It appears after the last stop's icon. This 
+                icon is NOT combinable with state pseudo-classes. For example: 
+                <ul>
+                  <li>VALID: af|train::parent-end-icon;</li>
+                  <li>INVALID: af|train::parent-end:read-only-icon;</li>
+                </ul></td>
           </tr>
         </table>
       </subsection>
@@ -3119,7 +3346,7 @@
           </tr>
         </table>
       </subsection>
-      <subsection name="tr:selectInputDate Component">
+      <subsection name="tr:inputDate Component">
         <table>
           <tr>
             <th colspan="2">

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.java?view=diff&rev=451772&r1=451771&r2=451772
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.java Sun Oct  1 10:26:55 2006
@@ -18,8 +18,14 @@
 
 
 import java.io.IOException;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
+import java.util.TreeMap;
 
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
@@ -29,11 +35,14 @@
 import org.apache.myfaces.trinidad.bean.FacesBean;
 import org.apache.myfaces.trinidad.component.UIXProcess;
 import org.apache.myfaces.trinidad.component.core.nav.CoreTrain;
-import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.context.Agent;
 import org.apache.myfaces.trinidad.context.FormData;
 import org.apache.myfaces.trinidad.context.RenderingContext;
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.skin.Icon;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.OutputUtils;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.ProcessUtils;
+import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinProperties;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinSelectors;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.XhtmlConstants;
 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.XhtmlRenderer;
@@ -41,7 +50,7 @@
 
 /**
  * Renderer for process train components
- * * <p>
+ * 
  */
 public class TrainRenderer
   extends XhtmlRenderer
@@ -144,49 +153,78 @@
 
   @Override
   protected void encodeAll(
-    FacesContext context, 
+    FacesContext     context, 
     RenderingContext arc, 
-    UIComponent component, 
-    FacesBean bean)
+    UIComponent      component, 
+    FacesBean        bean)
     throws IOException
   {
+    if(!(component instanceof UIXProcess))
+    {
+      throw new ClassCastException("TrainRenderer can only renders instances of " + 
+                                   UIXProcess.class.getName() + 
+                                   ", found " + 
+                                   component.getClass().getName());
+    }
+    
     UIXProcess process = (UIXProcess) component;
     UIComponent stamp = process.getNodeStamp();
 
     if (stamp != null)
     {
-      Object oldPath = process.getRowKey();
-      Object newPath = null;
-      boolean isNewPath = _setNewPath(process);
-      if (isNewPath)
+      Train train = new Train(context, arc, process, stamp);
+      try
       {
+        process.setRowKey(train.getFocusRowKey());
+        
+        // Renders some fields and scripts
+        _renderHiddenFields(context, arc, train);
+        
         ResponseWriter writer = context.getResponseWriter();
-
-        TrainRenderer.TrainState trainState = 
-        _getTrainState(context, arc, process);
-        _renderHiddenFields(context, arc, trainState);
-
-        writer.startElement("table", component);
-        OutputUtils.renderLayoutTableAttributes(context, arc, "0", null);
-        writer.writeAttribute("align", "center", null);
-        newPath = process.getRowKey();
-        process.setRowKey(oldPath);
+        
+        // Need to render the frame even if there's no visible station
+        // to support PPR.
+        writer.startElement(XhtmlConstants.TABLE_ELEMENT, component);
+        process.setRowKey(train.getInitialRowKey());
         renderId(context, component);
-        process.setRowKey(newPath);
         renderAllAttributes(context, arc, bean);
-
-        int length = process.getRowCount();
-
-        if (length == 0)
-          return;
-
-        _encodeChildren(context, arc, process, stamp, trainState, length);
-
-        writer.endElement("table");
-
-        process.setRowKey(oldPath);
+        // Does not seem to be needed and this is not XHTML 1.0 Strict compliant
+        // writer.writeAttribute("align", "center", null);
+        
+        if(!train.getStations().isEmpty())
+        {
+          // There're visible stations currently, let render them.
+          writer.startElement(XhtmlConstants.TABLE_BODY_ELEMENT, null);
+          _renderTrain(context, arc, process, bean, stamp, train);
+          writer.endElement(XhtmlConstants.TABLE_BODY_ELEMENT);
+        }
+        
+        writer.endElement(XhtmlConstants.TABLE_ELEMENT);
       }
+      finally
+      {
+        // Always restore the model, whatever happened
+        process.setRowKey(train.getInitialRowKey());
+      }
+    }
+    else
+    {
+      _LOG.warning("Train expect a nodeStamp facet, " +
+          "no such facet was found for train " + component);
     }
+    /*
+      _encodeChildren(context, arc, process, stamp, trainState, length);
+    */
+  }
+  
+  @Override
+  protected void renderAllAttributes(
+      FacesContext     context,
+      RenderingContext arc,
+      FacesBean        bean) throws IOException
+  {
+    super.renderAllAttributes(context, arc, bean);
+    OutputUtils.renderLayoutTableAttributes(context, arc, "0", null);
   }
 
   /**
@@ -195,603 +233,1631 @@
    */
   @Override
   protected void renderStyleAttributes(
-    FacesContext context, 
+    FacesContext     context, 
     RenderingContext arc, 
-    FacesBean bean)
+    FacesBean        bean)
     throws IOException
   {
-    renderStyleAttributes(context, arc, bean, 
-                          SkinSelectors.AF_PROCESS_TRAIN_STYLE_CLASS);
+    renderStyleAttributes(context, 
+                          arc, 
+                          bean, 
+                          SkinSelectors.AF_TRAIN_ROOT_STYLE_CLASS);
+  }
+  
+  private void _preRenderIconBlock(
+      FacesContext     context,
+      RenderingContext arc) throws IOException
+  {
+    ResponseWriter writer = context.getResponseWriter();
+    
+    // Icon cell
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+
+    // Icons need to be in a table to stretch well
+    writer.startElement(XhtmlConstants.TABLE_ELEMENT, null);
+    OutputUtils.renderLayoutTableAttributes(context, arc, "0", null);
+    writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, "width: 100%", null);
+    
+    writer.startElement(XhtmlConstants.TABLE_BODY_ELEMENT, null);
+    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
+  }
+  
+  private void _postRenderIconBlock(FacesContext context) throws IOException
+  {
+    ResponseWriter writer = context.getResponseWriter();
+    
+    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
+    writer.endElement(XhtmlConstants.TABLE_BODY_ELEMENT);
+    writer.endElement(XhtmlConstants.TABLE_ELEMENT);
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
   }
 
-
-  /**
-  * Initialize the station state
-  */
-  private void _initializeStationState(
-    FacesContext context, 
+  private void _renderHiddenFields(
+    FacesContext     context, 
     RenderingContext arc, 
-    TrainRenderer.TrainState train, 
-    TrainRenderer.StationState station, 
-    int currVisChildIndex, 
-    int prevVisChildIndex, 
-    int nextVisChildIndex, 
-    boolean isCurrChildDisabled, 
-    boolean isPrevChildDisabled, 
-    boolean isNextChildDisabled)
+    Train            train)
+    throws IOException
   {
-    station.isPreviousLink = false;
-    station.isMoreLink = false;
-    station.isDisabled = false;
-    station.isNextDisabled = false;
-    station.isPrevDisabled = false;
-    station.index = currVisChildIndex;
+    if((train.getFormName() != null) && supportsScripting(arc))
+    {
+      // render hidden fields to hold the form data
+      FormData formData = arc.getFormData();
+      if (formData != null)
+      {
+        formData.addNeededValue(XhtmlConstants.EVENT_PARAM);
+        formData.addNeededValue(XhtmlConstants.SOURCE_PARAM);
+        formData.addNeededValue(XhtmlConstants.VALUE_PARAM);
+        formData.addNeededValue(XhtmlConstants.SIZE_PARAM);
+      }
 
-    // train.startIndex is the index into the List that is the
-    // start of the train. The algorithm is dependent upon the BLAF spec.
-    if (currVisChildIndex == train.startIndex - 1)
+      // Render script submission code.
+      ProcessUtils.renderNavSubmitScript(context, arc);
+    }
+  }
+  
+  private void _renderContentRowLtr(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      UIComponent      stamp,
+      Train            train) throws IOException
+  {
+    ParentTrain parentTrain = train.getParentTrain();
+    
+    // Render parent start
+    if(parentTrain != null && parentTrain.hasParentStart())
+    {
+      _renderParentContent(context, arc, parentTrain.getParentStart());
+    }
+    
+    for(Station station : train.getStations())
+    {
+      _renderStationContent(context, arc, process, stamp, station);
+    }
+    
+    // Render parent end
+    if(parentTrain != null && parentTrain.hasParentEnd())
     {
-      station.isPreviousLink = true;
+      _renderParentContent(context, arc, parentTrain.getParentEnd());
     }
-    else if (currVisChildIndex == train.startIndex + _MAX_NUM_LINK_INDEX)
+  }
+  
+  private void _renderContentRowRtl(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      UIComponent      stamp,
+      Train            train) throws IOException
+  {
+    ParentTrain parentTrain = train.getParentTrain();
+    
+    // Render parent start
+    if(parentTrain != null && parentTrain.hasParentEnd())
+    {
+      _renderParentContent(context, arc, parentTrain.getParentEnd());
+    }
+    
+    List<Station>         stations = train.getStations();
+    ListIterator<Station> iterator = stations.listIterator(stations.size());
+    while(iterator.hasPrevious())
+    {
+      _renderStationContent(context, arc, process, stamp, iterator.previous());
+    }
+    
+    // Render parent end
+    if(parentTrain != null && parentTrain.hasParentStart())
     {
-      station.isMoreLink = true;
+      _renderParentContent(context, arc, parentTrain.getParentStart());
     }
+  }
+  
+  private void _renderIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      List<String>     iconNames,
+      String           shortDesc,
+      String           styleClass,
+      String           iconStyleClass,
+      List<String>     stateStyleClasses) throws IOException
+  {
+    ResponseWriter writer = context.getResponseWriter();
 
-    // selected nodes cannot be disabled,
-    // so don't bother getting disabled attribute for the selected node
-
-    if (currVisChildIndex != NO_CHILD_INDEX && 
-        currVisChildIndex != train.selectedIndex)
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+    
+    stateStyleClasses.add(styleClass);
+    stateStyleClasses.add(iconStyleClass);
+    
+    renderStyleClasses(context, 
+                       arc, 
+                       stateStyleClasses.toArray(_EMPTY_STRING_ARRAY));
+    
+    if(iconNames != null)
+    {
+      // Render the first valid icon found. The list should be in 
+      // decreasing priority order.
+      for(String iconName : iconNames)
+      {
+        Icon icon = arc.getIcon(iconName);
+        if(icon != null)
+        {
+          OutputUtils.renderIcon(context, arc, icon, shortDesc, null);
+          break;
+        }
+      }
+    }
+    
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
+  }
+  
+  private void _renderIconRowLtr(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      UIComponent      stamp,
+      Train            train) throws IOException
+  {
+    ParentTrain parentTrain = train.getParentTrain();
+    
+    // Render parent start
+    if(parentTrain != null && parentTrain.hasParentStart())
+    {
+      _renderParentStartLtr(context, arc, process, train);
+    }
+    
+    for(Station station : train.getStations())
+    {
+      _renderStationIconLtr(context, arc, process, station);
+    }
+    
+    // Render parent end
+    if(parentTrain != null && parentTrain.hasParentEnd())
     {
-      station.isDisabled = isCurrChildDisabled;
+      _renderParentEndLtr(context, arc, process, train);
     }
-
-    // get disabled information about the previous and the next child.
-    // selectedIndex cannot act disabled
-    //
-    if (prevVisChildIndex != NO_CHILD_INDEX && 
-        prevVisChildIndex != train.selectedIndex)
+  }
+  
+  private void _renderIconRowRtl(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      UIComponent      stamp,
+      Train            train) throws IOException
+  {
+    ParentTrain parentTrain = train.getParentTrain();
+    
+    // Render parent end
+    if(parentTrain != null && parentTrain.hasParentEnd())
+    {
+      _renderParentEndRtl(context, arc, process, train);
+    }
+    
+    List<Station>         stations = train.getStations();
+    ListIterator<Station> iterator = stations.listIterator(stations.size());
+    while(iterator.hasPrevious())
+    {
+      _renderStationIconRtl(context, arc, process, iterator.previous());
+    }
+    
+    // Render parent start
+    if(parentTrain != null && parentTrain.hasParentStart())
     {
-      station.isPrevDisabled = isPrevChildDisabled;
+      _renderParentStartRtl(context, arc, process, train);
     }
-
-    if (nextVisChildIndex != NO_CHILD_INDEX && 
-        nextVisChildIndex != train.selectedIndex)
+    
+  }
+  
+  private void _renderJoin(
+      FacesContext     context,
+      RenderingContext arc,
+      String           stateStyleClass,
+      boolean          overflow) throws IOException
+  {
+    if(stateStyleClass == _STATE_PARENT)
+    {
+      _renderJoin(context,
+                  arc,
+                  SkinSelectors.AF_TRAIN_PARENT_JOIN_STYLE_CLASS,
+                  null);
+    }
+    else if(overflow)
+    {
+      _renderJoin(context,
+                  arc,
+                  SkinSelectors.AF_TRAIN_OVERFLOW_JOIN_STYLE_CLASS,
+                  stateStyleClass);
+    }
+    else
     {
-      station.isNextDisabled = isNextChildDisabled;
-
+      _renderJoin(context,
+                  arc,
+                  SkinSelectors.AF_TRAIN_JOIN_STYLE_CLASS,
+                  stateStyleClass);
     }
+  }
+  
+  private void _renderJoin(
+      FacesContext     context,
+      RenderingContext arc,
+      String           joinStyleClass,
+      String           stateStyleClass) throws IOException
+  {
+    ResponseWriter writer = context.getResponseWriter();
 
-    //
-    // get the selected and visited flags for our node
-    //
-    station.isSelected = (currVisChildIndex == train.selectedIndex);
-    station.isVisited = (currVisChildIndex <= train.maxVisitedIndex);
-    station.isNextVisited = (currVisChildIndex < train.maxVisitedIndex);
-    station.isNext = (currVisChildIndex == (train.maxVisitedIndex + 1));
-    // if previous station is "next", and disabled, mark this as "next".
-    if ((currVisChildIndex - 1 == (train.maxVisitedIndex + 1)) && 
-        (station.isPrevDisabled))
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+    renderStyleClasses(context, 
+                       arc, 
+                       new String[]{
+                         joinStyleClass, 
+                         stateStyleClass});
+    
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
+  }
+  
+  private void _renderJoinIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      String           stateStyleClass,
+      boolean          overflow) throws IOException
+  {
+    if(stateStyleClass == _STATE_PARENT)
+    {
+      _renderJoinIconBlock(context,
+                           arc,
+                           SkinSelectors.AF_TRAIN_PARENT_JOIN_STYLE_CLASS,
+                           null);
+    }
+    else if(overflow)
+    {
+      _renderJoinIconBlock(context,
+                           arc,
+                           SkinSelectors.AF_TRAIN_OVERFLOW_JOIN_STYLE_CLASS,
+                           stateStyleClass);
+    }
+    else
     {
-      station.isNext = true;
+      _renderJoinIconBlock(context,
+                           arc,
+                           SkinSelectors.AF_TRAIN_JOIN_STYLE_CLASS,
+                           stateStyleClass);
     }
   }
-
-  /**
-   * Returns the MAX_VISITED_ATTR
-   * @todo =-=jmw Hopefully the controller will tell us this someday.
-   */
-  private static Object _getMaxVisited(
-    RenderingContext arc, 
-    UIComponent component)
+  
+  private void _renderJoinIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      String           joinStyleClass,
+      String           stateStyleClass) throws IOException
   {
-    // return component.getAttributes().get("maxVisited");
-    return null;
-  }
+    ResponseWriter writer = context.getResponseWriter();
 
-  /**
-   * Get the maxVisited attribute from the node and return it.
-   */
-  private int _getMaxVisitedIndex(
-    RenderingContext arc, 
-    UIComponent component)
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+    writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, "width: 50%", null);
+    renderStyleClasses(context, 
+                       arc, 
+                       new String[]{
+                         joinStyleClass, 
+                         stateStyleClass});
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
+  }
+  
+  private void _renderParentContent(
+      FacesContext     context, 
+      RenderingContext arc,
+      Station          parent) throws IOException
   {
-    int maxVisitedIndex = NO_CHILD_INDEX;
-    Integer maxVisited = (Integer) _getMaxVisited(arc, component);
-    if (maxVisited != null)
-    {
-      maxVisitedIndex = maxVisited.intValue();
+    ResponseWriter writer = context.getResponseWriter();
+    
+    String  baseStyleClass = parent.getBaseStyleClass();
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+    writer.writeAttribute(XhtmlConstants.COLSPAN_ATTRIBUTE, "3", null);
+    renderStyleClasses(context, 
+                       arc, 
+                       new String[]{
+                         baseStyleClass,
+                         baseStyleClass + _SUFFIX_CONTENT});
+    
+    /* -= Simon =-
+     * FIXME HACK for MSIE CSS bug involving composite style classes.
+     *       Since the bug is most obvious with join background images
+     *       I hard code background-image to none to fix it.
+     *       See Jira for issue ADFFACES-206.
+     */
+    if(arc.getAgent().getAgentName().equalsIgnoreCase(Agent.AGENT_IE))
+    {
+      writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, 
+                            "background-image:none;", 
+                            null);
     }
-    return maxVisitedIndex;
+    
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
   }
-
-  /**
-   * Return what the starting index into the stations List.
-   */
-  private int _getStartIndex(int numPages, int originalSelectedIndex)
+  
+  private void _renderParentEnd(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train,
+      String           leftState,
+      String           rightState) throws IOException
+  {
+    // Add join
+    _renderJoin(context, arc, leftState, false);
+    
+    // Icon cell
+    _preRenderIconBlock(context, arc);
+        
+    // Add join
+    _renderJoinIconBlock(context, arc, leftState, false);
+    
+    // Add the parent's stop icon
+    _renderParentEndIconBlock(context, arc, process, train);
+    
+    // Add join
+    _renderJoinIconBlock(context, arc, rightState, false);
+        
+    // End icon cell
+    _postRenderIconBlock(context);
+    
+    // Add join
+    _renderJoin(context, arc, rightState, false);
+  }
+  
+  private void _renderParentEndLtr(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    _renderParentEnd(context, 
+                     arc, 
+                     process, 
+                     train, 
+                     _STATE_PARENT, 
+                     null);
+  }
+  
+  private void _renderParentEndRtl(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    _renderParentEnd(context, 
+                     arc, 
+                     process, 
+                     train, 
+                     null, 
+                     _STATE_PARENT);
+  }
+  
+  private void _renderParentEndIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    assert train.getParentTrain().hasParentEnd();
+
+    Station parent = train.getParentTrain().getParentEnd();
+    
+    process.setRowKey(parent.getRowKey());
+    
+    _renderStationIconBlock(context, arc, process, parent);
+    
+    // Restore model
+    process.setRowKey(train.getInitialRowKey());
+  }
+  
+  private void _renderParentStartIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    assert train.getParentTrain().hasParentStart();
+
+    Station parent = train.getParentTrain().getParentStart();
+    
+    process.setRowKey(parent.getRowKey());
+
+    _renderStationIconBlock(context, arc, process, parent);
+    
+    // Restore model
+    process.setRowKey(train.getInitialRowKey());
+  }
+  
+  private void _renderParentStart(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train,
+      String           leftState,
+      String           rightState) throws IOException
+  {
+    // Add join
+    _renderJoin(context, arc, leftState, false);
+    
+    // Icon cell
+    _preRenderIconBlock(context, arc);
+        
+    // Add join
+    _renderJoinIconBlock(context, arc, leftState, false);
+    
+    // Add the parent's stop icon
+    _renderParentStartIconBlock(context, arc, process, train);
+    
+    // Add join
+    _renderJoinIconBlock(context, arc, rightState, false);
+        
+    _postRenderIconBlock(context);
+    // End icon cell
+    
+    // Add join
+    _renderJoin(context, arc, rightState, false);
+  }
+  
+  private void _renderParentStartLtr(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    _renderParentStart(context, 
+                       arc, 
+                       process, 
+                       train, 
+                       null, 
+                       _STATE_PARENT);
+  }
+  
+  private void _renderParentStartRtl(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Train            train) throws IOException
+  {
+    _renderParentStart(context, 
+                       arc, 
+                       process, 
+                       train, 
+                       _STATE_PARENT, 
+                       null);
+  }
+  
+  private void _renderStationContent(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      UIComponent      stamp,
+      Station          station) throws IOException
   {
-    int currentMinIndex = 0;
-
-    if (numPages <= _MAX_NUM_LINK_INDEX)
-      return currentMinIndex;
-
-    int selectedIndex = originalSelectedIndex;
-    int currentMaxIndex = _MAX_NUM_LINK_INDEX - 1;
-
-    if (selectedIndex < currentMaxIndex)
+    ResponseWriter writer = context.getResponseWriter();
+    
+    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
+    
+    writer.writeAttribute(XhtmlConstants.COLSPAN_ATTRIBUTE, "3", null);
+    
+    String baseStyleClass = station.getBaseStyleClass();
+    
+    List<String> stateStyleClasses = station.getStates();
+    stateStyleClasses.add(baseStyleClass);
+    stateStyleClasses.add(baseStyleClass + _SUFFIX_CONTENT);
+
+    renderStyleClasses(context, 
+                       arc, 
+                       stateStyleClasses.toArray(_EMPTY_STRING_ARRAY));
+        
+    /* -= Simon =-
+     * FIXME HACK for MSIE CSS bug involving composite style classes.
+     *       Since the bug is most obvious with join background images
+     *       I hard code background-image to none to fix it.
+     *       See Jira for issue ADFFACES-206.
+     */
+    if(arc.getAgent().getAgentName().equalsIgnoreCase(Agent.AGENT_IE))
+    {
+      writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, 
+                            "background-image:none;", 
+                            null);
+    }
+    
+    Map<String, String> originalMap = arc.getSkinResourceKeyMap();
+    
+    // Init the model
+    process.setRowIndex(station.getRowIndex());
+    try
     {
-      return currentMinIndex; //0
+      arc.setSkinResourceKeyMap(_RESOURCE_KEY_MAP);
+      encodeChild(context, stamp);
     }
-
-    // the algorithm below works, but I thought it was too cryptic
-    // return (selectedIndex -
-    //            (((selectedIndex-1)%(_MAX_NUM_LINK_INDEX-2))+1));
-
-    // loop until the selectedIndex range is found or
-    // we have gone past the number of nodes in the train.
-    // Then we'll know what index to start the visible portion of the train.
-    while (numPages > currentMaxIndex)
+    finally
     {
-      currentMinIndex = currentMaxIndex - 1;
-      currentMaxIndex += (_MAX_NUM_LINK_INDEX - 2);
-      if (selectedIndex > currentMinIndex && 
-          selectedIndex < currentMaxIndex)
-        return currentMinIndex;
+      arc.setSkinResourceKeyMap(originalMap);
     }
-
-    return currentMinIndex;
+    
+    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
   }
-
-
-  /**
-   * Gather up the train state: selectedIndex, maxVisitedIndex, startIndex,
-   *  isSubTrain, formName, id,.
-   *  This way all the parameters we need to pass around to various methods
-   *  are all in one place, the TrainState
-   * @param arc RenderingContext
-   * @param context FacesContext
-   * @param process the UIXProcess component.
-   * @return
-   */
-  private TrainState _getTrainState(
-    FacesContext context, 
-    RenderingContext arc, 
-    UIXProcess process)
-  {
-    TrainRenderer.TrainState state = 
-      new TrainRenderer.TrainState();
-
-    state.selectedIndex = process.getRowIndex();
-
-    // get highest node in train visited
-    state.maxVisitedIndex = _getMaxVisitedIndex(arc, process);
-
-    // default to selectedIndex if it wasn't set
-    // or if it was set to be greater than selectedIndex
-    if (state.maxVisitedIndex == NO_CHILD_INDEX || 
-        state.maxVisitedIndex < state.selectedIndex)
-    {
-      state.maxVisitedIndex = state.selectedIndex;
-    }
-
-    int totalPages = process.getRowCount();
-    state.startIndex = _getStartIndex(totalPages, state.selectedIndex);
-
-    state.subTrain = _isSubTrain(process);
-
-    state.formName = arc.getFormData().getName();
-
-    String id = process.getClientId(context);
-    state.id = (id != null)? id: null;
-
-    return state;
+  
+  private void _renderStationIcon(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Station          station,
+      String           leftJoinState,
+      String           rightJoinState,
+      boolean          overflowLeft,
+      boolean          overflowRight) throws IOException
+  {
+    // Add join
+    _renderJoin(context, arc, leftJoinState, overflowLeft);
+    
+    // Icon cell
+    _preRenderIconBlock(context, arc);
+        
+    // Add join
+    _renderJoinIconBlock(context, arc, leftJoinState, overflowLeft);
+    
+    // Add the parent's stop icon
+    _renderStationIconBlock(context, arc, process, station);
+    
+    // Add join
+    _renderJoinIconBlock(context, arc, rightJoinState, overflowRight);
+        
+    // End icon cell
+    _postRenderIconBlock(context);
+    
+    // Add join
+    _renderJoin(context, arc, rightJoinState, overflowRight);
   }
-
-
-  /**
-   * Get the subTrain attribute from the node and return it.
-   */
-  private boolean _isSubTrain(UIXProcess component)
-  {
-    Object focusRowKey = component.getFocusRowKey();
-    if (focusRowKey != null && (component.getDepth(focusRowKey) > 0))
-      return true;
-
-    return false;
+  
+  private void _renderStationIconBlock(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Station          station) throws IOException
+  {
+    process.setRowIndex(station.getRowIndex());
+
+    String baseStyleClass = station.getBaseStyleClass();
+    
+    _renderIconBlock(context, 
+                     arc, 
+                     station.getIconNames(), 
+                     station.getLabel(),
+                     baseStyleClass,
+                     baseStyleClass + _SUFFIX_ICON_CELL,
+                     station.getStates());
   }
-
-
-  private void _encodeChildren(
-    FacesContext context, 
-    RenderingContext arc, 
-    UIXProcess process, 
-    UIComponent stamp, 
-    TrainState train, 
-    int length)
-    throws IOException
+  
+  private void _renderStationIconLtr(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Station          station) throws IOException
+  {
+    _renderStationIcon(context, 
+                       arc, 
+                       process, 
+                       station, 
+                       station.getStartJoinState(), 
+                       station.getEndJoinState(),
+                       station.hasPrevious() && station.getPrevious().isOverflowStart(),
+                       station.hasNext()     && station.getNext().isOverflowEnd());
+  }
+  
+  private void _renderStationIconRtl(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      Station          station) throws IOException
+  {
+    _renderStationIcon(context, 
+                       arc, 
+                       process, 
+                       station, 
+                       station.getEndJoinState(), 
+                       station.getStartJoinState(),
+                       station.hasNext()     && station.getNext().isOverflowEnd(),
+                       station.hasPrevious() && station.getPrevious().isOverflowStart());
+  }
+  
+  private void _renderTrain(
+      FacesContext     context, 
+      RenderingContext arc,
+      UIXProcess       process,
+      FacesBean        bean,
+      UIComponent      stamp,
+      Train            train) throws IOException
   {
-
     ResponseWriter writer = context.getResponseWriter();
 
-    // start FOR SUBTRAIN
-    // If subTrain, add a row and on the first and last cells, render
-    // a border which looks like a sub-train
-    if (train.subTrain)
-    {
-      _renderSubTrainRow(context, arc, train, length, writer);
-
-      writer.startElement("tr", null);
-      writer.startElement("td", null);
-      writer.endElement("td");
+    // Start of the icon row
+    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
+    
+    if(arc.isRightToLeft())
+    {
+      _renderIconRowRtl(context, arc, process, stamp, train);
     }
     else
     {
-      writer.startElement("tr", null);
+      _renderIconRowLtr(context, arc, process, stamp, train);
     }
+    
+    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
 
-    // loop through each rendered station.
-    int lastTrainIndex = train.startIndex + _getMaxLinks(arc, process);
-
-    if (length <= lastTrainIndex)
+    // Start of the content row
+    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
+    
+    if(arc.isRightToLeft())
     {
-      lastTrainIndex = length;
+      _renderContentRowRtl(context, arc, process, stamp, train);
     }
     else
     {
-      lastTrainIndex++; // length of train is larger than the visible
-      // number of train stations, so make room for the more
-      // by adding one to the lastTrainIndex.
+      _renderContentRowLtr(context, arc, process, stamp, train);
     }
-    int currVisChildIndex = Math.max(0, train.startIndex - 1);
-    boolean isPrevVisChildDisabled = false;
-    boolean isCurrVisChildDisabled = false;
-    boolean isNextVisChildDisabled = false;
-
-    process.setRowIndex(currVisChildIndex);
-
-    isCurrVisChildDisabled = 
-        Boolean.TRUE.equals(stamp.getAttributes().get("disabled"));
-    // getBooleanAttributeValue(context, stamp, DISABLED_ATTR, false);
-
-    for (; currVisChildIndex < lastTrainIndex; currVisChildIndex++)
+    
+    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
+  }
+  
+  private static class Train
+  {
+    public Train(
+        FacesContext     context, 
+        RenderingContext arc, 
+        UIXProcess       process,
+        UIComponent      stamp)
+    {
+      // Save the model state
+      int maxVisitedIndex  = _getMaxVisitedIndex(arc, process);
+      int activeIndex      = _loadStations(process, stamp, maxVisitedIndex);
+      int visibleStopCount = _getVisibleStopCount(arc);
+      
+      _formName     = arc.getFormData().getName();
+      _isSubTrain   = _loadIsSubTrain(process);
+      
+      if(!_stations.isEmpty())
+      {
+        // There's something visible in the train
+        if(_stations.size() > visibleStopCount)
+        {
+          // We have overflow, let resolve it
+          _resolveOverflow(visibleStopCount, activeIndex);
+        }
+        else
+        {
+          // No overflow, yay!
+          _resolveStandard();
+        }
+        
+        _initLabels(arc, process, stamp);
+        _initParentTrain(arc, process, stamp);
+      }
+    }
+    
+    public Object getFocusRowKey()
     {
-      //
-      // get index of the child and
-      // determine if it is within the range in which it will be rendered.
-      //
-      int prevVisChildIndex = 
-        ((currVisChildIndex == 0)? NO_CHILD_INDEX: currVisChildIndex - 1);
-      int nextVisChildIndex = 
-        ((currVisChildIndex == length - 1)? NO_CHILD_INDEX: 
-         currVisChildIndex + 1);
-
-      process.setRowIndex(nextVisChildIndex);
-
-      isNextVisChildDisabled = 
-          Boolean.TRUE.equals(stamp.getAttributes().get("disabled"));
-
-      process.setRowIndex(currVisChildIndex);
-
-      // initialized state of the station
-      TrainRenderer.StationState station = train.station;
-      _initializeStationState(context, arc, train, station, 
-                             currVisChildIndex, prevVisChildIndex, 
-                             nextVisChildIndex, isCurrVisChildDisabled, 
-                             isPrevVisChildDisabled, 
-                             isNextVisChildDisabled);
-
-      // set up for next pass
-      isPrevVisChildDisabled = isCurrVisChildDisabled;
-      isCurrVisChildDisabled = isNextVisChildDisabled;
-
-      Object label = stamp.getAttributes().get("text");
-
-      String currVisChildText = null;
-
-      // Get text from link, or Previous or More text if appropriate
-      currVisChildText = _getTextForStation(arc, station, label);
-
-      String currVisChildID = null;
-
-      process.setRowIndex(currVisChildIndex);
-      _renderLink(context, arc, stamp, writer, train, currVisChildText, 
-                 currVisChildID, station);
-
+      return _focusRowKey;
     }
-
-    if (train.subTrain)
+    
+    public String getFormName()
     {
-      writer.startElement("td", null);
-      writer.endElement("td");
+      return _formName;
+    }
+    
+    public Object getInitialRowKey()
+    {
+      return _initialRowKey;
+    }
+    
+    public ParentTrain getParentTrain()
+    {
+      return _parent;
+    }
+    
+    public List<Station> getStations()
+    {
+      return _stations;
+    }
+    
+    public boolean isSubTrain()
+    {
+      return _isSubTrain;
+    }
+    
+    private void _createStation(
+        UIXProcess  process,
+        UIComponent stamp,
+        int         index,
+        boolean     active,
+        boolean     visited)
+    {
+      process.setRowIndex(index);
+      if(stamp.isRendered())
+      {
+        // The station will be visible.
+        _stations.add(new Station(this,
+                                  stamp, 
+                                  index, 
+                                  process.getRowKey(), 
+                                  active, 
+                                  visited));
+      }
     }
 
-    writer.endElement("tr");
-  }
-
-  /**
-   * Renders the link under the train node
-   *
-   */
-  private void _renderLink(
-    FacesContext context, 
-    RenderingContext arc, 
-    UIComponent stamp, 
-    ResponseWriter writer, 
-    TrainRenderer.TrainState train, 
-    String currVisChildText, 
-    String currVisChildID, 
-    TrainRenderer.StationState station)
-    throws IOException
-  {
-    //
-    // Write the link under the train node.
-    //
-    writer.startElement("td", null);
-    writer.writeAttribute("colspan", "2", null);
-
-    String styleClass = 
-      (station.isSelected)? SkinSelectors.AF_PROCESS_TRAIN_ACTIVE_STYLE_CLASS: 
-      (station.isDisabled && !station.isMoreLink)? 
-      SkinSelectors.AF_PROCESS_TRAIN_DISABLED_STYLE_CLASS: 
-      (station.isVisited)? 
-      SkinSelectors.AF_PROCESS_TRAIN_VISITED_STYLE_CLASS: 
-      SkinSelectors.AF_PROCESS_TRAIN_UNVISITED_STYLE_CLASS;
-
-    renderStyleClass(context, arc, styleClass);
+    /**
+     * Get the maxVisited attribute from the node and return it.
+     */
+    private int _getMaxVisitedIndex(
+      RenderingContext arc, 
+      UIComponent component)
+    {
+      int maxVisitedIndex = NO_CHILD_INDEX;
+      Integer maxVisited = (Integer) _getMaxVisited(arc, component);
+      if (maxVisited != null)
+      {
+        maxVisitedIndex = maxVisited.intValue();
+      }
+      return maxVisitedIndex;
+    }
 
-    Map<String, String> originalResourceKeyMap = arc.getSkinResourceKeyMap();
-    try
+    /**
+     * Returns the MAX_VISITED_ATTR
+     * @todo =-=jmw Hopefully the controller will tell us this someday.
+     */
+    private static Object _getMaxVisited(
+      RenderingContext arc, 
+      UIComponent component)
+    {
+      // return component.getAttributes().get("maxVisited");
+      return null;
+    }
+    
+    private int _getVisibleStopCount(RenderingContext arc)
+    {
+      Object propValue = 
+        arc.getSkin().getProperty(SkinProperties.AF_TRAIN_VISIBLE_STOP_COUNT);
+      
+      if(propValue == null)
+      {
+        return DEFAULT_MAX_VISIBLE_STOP_COUNT;
+      }
+      
+      try
+      {
+        int count = Integer.parseInt(propValue.toString());
+        if(count <= 0)
+        {
+          _LOG.warning("Visible stop count must be > 0, found " + count);
+          return DEFAULT_MAX_VISIBLE_STOP_COUNT;
+        }
+        
+        return count;
+      }
+      catch(NumberFormatException e)
+      {
+        _LOG.warning("Visible stop count must be an integer, found " + propValue);
+        return DEFAULT_MAX_VISIBLE_STOP_COUNT;
+      }
+    }
+    
+    private void _initLabels(
+        RenderingContext arc, 
+        UIXProcess       process,
+        UIComponent      stamp)
     {
-      arc.setSkinResourceKeyMap(_RESOURCE_KEY_MAP);
-      _renderStamp(context, stamp);
+      for(Station s : _stations)
+      {
+        process.setRowIndex(s.getRowIndex());
+        s.initLabel(arc, stamp);
+      }
     }
-    finally
+    
+    private void _initParentTrain(
+        RenderingContext arc, 
+        UIXProcess       process,
+        UIComponent      stamp)
     {
-      arc.setSkinResourceKeyMap(originalResourceKeyMap);
+      if(_isSubTrain)
+      {
+        if(_shouldRenderParentTrain(arc))
+        {
+          _parent = new ParentTrain(arc, process, stamp, this);
+          if(!_parent.hasParentStart() && !_parent.hasParentEnd())
+          {
+            _isSubTrain = false;
+          }
+        }
+        else
+        {
+          _isSubTrain = false;
+        }
+      }
     }
-    writer.endElement("td");
-  }
-
-  /**
-   * Called to render a child.  This method does not update the
-   * rendering context (by calling pushChild() and popChild()
-   * as needed);  subclasses need to use renderIndexedChild() or
-   * renderNamedChild() for that purpose.
-   * <p>
-   * @param context the faces context
-   * @param child the child under consideration
-   */
-  private void _renderStamp(FacesContext context, UIComponent child)
-    throws IOException
-  {
-    if (child != null)
+    
+    /**
+     * Determine if this train is a sub-train.
+     */
+    private boolean _loadIsSubTrain(UIXProcess process)
     {
-      encodeChild(context, child);
-      // child.render(context);
+      Object focusRowKey = process.getFocusRowKey();
+      if (focusRowKey != null && (process.getDepth(focusRowKey) > 0))
+      {
+        return true;
+      }
+      
+      return false;
     }
-  }
-
-  /**
-  * return the string to use for the text of the station
-  * it is the text of the link or "Previous" or "More"
-  */
-  private String _getTextForStation(
-    RenderingContext arc, 
-    TrainRenderer.StationState station, 
-    Object textObj)
-  {
-    String textValue = (textObj == null)? null: textObj.toString();
-    final String currText;
+    
+    private int _loadStations(
+        UIXProcess  process,
+        UIComponent stamp,
+        int         maxVisitedIndex)
+    {
+      _initialRowKey = process.getRowKey();
+      try
+      {
+        // Set the model on the focus item
+        _focusRowKey = process.getFocusRowKey();
+        process.setRowKey(_focusRowKey);
+        
+        int count       = process.getRowCount();
+        int activeIndex = process.getRowIndex();
+        int index       = 0;
+        
+        assert activeIndex < count;
+        
+        _stations = new ArrayList<Station>(count);
+        
+        // Process visited stations
+        for(; index < activeIndex; index++)
+        {
+          _createStation(process, stamp, index, false, true);
+        }
+        
+        assert index == activeIndex;
+        
+        _createStation(process, stamp, index, true, true);
+        index++;
+        // Might have an invisible active station. Thsi is weird, but still.
+        // You never know what users want to do, but let support 
+        // it nevertheless for now. 
+        // selectedIndex is either the active station index or the index 
+        // of the station just before the selected one if active is not visible.
+        activeIndex = _stations.size() - 1;
+        
+        if(maxVisitedIndex != NO_CHILD_INDEX)
+        {
+          for(; index < maxVisitedIndex; index++)
+          {
+            _createStation(process, stamp, index, false, true);
+          }
+        }
+        
+        for(; index < count; index++)
+        {
+          _createStation(process, stamp, index, false, false);
+        }
+        
+        return activeIndex;
+      }
+      finally
+      {
+        // Restore the model's state
+        process.setRowKey(_initialRowKey);
+      }
+    }
+    
+    private void _resolveOverflow(
+        int visibleStopCount,
+        int activeIndex)
+    {
+      assert _stations != null;
+      assert activeIndex >= -1;
+      assert activeIndex < _stations.size();
+      
+      // First, resolve chaining
+      _resolveStandard();
+      
+      // We have more stations than the max available, so we have an overflow
+      // for sure.
+      if(activeIndex <= 0)
+      {
+        // Overflow to the following group only
+        _resolveOverflowEnd(visibleStopCount);
+        _stations = _stations.subList(0, visibleStopCount + 1);
+      }
+      else
+      {
+        // Get the visible group index
+        int groupIndex = activeIndex / visibleStopCount;
+        int startIndex = 0;
+        int endIndex   = _stations.size();
+        if(groupIndex > 0)
+        {
+          // We have a previous overflow
+          startIndex = groupIndex * visibleStopCount - 1;
+          _resolveOverflowStart(startIndex);
+        }
+        
+        int maxGroupIndex = (_stations.size() - 1) / visibleStopCount;
+        
+        if(groupIndex < maxGroupIndex)
+        {
+          // We have a next overflow
+          int overflowIndex = (groupIndex + 1) * visibleStopCount;
+          
+          // endIndex is exclusive
+          endIndex = overflowIndex + 1;
+          
+          _resolveOverflowEnd(overflowIndex);
+        }
 
-    if (textValue != null && !station.isPreviousLink && 
-        !station.isMoreLink)
+        _stations = _stations.subList(startIndex, endIndex);
+      }
+    }
+    
+    private void _resolveOverflowEnd(int index)
+    {
+      assert _stations != null;
+      assert index >= 0;
+      assert index < _stations.size();
+      
+      Station station = _stations.get(index);
+      station.setOverflowEnd(true);
+      if(station.hasPrevious() && station.getPrevious().isDisabled())
+      {
+        // If previous stop is disabled, so is the overflow
+        station.setDisabled(true);
+      }
+      
+      station.setNext(null);
+    }
+    
+    private void _resolveOverflowStart(int index)
+    {
+      assert _stations != null;
+      assert index >= 0;
+      assert index < _stations.size();
+      
+      Station station = _stations.get(index);
+      station.setOverflowStart(true);
+      if(station.hasNext() && station.getNext().isDisabled())
+      {
+        // If next stop is disabled, so is the overflow
+        station.setDisabled(true);
+      }
+      
+      station.setPrevious(null);
+    }
+    
+    private void _resolveStandard()
+    {
+      if(_stations.size() > 1)
+      {
+        Iterator<Station> iterator = _stations.iterator();
+        
+        Station previous = null;
+        Station current  = iterator.next();
+        Station next     = iterator.next();
+        
+        _updateStation(current, previous, next);
+        
+        while(iterator.hasNext())
+        {
+          previous = current;
+          current  = next;
+          next     = iterator.next();
+          _updateStation(current, previous, next);
+        }
+        
+        next.setPrevious(current);
+      }
+    }
+    
+    private boolean _shouldRenderParentTrain(RenderingContext arc)
+    {
+      Object propValue = 
+        arc.getSkin().getProperty(SkinProperties.AF_TRAIN_RENDER_PARENT_TRAIN);
+      
+      if(propValue == null)
+      {
+        return DEFAULT_RENDER_PARENT_TRAIN;
+      }
+      
+      return Boolean.TRUE.equals(propValue);
+    }
+    
+    private void _updateStation(
+        Station current,
+        Station previous,
+        Station next)
+    {
+      current.setPrevious(previous);
+      current.setNext(next);
+    }
+    
+    private Object        _focusRowKey;
+    private String        _formName;
+    private Object        _initialRowKey;
+    private boolean       _isSubTrain;
+    private ParentTrain   _parent;
+    private List<Station> _stations;
+  }
+
+  private static class Station
+  {
+    public Station(
+        Train  train,
+        int    index,
+        Object rowKey)
+    {
+      _rowIndex    = index;
+      _rowKey      = rowKey;
+      _active      = false;
+      _visited     = false;
+      _disabled    = false;
+      _readOnly    = false;
+      _parentEnd   = false;
+      _parentStart = false;
+      _train       = train;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public Station(
+        Train       train,
+        UIComponent stamp,
+        int         index,
+        Object      rowKey,
+        boolean     active,
+        boolean     visited)
+    {
+      Map<String, Object> attributes = stamp.getAttributes();
+      
+      _rowIndex    = index;
+      _rowKey      = rowKey;
+      _active      = active;
+      _visited     = visited;
+      _disabled    = _getBooleanAttribute(attributes, "disabled", false);
+      _readOnly    = _getBooleanAttribute(attributes, "readOnly", false);
+      _parentEnd   = false;
+      _parentStart = false;
+      _train       = train;
+    }
+    
+    public String getBaseStyleClass()
     {
-      // if we are in screen reader mode, then we must render more descriptive
-      // text.
-      // see bug 1801348 REMOVE ONE SET OF TRAIN TEXT IN ACCESSIBLE MODE
-      if (isScreenReaderMode(arc))
+      if(isOverflowEnd())
+      {
+        return SkinSelectors.AF_TRAIN_OVERFLOW_END_STYLE_CLASS;
+      }
+      else if(isOverflowStart())
+      {
+        return SkinSelectors.AF_TRAIN_OVERFLOW_START_STYLE_CLASS;
+      }
+      else if(isParentStart())
+      {
+        return SkinSelectors.AF_TRAIN_PARENT_START_STYLE_CLASS;
+      }
+      else if(isParentEnd())
       {
-        currText = _getDisabledUserText(arc, station, textValue);
+        return SkinSelectors.AF_TRAIN_PARENT_END_STYLE_CLASS;
       }
       else
       {
-        currText = textValue;
+        return SkinSelectors.AF_TRAIN_STOP_STYLE_CLASS;
       }
     }
-    else if (station.isPreviousLink)
+    
+    public String getEndJoinState()
     {
-      currText = arc.getTranslatedString(_PREVIOUS_KEY);
+      if(isOverflowEnd())
+      {
+        return null;
+      }
+      else if(!hasNext())
+      {
+        ParentTrain parent = _train.getParentTrain();
+        if(parent != null && parent.hasParentEnd())
+        {
+          return _STATE_PARENT;
+        }
+        else
+        {
+          return null;
+        }
+      }
+      else if(isDisabled() || getNext().isDisabled())
+      {
+        return _STATE_DISABLED;
+      }
+      else if(getNext().isVisited())
+      {
+        return _STATE_VISITED;
+      }
+      else
+      {
+        return _STATE_UNVISITED;
+      }
     }
-    else if (station.isMoreLink)
+    
+    public List<String> getIconNames()
     {
-      currText = arc.getTranslatedString(_MORE_KEY);
+      if(isOverflowEnd())
+      {
+        return _getOverflowEndIconNames();
+      }
+      else if(isOverflowStart())
+      {
+        return _getOverflowStartIconNames();
+      }
+      else if(isParentStart())
+      {
+        return Collections.singletonList(SkinSelectors.AF_TRAIN_PARENT_START_ICON_NAME);
+      }
+      else if(isParentEnd())
+      {
+        return Collections.singletonList(SkinSelectors.AF_TRAIN_PARENT_END_ICON_NAME);
+      }
+      else
+      {
+        return _getStopIconNames();
+      }
     }
-    else
-      currText = null;
-
-    return currText;
-  }
-
-  private String _getDisabledUserText(
-    RenderingContext arc, 
-    TrainRenderer.StationState station, 
-    String textString)
-  {
-    String altTextKey = 
-      station.isSelected? _ACTIVE_KEY: station.isVisited? _VISITED_KEY: 
-                                       _NEXT_KEY;
-
-    String[] parameters = new String[]
-      { textString };
-
-    String altText = 
-      XhtmlUtils.getFormattedString(arc.getTranslatedString(altTextKey), 
-                                    parameters);
-    return altText;
-  }
-
-  /**
-   * Returns the max number of links to show
-   */
-  private int _getMaxLinks(
-    RenderingContext arc, 
-    UIComponent component)
-  {
-    return _MAX_NUM_LINK_INDEX;
-  }
-
-  private void _renderSubTrainRow(
-    FacesContext context, 
-    RenderingContext arc, 
-    TrainRenderer.TrainState train, 
-    int length, 
-    ResponseWriter writer)
-    throws IOException
-  {
-    boolean isRTL = arc.getLocaleContext().isRightToLeft();
-
-    writer.startElement("tr", null);
-
-    if (isRTL)
-      _renderSubTrainCell(context, arc, 
-                          SkinSelectors.TRAIN_SUB_RIGHT_STYLE_CLASS, 
-                          writer);
-    else
-      _renderSubTrainCell(context, arc, 
-                          SkinSelectors.AF_PROCESS_TRAIN_SUB_START_STYLE_CLASS, 
-                          writer);
-
-    _renderSubTrainBlankCells(train, length, writer);
-
-    if (isRTL)
-      _renderSubTrainCell(context, arc, 
-                          SkinSelectors.AF_PROCESS_TRAIN_SUB_START_STYLE_CLASS, 
-                          writer);
-    else
-      _renderSubTrainCell(context, arc, 
-                          SkinSelectors.TRAIN_SUB_RIGHT_STYLE_CLASS, 
-                          writer);
-
-    writer.endElement("tr");
-  }
-
-  private void _renderSubTrainCell(
-    FacesContext context, 
-    RenderingContext arc, 
-    String style, 
-    ResponseWriter writer)
-    throws IOException
-  {
-    writer.startElement("td", null);
-    renderStyleClass(context, arc, style);
-    renderSpacer(context, arc, "14", "2");
-    writer.endElement("td");
-  }
-
-  /**
-   * renders a td with colSpan equal to the number of visible stations
-   * including the Previous and More if they are there.
-   * @param train
-   * @param length
-   * @param writer
-   * @throws IOException
-   */
-  private void _renderSubTrainBlankCells(TrainRenderer.TrainState train, 
-                                         int length, ResponseWriter writer)
-    throws IOException
-  {
-    writer.startElement("td", null);
-
-    // figure out the number of stations
-    int startIndex = Math.max(0, train.startIndex - 1);
-    int lastTrainIndex = train.startIndex + _MAX_NUM_LINK_INDEX;
-    if (length <= lastTrainIndex)
-      lastTrainIndex = length;
-    else
+    
+    public String getLabel()
     {
-      // when the length of train is larger than the visible
-      // number of train stations, we render a More link.
-      // so make room for the more
-      // by adding one to the lastTrainIndex.
-      lastTrainIndex++;
-    }
-    String numberOfStations = 
-      Integer.toString((lastTrainIndex - startIndex) * 2);
-    writer.writeAttribute("colspan", numberOfStations, null);
-    writer.endElement("td");
-  }
+      return _label;
+    }
+    
+    public Station getNext()
+    {
+      return _next;
+    }
+    
+    public Station getPrevious()
+    {
+      return _previous;
+    }
+    
+    public int getRowIndex()
+    {
+      return _rowIndex;
+    }
+    
+    public Object getRowKey()
+    {
+      return _rowKey;
+    }
+    
+    public String getStartJoinState()
+    {
+      if(isOverflowStart())
+      {
+        return null;
+      }
+      else if(!hasPrevious())
+      {
+        ParentTrain parent = _train.getParentTrain();
+        if(parent != null && parent.hasParentStart())
+        {
+          return _STATE_PARENT;
+        }
+        else
+        {
+          return null;
+        }
+      }
+      else if(isDisabled() || getPrevious().isDisabled())
+      {
+        return _STATE_DISABLED;
+      }
+      else if(isVisited())
+      {
+        return _STATE_VISITED;
+      }
+      else
+      {
+        return _STATE_UNVISITED;
+      }
+    }
+    
+    public List<String> getStates()
+    {
+      List<String> states = new ArrayList<String>(5);
+      if(isParentStart() || isParentEnd())
+      {
+        return states;
+      }
+      
+      if(isDisabled())
+      {
+        states.add(_STATE_DISABLED);
+        return states;
+      }
+      
+      if(isActive())
+      {
+        states.add(_STATE_ACTIVE);
+      }
+      else if(isVisited())
+      {
+        states.add(_STATE_VISITED);
+      }
+      else
+      {
+        states.add(_STATE_UNVISITED);
+      }
+      
+      if(isReadOnly())
+      {
+        states.add(_STATE_READ_ONLY);
+      }
+      
+      return states;
+    }
+    
+    public boolean hasNext()
+    {
+      return _next != null;
+    }
+    
+    public boolean hasPrevious()
+    {
+      return _previous != null;
+    }
 
 
-  private void _renderHiddenFields(
-    FacesContext context, 
-    RenderingContext arc, 
-    TrainState train)
-    throws IOException
-  {
-    if ((train.formName != null) && supportsScripting(arc))
+    /**
+     * return the string to use for the text of the station
+     * it is the text of the link or "Previous" or "More"
+     */
+    public void initLabel(
+      RenderingContext arc, 
+      UIComponent      stamp)
     {
-      // render hidden fields to hold the form data
-      FormData fData = arc.getFormData();
-      if (fData != null)
+      if(isOverflowStart())
       {
-        fData.addNeededValue(XhtmlConstants.EVENT_PARAM);
-        fData.addNeededValue(XhtmlConstants.SOURCE_PARAM);
-        fData.addNeededValue(XhtmlConstants.VALUE_PARAM);
-        fData.addNeededValue(XhtmlConstants.SIZE_PARAM);
+        _label = arc.getTranslatedString(_PREVIOUS_KEY);
+      }
+      else if(isOverflowEnd())
+      {
+        _label = arc.getTranslatedString(_MORE_KEY);
+      }
+      else
+      {
+        Object text = stamp.getAttributes().get("text");
+        if(text != null)
+        {
+          _label = text.toString();
+          if (isScreenReaderMode(arc))
+          {
+            _label = _getDisabledUserText(arc, _label);
+          }
+        }
+        else
+        {
+          _label = null;
+        }
       }
-
-      // Render script submission code.
-      ProcessUtils.renderNavSubmitScript(context, arc);
     }
-  }
+    
+    public boolean isActive()
+    {
+      return _active;
+    }
+    
+    public boolean isDisabled()
+    {
+      return _disabled;
+    }
+    
+    public boolean isNextDisabled()
+    {
+      return hasNext() && _next.isDisabled();
+    }
+    
+    public boolean isOverflowEnd()
+    {
+      return _overflowEnd;
+    }
+    
+    public boolean isOverflowStart()
+    {
+      return _overflowStart;
+    }
+    
+    public boolean isParentEnd()
+    {
+      return _parentEnd;
+    }
+    
+    public boolean isParentStart()
+    {
+      return _parentStart;
+    }
+    
+    public boolean isPreviousDisabled()
+    {
+      return hasPrevious() && _previous.isDisabled();
+    }
+    
+    public boolean isReadOnly()
+    {
+      return _readOnly;
+    }
+    
+    public boolean isVisited()
+    {
+      return _visited;
+    }
+    
+    public void setDisabled(boolean disabled)
+    {
+      _disabled = disabled;
+    }
+    
+    public void setNext(Station next)
+    {
+      _next = next;
+    }
+    
+    public void setOverflowEnd(boolean overflowEnd)
+    {
+      _overflowEnd = overflowEnd;
+    }
+    
+    public void setOverflowStart(boolean overflowStart)
+    {
+      _overflowStart = overflowStart;
+      _visited       = true;
+    }
+    
+    public void setParentEnd(boolean parentEnd)
+    {
+      _parentEnd = parentEnd;
+    }
+    
+    public void setParentStart(boolean parentStart)
+    {
+      _parentStart = parentStart;
+    }
+    
+    public void setPrevious(Station previous)
+    {
+      _previous = previous;
+    }
+    
+    public void setReadOnly(boolean readOnly)
+    {
+      _readOnly = readOnly;
+    }
+    
+    private boolean _getBooleanAttribute(
+        Map<String, Object> attributes,
+        String              attributeName,
+        boolean             defaultValue)
+    {
+      Object value = attributes.get(attributeName);
+      if(value == null)
+      {
+        return defaultValue;
+      }
+      
+      return Boolean.TRUE.equals(value);
+    }
 
+    private String _getDisabledUserText(
+      RenderingContext arc,
+      String           text)
+    {
+      String altTextKey;
+      if(isActive())
+      {
+        altTextKey = _ACTIVE_KEY;
+      }
+      else if(isVisited())
+      {
+        altTextKey = _VISITED_KEY;
+      }
+      else
+      {
+        altTextKey = _NEXT_KEY;
+      }
 
-  private boolean _setNewPath(UIXProcess component)
-  {
-    Object focusPath = component.getFocusRowKey();
-    component.setRowKey(focusPath);
-    return true;
+      String altText = 
+        XhtmlUtils.getFormattedString(arc.getTranslatedString(altTextKey), 
+                                      new String[]{text});
+      
+      return altText;
+    }
+    
+    private List<String> _getIconNames(String baseSelector)
+    {
+      LinkedList<String> names = new LinkedList<String>();
+
+      StringBuilder builder = new StringBuilder(64);
+      builder.append(baseSelector);
+      
+      int suffixLength = SkinSelectors.ICON_SUFFIX.length();
+      int baseIndex    = builder.length();
+      
+      builder.append(SkinSelectors.ICON_SUFFIX);
+      names.addFirst(builder.toString());
+      builder.delete(baseIndex, baseIndex + suffixLength);
+      
+      if(isDisabled())
+      {
+        builder.append(_SUFFIX_DISABLED);
+        builder.append(SkinSelectors.ICON_SUFFIX);
+        names.addFirst(builder.toString());
+      }
+      else 
+      {
+        if(isActive())
+        {
+          builder.append(_SUFFIX_ACTIVE);
+        }
+        else if(isVisited())
+        {
+          builder.append(_SUFFIX_VISITED);
+        }
+        else
+        {
+          builder.append(_SUFFIX_UNVISITED);
+        }
+        
+        baseIndex = builder.length();
+        
+        builder.append(SkinSelectors.ICON_SUFFIX);
+        names.addFirst(builder.toString());
+        builder.delete(baseIndex, baseIndex + suffixLength);
+        
+        if(isReadOnly())
+        {
+          builder.append(_SUFFIX_READ_ONLY);
+          builder.append(SkinSelectors.ICON_SUFFIX);
+          names.addFirst(builder.toString());
+        }
+      }
+      
+      return names;
+    }
+    
+    private List<String> _getOverflowEndIconNames()
+    {
+      return _getIconNames(SkinSelectors.AF_TRAIN_OVERFLOW_END_STYLE_CLASS);
+    }
+    
+    private List<String> _getOverflowStartIconNames()
+    {
+      return _getIconNames(SkinSelectors.AF_TRAIN_OVERFLOW_START_STYLE_CLASS);
+    }
+    
+    private List<String> _getStopIconNames()
+    {
+      return _getIconNames(SkinSelectors.AF_TRAIN_STOP_STYLE_CLASS);
+    }
+    
+    private boolean _active;       // Is this station the active one?
+    private boolean _disabled;     // Disabled attribute
+    private boolean _overflowEnd; // Is this station the next step set link?
+    private boolean _overflowStart; // Is this station the prev step set link?
+    private boolean _parentEnd;    // Is this station a parent end?
+    private boolean _parentStart;  // Is this station a parent start?
+    private boolean _readOnly;     // Read only attribute
+    private boolean _visited;      // Is this station visited?
+    
+    private int _rowIndex; // Row index
+    
+    private Object _rowKey; // Row key
+    
+    private String _label; // This station's label
+    
+    private Station _next;
+    private Station _previous;
+    
+    private Train _train;
   }
-
-  protected static class TrainState
+  
+  private static class ParentTrain
   {
-    public TrainState()
+    public ParentTrain(
+        RenderingContext arc,
+        UIXProcess       process,
+        UIComponent      stamp,
+        Train            train)
+    {
+      List<Station> stations     = train.getStations();
+      int           stationCount = stations.size();
+      
+      boolean hasParentStart = !stations.get(0).isOverflowStart();
+      boolean hasParentEnd   = !stations.get(stationCount - 1).isOverflowEnd();
+      
+      if(hasParentStart || hasParentEnd)
+      {
+        Object parentStartRowKey = process.getContainerRowKey();
+        process.setRowKey(parentStartRowKey);
+        int rowIndex = process.getRowIndex();
+        if(hasParentStart)
+        {
+          _parentStart = new Station(train, rowIndex, parentStartRowKey);
+          _parentStart.setParentStart(true);
+          _parentStart.initLabel(arc, stamp);
+        }
+        
+        rowIndex = rowIndex + 1;
+        
+        // Check if the parent has more steps, render it only if it does
+        hasParentEnd = rowIndex < process.getRowCount();
+        if(hasParentEnd)
+        {
+          process.setRowIndex(rowIndex);
+          _parentEnd = new Station(train, rowIndex, process.getRowKey());
+          _parentEnd.setParentEnd(true);
+          _parentEnd.initLabel(arc, stamp);
+        }
+        
+        // Restore the model
+        process.setRowKey(train.getInitialRowKey());
+      }
+    }
+    
+    public Station getParentEnd()
     {
-      station = new TrainRenderer.StationState();
+      return _parentEnd;
     }
-    public int startIndex;
-    public int maxVisitedIndex;
-    public int selectedIndex;
-    public boolean subTrain;
-    public String formName;
-    public String id;
-    public TrainRenderer.StationState station;
-  }
-
-  protected static class StationState
-  {
-
-    public boolean isSelected;
-    // is this the station that is right AFTER the selected station.
-    public boolean isNext;
-    public boolean isVisited; // has this station been visited already?
-    public boolean isPreviousLink; // is this the Previous link?
-    public boolean isMoreLink; // is this the More link?
-    public boolean isDisabled; // is this station disabled?
-    public boolean isNextDisabled; // is the next station disabled?
-    public boolean isPrevDisabled; // is the previous station disabled?
-    public boolean isNextVisited; // is the next station visited?
-    public int index; // the index of this node
-  } //end StationState
+    
+    public Station getParentStart()
+    {
+      return _parentStart;
+    }
+    
+    public boolean hasParentEnd()
+    {
+      return _parentEnd != null;
+    }
+    
+    public boolean hasParentStart()
+    {
+      return _parentStart != null;
+    }
+    
+    private Station _parentEnd;
+    private Station _parentStart;
+  }
 
-  private static final int  _MAX_NUM_LINK_INDEX = 
-    6; //number of visible links
+  /**
+   * Gives the amount of visible stops that get rendered by default if no 
+   * amount is specified by the -ora-visible-stop-count skin property.
+   */
+  public static final int DEFAULT_MAX_VISIBLE_STOP_COUNT  = 6;
+  
+  /**
+   * Determines if the parent train of sub-trains should be rendered by 
+   * default if not specified by the -ora-render-parent-train skin property.
+   */
+  public static final boolean DEFAULT_RENDER_PARENT_TRAIN = false;
+  
+  private static final String _STATE_ACTIVE    = SkinSelectors.STATE_PREFIX + "Selected";
+  private static final String _STATE_DISABLED  = SkinSelectors.STATE_PREFIX + "Disabled";
+  private static final String _STATE_PARENT    = SkinSelectors.STATE_PREFIX + "Parent";
+  private static final String _STATE_READ_ONLY = SkinSelectors.STATE_PREFIX + "ReadOnly";
+  private static final String _STATE_UNVISITED = SkinSelectors.STATE_PREFIX + "Unvisited";
+  private static final String _STATE_VISITED   = SkinSelectors.STATE_PREFIX + "Visited";
+  
+  private static final String _SUFFIX_CONTENT    = "-content";
+  private static final String _SUFFIX_ICON_CELL  = "-icon-cell";
+
+  private static final String _SUFFIX_ACTIVE     = ":selected";
+  private static final String _SUFFIX_DISABLED   = ":disabled";
+  private static final String _SUFFIX_READ_ONLY  = ":read-only";
+  private static final String _SUFFIX_UNVISITED  = ":unvisited";
+  private static final String _SUFFIX_VISITED    = ":visited";
 
   /**
- * The following keys are used to get at the corresponding translated
- * strings.
- */
+   * The following keys are used to get at the corresponding translated
+   * strings.
+   */
   private static final String _VISITED_KEY = "af_train.VISITED_TIP";
   private static final String _ACTIVE_KEY = "af_train.ACTIVE_TIP";
   private static final String _NEXT_KEY = "af_train.NEXT_TIP";
@@ -800,23 +1866,22 @@
 
   private static final TrinidadLogger _LOG = 
     TrinidadLogger.createTrinidadLogger(TrainRenderer.class);
-
-  // for now keep the OraLink/OraDisabledLink styles on the 'a', and
-  // append train link style class.
-  private static final Map<String, String> _RESOURCE_KEY_MAP = 
-    new HashMap<String, String>();
-  private static final String _TRAIN_DISABLED_LINK = 
-    SkinSelectors.LINK_DISABLED_STYLE_CLASS + " " + 
-    SkinSelectors.AF_PROCESS_TRAIN_LINK_STYLE_CLASS;
-  private static final String _TRAIN_ENABLED_LINK = 
-    SkinSelectors.LINK_STYLE_CLASS + " " + 
-    SkinSelectors.AF_PROCESS_TRAIN_LINK_STYLE_CLASS;
-  
-  static {
-    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_DISABLED_STYLE_CLASS, 
-                          _TRAIN_DISABLED_LINK);
-    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_STYLE_CLASS, 
-                          _TRAIN_ENABLED_LINK);
+  
+  private static final String[] _EMPTY_STRING_ARRAY;
+  
+  private static final Map<String, String> _RESOURCE_KEY_MAP;
+  
+  static
+  {
+    _EMPTY_STRING_ARRAY = new String[0];
+    
+    // Not adding the base link classes as before, those are a nuisance 
+    // while defining the skin since oyu cannot inhibit them as they're
+    // on the same level as the train selectors.
+    _RESOURCE_KEY_MAP = new TreeMap<String, String>();
+    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_STYLE_CLASS,
+                          SkinSelectors.AF_TRAIN_LINK_STYLE_CLASS);
+    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_DISABLED_STYLE_CLASS,
+                          SkinSelectors.AF_TRAIN_LINK_STYLE_CLASS);
   }
-
 }

Modified: incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.java
URL: http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.java?view=diff&rev=451772&r1=451771&r2=451772
==============================================================================
--- incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.java (original)
+++ incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.java Sun Oct  1 10:26:55 2006
@@ -39,14 +39,18 @@
   // FIXME: Name inconsistency, should be AF_PANEL_HEADER
   public static final String AF_PANELHEADER_INDENT_CONTENT = 
     "af|panelHeader-ora-indent-content";
+  public static final String AF_PANEL_BORDER_LAYOUT_SPACER_WIDTH =
+    "af|panelBorderLayout-ora-spacer-width";
   public static final String AF_PANEL_LIST_DEFAULT_COLUMNS =
     "af|panelList-ora-default-columns";    
   public static final String AF_TABLE_REPEAT_CONTROL_BAR =
     "af|table-ora-repeat-control-bar";
   public static final String AF_TABLE_SELECTION_BAR_IN_TABLE =
     "af|table-ora-selection-bar-in-table";
+  public static final String AF_TRAIN_RENDER_PARENT_TRAIN =
+    "af|train-ora-render-parent-train";
+  public static final String AF_TRAIN_VISIBLE_STOP_COUNT =
+    "af|train-ora-visible-stop-count";
   public static final String AF_TREE_TABLE_SPACER_WIDTH =
     "af|treeTable-ora-spacer-width";
-  public static final String AF_PANEL_BORDER_LAYOUT_SPACER_WIDTH =
-    "af|panelBorderLayout-ora-spacer-width";
 }



RE: svn commit: r451772 [1/8] - in /incubator/adffaces/trunk/trinidad: src/site/xdoc/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/desktop/ trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/renderkit/core/xh

Posted by Pavitra Subramaniam <pa...@oracle.com>.
Hello Simon,

Thanks for checking in the new train renderer. I have one comment.

- Can you update the af|train::link to be af|train::stop-link, like we agreed upon? ::link is too generic as I have to support a version where the stop icons are links besides the stop label as well.


Thanks
- Pavitra
 

> -----Original Message-----
> From: slessard@apache.org [mailto:slessard@apache.org] 
> Sent: Sunday, October 01, 2006 10:27 AM
> To: adffaces-commits@incubator.apache.org
> Subject: svn commit: r451772 [1/8] - in 
> /incubator/adffaces/trunk/trinidad: src/site/xdoc/ 
> trinidad-impl/src/main/java/org/apache/myfaces/trinidadinterna
l/renderkit/core/desktop/ trinidad-> impl/src/main/java/org/apache/myfaces/trinidadinternal/renderk
> it/core/xht...
> 
> Author: slessard
> Date: Sun Oct  1 10:26:55 2006
> New Revision: 451772
> 
> URL: http://svn.apache.org/viewvc?view=rev&rev=451772
> Log:
> Applied patch for ADFFACES-60 with some small changes to 
> compile with the new public rendering API.
> 
> Modified:
>     incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.> java
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.j> ava
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinSelectors.ja> va
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/resou
rces/META-INF/adf/styles/base-desktop.xss
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/resou
rces/META-INF/adf/styles/minimal-desktop.xss
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-minimal-> golden.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-> minimalIE-golden.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-> minimalIERtl-golden.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-> minimalInacc-golden.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-> minimalSaf-golden.xml
>     
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/test/resou
rces/org/apache/myfaces/trinidadinternal/renderkit/golden/train-> minimalScrRdr-golden.xml
> 
> Modified: 
> incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors.xml
> URL: 
> http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad
> /src/site/xdoc/skin-selectors.xml?view=diff&rev=451772&r1=4517
> 71&r2=451772
> ==============================================================
> ================
> --- 
> incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors
> .xml (original)
> +++ 
> incubator/adffaces/trunk/trinidad/src/site/xdoc/skin-selectors
> .xml Sun Oct  1 10:26:55 2006
> @@ -2954,7 +2954,52 @@
>            </tr>
>          </table>
>        </subsection>
> -      <subsection name="tr:processTrain Component">
> +      <subsection name="tr:train Component">
> +        <table>
> +          <tr>
> +            <th colspan="2">
> +              <i>Properties</i>
> +            </th>
> +          </tr>
> +          <tr>
> +            <th>Name</th>
> +            <th>Description</th>
> +          </tr>
> +          <tr>
> +            <td>-ora-render-parent-train</td>
> +            <td>Boolean value that specifies if parent train 
> icons should be
> +                rendered if this train is a sub-train. A 
> sub-train is a process
> +                not located at the root of its TreeModel.</td>
> +          </tr>
> +          <tr>
> +            <td>-ora-visible-stop-count</td>
> +            <td>Strictly positive integer value specifying 
> the maximum amount of
> +                stops visible at a time.</td>
> +          </tr>
> +        </table>
> +        <table>
> +          <tr>
> +            <th colspan="2">
> +              <i>Aliases</i>
> +            </th>
> +          </tr>
> +          <tr>
> +            <th>Name</th>
> +            <th>Description</th>
> +          </tr>
> +          <tr>
> +            <td>.AFTrainContent:alias</td>
> +            <td>Styles all train station content.</td>
> +          </tr>
> +          <tr>
> +            <td>.AFTrainIconCell:alias</td>
> +            <td>Styles all train station icons.</td>
> +          </tr>
> +          <tr>
> +            <td>.AFTrainJoin:alias</td>
> +            <td>Styles all train station joins.</td>
> +          </tr>
> +        </table>
>          <table>
>            <tr>
>              <th colspan="2">
> @@ -2966,33 +3011,215 @@
>              <th>Description</th>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-active</td>
> -            <td>Styles the active train station.</td>
> +            <td>af|train</td>
> +            <td>Styles the whole train.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::stop</td>
> +            <td>Styles a train stop. A stop include both the 
> icon and its content.
> +                This selector is often combined with one or 
> more station pseudo-classes like
> +                :selected, :unvisited, :disabled, :read-only 
> and .p_AFVisited and
> +                used in descendant selector, e.g. 
> "af|train::stop:unvisited af|train::link"
> +                would style the link generated for an 
> unvisited stop.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::stop-icon-cell</td>
> +            <td>Styles the icon section of the stop. This 
> selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainIconCell:alias.
> +                </td>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-visited</td>
> -            <td>Styles the visited train stations.</td>
> +            <td>af|train::stop-content</td>
> +            <td>Styles the content section of the stop. This 
> selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainContent:alias.
> +                </td>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-disabled</td>
> -            <td>Styles the disabled train stations.</td>
> +            <td>af|train::join</td>
> +            <td>Styles the join between stop icons. This 
> selector is combinable with state
> +                pseudo-classes, except :selected. This 
> selector includes .AFTrainJoin:alias.
> +                </td>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-unvisited</td>
> -            <td>Styles the unvisited train stations.</td>
> +            <td>af|train::overflow-start</td>
> +            <td>Styles a train start overflow. A start 
> overflow is generated when the amount
> +                of steps in the process is higher than the 
> amount of visibled stop defined by
> +                the -ora-visible-stop-count property. In LTR 
> mode, this selector represents
> +                the left side overflow (previous step 
> group). This selector includes both the
> +                icon and its content. This selector is 
> combinable with state pseudo-classes.</td>
>            </tr>
>            <tr>
> -            <td>.AFTrainStation:alias</td>
> -            <td>Style that is included by all of the 
> af|processTrain* style
> -                classes.</td>
> +            <td>af|train::overflow-start-icon-cell</td>
> +            <td>Styles the icon section of the 
> overflow-start. This selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainIconCell:alias.
> +                </td>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-visited 
> af|processTrain:: link</td>
> -            <td>Styles the visited train stations links.</td>
> +            <td>af|train::overflow-start-content</td>
> +            <td>Styles the content section of the 
> overflow-start. This selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainContent:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::overflow-end</td>
> +            <td>Styles a train end overflow. A end overflow 
> is generated when the amount
> +                of steps in the process is higher than the 
> amount of visibled stop defined by
> +                the -ora-visible-stop-count property. In LTR 
> mode, this selector represents
> +                the right side overflow (next step group). 
> This selector includes both the
> +                icon and its content. This selector is 
> combinable with state pseudo-classes.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::overflow-end-icon-cell</td>
> +            <td>Styles the icon section of the overflow-end. 
> This selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainIconCell:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::overflow-end-content</td>
> +            <td>Styles the content section of the 
> overflow-end. This selector is combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainContent:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::join-overflow</td>
> +            <td>Styles the join between overflows and stop 
> icons. This selector is combinable
> +                with state pseudo-classes, except :selected. 
> This selector includes
> +                .AFTrainJoin:alias.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-start</td>
> +            <td>Styles a train start parent. A parent is 
> generated when the current train is
> +                not located at the root of its TreeModel and 
> that -ora-render-parent-train
> +                property is set to true. In LTR mode this 
> will be rendered to the left of the
> +                first step's stop. This selector includes 
> both the icon and its content.
> +                This selector is NOT combinable with state 
> pseudo-classes.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-start-icon-cell</td>
> +            <td>Styles the icon section of the parent-start. 
> This selector is NOT combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainIconCell:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-start-content</td>
> +            <td>Styles the content section of the 
> parent-start. This selector is NOT combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainContent:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-end</td>
> +            <td>Styles a train end parent. A parent is 
> generated when the current train is
> +                not located at the root of its TreeModel and 
> that -ora-render-parent-train
> +                property is set to true. In LTR mode this 
> will be rendered to the right of
> +                the last step's stop. This selector includes 
> both the icon and its content.
> +                This selector is NOT combinable with state 
> pseudo-classes.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-end-icon-cell</td>
> +            <td>Styles the icon section of the parent-end. 
> This selector is NOT combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainIconCell:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-end-content</td>
> +            <td>Styles the content section of the 
> parent-end. This selector is NOT combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainContent:alias.
> +                </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::join-parent</td>
> +            <td>Styles the join between parent and stop 
> icons. This selector is NOT combinable
> +                with state pseudo-classes. This selector 
> includes .AFTrainJoin:alias.</td>
> +          </tr>
> +          <tr>
> +            <td>af|train::link</td>
> +            <td>Styles the links generated within the stop's 
> content. This selector is
> +                NOT combinable with state pseudo-classes</td>
> +          </tr>
> +        </table>
> +        <table>
> +          <tr>
> +            <th colspan="2">
> +              <i>Icons</i>
> +            </th>
> +          </tr>
> +          <tr>
> +            <th>Name</th>
> +            <th>Description</th>
>            </tr>
>            <tr>
> -            <td>af|processTrain::step-unvisited 
> af|processTrain::link</td>
> -            <td>Styles the unvisited train stations links.</td>
> +            <td colspan="2">
> +              Stop and overflow icons are combinable with 
> one or more state pseudo-classes.
> +              For example, it's possible to build icons for 
> a very specific situations like
> +              a read-only visited stop being different from 
> a read-only unvisited stop. The
> +              valid classes are:
> +              <ul>
> +                <li>:visited for steps that were already 
> completed (assumed to be all steps
> +                    before the current one, this might 
> change in the future);</li>
> +                <li>:selected for the current step. This 
> state is not available to overflows;
> +                    </li>
> +                <li>:unvisited for steps that were not 
> completed by the user (assumed to be
> +                    all steps after the current one, this 
> might change in the future);</li>
> +                <li>:read-only for steps that cannot be 
> accessed directly by the user by
> +                    clicking on the link (clicking on the 
> icon is not implemented at this
> +                    time). This state is combinable with any 
> of the previous three states,
> +                    but must be placed after them, e.g. 
> :selected:read-only-icon is valid,
> +                    while :read-only:selected is not;</li>
> +                <li>:disabled for steps that cannot be 
> accessed by the user using the link
> +                    and should theorically not be accessible 
> at all until some condition is
> +                    met. This state has absolute priority 
> and is not combinable with any other
> +                    state.</li>
> +              </ul>
> +            </td>
> +          </tr>
> +          <tr>
> +            <td>af|train::stop&lt;states&gt;-icon</td>
> +            <td>Define the icons for stops. At least one 
> state must be specified.
> +                For example:
> +                <ul>
> +                  <li>VALID: af|train::stop:selected-icon;</li>
> +                  <li>INVALID: af|train::stop-icon;</li>
> +                </ul></td>
> +          </tr>
> +          <tr>
> +            <td>af|train::overflow-start&lt;states&gt;-icon</td>
> +            <td>Define the icons for overflows toward the 
> previous step group. At least one
> +                state must be specified.
> +                For example:
> +                <ul>
> +                  <li>VALID: 
> af|train::overflow-start:read-only-icon;</li>
> +                  <li>INVALID: af|train::overflow-start-icon;</li>
> +                </ul></td>
> +          </tr>
> +          <tr>
> +            <td>af|train::overflow-end&lt;states&gt;-icon</td>
> +            <td>Define the icons for overflows toward the 
> next step group. At least one
> +                state must be specified.
> +                For example:
> +                <ul>
> +                  <li>VALID: 
> af|train::overflow-end:read-only-icon;</li>
> +                  <li>INVALID: af|train::overflow-end-icon;</li>
> +                </ul></td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-start&lt;states&gt;-icon</td>
> +            <td>Define the icons for the parent train that 
> include this train.
> +                It appears before the first stop's icon. 
> This icon is NOT
> +                combinable with state pseudo-classes. For example:
> +                <ul>
> +                  <li>VALID: af|train::parent-start-icon;</li>
> +                  <li>INVALID: 
> af|train::parent-start:read-only-icon;</li>
> +                </ul></td>
> +          </tr>
> +          <tr>
> +            <td>af|train::parent-end&lt;states&gt;-icon</td>
> +            <td>Define the icons for the parent train that 
> follow the parent train
> +                including this train. It appears after the 
> last stop's icon. This
> +                icon is NOT combinable with state 
> pseudo-classes. For example:
> +                <ul>
> +                  <li>VALID: af|train::parent-end-icon;</li>
> +                  <li>INVALID: 
> af|train::parent-end:read-only-icon;</li>
> +                </ul></td>
>            </tr>
>          </table>
>        </subsection>
> @@ -3119,7 +3346,7 @@
>            </tr>
>          </table>
>        </subsection>
> -      <subsection name="tr:selectInputDate Component">
> +      <subsection name="tr:inputDate Component">
>          <table>
>            <tr>
>              <th colspan="2">
> 
> Modified: 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.> java
> URL: 
> http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad
> /trinidad-impl/src/main/java/org/apache/myfaces/trinidadintern
al/renderkit/core/desktop/TrainRenderer.java?view=diff&rev=451772&r1=> 451771&r2=451772
> ==============================================================
> ================
> --- 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.> java (original)
> +++ 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/desktop/TrainRenderer.> java Sun Oct  1 10:26:55 2006
> @@ -18,8 +18,14 @@
>  
>  
>  import java.io.IOException;
> -import java.util.HashMap;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Iterator;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.ListIterator;
>  import java.util.Map;
> +import java.util.TreeMap;
>  
>  import javax.faces.component.UIComponent;
>  import javax.faces.context.FacesContext;
> @@ -29,11 +35,14 @@
>  import org.apache.myfaces.trinidad.bean.FacesBean;
>  import org.apache.myfaces.trinidad.component.UIXProcess;
>  import org.apache.myfaces.trinidad.component.core.nav.CoreTrain;
> -import org.apache.myfaces.trinidad.logging.TrinidadLogger;
> +import org.apache.myfaces.trinidad.context.Agent;
>  import org.apache.myfaces.trinidad.context.FormData;
>  import org.apache.myfaces.trinidad.context.RenderingContext;
> +import org.apache.myfaces.trinidad.logging.TrinidadLogger;
> +import org.apache.myfaces.trinidad.skin.Icon;
>  import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.OutputUtils;
>  import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.ProcessUtils;
> +import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinP
> roperties;
>  import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SkinS
> electors;
>  import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.Xhtml
> Constants;
>  import 
> org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.Xhtml
> Renderer;
> @@ -41,7 +50,7 @@
>  
>  /**
>   * Renderer for process train components
> - * * <p>
> + *
>   */
>  public class TrainRenderer
>    extends XhtmlRenderer
> @@ -144,49 +153,78 @@
>  
>    @Override
>    protected void encodeAll(
> -    FacesContext context,
> +    FacesContext     context,
>      RenderingContext arc,
> -    UIComponent component,
> -    FacesBean bean)
> +    UIComponent      component,
> +    FacesBean        bean)
>      throws IOException
>    {
> +    if(!(component instanceof UIXProcess))
> +    {
> +      throw new ClassCastException("TrainRenderer can only 
> renders instances of " +
> +                                   UIXProcess.class.getName() +
> +                                   ", found " +
> +                                   component.getClass().getName());
> +    }
> +
>      UIXProcess process = (UIXProcess) component;
>      UIComponent stamp = process.getNodeStamp();
>  
>      if (stamp != null)
>      {
> -      Object oldPath = process.getRowKey();
> -      Object newPath = null;
> -      boolean isNewPath = _setNewPath(process);
> -      if (isNewPath)
> +      Train train = new Train(context, arc, process, stamp);
> +      try
>        {
> +        process.setRowKey(train.getFocusRowKey());
> +
> +        // Renders some fields and scripts
> +        _renderHiddenFields(context, arc, train);
> +
>          ResponseWriter writer = context.getResponseWriter();
> -
> -        TrainRenderer.TrainState trainState =
> -        _getTrainState(context, arc, process);
> -        _renderHiddenFields(context, arc, trainState);
> -
> -        writer.startElement("table", component);
> -        OutputUtils.renderLayoutTableAttributes(context, 
> arc, "0", null);
> -        writer.writeAttribute("align", "center", null);
> -        newPath = process.getRowKey();
> -        process.setRowKey(oldPath);
> +
> +        // Need to render the frame even if there's no 
> visible station
> +        // to support PPR.
> +        writer.startElement(XhtmlConstants.TABLE_ELEMENT, component);
> +        process.setRowKey(train.getInitialRowKey());
>          renderId(context, component);
> -        process.setRowKey(newPath);
>          renderAllAttributes(context, arc, bean);
> -
> -        int length = process.getRowCount();
> -
> -        if (length == 0)
> -          return;
> -
> -        _encodeChildren(context, arc, process, stamp, 
> trainState, length);
> -
> -        writer.endElement("table");
> -
> -        process.setRowKey(oldPath);
> +        // Does not seem to be needed and this is not XHTML 
> 1.0 Strict compliant
> +        // writer.writeAttribute("align", "center", null);
> +
> +        if(!train.getStations().isEmpty())
> +        {
> +          // There're visible stations currently, let render them.
> +          
> writer.startElement(XhtmlConstants.TABLE_BODY_ELEMENT, null);
> +          _renderTrain(context, arc, process, bean, stamp, train);
> +          writer.endElement(XhtmlConstants.TABLE_BODY_ELEMENT);
> +        }
> +
> +        writer.endElement(XhtmlConstants.TABLE_ELEMENT);
>        }
> +      finally
> +      {
> +        // Always restore the model, whatever happened
> +        process.setRowKey(train.getInitialRowKey());
> +      }
> +    }
> +    else
> +    {
> +      _LOG.warning("Train expect a nodeStamp facet, " +
> +          "no such facet was found for train " + component);
>      }
> +    /*
> +      _encodeChildren(context, arc, process, stamp, 
> trainState, length);
> +    */
> +  }
> +
> +  @Override
> +  protected void renderAllAttributes(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      FacesBean        bean) throws IOException
> +  {
> +    super.renderAllAttributes(context, arc, bean);
> +    OutputUtils.renderLayoutTableAttributes(context, arc, "0", null);
>    }
>  
>    /**
> @@ -195,603 +233,1631 @@
>     */
>    @Override
>    protected void renderStyleAttributes(
> -    FacesContext context,
> +    FacesContext     context,
>      RenderingContext arc,
> -    FacesBean bean)
> +    FacesBean        bean)
>      throws IOException
>    {
> -    renderStyleAttributes(context, arc, bean,
> -                          
> SkinSelectors.AF_PROCESS_TRAIN_STYLE_CLASS);
> +    renderStyleAttributes(context,
> +                          arc,
> +                          bean,
> +                          SkinSelectors.AF_TRAIN_ROOT_STYLE_CLASS);
> +  }
> +
> +  private void _preRenderIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc) throws IOException
> +  {
> +    ResponseWriter writer = context.getResponseWriter();
> +
> +    // Icon cell
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +
> +    // Icons need to be in a table to stretch well
> +    writer.startElement(XhtmlConstants.TABLE_ELEMENT, null);
> +    OutputUtils.renderLayoutTableAttributes(context, arc, "0", null);
> +    writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, 
> "width: 100%", null);
> +
> +    writer.startElement(XhtmlConstants.TABLE_BODY_ELEMENT, null);
> +    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
> +  }
> +
> +  private void _postRenderIconBlock(FacesContext context) 
> throws IOException
> +  {
> +    ResponseWriter writer = context.getResponseWriter();
> +
> +    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
> +    writer.endElement(XhtmlConstants.TABLE_BODY_ELEMENT);
> +    writer.endElement(XhtmlConstants.TABLE_ELEMENT);
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
>    }
>  
> -
> -  /**
> -  * Initialize the station state
> -  */
> -  private void _initializeStationState(
> -    FacesContext context,
> +  private void _renderHiddenFields(
> +    FacesContext     context,
>      RenderingContext arc,
> -    TrainRenderer.TrainState train,
> -    TrainRenderer.StationState station,
> -    int currVisChildIndex,
> -    int prevVisChildIndex,
> -    int nextVisChildIndex,
> -    boolean isCurrChildDisabled,
> -    boolean isPrevChildDisabled,
> -    boolean isNextChildDisabled)
> +    Train            train)
> +    throws IOException
>    {
> -    station.isPreviousLink = false;
> -    station.isMoreLink = false;
> -    station.isDisabled = false;
> -    station.isNextDisabled = false;
> -    station.isPrevDisabled = false;
> -    station.index = currVisChildIndex;
> +    if((train.getFormName() != null) && supportsScripting(arc))
> +    {
> +      // render hidden fields to hold the form data
> +      FormData formData = arc.getFormData();
> +      if (formData != null)
> +      {
> +        formData.addNeededValue(XhtmlConstants.EVENT_PARAM);
> +        formData.addNeededValue(XhtmlConstants.SOURCE_PARAM);
> +        formData.addNeededValue(XhtmlConstants.VALUE_PARAM);
> +        formData.addNeededValue(XhtmlConstants.SIZE_PARAM);
> +      }
>  
> -    // train.startIndex is the index into the List that is the
> -    // start of the train. The algorithm is dependent upon 
> the BLAF spec.
> -    if (currVisChildIndex == train.startIndex - 1)
> +      // Render script submission code.
> +      ProcessUtils.renderNavSubmitScript(context, arc);
> +    }
> +  }
> +
> +  private void _renderContentRowLtr(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      UIComponent      stamp,
> +      Train            train) throws IOException
> +  {
> +    ParentTrain parentTrain = train.getParentTrain();
> +
> +    // Render parent start
> +    if(parentTrain != null && parentTrain.hasParentStart())
> +    {
> +      _renderParentContent(context, arc, 
> parentTrain.getParentStart());
> +    }
> +
> +    for(Station station : train.getStations())
> +    {
> +      _renderStationContent(context, arc, process, stamp, station);
> +    }
> +
> +    // Render parent end
> +    if(parentTrain != null && parentTrain.hasParentEnd())
>      {
> -      station.isPreviousLink = true;
> +      _renderParentContent(context, arc, parentTrain.getParentEnd());
>      }
> -    else if (currVisChildIndex == train.startIndex + 
> _MAX_NUM_LINK_INDEX)
> +  }
> +
> +  private void _renderContentRowRtl(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      UIComponent      stamp,
> +      Train            train) throws IOException
> +  {
> +    ParentTrain parentTrain = train.getParentTrain();
> +
> +    // Render parent start
> +    if(parentTrain != null && parentTrain.hasParentEnd())
> +    {
> +      _renderParentContent(context, arc, parentTrain.getParentEnd());
> +    }
> +
> +    List<Station>         stations = train.getStations();
> +    ListIterator<Station> iterator = 
> stations.listIterator(stations.size());
> +    while(iterator.hasPrevious())
> +    {
> +      _renderStationContent(context, arc, process, stamp, 
> iterator.previous());
> +    }
> +
> +    // Render parent end
> +    if(parentTrain != null && parentTrain.hasParentStart())
>      {
> -      station.isMoreLink = true;
> +      _renderParentContent(context, arc, 
> parentTrain.getParentStart());
>      }
> +  }
> +
> +  private void _renderIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      List<String>     iconNames,
> +      String           shortDesc,
> +      String           styleClass,
> +      String           iconStyleClass,
> +      List<String>     stateStyleClasses) throws IOException
> +  {
> +    ResponseWriter writer = context.getResponseWriter();
>  
> -    // selected nodes cannot be disabled,
> -    // so don't bother getting disabled attribute for the 
> selected node
> -
> -    if (currVisChildIndex != NO_CHILD_INDEX &&
> -        currVisChildIndex != train.selectedIndex)
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +
> +    stateStyleClasses.add(styleClass);
> +    stateStyleClasses.add(iconStyleClass);
> +
> +    renderStyleClasses(context,
> +                       arc,
> +                       
> stateStyleClasses.toArray(_EMPTY_STRING_ARRAY));
> +
> +    if(iconNames != null)
> +    {
> +      // Render the first valid icon found. The list should be in
> +      // decreasing priority order.
> +      for(String iconName : iconNames)
> +      {
> +        Icon icon = arc.getIcon(iconName);
> +        if(icon != null)
> +        {
> +          OutputUtils.renderIcon(context, arc, icon, 
> shortDesc, null);
> +          break;
> +        }
> +      }
> +    }
> +
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
> +  }
> +
> +  private void _renderIconRowLtr(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      UIComponent      stamp,
> +      Train            train) throws IOException
> +  {
> +    ParentTrain parentTrain = train.getParentTrain();
> +
> +    // Render parent start
> +    if(parentTrain != null && parentTrain.hasParentStart())
> +    {
> +      _renderParentStartLtr(context, arc, process, train);
> +    }
> +
> +    for(Station station : train.getStations())
> +    {
> +      _renderStationIconLtr(context, arc, process, station);
> +    }
> +
> +    // Render parent end
> +    if(parentTrain != null && parentTrain.hasParentEnd())
>      {
> -      station.isDisabled = isCurrChildDisabled;
> +      _renderParentEndLtr(context, arc, process, train);
>      }
> -
> -    // get disabled information about the previous and the 
> next child.
> -    // selectedIndex cannot act disabled
> -    //
> -    if (prevVisChildIndex != NO_CHILD_INDEX &&
> -        prevVisChildIndex != train.selectedIndex)
> +  }
> +
> +  private void _renderIconRowRtl(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      UIComponent      stamp,
> +      Train            train) throws IOException
> +  {
> +    ParentTrain parentTrain = train.getParentTrain();
> +
> +    // Render parent end
> +    if(parentTrain != null && parentTrain.hasParentEnd())
> +    {
> +      _renderParentEndRtl(context, arc, process, train);
> +    }
> +
> +    List<Station>         stations = train.getStations();
> +    ListIterator<Station> iterator = 
> stations.listIterator(stations.size());
> +    while(iterator.hasPrevious())
> +    {
> +      _renderStationIconRtl(context, arc, process, 
> iterator.previous());
> +    }
> +
> +    // Render parent start
> +    if(parentTrain != null && parentTrain.hasParentStart())
>      {
> -      station.isPrevDisabled = isPrevChildDisabled;
> +      _renderParentStartRtl(context, arc, process, train);
>      }
> -
> -    if (nextVisChildIndex != NO_CHILD_INDEX &&
> -        nextVisChildIndex != train.selectedIndex)
> +
> +  }
> +
> +  private void _renderJoin(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      String           stateStyleClass,
> +      boolean          overflow) throws IOException
> +  {
> +    if(stateStyleClass == _STATE_PARENT)
> +    {
> +      _renderJoin(context,
> +                  arc,
> +                  SkinSelectors.AF_TRAIN_PARENT_JOIN_STYLE_CLASS,
> +                  null);
> +    }
> +    else if(overflow)
> +    {
> +      _renderJoin(context,
> +                  arc,
> +                  SkinSelectors.AF_TRAIN_OVERFLOW_JOIN_STYLE_CLASS,
> +                  stateStyleClass);
> +    }
> +    else
>      {
> -      station.isNextDisabled = isNextChildDisabled;
> -
> +      _renderJoin(context,
> +                  arc,
> +                  SkinSelectors.AF_TRAIN_JOIN_STYLE_CLASS,
> +                  stateStyleClass);
>      }
> +  }
> +
> +  private void _renderJoin(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      String           joinStyleClass,
> +      String           stateStyleClass) throws IOException
> +  {
> +    ResponseWriter writer = context.getResponseWriter();
>  
> -    //
> -    // get the selected and visited flags for our node
> -    //
> -    station.isSelected = (currVisChildIndex == train.selectedIndex);
> -    station.isVisited = (currVisChildIndex <= train.maxVisitedIndex);
> -    station.isNextVisited = (currVisChildIndex < 
> train.maxVisitedIndex);
> -    station.isNext = (currVisChildIndex == 
> (train.maxVisitedIndex + 1));
> -    // if previous station is "next", and disabled, mark 
> this as "next".
> -    if ((currVisChildIndex - 1 == (train.maxVisitedIndex + 1)) &&
> -        (station.isPrevDisabled))
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +    renderStyleClasses(context,
> +                       arc,
> +                       new String[]{
> +                         joinStyleClass,
> +                         stateStyleClass});
> +
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
> +  }
> +
> +  private void _renderJoinIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      String           stateStyleClass,
> +      boolean          overflow) throws IOException
> +  {
> +    if(stateStyleClass == _STATE_PARENT)
> +    {
> +      _renderJoinIconBlock(context,
> +                           arc,
> +                           
> SkinSelectors.AF_TRAIN_PARENT_JOIN_STYLE_CLASS,
> +                           null);
> +    }
> +    else if(overflow)
> +    {
> +      _renderJoinIconBlock(context,
> +                           arc,
> +                           
> SkinSelectors.AF_TRAIN_OVERFLOW_JOIN_STYLE_CLASS,
> +                           stateStyleClass);
> +    }
> +    else
>      {
> -      station.isNext = true;
> +      _renderJoinIconBlock(context,
> +                           arc,
> +                           SkinSelectors.AF_TRAIN_JOIN_STYLE_CLASS,
> +                           stateStyleClass);
>      }
>    }
> -
> -  /**
> -   * Returns the MAX_VISITED_ATTR
> -   * @todo =-=jmw Hopefully the controller will tell us this someday.
> -   */
> -  private static Object _getMaxVisited(
> -    RenderingContext arc,
> -    UIComponent component)
> +
> +  private void _renderJoinIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      String           joinStyleClass,
> +      String           stateStyleClass) throws IOException
>    {
> -    // return component.getAttributes().get("maxVisited");
> -    return null;
> -  }
> +    ResponseWriter writer = context.getResponseWriter();
>  
> -  /**
> -   * Get the maxVisited attribute from the node and return it.
> -   */
> -  private int _getMaxVisitedIndex(
> -    RenderingContext arc,
> -    UIComponent component)
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +    writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE, 
> "width: 50%", null);
> +    renderStyleClasses(context,
> +                       arc,
> +                       new String[]{
> +                         joinStyleClass,
> +                         stateStyleClass});
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
> +  }
> +
> +  private void _renderParentContent(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      Station          parent) throws IOException
>    {
> -    int maxVisitedIndex = NO_CHILD_INDEX;
> -    Integer maxVisited = (Integer) _getMaxVisited(arc, component);
> -    if (maxVisited != null)
> -    {
> -      maxVisitedIndex = maxVisited.intValue();
> +    ResponseWriter writer = context.getResponseWriter();
> +
> +    String  baseStyleClass = parent.getBaseStyleClass();
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +    writer.writeAttribute(XhtmlConstants.COLSPAN_ATTRIBUTE, 
> "3", null);
> +    renderStyleClasses(context,
> +                       arc,
> +                       new String[]{
> +                         baseStyleClass,
> +                         baseStyleClass + _SUFFIX_CONTENT});
> +
> +    /* -= Simon =-
> +     * FIXME HACK for MSIE CSS bug involving composite style classes.
> +     *       Since the bug is most obvious with join 
> background images
> +     *       I hard code background-image to none to fix it.
> +     *       See Jira for issue ADFFACES-206.
> +     */
> +    
> if(arc.getAgent().getAgentName().equalsIgnoreCase(Agent.AGENT_IE))
> +    {
> +      writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE,
> +                            "background-image:none;",
> +                            null);
>      }
> -    return maxVisitedIndex;
> +
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
>    }
> -
> -  /**
> -   * Return what the starting index into the stations List.
> -   */
> -  private int _getStartIndex(int numPages, int originalSelectedIndex)
> +
> +  private void _renderParentEnd(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train,
> +      String           leftState,
> +      String           rightState) throws IOException
> +  {
> +    // Add join
> +    _renderJoin(context, arc, leftState, false);
> +
> +    // Icon cell
> +    _preRenderIconBlock(context, arc);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, leftState, false);
> +
> +    // Add the parent's stop icon
> +    _renderParentEndIconBlock(context, arc, process, train);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, rightState, false);
> +
> +    // End icon cell
> +    _postRenderIconBlock(context);
> +
> +    // Add join
> +    _renderJoin(context, arc, rightState, false);
> +  }
> +
> +  private void _renderParentEndLtr(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    _renderParentEnd(context,
> +                     arc,
> +                     process,
> +                     train,
> +                     _STATE_PARENT,
> +                     null);
> +  }
> +
> +  private void _renderParentEndRtl(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    _renderParentEnd(context,
> +                     arc,
> +                     process,
> +                     train,
> +                     null,
> +                     _STATE_PARENT);
> +  }
> +
> +  private void _renderParentEndIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    assert train.getParentTrain().hasParentEnd();
> +
> +    Station parent = train.getParentTrain().getParentEnd();
> +
> +    process.setRowKey(parent.getRowKey());
> +
> +    _renderStationIconBlock(context, arc, process, parent);
> +
> +    // Restore model
> +    process.setRowKey(train.getInitialRowKey());
> +  }
> +
> +  private void _renderParentStartIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    assert train.getParentTrain().hasParentStart();
> +
> +    Station parent = train.getParentTrain().getParentStart();
> +
> +    process.setRowKey(parent.getRowKey());
> +
> +    _renderStationIconBlock(context, arc, process, parent);
> +
> +    // Restore model
> +    process.setRowKey(train.getInitialRowKey());
> +  }
> +
> +  private void _renderParentStart(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train,
> +      String           leftState,
> +      String           rightState) throws IOException
> +  {
> +    // Add join
> +    _renderJoin(context, arc, leftState, false);
> +
> +    // Icon cell
> +    _preRenderIconBlock(context, arc);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, leftState, false);
> +
> +    // Add the parent's stop icon
> +    _renderParentStartIconBlock(context, arc, process, train);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, rightState, false);
> +
> +    _postRenderIconBlock(context);
> +    // End icon cell
> +
> +    // Add join
> +    _renderJoin(context, arc, rightState, false);
> +  }
> +
> +  private void _renderParentStartLtr(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    _renderParentStart(context,
> +                       arc,
> +                       process,
> +                       train,
> +                       null,
> +                       _STATE_PARENT);
> +  }
> +
> +  private void _renderParentStartRtl(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Train            train) throws IOException
> +  {
> +    _renderParentStart(context,
> +                       arc,
> +                       process,
> +                       train,
> +                       _STATE_PARENT,
> +                       null);
> +  }
> +
> +  private void _renderStationContent(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      UIComponent      stamp,
> +      Station          station) throws IOException
>    {
> -    int currentMinIndex = 0;
> -
> -    if (numPages <= _MAX_NUM_LINK_INDEX)
> -      return currentMinIndex;
> -
> -    int selectedIndex = originalSelectedIndex;
> -    int currentMaxIndex = _MAX_NUM_LINK_INDEX - 1;
> -
> -    if (selectedIndex < currentMaxIndex)
> +    ResponseWriter writer = context.getResponseWriter();
> +
> +    writer.startElement(XhtmlConstants.TABLE_DATA_ELEMENT, null);
> +
> +    writer.writeAttribute(XhtmlConstants.COLSPAN_ATTRIBUTE, 
> "3", null);
> +
> +    String baseStyleClass = station.getBaseStyleClass();
> +
> +    List<String> stateStyleClasses = station.getStates();
> +    stateStyleClasses.add(baseStyleClass);
> +    stateStyleClasses.add(baseStyleClass + _SUFFIX_CONTENT);
> +
> +    renderStyleClasses(context,
> +                       arc,
> +                       
> stateStyleClasses.toArray(_EMPTY_STRING_ARRAY));
> +
> +    /* -= Simon =-
> +     * FIXME HACK for MSIE CSS bug involving composite style classes.
> +     *       Since the bug is most obvious with join 
> background images
> +     *       I hard code background-image to none to fix it.
> +     *       See Jira for issue ADFFACES-206.
> +     */
> +    
> if(arc.getAgent().getAgentName().equalsIgnoreCase(Agent.AGENT_IE))
> +    {
> +      writer.writeAttribute(XhtmlConstants.STYLE_ATTRIBUTE,
> +                            "background-image:none;",
> +                            null);
> +    }
> +
> +    Map<String, String> originalMap = arc.getSkinResourceKeyMap();
> +
> +    // Init the model
> +    process.setRowIndex(station.getRowIndex());
> +    try
>      {
> -      return currentMinIndex; //0
> +      arc.setSkinResourceKeyMap(_RESOURCE_KEY_MAP);
> +      encodeChild(context, stamp);
>      }
> -
> -    // the algorithm below works, but I thought it was too cryptic
> -    // return (selectedIndex -
> -    //            (((selectedIndex-1)%(_MAX_NUM_LINK_INDEX-2))+1));
> -
> -    // loop until the selectedIndex range is found or
> -    // we have gone past the number of nodes in the train.
> -    // Then we'll know what index to start the visible 
> portion of the train.
> -    while (numPages > currentMaxIndex)
> +    finally
>      {
> -      currentMinIndex = currentMaxIndex - 1;
> -      currentMaxIndex += (_MAX_NUM_LINK_INDEX - 2);
> -      if (selectedIndex > currentMinIndex &&
> -          selectedIndex < currentMaxIndex)
> -        return currentMinIndex;
> +      arc.setSkinResourceKeyMap(originalMap);
>      }
> -
> -    return currentMinIndex;
> +
> +    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
>    }
> -
> -
> -  /**
> -   * Gather up the train state: selectedIndex, 
> maxVisitedIndex, startIndex,
> -   *  isSubTrain, formName, id,.
> -   *  This way all the parameters we need to pass around to 
> various methods
> -   *  are all in one place, the TrainState
> -   * @param arc RenderingContext
> -   * @param context FacesContext
> -   * @param process the UIXProcess component.
> -   * @return
> -   */
> -  private TrainState _getTrainState(
> -    FacesContext context,
> -    RenderingContext arc,
> -    UIXProcess process)
> -  {
> -    TrainRenderer.TrainState state =
> -      new TrainRenderer.TrainState();
> -
> -    state.selectedIndex = process.getRowIndex();
> -
> -    // get highest node in train visited
> -    state.maxVisitedIndex = _getMaxVisitedIndex(arc, process);
> -
> -    // default to selectedIndex if it wasn't set
> -    // or if it was set to be greater than selectedIndex
> -    if (state.maxVisitedIndex == NO_CHILD_INDEX ||
> -        state.maxVisitedIndex < state.selectedIndex)
> -    {
> -      state.maxVisitedIndex = state.selectedIndex;
> -    }
> -
> -    int totalPages = process.getRowCount();
> -    state.startIndex = _getStartIndex(totalPages, 
> state.selectedIndex);
> -
> -    state.subTrain = _isSubTrain(process);
> -
> -    state.formName = arc.getFormData().getName();
> -
> -    String id = process.getClientId(context);
> -    state.id = (id != null)? id: null;
> -
> -    return state;
> +
> +  private void _renderStationIcon(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Station          station,
> +      String           leftJoinState,
> +      String           rightJoinState,
> +      boolean          overflowLeft,
> +      boolean          overflowRight) throws IOException
> +  {
> +    // Add join
> +    _renderJoin(context, arc, leftJoinState, overflowLeft);
> +
> +    // Icon cell
> +    _preRenderIconBlock(context, arc);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, leftJoinState, overflowLeft);
> +
> +    // Add the parent's stop icon
> +    _renderStationIconBlock(context, arc, process, station);
> +
> +    // Add join
> +    _renderJoinIconBlock(context, arc, rightJoinState, 
> overflowRight);
> +
> +    // End icon cell
> +    _postRenderIconBlock(context);
> +
> +    // Add join
> +    _renderJoin(context, arc, rightJoinState, overflowRight);
>    }
> -
> -
> -  /**
> -   * Get the subTrain attribute from the node and return it.
> -   */
> -  private boolean _isSubTrain(UIXProcess component)
> -  {
> -    Object focusRowKey = component.getFocusRowKey();
> -    if (focusRowKey != null && (component.getDepth(focusRowKey) > 0))
> -      return true;
> -
> -    return false;
> +
> +  private void _renderStationIconBlock(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Station          station) throws IOException
> +  {
> +    process.setRowIndex(station.getRowIndex());
> +
> +    String baseStyleClass = station.getBaseStyleClass();
> +
> +    _renderIconBlock(context,
> +                     arc,
> +                     station.getIconNames(),
> +                     station.getLabel(),
> +                     baseStyleClass,
> +                     baseStyleClass + _SUFFIX_ICON_CELL,
> +                     station.getStates());
>    }
> -
> -
> -  private void _encodeChildren(
> -    FacesContext context,
> -    RenderingContext arc,
> -    UIXProcess process,
> -    UIComponent stamp,
> -    TrainState train,
> -    int length)
> -    throws IOException
> +
> +  private void _renderStationIconLtr(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Station          station) throws IOException
> +  {
> +    _renderStationIcon(context,
> +                       arc,
> +                       process,
> +                       station,
> +                       station.getStartJoinState(),
> +                       station.getEndJoinState(),
> +                       station.hasPrevious() && 
> station.getPrevious().isOverflowStart(),
> +                       station.hasNext()     && 
> station.getNext().isOverflowEnd());
> +  }
> +
> +  private void _renderStationIconRtl(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      Station          station) throws IOException
> +  {
> +    _renderStationIcon(context,
> +                       arc,
> +                       process,
> +                       station,
> +                       station.getEndJoinState(),
> +                       station.getStartJoinState(),
> +                       station.hasNext()     && 
> station.getNext().isOverflowEnd(),
> +                       station.hasPrevious() && 
> station.getPrevious().isOverflowStart());
> +  }
> +
> +  private void _renderTrain(
> +      FacesContext     context,
> +      RenderingContext arc,
> +      UIXProcess       process,
> +      FacesBean        bean,
> +      UIComponent      stamp,
> +      Train            train) throws IOException
>    {
> -
>      ResponseWriter writer = context.getResponseWriter();
>  
> -    // start FOR SUBTRAIN
> -    // If subTrain, add a row and on the first and last cells, render
> -    // a border which looks like a sub-train
> -    if (train.subTrain)
> -    {
> -      _renderSubTrainRow(context, arc, train, length, writer);
> -
> -      writer.startElement("tr", null);
> -      writer.startElement("td", null);
> -      writer.endElement("td");
> +    // Start of the icon row
> +    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
> +
> +    if(arc.isRightToLeft())
> +    {
> +      _renderIconRowRtl(context, arc, process, stamp, train);
>      }
>      else
>      {
> -      writer.startElement("tr", null);
> +      _renderIconRowLtr(context, arc, process, stamp, train);
>      }
> +
> +    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
>  
> -    // loop through each rendered station.
> -    int lastTrainIndex = train.startIndex + 
> _getMaxLinks(arc, process);
> -
> -    if (length <= lastTrainIndex)
> +    // Start of the content row
> +    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);
> +
> +    if(arc.isRightToLeft())
>      {
> -      lastTrainIndex = length;
> +      _renderContentRowRtl(context, arc, process, stamp, train);
>      }
>      else
>      {
> -      lastTrainIndex++; // length of train is larger than the visible
> -      // number of train stations, so make room for the more
> -      // by adding one to the lastTrainIndex.
> +      _renderContentRowLtr(context, arc, process, stamp, train);
>      }
> -    int currVisChildIndex = Math.max(0, train.startIndex - 1);
> -    boolean isPrevVisChildDisabled = false;
> -    boolean isCurrVisChildDisabled = false;
> -    boolean isNextVisChildDisabled = false;
> -
> -    process.setRowIndex(currVisChildIndex);
> -
> -    isCurrVisChildDisabled =
> -        Boolean.TRUE.equals(stamp.getAttributes().get("disabled"));
> -    // getBooleanAttributeValue(context, stamp, 
> DISABLED_ATTR, false);
> -
> -    for (; currVisChildIndex < lastTrainIndex; currVisChildIndex++)
> +
> +    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);
> +  }
> +
> +  private static class Train
> +  {
> +    public Train(
> +        FacesContext     context,
> +        RenderingContext arc,
> +        UIXProcess       process,
> +        UIComponent      stamp)
> +    {
> +      // Save the model state
> +      int maxVisitedIndex  = _getMaxVisitedIndex(arc, process);
> +      int activeIndex      = _loadStations(process, stamp, 
> maxVisitedIndex);
> +      int visibleStopCount = _getVisibleStopCount(arc);
> +
> +      _formName     = arc.getFormData().getName();
> +      _isSubTrain   = _loadIsSubTrain(process);
> +
> +      if(!_stations.isEmpty())
> +      {
> +        // There's something visible in the train
> +        if(_stations.size() > visibleStopCount)
> +        {
> +          // We have overflow, let resolve it
> +          _resolveOverflow(visibleStopCount, activeIndex);
> +        }
> +        else
> +        {
> +          // No overflow, yay!
> +          _resolveStandard();
> +        }
> +
> +        _initLabels(arc, process, stamp);
> +        _initParentTrain(arc, process, stamp);
> +      }
> +    }
> +
> +    public Object getFocusRowKey()
>      {
> -      //
> -      // get index of the child and
> -      // determine if it is within the range in which it 
> will be rendered.
> -      //
> -      int prevVisChildIndex =
> -        ((currVisChildIndex == 0)? NO_CHILD_INDEX: 
> currVisChildIndex - 1);
> -      int nextVisChildIndex =
> -        ((currVisChildIndex == length - 1)? NO_CHILD_INDEX:
> -         currVisChildIndex + 1);
> -
> -      process.setRowIndex(nextVisChildIndex);
> -
> -      isNextVisChildDisabled =
> -          Boolean.TRUE.equals(stamp.getAttributes().get("disabled"));
> -
> -      process.setRowIndex(currVisChildIndex);
> -
> -      // initialized state of the station
> -      TrainRenderer.StationState station = train.station;
> -      _initializeStationState(context, arc, train, station,
> -                             currVisChildIndex, prevVisChildIndex,
> -                             nextVisChildIndex, 
> isCurrVisChildDisabled,
> -                             isPrevVisChildDisabled,
> -                             isNextVisChildDisabled);
> -
> -      // set up for next pass
> -      isPrevVisChildDisabled = isCurrVisChildDisabled;
> -      isCurrVisChildDisabled = isNextVisChildDisabled;
> -
> -      Object label = stamp.getAttributes().get("text");
> -
> -      String currVisChildText = null;
> -
> -      // Get text from link, or Previous or More text if appropriate
> -      currVisChildText = _getTextForStation(arc, station, label);
> -
> -      String currVisChildID = null;
> -
> -      process.setRowIndex(currVisChildIndex);
> -      _renderLink(context, arc, stamp, writer, train, 
> currVisChildText,
> -                 currVisChildID, station);
> -
> +      return _focusRowKey;
>      }
> -
> -    if (train.subTrain)
> +
> +    public String getFormName()
>      {
> -      writer.startElement("td", null);
> -      writer.endElement("td");
> +      return _formName;
> +    }
> +
> +    public Object getInitialRowKey()
> +    {
> +      return _initialRowKey;
> +    }
> +
> +    public ParentTrain getParentTrain()
> +    {
> +      return _parent;
> +    }
> +
> +    public List<Station> getStations()
> +    {
> +      return _stations;
> +    }
> +
> +    public boolean isSubTrain()
> +    {
> +      return _isSubTrain;
> +    }
> +
> +    private void _createStation(
> +        UIXProcess  process,
> +        UIComponent stamp,
> +        int         index,
> +        boolean     active,
> +        boolean     visited)
> +    {
> +      process.setRowIndex(index);
> +      if(stamp.isRendered())
> +      {
> +        // The station will be visible.
> +        _stations.add(new Station(this,
> +                                  stamp,
> +                                  index,
> +                                  process.getRowKey(),
> +                                  active,
> +                                  visited));
> +      }
>      }
>  
> -    writer.endElement("tr");
> -  }
> -
> -  /**
> -   * Renders the link under the train node
> -   *
> -   */
> -  private void _renderLink(
> -    FacesContext context,
> -    RenderingContext arc,
> -    UIComponent stamp,
> -    ResponseWriter writer,
> -    TrainRenderer.TrainState train,
> -    String currVisChildText,
> -    String currVisChildID,
> -    TrainRenderer.StationState station)
> -    throws IOException
> -  {
> -    //
> -    // Write the link under the train node.
> -    //
> -    writer.startElement("td", null);
> -    writer.writeAttribute("colspan", "2", null);
> -
> -    String styleClass =
> -      (station.isSelected)? 
> SkinSelectors.AF_PROCESS_TRAIN_ACTIVE_STYLE_CLASS:
> -      (station.isDisabled && !station.isMoreLink)?
> -      SkinSelectors.AF_PROCESS_TRAIN_DISABLED_STYLE_CLASS:
> -      (station.isVisited)?
> -      SkinSelectors.AF_PROCESS_TRAIN_VISITED_STYLE_CLASS:
> -      SkinSelectors.AF_PROCESS_TRAIN_UNVISITED_STYLE_CLASS;
> -
> -    renderStyleClass(context, arc, styleClass);
> +    /**
> +     * Get the maxVisited attribute from the node and return it.
> +     */
> +    private int _getMaxVisitedIndex(
> +      RenderingContext arc,
> +      UIComponent component)
> +    {
> +      int maxVisitedIndex = NO_CHILD_INDEX;
> +      Integer maxVisited = (Integer) _getMaxVisited(arc, component);
> +      if (maxVisited != null)
> +      {
> +        maxVisitedIndex = maxVisited.intValue();
> +      }
> +      return maxVisitedIndex;
> +    }
>  
> -    Map<String, String> originalResourceKeyMap = 
> arc.getSkinResourceKeyMap();
> -    try
> +    /**
> +     * Returns the MAX_VISITED_ATTR
> +     * @todo =-=jmw Hopefully the controller will tell us 
> this someday.
> +     */
> +    private static Object _getMaxVisited(
> +      RenderingContext arc,
> +      UIComponent component)
> +    {
> +      // return component.getAttributes().get("maxVisited");
> +      return null;
> +    }
> +
> +    private int _getVisibleStopCount(RenderingContext arc)
> +    {
> +      Object propValue =
> +        
> arc.getSkin().getProperty(SkinProperties.AF_TRAIN_VISIBLE_STOP_COUNT);
> +
> +      if(propValue == null)
> +      {
> +        return DEFAULT_MAX_VISIBLE_STOP_COUNT;
> +      }
> +
> +      try
> +      {
> +        int count = Integer.parseInt(propValue.toString());
> +        if(count <= 0)
> +        {
> +          _LOG.warning("Visible stop count must be > 0, 
> found " + count);
> +          return DEFAULT_MAX_VISIBLE_STOP_COUNT;
> +        }
> +
> +        return count;
> +      }
> +      catch(NumberFormatException e)
> +      {
> +        _LOG.warning("Visible stop count must be an integer, 
> found " + propValue);
> +        return DEFAULT_MAX_VISIBLE_STOP_COUNT;
> +      }
> +    }
> +
> +    private void _initLabels(
> +        RenderingContext arc,
> +        UIXProcess       process,
> +        UIComponent      stamp)
>      {
> -      arc.setSkinResourceKeyMap(_RESOURCE_KEY_MAP);
> -      _renderStamp(context, stamp);
> +      for(Station s : _stations)
> +      {
> +        process.setRowIndex(s.getRowIndex());
> +        s.initLabel(arc, stamp);
> +      }
>      }
> -    finally
> +
> +    private void _initParentTrain(
> +        RenderingContext arc,
> +        UIXProcess       process,
> +        UIComponent      stamp)
>      {
> -      arc.setSkinResourceKeyMap(originalResourceKeyMap);
> +      if(_isSubTrain)
> +      {
> +        if(_shouldRenderParentTrain(arc))
> +        {
> +          _parent = new ParentTrain(arc, process, stamp, this);
> +          if(!_parent.hasParentStart() && !_parent.hasParentEnd())
> +          {
> +            _isSubTrain = false;
> +          }
> +        }
> +        else
> +        {
> +          _isSubTrain = false;
> +        }
> +      }
>      }
> -    writer.endElement("td");
> -  }
> -
> -  /**
> -   * Called to render a child.  This method does not update the
> -   * rendering context (by calling pushChild() and popChild()
> -   * as needed);  subclasses need to use renderIndexedChild() or
> -   * renderNamedChild() for that purpose.
> -   * <p>
> -   * @param context the faces context
> -   * @param child the child under consideration
> -   */
> -  private void _renderStamp(FacesContext context, UIComponent child)
> -    throws IOException
> -  {
> -    if (child != null)
> +
> +    /**
> +     * Determine if this train is a sub-train.
> +     */
> +    private boolean _loadIsSubTrain(UIXProcess process)
>      {
> -      encodeChild(context, child);
> -      // child.render(context);
> +      Object focusRowKey = process.getFocusRowKey();
> +      if (focusRowKey != null && (process.getDepth(focusRowKey) > 0))
> +      {
> +        return true;
> +      }
> +
> +      return false;
>      }
> -  }
> -
> -  /**
> -  * return the string to use for the text of the station
> -  * it is the text of the link or "Previous" or "More"
> -  */
> -  private String _getTextForStation(
> -    RenderingContext arc,
> -    TrainRenderer.StationState station,
> -    Object textObj)
> -  {
> -    String textValue = (textObj == null)? null: textObj.toString();
> -    final String currText;
> +
> +    private int _loadStations(
> +        UIXProcess  process,
> +        UIComponent stamp,
> +        int         maxVisitedIndex)
> +    {
> +      _initialRowKey = process.getRowKey();
> +      try
> +      {
> +        // Set the model on the focus item
> +        _focusRowKey = process.getFocusRowKey();
> +        process.setRowKey(_focusRowKey);
> +
> +        int count       = process.getRowCount();
> +        int activeIndex = process.getRowIndex();
> +        int index       = 0;
> +
> +        assert activeIndex < count;
> +
> +        _stations = new ArrayList<Station>(count);
> +
> +        // Process visited stations
> +        for(; index < activeIndex; index++)
> +        {
> +          _createStation(process, stamp, index, false, true);
> +        }
> +
> +        assert index == activeIndex;
> +
> +        _createStation(process, stamp, index, true, true);
> +        index++;
> +        // Might have an invisible active station. Thsi is 
> weird, but still.
> +        // You never know what users want to do, but let support
> +        // it nevertheless for now.
> +        // selectedIndex is either the active station index 
> or the index
> +        // of the station just before the selected one if 
> active is not visible.
> +        activeIndex = _stations.size() - 1;
> +
> +        if(maxVisitedIndex != NO_CHILD_INDEX)
> +        {
> +          for(; index < maxVisitedIndex; index++)
> +          {
> +            _createStation(process, stamp, index, false, true);
> +          }
> +        }
> +
> +        for(; index < count; index++)
> +        {
> +          _createStation(process, stamp, index, false, false);
> +        }
> +
> +        return activeIndex;
> +      }
> +      finally
> +      {
> +        // Restore the model's state
> +        process.setRowKey(_initialRowKey);
> +      }
> +    }
> +
> +    private void _resolveOverflow(
> +        int visibleStopCount,
> +        int activeIndex)
> +    {
> +      assert _stations != null;
> +      assert activeIndex >= -1;
> +      assert activeIndex < _stations.size();
> +
> +      // First, resolve chaining
> +      _resolveStandard();
> +
> +      // We have more stations than the max available, so we 
> have an overflow
> +      // for sure.
> +      if(activeIndex <= 0)
> +      {
> +        // Overflow to the following group only
> +        _resolveOverflowEnd(visibleStopCount);
> +        _stations = _stations.subList(0, visibleStopCount + 1);
> +      }
> +      else
> +      {
> +        // Get the visible group index
> +        int groupIndex = activeIndex / visibleStopCount;
> +        int startIndex = 0;
> +        int endIndex   = _stations.size();
> +        if(groupIndex > 0)
> +        {
> +          // We have a previous overflow
> +          startIndex = groupIndex * visibleStopCount - 1;
> +          _resolveOverflowStart(startIndex);
> +        }
> +
> +        int maxGroupIndex = (_stations.size() - 1) / 
> visibleStopCount;
> +
> +        if(groupIndex < maxGroupIndex)
> +        {
> +          // We have a next overflow
> +          int overflowIndex = (groupIndex + 1) * visibleStopCount;
> +
> +          // endIndex is exclusive
> +          endIndex = overflowIndex + 1;
> +
> +          _resolveOverflowEnd(overflowIndex);
> +        }
>  
> -    if (textValue != null && !station.isPreviousLink &&
> -        !station.isMoreLink)
> +        _stations = _stations.subList(startIndex, endIndex);
> +      }
> +    }
> +
> +    private void _resolveOverflowEnd(int index)
> +    {
> +      assert _stations != null;
> +      assert index >= 0;
> +      assert index < _stations.size();
> +
> +      Station station = _stations.get(index);
> +      station.setOverflowEnd(true);
> +      if(station.hasPrevious() && station.getPrevious().isDisabled())
> +      {
> +        // If previous stop is disabled, so is the overflow
> +        station.setDisabled(true);
> +      }
> +
> +      station.setNext(null);
> +    }
> +
> +    private void _resolveOverflowStart(int index)
> +    {
> +      assert _stations != null;
> +      assert index >= 0;
> +      assert index < _stations.size();
> +
> +      Station station = _stations.get(index);
> +      station.setOverflowStart(true);
> +      if(station.hasNext() && station.getNext().isDisabled())
> +      {
> +        // If next stop is disabled, so is the overflow
> +        station.setDisabled(true);
> +      }
> +
> +      station.setPrevious(null);
> +    }
> +
> +    private void _resolveStandard()
> +    {
> +      if(_stations.size() > 1)
> +      {
> +        Iterator<Station> iterator = _stations.iterator();
> +
> +        Station previous = null;
> +        Station current  = iterator.next();
> +        Station next     = iterator.next();
> +
> +        _updateStation(current, previous, next);
> +
> +        while(iterator.hasNext())
> +        {
> +          previous = current;
> +          current  = next;
> +          next     = iterator.next();
> +          _updateStation(current, previous, next);
> +        }
> +
> +        next.setPrevious(current);
> +      }
> +    }
> +
> +    private boolean _shouldRenderParentTrain(RenderingContext arc)
> +    {
> +      Object propValue =
> +        
> arc.getSkin().getProperty(SkinProperties.AF_TRAIN_RENDER_PAREN
> T_TRAIN);
> +
> +      if(propValue == null)
> +      {
> +        return DEFAULT_RENDER_PARENT_TRAIN;
> +      }
> +
> +      return Boolean.TRUE.equals(propValue);
> +    }
> +
> +    private void _updateStation(
> +        Station current,
> +        Station previous,
> +        Station next)
> +    {
> +      current.setPrevious(previous);
> +      current.setNext(next);
> +    }
> +
> +    private Object        _focusRowKey;
> +    private String        _formName;
> +    private Object        _initialRowKey;
> +    private boolean       _isSubTrain;
> +    private ParentTrain   _parent;
> +    private List<Station> _stations;
> +  }
> +
> +  private static class Station
> +  {
> +    public Station(
> +        Train  train,
> +        int    index,
> +        Object rowKey)
> +    {
> +      _rowIndex    = index;
> +      _rowKey      = rowKey;
> +      _active      = false;
> +      _visited     = false;
> +      _disabled    = false;
> +      _readOnly    = false;
> +      _parentEnd   = false;
> +      _parentStart = false;
> +      _train       = train;
> +    }
> +
> +    @SuppressWarnings("unchecked")
> +    public Station(
> +        Train       train,
> +        UIComponent stamp,
> +        int         index,
> +        Object      rowKey,
> +        boolean     active,
> +        boolean     visited)
> +    {
> +      Map<String, Object> attributes = stamp.getAttributes();
> +
> +      _rowIndex    = index;
> +      _rowKey      = rowKey;
> +      _active      = active;
> +      _visited     = visited;
> +      _disabled    = _getBooleanAttribute(attributes, 
> "disabled", false);
> +      _readOnly    = _getBooleanAttribute(attributes, 
> "readOnly", false);
> +      _parentEnd   = false;
> +      _parentStart = false;
> +      _train       = train;
> +    }
> +
> +    public String getBaseStyleClass()
>      {
> -      // if we are in screen reader mode, then we must 
> render more descriptive
> -      // text.
> -      // see bug 1801348 REMOVE ONE SET OF TRAIN TEXT IN 
> ACCESSIBLE MODE
> -      if (isScreenReaderMode(arc))
> +      if(isOverflowEnd())
> +      {
> +        return SkinSelectors.AF_TRAIN_OVERFLOW_END_STYLE_CLASS;
> +      }
> +      else if(isOverflowStart())
> +      {
> +        return SkinSelectors.AF_TRAIN_OVERFLOW_START_STYLE_CLASS;
> +      }
> +      else if(isParentStart())
> +      {
> +        return SkinSelectors.AF_TRAIN_PARENT_START_STYLE_CLASS;
> +      }
> +      else if(isParentEnd())
>        {
> -        currText = _getDisabledUserText(arc, station, textValue);
> +        return SkinSelectors.AF_TRAIN_PARENT_END_STYLE_CLASS;
>        }
>        else
>        {
> -        currText = textValue;
> +        return SkinSelectors.AF_TRAIN_STOP_STYLE_CLASS;
>        }
>      }
> -    else if (station.isPreviousLink)
> +
> +    public String getEndJoinState()
>      {
> -      currText = arc.getTranslatedString(_PREVIOUS_KEY);
> +      if(isOverflowEnd())
> +      {
> +        return null;
> +      }
> +      else if(!hasNext())
> +      {
> +        ParentTrain parent = _train.getParentTrain();
> +        if(parent != null && parent.hasParentEnd())
> +        {
> +          return _STATE_PARENT;
> +        }
> +        else
> +        {
> +          return null;
> +        }
> +      }
> +      else if(isDisabled() || getNext().isDisabled())
> +      {
> +        return _STATE_DISABLED;
> +      }
> +      else if(getNext().isVisited())
> +      {
> +        return _STATE_VISITED;
> +      }
> +      else
> +      {
> +        return _STATE_UNVISITED;
> +      }
>      }
> -    else if (station.isMoreLink)
> +
> +    public List<String> getIconNames()
>      {
> -      currText = arc.getTranslatedString(_MORE_KEY);
> +      if(isOverflowEnd())
> +      {
> +        return _getOverflowEndIconNames();
> +      }
> +      else if(isOverflowStart())
> +      {
> +        return _getOverflowStartIconNames();
> +      }
> +      else if(isParentStart())
> +      {
> +        return 
> Collections.singletonList(SkinSelectors.AF_TRAIN_PARENT_START_
> ICON_NAME);
> +      }
> +      else if(isParentEnd())
> +      {
> +        return 
> Collections.singletonList(SkinSelectors.AF_TRAIN_PARENT_END_IC
> ON_NAME);
> +      }
> +      else
> +      {
> +        return _getStopIconNames();
> +      }
>      }
> -    else
> -      currText = null;
> -
> -    return currText;
> -  }
> -
> -  private String _getDisabledUserText(
> -    RenderingContext arc,
> -    TrainRenderer.StationState station,
> -    String textString)
> -  {
> -    String altTextKey =
> -      station.isSelected? _ACTIVE_KEY: station.isVisited? 
> _VISITED_KEY:
> -                                       _NEXT_KEY;
> -
> -    String[] parameters = new String[]
> -      { textString };
> -
> -    String altText =
> -      
> XhtmlUtils.getFormattedString(arc.getTranslatedString(altTextKey),
> -                                    parameters);
> -    return altText;
> -  }
> -
> -  /**
> -   * Returns the max number of links to show
> -   */
> -  private int _getMaxLinks(
> -    RenderingContext arc,
> -    UIComponent component)
> -  {
> -    return _MAX_NUM_LINK_INDEX;
> -  }
> -
> -  private void _renderSubTrainRow(
> -    FacesContext context,
> -    RenderingContext arc,
> -    TrainRenderer.TrainState train,
> -    int length,
> -    ResponseWriter writer)
> -    throws IOException
> -  {
> -    boolean isRTL = arc.getLocaleContext().isRightToLeft();
> -
> -    writer.startElement("tr", null);
> -
> -    if (isRTL)
> -      _renderSubTrainCell(context, arc,
> -                          SkinSelectors.TRAIN_SUB_RIGHT_STYLE_CLASS,
> -                          writer);
> -    else
> -      _renderSubTrainCell(context, arc,
> -                          
> SkinSelectors.AF_PROCESS_TRAIN_SUB_START_STYLE_CLASS,
> -                          writer);
> -
> -    _renderSubTrainBlankCells(train, length, writer);
> -
> -    if (isRTL)
> -      _renderSubTrainCell(context, arc,
> -                          
> SkinSelectors.AF_PROCESS_TRAIN_SUB_START_STYLE_CLASS,
> -                          writer);
> -    else
> -      _renderSubTrainCell(context, arc,
> -                          SkinSelectors.TRAIN_SUB_RIGHT_STYLE_CLASS,
> -                          writer);
> -
> -    writer.endElement("tr");
> -  }
> -
> -  private void _renderSubTrainCell(
> -    FacesContext context,
> -    RenderingContext arc,
> -    String style,
> -    ResponseWriter writer)
> -    throws IOException
> -  {
> -    writer.startElement("td", null);
> -    renderStyleClass(context, arc, style);
> -    renderSpacer(context, arc, "14", "2");
> -    writer.endElement("td");
> -  }
> -
> -  /**
> -   * renders a td with colSpan equal to the number of 
> visible stations
> -   * including the Previous and More if they are there.
> -   * @param train
> -   * @param length
> -   * @param writer
> -   * @throws IOException
> -   */
> -  private void 
> _renderSubTrainBlankCells(TrainRenderer.TrainState train,
> -                                         int length, 
> ResponseWriter writer)
> -    throws IOException
> -  {
> -    writer.startElement("td", null);
> -
> -    // figure out the number of stations
> -    int startIndex = Math.max(0, train.startIndex - 1);
> -    int lastTrainIndex = train.startIndex + _MAX_NUM_LINK_INDEX;
> -    if (length <= lastTrainIndex)
> -      lastTrainIndex = length;
> -    else
> +
> +    public String getLabel()
>      {
> -      // when the length of train is larger than the visible
> -      // number of train stations, we render a More link.
> -      // so make room for the more
> -      // by adding one to the lastTrainIndex.
> -      lastTrainIndex++;
> -    }
> -    String numberOfStations =
> -      Integer.toString((lastTrainIndex - startIndex) * 2);
> -    writer.writeAttribute("colspan", numberOfStations, null);
> -    writer.endElement("td");
> -  }
> +      return _label;
> +    }
> +
> +    public Station getNext()
> +    {
> +      return _next;
> +    }
> +
> +    public Station getPrevious()
> +    {
> +      return _previous;
> +    }
> +
> +    public int getRowIndex()
> +    {
> +      return _rowIndex;
> +    }
> +
> +    public Object getRowKey()
> +    {
> +      return _rowKey;
> +    }
> +
> +    public String getStartJoinState()
> +    {
> +      if(isOverflowStart())
> +      {
> +        return null;
> +      }
> +      else if(!hasPrevious())
> +      {
> +        ParentTrain parent = _train.getParentTrain();
> +        if(parent != null && parent.hasParentStart())
> +        {
> +          return _STATE_PARENT;
> +        }
> +        else
> +        {
> +          return null;
> +        }
> +      }
> +      else if(isDisabled() || getPrevious().isDisabled())
> +      {
> +        return _STATE_DISABLED;
> +      }
> +      else if(isVisited())
> +      {
> +        return _STATE_VISITED;
> +      }
> +      else
> +      {
> +        return _STATE_UNVISITED;
> +      }
> +    }
> +
> +    public List<String> getStates()
> +    {
> +      List<String> states = new ArrayList<String>(5);
> +      if(isParentStart() || isParentEnd())
> +      {
> +        return states;
> +      }
> +
> +      if(isDisabled())
> +      {
> +        states.add(_STATE_DISABLED);
> +        return states;
> +      }
> +
> +      if(isActive())
> +      {
> +        states.add(_STATE_ACTIVE);
> +      }
> +      else if(isVisited())
> +      {
> +        states.add(_STATE_VISITED);
> +      }
> +      else
> +      {
> +        states.add(_STATE_UNVISITED);
> +      }
> +
> +      if(isReadOnly())
> +      {
> +        states.add(_STATE_READ_ONLY);
> +      }
> +
> +      return states;
> +    }
> +
> +    public boolean hasNext()
> +    {
> +      return _next != null;
> +    }
> +
> +    public boolean hasPrevious()
> +    {
> +      return _previous != null;
> +    }
>  
>  
> -  private void _renderHiddenFields(
> -    FacesContext context,
> -    RenderingContext arc,
> -    TrainState train)
> -    throws IOException
> -  {
> -    if ((train.formName != null) && supportsScripting(arc))
> +    /**
> +     * return the string to use for the text of the station
> +     * it is the text of the link or "Previous" or "More"
> +     */
> +    public void initLabel(
> +      RenderingContext arc,
> +      UIComponent      stamp)
>      {
> -      // render hidden fields to hold the form data
> -      FormData fData = arc.getFormData();
> -      if (fData != null)
> +      if(isOverflowStart())
>        {
> -        fData.addNeededValue(XhtmlConstants.EVENT_PARAM);
> -        fData.addNeededValue(XhtmlConstants.SOURCE_PARAM);
> -        fData.addNeededValue(XhtmlConstants.VALUE_PARAM);
> -        fData.addNeededValue(XhtmlConstants.SIZE_PARAM);
> +        _label = arc.getTranslatedString(_PREVIOUS_KEY);
> +      }
> +      else if(isOverflowEnd())
> +      {
> +        _label = arc.getTranslatedString(_MORE_KEY);
> +      }
> +      else
> +      {
> +        Object text = stamp.getAttributes().get("text");
> +        if(text != null)
> +        {
> +          _label = text.toString();
> +          if (isScreenReaderMode(arc))
> +          {
> +            _label = _getDisabledUserText(arc, _label);
> +          }
> +        }
> +        else
> +        {
> +          _label = null;
> +        }
>        }
> -
> -      // Render script submission code.
> -      ProcessUtils.renderNavSubmitScript(context, arc);
>      }
> -  }
> +
> +    public boolean isActive()
> +    {
> +      return _active;
> +    }
> +
> +    public boolean isDisabled()
> +    {
> +      return _disabled;
> +    }
> +
> +    public boolean isNextDisabled()
> +    {
> +      return hasNext() && _next.isDisabled();
> +    }
> +
> +    public boolean isOverflowEnd()
> +    {
> +      return _overflowEnd;
> +    }
> +
> +    public boolean isOverflowStart()
> +    {
> +      return _overflowStart;
> +    }
> +
> +    public boolean isParentEnd()
> +    {
> +      return _parentEnd;
> +    }
> +
> +    public boolean isParentStart()
> +    {
> +      return _parentStart;
> +    }
> +
> +    public boolean isPreviousDisabled()
> +    {
> +      return hasPrevious() && _previous.isDisabled();
> +    }
> +
> +    public boolean isReadOnly()
> +    {
> +      return _readOnly;
> +    }
> +
> +    public boolean isVisited()
> +    {
> +      return _visited;
> +    }
> +
> +    public void setDisabled(boolean disabled)
> +    {
> +      _disabled = disabled;
> +    }
> +
> +    public void setNext(Station next)
> +    {
> +      _next = next;
> +    }
> +
> +    public void setOverflowEnd(boolean overflowEnd)
> +    {
> +      _overflowEnd = overflowEnd;
> +    }
> +
> +    public void setOverflowStart(boolean overflowStart)
> +    {
> +      _overflowStart = overflowStart;
> +      _visited       = true;
> +    }
> +
> +    public void setParentEnd(boolean parentEnd)
> +    {
> +      _parentEnd = parentEnd;
> +    }
> +
> +    public void setParentStart(boolean parentStart)
> +    {
> +      _parentStart = parentStart;
> +    }
> +
> +    public void setPrevious(Station previous)
> +    {
> +      _previous = previous;
> +    }
> +
> +    public void setReadOnly(boolean readOnly)
> +    {
> +      _readOnly = readOnly;
> +    }
> +
> +    private boolean _getBooleanAttribute(
> +        Map<String, Object> attributes,
> +        String              attributeName,
> +        boolean             defaultValue)
> +    {
> +      Object value = attributes.get(attributeName);
> +      if(value == null)
> +      {
> +        return defaultValue;
> +      }
> +
> +      return Boolean.TRUE.equals(value);
> +    }
>  
> +    private String _getDisabledUserText(
> +      RenderingContext arc,
> +      String           text)
> +    {
> +      String altTextKey;
> +      if(isActive())
> +      {
> +        altTextKey = _ACTIVE_KEY;
> +      }
> +      else if(isVisited())
> +      {
> +        altTextKey = _VISITED_KEY;
> +      }
> +      else
> +      {
> +        altTextKey = _NEXT_KEY;
> +      }
>  
> -  private boolean _setNewPath(UIXProcess component)
> -  {
> -    Object focusPath = component.getFocusRowKey();
> -    component.setRowKey(focusPath);
> -    return true;
> +      String altText =
> +        
> XhtmlUtils.getFormattedString(arc.getTranslatedString(altTextKey),
> +                                      new String[]{text});
> +
> +      return altText;
> +    }
> +
> +    private List<String> _getIconNames(String baseSelector)
> +    {
> +      LinkedList<String> names = new LinkedList<String>();
> +
> +      StringBuilder builder = new StringBuilder(64);
> +      builder.append(baseSelector);
> +
> +      int suffixLength = SkinSelectors.ICON_SUFFIX.length();
> +      int baseIndex    = builder.length();
> +
> +      builder.append(SkinSelectors.ICON_SUFFIX);
> +      names.addFirst(builder.toString());
> +      builder.delete(baseIndex, baseIndex + suffixLength);
> +
> +      if(isDisabled())
> +      {
> +        builder.append(_SUFFIX_DISABLED);
> +        builder.append(SkinSelectors.ICON_SUFFIX);
> +        names.addFirst(builder.toString());
> +      }
> +      else
> +      {
> +        if(isActive())
> +        {
> +          builder.append(_SUFFIX_ACTIVE);
> +        }
> +        else if(isVisited())
> +        {
> +          builder.append(_SUFFIX_VISITED);
> +        }
> +        else
> +        {
> +          builder.append(_SUFFIX_UNVISITED);
> +        }
> +
> +        baseIndex = builder.length();
> +
> +        builder.append(SkinSelectors.ICON_SUFFIX);
> +        names.addFirst(builder.toString());
> +        builder.delete(baseIndex, baseIndex + suffixLength);
> +
> +        if(isReadOnly())
> +        {
> +          builder.append(_SUFFIX_READ_ONLY);
> +          builder.append(SkinSelectors.ICON_SUFFIX);
> +          names.addFirst(builder.toString());
> +        }
> +      }
> +
> +      return names;
> +    }
> +
> +    private List<String> _getOverflowEndIconNames()
> +    {
> +      return 
> _getIconNames(SkinSelectors.AF_TRAIN_OVERFLOW_END_STYLE_CLASS);
> +    }
> +
> +    private List<String> _getOverflowStartIconNames()
> +    {
> +      return 
> _getIconNames(SkinSelectors.AF_TRAIN_OVERFLOW_START_STYLE_CLASS);
> +    }
> +
> +    private List<String> _getStopIconNames()
> +    {
> +      return _getIconNames(SkinSelectors.AF_TRAIN_STOP_STYLE_CLASS);
> +    }
> +
> +    private boolean _active;       // Is this station the active one?
> +    private boolean _disabled;     // Disabled attribute
> +    private boolean _overflowEnd; // Is this station the 
> next step set link?
> +    private boolean _overflowStart; // Is this station the 
> prev step set link?
> +    private boolean _parentEnd;    // Is this station a parent end?
> +    private boolean _parentStart;  // Is this station a parent start?
> +    private boolean _readOnly;     // Read only attribute
> +    private boolean _visited;      // Is this station visited?
> +
> +    private int _rowIndex; // Row index
> +
> +    private Object _rowKey; // Row key
> +
> +    private String _label; // This station's label
> +
> +    private Station _next;
> +    private Station _previous;
> +
> +    private Train _train;
>    }
> -
> -  protected static class TrainState
> +
> +  private static class ParentTrain
>    {
> -    public TrainState()
> +    public ParentTrain(
> +        RenderingContext arc,
> +        UIXProcess       process,
> +        UIComponent      stamp,
> +        Train            train)
> +    {
> +      List<Station> stations     = train.getStations();
> +      int           stationCount = stations.size();
> +
> +      boolean hasParentStart = !stations.get(0).isOverflowStart();
> +      boolean hasParentEnd   = !stations.get(stationCount - 
> 1).isOverflowEnd();
> +
> +      if(hasParentStart || hasParentEnd)
> +      {
> +        Object parentStartRowKey = process.getContainerRowKey();
> +        process.setRowKey(parentStartRowKey);
> +        int rowIndex = process.getRowIndex();
> +        if(hasParentStart)
> +        {
> +          _parentStart = new Station(train, rowIndex, 
> parentStartRowKey);
> +          _parentStart.setParentStart(true);
> +          _parentStart.initLabel(arc, stamp);
> +        }
> +
> +        rowIndex = rowIndex + 1;
> +
> +        // Check if the parent has more steps, render it 
> only if it does
> +        hasParentEnd = rowIndex < process.getRowCount();
> +        if(hasParentEnd)
> +        {
> +          process.setRowIndex(rowIndex);
> +          _parentEnd = new Station(train, rowIndex, 
> process.getRowKey());
> +          _parentEnd.setParentEnd(true);
> +          _parentEnd.initLabel(arc, stamp);
> +        }
> +
> +        // Restore the model
> +        process.setRowKey(train.getInitialRowKey());
> +      }
> +    }
> +
> +    public Station getParentEnd()
>      {
> -      station = new TrainRenderer.StationState();
> +      return _parentEnd;
>      }
> -    public int startIndex;
> -    public int maxVisitedIndex;
> -    public int selectedIndex;
> -    public boolean subTrain;
> -    public String formName;
> -    public String id;
> -    public TrainRenderer.StationState station;
> -  }
> -
> -  protected static class StationState
> -  {
> -
> -    public boolean isSelected;
> -    // is this the station that is right AFTER the selected station.
> -    public boolean isNext;
> -    public boolean isVisited; // has this station been 
> visited already?
> -    public boolean isPreviousLink; // is this the Previous link?
> -    public boolean isMoreLink; // is this the More link?
> -    public boolean isDisabled; // is this station disabled?
> -    public boolean isNextDisabled; // is the next station disabled?
> -    public boolean isPrevDisabled; // is the previous 
> station disabled?
> -    public boolean isNextVisited; // is the next station visited?
> -    public int index; // the index of this node
> -  } //end StationState
> +
> +    public Station getParentStart()
> +    {
> +      return _parentStart;
> +    }
> +
> +    public boolean hasParentEnd()
> +    {
> +      return _parentEnd != null;
> +    }
> +
> +    public boolean hasParentStart()
> +    {
> +      return _parentStart != null;
> +    }
> +
> +    private Station _parentEnd;
> +    private Station _parentStart;
> +  }
>  
> -  private static final int  _MAX_NUM_LINK_INDEX =
> -    6; //number of visible links
> +  /**
> +   * Gives the amount of visible stops that get rendered by 
> default if no
> +   * amount is specified by the -ora-visible-stop-count skin 
> property.
> +   */
> +  public static final int DEFAULT_MAX_VISIBLE_STOP_COUNT  = 6;
> +
> +  /**
> +   * Determines if the parent train of sub-trains should be 
> rendered by
> +   * default if not specified by the 
> -ora-render-parent-train skin property.
> +   */
> +  public static final boolean DEFAULT_RENDER_PARENT_TRAIN = false;
> +
> +  private static final String _STATE_ACTIVE    = 
> SkinSelectors.STATE_PREFIX + "Selected";
> +  private static final String _STATE_DISABLED  = 
> SkinSelectors.STATE_PREFIX + "Disabled";
> +  private static final String _STATE_PARENT    = 
> SkinSelectors.STATE_PREFIX + "Parent";
> +  private static final String _STATE_READ_ONLY = 
> SkinSelectors.STATE_PREFIX + "ReadOnly";
> +  private static final String _STATE_UNVISITED = 
> SkinSelectors.STATE_PREFIX + "Unvisited";
> +  private static final String _STATE_VISITED   = 
> SkinSelectors.STATE_PREFIX + "Visited";
> +
> +  private static final String _SUFFIX_CONTENT    = "-content";
> +  private static final String _SUFFIX_ICON_CELL  = "-icon-cell";
> +
> +  private static final String _SUFFIX_ACTIVE     = ":selected";
> +  private static final String _SUFFIX_DISABLED   = ":disabled";
> +  private static final String _SUFFIX_READ_ONLY  = ":read-only";
> +  private static final String _SUFFIX_UNVISITED  = ":unvisited";
> +  private static final String _SUFFIX_VISITED    = ":visited";
>  
>    /**
> - * The following keys are used to get at the corresponding translated
> - * strings.
> - */
> +   * The following keys are used to get at the corresponding 
> translated
> +   * strings.
> +   */
>    private static final String _VISITED_KEY = "af_train.VISITED_TIP";
>    private static final String _ACTIVE_KEY = "af_train.ACTIVE_TIP";
>    private static final String _NEXT_KEY = "af_train.NEXT_TIP";
> @@ -800,23 +1866,22 @@
>  
>    private static final TrinidadLogger _LOG =
>      TrinidadLogger.createTrinidadLogger(TrainRenderer.class);
> -
> -  // for now keep the OraLink/OraDisabledLink styles on the 'a', and
> -  // append train link style class.
> -  private static final Map<String, String> _RESOURCE_KEY_MAP =
> -    new HashMap<String, String>();
> -  private static final String _TRAIN_DISABLED_LINK =
> -    SkinSelectors.LINK_DISABLED_STYLE_CLASS + " " +
> -    SkinSelectors.AF_PROCESS_TRAIN_LINK_STYLE_CLASS;
> -  private static final String _TRAIN_ENABLED_LINK =
> -    SkinSelectors.LINK_STYLE_CLASS + " " +
> -    SkinSelectors.AF_PROCESS_TRAIN_LINK_STYLE_CLASS;
> -
> -  static {
> -    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_DISABLED_STYLE_CLASS,
> -                          _TRAIN_DISABLED_LINK);
> -    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_STYLE_CLASS,
> -                          _TRAIN_ENABLED_LINK);
> +
> +  private static final String[] _EMPTY_STRING_ARRAY;
> +
> +  private static final Map<String, String> _RESOURCE_KEY_MAP;
> +
> +  static
> +  {
> +    _EMPTY_STRING_ARRAY = new String[0];
> +
> +    // Not adding the base link classes as before, those are 
> a nuisance
> +    // while defining the skin since oyu cannot inhibit them 
> as they're
> +    // on the same level as the train selectors.
> +    _RESOURCE_KEY_MAP = new TreeMap<String, String>();
> +    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_STYLE_CLASS,
> +                          SkinSelectors.AF_TRAIN_LINK_STYLE_CLASS);
> +    _RESOURCE_KEY_MAP.put(SkinSelectors.LINK_DISABLED_STYLE_CLASS,
> +                          SkinSelectors.AF_TRAIN_LINK_STYLE_CLASS);
>    }
> -
>  }
> 
> Modified: 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.j> ava
> URL: 
> http://svn.apache.org/viewvc/incubator/adffaces/trunk/trinidad
> /trinidad-impl/src/main/java/org/apache/myfaces/trinidadintern
al/renderkit/core/xhtml/SkinProperties.java?view=diff&rev=451772&r1=> 451771&r2=451772
> ==============================================================
> ================
> --- 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.j> ava (original)
> +++ 
> incubator/adffaces/trunk/trinidad/trinidad-impl/src/main/java/
org/apache/myfaces/trinidadinternal/renderkit/core/xhtml/SkinProperties.j> ava Sun Oct  1 10:26:55 2006
> @@ -39,14 +39,18 @@
>    // FIXME: Name inconsistency, should be AF_PANEL_HEADER
>    public static final String AF_PANELHEADER_INDENT_CONTENT =
>      "af|panelHeader-ora-indent-content";
> +  public static final String AF_PANEL_BORDER_LAYOUT_SPACER_WIDTH =
> +    "af|panelBorderLayout-ora-spacer-width";
>    public static final String AF_PANEL_LIST_DEFAULT_COLUMNS =
>      "af|panelList-ora-default-columns";
>    public static final String AF_TABLE_REPEAT_CONTROL_BAR =
>      "af|table-ora-repeat-control-bar";
>    public static final String AF_TABLE_SELECTION_BAR_IN_TABLE =
>      "af|table-ora-selection-bar-in-table";
> +  public static final String AF_TRAIN_RENDER_PARENT_TRAIN =
> +    "af|train-ora-render-parent-train";
> +  public static final String AF_TRAIN_VISIBLE_STOP_COUNT =
> +    "af|train-ora-visible-stop-count";
>    public static final String AF_TREE_TABLE_SPACER_WIDTH =
>      "af|treeTable-ora-spacer-width";
> -  public static final String AF_PANEL_BORDER_LAYOUT_SPACER_WIDTH =
> -    "af|panelBorderLayout-ora-spacer-width";
>  }
> 
> 
> 
>