protected Iterator<MessageForRender> getMessages(
      FacesContext context, String forClientId, UIComponent component) {

    Iterator<MessageForRender> msgIter;

    if (forClientId != null) {

      if (forClientId.length() != 0) {

        UIComponent result = RendererUtils.getInstance().findComponentFor(component, forClientId);
        if (result == null) {
          msgIter = Iterators.emptyIterator();
        } else {
          String clientId = result.getClientId(context);
          msgIter = getMessagesForId(context, clientId);
        }
      } else {
        msgIter = getMessagesForId(context, null);
      }
    } else {
      msgIter = Iterators.emptyIterator();
      Iterator<String> clientIdsWithMessages = context.getClientIdsWithMessages();
      while (clientIdsWithMessages.hasNext()) {
        String clientId = (String) clientIdsWithMessages.next();
        msgIter = Iterators.concat(msgIter, getMessagesForId(context, clientId));
      }
    }

    return msgIter;
  }
 protected void encodeScript(FacesContext facesContext, UIComponent component) throws IOException {
   JavaScriptService javaScriptService = ServiceTracker.getService(JavaScriptService.class);
   JSFunction messageObject = new JSObject(getJSClassName(), component.getClientId(facesContext));
   Map<String, Object> attributes = component.getAttributes();
   Builder<String, Object> parametersBuilder = ImmutableMap.builder();
   String forId = (String) attributes.get("for");
   RendererUtils rendererUtils = RendererUtils.getInstance();
   if (!Strings.isNullOrEmpty(forId)) {
     UIComponent target = rendererUtils.findComponentFor(component, forId);
     if (null != target) {
       parametersBuilder.put("forComponentId", target.getClientId(facesContext));
     }
   }
   Severity level = getLevel(component);
   if (FacesMessage.SEVERITY_INFO != level) {
     parametersBuilder.put("level", level.getOrdinal());
   }
   if (!rendererUtils.isBooleanAttribute(component, "showSummary")) {
     parametersBuilder.put("showSummary", false);
   }
   if (rendererUtils.isBooleanAttribute(component, "showDetail")) {
     parametersBuilder.put("showDetail", true);
   }
   if (rendererUtils.isBooleanAttribute(component, "tooltip")) {
     parametersBuilder.put("tooltip", true);
   }
   if (isComponentMessages(component)) {
     parametersBuilder.put("isMessages", true);
   }
   messageObject.addParameter(parametersBuilder.build());
   // RendererUtils.getInstance().writeScript(facesContext, component, messageObject);
   javaScriptService.addPageReadyScript(facesContext, messageObject);
 }
 protected void setEventPhase(ItemChangeEvent event) {
   if (isImmediate()
       || (event.getNewItem() != null
           && RendererUtils.getInstance().isBooleanAttribute(event.getNewItem(), "immediate"))) {
     event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
   } else {
     event.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
   }
 }
 protected void encodeMessage(FacesContext facesContext, UIComponent component, Object msg)
     throws IOException {
   // TODO fix generator to properly detect iteration variable type
   MessageForRender message = (MessageForRender) msg;
   String summary = message.getSummary();
   String detail = message.getDetail();
   boolean showSummary = true;
   boolean showDetail = false;
   boolean isMessages = false;
   if (isComponentMessage(component)) {
     UIMessage uiMessage = (UIMessage) component;
     showSummary = uiMessage.isShowSummary();
     showDetail = uiMessage.isShowDetail();
   } else if (isComponentMessages(component)) {
     UIMessages uiMessages = (UIMessages) component;
     showSummary = uiMessages.isShowSummary();
     showDetail = uiMessages.isShowDetail();
     isMessages = true;
   }
   ResponseWriter responseWriter = facesContext.getResponseWriter();
   // Message id
   responseWriter.writeAttribute(
       "id", component.getClientId() + ':' + message.getSourceId(), null);
   // tooltip
   boolean wroteTooltip = RendererUtils.getInstance().isBooleanAttribute(component, "tooltip");
   if (wroteTooltip && !Strings.isNullOrEmpty(summary)) {
     responseWriter.writeAttribute("title", summary, null);
   }
   if (!wroteTooltip && showSummary) {
     writeMessageLabel(responseWriter, summary, isMessages ? "rf-msgs-sum" : "rf-msg-sum");
   }
   if (showDetail) {
     writeMessageLabel(responseWriter, detail, isMessages ? "rf-msgs-det" : "rf-msg-det");
   }
   message.rendered();
 }
/**
 * @author amarkhel
 * @since 2010-10-24
 */
@ResourceDependencies({
  @ResourceDependency(library = "org.richfaces", name = "ajax.reslib"),
  @ResourceDependency(library = "org.richfaces", name = "base-component.reslib"),
  @ResourceDependency(name = "jquery.position.js"),
  @ResourceDependency(name = "richfaces-event.js"),
  @ResourceDependency(library = "org.richfaces", name = "popup.js"),
  @ResourceDependency(library = "org.richfaces", name = "tooltip.js"),
  @ResourceDependency(library = "org.richfaces", name = "tooltip.ecss")
})
@JsfRenderer(type = "org.richfaces.TooltipRenderer", family = AbstractTooltip.COMPONENT_FAMILY)
public class TooltipRenderer extends DivPanelRenderer implements MetaComponentRenderer {
  public static final String HIDE = "hide";
  public static final String SHOW = "show";
  public static final String BEFORE_HIDE = "beforehide";
  public static final String BEFORE_SHOW = "beforeshow";
  private static final int DEFAULT_ZINDEX_VALUE = 1000;
  private static final RendererUtils RENDERER_UTILS = RendererUtils.getInstance();

  @Override
  protected void doDecode(FacesContext context, UIComponent component) {
    AbstractTooltip tooltip = (AbstractTooltip) component;

    Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();

    String compClientId = component.getClientId(context);
    String clientId = requestMap.get(compClientId);
    if (clientId != null && clientId.equals(compClientId)) {
      context.getPartialViewContext().getRenderIds().add(tooltip.getContentClientId(context));

      // TODO nick - this should be done on encode, not on decode
      addOnCompleteParam(context, tooltip.getClientId(context));
      context.renderResponse();
    }
  }

  protected static void addOnCompleteParam(FacesContext context, String tooltipId) {
    ExtendedPartialViewContext.getInstance(context)
        .appendOncomplete(
            new StringBuilder()
                .append("RichFaces.$('")
                .append(tooltipId)
                .append("').onCompleteHandler();")
                .toString());
  }

  @Override
  protected void doEncodeBegin(ResponseWriter writer, FacesContext context, UIComponent component)
      throws IOException {
    AbstractTooltip tooltip = (AbstractTooltip) component;

    writer.startElement(getMarkupElement(tooltip), component);
    writer.writeAttribute(ID_ATTRIBUTE, component.getClientId(context), "clientId");

    writer.writeAttribute(HtmlConstants.STYLE_ATTRIBUTE, "display: none;", null);
    writer.startElement(getMarkupElement(tooltip), component);
    writer.writeAttribute(ID_ATTRIBUTE, component.getClientId(context) + ":wrp", null);
    writer.writeAttribute(CLASS_ATTRIBUTE, getStyleClass(component), null);

    int zindex = tooltip.getZindex();
    if (zindex == Integer.MIN_VALUE) {
      zindex = DEFAULT_ZINDEX_VALUE;
    }

    String style = concatStyles("z-index:" + zindex, getStyle(component));
    if (style != null && style.trim().length() > 0) {
      writer.writeAttribute(HtmlConstants.STYLE_ATTRIBUTE, style, null);
    }

    renderPassThroughAttributes(context, component, getPassThroughAttributes());

    writer.startElement(getMarkupElement(tooltip), component);
    writer.writeAttribute(ID_ATTRIBUTE, component.getClientId(context) + ":cntr", null);
    writer.writeAttribute(CLASS_ATTRIBUTE, "rf-tt-cntr", null);

    if (tooltip.getMode() == TooltipMode.ajax) {
      encodeLoading(writer, context, tooltip);
    }

    encodeContentBegin(writer, context, tooltip);
  }

  private void encodeContentBegin(
      ResponseWriter writer, FacesContext context, AbstractTooltip tooltip) throws IOException {
    writer.startElement(getMarkupElement(tooltip), tooltip);
    writer.writeAttribute(ID_ATTRIBUTE, tooltip.getClientId(context) + ":content", null);
    writer.writeAttribute(CLASS_ATTRIBUTE, "rf-tt-cnt", null);
    if (tooltip.getChildCount() == 0) {
      writer.write(tooltip.getValue().toString());
    }
  }

  private void encodeLoading(ResponseWriter writer, FacesContext context, AbstractTooltip tooltip)
      throws IOException {
    writer.startElement(getMarkupElement(tooltip), tooltip);
    writer.writeAttribute(ID_ATTRIBUTE, tooltip.getClientId(context) + ":loading", null);
    writer.writeAttribute(CLASS_ATTRIBUTE, "rf-tt-loading", null);
    UIComponent loading = tooltip.getFacet("loading");
    if (loading != null && loading.isRendered()) {
      loading.encodeAll(context);
    } else {
      writer.writeText("Loading...", null);
    }
    writer.endElement(getMarkupElement(tooltip));
  }

  private String getMarkupElement(AbstractTooltip component) {
    switch (component.getLayout()) {
      case block:
        return HtmlConstants.DIV_ELEM;

      case inline:
        return HtmlConstants.SPAN_ELEM;

      default:
        throw new IllegalStateException("Unknown tooltip layout (=" + component.getLayout() + ")");
    }
  }

  @Override
  protected String getStyleClass(UIComponent component) {
    return concatClasses("rf-tt", attributeAsString(component, "styleClass"));
  }

  @Override
  protected JSObject getScriptObject(FacesContext context, UIComponent component) {
    return new JSObject(
        "RichFaces.ui.Tooltip",
        component.getClientId(context),
        getScriptObjectOptions(context, component));
  }

  @Override
  protected Map<String, Object> getScriptObjectOptions(
      FacesContext context, UIComponent component) {
    AbstractTooltip tooltip = (AbstractTooltip) component;

    Map<String, Object> options = new HashMap<String, Object>();

    RenderKitUtils.addToScriptHash(
        options, "ajax", getAjaxOptions(context, tooltip), TooltipMode.DEFAULT);

    Positioning jointPoint = tooltip.getJointPoint();
    if (jointPoint == null) {
      jointPoint = org.richfaces.component.Positioning.DEFAULT;
    }
    Positioning direction = tooltip.getDirection();
    if (direction == null) {
      direction = org.richfaces.component.Positioning.DEFAULT;
    }

    RenderKitUtils.addToScriptHash(
        options, "jointPoint", jointPoint.getValue(), Positioning.DEFAULT.getValue());
    RenderKitUtils.addToScriptHash(
        options, "direction", direction.getValue(), Positioning.DEFAULT.getValue());
    RenderKitUtils.addToScriptHash(options, "attached", tooltip.isAttached(), true);
    RenderKitUtils.addToScriptHash(options, "offset", getOffset(tooltip));
    RenderKitUtils.addToScriptHash(options, "mode", tooltip.getMode(), TooltipMode.DEFAULT);
    RenderKitUtils.addToScriptHash(options, "hideDelay", tooltip.getHideDelay(), 0);
    RenderKitUtils.addToScriptHash(options, "hideEvent", tooltip.getHideEvent(), "mouseleave");
    RenderKitUtils.addToScriptHash(options, "showDelay", tooltip.getShowDelay(), 0);
    RenderKitUtils.addToScriptHash(options, "showEvent", tooltip.getShowEvent(), "mouseenter");
    RenderKitUtils.addToScriptHash(options, "followMouse", tooltip.isFollowMouse(), true);
    RenderKitUtils.addToScriptHash(
        options,
        "target",
        RENDERER_UTILS.findComponentFor(component, tooltip.getTarget()).getClientId(context));

    addEventOption(context, tooltip, options, HIDE);
    addEventOption(context, tooltip, options, SHOW);
    addEventOption(context, tooltip, options, BEFORE_HIDE);
    addEventOption(context, tooltip, options, BEFORE_SHOW);

    return options;
  }

  public Integer[] getOffset(AbstractTooltip tooltip) {
    int horizontalOffset = tooltip.getHorizontalOffset();
    int verticalOffset = tooltip.getVerticalOffset();
    if (horizontalOffset == Integer.MIN_VALUE) {
      horizontalOffset = 10;
    }
    if (verticalOffset == Integer.MIN_VALUE) {
      verticalOffset = 10;
    }
    return new Integer[] {horizontalOffset, verticalOffset};
  }

  private void encodeContentEnd(
      ResponseWriter writer, FacesContext context, AbstractTooltip tooltip) throws IOException {
    writer.endElement(getMarkupElement(tooltip));
  }

  @Override
  protected void doEncodeEnd(ResponseWriter writer, FacesContext context, UIComponent component)
      throws IOException {
    encodeContentEnd(writer, context, (AbstractTooltip) component);

    writer.endElement(getMarkupElement((AbstractTooltip) component));

    writer.endElement(getMarkupElement((AbstractTooltip) component));

    writeJavaScript(writer, context, component);

    writer.endElement(getMarkupElement((AbstractTooltip) component));
  }

  public void encodeMetaComponent(
      FacesContext context, UIComponent component, String metaComponentId) throws IOException {
    if (AbstractTooltip.CONTENT_META_COMPONENT_ID.equals(metaComponentId)) {
      AbstractTooltip tooltip = (AbstractTooltip) component;
      PartialResponseWriter writer = context.getPartialViewContext().getPartialResponseWriter();
      writer.startUpdate(
          tooltip.getClientId(context) + ":" + AbstractTooltip.CONTENT_META_COMPONENT_ID);

      encodeContentBegin(writer, context, tooltip);
      for (UIComponent child : tooltip.getChildren()) {
        child.encodeAll(context);
      }
      encodeContentEnd(writer, context, tooltip);

      writer.endUpdate();
    }
  }

  public void decodeMetaComponent(
      FacesContext context, UIComponent component, String metaComponentId) {
    throw new UnsupportedOperationException();
  }

  @Override
  protected Class<? extends UIComponent> getComponentClass() {
    return AbstractTooltip.class;
  }

  @Override
  protected void doEncodeChildren(
      ResponseWriter writer, FacesContext context, UIComponent component) throws IOException {

    AbstractTooltip tooltip = (AbstractTooltip) component;

    if (tooltip.getMode() == TooltipMode.client) {
      if (tooltip.getChildCount() > 0) {
        for (UIComponent kid : tooltip.getChildren()) {
          kid.encodeAll(context);
        }
      }
    }
  }

  @Override
  public boolean getRendersChildren() {
    return true;
  }
}
/**
 * The &lt;rich:togglePanel&gt; component is used as a base for the other switchable components, the
 * &lt;rich:accordion&gt; component and the &lt;rich:tabPanel&gt; component. It provides an abstract
 * switchable component without any associated markup. As such, the &lt;rich:togglePanel&gt;
 * component could be customized to provide a switchable component when neither an accordion
 * component or a tab panel component is appropriate.
 *
 * @author akolonitsky
 * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
 */
@JsfComponent(
    tag =
        @Tag(
            type = TagType.Facelets,
            handler = "org.richfaces.view.facelets.html.TogglePanelTagHandler"),
    renderer = @JsfRenderer(type = "org.richfaces.TogglePanelRenderer"),
    attributes = {"core-props.xml", "events-mouse-props.xml", "i18n-props.xml"})
public abstract class AbstractTogglePanel extends UIOutput
    implements AbstractDivPanel, ItemChangeSource, MetaComponentResolver, MetaComponentEncoder {
  public static final String ACTIVE_ITEM_META_COMPONENT = "activeItem";
  public static final String COMPONENT_TYPE = "org.richfaces.TogglePanel";
  public static final String COMPONENT_FAMILY = "org.richfaces.TogglePanel";
  public static final String META_NAME_FIRST = "@first";
  public static final String META_NAME_PREV = "@prev";
  public static final String META_NAME_NEXT = "@next";
  public static final String META_NAME_LAST = "@last";
  // TODO What is MessageId ?
  public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";

  private static final Logger LOG = RichfacesLogger.RENDERKIT.getLogger();
  private static final RendererUtils UTILS = RendererUtils.getInstance();

  private String submittedActiveItem = null;

  private enum PropertyKeys {
    localValueSet,
    required,
    valid,
    immediate,
    switchType
  }

  protected AbstractTogglePanel() {
    setRendererType("org.richfaces.TogglePanelRenderer");
  }

  public static boolean isPanelItemDynamic(UIComponent component) {
    UIComponent parent = component.getParent();
    while (parent != null) {
      if (parent instanceof AbstractTogglePanel) {
        return false;
      }
      if (parent instanceof UIRepeat) {
        return true;
      }
      parent = parent.getParent();
    }
    return false;
  }

  // -------------------------------------------------- Editable Value Holder

  public Object getSubmittedValue() {
    return this.submittedActiveItem;
  }

  public void resetValue() {
    this.setValue(null);
    this.setSubmittedValue(null);
    this.setLocalValueSet(false);
    this.setValid(true);
  }

  public void setSubmittedValue(Object submittedValue) {
    this.submittedActiveItem = String.valueOf(submittedValue);
  }

  public boolean isLocalValueSet() {
    return (Boolean) getStateHelper().eval(PropertyKeys.localValueSet, false);
  }

  public void setLocalValueSet(boolean localValueSet) {
    getStateHelper().put(PropertyKeys.localValueSet, localValueSet);
  }

  public boolean isValid() {
    return (Boolean) getStateHelper().eval(PropertyKeys.valid, true);
  }

  public void setValid(boolean valid) {
    getStateHelper().put(PropertyKeys.valid, valid);
  }

  public boolean isRequired() {
    return (Boolean) getStateHelper().eval(PropertyKeys.required, false);
  }

  /**
   * Set the "required field" state for this component.
   *
   * @param required The new "required field" state
   */
  public void setRequired(boolean required) {
    getStateHelper().put(PropertyKeys.required, required);
  }

  /**
   * Flag indicating that this component's value must be converted and validated immediately (that
   * is, during Apply Request Values phase), rather than waiting until Process Validations phase.
   */
  @Attribute
  public boolean isImmediate() {
    return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false);
  }

  public void setImmediate(boolean immediate) {
    getStateHelper().put(PropertyKeys.immediate, immediate);
  }

  // ----------------------------------------------------- UIComponent Methods

  @Override
  public void encodeBegin(FacesContext facesContext) throws IOException {
    updateActiveName(getActiveItem());
    super.encodeBegin(facesContext);
  }

  public String updateActiveName(final String activeItemName) {
    boolean valid = false;

    if (!Strings.isNullOrEmpty(activeItemName)) {
      valid = isValidName(activeItemName);
    }

    if (!valid) {
      String firstItemName = getFirstNonDisabledItemName();
      if (firstItemName != null) {
        setActiveItem(firstItemName);
        return firstItemName;
      }
    }
    return activeItemName;
  }

  private Boolean isValidName(final String name) {
    final AtomicReference<Boolean> result = new AtomicReference<Boolean>(Boolean.FALSE);

    visitTogglePanelItems(
        this,
        new TogglePanelVisitCallback() {
          @Override
          public VisitResult visit(FacesContext facesContext, TogglePanelVisitState visitState) {
            AbstractTogglePanelItemInterface panelItem = visitState.getItem();
            if (name.equals(panelItem.getName())) {
              if (panelItem instanceof AbstractTogglePanelTitledItem) {
                AbstractTogglePanelTitledItem titledItem =
                    (AbstractTogglePanelTitledItem) panelItem;
                result.set(!titledItem.isDisabled());
              } else {
                result.set(Boolean.TRUE);
              }
              return VisitResult.COMPLETE;
            }
            return VisitResult.ACCEPT;
          }
        });

    return result.get();
  }

  /** Returns name of first non-disabled item in the list of panel's items. */
  private String getFirstNonDisabledItemName() {
    final AtomicReference<String> result = new AtomicReference<String>(null);

    visitTogglePanelItems(
        this,
        new TogglePanelVisitCallback() {
          @Override
          public VisitResult visit(FacesContext facesContext, TogglePanelVisitState visitState) {
            AbstractTogglePanelItemInterface panelItem = visitState.getItem();
            if (panelItem instanceof AbstractTogglePanelTitledItem) {
              AbstractTogglePanelTitledItem titledItem = (AbstractTogglePanelTitledItem) panelItem;
              if (!titledItem.isDisabled()) {
                result.set(titledItem.getName());
                return VisitResult.COMPLETE;
              } else {
                return VisitResult.ACCEPT;
              }
            } else {
              result.set(panelItem.getName());
              return VisitResult.COMPLETE;
            }
          }
        });

    return result.get();
  }

  /**
   * Specialized decode behavior on top of that provided by the superclass. In addition to the
   * standard <code>processDecodes</code> behavior inherited from {@link
   * javax.faces.component.UIComponentBase}, calls <code>processValue()</code> if the the <code>
   * immediate</code> property is true; if the component is invalid afterwards or a <code>
   * RuntimeException</code> is thrown, calls {@link FacesContext#renderResponse}.
   *
   * @throws NullPointerException {@inheritDoc}
   */
  @Override
  public void processDecodes(FacesContext facesContext) {
    if (facesContext == null) {
      throw new NullPointerException();
    }

    // Skip processing if our rendered flag is false
    if (!isRendered()) {
      return;
    }

    pushComponentToEL(facesContext, null);

    final String activeItem = getActiveItemValue();
    EnumSet<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
    VisitContext visitContext = new FullVisitContext(facesContext, hints);
    this.visitTree(
        visitContext,
        new VisitCallback() {
          @Override
          public VisitResult visit(VisitContext context, UIComponent target) {
            if (AbstractTogglePanel.this == target || target instanceof UIRepeat) {
              return VisitResult.ACCEPT; // Proceed with visit to target's children
            }
            if (isActiveItem(target, activeItem) || getSwitchType() == SwitchType.client) {
              target.processDecodes(context.getFacesContext());
            }
            return VisitResult
                .REJECT; // No need to visit target's children, as they were recursively visited
                         // above
          }
        });

    // Process this component itself
    try {
      decode(facesContext);
    } catch (RuntimeException e) {
      facesContext.renderResponse();
      throw e;
    } finally {
      popComponentFromEL(facesContext);
    }

    ItemChangeEvent event = createItemChangeEvent(facesContext);
    if (event != null) {
      event.queue();
    }
  }

  /**
   * In addition to the standard <code>processValidators</code> behavior inherited from {@link
   * javax.faces.component.UIComponentBase}, calls <code>processValue()</code> if the <code>
   * immediate</code> property is false (which is the default); if the component is invalid
   * afterwards, calls {@link FacesContext#renderResponse}. If a <code>RuntimeException</code> is
   * thrown during validation processing, calls {@link FacesContext#renderResponse} and re-throw the
   * exception.
   *
   * @throws NullPointerException {@inheritDoc}
   */
  @Override
  public void processValidators(FacesContext facesContext) {
    if (facesContext == null) {
      throw new NullPointerException();
    }

    // Skip processing if our rendered flag is false
    if (!isRendered()) {
      return;
    }

    pushComponentToEL(facesContext, null);
    Application app = facesContext.getApplication();
    app.publishEvent(facesContext, PreValidateEvent.class, this);

    final String activeItem = getActiveItemValue();
    EnumSet<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
    VisitContext visitContext = new FullVisitContext(facesContext, hints);
    this.visitTree(
        visitContext,
        new VisitCallback() {
          @Override
          public VisitResult visit(VisitContext context, UIComponent target) {
            if (AbstractTogglePanel.this == target || target instanceof UIRepeat) {
              return VisitResult.ACCEPT; // Proceed with visit to target's children
            }
            if (isActiveItem(target, activeItem) || getSwitchType() == SwitchType.client) {
              target.processValidators(context.getFacesContext());
            }
            return VisitResult
                .REJECT; // No need to visit target's children, as they were recursively visited
                         // above
          }
        });

    app.publishEvent(facesContext, PostValidateEvent.class, this);
    popComponentFromEL(facesContext);
  }

  /**
   * In addition to the standard <code>processUpdates</code> behavior inherited from {@link
   * javax.faces.component.UIComponentBase}, calls <code>updateModel()</code>. If the component is
   * invalid afterwards, calls {@link FacesContext#renderResponse}. If a <code>RuntimeException
   * </code> is thrown during update processing, calls {@link FacesContext#renderResponse} and
   * re-throw the exception.
   *
   * @throws NullPointerException {@inheritDoc}
   */
  @Override
  public void processUpdates(FacesContext facesContext) {
    if (facesContext == null) {
      throw new NullPointerException();
    }

    // Skip processing if our rendered flag is false
    if (!isRendered()) {
      return;
    }

    pushComponentToEL(facesContext, null);

    final String activeItem = getActiveItemValue();
    EnumSet<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
    VisitContext visitContext = new FullVisitContext(facesContext, hints);
    this.visitTree(
        visitContext,
        new VisitCallback() {
          @Override
          public VisitResult visit(VisitContext context, UIComponent target) {
            if (AbstractTogglePanel.this == target || target instanceof UIRepeat) {
              return VisitResult.ACCEPT; // Proceed with visit to target's children
            }
            if (isActiveItem(target, activeItem) || getSwitchType() == SwitchType.client) {
              target.processUpdates(context.getFacesContext());
            }
            return VisitResult
                .REJECT; // No need to visit target's children, as they were recursively visited
                         // above
          }
        });

    popComponentFromEL(facesContext);

    if (!isValid()) {
      facesContext.renderResponse();
    }
  }

  /** @throws NullPointerException {@inheritDoc} */
  @Override
  public void decode(FacesContext facesContext) {

    if (facesContext == null) {
      throw new NullPointerException();
    }

    // Force validity back to "true"
    setValid(true);
    super.decode(facesContext);
  }

  public void updateModel(FacesContext facesContext) {
    if (facesContext == null) {
      throw new NullPointerException();
    }

    if (!isValid() || !isLocalValueSet()) {
      return;
    }

    ValueExpression ve = getValueExpression("value");
    if (ve == null) {
      return;
    }

    Throwable caught = null;
    FacesMessage message = null;
    try {
      ve.setValue(facesContext.getELContext(), getLocalValue());
      setValue(null);
      setLocalValueSet(false);
    } catch (ELException e) {
      caught = e;
      String messageStr = e.getMessage();
      Throwable result = e.getCause();
      while (null != result && result.getClass().isAssignableFrom(ELException.class)) {
        messageStr = result.getMessage();
        result = result.getCause();
      }

      if (messageStr == null) {
        message =
            ServiceTracker.getService(MessageFactory.class)
                .createMessage(
                    facesContext,
                    FacesMessage.SEVERITY_ERROR,
                    FacesMessages.UIINPUT_UPDATE,
                    MessageUtil.getLabel(facesContext, this));
      } else {
        message = new FacesMessage(FacesMessage.SEVERITY_ERROR, messageStr, messageStr);
      }
      setValid(false);
    } catch (Exception e) {
      caught = e;
      // message = MessageFactory.getMessage(facesContext, UPDATE_MESSAGE_ID,
      // MessageFactory.getHeader(facesContext, this));
      setValid(false);
    }

    if (caught != null) {
      assert message != null;

      @SuppressWarnings({"ThrowableInstanceNeverThrown"})
      UpdateModelException toQueue = new UpdateModelException(message, caught);
      ExceptionQueuedEventContext eventContext =
          new ExceptionQueuedEventContext(facesContext, toQueue, this, PhaseId.UPDATE_MODEL_VALUES);
      facesContext
          .getApplication()
          .publishEvent(facesContext, ExceptionQueuedEvent.class, eventContext);
    }
  }

  private ItemChangeEvent createItemChangeEvent(FacesContext facesContext) {
    if (facesContext == null) {
      throw new NullPointerException();
    }

    // Submitted value == null means "the component was not submitted at all".
    String activeItem = getSubmittedActiveItem();
    if (activeItem == null) {
      return null;
    }

    String previous = (String) getValue();
    if (previous == null || !previous.equalsIgnoreCase(activeItem)) {
      UIComponent prevComp = null;
      UIComponent actvComp = null;

      if (previous != null) {
        try {
          prevComp = (UIComponent) getItem(previous);
        } catch (TogglePanelVisitException e) {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Cannot include dynamic TogglePanelComponents in itemChangeEvents");
          }
          prevComp = null;
        }
      }
      if (activeItem != null) {
        try {
          if (LOG.isDebugEnabled()) {
            LOG.debug("Cannot include dynamic TogglePanelComponents in itemChangeEvents");
          }
          actvComp = (UIComponent) getItem(activeItem);
        } catch (TogglePanelVisitException e) {
          actvComp = null;
        }
      }

      return new ItemChangeEvent(this, previous, prevComp, activeItem, actvComp);
    }
    return null;
  }

  @Override
  public void queueEvent(FacesEvent event) {
    if ((event instanceof ItemChangeEvent) && (event.getComponent() == this)) {
      setEventPhase((ItemChangeEvent) event);
    }
    super.queueEvent(event);
  }

  protected void setEventPhase(ItemChangeEvent event) {
    if (isImmediate()
        || (event.getNewItem() != null
            && RendererUtils.getInstance().isBooleanAttribute(event.getNewItem(), "immediate"))) {
      event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
    } else {
      event.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
    }
  }

  protected void setEventPhase(FacesEvent event) {
    if (isImmediate()) {
      event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
    } else {
      event.setPhaseId(PhaseId.INVOKE_APPLICATION);
    }
  }

  @Override
  public void broadcast(FacesEvent event) throws AbortProcessingException {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    if (event instanceof ItemChangeEvent) {
      setValue(((ItemChangeEvent) event).getNewItemName());
      setSubmittedActiveItem(null);
      if (event.getPhaseId() == PhaseId.UPDATE_MODEL_VALUES) {
        try {
          updateModel(facesContext);
        } catch (RuntimeException e) {
          facesContext.renderResponse();
          throw e;
        }
      } else {
        facesContext.renderResponse();
      }
    }
    super.broadcast(event);
  }

  // -------------------------------------------------- Panel Items Managing

  @Override
  public String getFamily() {
    return COMPONENT_FAMILY;
  }

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

  private String getActiveItemValue() {
    String value = getActiveItem();
    if (value == null) {
      value = getSubmittedActiveItem();
    }
    return value;
  }

  protected boolean isActiveItem(UIComponent kid) {
    return isActiveItem(kid, getActiveItemValue());
  }

  protected boolean isActiveItem(UIComponent kid, String value) {
    if (kid == null || !(kid instanceof AbstractTogglePanelItemInterface)) {
      return false;
    }

    return isActiveItem((AbstractTogglePanelItemInterface) kid, value);
  }

  protected boolean isActiveItem(AbstractTogglePanelItemInterface item, String value) {
    if (item == null || value == null) {
      return false;
    }

    return item.getName().equals(value);
  }

  public TogglePanelVisitState visitTogglePanelItems(
      final AbstractTogglePanel panel, final TogglePanelVisitCallback callback) {
    FacesContext facesContext = getFacesContext();
    EnumSet<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
    final TogglePanelVisitState visitState = new TogglePanelVisitState();

    VisitContext visitContext = new FullVisitContext(facesContext, hints);
    panel.visitTree(
        visitContext,
        new VisitCallback() {
          @Override
          public VisitResult visit(VisitContext context, UIComponent target) {
            if (target instanceof AbstractTogglePanelItemInterface) {
              AbstractTogglePanelItemInterface item = (AbstractTogglePanelItemInterface) target;
              visitState.setState(item.getName(), item);
              if (callback.visit(context.getFacesContext(), visitState) == VisitResult.COMPLETE) {
                visitState.setDynamic(item.isDynamicPanelItem());
                return VisitResult.COMPLETE;
              }
              visitState.increment();
              visitState.setState(null, null);
              return VisitResult.ACCEPT;
            } else if (AbstractTogglePanel.this == target || target instanceof UIRepeat) {
              return VisitResult.ACCEPT;
            } else {
              return VisitResult
                  .REJECT; // target is not a source of toggle panel items for this toggle panel
            }
          }
        });
    return visitState;
  }

  private TogglePanelVisitState getvisitStateByIndex(final int index) {
    TogglePanelVisitState visitState =
        visitTogglePanelItems(
            this,
            new TogglePanelVisitCallback() {
              @Override
              public VisitResult visit(
                  FacesContext facesContext, TogglePanelVisitState visitState) {
                if (index == visitState.getCount()) {
                  return VisitResult.COMPLETE;
                } else {
                  return VisitResult.ACCEPT;
                }
              }
            });
    return visitState;
  }

  public TogglePanelVisitState getVisitStateByName(final String name) {
    TogglePanelVisitState visitState =
        visitTogglePanelItems(
            this,
            new TogglePanelVisitCallback() {
              @Override
              public VisitResult visit(
                  FacesContext facesContext, TogglePanelVisitState visitState) {
                if (name.equals(visitState.getName())) {
                  return VisitResult.COMPLETE;
                } else {
                  return VisitResult.ACCEPT;
                }
              }
            });
    return visitState;
  }

  public AbstractTogglePanelItemInterface getItemByIndex(final int index) {
    TogglePanelVisitState visitState = getvisitStateByIndex(index);
    if (visitState.isDynamic()) {
      throw new TogglePanelVisitException(
          "Cannot access a dynamically generated AbstractToggleItemInterface directly. Use the visitor pattern instead.");
    }
    return visitState.getItem();
  }

  public String getNameByIndex(final int index) {
    if (!this.isRendered()) {
      return null;
    }
    return getvisitStateByIndex(index).getName();
  }

  public int getIndexByName(final String name) {
    if (!this.isRendered()) {
      return -1;
    }

    TogglePanelVisitState visitState = getVisitStateByName(name);
    if (visitState.getName() != null) {
      return visitState.getCount();
    } else {
      return -1;
    }
  }

  public String getClientIdByName(final String name) {
    if (!this.isRendered()) {
      return null;
    }

    TogglePanelVisitState visitState = getVisitStateByName(name);
    if (visitState.getName() != null) {
      return visitState.getClientId();
    } else {
      return null;
    }
  }

  public int getItemCount() {
    if (!this.isRendered()) {
      return 0;
    }
    TogglePanelVisitState visitState =
        visitTogglePanelItems(
            this,
            new TogglePanelVisitCallback() {
              @Override
              public VisitResult visit(
                  FacesContext facesContext, TogglePanelVisitState visitState) {
                return VisitResult.ACCEPT;
              }
            });
    return visitState.getCount();
  }

  public AbstractTogglePanelItemInterface getItem(String name) {
    if (META_NAME_FIRST.equals(name)) {
      return getFirstItem();
    } else if (META_NAME_PREV.equals(name)) {
      return getPrevItem();
    } else if (META_NAME_NEXT.equals(name)) {
      return getNextItem();
    } else if (META_NAME_LAST.equals(name)) {
      return getLastItem();
    } else {
      return getItemByIndex(getChildIndex(name));
    }
  }

  public AbstractTogglePanelItemInterface getFirstItem() {
    return getItemByIndex(0);
  }

  public AbstractTogglePanelItemInterface getPrevItem() {
    return getPrevItem(getActiveItem());
  }

  public AbstractTogglePanelItemInterface getPrevItem(String name) {
    return getItemByIndex(getIndexByName(name) - 1);
  }

  public AbstractTogglePanelItemInterface getNextItem() {
    return getNextItem(getActiveItem());
  }

  public AbstractTogglePanelItemInterface getNextItem(String name) {
    return getItemByIndex(getIndexByName(name) + 1);
  }

  public AbstractTogglePanelItemInterface getLastItem() {
    return getItemByIndex(getItemCount());
  }

  @Deprecated
  public int getChildIndex(String name) {
    if (name == null) {
      throw new IllegalArgumentException("Name is required parameter.");
    }

    return getIndexByName(name);
  }

  // ------------------------------------------------

  public String getSubmittedActiveItem() {
    return submittedActiveItem;
  }

  public void setSubmittedActiveItem(String submittedActiveItem) {
    this.submittedActiveItem = submittedActiveItem;
  }

  // ------------------------------------------------ Properties

  @Override
  @Attribute(hidden = true)
  public void setValue(Object value) {
    super.setValue(value);

    setLocalValueSet(true);
  }

  /**
   * Holds the active panel name. This name is a reference to the name identifier of the active
   * child &lt;rich:togglePanelItem&gt; component.
   */
  @Attribute
  public String getActiveItem() {
    return (String) getValue();
  }

  public void setActiveItem(String value) {
    setValue(value);
  }

  @Override
  public void setValueExpression(String name, ValueExpression binding) {
    if ("activeItem".equals(name)) {
      super.setValueExpression("value", binding);
    } else {
      super.setValueExpression(name, binding);
    }
  }

  /**
   * The switch mode when a panel is activated. One of: "client", "server", "ajax". Default: "ajax"
   */
  @Attribute(generate = false)
  public SwitchType getSwitchType() {
    SwitchType switchType = (SwitchType) getStateHelper().eval(PropertyKeys.switchType);
    if (switchType == null) {
      switchType = SwitchType.DEFAULT;
    }
    return switchType;
  }

  public void setSwitchType(SwitchType switchType) {
    getStateHelper().put(PropertyKeys.switchType, switchType);
  }

  @Attribute(hidden = true)
  public abstract boolean isLimitRender();

  /**
   * Applicable when cycling through the tabs. If "true", then when the last tab is active, cycling
   * to next will activate the first tab, if "false", cycling to next will have not effect. The
   * inverse applies for the first tab, and cycling to previous. Whether to Default: false
   */
  @Attribute
  public abstract boolean isCycledSwitching();

  @Attribute(hidden = true)
  public abstract Object getData();

  @Attribute(hidden = true)
  public abstract String getStatus();

  @Attribute(hidden = true)
  public abstract Object getExecute();

  @Attribute(hidden = true)
  public abstract Object getRender();

  /** Occurs on the server side when an item is changed through Ajax using the server mode */
  @Attribute
  public abstract MethodExpression getItemChangeListener();

  /** The client-side script method to be called after the item is changed. */
  @Attribute(events = @EventName("itemchange"))
  public abstract String getOnitemchange();

  /** The client-side script method to be called before the item is changed. */
  @Attribute(events = @EventName("beforeitemchange"))
  public abstract String getOnbeforeitemchange();

  // ------------------------------------------------ Event Processing Methods

  public void addItemChangeListener(ItemChangeListener listener) {
    addFacesListener(listener);
  }

  public ItemChangeListener[] getItemChangeListeners() {
    return (ItemChangeListener[]) getFacesListeners(ItemChangeListener.class);
  }

  public void removeItemChangeListener(ItemChangeListener listener) {
    removeFacesListener(listener);
  }

  public String resolveClientId(
      FacesContext facesContext, UIComponent contextComponent, String metaComponentId) {
    if (ACTIVE_ITEM_META_COMPONENT.equals(metaComponentId)) {
      return getClientId(facesContext)
          + MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR
          + metaComponentId;
    }
    return null;
  }

  public String substituteUnresolvedClientId(
      FacesContext facesContext, UIComponent contextComponent, String metaComponentId) {
    return null;
  }

  public void encodeMetaComponent(FacesContext facesContext, String metaComponentId)
      throws IOException {
    ((MetaComponentRenderer) getRenderer(facesContext))
        .encodeMetaComponent(facesContext, this, metaComponentId);
  }

  @Override
  public boolean visitTree(VisitContext context, VisitCallback callback) {
    if (!isVisitable(context)) {
      return false;
    }

    FacesContext facesContext = context.getFacesContext();
    pushComponentToEL(facesContext, null);

    try {
      VisitResult result = context.invokeVisitCallback(this, callback);

      if (result == VisitResult.COMPLETE) {
        return true;
      }

      if (result == VisitResult.ACCEPT) {
        if (context instanceof ExtendedVisitContext) {
          ExtendedVisitContext extendedVisitContext = (ExtendedVisitContext) context;
          if (extendedVisitContext.getVisitMode() == ExtendedVisitContextMode.RENDER) {

            result =
                extendedVisitContext.invokeMetaComponentVisitCallback(
                    this, callback, ACTIVE_ITEM_META_COMPONENT);
            if (result == VisitResult.COMPLETE) {
              return true;
            }
          }
        }
      }

      if (result == VisitResult.ACCEPT) {
        Iterator<UIComponent> kids = this.getFacetsAndChildren();

        while (kids.hasNext()) {
          boolean done = kids.next().visitTree(context, callback);

          if (done) {
            return true;
          }
        }
      }
    } finally {
      popComponentFromEL(facesContext);
    }

    return false;
  }
}