Uploaded image for project: 'MyFaces Core'
  1. MyFaces Core
  2. MYFACES-4430

Separate "begin tag" code from "end tag" code for HtmlCheckboxRendererBase

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Closed
    • Major
    • Resolution: Fixed
    • 2.3.9
    • 2.3.10, 2.3-next-M7, 3.0.2
    • General
    • None

    Description

      This ticket is very similar to MYFACES-4428, except it applies to HtmlSelectBooleanCheckbox. 

      We're trying to implement a few JSF components that extend from the generic JSF components/renderers. This particular component is extending javax.faces.component.html.HtmlSelectBooleanCheckbox.

      On the renderer side, we're grabbing a handle to the default renderer of the component:

      	private static final String PARENT_FAMILY = "javax.faces.Checkbox";
      	private static final String PARENT_RENDERER = "javax.faces.SelectBoolean";
      	static final Renderer getWrappedRenderer(FacesContext context) {
      		Renderer baseRenderer = context.getRenderKit().getRenderer(PARENT_FAMILY, PARENT_RENDERER);
      		return baseRenderer;
      	}

      Then we're going to attempt to write out some new CSS classes for the component and leave the rest of everything alone:

      	@Override
      	public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
      		Renderer baseRenderer = getWrappedRenderer(context);
      		baseRenderer.encodeBegin(context, component);
      		XXFaces.writeClasses(context, null, SF_FORM_CONTROL, BS_FORM_CONTROL);
      	}
      
      
      	@Override
      	public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
      		Renderer baseRenderer = getWrappedRenderer(context);
      		baseRenderer.encodeEnd(context, component);
      	} 

       

      The problem is HtmlCheckboxRendererBase does not have an encodeBegin() method, instead it does all of the "Start tag" encoding in the encodeEnd() method. This makes it impossible to create custom components that wrap the functionality of the default render kit without extending myfaces renderers and breaks any chance cross-platform components.

      I propose adding this method in HtmlCheckboxRendererBase:

          @Override    
       public void encodeBegin(FacesContext facesContext, UIComponent uiComponent)
              throws IOException
      {
          org.apache.myfaces.shared.renderkit.RendererUtils.checkParamValidity(facesContext, uiComponent, null);
          
          Map<String, List<ClientBehavior>> behaviors = null;
          if (uiComponent instanceof ClientBehaviorHolder)
          {
              behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
              if (!behaviors.isEmpty())
              {
                  ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
              }
          }
          
          if (uiComponent instanceof UISelectBoolean)
          {
              Boolean value = org.apache.myfaces.shared.renderkit.RendererUtils.getBooleanValue( uiComponent );
              boolean isChecked = value != null ? value.booleanValue() : false;
              renderCheckbox(facesContext, uiComponent, EXTERNAL_TRUE_VALUE, false,isChecked, true, null); 
                  //TODO: the selectBoolean is never disabled
          }
          else if (uiComponent instanceof UISelectMany)
          {
              // let the current impl do what it does in encodeEnd do nothing here just don't want exception
              // throw if it is this case
              log.finest("encodeBegin() doing nothing intentionally for UISelectMany");
          }
          else
          {
              throw new IllegalArgumentException("Unsupported component class "
                      + uiComponent.getClass().getName());
          }
      }

      and changing encodeEnd() to this following:

      Unable to find source-code formatter for language: keyword. Available languages are: actionscript, ada, applescript, bash, c, c#, c++, cpp, css, erlang, go, groovy, haskell, html, java, javascript, js, json, lua, none, nyan, objc, perl, php, python, r, rainbow, ruby, scala, sh, sql, swift, visualbasic, xml, yaml
          public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
      {
          if (uiComponent instanceof UISelectBoolean)
          {
              ResponseWriter writer = facesContext.getResponseWriter();
              writer.endElement(HTML.INPUT_ELEM);
          }
          else if (uiComponent instanceof UISelectMany)
          {
              renderCheckboxList(facesContext, (UISelectMany) uiComponent);
          }
          else
          {
              throw new IllegalArgumentException("Unsupported component class " + uiComponent.getClass().getName());
          }
      }

      and add this to the end of renderCheckbox()

      if (uiComponent instanceof UISelectMany)
      {
          writer.endElement(HTML.INPUT_ELEM);
      }

      These changes split the logic for encode end and begin for renderCheckbox() and leaves the rendering logic alone for renderCheckboxList(). Encode begin calls renderCheckbox and encodeEnd writes the end element. Encode begin does nothing for UISelectMany elements as it is implemented in encodeEnd.

      Im open to any feedback on this proposed change. 

      Attachments

        Activity

          People

            melloware Melloware
            glynn.leininger glynn leininiger
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: