org.metawidget.faces.renderkit.html.HtmlDivLayoutRenderer Maven / Gradle / Ivy
// Metawidget
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package org.metawidget.faces.renderkit.html;
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.component.html.HtmlInputHidden;
import javax.faces.component.html.HtmlMessage;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.metawidget.faces.FacesUtils;
import org.metawidget.faces.component.UIMetawidget;
import org.metawidget.faces.component.UIStub;
import org.metawidget.util.simple.StringUtils;
/**
* Layout to arrange widgets in HTML DIV
tags, with one DIV
per label and
* per widget, and a wrapper DIV
around both.
*
* This implementation recognizes the following <f:param>
parameters:
*
*
* divStyleClasses
- comma separated list of style classes to apply to the DIVs, in
* order of outer, label, required, component, errors
* outerStyle
- CSS styles to apply to the outer DIV
* labelStyle
- CSS styles to apply to the label DIV
* componentStyle
- this is the style applied to the DIV around the
* component, not to the component itself. The component itself can be styled using the
* style
attribute on the Metawidget tag
* requiredStyle
- CSS styles to apply to the required DIV
*
*
*
* @author Richard Kennard
*/
public class HtmlDivLayoutRenderer
extends HtmlLayoutRenderer {
//
// Public methods
//
@Override
public void encodeBegin( FacesContext context, UIComponent metawidgetComponent )
throws IOException {
UIMetawidget metawidget = (UIMetawidget) metawidgetComponent;
metawidget.putClientProperty( HtmlDivLayoutRenderer.class, null );
super.encodeBegin( context, metawidget );
// Determine outer styles
State state = getState( metawidget );
state.outerStyle = metawidget.getParameter( "outerStyle" );
// Determine label, component, required styles
state.labelStyle = metawidget.getParameter( "labelStyle" );
state.componentStyle = metawidget.getParameter( "componentStyle" );
state.requiredStyle = metawidget.getParameter( "requiredStyle" );
// Determine style classes
String styleClassesParameter = metawidget.getParameter( "divStyleClasses" );
if ( styleClassesParameter != null ) {
state.divStyleClasses = styleClassesParameter.split( StringUtils.SEPARATOR_COMMA );
}
// Start component
ResponseWriter writer = context.getResponseWriter();
writer.startElement( "div", metawidget );
writer.writeAttribute( "id", metawidget.getClientId( context ), "id" );
}
@Override
public void encodeChildren( FacesContext context, UIComponent component )
throws IOException {
List children = component.getChildren();
// For each child component...
for ( UIComponent childComponent : children ) {
// ...that is visible...
if ( childComponent instanceof UIStub && childComponent.getChildCount() == 0 ) {
continue;
}
if ( childComponent instanceof UIParameter ) {
continue;
}
if ( !childComponent.isRendered() ) {
continue;
}
// ...(and is not a hidden field)...
if ( childComponent instanceof HtmlInputHidden ) {
FacesUtils.render( context, childComponent );
continue;
}
// ...render the label...
layoutBeforeChild( context, component, childComponent );
// ...and render the component
layoutChild( context, component, childComponent );
layoutAfterChild( context, component, childComponent );
}
}
@Override
public void encodeEnd( FacesContext context, UIComponent metawidget )
throws IOException {
super.encodeEnd( context, metawidget );
ResponseWriter writer = context.getResponseWriter();
// Footer facet
UIComponent componentFooter = metawidget.getFacet( "footer" );
if ( componentFooter != null ) {
writer.startElement( "div", metawidget );
writeStyleAndClass( (UIMetawidget) metawidget, writer, "footer" );
// Render facet
FacesUtils.render( context, componentFooter );
writer.endElement( "div" );
}
// End component
writer.endElement( "div" );
}
@Override
protected HtmlMessage createInlineMessage( FacesContext context, UIComponent metawidget, String messageFor ) {
HtmlMessage message = super.createInlineMessage( context, metawidget, messageFor );
// Apply alternate style class (if any)
if ( message.getStyleClass() == null ) {
State state = getState( metawidget );
if ( state.divStyleClasses != null && state.divStyleClasses.length > 4 ) {
message.setStyleClass( state.divStyleClasses[4] );
}
}
return message;
}
//
// Protected methods
//
protected void layoutBeforeChild( FacesContext context, UIComponent metawidget, UIComponent componentChild )
throws IOException {
ResponseWriter writer = context.getResponseWriter();
State state = getState( metawidget );
// Outer
writer.startElement( "div", metawidget );
if ( state.outerStyle != null ) {
writer.writeAttribute( "style", state.outerStyle, null );
}
writeStyleClass( metawidget, writer, 0 );
// Label
layoutLabel( context, metawidget, componentChild );
// Component
//
// Note: it is debatable whether we should use DIVs inside DIVs or SPANs inside DIVs here.
// We choose the former, and the JBoss Seam demos do it both ways (Hotel Booking the latter,
// Groovy Hotel Booking the former)
writer.startElement( "div", metawidget );
if ( state.componentStyle != null ) {
writer.writeAttribute( "style", state.componentStyle, null );
}
writeStyleClass( metawidget, writer, 3 );
}
/**
* @return whether a label was written
*/
@Override
protected boolean layoutLabel( FacesContext context, UIComponent metawidget, UIComponent componentNeedingLabel )
throws IOException {
if ( getLabelText( componentNeedingLabel ) == null ) {
return false;
}
ResponseWriter writer = context.getResponseWriter();
writer.startElement( "div", metawidget );
State state = getState( metawidget );
if ( state.labelStyle != null ) {
writer.writeAttribute( "style", state.labelStyle, null );
}
writeStyleClass( metawidget, writer, 1 );
super.layoutLabel( context, metawidget, componentNeedingLabel );
layoutRequired( context, metawidget, componentNeedingLabel );
writer.endElement( "div" );
return true;
}
protected void layoutRequired( FacesContext context, UIComponent metawidget, UIComponent child )
throws IOException {
@SuppressWarnings( "unchecked" )
Map metadataAttributes = (Map) child.getAttributes().get( UIMetawidget.COMPONENT_ATTRIBUTE_METADATA );
if ( metadataAttributes == null ) {
return;
}
ResponseWriter writer = context.getResponseWriter();
if ( TRUE.equals( metadataAttributes.get( REQUIRED ) ) && !TRUE.equals( metadataAttributes.get( READ_ONLY ) ) && !( (UIMetawidget) metawidget ).isReadOnly() ) {
writer.startElement( "span", metawidget );
State state = getState( metawidget );
String requiredStyle = metadataAttributes.get( state.requiredStyle );
if ( requiredStyle != null ) {
writer.writeAttribute( "style", requiredStyle, null );
}
writeStyleClass( metawidget, writer, 2 );
writer.write( "*" );
writer.endElement( "span" );
}
}
/**
* @param metawidget the Metawidget doing the layout
* @param child the component being laid out
*/
protected void layoutAfterChild( FacesContext context, UIComponent metawidget, UIComponent child )
throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.endElement( "div" );
writer.endElement( "div" );
}
protected void writeStyleClass( UIComponent metawidget, ResponseWriter writer, int styleClass )
throws IOException {
State state = getState( metawidget );
if ( state.divStyleClasses == null || state.divStyleClasses.length <= styleClass ) {
return;
}
String columnClass = state.divStyleClasses[styleClass];
if ( columnClass.length() == 0 ) {
return;
}
writer.writeAttribute( "class", columnClass.trim(), null );
}
//
// Private methods
//
private State getState( UIComponent metawidget ) {
State state = (State) ( (UIMetawidget) metawidget ).getClientProperty( HtmlDivLayoutRenderer.class );
if ( state == null ) {
state = new State();
( (UIMetawidget) metawidget ).putClientProperty( HtmlDivLayoutRenderer.class, state );
}
return state;
}
//
// Inner class
//
/**
* Simple, lightweight structure for saving state.
*/
/* package private */static class State {
/* package private */String outerStyle;
/* package private */String labelStyle;
/* package private */String requiredStyle;
/* package private */String componentStyle;
/* package private */String[] divStyleClasses;
}
}