static {
    // Note: Java Package versioning is useless during development when
    //       we have no JARs, whereas this technique works with non-JAR
    //       classpaths as well.
    String version = "unknown";

    try {
      URL resource = ClassLoaderUtils.getResource("META-INF/trinidad-version.txt");
      if (resource != null) {
        BufferedReader br = null;
        try {
          InputStream in = resource.openStream();
          br = new BufferedReader(new InputStreamReader(in));
          version = br.readLine();
        } catch (IOException e) {
          _LOG.severe(e);
        } finally {
          if (br != null) br.close();
        }
      }
    } catch (IOException e) {
      _LOG.severe(e);
    } finally {
      _VERSION = version;
    }
  }
/**
 * The implementation of agent interface
 *
 * <p>This implementation supports agents recognized by all uix22 This class returns name strings
 * (instead of int's) Certain agents/platforms have been renamed - using "webkit" (instead of
 * safari), on recommendation from uix team - using "gecko" for all gecko based browsers - using
 * "ppc" (instead of windows) for platform
 *
 * <p>
 */
public class AgentImpl extends DefaultAgent {

  public AgentImpl() {
    this(false);
  }

  public AgentImpl(boolean nullAgentEntry) {
    if (nullAgentEntry) {
      _LOG.warning("UNKNOWN_AGENT_TYPE_CREATE_WITH_NULL");
      _entry = _NULL_AGENT_ENTRY;
    } else {
      _entry = new AgentEntry();
    }
  }

  /*  public AgentImpl(String userAgent, String accept)
  {
    _entry = _getAgentEntry(userAgent, accept);
  }
  */
  @Override
  public Object getType() {
    return _entry._type;
  }

  @Override
  public String getAgentName() {
    return _entry._agent;
  }

  @Override
  public String getAgentVersion() {
    return _entry._agentVersion;
  }

  @Override
  public String getPlatformName() {
    return _entry._platform;
  }

  @Override
  public String getPlatformVersion() {
    return _entry._platformVersion;
  }

  @Override
  public String getHardwareMakeModel() {
    return _entry._makeModel;
  }

  @Override
  public Map<Object, Object> getCapabilities() {
    return _requestCapabilities;
  }

  // setter methods for AgentImpl
  public void setType(Object type) {
    _entry._type = type;
  }

  public void setAgent(String agent) {
    _entry._agent = agent;
  }

  public void setAgentVersion(String version) {
    _entry._agentVersion = version;
  }

  public void setPlatform(String platform) {
    _entry._platform = platform;
  }

  public void setPlatformVersion(String version) {
    _entry._platformVersion = version;
  }

  public void setMakeModel(String makemodel) {
    _entry._makeModel = makemodel;
  }

  public void setAgentEntryToNULL() {
    _entry = _NULL_AGENT_ENTRY;
  }

  // Private entry structure to
  // store the Agent attributes
  private static class AgentEntry {
    Object _type = TYPE_UNKNOWN;
    String _agent;
    String _agentVersion;
    String _platform;
    String _platformVersion;
    String _makeModel;
  }

  void __addRequestCapability(CapabilityKey key, Object value) {
    if (_requestCapabilities == null) {
      _requestCapabilities = new HashMap<Object, Object>();
    }
    _requestCapabilities.put(key, value);
  }

  private HashMap<Object, Object> _requestCapabilities;
  private AgentEntry _entry;
  private static final AgentEntry _NULL_AGENT_ENTRY = new AgentEntry();
  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(AgentImpl.class);
}
public class ConfigParser {
  /** */
  public static RequestContextBean parseConfigFile(ExternalContext externalContext) {
    RequestContextBean bean = new RequestContextBean();

    InputStream in = externalContext.getResourceAsStream(_CONFIG_FILE);
    if (in != null) {
      try {
        InputSource input = new InputSource();
        input.setByteStream(in);
        input.setPublicId(_CONFIG_FILE);

        XMLReader reader = _SAX_PARSER_FACTORY.newSAXParser().getXMLReader();

        reader.setContentHandler(new Handler(bean, externalContext));
        reader.parse(input);
      } catch (IOException ioe) {
        _LOG.warning(ioe);
      } catch (ParserConfigurationException pce) {
        _LOG.warning(pce);
      } catch (SAXException saxe) {
        _LOG.warning(saxe);
      } finally {
        try {
          in.close();
        } catch (IOException ioe) {
          // Ignore
          ;
        }
      }
    }

    String classNameString =
        (String) bean.getProperty(RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY);
    if (classNameString != null) {
      classNameString = classNameString.trim();

      // check if this contains multiple class names for chained processors usecase.
      // Usually the class named are separated by space char.
      String classNames[] = classNameString.split("[ ]+");
      if (classNames.length == 1) {
        // This could be a single processor full override usecase or a chained
        // processor usecase that has only one processor.
        try {
          Class<UploadedFileProcessor> clazz =
              (Class<UploadedFileProcessor>) ClassLoaderUtils.loadClass(classNames[0]);
          if (ChainedUploadedFileProcessor.class.isAssignableFrom(clazz)) {
            // this single chained processor case
            ChainedUploadedFileProcessor cufp[] = {
              (ChainedUploadedFileProcessor) clazz.newInstance()
            };
            bean.setProperty(
                RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY,
                new CompositeUploadedFileProcessorImpl(Arrays.asList(cufp)));
          } else {
            // this is full override usecase
            bean.setProperty(RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY, clazz.newInstance());
          }

        } catch (Exception e) {
          _LOG.severe("CANNOT_INSTANTIATE_UPLOADEDFILEPROCESSOR", e);
          bean.setProperty(
              RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY,
              new CompositeUploadedFileProcessorImpl());
        }
      } else {
        try {
          // chained processors usecase, Multiple processors
          List<ChainedUploadedFileProcessor> processors =
              new ArrayList<ChainedUploadedFileProcessor>(classNames.length);
          for (String className : classNames) {
            Class<ChainedUploadedFileProcessor> clazz =
                (Class<ChainedUploadedFileProcessor>) ClassLoaderUtils.loadClass(className);
            processors.add(clazz.newInstance());
          }
          bean.setProperty(
              RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY,
              new CompositeUploadedFileProcessorImpl(processors));
        } catch (Exception e) {
          _LOG.severe("CANNOT_INSTANTIATE_UPLOADEDFILEPROCESSOR", e);
          bean.setProperty(
              RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY,
              new CompositeUploadedFileProcessorImpl());
        }
      }
    } else {
      // nothing specified, hence use default.
      bean.setProperty(
          RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY, new CompositeUploadedFileProcessorImpl());
    }

    UploadedFileProcessor ufp =
        (UploadedFileProcessor) bean.getProperty(RequestContextBean.UPLOADED_FILE_PROCESSOR_KEY);

    ufp.init(externalContext.getContext());

    if (_LOG.isInfo()) {
      Object debug = bean.getProperty(RequestContextBean.DEBUG_OUTPUT_KEY);
      if (Boolean.TRUE.equals(debug)) _LOG.info("RUNNING_IN_DEBUG_MODE", _CONFIG_FILE);
    }
    return bean;
  }

  private static class Handler extends DefaultHandler {
    @SuppressWarnings("unchecked")
    public Handler(RequestContextBean bean, ExternalContext context) {
      _applicationMap = context.getApplicationMap();
      _bean = bean;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts) {
      _currentText = "";
    }

    @Override
    public void characters(char[] ch, int start, int length) {
      if (_currentText != null) _currentText = _currentText + new String(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
      String currentText = _currentText;
      if (currentText == null) return;

      currentText = currentText.trim();
      if (!"".equals(currentText)) {
        PropertyKey key = _bean.getType().findKey(localName);
        if (key == null) {
          if (_LOG.isWarning()) _LOG.warning("ELEMENT_NOT_UNDERSTOOD", qName);
        } else {
          if (currentText.startsWith("#{") && currentText.endsWith("}")) {
            if (!key.getSupportsBinding()) {
              if (_LOG.isWarning()) _LOG.warning("NOT_SUPPORT_EL_EXPRESSION", qName);
            } else {
              ValueExpression expression =
                  LazyValueExpression.createValueExpression(_currentText, key.getType());
              _bean.setValueExpression(key, expression);
            }
          } else {
            Object value;

            if (key.getType() == Character.class) {
              value = currentText.charAt(0);
            } else if (key.getType() == Integer.class) {
              value = _getIntegerValue(currentText, qName);
            } else if (key.getType() == Long.class) {
              value = _getLongValue(currentText, qName);
            } else if (key.getType() == Boolean.class) {
              value = ("true".equalsIgnoreCase(currentText) ? Boolean.TRUE : Boolean.FALSE);
            } else if (key.getType() == TimeZone.class) {
              value = DateUtils.getSupportedTimeZone(currentText);
              if (value == null) {
                _LOG.warning("INVALID_TIMEZONE_IN_CONFIG", currentText);
              }
            } else if (key.getType() == Locale.class) {
              currentText = currentText.replace('_', '-');
              value = LocaleUtils.getLocaleForIANAString(currentText);
            } else if (key.getType().isEnum()) {
              // TODO: warn when value is not OK
              try {
                value = Enum.valueOf((Class<? extends Enum>) key.getType(), currentText);
              } catch (IllegalArgumentException iae) {
                _LOG.warning("INVALID_ENUM_IN_CONFIG", new Object[] {currentText, qName});
                return;
              }
            } else if (key.getType() == AccessibilityProfile.class) {
              value = _getAccessibilityProfile(currentText);
            } else {
              value = currentText;
            }

            if (key == RequestContextBean.REMOTE_DEVICE_REPOSITORY_URI) {
              _applicationMap.put("remote-device-repository-uri", value);
            } else if (key == RequestContextBean.CLIENT_VALIDATION_DISABLED_KEY) {
              if (Boolean.TRUE.equals(value))
                _bean.setProperty(
                    RequestContextBean.CLIENT_VALIDATION_KEY,
                    RequestContext.ClientValidation.DISABLED);
            } else {
              _bean.setProperty(key, value);
            }
          }
        }
      }

      _currentText = null;
    }

    private static Integer _getIntegerValue(String text, String qName) {
      Integer value = null;
      try {
        value = Integer.valueOf(text);
      } catch (NumberFormatException nfe) {
        if (_LOG.isWarning()) {
          _LOG.warning("ELEMENT_ONLY_ACCEPT_INTEGER", qName);
        }
      }
      return value;
    }

    private static Long _getLongValue(String text, String qName) {
      Long value = null;
      try {
        value = Long.valueOf(text);
      } catch (NumberFormatException nfe) {
        if (_LOG.isWarning()) {
          _LOG.warning("ELEMENT_ONLY_ACCEPT_LONG", qName);
        }
      }
      return value;
    }

    // Parses the text into an AccessibilityProfile.
    private static AccessibilityProfile _getAccessibilityProfile(String text) {
      AccessibilityProfile.ColorContrast colorContrast = null;
      AccessibilityProfile.FontSize fontSize = null;

      // Note: we do the parsing here in the ConfigParser instead of in
      // RequestContextImpl so that we can easily detect/log any problems
      // once at startup.  Also nice to do this here so that we have some
      // chance of actually logging line numbers, though at the moment it
      // looks like our Handler doesn't implement Locator.

      StringTokenizer tokens = new StringTokenizer(text);
      while (tokens.hasMoreTokens()) {
        String token = tokens.nextToken();

        if ("high-contrast".equals(token)) {
          colorContrast = AccessibilityProfile.ColorContrast.HIGH;
        } else if ("large-fonts".equals(token)) {
          fontSize = AccessibilityProfile.FontSize.LARGE;
        } else {
          _LOG.warning("INVALID_ACC_PROFILE", new Object[] {token});
        }
      }

      return AccessibilityProfile.getInstance(colorContrast, fontSize);
    }

    private RequestContextBean _bean;
    private String _currentText;
    private Map<String, Object> _applicationMap;
  }

  private static final SAXParserFactory _SAX_PARSER_FACTORY;

  static {
    _SAX_PARSER_FACTORY = SAXParserFactory.newInstance();
    _SAX_PARSER_FACTORY.setNamespaceAware(true);
  }

  private static final String _CONFIG_FILE = "/WEB-INF/trinidad-config.xml";
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(ConfigParser.class);
}
/**
 * Renderer for PanelRadio
 *
 * @version $Name: $ ($Revision$) $Date$
 */
public class CorePanelRadioRenderer extends ShowOneListRendererBase {
  /** {@inheritDoc} */
  @Override
  protected void renderListDisplay(
      FacesContext context, UIComponent component, String disclosedChildId) throws IOException {
    _LOG.finest("CorePanelRadioRenderer.renderRadioFacet: disclosedChildId: {0}", disclosedChildId);

    // This renders the select controls alongwith javascript onchange handler.
    UIXRenderingContext rCtx = getRenderingContext(context, component);

    String compId = component.getClientId(context);

    ResponseWriter out = context.getResponseWriter();

    // draw table to contain the select UI control
    out.startElement("table", component);
    out.writeAttribute("id", compId + _RADIO_TABLE_SUFFIX_ID_CONST, null);
    out.writeAttribute("border", "0", null);
    out.writeAttribute("cellspacing", "0", null);
    out.writeAttribute("cellpadding", "0", null);

    if (!XhtmlLafRenderer.isInaccessibleMode(rCtx)) {
      out.writeAttribute("summary", "", null);
    }

    out.startElement("tr", component);

    String label = (String) component.getAttributes().get("label");

    out.startElement("td", component);
    out.writeAttribute("align", "left", null);
    out.writeAttribute("nowrap", Boolean.TRUE, null);
    out.startElement("span", component);

    XhtmlLafRenderer.renderStyleClassAttribute(rCtx, SkinSelectors.AF_LABEL_TEXT_STYLE_CLASS);

    if (label != null) out.writeText(label, null);

    out.endElement("span");
    out.endElement("td");

    // Render filler / separator between label and select control
    renderSpacerTD(out, component, getLabelControlSeparatorSize());

    _renderRadioItemsInTD(context, component, out, rCtx, compId, disclosedChildId);

    out.endElement("tr");
    out.endElement("table");
  }

  /**
   * Generates markup for rendering HTML radio controls.
   *
   * <p>Each radio control is corresponding to a rendered UIXShowDetail child. The disable
   * showDetail children are shown as disabled radio buttons.
   */
  @SuppressWarnings("unchecked")
  private void _renderRadioItemsInTD(
      FacesContext context,
      UIComponent component,
      ResponseWriter out,
      UIXRenderingContext rCtx,
      String compId,
      String disclosedChildId)
      throws IOException {
    out.startElement("td", component);
    out.writeAttribute("valign", "top", null);
    out.writeAttribute("nowrap", Boolean.TRUE, null);

    String formName = RenderUtils.getFormId(context, component);

    // each of the radio buttons would occupy a td in a tr
    // so there will be as many rows as the number of children and each row
    // in turn will have only one td - to contain the radio button
    out.startElement("table", component);
    out.writeAttribute("id", compId + disclosedChildId, null);
    out.writeAttribute("summary", "", null);
    out.writeAttribute("border", "0", null);
    out.writeAttribute("cellspacing", "0", null);
    out.writeAttribute("cellpadding", "0", null);

    ListIterator<UIComponent> children = component.getChildren().listIterator();
    while (children.hasNext()) {
      UIComponent child = children.next();
      if (!(child instanceof UIXShowDetail)) {
        continue;
        // Can't do any thing with non-showDetail children.
      }
      UIXShowDetail detailItem = (UIXShowDetail) child;

      String childClientId = child.getClientId(context);
      out.startElement("tr", component);
      out.startElement("td", component);

      boolean isRTL = BaseLafUtils.isRightToLeft(rCtx);
      if (isRTL) {
        out.writeAttribute("align", "right", null);
      } else {
        out.writeAttribute("align", "left", null);
      }
      out.writeAttribute("valign", "top", null);
      out.writeAttribute("nowrap", Boolean.TRUE, null);

      out.startElement("span", component);
      out.writeAttribute("id", childClientId + _RADIO_SPAN_SUFFIX_ID_CONST, null);

      Boolean disabledObj =
          (Boolean) detailItem.getAttributes().get(UIConstants.DISABLED_ATTR.getAttributeName());
      boolean disabled = false; // by default is enabled.
      if (disabledObj != null) {
        disabled = disabledObj.booleanValue();
      }

      if (!disclosedChildId.equals(childClientId) && (!disabled)) {
        boolean isImmediate = detailItem.isImmediate();
        String submitJS =
            _getRadioSubmitJS(component, rCtx, formName, compId, childClientId, isImmediate);
        // PH:onclick javascript handler for a HTML SPAN element is not supported
        // on PIE, IE Mobile or Blackberry 4.0. Therefore, create onclick
        // javascript for non-PDAs only.
        if (!CoreRenderer.isPDA(RenderingContext.getCurrentInstance()))
          out.writeAttribute("onclick", submitJS, null);
      }

      // render the radio button now
      out.startElement("input", component);
      out.writeAttribute("id", childClientId, null);
      out.writeAttribute("value", childClientId, null);
      out.writeAttribute("name", compId, null);

      // PH: onclick javascript handler for an INPUT element is supported on a
      // PDA. Therefore, create javascript for onclick on an INPUT element
      // instead of a SPAN element.
      if (CoreRenderer.isPDA(RenderingContext.getCurrentInstance())) {
        boolean isImmediate = detailItem.isImmediate();
        String submitJS =
            _getRadioSubmitJS(component, rCtx, formName, compId, childClientId, isImmediate);
        out.writeAttribute("onclick", submitJS, null);
      }

      if (disabled) {
        out.writeAttribute("disabled", Boolean.TRUE, null);
      }

      out.writeAttribute("type", "radio", null);
      if (disclosedChildId.equals(childClientId)) {
        out.writeAttribute("checked", Boolean.TRUE, null);
      }
      out.endElement("input");

      out.startElement("label", component);
      out.writeAttribute("for", childClientId, null);

      Character accessChar = (Character) detailItem.getAttributes().get("accessKey");
      if (accessChar != null) {
        out.writeAttribute("accessKey", accessChar.toString(), null);
      }

      out.startElement("span", component);

      String radioSpanClass = getFieldTextClass();
      if (disabled) {
        radioSpanClass = SkinSelectors.AF_FIELD_TEXT_DISABLED_STYLE_CLASS;
      }
      XhtmlLafRenderer.renderStyleClassAttribute(rCtx, radioSpanClass);

      writeLabel(out, detailItem, (String) detailItem.getAttributes().get("text"));

      out.endElement("span");
      out.endElement("label");
      out.endElement("span");
      out.endElement("td");
      out.endElement("tr");
    }

    // For Non-JavaScript browsers, render a input element(type= submit) to
    // submit the page. Encode the name attribute with the parameter name
    // and value thus it would enable the browsers to include the name of
    // this element in its payLoad if it submits the page.

    if (!XhtmlRenderer.supportsScripting(RenderingContext.getCurrentInstance())) {
      out.startElement("tr", component);
      out.startElement("td", component);

      if (BaseLafUtils.isRightToLeft(rCtx)) {
        out.writeAttribute("align", "right", null);
      } else {
        out.writeAttribute("align", "left", null);
      }

      out.writeAttribute("valign", "top", null);
      out.writeAttribute("nowrap", Boolean.TRUE, null);

      String nameAttri =
          XhtmlUtils.getEncodedParameter(XhtmlConstants.MULTIPLE_VALUE_PARAM)
              + XhtmlUtils.getEncodedParameter(compId)
              + XhtmlUtils.getEncodedParameter(XhtmlConstants.EVENT_PARAM)
              + XhtmlConstants.SHOW_EVENT;

      out.startElement("span", null);
      out.startElement("input", null);
      out.writeAttribute("value", XhtmlConstants.NO_JS_PARAMETER_KEY_BUTTON, null);
      out.writeAttribute("type", "submit", null);
      out.writeAttribute("name", nameAttri, null);
      out.endElement("input");
      out.endElement("span");
      out.endElement("td");
      out.endElement("tr");
    }

    out.endElement("table");
    out.endElement("td");
  }

  /**
   * Gets onclick javascript to be associated with radio onlcick event.
   *
   * <p>Checks if component is contained within a form, if not, returns null. Further, checks if PPR
   * is supported and returns a script to be called for this case else returns a script where PPR is
   * not required.
   */
  private String _getRadioSubmitJS(
      UIComponent component,
      UIXRenderingContext rCtx,
      String formName,
      String compId,
      String detailChildId,
      boolean isImmediate) {
    if (formName == null) {
      _LOG.warning("PAGE_NOT_CONTAIN_FORM_ELEMENT");
      return null;
    }

    String validate = "1";
    if (isImmediate) {
      validate = "0";
    }

    // Check if PPR enabled, do a _submitPartialChange, else do a formSubmit.
    String onClickHandler = "";
    boolean pprEnabled = elementSupportsPartial(rCtx, compId);
    if (pprEnabled) {
      // PH:If agent is of type PDA, call submitForm instead of doing a full
      // page submission since PPR on this component is not supported although
      // pprEnabled is true for PIE and IE Mobile
      if (CoreRenderer.isPDA(RenderingContext.getCurrentInstance())) {
        StringBuilder jsBuff = new StringBuilder(135);
        jsBuff
            .append("submitForm('")
            .append(formName)
            .append("',")
            .append(validate)
            .append(",{event:'show',source:'")
            .append(detailChildId)
            .append("'});return true;");

        onClickHandler = jsBuff.toString();
      } else {
        StringBuilder jsBuff = new StringBuilder(220);
        jsBuff
            .append("_submitPartialChange('")
            .append(formName)
            .append("',")
            .append(validate)
            .append(", {event:'show',source:'")
            .append(detailChildId)
            .append("'});return true;");

        onClickHandler = jsBuff.toString();
      }
    } else {
      StringBuilder jsBuff = new StringBuilder(135);
      jsBuff
          .append("submitForm('")
          .append(formName)
          .append("',")
          .append(validate)
          .append(",{event:'show',source:'")
          .append(detailChildId)
          .append("'});return true;");
      onClickHandler = jsBuff.toString();
    }
    return onClickHandler;
  }

  private static final String _RADIO_TABLE_SUFFIX_ID_CONST = "_sor_tbl";
  private static final String _RADIO_SPAN_SUFFIX_ID_CONST = _RADIO_TABLE_SUFFIX_ID_CONST + "_span";

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(CorePanelRadioRenderer.class);
}
/**
 * Change specialization for re-ordering of children. While applying this Change, the specified
 * order of children is restored.
 *
 * @version $Name: $ ($Revision:
 *     adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/ReorderChildrenComponentChange.java#0
 *     $) $Date: 10-nov-2005.19:10:01 $
 */
public class ReorderChildrenComponentChange extends ComponentChange implements DocumentChange {
  /**
   * Constructs a ReorderChange with the given List of identifiers for children.
   *
   * @param childIds An in-order collection (List) of Ids (as java.lang.String) of child components.
   *     This List implementation should be of type java.io.Serializable in order to be persisted.
   *     If no identifier was passed, it would be assumed that the list consists of the Ids.
   * @throws IllegalArgumentException if supplied childIds were to be null.
   */
  public ReorderChildrenComponentChange(List<String> childIds) {
    this(childIds, "id");
  }

  /**
   * Constructs a ReorderChange with the given List of identifiers for children.
   *
   * @param childIds An in-order collection (List) of Ids (as java.lang.String) of child components.
   *     This List implementation should be of type java.io.Serializable in order to be persisted.
   * @param identifier Determines the type of identifiers which the List consists of.
   * @throws IllegalArgumentException if supplied childIds were to be null or supplied identifier
   *     was to be null or emtpy string.
   */
  public ReorderChildrenComponentChange(List<String> childIds, String identifier) {
    if (childIds == null)
      throw new IllegalArgumentException(
          _LOG.getMessage("CANNOT_CONSTRUCT_REORDERCHANGE_WITH_NULL_ID"));

    if (identifier == null || "".equals(identifier))
      throw new IllegalArgumentException(_LOG.getMessage("IDENTIFIER_TYPE_CANNOT_BE_NULL"));

    // make serializable copy of list
    _childIds = Collections.unmodifiableList(new ArrayList<String>(childIds));

    _identifier = identifier;
  }

  /** Returns an unmodifiable List of the identifiers for the children. */
  public List<String> getChildIds() {
    return _childIds;
  }

  /** Returns the identifier type. */
  public final String getIdentifier() {
    return _identifier;
  }

  /**
   * {@inheritDoc} In case children were to be removed between the time when this Change was added,
   * and the time when it was applied, maybe due to application of a RemoveChildrenChange, such
   * children are not re-instated. In case children were to be added between the time when this
   * Change was added, and the time when it was applied, maybe due to application of an
   * AddChildChange, such children are appended to the end of the list in preserving the order in
   * which they were added (that is they appear at the end).
   */
  @SuppressWarnings("unchecked")
  @Override
  public void changeComponent(UIComponent uiComponent) {
    int childCount = uiComponent.getChildCount();
    if (childCount == 0) return;

    // build order map of of current Nodes, keyed by id
    Map<String, UIComponent> childrenMap = new LinkedHashMap<String, UIComponent>();

    List<UIComponent> children = uiComponent.getChildren();

    int fakeIndex = 0;
    for (UIComponent child : children) {
      String attrValue = (String) child.getAttributes().get(_identifier);

      // create a dummy key to maintain order of children whose identifier
      // does not exist
      if (attrValue == null) {
        attrValue = Integer.valueOf(fakeIndex++).toString();
      }
      childrenMap.put(attrValue, child);
    }

    // remove the children so that we can add them back in
    children.clear();

    //
    // put children back in, in order
    //
    for (String currReorderID : _childIds) {
      UIComponent currChild = childrenMap.remove(currReorderID);

      if (currChild != null) {
        children.add(currChild);
      }
    }

    // add in all of the rest of the children in
    // relative order they originally appeared
    children.addAll(childrenMap.values());
  }

  /**
   * {@inheritDoc} In case children were to be removed between the time when this Change was added,
   * and the time when it was applied, maybe due to application of a RemoveChildrenChange, such
   * children are not re-instated. In case children were to be added between the time when this
   * Change was added, and the time when it was applied, maybe due to application of an
   * AddChildChange, such children are appended to the end of the list in preserving the order in
   * which they were added (that is they appear at the end).
   */
  public void changeDocument(Node componentNode) {
    // build order map of of current Nodes, keyed by id
    LinkedHashMap<String, Node> currChildrenMap = new LinkedHashMap<String, Node>(13);

    Node currChild = componentNode.getFirstChild();

    int fakeIndex = 0;
    while (currChild != null) {
      NamedNodeMap attributes = currChild.getAttributes();

      String currKey = null;
      if (attributes != null) {
        Node idAttr = attributes.getNamedItem(_identifier);

        if (idAttr != null) {
          currKey = idAttr.getNodeValue();
        }
      }

      // create a dummy key to maintain order of non-ided children
      if (currKey == null) {
        // =-= bts What about insignificant whitespace?
        currKey = Integer.valueOf(fakeIndex++).toString();
      }

      currChildrenMap.put(currKey, currChild);

      // remove the children so that we can add them back in
      componentNode.removeChild(currChild);

      // next node is first node again
      currChild = componentNode.getFirstChild();
    }

    //
    // put children back in, in order
    //
    for (String currReorderID : _childIds) {
      currChild = currChildrenMap.remove(currReorderID);
      if (currChild != null) {
        componentNode.appendChild(currChild);
      }
    }

    // add in all of the rest of the children in
    // relative order they originally appeared
    for (Map.Entry<String, Node> entry : currChildrenMap.entrySet()) {
      componentNode.appendChild(entry.getValue());
    }
  }

  /** Returns true if adding the DocumentChange should force the JSP Document to reload */
  public boolean getForcesDocumentReload() {
    return false;
  }

  private final List<String> _childIds;
  private final String _identifier;
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(ReorderChildrenComponentChange.class);
  private static final long serialVersionUID = 1L;
}
/**
 * Base class for Renderers that generate images.
 *
 * @version $Name: $ ($Revision$) $Date$
 * @deprecated This class comes from the old Java 1.2 UIX codebase and should not be used anymore.
 */
@Deprecated
abstract class GeneratedImageRenderer extends HtmlLafRenderer implements ImageConstants {

  protected void renderImage(
      UIXRenderingContext context, UINode node, ImageProviderResponse response) throws IOException {
    renderImage(context, node, response, null);
  }

  /**
   * we do not want shortDesc rendered by XhtmlLafRenderer as this puts it as the title attribute;
   * instead we write shortDesc as the alt text of the image.
   */
  @Override
  protected void renderShortDesc(UIXRenderingContext context, UINode node) {}

  protected void renderImage(
      UIXRenderingContext context, UINode node, ImageProviderResponse response, String mapName)
      throws IOException {
    // We assume that we have an image to render
    assert (response != null);

    boolean disabled = isDisabled(context, node);
    boolean hasMap = (response.getMapAreas() != null);
    Object shortDesc = getShortDesc(context, node);

    boolean hasLink = !disabled && !hasMap;
    Object destination = hasLink ? getDestination(context, node) : null;

    renderImage(context, node, response, hasMap, mapName, shortDesc, destination);
  }

  protected void renderImage(
      UIXRenderingContext context,
      UINode node,
      ImageProviderResponse response,
      boolean hasMap,
      String mapName,
      Object shortDesc,
      Object destination)
      throws IOException {
    assert node != null;

    boolean hasLink = (destination != null);
    Object longDesc = getLongDesc(context, node);
    String imageStyle = getImageStyle(context, node);
    String imageStyleClass = getImageStyleClass(context, node);

    ResponseWriter writer = context.getResponseWriter();
    UIComponent component = (node == null) ? null : node.getUIComponent();

    if (hasLink) {
      writer.startElement("a", component);
      renderEncodedActionURI(context, "href", destination);
      renderAttribute(context, node, "target", TARGET_FRAME_ATTR);

      // Don't render access key on Netscape... Netscape doesn't
      // support access keys - if this ever changes, it would
      // be confusing if we rendered the accessKey attr without
      // also underlining the access key in the corresponding text.
      if (!isNetscape(context)) {
        renderButtonAccessKey(context, node);
      }

      // If we have a link, we render the standard attributes on
      // the link instead of on the image
      renderAttributes(context, node);
    }

    writer.startElement("img", component);

    // Write out all of the standard attrs
    if (!hasLink) renderAttributes(context, node);

    // Write out the image url
    writeCacheImageURI(context, "src", response.getImageURI());

    // Write out the description attrs.
    renderAltAndTooltipForImage(context, shortDesc);
    renderAttribute(context, "longdesc", longDesc);

    // Null out the border
    renderAttribute(context, "border", "0");

    // Render alignment.  Is this necessary?
    renderHAlign(context, node);

    // This is to address bug #2047577
    // Instead of adding an attribute to control placement of the button,
    // we just force it to middle (which is what everybody wants anyway).
    // We have to make sure we don't put the align attribute in twice.
    // We allow the hAlign attribute to take precedence.
    if (node.getAttributeValue(context, H_ALIGN_ATTR) == null) {
      Object valign = getVAlign(context, node);
      if (valign != null) renderAttribute(context, "align", valign);
    }

    // Render the width/height
    int width = response.getWidth();
    int height = response.getHeight();

    if (width != ImageProviderResponse.UNKNOWN_SIZE)
      renderAttribute(context, "width", IntegerUtils.getString(width));
    if (height != ImageProviderResponse.UNKNOWN_SIZE)
      renderAttribute(context, "height", IntegerUtils.getString(height));

    // The image map
    if (hasMap) writer.writeAttribute("usemap", "#" + mapName, null);

    if (imageStyle != null) renderAttribute(context, "style", imageStyle);
    if (imageStyleClass != null) renderStyleClassAttribute(context, imageStyleClass);

    writer.endElement("img");

    if (hasLink) writer.endElement("a");
  }

  @Override
  protected Object getText(UIXRenderingContext context, UINode node) {
    return node.getAttributeValue(context, TEXT_ATTR);
  }

  @Override
  protected Object getShortDesc(UIXRenderingContext context, UINode node) {
    Object desc = node.getAttributeValue(context, SHORT_DESC_ATTR);

    if ((desc == null) && !isInaccessibleMode(context)) {
      return getText(context, node);
    }

    return desc;
  }

  protected Object getImageName(UIXRenderingContext context, UINode node) {
    return node.getAttributeValue(context, ID_ATTR);
  }

  protected Object getLongDesc(UIXRenderingContext context, UINode node) {
    return node.getAttributeValue(context, LONG_DESC_URL_ATTR);
  }

  /** Returns the destination to use for the GeneratedImageRenderer */
  protected Object getDestination(UIXRenderingContext context, UINode node) {
    if (!supportsNavigation(context)) return null;

    Object destination = node.getAttributeValue(context, DESTINATION_ATTR);

    // If we have an onclick handler, always provide a destination
    if ((destination == null) && supportsIntrinsicEvents(context)) {
      Object onClick = getOnClick(context, node);

      if (onClick != null) {
        destination = "#";
      }
    }

    return destination;
  }

  protected String getImageStyle(UIXRenderingContext context, UINode node) {
    return null;
  }

  protected String getImageStyleClass(UIXRenderingContext context, UINode node) {
    return null;
  }

  protected void renderButtonAccessKey(UIXRenderingContext context, UINode node)
      throws IOException {
    renderAttribute(context, node, "accesskey", ACCESS_KEY_ATTR);
  }

  protected static String getURLAttribute(
      UIXRenderingContext context, UINode node, AttributeKey attrKey) {
    Object o = node.getAttributeValue(context, attrKey);
    if (o != null) return o.toString();

    return null;
  }

  // Returns the style for the specified name
  protected static Style getStyle(UIXRenderingContext context, UINode node, String name) {
    if (name == null) return null;

    StyleMap map = context.getStyleContext().getStyleMap();
    if (map == null) return null;

    return map.getStyleByName(context.getStyleContext(), name);
  }

  // Returns the vertical alignment
  protected Object getVAlign(UIXRenderingContext context, UINode node) {
    return null;
  }

  protected static int getFontStyle(
      UIXRenderingContext context,
      UINode node,
      Style classStyle,
      Style inlineStyle,
      String styleName) {
    int fontStyle = Font.PLAIN;
    int fontWeight = Font.PLAIN;
    boolean gotStyle = false;
    boolean gotWeight = false;
    Object value = null;

    // First, try getting font-style and font-weight from inline style
    if (inlineStyle != null) {
      value = _parseValue(inlineStyle, null, Style.FONT_STYLE_KEY);

      if (value != null) {
        fontStyle = _getAWTFontStyle(value);
        gotStyle = true;
      }

      value = _parseValue(inlineStyle, null, Style.FONT_WEIGHT_KEY);
      if (value != null) {
        fontWeight = _getAWTFontWeight(value);
        gotWeight = true;
      }
    }

    if (classStyle != null) {
      if (!gotStyle) {
        value = _parseValue(classStyle, styleName, Style.FONT_STYLE_KEY);
        if (value != null) fontStyle = _getAWTFontStyle(value);
      }

      if (!gotWeight) {
        value = _parseValue(classStyle, styleName, Style.FONT_WEIGHT_KEY);

        if (value != null) fontWeight = _getAWTFontWeight(value);
      }
    }

    return (fontStyle | fontWeight);
  }

  protected static int getFontSize(
      UIXRenderingContext context,
      UINode node,
      Style classStyle,
      Style inlineStyle,
      String styleName) {
    // First, try size from inline font
    if (inlineStyle != null) {
      Object value = _parseValue(inlineStyle, null, Style.FONT_SIZE_KEY);

      if (value instanceof Integer) return ((Integer) value).intValue();
    }

    if (styleName != null) {
      Object value = _parseValue(classStyle, styleName, Style.FONT_SIZE_KEY);

      if (value instanceof Integer) return ((Integer) value).intValue();
    }

    return _DEFAULT_FONT_SIZE;
  }

  @SuppressWarnings("unchecked")
  protected static Collection<Object> getFontFamilies(
      UIXRenderingContext context, UINode node, Style style, String styleName) {
    if (style != null) {
      if (_parseValue(style, null, Style.FONT_FAMILIES_KEY) instanceof Collection) {
        return (Collection<Object>) _parseValue(style, null, Style.FONT_FAMILIES_KEY);
      } else {
        Collection<Object> parsedValueList = new ArrayList<Object>();
        parsedValueList.add(_parseValue(style, null, Style.FONT_FAMILIES_KEY));
        return parsedValueList;
      }
    }

    return null;
  }

  protected static Color getBackground(
      UIXRenderingContext context,
      UINode node,
      Style classStyle,
      Style inlineStyle,
      String styleName) {
    if (inlineStyle != null) {
      Color background = (Color) _parseValue(inlineStyle, null, Style.BACKGROUND_KEY);
      if (background != null) return background;
    }

    return (Color) _parseValue(classStyle, styleName, Style.BACKGROUND_KEY);
  }

  protected static Color getForeground(
      UIXRenderingContext context,
      UINode node,
      Style classStyle,
      Style inlineStyle,
      String styleName) {
    if (inlineStyle != null) {
      Color foreground = (Color) _parseValue(inlineStyle, null, Style.FOREGROUND_KEY);
      if (foreground != null) return foreground;
    }

    return (Color) _parseValue(classStyle, styleName, Style.FOREGROUND_KEY);
  }

  protected static Color getSurroundingColor(UIXRenderingContext context) {
    if (BaseDesktopUtils.supportsTransparentImages(context)) return null;

    return BaseDesktopUtils.getBackgroundColor(context);
  }

  protected static boolean isTextAntialiased(
      UIXRenderingContext context, UINode node, Style classStyle, Style inlineStyle) {
    if (inlineStyle != null) {
      Object value = inlineStyle.getParsedProperty(Style.TEXT_ANTIALIAS_KEY);
      return Boolean.TRUE.equals(value);
    }

    if (classStyle != null) {
      Object value = classStyle.getParsedProperty(Style.TEXT_ANTIALIAS_KEY);
      return Boolean.TRUE.equals(value);
    }

    return false;
  }

  private static Object _parseValue(Style style, String styleName, ParsedPropertyKey key) {
    if (style == null) return null;

    Object value = null;

    try {
      value = style.getParsedProperty(key);
    } catch (PropertyParseException e) {
      if (_LOG.isWarning())
        _LOG.warning(
            styleName == null
                ? "Error while parsing inline style"
                : "Error while parsing style class \"" + styleName + "\"",
            e);
    }

    return value;
  }

  private static int _getAWTFontStyle(Object style) {
    if (style == Style.ITALIC_FONT_STYLE) return Font.ITALIC;

    return Font.PLAIN;
  }

  private static int _getAWTFontWeight(Object weight) {
    if (weight == Style.BOLD_FONT_WEIGHT) return Font.BOLD;

    return Font.PLAIN;
  }

  private static final int _DEFAULT_FONT_SIZE = 12;
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(GeneratedImageRenderer.class);
}
class PageFlowScopeMap implements Map<String, Object>, Serializable {
  /** Return a PageFlowScopeMap stored with a token. */
  public static PageFlowScopeMap getPageFlowScopeMap(
      FacesContext context, String token, int lifetime) {
    TokenCache cache = _getRootTokenCache(context, lifetime);
    PageFlowScopeMap map = _getPageFlowScopeMap(context, cache, token);

    if (_LOG.isFine()) {
      _LOG.fine(
          "pageFlowScope: found map {0} at token {1}",
          new Object[] {(map == null) ? (Object) "null" : map, token});
    }

    if (map == null) {
      return null;
    } else {
      // Don't return the same instance of PageFlowScopeMap as was used
      // on the previous page;  otherwise, for instance, we'll overwrite
      // its token as we mutate.  Instead, create a new PageFlowScopeMap,
      // but reuse the _map;  we'll clone the _map itself if we mutate
      return new PageFlowScopeMap(map._map, token, map._sharedData);
    }
  }

  private static PageFlowScopeMap _getPageFlowScopeMap(
      FacesContext context, TokenCache cache, String token) {
    if (token == null) throw new NullPointerException();

    int lastSeparator = token.lastIndexOf(TokenCache.SEPARATOR_CHAR);
    String parentToken;
    String childToken;
    if (lastSeparator < 0) {
      parentToken = null;
      childToken = token;
    } else {
      parentToken = token.substring(0, lastSeparator);
      childToken = token.substring(lastSeparator + 1);
    }

    Map<String, Object> storeMap = _createMapToStore(context, parentToken);
    return (PageFlowScopeMap) storeMap.get(childToken);
  }

  /** Only for serialization */
  public PageFlowScopeMap() {}

  public PageFlowScopeMap(int lifetime) {
    this(new HashMap<String, Object>(13), null, new SharedData(lifetime));
  }

  private PageFlowScopeMap(HashMap<String, Object> map, String token, SharedData sharedData) {
    _map = map;
    _sharedData = sharedData;
    _token = token;
  }

  //
  // Create a PageFlowScopeMap pointing at a parent
  //
  private PageFlowScopeMap(PageFlowScopeMap parent, boolean copyParent) {
    assert (parent != null);

    _sharedData = new SharedData(parent._sharedData._lifetime);
    _sharedData._parent = parent;

    _map = new HashMap<String, Object>();
    if (copyParent) _map.putAll(parent._map);
  }

  public PageFlowScopeMap getParent() {
    return _sharedData._parent;
  }

  public synchronized String getToken(FacesContext context) {
    if (_token != null) return _token;

    // Don't need a token when nothing's in the map, and we
    // don't have a parent
    if (isEmpty() && (_sharedData._children == null) && (_sharedData._parent == null)) return null;

    String parentToken;
    TokenCache cache;
    if (_sharedData._parent != null) {
      parentToken = _sharedData._parent.getToken(context);
      cache = _sharedData._parent._getTokenCache();
    } else {
      parentToken = null;
      cache = _getRootTokenCache(context, _sharedData._lifetime);
    }

    Map<String, Object> store = _createMapToStore(context, parentToken);

    String token = cache.addNewEntry(this, store);

    if (parentToken != null) token = parentToken + TokenCache.SEPARATOR_CHAR + token;

    _token = token;

    // With a new token, there cannot be any shared children
    // with a prior request.
    if (_sharedData._children != null) {
      // =-=AEW NEED TO CLONE SHARED DATA
      _LOG.fine("Discarding child PageFlowScopes; new token is {0}", token);
      _sharedData._children = null;
    }

    return _token;
  }

  @SuppressWarnings("unchecked")
  private static Map<String, Object> _createMapToStore(FacesContext context, String parentToken) {
    String fullToken;
    if (parentToken == null) {
      fullToken = _PAGE_FLOW_SCOPE_CACHE + TokenCache.SEPARATOR_CHAR;
    } else {
      fullToken =
          (_PAGE_FLOW_SCOPE_CACHE
              + TokenCache.SEPARATOR_CHAR
              + parentToken
              + TokenCache.SEPARATOR_CHAR);
    }

    return new SubKeyMap(context.getExternalContext().getSessionMap(), fullToken);
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof PageFlowScopeMap) o = ((PageFlowScopeMap) o)._map;

    return _map.equals(o);
  }

  @Override
  public int hashCode() {
    return _map.hashCode();
  }

  public int size() {
    return _map.size();
  }

  public boolean isEmpty() {
    return _map.isEmpty();
  }

  public boolean containsKey(Object key) {
    return _map.containsKey(key);
  }

  public boolean containsValue(Object value) {
    return _map.containsValue(value);
  }

  public Collection<Object> values() {
    // Use an unmodifiableCollection to save me the headache
    // of catching mutations
    return Collections.unmodifiableCollection(_map.values());
  }

  public Set<Map.Entry<String, Object>> entrySet() {
    // Use an unmodifiableSet to save me the headache
    // of catching mutations
    return Collections.unmodifiableSet(_map.entrySet());
  }

  public Set<String> keySet() {
    // Use an unmodifiableSet to save me the headache
    // of catching mutations
    return Collections.unmodifiableSet(_map.keySet());
  }

  public Object get(Object key) {
    return _map.get(key);
  }

  public Object put(String key, Object value) {
    _detachIfNeeded();
    if (_LOG.isFine()) {
      _LOG.fine("pageFlowScope: put({0}, {1})", new Object[] {key, value});
    }

    return _map.put(key, value);
  }

  public Object remove(Object key) {
    _detachIfNeeded();
    if (_LOG.isFine()) {
      _LOG.fine("pageFlowScope: remove({0})", key);
    }

    return _map.remove(key);
  }

  public void putAll(Map<? extends String, ? extends Object> t) {
    _detachIfNeeded();
    if (_LOG.isFine()) {
      _LOG.fine("pageFlowScope: putAll({0})", t);
    }
    _map.putAll(t);
  }

  public void clear() {
    _detachIfNeeded();
    if (_LOG.isFine()) {
      _LOG.fine("pageFlowScope: clear()");
    }
    _map.clear();
  }

  public PageFlowScopeMap createChild(boolean copyParent) {
    return new PageFlowScopeMap(this, copyParent);
  }

  public void discard() {
    FacesContext context = FacesContext.getCurrentInstance();

    String token = getToken(context);
    int lastSeparator = token.lastIndexOf(TokenCache.SEPARATOR_CHAR);

    String parentToken;
    String childToken;
    if (lastSeparator < 0) {
      parentToken = null;
      childToken = token;
    } else {
      parentToken = token.substring(0, lastSeparator);
      childToken = token.substring(lastSeparator + 1);
    }

    // Remove ourselves
    if (_sharedData._parent != null) {
      Map<String, Object> storeMap = _createMapToStore(context, parentToken);
      _sharedData._parent._sharedData._children.removeOldEntry(childToken, storeMap);
    }

    // And clean up all of our children
    _removeAllChildren(context, token);
  }

  private void _removeAllChildren(FacesContext context, String token) {
    // Clear everything - note that because of naming conventions,
    // this will in fact automatically recurse through all children
    // grandchildren etc. - which is kind of a design flaw of SubKeyMap,
    // but one we're relying on
    Map<String, Object> store = _createMapToStore(context, token);
    store.clear();
    _sharedData._children = null;
  }

  @Override
  public String toString() {
    return "PageFlowScopeMap@"
        + System.identityHashCode(this)
        + "[_map="
        + _map
        + ", _token="
        + _token
        + ",_children="
        + _sharedData._children
        + "]";
  }

  private static TokenCache _getRootTokenCache(FacesContext context, int lifetime) {
    return TokenCache.getTokenCacheFromSession(
        context.getExternalContext(), _PAGE_FLOW_SCOPE_CACHE, true, lifetime);
  }

  private TokenCache _getTokenCache() {
    if (_sharedData._children == null)
      _sharedData._children = new TokenCache(_sharedData._lifetime);

    return _sharedData._children;
  }

  // =-=AEW This strategy assumes that the PageFlowScopeMap
  // will be inherited from a prior request, have things
  // added and removed prior to Render Response *without
  // the token being requested*, then have the token used
  // repeatedly during Render Response *without further
  // mutations*.  Both of these assumptions seem very
  // dubious!
  @SuppressWarnings("unchecked")
  private void _detachIfNeeded() {
    if (_token != null) {
      _map = (HashMap<String, Object>) _map.clone();
      _token = null;

      // =-=AEW When do we discard children?
    }
  }

  public static class SharedData implements Serializable {
    public SharedData() {}

    public SharedData(int lifetime) {
      _lifetime = lifetime;
    }

    private int _lifetime;
    // =-=AEW Make transient for efficiency
    private PageFlowScopeMap _parent;
    private TokenCache _children;
    private static final long serialVersionUID = 1L;
  }

  // DELETE AFTER DIALOG SERVICE IS CLEANED UP
  boolean __invalid;

  private SharedData _sharedData;
  private String _token;
  private HashMap<String, Object> _map;

  private static final String _PAGE_FLOW_SCOPE_CACHE =
      "org.apache.myfaces.trinidadinternal.application.PageFlowScope";
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(PageFlowScopeMap.class);
  private static final long serialVersionUID = 1L;
}
public class TableSelectOneRenderer extends XhtmlRenderer {
  public TableSelectOneRenderer(FacesBean.Type type) {
    super(type);
  }

  @Override
  protected void findTypeConstants(FacesBean.Type type) {
    super.findTypeConstants(type);
    _renderer = createCellRenderer(type);
  }

  //
  // Decode
  //
  @SuppressWarnings("unchecked")
  @Override
  protected void decode(
      FacesContext facesContext,
      UIComponent component,
      @SuppressWarnings("unused") FacesBean facesBean,
      @SuppressWarnings("unused") String clientId) {
    UIXCollection table = (UIXCollection) component;
    Object oldKey = table.getRowKey();

    try {
      // Set the row key to null to force the clientId to be correct
      table.setRowKey(null);

      String selectionParam = __getSelectionParameterName(facesContext, table);

      Map<String, String> parameters = facesContext.getExternalContext().getRequestParameterMap();

      _LOG.finest("Params:{0}", parameters);

      String selection = parameters.get(selectionParam);

      if (selection != null) {
        final RowKeySet state;
        if (table instanceof UIXTable) state = ((UIXTable) table).getSelectedRowKeys();
        else state = ((UIXTree) table).getSelectedRowKeys();

        table.setClientRowKey(selection);
        // If the key is not already selected, or the state is more than one
        // (someone changed the table selection from multiple to single),
        // update the keys
        if (!state.isContained() || state.size() > 1) {
          RowKeySet unselected = state.clone();
          // TODO : do not mutate the selectedRowKeys here.
          // instead, mutate when event is broadcast:
          state.clear();
          state.add();
          // clone, so that subsequent mutations of "state" will
          // not affect the parameters of this event: bug 4733858:
          RowKeySet selected = state.clone();
          FacesEvent event = new SelectionEvent(table, unselected, selected);
          event.queue();
        }
      }
    } finally {
      table.setRowKey(oldKey);
    }
  }

  //
  // Encode
  //
  @Override
  public boolean getRendersChildren() {
    return true;
  }

  @Override
  protected void encodeAll(
      FacesContext context, RenderingContext rc, UIComponent component, FacesBean bean)
      throws IOException {
    TableRenderingContext tContext = TableRenderingContext.getCurrentInstance();

    if (tContext == null) {
      _LOG.severe("TABLESELECT_COMPONENT_MAY_ONLY_INSIDE_TABLE_AND_TREETABLE");
      return;
    }

    RenderStage stage = tContext.getRenderStage();
    switch (stage.getStage()) {
      case RenderStage.SUB_CONTROL_BAR_STAGE:
      case RenderStage.UPPER_CONTROL_BAR_STAGE:
      case RenderStage.LOWER_CONTROL_BAR_STAGE:
        break;

      case RenderStage.DATA_STAGE:
        renderCellContent(context, rc, tContext, component, bean);
        break;

      default:
        throw new AssertionError("bad renderStage:" + stage.getStage());
    }
  }

  protected boolean isSelectOne() {
    return true;
  }

  protected CoreRenderer createCellRenderer(FacesBean.Type type) {
    return new Radio(type);
  }

  protected void renderCellContent(
      FacesContext context,
      RenderingContext rc,
      TableRenderingContext tContext,
      UIComponent component,
      FacesBean bean)
      throws IOException {
    rc.setCurrentClientId(tContext.getTableId());
    delegateRenderer(context, rc, component, bean, _renderer);
    rc.setCurrentClientId(null);
  }

  /** Get the name of the parameter for the selection; package-private for testing. */
  static String __getSelectionParameterName(FacesContext context, UIComponent table) {
    return (table.getClientId(context)
        + NamingContainer.SEPARATOR_CHAR
        + XhtmlConstants.SELECTED_KEY);
  }

  public static class Radio extends SimpleSelectBooleanCheckboxRenderer {
    public Radio(FacesBean.Type type) {
      super(type);
    }

    @Override
    protected String getCompositeId(String clientId) {
      return null;
    }

    /** we do not want to render the simple span for the checkbox. */
    @Override
    protected boolean getRenderSimpleSpan(UIComponent component, FacesBean bean) {
      return false;
    }

    /** don't render a special content style class on the radio. */
    @Override
    protected String getContentStyleClass(UIComponent component, FacesBean bean) {
      return null;
    }

    @Override
    protected void renderId(FacesContext context, UIComponent component) throws IOException {
      TableRenderingContext tContext = TableRenderingContext.getCurrentInstance();
      String param =
          (tContext.getTableId() + NamingContainer.SEPARATOR_CHAR + XhtmlConstants.SELECTED_KEY);
      ResponseWriter writer = context.getResponseWriter();
      writer.writeAttribute("name", param, null);
      // =-=AEW Inefficient.  We only need the "id" when there's
      // a shortDescription (which is when we'll get a label)
      if (getShortDesc(component, getFacesBean(component)) != null)
        writer.writeAttribute("id", getClientId(context, component), null);
    }

    @Override
    protected String getClientId(FacesContext context, UIComponent component) {
      // We use the table's container client ID
      return component.getContainerClientId(context);
    }

    @Override
    protected Object getSubmittedValue(UIComponent component, FacesBean bean) {
      TableRenderingContext tContext = TableRenderingContext.getCurrentInstance();
      return tContext.getSelectedRowKeys().isContained() ? Boolean.TRUE : Boolean.FALSE;
    }

    @Override
    protected Object getType() {
      return "radio";
    }

    @Override
    protected Object getValueAttr(RenderingContext rc) {
      TableRenderingContext tContext = TableRenderingContext.getCurrentInstance();
      return ((UIXCollection) tContext.getCollectionComponent()).getClientRowKey();
    }

    @Override
    protected String getShortDesc(UIComponent component, FacesBean bean) {
      String key = getDefaultShortDescKey();
      RenderingContext arc = RenderingContext.getCurrentInstance();
      return arc.getTranslatedString(key);
    }

    protected String getDefaultShortDescKey() {
      return "af_tableSelectOne.SELECT_COLUMN_HEADER";
    }

    @Override
    protected char getAccessKey(UIComponent component, FacesBean bean) {
      return CHAR_UNDEFINED;
    }

    @Override
    protected boolean isImmediate(UIComponent component, FacesBean bean) {
      TableRenderingContext tContext = TableRenderingContext.getCurrentInstance();
      return tContext.isImmediate();
    }

    @Override
    protected boolean getReadOnly(FacesContext context, UIComponent component, FacesBean bean) {
      return false;
    }

    @Override
    protected boolean getDisabled(UIComponent component, FacesBean bean) {
      return false;
    }

    /** @todo Support? */
    @Override
    protected String getOnblur(UIComponent component, FacesBean bean) {
      return null;
    }

    /** @todo Support? */
    @Override
    protected String getOnfocus(UIComponent component, FacesBean bean) {
      return null;
    }

    @Override
    protected String getOnchange(UIComponent component, FacesBean bean) {
      return null;
    }

    protected String getOnselect(UIComponent component, FacesBean bean) {
      return null;
    }

    @Override
    protected String getText(UIComponent component, FacesBean bean) {
      return null;
    }
  }

  private CoreRenderer _renderer;

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(TableSelectOneRenderer.class);
}
public class FileDownloadActionListenerTag extends TrinidadTagSupport {
  public void setContentType(ValueExpression contentType) {
    _contentType = contentType;
  }

  public void setFilename(ValueExpression filename) {
    _filename = filename;
  }

  public void setMethod(MethodExpression method) {
    _method = method;
  }

  @Override
  public int doStartTag() throws JspException {
    UIComponentTag tag = UIComponentTag.getParentUIComponentTag(pageContext);
    if (tag == null) {
      throw new JspException(
          _LOG.getMessage("FILEDOWNLOADACTIONLISTENER_MUST_INSIDE_UICOMPONENT_TAG"));
    }

    // Only run on the first time the tag executes
    if (!tag.getCreated()) return SKIP_BODY;

    UIComponent component = tag.getComponentInstance();
    if (!(component instanceof ActionSource)) {
      throw new JspException(
          _LOG.getMessage("FILEDOWNLOADACTIONLISTENER_MUST_INSIDE_UICOMPONENT_TAG"));
    }

    ELContextTag parentELContext = (ELContextTag) findAncestorWithClass(this, ELContextTag.class);

    Application application = FacesContext.getCurrentInstance().getApplication();

    FileDownloadActionListener listener = new FileDownloadActionListener();

    if (_filename != null) {
      listener.setValueExpression(FileDownloadActionListener.FILENAME_KEY, _filename);
    }

    if (_contentType != null) {
      listener.setValueExpression(FileDownloadActionListener.CONTENT_TYPE_KEY, _contentType);
    }

    listener.setMethod(_method);

    ((ActionSource) component).addActionListener(listener);

    return super.doStartTag();
  }

  @Override
  public void release() {
    super.release();
    _contentType = null;
    _filename = null;
    _method = null;
  }

  private ValueExpression _contentType;
  private ValueExpression _filename;
  private MethodExpression _method;
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(FileDownloadActionListenerTag.class);
}
/** This parses a skin css file into namespace map and selector/properties. */
public class SkinCSSParser {
  public SkinCSSParser() {}

  public void parseCSSDocument(Reader in, SkinCSSDocumentHandler documentHandler) {

    try {
      CSSScanner scanner = new CSSScanner(in);
      documentHandler.startDocument();
      List<String> selectorList = null;

      // start scanning the document
      // return comments /* xxx */
      // return @rules, which end with ';'
      // return selectors, which end with a {
      // return properties, which end with a }
      int currentType = _nextIgnoreSpaces(scanner);

      while (currentType != CSSLexicalUnits.EOF) {
        if (currentType == CSSLexicalUnits.COMMENT)
          documentHandler.comment(scanner.getStringValue());
        else if (currentType == CSSLexicalUnits.AT_KEYWORD)
          documentHandler.atRule(scanner.getStringValue());
        else if (currentType == CSSLexicalUnits.LEFT_CURLY_BRACE) {
          documentHandler.startSelector();
          selectorList = _parseSelectorString(scanner.getStringValue());
        } else if (currentType == CSSLexicalUnits.RIGHT_CURLY_BRACE) {
          String properties = scanner.getStringValue();
          _handlePropertiesString(documentHandler, properties);
          if (selectorList == null) {
            if (_LOG.isWarning()) {
              _LOG.warning("IGNORING_PROPERTIES_WITHOUT_SELECTOR", properties);
            }
          }
          documentHandler.endSelector(selectorList);
        }
        currentType = _nextIgnoreSpaces(scanner);
      }
    } finally {
      documentHandler.endDocument();
    }
  }

  /**
   * given a string that denotes the selectors in a css file, parse this further into a list of
   * selectors. (the selectors are deliminated by commas)
   */
  private List<String> _parseSelectorString(String selectors) {
    // give a list of selectors, deliminated by commas, parse into a List.
    // loop thru each character until I get to a left bracket.
    if (selectors == null) return null;

    List<String> selectorList = new ArrayList<String>();

    // pull apart by commas
    // don't skip whitespace since whitespace means descendant selectors in css
    String[] selector = _splitString(selectors, ',', false);

    String trimmedSelector;
    for (int i = 0; i < selector.length; i++) {
      // the first selector might have extra }
      // this is a common typo, to have extra }s.
      if (i == 0) {
        trimmedSelector = _trimChar(selector[i].trim(), '}');
      } else {
        trimmedSelector = selector[i].trim();
      }
      // skip the selector if it is empty
      if ("".equals(trimmedSelector)) {
        if (_LOG.isWarning()) _LOG.warning("ERR_PARSING_SKIN_SELECTOR", selectors);
      } else selectorList.add(trimmedSelector);
    }

    return selectorList;
  }

  /**
   * given a string that denotes the properties of one or more selectors, parse further into
   * name/value pairs and call documentHandler's property callback.
   */
  private void _handlePropertiesString(SkinCSSDocumentHandler documentHandler, String properties) {
    if (properties == null) return;

    // first, parse out any comments
    Matcher matcher = _COMMENT_PATTERN.matcher(properties);
    properties = matcher.replaceAll("");
    // split into name and value (don't skip whitespace since properties like padding: 0px 5px
    // need the spaces)
    String[] property = _splitString(properties, ';', false);

    for (int i = 0; i < property.length; i++) {
      int indexOfColon = property[i].indexOf(':');
      if ((indexOfColon > -1) && (indexOfColon < property[i].length())) {
        String name = property[i].substring(0, indexOfColon);
        String value = property[i].substring(indexOfColon + 1);
        documentHandler.property(name.trim(), value.trim());
      }
    }
  }

  /**
   * return the array of strings computed by splitting this string around matches of the given
   * character
   *
   * @param in
   * @param charDelimiter
   * @param skipWhitespace if true, whitespace is skipped and not included in the return Strings in
   *     the String array.
   * @return String[] The array of Strings computed by splitting the input String around matches of
   *     the charDelimiter
   */
  private static String[] _splitString(String in, char charDelimiter, boolean skipWhitespace) {
    // return a String[] with each piece that is deliminated by the inChar.
    int length = in.length();
    StringBuffer buffer = new StringBuffer(length);
    List<String> splitList = new ArrayList<String>();

    for (int i = 0; i < length; i++) {
      char c = in.charAt(i);
      if (c == charDelimiter) {
        // we hit the delimiter, so put it in the splitList and start a new buffer.
        splitList.add(buffer.toString());
        buffer = new StringBuffer(length);
      } else {
        // it's ok to put the character in the buffer if we don't want to skip whitespace
        // or if it isn't whitespace to begin with.
        if (!skipWhitespace || !(Character.isWhitespace(c))) buffer.append(c);
      }
    }
    // we are done with all the characters
    String lastString = buffer.toString();
    if (lastString.length() > 0) splitList.add(lastString);
    return splitList.toArray(_EMPTY_STRING_ARRAY);
  }

  private static String _trimChar(String in, char c) {
    int len = in.length();
    char currentChar = in.charAt(0);
    if (currentChar != c) return in;

    for (int i = 1; i < len; i++) {
      currentChar = in.charAt(i);
      if (currentChar != c) {

        return in.substring(i);
      }
    }
    return in;
  }

  // ignores spaces.
  private int _nextIgnoreSpaces(CSSScanner scanner) {
    int currentType = scanner.getNextToken();
    while (currentType == CSSLexicalUnits.SPACE) {
      currentType = scanner.getNextToken();
    }
    return currentType;
  }

  // This class builds up tokens for SPACE, COMMENT, AT_RULE,
  // LEFT_CURLY_BRACE (selectorList), and RIGHT_CURLY_BRACE (properties)
  // A token is stored in the _buffer object.
  private static class CSSScanner {
    public CSSScanner(Reader reader) {
      _reader = reader;
    }

    public String getStringValue() {
      if (_end <= 0) return null;
      else return new String(_buffer, 0, _end);
    }

    // get the next token in the buffer and return the type
    public int getNextToken() {
      _position = 0;
      _fillToken();

      _end = _position;

      // strip off the final brace if needed
      if (_type == CSSLexicalUnits.RIGHT_CURLY_BRACE || _type == CSSLexicalUnits.LEFT_CURLY_BRACE)
        _end--;

      if (_currentChar == -1) return CSSLexicalUnits.EOF;
      return _type;
    }

    private void _fillToken() {
      while (true) {
        _nextChar();
        switch (_currentChar) {
          case -1:
            _type = CSSLexicalUnits.EOF;
            break;

          case ' ':
          case '\t':
          case '\n':
          case '\f':
          case '\r':
            if (_type != CSSLexicalUnits.LEFT_CURLY_BRACE) {
              _type = CSSLexicalUnits.SPACE;
              return;
            }
            // fall through to LEFT_CURLY_BRACE

          case '/':
            if (_type != CSSLexicalUnits.LEFT_CURLY_BRACE) {
              // check for comment. If it is a comment, set the type and return
              // if it isn't a comment, keep looping to get more characters.
              _nextChar();
              if (_currentChar == '*') {
                // WE ARE IN A COMMENT
                // loop and get characters into buffer until we get '*/'

                _nextChar();
                int prevChar;
                while (_currentChar != -1) {

                  prevChar = _currentChar;
                  _nextChar();
                  if ((prevChar == '*') && (_currentChar == '/')) break;
                }

                _type = CSSLexicalUnits.COMMENT;
                return;
              }
              // wasn't a comment, so keep going on, filling the buffer with
              // each _nextChar call.
              break;
            }

          case '@':
            if (_type != CSSLexicalUnits.LEFT_CURLY_BRACE) {
              // found @.
              // @namespace is treated differently than other @rules.
              // These are the formats:
              // @namespace foo url(http://www.foo.com);
              // @agent {
              //    af|inputText::content{color:red; background-color:blue;}
              // }
              // @platform {...}
              // If @namespace, go 'til the semi-colon
              // Else, go until the start/end brace match.

              // found @. keep getting characters until we get a ; or end of file.
              /*
              _nextChar();

              while ((_currentChar != -1) && (_currentChar != ';'))
              {
                _nextChar();
              }
              */
              _nextChar();
              // go until ; or until {} match
              int openBraceCount = 0;
              boolean openBraceCountStarted = false;
              while ((_currentChar != -1)) {

                if (_currentChar == '{') openBraceCount++;
                if (openBraceCount == 1) openBraceCountStarted = true;
                if (_currentChar == '}' && openBraceCountStarted) {
                  openBraceCount--;
                  if (openBraceCountStarted && openBraceCount == 0) {
                    break;
                  }
                }
                if (_currentChar == ';' && openBraceCount == 0) {
                  break;
                }
                _nextChar();
              }

              _type = CSSLexicalUnits.AT_KEYWORD;
              return;
            }

          default:
            if (_type == CSSLexicalUnits.LEFT_CURLY_BRACE) {
              // these are the properties,
              // keep going until we have all the properties
              while ((_currentChar != -1) && (_currentChar != '}')) {
                _nextChar();
              }
              _type = CSSLexicalUnits.RIGHT_CURLY_BRACE;
            } else {
              while ((_currentChar != -1) && (_currentChar != '{')) {
                _nextChar();
              }
              _type = CSSLexicalUnits.LEFT_CURLY_BRACE;
            }
            return;
        } // end switch

        if (_currentChar == -1) {
          _type = CSSLexicalUnits.EOF;
          return;
        }
      }
    }

    // fill buffer with one more character
    private void _nextChar() {
      try {
        _currentChar = _reader.read();
      } catch (IOException e) {
        if (_LOG.isSevere()) {
          _LOG.severe("ERR_READING_SKIN_CSS_FILE", e);
        }
        _currentChar = -1;
        return;
      }
      // need to make sure buffer doesn't go over its size
      if (_buffer.length <= _position) {
        // increase buffer size by 50%
        char[] tmp = new char[_buffer.length + (_buffer.length / 2)];
        // copy over buffer to new buffer
        for (int i = 0; i < _buffer.length; i++) tmp[i] = _buffer[i];
        // pt _buffer to bigger buffer.
        _buffer = tmp;
      }

      _buffer[_position++] = (char) _currentChar;
    }

    private Reader _reader;
    // buffer parameters
    private char[] _buffer = new char[1024];
    private int _position;
    private int _end;
    // type of token (it will be a CSSLexicalUnits constant)
    private int _type;
    // current character. -1 means EOF
    private int _currentChar;
  }

  /** constants that we use to keep track of what type of token of the css file we have parsed. */
  private static class CSSLexicalUnits {
    public static final int EOF = 0;
    public static final int LEFT_CURLY_BRACE = 1;
    public static final int RIGHT_CURLY_BRACE = 2;
    public static final int SPACE = 3;
    public static final int COMMENT = 4;
    public static final int AT_KEYWORD = 5;
  }

  // this is the pattern for finding comments. We want to strip out
  // comments from the properties, and we use this pattern to do it.
  private static final Pattern _COMMENT_PATTERN = Pattern.compile("(?s)/\\*.*?\\*/");

  private static final String[] _EMPTY_STRING_ARRAY = new String[0];

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(SkinCSSParser.class);
}
/** Subclass of UIComponentTag to add convenience methods, and optimize where appropriate. */
public abstract class UIXComponentELTag extends UIComponentELTag {
  public UIXComponentELTag() {}

  public void setAttributeChangeListener(MethodExpression attributeChangeListener) {
    _attributeChangeListener = attributeChangeListener;
  }

  @Override
  public int doStartTag() throws JspException {
    int retVal = super.doStartTag();

    // pu: There could have been some validation error during property setting
    //  on the bean, this is the closest opportunity to burst out.
    if (_validationError != null) throw new JspException(_validationError);

    return retVal;
  }

  @Override
  protected final void setProperties(UIComponent component) {
    if (component instanceof UIViewRoot) {
      throw new IllegalStateException(
          "<f:view> was not present on this page; tag "
              + this
              + "encountered without an <f:view> being processed.");
    }

    super.setProperties(component);

    UIXComponent uixComponent = (UIXComponent) component;

    if (_attributeChangeListener != null) {
      uixComponent.setAttributeChangeListener(_attributeChangeListener);
    }

    setProperties(uixComponent.getFacesBean());
  }

  protected void setProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      bean.setProperty(key, expression.getValue(null));
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.lang.String[]. If the value is an EL expression, it will be stored
   * as a ValueExpression. Otherwise, it will parsed as a whitespace-separated series of strings.
   * Null values are ignored.
   */
  protected void setStringArrayProperty(
      FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      bean.setProperty(key, _parseNameTokens(expression.getValue(null)));
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.util.List<java.lang.String>. If the value is an EL expression, it
   * will be stored as a ValueExpression. Otherwise, it will parsed as a whitespace-separated series
   * of strings. Null values are ignored.
   */
  protected void setStringListProperty(
      FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      bean.setProperty(key, _parseNameTokensAsList(expression.getValue(null)));
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.util.Set<java.lang.String>. If the value is an EL expression, it
   * will be stored as a ValueExpression. Otherwise, it will parsed as a whitespace-separated series
   * of strings. Null values are ignored.
   */
  protected void setStringSetProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      bean.setProperty(key, _parseNameTokensAsSet(expression.getValue(null)));
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.lang.Number. If the value is an EL expression, it will be stored as
   * a ValueBinding. Otherwise, it will parsed with Integer.valueOf() or Double.valueOf() . Null
   * values are ignored.
   */
  protected void setNumberProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      Object value = expression.getValue(null);
      if (value != null) {
        if (value instanceof Number) {
          bean.setProperty(key, value);
        } else {
          String valueStr = value.toString();
          if (valueStr.indexOf('.') == -1) bean.setProperty(key, Integer.valueOf(valueStr));
          else bean.setProperty(key, Double.valueOf(valueStr));
        }
      }
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type int[]. If the value is an EL expression, it will be stored as a
   * ValueExpression. Otherwise, it will parsed as a whitespace-separated series of ints. Null
   * values are ignored.
   */
  protected void setIntArrayProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      Object value = expression.getValue(null);
      if (value != null) {
        String[] strings = _parseNameTokens(value);
        final int[] ints;
        if (strings != null) {
          try {
            ints = new int[strings.length];
            for (int i = 0; i < strings.length; i++) {
              int j = Integer.parseInt(strings[i]);
              ints[i] = j;
            }
          } catch (NumberFormatException e) {
            _LOG.severe("CANNOT_CONVERT_INTO_INT_ARRAY", value);
            _LOG.severe(e);
            return;
          }
        }
      }
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.util.Date. If the value is an EL expression, it will be stored as a
   * ValueExpression. Otherwise, it will parsed as an ISO 8601 date (yyyy-MM-dd). Null values are
   * ignored.
   */
  protected void setDateProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      bean.setProperty(key, _parseISODate(expression.getValue(null)));
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  /**
   * Set a property of type java.util.Date. If the value is an EL expression, it will be stored as a
   * ValueBinding. Otherwise, it will parsed as an ISO 8601 date (yyyy-MM-dd) and the time
   * components (hour, min, second, millisecond) maximized. Null values are ignored.
   */
  protected void setMaxDateProperty(FacesBean bean, PropertyKey key, ValueExpression expression) {
    if (expression == null) return;

    if (expression.isLiteralText()) {
      Date d = _parseISODate(expression.getValue(null));
      Calendar c = Calendar.getInstance();
      TimeZone tz = RequestContext.getCurrentInstance().getTimeZone();
      if (tz != null) c.setTimeZone(tz);
      c.setTime(d);
      // Original value had 00:00:00 for hours,mins, seconds now maximize those
      // to get the latest time value for the date supplied.
      c.set(Calendar.HOUR_OF_DAY, 23);
      c.set(Calendar.MINUTE, 59);
      c.set(Calendar.SECOND, 59);
      c.set(Calendar.MILLISECOND, 999);
      bean.setProperty(key, c.getTime());
    } else {
      bean.setValueExpression(key, expression);
    }
  }

  protected void setProperties(FacesBean bean) {
    // Could be abstract, but it's easier to *always* call super.setProperties(),
    // and perhaps we'll have something generic in here, esp. if we take
    // over "rendered" from UIComponentTag
  }

  /**
   * Sets any fatal validation error that could have happened during property setting. If this is
   * set, tag execution aborts with a JspException at the end of doStartTag().
   *
   * @param validationError
   */
  protected void setValidationError(String validationError) {
    _validationError = validationError;
  }

  /**
   * Parse a string into a java.util.Date object. The string must be in ISO 9601 format
   * (yyyy-MM-dd).
   */
  private static final Date _parseISODate(Object o) {
    if (o == null) return null;

    String stringValue = o.toString();
    try {
      return _getDateFormat().parse(stringValue);
    } catch (ParseException pe) {
      _LOG.info("CANNOT_PARSE_VALUE_INTO_DATE", stringValue);
      return null;
    }
  }

  /**
   * Parses a whitespace separated series of name tokens.
   *
   * @param stringValue the full string
   * @return an array of each constituent value, or null if there are no tokens (that is, the string
   *     is empty or all whitespace)
   * @todo Move to utility function somewhere (ADF Share?)
   */
  private static final String[] _parseNameTokens(Object o) {
    List<String> list = _parseNameTokensAsList(o);

    if (list == null) return null;

    return list.toArray(new String[list.size()]);
  }

  private static final List<String> _parseNameTokensAsList(Object o) {
    if (o == null) return null;

    String stringValue = o.toString();
    ArrayList<String> list = new ArrayList<String>(5);

    int length = stringValue.length();
    boolean inSpace = true;
    int start = 0;
    for (int i = 0; i < length; i++) {
      char ch = stringValue.charAt(i);

      // We're in whitespace;  if we've just departed
      // a run of non-whitespace, append a string.
      // Now, why do we use the supposedly deprecated "Character.isSpace()"
      // function instead of "isWhitespace"?  We're following XML rules
      // here for the meaning of whitespace, which specifically
      // EXCLUDES general Unicode spaces.
      if (Character.isWhitespace(ch)) {
        if (!inSpace) {
          list.add(stringValue.substring(start, i));
          inSpace = true;
        }
      }
      // We're out of whitespace;  if we've just departed
      // a run of whitespace, start keeping track of this string
      else {
        if (inSpace) {
          start = i;
          inSpace = false;
        }
      }
    }

    if (!inSpace) list.add(stringValue.substring(start));

    if (list.isEmpty()) return null;

    return list;
  }

  private static final Set<String> _parseNameTokensAsSet(Object o) {
    List<String> list = _parseNameTokensAsList(o);

    if (list == null) return null;
    else return new HashSet(list);
  }

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(UIXComponentELTag.class);

  // We rely strictly on ISO 8601 formats
  private static DateFormat _getDateFormat() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    TimeZone tz = RequestContext.getCurrentInstance().getTimeZone();
    if (tz != null) sdf.setTimeZone(tz);
    return sdf;
  }

  //  No more used anywhere in Trinidad code, so deprecate since 2.0.x.
  @Deprecated
  public static final String DOCUMENT_CREATED_KEY = "org.apache.myfaces.trinidad.DOCUMENTCREATED";

  private MethodExpression _attributeChangeListener;
  private String _validationError;
}
/**
 * Utility class dealing with Locale-related issues, including common direction and alignment
 * constants.
 *
 * <p>
 *
 * @version $Name: $ ($Revision:
 *     adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/util/nls/LocaleUtils.java#0
 *     $) $Date: 10-nov-2005.18:49:13 $
 */
public final class LocaleUtils {

  /** Reading direction constant */
  public static final int DIRECTION_DEFAULT = 0;

  /** Reading direction constant */
  public static final int DIRECTION_LEFTTORIGHT = 1;

  /** Reading direction constant */
  public static final int DIRECTION_RIGHTTOLEFT = 2;

  /** Conversion function to go from LocaleContext to the obsolete reading direction API. */
  public static int getReadingDirection(LocaleContext localeContext) {
    return localeContext.isRightToLeft() ? DIRECTION_RIGHTTOLEFT : DIRECTION_LEFTTORIGHT;
  }

  /** Given a locale, returns the default reading direction. */
  public static int getReadingDirectionForLocale(Locale loc) {
    if (loc == null) {
      loc = Locale.getDefault();
    }

    String language = loc.getLanguage();

    // arabic and hebrew are right-to-left languages.  We treat "iw" as
    // hebrew because "iw" was the old code for Hebrew and the JDK
    // still uses it
    if (language.equals("ar") || language.equals("he") || language.equals("iw")) {
      return DIRECTION_RIGHTTOLEFT;
    } else {
      return DIRECTION_LEFTTORIGHT;
    }
  }

  /** Decodes an IANA string (e.g., en-us) into a Locale object. */
  public static Locale getLocaleForIANAString(String ianaString) {
    if ((ianaString == null) || "".equals(ianaString)) return null;

    String language;
    String country = "";
    String variant = "";

    int dashIndex = ianaString.indexOf('-');
    if (dashIndex < 0) {
      language = ianaString;
    } else {
      language = ianaString.substring(0, dashIndex);
      int start = dashIndex + 1;
      dashIndex = ianaString.indexOf('-', start);
      if (dashIndex < 0) {
        country = ianaString.substring(start);
      } else {
        country = ianaString.substring(start, dashIndex);
        variant = ianaString.substring(dashIndex + 1);
      }
    }

    /*
     * Validate the rules for Locale per its Javadoc:
     * - The language argument is a valid ISO Language Code.
     *   These codes are the lower-case, two-letter codes as defined by ISO-639.
     * - The country argument is a valid ISO Country Code. These
     *   codes are the upper-case, two-letter codes as defined by ISO-3166.
     *
     *   Rather than checking a list, we check the length and case and ignore
     *   the arguments which fail to meet those criteria (use defaults instead).
     */
    if (language.length() != 2) {
      language = "";
      _LOG.warning("INVALID_LOCALE_LANG_LENGTH", ianaString);
    } else {
      if (Character.isUpperCase(language.charAt(0)) || Character.isUpperCase(language.charAt(1))) {
        language = "";
        _LOG.warning("INVALID_LOCALE_LANG_CASE", ianaString);
      }
    }
    if (language.length() == 0) {
      FacesContext fc = FacesContext.getCurrentInstance();
      if (fc.getViewRoot() != null) {
        return (fc.getViewRoot().getLocale());
      } else {
        // ViewRoot may be null, e.g. when TranslationsResourceLoader is called
        // from the ResourceServlet.
        //
        // Cast to a servletRequest here because this should only happen
        // when run from the Trinidad ResourceServlet.  Even in a portlet
        // environment we should have a servlet request in this circumstance.
        // If we need to handle getting the locale from portlet objects, we
        // should add a utility to do so in ExternalContextUtils.
        ServletRequest req = (ServletRequest) fc.getExternalContext().getRequest();
        return (req.getLocale());
      }
    }

    if (country.length() > 0) {
      if (country.length() != 2) {
        country = "";
        _LOG.warning("INVALID_LOCALE_COUNTRY_LENGTH", ianaString);
      } else {
        if (Character.isLowerCase(country.charAt(0)) || Character.isLowerCase(country.charAt(1))) {
          country = "";
          _LOG.warning("INVALID_LOCALE_COUNTRY_CASE", ianaString);
        }
      }
    }

    if (variant.indexOf('/') > 0) {
      // Disallow slashes in the variant to avoid XSS
      variant = "";
      _LOG.warning("INVALID_LOCALE_VARIANT_HAS_SLASH", ianaString);
    }

    return new Locale(language, country, variant);
  }

  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(LocaleUtils.class);
}
Exemple #13
0
/**
 * Creates a CollectionModel that is sortable. All properties that implement java.lang.Comparable
 * are deemed sortable.
 */
public class SortableModel extends CollectionModel {
  /**
   * Create a new SortableModel from the given instance.
   *
   * @param model This will be converted into a {@link DataModel}
   * @see #setWrappedData
   */
  public SortableModel(Object model) {
    setWrappedData(model);
  }

  /**
   * No arg constructor for use as a managed-bean. Must call setWrappedData before using this
   * instance.
   */
  public SortableModel() {}

  @Override
  public Object getRowData() {
    return _model.getRowData();
  }

  @Override
  public Object getWrappedData() {
    return _wrappedData;
  }

  @Override
  public boolean isRowAvailable() {
    return _model.isRowAvailable();
  }

  /**
   * Sets the underlying data being managed by this instance.
   *
   * @param data This Object will be converted into a {@link DataModel}.
   * @see ModelUtils#toDataModel
   */
  @Override
  public void setWrappedData(Object data) {
    _baseIndicesList = null;
    _model = ModelUtils.toDataModel(data);
    _sortCriterion = null;
    _sortedIndicesList = null;
    _wrappedData = data;
  }

  @Override
  public int getRowCount() {
    return _model.getRowCount();
  }

  @Override
  public void setRowIndex(int rowIndex) {
    int baseIndex = _toBaseIndex(rowIndex);
    _model.setRowIndex(baseIndex);
  }

  @Override
  public int getRowIndex() {
    int baseIndex = _model.getRowIndex();
    return _toSortedIndex(baseIndex);
  }

  /**
   * Gets the row key of the current row
   *
   * @inheritDoc
   */
  @Override
  public Object getRowKey() {
    return isRowAvailable() ? _model.getRowIndex() : null;
  }

  /**
   * Finds the row with the matching key and makes it current
   *
   * @inheritDoc
   */
  @Override
  public void setRowKey(Object key) {
    _model.setRowIndex(_toRowIndex(key));
  }

  public void addDataModelListener(DataModelListener listener) {
    _model.addDataModelListener(listener);
  }

  public DataModelListener[] getDataModelListeners() {
    return _model.getDataModelListeners();
  }

  public void removeDataModelListener(DataModelListener listener) {
    _model.removeDataModelListener(listener);
  }

  /**
   * Checks to see if the underlying collection is sortable by the given property.
   *
   * @param property The name of the property to sort the underlying collection by.
   * @return true, if the property implements java.lang.Comparable
   */
  @Override
  public boolean isSortable(String property) {
    final int oldIndex = _model.getRowIndex();
    try {
      _model.setRowIndex(0);
      if (!_model.isRowAvailable())
        return false; // if there is no data in the table then nothing is sortable

      Object data = _model.getRowData();
      try {
        // TODO clean up that _getELXyz() calls
        FacesContext context = FacesContext.getCurrentInstance();
        ELResolver resolver = _getELResolver(context);
        ELContext elContext = _getELContext(context, resolver);
        Object propertyValue = evaluateProperty(resolver, elContext, data, property);
        // when the value is null, we don't know if we can sort it.
        // by default let's support sorting of null values, and let the user
        // turn off sorting if necessary:
        return (propertyValue instanceof Comparable) || (propertyValue == null);
      } catch (RuntimeException e) {
        // don't propagate this exception out. This is because it might break
        // the VE.
        _LOG.warning(e);
        return false;
      }
    } finally {
      _model.setRowIndex(oldIndex);
    }
  }

  private Object evaluateProperty(
      ELResolver resolver, ELContext context, Object base, String property) {
    // simple property -> resolve value directly
    if (!property.contains(".")) return resolver.getValue(context, base, property);

    int index = property.indexOf('.');
    Object newBase = resolver.getValue(context, base, property.substring(0, index));

    return evaluateProperty(resolver, context, newBase, property.substring(index + 1));
  }

  @Override
  public List<SortCriterion> getSortCriteria() {
    if (_sortCriterion == null) {
      return Collections.emptyList();
    } else {
      return Collections.singletonList(_sortCriterion);
    }
  }

  @Override
  public void setSortCriteria(List<SortCriterion> criteria) {
    if ((criteria == null) || (criteria.isEmpty())) {
      _sortCriterion = null;
      // restore unsorted order:
      _baseIndicesList = _sortedIndicesList = null;
    } else {
      SortCriterion sc = criteria.get(0);
      if ((_sortCriterion == null) || (!_sortCriterion.equals(sc))) {
        _sortCriterion = sc;
        _sort(_sortCriterion.getProperty(), _sortCriterion.isAscending());
      }
    }
  }

  @Override
  public String toString() {
    return "SortableModel[" + _model + "]";
  }

  /**
   * Sorts the underlying collection by the given property, in the given direction.
   *
   * @param property The name of the property to sort by. The value of this property must implement
   *     java.lang.Comparable.
   * @param isAscending true if the collection is to be sorted in ascending order.
   * @todo support -1 for rowCount
   */
  private void _sort(String property, boolean isAscending) {
    //    if (property.equals(_sortBy) && (isAscending == _sortOrder))
    //    {
    //      return;
    //    }
    //
    //    _sortBy = property;
    //    _sortOrder = isAscending;

    // TODO: support -1 for rowCount:
    int sz = getRowCount();
    if ((_baseIndicesList == null) || (_baseIndicesList.size() != sz)) {
      // we do not want to mutate the original data.
      // however, instead of copying the data and sorting the copy,
      // we will create a list of indices into the original data, and
      // sort the indices. This way, when certain rows are made current
      // in this Collection, we can make them current in the underlying
      // DataModel as well.

      _baseIndicesList = new IntList(sz);
    }

    final int rowIndex = _model.getRowIndex();
    _model.setRowIndex(0);
    // Make sure the model has that row 0! (It could be empty.)
    if (_model.isRowAvailable()) {
      FacesContext context = FacesContext.getCurrentInstance();
      ELResolver resolver = _getELResolver(context);
      ELContext elContext = _getELContext(context, resolver);
      Comparator<Integer> comp = new Comp(resolver, elContext, property);
      if (!isAscending) comp = new Inverter<Integer>(comp);

      Collections.sort(_baseIndicesList, comp);
      _sortedIndicesList = null;
    }

    _model.setRowIndex(rowIndex);
  }

  private int _toSortedIndex(int baseIndex) {
    if ((_sortedIndicesList == null) && (_baseIndicesList != null)) {
      _sortedIndicesList = (IntList) _baseIndicesList.clone();
      for (int i = 0; i < _baseIndicesList.size(); i++) {
        Integer base = _baseIndicesList.get(i);
        _sortedIndicesList.set(base.intValue(), i);
      }
    }

    return _convertIndex(baseIndex, _sortedIndicesList);
  }

  private int _toBaseIndex(int sortedIndex) {
    return _convertIndex(sortedIndex, _baseIndicesList);
  }

  private int _convertIndex(int index, List<Integer> indices) {
    if (index < 0) // -1 is special
    return index;

    if ((indices != null) && (indices.size() > index)) {
      index = indices.get(index).intValue();
    }
    return index;
  }

  private int _toRowIndex(Object rowKey) {
    if (rowKey == null) return -1;

    try {
      return ((Integer) rowKey).intValue();
    } catch (ClassCastException e) {
      _LOG.warning("INVALID_ROWKEY", new Object[] {rowKey, rowKey.getClass()});
      _LOG.warning(e);
      return -1;
    }
  }

  private static final class IntList extends ArrayList<Integer> {
    public IntList(int size) {
      super(size);
      _expandToSize(size);
    }

    private void _expandToSize(int desiredSize) {
      for (int i = 0; i < desiredSize; i++) {
        add(i);
      }
    }

    private static final long serialVersionUID = 1L;
  }

  private final class Comp implements Comparator<Integer> {
    public Comp(ELResolver resolver, ELContext context, String property) {
      _resolver = resolver;
      _context = context;
      _prop = property;
    }

    @SuppressWarnings("unchecked")
    public int compare(Integer o1, Integer o2) {
      int index1 = o1.intValue();
      int index2 = o2.intValue();

      _model.setRowIndex(index1);
      Object instance1 = _model.getRowData();
      Object value1 = evaluateProperty(_resolver, _context, instance1, _prop);

      _model.setRowIndex(index2);
      Object instance2 = _model.getRowData();
      Object value2 = evaluateProperty(_resolver, _context, instance2, _prop);

      if (value1 == null) return (value2 == null) ? 0 : -1;

      if (value2 == null) return 1;

      // bug 4545164. Sometimes, isSortable returns true
      // even if the underlying object is not a Comparable.
      // This happens if the object at rowIndex zero is null.
      // So test before we cast:
      if (value1 instanceof Comparable) {
        return ((Comparable<Object>) value1).compareTo(value2);
      } else {
        // if the object is not a Comparable, then
        // the best we can do is string comparison:
        return value1.toString().compareTo(value2.toString());
      }
    }

    private final ELResolver _resolver;
    private final ELContext _context;
    private final String _prop;
  }

  private static final class Inverter<T> implements Comparator<T> {
    public Inverter(Comparator<T> comp) {
      _comp = comp;
    }

    public int compare(T o1, T o2) {
      return _comp.compare(o2, o1);
    }

    private final Comparator<T> _comp;
  }

  /** Quickie implementation of ELContext for use if we're not being called in the JSF lifecycle */
  private static final class ELContextImpl extends ELContext {
    public ELContextImpl(ELResolver resolver) {
      _resolver = resolver;
    }

    @Override
    public ELResolver getELResolver() {
      return _resolver;
    }

    @Override
    public FunctionMapper getFunctionMapper() {
      // Because we're only really being used to pass
      // to an ELResolver, no FunctionMapper is needed
      return null;
    }

    @Override
    public VariableMapper getVariableMapper() {
      // Because we're only really being used to pass
      // to an ELResolver, no VariableMapper is needed
      return null;
    }

    private final ELResolver _resolver;
  }

  static Object __resolveProperty(Object object, String propertyName) {
    FacesContext context = FacesContext.getCurrentInstance();
    ELResolver resolver = _getELResolver(context);
    ELContext elContext = _getELContext(context, resolver);
    return resolver.getValue(elContext, object, propertyName);
  }

  private static ELContext _getELContext(FacesContext context, ELResolver resolver) {
    // Hopefully, we have a FacesContext.  If not, we're
    // going to have to synthesize one!
    if (context != null) return context.getELContext();

    return new ELContextImpl(resolver);
  }

  private static ELResolver _getELResolver(FacesContext context) {
    // First try the FacesContext, which is a faster way to
    // get the ELResolver (and the 99.9% scenario)
    if (context != null) return context.getApplication().getELResolver();

    // If that fails, then we're likely outside of the JSF lifecycle.
    // Look to the ApplicationFactory.
    ApplicationFactory factory =
        (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
    return factory.getApplication().getELResolver();
  }

  private SortCriterion _sortCriterion = null;

  private DataModel _model = null;
  private Object _wrappedData = null;

  private IntList _sortedIndicesList = null, // from baseIndex to sortedIndex
      _baseIndicesList = null; // from sortedIndex to baseIndex

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(SortableModel.class);
}
/** Code specific to a Menu Model's GroupNode. */
public class GroupNode extends MenuNode {
  /** Constructs a GroupNode */
  public GroupNode() {
    super();
  }

  /**
   * Called by the Default ActionListener when a menu node is clicked/selected.
   *
   * @return String outcome or viewId used during a POST for navigation.
   */
  @Override
  public String doAction() {
    // Call the doAction method of my idref node
    return getRefNode().doAction();
  }

  /**
   * Get the Destination URL of a page for a GET.
   *
   * @return String URL of a page.
   */
  @Override
  public String getDestination() {
    // Call the getDestination method of my idref node
    return getRefNode().getDestination();
  }

  /**
   * Sets the idref of the node.
   *
   * <p>The value of this attribute is an "id" of another node This tells the pointing node where to
   * obtain its viewId and takes precedence (and will replace) the pointing nodes viewId, if one
   * exists. This should point to a node of the same style, e.g. actionNode points to actionNode.
   *
   * @param idref - String name pointing to the "id" of another node
   */
  public void setIdRef(String idref) {
    _idref = idref;

    // Create a list of idref's for easier access
    if (_idref != null) _makeIdRefList(idref);
  }

  /**
   * Get the node whose id matches this node's idref attribute value.
   *
   * @return the MenuNode whose id matches this node's idref attribute value.
   */
  @Override
  public MenuNode getRefNode() {
    MenuNode refNode = null;

    // create one if it does not exist
    // should not happen, but can't hurt
    if (_idrefList == null) {
      String idref = getIdRef();
      _makeIdRefList(idref);
    }

    // Get idrefList
    String[] idrefList = _getIdRefList();

    // get group node's children
    List<MenuNode> children = getChildren();

    // Traverse the list. Do the following:
    //    o get Node from Model's hashMap of nodes and ids
    //    o check attributes (rendered, disabled, readOnly)
    //    o if they are ok, return the node
    for (int i = 0; i < Array.getLength(idrefList); i++) {
      Iterator<MenuNode> childIter = children.iterator();

      // All node "id" attribute values had the node's
      // system hashcode id appended to the id when
      // placed in the model's idNodeMap.
      //
      // Each id in the idreflist of a group node does
      // NOT have this node sys id appended it to it
      // and needs to or we won't find the group's
      // ref node.
      //
      // Since group nodes can only point to one of
      // its children, we iterate through them, get
      // their sys id and append it to idref until
      // we find a match (or not).
      while (childIter.hasNext()) {
        MenuNode childNode = childIter.next();
        String nodeSysId = childNode.getNodeSysId();

        // Need to append mode's sys id here to create a
        // unique id.
        String refNodeId = idrefList[i] + nodeSysId;

        refNode = (MenuNode) getRootModel().getNode(refNodeId);

        // if nothing found, move on to the next child
        if (refNode != null) break;
      }

      if (refNode == null) continue;

      // Check the attributes of the found node
      if (!refNode.getRendered()
          || refNode.getDisabled()
          || refNode.getReadOnly()
          || !refNode.getVisible()) {
        refNode = null;
        continue;
      }

      // Ok, we have a valid RefNode
      break;
    }

    // If no valid node is found,
    // log an error
    if (refNode == null) {
      _LOG.severe("GroupNode " + getLabel() + " refers to no valid node.\n");
      return null;
    }

    return refNode;
  }

  /**
   * Get the id of the node referred to by the idref attribute of this node.
   *
   * @return String id of the node referred to by the idref attribure of this node.
   */
  public String getIdRef() {
    return _idref;
  }

  /* =============================================================
   * Private methods
   * =============================================================*/

  /**
   * _getIdRefList. gets the list of idrefs for this node.
   *
   * @return String[] list of idrefs for this node.
   */
  private String[] _getIdRefList() {
    return _idrefList;
  }

  /**
   * Make a list of idref entries from the nodes String of idref's.
   *
   * <p>This should only be called from the node's setIdRef method. So if it is called more than
   * once (highly unlikely), simply empty out the previous contents.
   *
   * @param entries - String of String entries
   */
  private void _makeIdRefList(String entries) {
    _idrefList = entries.trim().split("\\s+");
  }

  private String _idref = null;
  private String[] _idrefList = null;

  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(GroupNode.class);
}
Exemple #15
0
/**
 * Private utility methods for loading source icons.
 *
 * @version $Name: $ ($Revision:
 *     adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/image/laf/browser/SourceUtils.java#0
 *     $) $Date: 10-nov-2005.19:05:11 $
 */
class SourceUtils {
  public static Image getSourceIcon(ImageContext context, Map<Object, Object> properties) {
    return getSourceIcon(context, properties, ImageConstants.SOURCE_INPUT_STREAM_PROVIDER_KEY);
  }

  /** Returns the source Image for the specified requested properties */
  public static Image getSourceIcon(
      ImageContext context, Map<Object, Object> properties, Object key) {
    InputStreamProvider provider = (InputStreamProvider) properties.get(key);

    // If we can't get a provider, we can't get an Image
    if (provider == null) {
      _log(properties, _PROVIDER_ERROR + " (" + _getKeyName(key) + ")", null);

      return null;
    }

    InputStream in = null;

    try {
      in = provider.openInputStream();
    } catch (IOException e) {
      _log(properties, _INPUT_STREAM_ERROR, e);
      return null;
    }

    if (in == null) {
      _log(properties, _INPUT_STREAM_ERROR, null);
      return null;
    }

    // Note: getImageFromStream() closes up our InputStream,
    // so we do not explicitly call InputStream.close() here.
    Image source = ImageUtils.getImageFromStream(in);

    if (source == null) {
      _log(properties, _IMAGE_ERROR, null);
    }

    return source;
  }

  // Logs a warning
  private static void _log(Map<Object, Object> properties, String message, Throwable t) {
    if (_LOG.isWarning()) {
      String source = (String) properties.get(ImageConstants.SOURCE_KEY);

      if (source != null) message += ("for source icon " + source);

      _LOG.warning(message, t);
    }
  }

  private static String _getKeyName(Object key) {
    return key.toString();
  }

  // Error messages
  private static final String _PROVIDER_ERROR = "Could not get InputStreamProvider";
  private static final String _INPUT_STREAM_ERROR = "Could not get InputStream";
  private static final String _IMAGE_ERROR = "Could not create image";

  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(SourceUtils.class);
}
/**
 * Booolean BoundValue that compares either two BoundValues or a BoundValues and an Object with a
 * comparison operator and returns the Boolean result.
 *
 * <p><STRONG> Only BoundValues that return <CODE>java.lang.Numbers</CODE> or <CODE>
 * java.lang.Numbers</CODE> can be used with comparisons other than <CODE>COMPARISON_EQUALS</CODE>
 * and <CODE>COMPARISON_NOT_EQUALS</CODE> </STRONG>
 *
 * <p>
 *
 * @version $Name: $ ($Revision$) $Date$
 * @deprecated This class comes from the old Java 1.2 UIX codebase and should not be used anymore.
 */
@Deprecated
public class ComparisonBoundValue implements BoundValue {
  /** True if the left and right sides are equivalent. */
  public static final int COMPARISON_EQUALS = 1;

  /** True if the left and right sides are not equivalent. */
  public static final int COMPARISON_NOT_EQUALS = ~COMPARISON_EQUALS;

  /**
   * True if the left side is greater than the right side.
   *
   * <p><STRONG>This requires that both sides be or return <CODE>java.lang.Number</CODE>s.
   */
  public static final int COMPARISON_GREATER_THAN = 2;

  /**
   * True if the left side is greater than or equal to the right side.
   *
   * <p><STRONG>This requires that both sides be or return <CODE>java.lang.Number</CODE>s.
   */
  public static final int COMPARISON_GREATER_THAN_OR_EQUALS =
      COMPARISON_GREATER_THAN + COMPARISON_EQUALS;

  /**
   * True if the left side is less than the right side.
   *
   * <p><STRONG>This requires that both sides be or return <CODE>java.lang.Number</CODE>s.
   */
  public static final int COMPARISON_LESS_THAN = ~COMPARISON_GREATER_THAN_OR_EQUALS;

  /**
   * True if the left side is less than or equal to the right side.
   *
   * <p><STRONG>This requires that both sides be or return <CODE>java.lang.Number</CODE>s.
   */
  public static final int COMPARISON_LESS_THAN_OR_EQUALS = ~COMPARISON_GREATER_THAN;

  public ComparisonBoundValue(int comparison, BoundValue leftSideValue, BoundValue rightSideValue) {
    if (leftSideValue == null)
      throw new IllegalArgumentException(_LOG.getMessage("NULL_LEFTSIDEVALUE"));
    if (rightSideValue == null)
      throw new IllegalArgumentException(_LOG.getMessage("NULL_RIGHTSIDEVALUE"));

    if ((comparison < COMPARISON_LESS_THAN) || (comparison > COMPARISON_GREATER_THAN_OR_EQUALS))
      throw new IllegalArgumentException(_LOG.getMessage("UNKNOWN_COMPARISON"));

    _comparison = comparison;

    _leftSideValue = leftSideValue;
    _rightSideValue = rightSideValue;
  }

  public ComparisonBoundValue(int comparison, BoundValue leftSideValue, Object rightSide) {
    this(comparison, leftSideValue, new FixedBoundValue(rightSide));
  }

  public static ComparisonBoundValue createExistsValue(BoundValue existenceValue) {
    return new ComparisonBoundValue(
        COMPARISON_NOT_EQUALS, existenceValue, FixedBoundValue.NULL_VALUE);
  }

  /** Calculates the current state of the model. */
  public Object getValue(UIXRenderingContext context) {
    Object leftSide = _leftSideValue.getValue(context);
    Object rightSide = _rightSideValue.getValue(context);

    //
    // determine the comparison to attempt
    //
    boolean isNot = false;

    int comparison = _comparison;

    // make sure that we can always attempt an equivalence comparison,
    // by inverting the comparison if necessary
    if ((comparison & COMPARISON_EQUALS) == 0) {
      comparison = ~comparison;
      isNot = true;
    }

    boolean areBothNumbers = (leftSide instanceof Number) && (rightSide instanceof Number);
    boolean newResult;
    // attempt equivalence comparison
    if (leftSide == rightSide) {
      newResult = true;
    } else if (leftSide != null) {
      if (areBothNumbers) newResult = _equalsForNumbers((Number) leftSide, (Number) rightSide);
      else newResult = leftSide.equals(rightSide);
    } else {
      newResult = false;
    }

    // if we have a greter than / less than comparison and the equivalence
    // comparison failed, tried the other comparison
    if (!newResult && (comparison != COMPARISON_EQUALS)) {
      // numeric comparisons against null always fail
      if ((leftSide == null) || (rightSide == null)) return Boolean.FALSE;

      if (!areBothNumbers) {
        if (_LOG.isSevere())
          _LOG.severe(new IllegalArgumentException("Numeric comparisons only allowed on numbers"));

        return Boolean.FALSE;
      }

      Number leftNumber = (Number) leftSide;
      Number rightNumber = (Number) rightSide;

      //
      // at this point the comparison will be either less than or equals
      // or greater than or equals.  Since we know that the equals portion
      // must have failed, we can simply treat all comparisons as greater
      // than and invert the result for less than
      //
      if (comparison == COMPARISON_LESS_THAN_OR_EQUALS) {
        isNot = !isNot;
      }

      if (leftNumber instanceof Long) {
        newResult = (leftNumber.longValue() > rightNumber.longValue());
      } else {
        newResult = (leftNumber.doubleValue() > rightNumber.doubleValue());
      }
    }

    if (isNot) newResult = !newResult;

    return (newResult) ? Boolean.TRUE : Boolean.FALSE;
  }

  private static boolean _equalsForNumbers(Number a, Number b) {
    if ((a == null) || (b == null)) return (a == b);

    Class<?> ac = a.getClass();
    Class<?> bc = b.getClass();
    if (ac == bc) return a.equals(b);

    if ((ac == Long.class) || (ac == Integer.class) || (ac == Short.class) || (ac == Byte.class))
      return _equalsForLong(a.longValue(), b, bc);

    if ((ac == Double.class) || (ac == Float.class))
      return _equalsForDouble(a.doubleValue(), b, bc);

    if (ac == BigInteger.class) return _equalsForBigInteger((BigInteger) a, b, bc);

    if (ac == BigDecimal.class) return _equalsForBigDecimal((BigDecimal) a, b, bc);

    return a.equals(b);
  }

  private static boolean _equalsForLong(long a, Number b, Class<?> bc) {
    if ((bc == Double.class) || (bc == Float.class)) {
      return (b.doubleValue() == a);
    }

    if (bc == BigDecimal.class) {
      return BigDecimal.valueOf(a).equals(b);
    }

    if (bc == BigInteger.class) {
      return BigInteger.valueOf(a).equals(b);
    }

    return a == b.longValue();
  }

  private static boolean _equalsForDouble(double a, Number b, Class<?> bc) {
    if (bc == BigDecimal.class) {
      return new BigDecimal(a).equals(b);
    }

    if (bc == BigInteger.class) {
      return new BigDecimal(a).equals(new BigDecimal((BigInteger) b));
    }

    return a == b.doubleValue();
  }

  private static boolean _equalsForBigInteger(BigInteger a, Number b, Class<?> bc) {
    if (bc == BigDecimal.class) {
      return new BigDecimal(a).equals(b);
    }

    if ((bc == Double.class) || (bc == Float.class)) {
      return new BigDecimal(a).equals(new BigDecimal(b.doubleValue()));
    }

    return BigInteger.valueOf(b.longValue()).equals(a);
  }

  private static boolean _equalsForBigDecimal(BigDecimal a, Number b, Class<?> bc) {
    if (bc == BigInteger.class) {
      return a.equals(new BigDecimal((BigInteger) b));
    }

    if ((bc == Double.class) || (bc == Float.class)) {
      return a.equals(new BigDecimal(b.doubleValue()));
    }

    return a.equals(BigDecimal.valueOf(b.longValue()));
  }

  private BoundValue _leftSideValue;
  private BoundValue _rightSideValue;
  private int _comparison;
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(ComparisonBoundValue.class);
}
/**
 * A resource loader implementation which loads resources for the core renderkit.
 *
 * @todo Dynamic version number
 */
public class CoreRenderKitResourceLoader extends RegexResourceLoader {
  public CoreRenderKitResourceLoader(ResourceLoader parent) {
    register("(/.*/Common.*\\.js)", new CoreCommonScriptsResourceLoader(false));
    register("(/.*/DebugCommon.*\\.js)", new CoreCommonScriptsResourceLoader(true));

    register("(/.*/CoreFmt.*\\.js)", new CoreFormatScriptsResourceLoader(false));
    register("(/.*/DebugCoreFmt.*\\.js)", new CoreFormatScriptsResourceLoader(true));

    register("(/.*LocaleElements.*\\.js)", new LocaleElementsResourceLoader());

    register(
        "(/.*\\.(css|cur|ico|jpg|gif|png|jpeg|svg|js))", new CoreClassLoaderResourceLoader(parent));
  }

  public static String getLocaleElementsURI(String str, Boolean incVersion) {
    StringBuffer base = new StringBuffer("/adf/jsLibs/resources/");

    base.append(str);
    base.append("_");

    String locStr = getLocale();

    base.append(locStr);
    if (incVersion) base.append(_VERSION);
    base.append(".js");

    return base.toString();
  }

  public static String getLocale() {
    String path =
        ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest())
            .getPathInfo();
    String locStr = "";

    int locIndex = path.indexOf("LocaleElements") + "LocaleElements_".length();
    int index = path.indexOf(_VERSION);

    if (index < 0) index = path.indexOf(".js");

    if (index >= 0) locStr = path.substring(locIndex, index);

    return locStr;
  }

  public static String __getVersion() {
    return _VERSION;
  }

  // Path to ResourceServlet
  // Version string to append to library, style sheet URIs
  private static final String _VERSION;

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(CoreRenderKitResourceLoader.class);

  static {
    // Note: Java Package versioning is useless during development when
    //       we have no JARs, whereas this technique works with non-JAR
    //       classpaths as well.
    String version = "unknown";

    try {
      URL resource = ClassLoaderUtils.getResource("META-INF/trinidad-version.txt");
      if (resource != null) {
        BufferedReader br = null;
        try {
          InputStream in = resource.openStream();
          br = new BufferedReader(new InputStreamReader(in));
          version = br.readLine();
        } catch (IOException e) {
          _LOG.severe(e);
        } finally {
          if (br != null) br.close();
        }
      }
    } catch (IOException e) {
      _LOG.severe(e);
    } finally {
      _VERSION = version;
    }
  }
}
/**
 * The includeCompactProperty node is a data structure to store this syntax: border: 1px solid
 * -tr-property-ref(".AFDarkColor:alias",color); which should resolve to border: 1px solid #cccccc;
 * (if #cccccc is .AFDarkColor:alias's color. This is similar to includeProperty element, but it is
 * used on compact css property values. An IncludePropertyNode is a data structure to store this
 * syntax: color: -tr-property-ref(".AFDarkColor:alias",color); includeProperty element is used to
 * include a single property of one style within another style. Thus, the includeProperty element is
 * very similar to the includeStyle element. The only difference is that includeStyle includes all
 * properties of the referenced style, whereas includeProperty includes only a single property.
 *
 * @version $Name: $ ($Revision:
 *     adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/style/xml/parse/IncludeCompactPropertyNode.java#0
 *     $) $Date: 10-nov-2005.18:58:07 $
 */
public class IncludeCompactPropertyNode {

  /**
   * Creates an IncludeCompactPropertyNode. If we have border: 1px solid
   * -tr-property-ref(".AFDarkColor:alias",color); in the css file, then localPropertyName ==
   * border, propertyValues == {"1px solid"}, and includePropertyNodeList will contain the
   * -tr-property-ref(".AFDarkColor:alias",color);
   *
   * @param includePropertyNodeList
   * @param propertyValues
   * @param localPropertyName
   */
  public IncludeCompactPropertyNode(
      String propertyValues,
      List<IncludePropertyNode> includePropertyNodeList,
      String localPropertyName) {
    // The caller of this constructor must have all these values filled out.
    if (propertyValues == null) throw new IllegalArgumentException();
    if (includePropertyNodeList == null) throw new IllegalArgumentException();
    if (localPropertyName == null) throw new IllegalArgumentException();

    _propertyValues = propertyValues;
    _includePropertyNodeList = includePropertyNodeList;
    _localPropertyName = localPropertyName;
  }

  /** Returns the name of the style to include. */
  public String getLocalPropertyName() {
    return _localPropertyName;
  }

  /** Returns the name of the style to include. */
  public String getPropertyValues() {
    return _propertyValues;
  }

  public Collection<IncludePropertyNode> getIncludedProperties() {
    return _includePropertyNodeList;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof IncludeCompactPropertyNode)) return false;

    // obj at this point must be an IncludeCompactPropertyNode
    IncludeCompactPropertyNode test = (IncludeCompactPropertyNode) obj;
    return (_localPropertyName == test._localPropertyName
            || (_localPropertyName != null && _localPropertyName.equals(test._localPropertyName)))
        && (_propertyValues == test._propertyValues
            || (_propertyValues != null && _propertyValues.equals(test._propertyValues)))
        && (_includePropertyNodeList == test._includePropertyNodeList
            || (_includePropertyNodeList != null
                && _includePropertyNodeList.equals(test._includePropertyNodeList)));
  }

  @Override
  public int hashCode() {
    int hash = 17;
    hash = 37 * hash + ((null == _localPropertyName) ? 0 : _localPropertyName.hashCode());
    hash = 37 * hash + ((null == _propertyValues) ? 0 : _propertyValues.hashCode());
    hash =
        37 * hash + ((null == _includePropertyNodeList) ? 0 : _includePropertyNodeList.hashCode());

    return hash;
  }

  @Override
  // TODO jmwOctober. test this
  public String toString() {
    return "[propertyValues="
        + _propertyValues
        + ", "
        + "includePropertyNodeList="
        + _includePropertyNodeList
        + "]";
  }

  private final String _propertyValues;
  private final List<IncludePropertyNode> _includePropertyNodeList;
  private final String _localPropertyName;

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(IncludeCompactPropertyNode.class);
}
/** Renderer for process train components */
public class TrainRenderer extends XhtmlRenderer {
  /** Constructor. */
  public TrainRenderer() {
    super(CoreTrain.TYPE);
  }

  /** */
  @SuppressWarnings("unchecked")
  @Override
  public void decode(FacesContext context, UIComponent component) {
    Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();

    Object event = requestMap.get(XhtmlConstants.EVENT_PARAM);

    if ((event != null) && event.equals(XhtmlConstants.GOTO_EVENT)) {
      Object source = requestMap.get(XhtmlConstants.SOURCE_PARAM);

      if (source != null && source.equals(component.getClientId(context))) {

        Object valueObject = requestMap.get(XhtmlConstants.VALUE_PARAM);

        // we piggyback on the size parameter.
        // 0 means we are moving to a previous step, 1 means we are
        // moving to the next step.
        Object sizeObject = requestMap.get(XhtmlConstants.SIZE_PARAM);

        if (valueObject != null) {
          int value = -1;

          try {
            value = Integer.parseInt(valueObject.toString());
          } catch (NumberFormatException nfe) {
            _LOG.severe(nfe);
          }

          int size = 0;

          try {
            size = Integer.parseInt(sizeObject.toString());
          } catch (NumberFormatException nfe) {
            _LOG.warning(nfe);
          }

          if (size < 0) size = 0;

          if (value >= 0) {
            UIXProcess process = (UIXProcess) component;
            Object oldPath = process.getRowKey();
            Object focusPath = process.getFocusRowKey();
            process.setRowKey(focusPath);
            UIComponent stamp = process.getNodeStamp();
            int index = process.getRowIndex();

            if (size == 0) {
              index = ProcessUtils.getBackIndex(process, stamp, index);
            } else {
              index = ProcessUtils.getNextIndex(process, stamp, index);
            }

            process.setRowIndex(index);
            new ActionEvent(stamp).queue();
            process.setRowKey(oldPath);
          }
        }
      }
    }
  }

  /** @return */
  @Override
  public boolean getRendersChildren() {
    return true;
  }

  @Override
  protected void encodeAll(
      FacesContext context, RenderingContext arc, UIComponent component, FacesBean bean)
      throws IOException {
    // Since Train is a naming container, we can be more
    // efficient about skipping its children
    if (!PartialPageUtils.containsPprTargets(arc, component, getClientId(context, component))) {
      return;
    }

    if (!(component instanceof UIXProcess)) {
      throw new ClassCastException(
          _LOG.getMessage(
              "TRAINRENDERER_ONLY_RENDERS_INSTANCE",
              new Object[] {UIXProcess.class.getName(), component.getClass().getName()}));
    }

    if (arc.getFormData() == null) {
      _LOG.warning("TRAIN_MUST_INSIDE_FORM");
      return;
    }

    UIXProcess process = (UIXProcess) component;
    UIComponent stamp = process.getNodeStamp();

    if (stamp != null) {
      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();

        // 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);
        renderAllAttributes(context, arc, bean);
        // Does not seem to be needed and this is not XHTML 1.0 Strict compliant
        // writer.writeAttribute("align", "center", null);

        if (!train.getStations().isEmpty()) {
          process.setRowKey(train.getFocusRowKey());

          // 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("NODESTAMP_FACET_NOT_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);
  }

  /** This is how we can render both the user defined styleClass and our component style class */
  @Override
  protected void renderStyleAttributes(FacesContext context, RenderingContext arc, FacesBean bean)
      throws IOException {
    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);
  }

  private void _renderHiddenFields(FacesContext context, RenderingContext arc, Train train)
      throws IOException {
    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);
      }

      // 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()) {
      _renderParentContent(context, arc, parentTrain.getParentEnd());
    }
  }

  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()) {
      _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();

    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, 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()) {
      _renderParentEndLtr(context, arc, process, train);
    }
  }

  private void _renderIconRowRtl(
      FacesContext context, RenderingContext arc, UIXProcess process, 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()) {
      _renderParentStartRtl(context, arc, process, train);
    }
  }

  private void _renderJoin(
      FacesContext context, RenderingContext arc, String stateStyleClass, boolean overflow)
      throws IOException {
    if (_STATE_PARENT.equals(stateStyleClass)) {
      _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 {
      _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();

    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 (_STATE_PARENT.equals(stateStyleClass)) {
      _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 {
      _renderJoinIconBlock(context, arc, SkinSelectors.AF_TRAIN_JOIN_STYLE_CLASS, stateStyleClass);
    }
  }

  private void _renderJoinIconBlock(
      FacesContext context, RenderingContext arc, String joinStyleClass, String stateStyleClass)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();

    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 {
    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);
    }

    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
  }

  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 {
    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 {
      arc.setSkinResourceKeyMap(_RESOURCE_KEY_MAP);
      encodeChild(context, stamp);
    } finally {
      arc.setSkinResourceKeyMap(originalMap);
    }

    writer.endElement(XhtmlConstants.TABLE_DATA_ELEMENT);
  }

  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);
  }

  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 _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 of the icon row
    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);

    if (arc.isRightToLeft()) {
      _renderIconRowRtl(context, arc, process, train);
    } else {
      _renderIconRowLtr(context, arc, process, train);
    }

    writer.endElement(XhtmlConstants.TABLE_ROW_ELEMENT);

    // Start of the content row
    writer.startElement(XhtmlConstants.TABLE_ROW_ELEMENT, null);

    if (arc.isRightToLeft()) {
      _renderContentRowRtl(context, arc, process, stamp, train);
    } else {
      _renderContentRowLtr(context, arc, process, stamp, train);
    }

    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 activeIndex = _loadStations(process, stamp);
      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() {
      return _focusRowKey;
    }

    public String getFormName() {
      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) {
      process.setRowIndex(index);
      if (stamp.isRendered()) {
        // The station will be visible.
        _stations.add(new Station(this, stamp, index, process.getRowKey(), active));
      }
    }

    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_ABOVE_ZERO", count);
          return DEFAULT_MAX_VISIBLE_STOP_COUNT;
        }

        return count;
      } catch (NumberFormatException e) {
        _LOG.warning("VISIBLE_STOP_COUNT_MYST_INTEGER", propValue);
        return DEFAULT_MAX_VISIBLE_STOP_COUNT;
      }
    }

    private void _initLabels(RenderingContext arc, UIXProcess process, UIComponent stamp) {
      for (Station s : _stations) {
        process.setRowIndex(s.getRowIndex());
        s.initLabel(arc, stamp);
      }
    }

    private void _initParentTrain(RenderingContext arc, UIXProcess process, UIComponent stamp) {
      if (_isSubTrain) {
        if (_shouldRenderParentTrain(arc)) {
          _parent = new ParentTrain(arc, process, stamp, this);
          if (!_parent.hasParentStart() && !_parent.hasParentEnd()) {
            _isSubTrain = false;
          }
        } else {
          _isSubTrain = false;
        }
      }
    }

    /** Determine if this train is a sub-train. */
    private boolean _loadIsSubTrain(UIXProcess process) {
      Object focusRowKey = process.getFocusRowKey();
      if (focusRowKey != null && (process.getDepth(focusRowKey) > 0)) {
        return true;
      }

      return false;
    }

    private int _loadStations(UIXProcess process, UIComponent stamp) {
      _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);
        boolean bActiveStop = false;

        // Process visited stations
        for (; index < count; index++) {
          bActiveStop = (index == activeIndex);
          _createStation(process, stamp, index, bActiveStop);
          if (bActiveStop) {
            // 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;
          }
        }
        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);
        }

        _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;
      _parentEnd = false;
      _parentStart = false;
      _train = train;
    }

    @SuppressWarnings("unchecked")
    public Station(Train train, UIComponent stamp, int index, Object rowKey, boolean active) {
      Map<String, Object> attributes = stamp.getAttributes();

      _rowIndex = index;
      _rowKey = rowKey;
      _active = active;
      _selected = _getBooleanAttribute(attributes, "selected", false);
      _visited = _getBooleanAttribute(attributes, "visited", false);
      _disabled = _getBooleanAttribute(attributes, "disabled", false);
      _parentEnd = false;
      _parentStart = false;
      _train = train;
    }

    public String getBaseStyleClass() {
      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()) {
        return SkinSelectors.AF_TRAIN_PARENT_END_STYLE_CLASS;
      } else {
        return SkinSelectors.AF_TRAIN_STOP_STYLE_CLASS;
      }
    }

    public String getEndJoinState() {
      if (isOverflowEnd()) {
        return null;
      } else if (!hasNext()) {
        ParentTrain parent = _train.getParentTrain();
        if (parent != null && parent.hasParentEnd()) {
          return _STATE_PARENT;
        } else {
          return null;
        }
      } else if (getNext().isVisited()) {
        return _STATE_VISITED;
      } else {
        if (getNext().isDisabled()) {
          return _STATE_DISABLED;
        }
        return _STATE_UNVISITED;
      }
    }

    public List<String> getIconNames() {
      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();
      }
    }

    public String getLabel() {
      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 (isVisited()) {
        return _STATE_VISITED;
      } else {
        if (isDisabled()) {
          return _STATE_DISABLED;
        }
        return _STATE_UNVISITED;
      }
    }

    public List<String> getStates() {
      List<String> states = new ArrayList<String>(5);
      if (isParentStart() || isParentEnd()) {
        return states;
      }

      if (isActive() || isSelected()) {
        states.add(_STATE_ACTIVE);
        return states;
      } else if (isVisited()) {
        states.add(_STATE_VISITED);
      } else {
        states.add(_STATE_UNVISITED);
      }

      if (isDisabled()) {
        states.add(_STATE_READ_ONLY);
      }

      return states;
    }

    public boolean hasNext() {
      return _next != null;
    }

    public boolean hasPrevious() {
      return _previous != null;
    }

    /**
     * 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) {
      if (isOverflowStart()) {
        _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;
        }
      }
    }

    public boolean isActive() {
      return _active;
    }

    public boolean isSelected() {
      return _selected;
    }

    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 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;
    }

    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() || isSelected()) {
        altTextKey = _ACTIVE_KEY;
      } else if (isVisited()) {
        altTextKey = _VISITED_KEY;
      } else {
        altTextKey = _NEXT_KEY;
      }

      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 (isActive() || isSelected()) {
        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 (isDisabled()) {
        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 _selected; // Is this station the selected 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 _visited; // visited attribute

    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;
  }

  private static class ParentTrain {
    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() {
      return _parentEnd;
    }

    public Station getParentStart() {
      return _parentStart;
    }

    public boolean hasParentEnd() {
      return _parentEnd != null;
    }

    public boolean hasParentStart() {
      return _parentStart != null;
    }

    private Station _parentEnd;
    private Station _parentStart;
  }

  /**
   * Gives the amount of visible stops that get rendered by default if no amount is specified by the
   * -tr-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 -tr-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_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. */
  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";
  private static final String _MORE_KEY = "af_train.MORE";
  private static final String _PREVIOUS_KEY = "af_train.PREVIOUS";

  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(TrainRenderer.class);

  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);
  }
}
/** User: Mahnaz Ebrahimi Date: Aug 30, 2008 */
public class JSFPagingTreeModel extends ChildPropertyTreeModel {
  private Object instance;
  private TreePagingModelFactory pagingModelFactory;
  private CoreTree tree;
  private CoreTreeTable treeTable;
  private int pageSize;
  private Object selectedObject;

  public JSFPagingTreeModel(
      Object instance,
      String childNameProperty,
      TreePagingModelFactory pagingModelFactory,
      CoreTree tree,
      int pageSize) {
    this.instance = instance;
    this.pagingModelFactory = pagingModelFactory;
    this.tree = tree;
    this.pageSize = pageSize;
    JSFPagedTableModel model = new JSFPagedTableModel(instance, pagingModelFactory, pageSize);
    JSFDefaultMutableTreeNode root = new JSFDefaultMutableTreeNode(instance);
    _path.add(root);
    root.childModel = model;
    super.setChildProperty(childNameProperty);
  }

  public JSFPagingTreeModel(
      Object instance,
      String childNameProperty,
      TreePagingModelFactory pagingModelFactory,
      CoreTreeTable treeTable,
      int pageSize) {
    this.instance = instance;
    this.pagingModelFactory = pagingModelFactory;
    this.treeTable = treeTable;
    this.pageSize = pageSize;
    JSFPagedTableModel model = new JSFPagedTableModel(instance, pagingModelFactory, pageSize);
    JSFDefaultMutableTreeNode root = new JSFDefaultMutableTreeNode(instance);
    _path.add(root);
    root.childModel = model;
    super.setChildProperty(childNameProperty);
  }

  public Object getRowKey() {
    final int sz = _path.size() - 1;
    Object lastRowkey = _getRowKey();
    if ((sz == 0) && (lastRowkey == null)) return null; // root collection

    List<Object> path = new ArrayList<Object>(sz + 1);
    if (sz > 0) {
      for (int i = 0; i < sz; i++) {
        JSFDefaultMutableTreeNode node = _getNode(i);
        path.add(node.childModel.getRowKey());
      }
    }
    path.add(lastRowkey);
    return path;
  }

  public void setRowKey(Object rowKey) {
    JSFDefaultMutableTreeNode root = _getNode(0);
    _path.clear();
    _path.add(root);

    List<Object> path = (List<Object>) rowKey;
    if ((path == null) || (path.size() == 0)) {
      setRowIndex(-1);
      return;
    }
    int lastIndex = path.size() - 1;
    for (int i = 0; i < lastIndex; i++) {
      Object pathKey = path.get(i);
      _setRowKey(pathKey);
      enterContainer();
    }
    _setRowKey(path.get(lastIndex));
  }

  public int getRowCount() {
    return _getModel().getRowCount();
  }

  public Object getRowData() {
    return _getModel().getRowData();
  }

  public boolean isRowAvailable() {
    return _getModel().isRowAvailable();
  }

  public boolean isContainer() {
    Object rowData = getRowData();
    Object value = getChildData(rowData);

    if (value != null) {
      if (value instanceof Collection<?>) {
        return !((Collection<?>) value).isEmpty();
      } else if (value.getClass().isArray()) {
        return Array.getLength(value) > 0;
      } else if (value instanceof DataModel) {
        return ((DataModel) value).getRowCount() > 0;
      }
    }
    return value != null;
  }

  public void enterContainer() {
    Object rowData = getRowData();
    if (rowData == null) {
      throw new IllegalStateException(_LOG.getMessage("NULL_ROWDATA"));
    }
    JSFDefaultMutableTreeNode node = new JSFDefaultMutableTreeNode(rowData);
    _path.add(node);
  }

  public void exitContainer() {
    int sz = _path.size();
    if (sz > 1) _path.remove(sz - 1);
    else throw new IllegalStateException(_LOG.getMessage("CANNOT_EXIT_ROOT_CONTAINER"));
  }

  public Object getContainerRowKey(Object childKey) {
    List<Object> path = (List<Object>) childKey;
    if ((path == null) || (path.size() <= 1)) return null;
    return new ArrayList<Object>(path.subList(0, path.size() - 1));
  }

  public Object getWrappedData() {
    return _wrappedData;
  }

  public int getRowIndex() {
    return _getModel().getRowIndex();
  }

  public void setRowIndex(int rowIndex) {
    _getModel().setRowIndex(rowIndex);
  }

  protected JSFPagedTableModel createChildModel(Object parent) {
    JSFPagedTableModel model = new JSFPagedTableModel(pagingModelFactory, parent);
    model.setRowIndex(-1);
    return model;
  }

  private Object _getRowKey() {
    return _getModel().getRowKey();
  }

  private void _setRowKey(Object key) {
    _getModel().setRowKey(key);
  }

  private JSFDefaultMutableTreeNode _getCurrentNode() {
    return _getNode(_path.size() - 1);
  }

  private JSFDefaultMutableTreeNode _getNode(int index) {
    return _path.get(index);
  }

  private JSFPagedTableModel _getModel() {
    JSFDefaultMutableTreeNode node = _getCurrentNode();
    JSFPagedTableModel model = node.getChildModel();

    if (model == null) {
      model = createChildModel(node.getUserObject());
      node.setChildModel(model);
    }
    return model;
  }

  public CoreTree getTree() {
    return tree;
  }

  public void setTree(CoreTree tree) {
    this.tree = tree;
  }

  public CoreTreeTable getTreeTable() {
    return treeTable;
  }

  public void setTreeTable(CoreTreeTable treeTable) {
    this.treeTable = treeTable;
  }

  public void selectionPerformed(SelectionEvent ev) {
    Object selected = getFirstRowKey(ev.getAddedSet());
    if (selected == null) {
      Object unselected = getFirstRowKey(ev.getRemovedSet());
      _getModel().setRowKey(unselected); // @todo
      if (isSelected(getRowData())) {
        setSelectedObject(null);
      }

    } else {
      _getModel().setRowKey(selected); // @todo
      setSelectedObject(getRowData());
    }
  }

  private Object getFirstRowKey(RowKeySet set) {
    for (Object key : set) {
      return key;
    }
    return null;
  }

  public Object getSelectedObject() {
    return selectedObject;
  }

  public boolean isSelected(Object data) {
    return selectedObject != null && selectedObject.equals(data);
  }

  public void setSelectedObject(Object selectedObject) {
    this.selectedObject = selectedObject;
  }

  private final List<JSFDefaultMutableTreeNode> _path = new ArrayList<JSFDefaultMutableTreeNode>(5);
  private Object _wrappedData = null;
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(ChildPropertyTreeModel.class);

  private class JSFDefaultMutableTreeNode extends DefaultMutableTreeNode {
    private JSFPagedTableModel childModel = null;

    private JSFDefaultMutableTreeNode() {
      this(null);
    }

    public JSFDefaultMutableTreeNode(Object userObject) {
      this(userObject, true, null);
    }

    public JSFDefaultMutableTreeNode(
        Object userObject, boolean allowsChildren, JSFPagedTableModel childModel) {
      super(userObject, allowsChildren);
      this.childModel = childModel;
    }

    public JSFPagedTableModel getChildModel() {
      return childModel;
    }

    public void setChildModel(JSFPagedTableModel childModel) {
      this.childModel = childModel;
    }
  }
}
/**
 * Abstracts out the retrieval of ImageProviderResponses for dual ramp colorization.
 *
 * <p>
 *
 * @version $Name: $ ($Revision$) $Date$
 * @deprecated This class comes from the old Java 1.2 UIX codebase and should not be used anymore.
 */
@Deprecated
public abstract class AccentedLafIconProvider extends ColorizedLafIconProvider
    implements XhtmlLafConstants {

  /** Returns an icon, given its index. */
  protected abstract Icon getIcon(IconKey iconKey);

  /**
   * Tests whether the the icon at the specified index is transparent. By default, all icons are
   * considered transparent.
   */
  protected boolean isTransparent(IconKey iconKey) {
    return true;
  }

  protected abstract ImageProviderRequest createCoreIconRequest(
      ImageContext context,
      String source,
      Class<LookAndFeel> lookAndFeel,
      int direction,
      Color color,
      Color surroundingColor,
      NameResolver resolver);

  protected abstract ImageProviderRequest createAccentIconRequest(
      ImageContext context,
      String source,
      Class<LookAndFeel> lookAndFeel,
      int direction,
      Color color,
      Color surroundingColor,
      NameResolver resolver);

  /** Returns an image from the ImageProvider */
  @Override
  public ImageProviderResponse getColorizedIcon(UIXRenderingContext context, IconKey iconKey) {
    ImageProvider provider =
        (ImageProvider)
            context.getProperty(
                ImageConstants.TECATE_NAMESPACE, ImageConstants.IMAGE_PROVIDER_PROPERTY);

    Icon icon = getIcon(iconKey);
    String iconName = null;

    if (icon != null) iconName = icon.getName();

    if (provider == null) {
      if (_LOG.isWarning()) _LOG.warning("CANNOT_GET_IMAGE_PROVIDER_FOR_ICON", iconName);

      return null;
    }

    // Get the context and request objects
    ImageContext imageContext = context.getImageContext();
    ImageProviderRequest request = _getIconRequest(context, iconKey);

    // Make the request
    ImageProviderResponse response = provider.getImage(imageContext, request);

    // Log any problems
    if (response == null) {
      if (_LOG.isWarning()) _LOG.warning("CANNOT_GET_COLORIZED_ICON", iconName);
    }

    return response;
  }

  // Returns the request object for the icon at the specified index
  private ImageProviderRequest _getIconRequest(UIXRenderingContext context, IconKey iconKey) {
    // We need three pieces of info for the icon request:

    Icon icon = getIcon(iconKey);

    // Get the index of the icon name in the _ICONS list
    String source = null;
    boolean isDirectionIndependent = false;
    boolean isCoreColor = false;
    Class<LookAndFeel> lookAndFeel = null;
    NameResolver resolver = null;

    if (icon != null) {
      source = icon.getName();
      isDirectionIndependent = icon.isSymmetric();
      isCoreColor = icon.isCoreColor();
      lookAndFeel = icon.getLookAndFeel();
      resolver = icon.getNameResolver();
    }

    if (source == null) {
      _LOG.warning("CANNOT_FIND_ICON_WITH_GIVEN_KEY");
      return null;
    }

    // Get the direction.  If the source icon is symmetrical, we
    // always use the LTR version.
    int direction = -1;

    if (isDirectionIndependent) direction = LocaleUtils.DIRECTION_LEFTTORIGHT;
    else direction = LocaleUtils.getReadingDirection(context.getLocaleContext());

    assert (direction != -1);

    // Get the color for this request.  The color is either dark or
    // dark accent depending on whether the source icon uses core or
    // accent colors.
    Color color = null;
    ImageContext imageContext = context.getImageContext();

    if (isCoreColor) color = _getCoreColor(context);
    else color = _getAccentColor(context);

    // Get the surrounding color for this icon.
    Color surroundingColor = _getSurroundingColor(context, iconKey);

    // =-=ags To avoid lots of object allocations, we might want
    //        to keep a pool of IconRequest objects around or something.
    if (isCoreColor) {
      return createCoreIconRequest(
          imageContext, source, lookAndFeel, direction, color, surroundingColor, resolver);
    }

    return createAccentIconRequest(
        imageContext, source, lookAndFeel, direction, color, surroundingColor, resolver);
  }

  // Gets the Core color to use as the background color when
  // colorizing blue icons.
  private static Color _getCoreColor(UIXRenderingContext context) {
    return _getColor(context, BGCOLOR_DARK_STYLE_CLASS, _CORE_COLOR_KEY, _DEFAULT_CORE_COLOR);
  }

  // Gets the Accent color to use as the background color when
  // colorizing tan icons.
  private static Color _getAccentColor(UIXRenderingContext context) {
    return _getColor(context, BGACCENT_DARK_STYLE_CLASS, _ACCENT_COLOR_KEY, _DEFAULT_ACCENT_COLOR);
  }

  /**
   * Gets a Color stored as a property on the RenderingContext using the specified key. Or if not
   * found on, gets the color from the Ocelot StyleMap using the specified style class name.
   */
  private static Color _getColor(
      UIXRenderingContext context, String styleClass, Object key, Color defaultColor) {
    // First check for the color on the RenderingContext
    Color color = (Color) context.getProperty(UIConstants.MARLIN_NAMESPACE, key);

    if (color != null) return color;

    // If the color hasn't been stored on the RenderingContext, get it
    // from the style map.
    StyleMap map = context.getStyleContext().getStyleMap();

    if (map != null) {
      Style style = map.getStyleByClass(context.getStyleContext(), styleClass);
      if (style != null) {
        try {
          color = (Color) style.getParsedProperty(Style.BACKGROUND_KEY);
        } catch (PropertyParseException e) {
          // This should really be reported at parse time
          _LOG.info(e);
        }
      }
    }

    if (color == null) color = defaultColor;

    // Cache the color on the RenderingContext
    context.setProperty(UIConstants.MARLIN_NAMESPACE, key, color);

    return color;
  }

  // Return the surrounding color to use for the icon at
  // the specified index
  private Color _getSurroundingColor(UIXRenderingContext context, IconKey iconKey) {
    // If the image is transparent and the Agent does not support
    // transparent images, we grab the current background color off of
    // the style attrs stack
    if (isTransparent(iconKey) && !XhtmlLafUtils.supportsTransparentImages(context)) {
      return XhtmlLafUtils.getBackgroundColor(context);
    }

    return null;
  }

  // Keys for obtaining colorization colors from the RenderingContext
  private static final String _ACCENT_COLOR_KEY = "_accentColor";
  private static final String _CORE_COLOR_KEY = "_coreColor";

  // Default values for colorization colors
  private static final Color _DEFAULT_ACCENT_COLOR = new Color(204, 204, 153);
  private static final Color _DEFAULT_CORE_COLOR = new Color(51, 102, 153);
  private static final TrinidadLogger _LOG =
      TrinidadLogger.createTrinidadLogger(AccentedLafIconProvider.class);
}