Hi everybody.

I'm working on a trinidad component for do layout (as part of a Google
Summer of Code) more easy and with less code than panelFormLayout component.

Checking the code, I found a strong coupling between the classes
PanelFormLayoutRenderer and LabelAndMessageRenderer.

I found the following points:

1. LabelAndMessageRenderer has about 3 behaviors, depending what is his
parent components. In the code it checks in a method something like
that:

 private boolean _isFormRendererType(String rendererType)
 {
   return "org.apache.myfaces.trinidad.Form".equals(rendererType) ||
       "org.apache.myfaces.trinidad.FormLayout".equals(rendererType) ||
       "org.apache.myfaces.trinidad.rich.Form".equals(rendererType) ;
 }

Because my component is a new component, i have to add a line like this

   return "org.apache.myfaces.trinidad.Form".equals(rendererType) ||
       "org.apache.myfaces.trinidad.FormLayout".equals(rendererType) ||
       "org.apache.myfaces.trinidad.rich.Form".equals(rendererType) ||
       "org.apache.myfaces.trinidad.TableLayout".equals(rendererType);

But this is very hacky. I have to do this because i need that the method in
encodeAll of LabelAndMessageRenderer

boolean needsPanelFormLayout = _isParentPanelForm(component);

returns true, because my component layout a table like panelFormLayout.

2. In other part of the code, LabelAndMessageRenderer call this method (in
encodeAll)

     if (needsPanelFormLayout)
     {
       if(PanelFormLayoutRenderer.encodeBetweenLabelAndFieldCells(context,
arc, rw))
       {
         renderRootDomElementStyles(context, arc, component, bean);
       }
     }

What if my component has another behavior to this method?
PanelFormLayoutRenderer detects if this panelFormLayout is inside
another panelFormLayout and because if it is the case, it adds between Label
and Field Cells something like this

     rw.endElement("tr"); // label row
     rw.startElement("tr", null); // field row

So, the label is rendered on top of the field.

3. LabelAndMessageRenderer do this for render a Label an a field (please
look the parts in yellow)

 private void _renderLabelCell(
   FacesContext        context,
   RenderingContext arc,
   UIComponent         component,
   FacesBean           bean,
   boolean             labelExists) throws IOException
 {
   ResponseWriter rw = context.getResponseWriter();
   rw.startElement("td", null);

   // render labelStyleClass and defaultStyleClass.
   renderStyleClasses(context, arc, new String[]{
                      getLabelStyleClass(bean),
                      _getDefaultLabelStyleClass(arc,
                         SkinSelectors.AF_LABEL_TEXT_STYLE_CLASS)});

   String labelInlineStyle = getLabelInlineStyleKey(bean);
   rw.writeAttribute("style", labelInlineStyle, null);

   String valign = getDefaultLabelValign(bean);

   rw.writeAttribute("valign", valign, null);
   if (isDesktop(arc))
     rw.writeAttribute("nowrap", Boolean.TRUE, null);

   if (labelExists)
   {
     rw.writeAttribute("width",
                       arc.getProperties().get(_LABEL_CELL_WIDTH_KEY),
                       null);
   }

   delegateRenderer(context, arc, component, bean, _label);
   rw.endElement("td");
 }

 private void _renderFieldCell(
   FacesContext        context,
   RenderingContext arc,
   UIComponent         component,
   FacesBean           bean,
   boolean             labelExists,
   boolean             needsPanelFormLayout,
   boolean             isInline) throws IOException
 {
   ResponseWriter rw = context.getResponseWriter();
   rw.startElement("td", null);

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

   renderStyleClass(context, arc, SkinSelectors.AF_CONTENT_CELL_STYLE_CLASS);

   if (labelExists)
     rw.writeAttribute("width",
                       arc.getProperties().get(_FIELD_CELL_WIDTH_KEY),
                       null);

   renderFieldCellContents(context, arc, component, bean);

   // The panelForm places messages below the fields, not on a separate
   // row:
   if (needsPanelFormLayout)
   {
     // =-= mcc PPR PROBLEM!!!  We should always be rendering the "div",
     //     and always rendering an ID, if we ever want it to be PPR
     //     replaceable:
     if (isInline || hasMessage(context, arc, component, bean))
     {
       rw.startElement("div", null);
       renderStyleClass(context, arc,
                        SkinSelectors.AF_COMPONENT_MESSAGE_CELL_STYLE_CLASS
);
       _renderMessageCellContents(context, arc, component, bean);
       rw.endElement("div");
     }
   }

   // bug 2484841: PDA: TOO MUCH WHITESPACE BETWEEN
   //                   INPUT ELEMENTS IN LABELEDFIELD
   // This is a browser bug workaround, hopefully we can remove it
eventually
   if (isPDA(arc) && isIE(arc))
   {
     rw.startElement("div", null);
     renderSpacer(context, arc, "1", "0");
     rw.endElement("div");
   }

   rw.endElement("td");
 }

I need to add an attribute in the td tag like <td colspan=2 rowspan=3
height=XXX width=XXX .........>, but if i want this, i need to
modify LabelAndMessageRenderer to recognize if the parent component is my
component, check if it has an attribute like this

                   <mycomp:tableFormLayout labelWidth="100"  width="400"
height="300"
                       fieldWidth="100" rows="100" columns="1*;1*;1*" >
                       <tr:selectOneChoice label="Salutation">
                           <f:selectItem itemLabel="1 Option" itemValue="1"
/>
                           <f:attribute name="spanXItem" value="2"/>
                           <f:attribute name="spanYItem" value="3"/>
                       </tr:selectOneChoice>
                   </mycomp:tableFormLayout>

and finally add a colspan or rowspan (height and width are optional).

---------
Conclusion?: It's necesary to decouple PanelFormLayoutRenderer and other
FormRenderers with LabelAndMessageRenderer in order to avoid
those hacks. I propose to create an interface that implements some affected
methods or create new ones, and delegate this rendering to the parent
classes.

I want to you what should be better to do in that case. If its necesary to
refactor the classes, how it can be done.

Thanks for your attention

regards

Att: Leonardo Uribe
Ingeniero de Sistemas
Pontificia Universidad Javeriana
Ingeniero Electronico
Universidad Nacional de Colombia

Reply via email to