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