View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
21  
22  import org.apache.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.ComponentTypes;
24  import org.apache.myfaces.tobago.component.Facets;
25  import org.apache.myfaces.tobago.component.RendererTypes;
26  import org.apache.myfaces.tobago.component.SupportsMarkup;
27  import org.apache.myfaces.tobago.component.UIColumnSelector;
28  import org.apache.myfaces.tobago.component.UICommand;
29  import org.apache.myfaces.tobago.component.UILink;
30  import org.apache.myfaces.tobago.component.UIMenu;
31  import org.apache.myfaces.tobago.component.UIMenuCommand;
32  import org.apache.myfaces.tobago.component.UIOut;
33  import org.apache.myfaces.tobago.component.UIReload;
34  import org.apache.myfaces.tobago.component.UISheet;
35  import org.apache.myfaces.tobago.component.UIToolBar;
36  import org.apache.myfaces.tobago.config.Configurable;
37  import org.apache.myfaces.tobago.context.ClientProperties;
38  import org.apache.myfaces.tobago.context.Markup;
39  import org.apache.myfaces.tobago.context.ResourceManager;
40  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
41  import org.apache.myfaces.tobago.event.PageAction;
42  import org.apache.myfaces.tobago.internal.component.AbstractUIColumn;
43  import org.apache.myfaces.tobago.internal.component.AbstractUIColumnNode;
44  import org.apache.myfaces.tobago.internal.component.AbstractUIData;
45  import org.apache.myfaces.tobago.internal.component.AbstractUIOut;
46  import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
47  import org.apache.myfaces.tobago.internal.context.ResourceManagerFactory;
48  import org.apache.myfaces.tobago.internal.layout.Cell;
49  import org.apache.myfaces.tobago.internal.layout.Grid;
50  import org.apache.myfaces.tobago.internal.layout.OriginCell;
51  import org.apache.myfaces.tobago.internal.util.StringUtils;
52  import org.apache.myfaces.tobago.layout.Display;
53  import org.apache.myfaces.tobago.layout.LayoutBase;
54  import org.apache.myfaces.tobago.layout.Measure;
55  import org.apache.myfaces.tobago.layout.TextAlign;
56  import org.apache.myfaces.tobago.model.ExpandedState;
57  import org.apache.myfaces.tobago.model.SheetState;
58  import org.apache.myfaces.tobago.model.TreePath;
59  import org.apache.myfaces.tobago.renderkit.LayoutComponentRendererBase;
60  import org.apache.myfaces.tobago.renderkit.css.Classes;
61  import org.apache.myfaces.tobago.renderkit.css.Style;
62  import org.apache.myfaces.tobago.renderkit.html.Command;
63  import org.apache.myfaces.tobago.renderkit.html.CommandMap;
64  import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
65  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
66  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
67  import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
68  import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
69  import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
70  import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
71  import org.apache.myfaces.tobago.util.ComponentUtils;
72  import org.apache.myfaces.tobago.util.CreateComponentUtils;
73  import org.apache.myfaces.tobago.util.FacetUtils;
74  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
75  import org.slf4j.Logger;
76  import org.slf4j.LoggerFactory;
77  
78  import javax.faces.application.Application;
79  import javax.faces.component.UIColumn;
80  import javax.faces.component.UIComponent;
81  import javax.faces.component.UISelectOne;
82  import javax.faces.context.FacesContext;
83  import java.io.IOException;
84  import java.text.MessageFormat;
85  import java.util.ArrayList;
86  import java.util.Collections;
87  import java.util.List;
88  import java.util.Locale;
89  import java.util.Map;
90  
91  public class SheetRenderer extends LayoutComponentRendererBase {
92  
93    private static final Logger LOG = LoggerFactory.getLogger(SheetRenderer.class);
94  
95    public static final String WIDTHS_POSTFIX = ComponentUtils.SUB_SEPARATOR + "widths";
96    public static final String SCROLL_POSTFIX = ComponentUtils.SUB_SEPARATOR + "scrollPosition";
97    public static final String SELECTED_POSTFIX = ComponentUtils.SUB_SEPARATOR + "selected";
98  
99    private static final Integer HEIGHT_0 = 0;
100 
101   @Override
102   public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
103     super.prepareRender(facesContext, component);
104     ensureHeader(facesContext, (UISheet) component);
105   }
106 
107   private void ensureHeader(FacesContext facesContext, UISheet sheet) {
108     UIComponent header = sheet.getHeader();
109     if (header == null) {
110       header = CreateComponentUtils.createComponent(facesContext, ComponentTypes.PANEL, null, "_header");
111 // XXX ??? what about input, etc.?
112 //      header.setTransient(true);
113       final List<AbstractUIColumn> columns = sheet.getRenderedColumns();
114       int i = 0;
115       for (AbstractUIColumn column : columns) {
116         final AbstractUIOut out = (AbstractUIOut) CreateComponentUtils.createComponent(
117             facesContext, ComponentTypes.OUT, RendererTypes.OUT, "_col" + i);
118 //        out.setValue(column.getLabel());
119         out.setValue(column.getAttributes().get(Attributes.LABEL));
120         header.getChildren().add(out);
121           i++;
122       }
123       sheet.setHeader(header);
124     }
125   }
126 
127   @Override
128   public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
129 
130     UISheet sheet = (UISheet) uiComponent;
131 
132     Style style = new Style(facesContext, sheet);
133 
134     final String sheetId = sheet.getClientId(facesContext);
135 
136     TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
137 
138     // Outer sheet div
139     writer.startElement(HtmlElements.DIV, sheet);
140     writer.writeIdAttribute(sheetId);
141     HtmlRendererUtils.writeDataAttributes(facesContext, writer, sheet);
142     writer.writeClassAttribute(Classes.create(sheet));
143     writer.writeStyleAttribute(style);
144     UIComponent facetReload = sheet.getFacet(Facets.RELOAD);
145     if (facetReload != null && facetReload instanceof UIReload && facetReload.isRendered()) {
146       UIReload update = (UIReload) facetReload;
147       writer.writeAttribute(DataAttributes.RELOAD, update.getFrequency());
148     }
149 
150     writer.writeAttribute(DataAttributes.PARTIALLY,
151         HtmlRendererUtils.getRenderedPartiallyJavascriptArray(facesContext, sheet, sheet), false);
152     writer.writeAttribute(DataAttributes.SELECTIONMODE, sheet.getSelectable(), false);
153     writer.writeAttribute(DataAttributes.FIRST, Integer.toString(sheet.getFirst()), false);
154 
155     boolean rowAction = HtmlRendererUtils.renderSheetCommands(sheet, facesContext, writer);
156 
157     renderSheet(facesContext, sheet, rowAction, style);
158 
159     writer.endElement(HtmlElements.DIV);
160   }
161 
162   private void renderSheet(FacesContext facesContext, UISheet sheet, boolean hasClickAction, Style style)
163       throws IOException {
164     final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
165     final ResourceManager resourceManager = ResourceManagerFactory.getResourceManager(facesContext);
166     final String contextPath = facesContext.getExternalContext().getRequestContextPath();
167     final String sheetId = sheet.getClientId(facesContext);
168 
169     Measure sheetHeight;
170     if (style.getHeight() == null) {
171       // FIXME: nullpointer if height not defined
172       LOG.error("no height in parent container, setting to 100");
173       sheetHeight = Measure.valueOf(100);
174     } else {
175       sheetHeight = style.getHeight();
176     }
177     final Measure footerHeight = getFooterHeight(facesContext, sheet);
178     final Measure headerHeight = getHeaderHeight(facesContext, sheet);
179     final String selectable = sheet.getSelectable();
180 
181     final Application application = facesContext.getApplication();
182     final SheetState state = sheet.getSheetState(facesContext);
183     final List<Integer> columnWidths = sheet.getWidthList();
184 
185     final List<Integer> selectedRows = getSelectedRows(sheet, state);
186     final List<AbstractUIColumn> renderedColumnList = sheet.getRenderedColumns();
187 
188     writer.startElement(HtmlElements.INPUT, null);
189     writer.writeIdAttribute(sheetId + WIDTHS_POSTFIX);
190     writer.writeNameAttribute(sheetId + WIDTHS_POSTFIX);
191     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
192     writer.writeAttribute(HtmlAttributes.VALUE, StringUtils.joinWithSurroundingSeparator(columnWidths), false);
193     writer.endElement(HtmlElements.INPUT);
194 
195     writer.startElement(HtmlElements.INPUT, null);
196     writer.writeIdAttribute(sheetId + SCROLL_POSTFIX);
197     writer.writeNameAttribute(sheetId + SCROLL_POSTFIX);
198     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
199     Integer[] scrollPosition = sheet.getScrollPosition();
200     if (scrollPosition != null) {
201       String scroll = scrollPosition[0] + ";" + scrollPosition[1];
202       writer.writeAttribute(HtmlAttributes.VALUE, scroll, false);
203     } else {
204       writer.writeAttribute(HtmlAttributes.VALUE, "", false);
205     }
206     writer.endElement(HtmlElements.INPUT);
207 
208     if (!UISheet.NONE.equals(selectable)) {
209       writer.startElement(HtmlElements.INPUT, null);
210       writer.writeIdAttribute(sheetId + SELECTED_POSTFIX);
211       writer.writeNameAttribute(sheetId + SELECTED_POSTFIX);
212       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
213       writer.writeAttribute(
214           HtmlAttributes.VALUE, StringUtils.joinWithSurroundingSeparator(selectedRows), true);
215       writer.endElement(HtmlElements.INPUT);
216     }
217 
218     ExpandedState expandedState = null;
219     StringBuilder expandedValue = null;
220     if (sheet.isTreeModel()) {
221       expandedState = sheet.getExpandedState();
222       expandedValue = new StringBuilder(",");
223     }
224 
225     final boolean showHeader = sheet.isShowHeader();
226     final boolean ie6SelectOneFix = showHeader
227         && ClientProperties.getInstance(facesContext).getUserAgent().isMsie6()
228             && ComponentUtils.findDescendant(sheet, UISelectOne.class) != null;
229 
230 // BEGIN RENDER BODY CONTENT
231     Style bodyStyle = new Style();
232 /*
233     bodyStyle.setPosition(Position.RELATIVE);
234 */
235     Measure tableBodyWidth = sheet.getCurrentWidth().subtractNotNegative(getContentBorder(facesContext, sheet));
236     bodyStyle.setWidth(tableBodyWidth);
237     if (sheet.isPagingVisible()) {
238       sheetHeight = sheetHeight.subtract(footerHeight);
239     }
240     if (ie6SelectOneFix) {
241       bodyStyle.setTop(headerHeight);
242     }
243     if (showHeader) {
244       sheetHeight = sheetHeight.subtract(headerHeight);
245     }
246     bodyStyle.setHeight(sheetHeight);
247 
248     if (showHeader) {
249       renderColumnHeaders(
250           facesContext, sheet, writer, resourceManager, contextPath, sheetId, renderedColumnList, tableBodyWidth);
251     }
252 
253     writer.startElement(HtmlElements.DIV, null);
254     writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "data_div");
255     writer.writeClassAttribute(Classes.create(sheet, "body"));
256 
257 /*
258     bodyStyle.setPaddingTop(ie6SelectOneFix ? Measure.ZERO : headerHeight);
259 */
260 
261     writer.writeStyleAttribute(bodyStyle);
262     bodyStyle.setHeight(null);
263     bodyStyle.setTop(null);
264     Style sheetBodyStyle = new Style(bodyStyle);
265     if (sheet.getNeedVerticalScrollbar() == null) {
266       LOG.warn("Value of needVerticalScrollbar undefined!"); // why this value isn't set by the layout manager?
267     } else {
268       if (sheet.getNeedVerticalScrollbar()) {
269         tableBodyWidth = tableBodyWidth.subtractNotNegative(getVerticalScrollbarWeight(facesContext, sheet));
270       }
271     }
272     sheetBodyStyle.setWidth(tableBodyWidth);
273 
274     writer.startElement(HtmlElements.TABLE, null);
275     writer.writeAttribute(HtmlAttributes.CELLSPACING, "0", false);
276     writer.writeAttribute(HtmlAttributes.CELLPADDING, "0", false);
277     writer.writeAttribute(HtmlAttributes.SUMMARY, "", false);
278     writer.writeClassAttribute(Classes.create(sheet, "bodyTable"));
279     writer.writeStyleAttribute(sheetBodyStyle);
280 
281     if (columnWidths != null) {
282       writer.startElement(HtmlElements.COLGROUP, null);
283       for (Integer columnWidth : columnWidths) {
284         writer.startElement(HtmlElements.COL, null);
285         writer.writeAttribute(HtmlAttributes.WIDTH, columnWidth);
286         writer.endElement(HtmlElements.COL);
287       }
288       writer.endElement(HtmlElements.COLGROUP);
289     }
290 
291     // Print the Content
292 
293     if (LOG.isDebugEnabled()) {
294       LOG.debug("first = " + sheet.getFirst() + "   rows = " + sheet.getRows());
295     }
296 
297     final String var = sheet.getVar();
298 
299     boolean odd = false;
300     boolean emptySheet = true;
301     // rows = 0 means: show all
302     final int last = sheet.isRowsUnlimited() ? Integer.MAX_VALUE : sheet.getFirst() + sheet.getRows();
303     for (int rowIndex = sheet.getFirst(); rowIndex < last; rowIndex++) {
304       sheet.setRowIndex(rowIndex);
305       if (!sheet.isRowAvailable()) {
306         break;
307       }
308       emptySheet = false;
309       odd = !odd;
310 
311       if (LOG.isDebugEnabled()) {
312         LOG.debug("var       " + var);
313         LOG.debug("list      " + sheet.getValue());
314       }
315 
316       if (sheet.isTreeModel()) {
317         final TreePath path = sheet.getPath();
318         if (sheet.isFolder() && expandedState.isExpanded(path)) {
319           expandedValue.append(rowIndex);
320           expandedValue.append(",");
321         }
322       }
323 
324       writer.startElement(HtmlElements.TR, null);
325       Markup rowMarkup = odd ? Markup.ODD : Markup.EVEN;
326       final boolean selected = selectedRows.contains(rowIndex);
327       if (selected) {
328         rowMarkup = rowMarkup.add(Markup.SELECTED);
329       }
330       writer.writeClassAttribute(Classes.create(sheet, "row", rowMarkup));
331       if (!sheet.isRowVisible()) {
332         Style rowStyle = new Style();
333         rowStyle.setDisplay(Display.NONE);
334         writer.writeStyleAttribute(rowStyle);
335       }
336       final String parentId = sheet.getRowParentClientId();
337       if (parentId != null) {
338         writer.writeAttribute(DataAttributes.TREEPARENT, parentId, false);
339       }
340 
341       int columnIndex = -1;
342       for (UIColumn column : renderedColumnList) {
343         columnIndex++;
344 
345         writer.startElement(HtmlElements.TD, column);
346 
347         Markup markup = column instanceof SupportsMarkup ? ((SupportsMarkup) column).getMarkup() : Markup.NULL;
348         if (markup == null) {
349           markup = Markup.NULL;
350         }
351         if (columnIndex == 0) {
352           markup = markup.add(Markup.FIRST);
353         }
354         if (hasClickAction) {
355           markup = markup.add(Markup.CLICKABLE);
356         }
357         if (isPure(column)) {
358           markup = markup.add(Markup.PURE);
359         }
360         writer.writeClassAttribute(Classes.create(sheet, "cell", markup));
361         final TextAlign align = TextAlign.parse((String) column.getAttributes().get(Attributes.ALIGN));
362         if (align != null) {
363           Style alignStyle = new Style();
364           alignStyle.setTextAlign(align);
365           writer.writeStyleAttribute(alignStyle);
366         }
367 
368         if (column instanceof UIColumnSelector) {
369           final boolean disabled = ComponentUtils.getBooleanAttribute(column, Attributes.DISABLED);
370           writer.startElement(HtmlElements.INPUT, null);
371           writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.CHECKBOX, false);
372           writer.writeAttribute(HtmlAttributes.CHECKED, selected);
373           writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
374           writer.writeIdAttribute(sheetId + "_data_row_selector_" + rowIndex);
375           writer.writeClassAttribute(Classes.create(sheet, "columnSelector"));
376           writer.endElement(HtmlElements.INPUT);
377         } else if (column instanceof AbstractUIColumnNode) {
378           RenderUtils.prepareRendererAll(facesContext, column);
379           RenderUtils.encode(facesContext, column);
380         } else {
381           List<UIComponent> children = sheet.getRenderedChildrenOf(column);
382           for (UIComponent grandKid : children) {
383             // set height to 0 to prevent use of layoutheight from parent
384             grandKid.getAttributes().put(Attributes.LAYOUT_HEIGHT, HEIGHT_0);
385             // XXX hotfix
386             if (grandKid instanceof LayoutBase) {
387               LayoutBase base = (LayoutBase) grandKid;
388               if (base.getLeft() != null) {
389                 base.setLeft(null);
390               }
391               if (base.getTop() != null) {
392                 base.setTop(null);
393               }
394             }
395             RenderUtils.prepareRendererAll(facesContext, grandKid);
396             RenderUtils.encode(facesContext, grandKid);
397           }
398         }
399 
400         writer.endElement(HtmlElements.TD);
401       }
402 
403       writer.startElement(HtmlElements.TD, null);
404       writer.writeClassAttribute(Classes.create(sheet, "cell", Markup.FILLER));
405 //      writer.write("&nbsp;");
406       writer.startElement(HtmlElements.DIV, null);
407       writer.endElement(HtmlElements.DIV);
408       writer.endElement(HtmlElements.TD);
409 
410       writer.endElement(HtmlElements.TR);
411     }
412 
413     sheet.setRowIndex(-1);
414 
415     if (emptySheet && showHeader) {
416       writer.startElement(HtmlElements.TR, null);
417       int columnIndex = -1;
418       for (UIColumn ignored : renderedColumnList) {
419         columnIndex++;
420         writer.startElement(HtmlElements.TD, null);
421         writer.startElement(HtmlElements.DIV, null);
422         Integer divWidth = sheet.getWidthList().get(columnIndex);
423         Style divStyle = new Style();
424         divStyle.setWidth(Measure.valueOf(divWidth));
425         writer.writeStyleAttribute(divStyle);
426         writer.endElement(HtmlElements.DIV);
427         writer.endElement(HtmlElements.TD);
428       }
429       writer.startElement(HtmlElements.TD, null);
430       writer.writeClassAttribute(Classes.create(sheet, "cell", Markup.FILLER));
431 //      writer.write("&nbsp;");
432       writer.startElement(HtmlElements.DIV, null);
433       writer.endElement(HtmlElements.DIV);
434       writer.endElement(HtmlElements.TD);
435       writer.endElement(HtmlElements.TR);
436     }
437 
438     writer.endElement(HtmlElements.TABLE);
439     writer.endElement(HtmlElements.DIV);
440 
441 // END RENDER BODY CONTENT
442 
443     if (sheet.isPagingVisible()) {
444       Style footerStyle = new Style();
445       footerStyle.setWidth(sheet.getCurrentWidth());
446       if (ie6SelectOneFix) {
447         footerStyle.setTop(headerHeight);
448       }
449       writer.startElement(HtmlElements.DIV, sheet);
450       writer.writeClassAttribute(Classes.create(sheet, "footer"));
451       writer.writeStyleAttribute(footerStyle);
452 
453       // show row range
454       final Markup showRowRange = markupForLeftCenterRight(sheet.getShowRowRange());
455       if (showRowRange != Markup.NULL) {
456         UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_ROW);
457         if (pagerCommand == null) {
458           pagerCommand = createPagingCommand(application, PageAction.TO_ROW, false);
459           sheet.getFacets().put(Facets.PAGER_ROW, pagerCommand);
460         }
461         final String pagerCommandId = pagerCommand.getClientId(facesContext);
462 
463         writer.startElement(HtmlElements.SPAN, null);
464         writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showRowRange));
465         writer.writeAttribute(HtmlAttributes.TITLE,
466             ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetPagingInfoRowPagingTip"), true);
467         writer.flush(); // is needed in some cases, e. g. TOBAGO-1094
468         writer.write(createSheetPagingInfo(sheet, facesContext, pagerCommandId, true));
469         writer.endElement(HtmlElements.SPAN);
470       }
471 
472       // show direct links
473       final Markup showDirectLinks = markupForLeftCenterRight(sheet.getShowDirectLinks());
474       if (showDirectLinks != Markup.NULL) {
475         writer.startElement(HtmlElements.SPAN, null);
476         writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showDirectLinks));
477         writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "pagingLinks");
478         writeDirectPagingLinks(writer, facesContext, application, sheet);
479         writer.endElement(HtmlElements.SPAN);
480       }
481 
482       // show page range
483       final Markup showPageRange = markupForLeftCenterRight(sheet.getShowPageRange());
484       if (showPageRange != Markup.NULL) {
485         UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_PAGE);
486         if (pagerCommand == null) {
487           pagerCommand = createPagingCommand(application, PageAction.TO_PAGE, false);
488           sheet.getFacets().put(Facets.PAGER_PAGE, pagerCommand);
489         }
490         final String pagerCommandId = pagerCommand.getClientId(facesContext);
491 
492         writer.startElement(HtmlElements.SPAN, null);
493         writer.writeClassAttribute(Classes.create(sheet, "pagingOuter", showPageRange));
494         writer.writeIdAttribute(sheetId + ComponentUtils.SUB_SEPARATOR + "pagingPages");
495         writer.writeText("");
496 
497         boolean atBeginning = sheet.isAtBeginning();
498         link(facesContext, application, atBeginning, PageAction.FIRST, sheet);
499         link(facesContext, application, atBeginning, PageAction.PREV, sheet);
500         writer.startElement(HtmlElements.SPAN, null);
501         writer.writeClassAttribute(Classes.create(sheet, "pagingText"));
502         writer.writeAttribute(HtmlAttributes.TITLE,
503             ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetPagingInfoPagePagingTip"), true);
504         writer.flush(); // is needed in some cases, e. g. TOBAGO-1094
505         writer.write(createSheetPagingInfo(sheet, facesContext, pagerCommandId, false));
506         writer.endElement(HtmlElements.SPAN);
507         boolean atEnd = sheet.isAtEnd();
508         link(facesContext, application, atEnd, PageAction.NEXT, sheet);
509         link(facesContext, application, atEnd || !sheet.hasRowCount(), PageAction.LAST, sheet);
510         writer.endElement(HtmlElements.SPAN);
511       }
512 
513       writer.endElement(HtmlElements.DIV);
514     }
515 
516     if (sheet.isTreeModel()) {
517       writer.startElement(HtmlElements.INPUT, sheet);
518       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
519       final String expandedId = sheetId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_EXPANDED;
520       writer.writeNameAttribute(expandedId);
521       writer.writeIdAttribute(expandedId);
522       writer.writeClassAttribute(Classes.create(sheet, AbstractUIData.SUFFIX_EXPANDED));
523       writer.writeAttribute(HtmlAttributes.VALUE, expandedValue.toString(), false);
524       writer.endElement(HtmlElements.INPUT);
525     }
526   }
527 
528   /**
529    * Differ between simple content and complex content.
530    * Decide if the content of a cell needs usually the whole possible space or
531    * is the character of the content like flowing text.
532    * In the second case, the style usually sets a padding.<br/>
533    * Pure is needed for &lt;tc:panel>,  &lt;tc:in>, etc.<br/>
534    * Pure is not needed for  &lt;tc:out> and &lt;tc:link>
535    */
536   private boolean isPure(UIColumn column) {
537     for (UIComponent child : column.getChildren()) {
538       if (!(child instanceof UIOut) && !(child instanceof UILink)) {
539         return true;
540       }
541     }
542     return false;
543   }
544 
545   private String createSheetPagingInfo(UISheet sheet, FacesContext facesContext, String pagerCommandId, boolean row) {
546     String sheetPagingInfo;
547     if (sheet.getRowCount() != 0) {
548       Locale locale = facesContext.getViewRoot().getLocale();
549       int first = row ? sheet.getFirst() + 1 : sheet.getCurrentPage() + 1;
550       int last = sheet.hasRowCount()
551           ? row ? sheet.getLastRowIndexOfCurrentPage() : sheet.getPages()
552           : -1;
553       final boolean unknown = !sheet.hasRowCount();
554       final String key = "sheetPagingInfo"
555           + (unknown ? "Undefined" : "")
556           + (first == last ? "Single" : "")
557           + (row ? "Row" : "Page")
558           + (first == last ? "" : "s"); // plural
559       final String message = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", key);
560       final MessageFormat detail = new MessageFormat(message, locale);
561       Object[] args = {
562           first,
563           last == -1 ? "?" : last,
564           unknown ? "" : sheet.getRowCount(),
565           pagerCommandId + ComponentUtils.SUB_SEPARATOR + "text"
566       };
567       sheetPagingInfo = detail.format(args);
568     } else {
569       sheetPagingInfo =
570           ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
571               "sheetPagingInfoEmpty" + (row ? "Row" : "Page"));
572     }
573     return sheetPagingInfo;
574   }
575 
576   @Override
577   public void decode(FacesContext facesContext, UIComponent component) {
578     super.decode(facesContext, component);
579 
580     UISheet sheet = (UISheet) component;
581 
582     String key = sheet.getClientId(facesContext) + WIDTHS_POSTFIX;
583 
584     Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
585     if (requestParameterMap.containsKey(key)) {
586       String widths = (String) requestParameterMap.get(key);
587       if (widths.trim().length() > 0) {
588         sheet.getAttributes().put(Attributes.WIDTH_LIST_STRING, widths);
589       }
590     }
591 
592     key = sheet.getClientId(facesContext) + SELECTED_POSTFIX;
593     if (requestParameterMap.containsKey(key)) {
594       String selected = (String) requestParameterMap.get(key);
595       if (LOG.isDebugEnabled()) {
596         LOG.debug("selected = " + selected);
597       }
598       List<Integer> selectedRows;
599       try {
600         selectedRows = StringUtils.parseIntegerList(selected);
601       } catch (NumberFormatException e) {
602         LOG.warn(selected, e);
603         selectedRows = Collections.emptyList();
604       }
605 
606       sheet.getAttributes().put(Attributes.SELECTED_LIST_STRING, selectedRows);
607     }
608 
609     key = sheet.getClientId(facesContext) + SCROLL_POSTFIX;
610     String value = (String) requestParameterMap.get(key);
611     if (value != null) {
612       Integer[] scrollPosition = SheetState.parseScrollPosition(value);
613       if (scrollPosition != null) {
614         //noinspection unchecked
615         sheet.getAttributes().put(Attributes.SCROLL_POSITION, scrollPosition);
616       }
617     }
618 
619     RenderUtils.decodedStateOfTreeData(facesContext, sheet);
620   }
621 
622   private Measure getHeaderHeight(FacesContext facesContext, UISheet sheet) {
623     int rows = sheet.getHeaderGrid().getRowCount();
624     return sheet.isShowHeader()
625         ? getResourceManager().getThemeMeasure(facesContext, sheet, "headerHeight").multiply(rows)
626         : Measure.ZERO;
627   }
628 
629   private Measure getRowHeight(FacesContext facesContext, UISheet sheet) {
630     return getResourceManager().getThemeMeasure(facesContext, sheet, "rowHeight");
631   }
632 
633   private Measure getFooterHeight(FacesContext facesContext, UISheet sheet) {
634     return sheet.isPagingVisible()
635         ? getResourceManager().getThemeMeasure(facesContext, sheet, "footerHeight")
636         : Measure.ZERO;
637   }
638 
639   private Markup markupForLeftCenterRight(String name) {
640     if ("left".equals(name)) {
641       return Markup.LEFT;
642     }
643     if ("center".equals(name)) {
644       return Markup.CENTER;
645     }
646     if ("right".equals(name)) {
647       return Markup.RIGHT;
648     }
649     return Markup.NULL;
650   }
651 
652   private String checkPagingAttribute(String name) {
653     if (isNotNone(name)) {
654       return name;
655     } else {
656       if (!"none".equals(name)) {
657         LOG.warn("Illegal value in sheets paging attribute: '" + name + "'");
658       }
659       return "none";
660     }
661   }
662 
663   private boolean isNotNone(String value) {
664     // todo: use enum type instead of string
665     return "left".equals(value) || "center".equals(value) || "right".equals(value);
666   }
667 
668   @Override
669   public boolean getRendersChildren() {
670     return true;
671   }
672 
673   private List<Integer> getSelectedRows(UISheet data, SheetState state) {
674     List<Integer> selected = (List<Integer>)
675         data.getAttributes().get(Attributes.SELECTED_LIST_STRING);
676     if (selected == null && state != null) {
677       selected = state.getSelectedRows();
678     }
679     if (selected == null) {
680       selected = Collections.emptyList();
681     }
682     return selected;
683   }
684 
685   private void link(FacesContext facesContext, Application application,
686                     boolean disabled, PageAction command, UISheet data)
687       throws IOException {
688     UICommand link = createPagingCommand(application, command, disabled);
689 
690     data.getFacets().put(command.getToken(), link);
691 
692 
693     String tip = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago",
694         "sheet" + command.getToken());
695     String image = ResourceManagerUtils.getImageWithPath(facesContext,
696         "image/sheet" + command.getToken() + (disabled ? "Disabled" : "") + ".gif");
697 
698     TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
699     writer.startElement(HtmlElements.IMG, null);
700     writer.writeIdAttribute(data.getClientId(facesContext)
701         + ComponentUtils.SUB_SEPARATOR + "pagingPages" + ComponentUtils.SUB_SEPARATOR + command.getToken());
702     Classes pagerClasses = Classes.create(data, "footerPagerButton", disabled ? Markup.DISABLED : null);
703     writer.writeClassAttribute(pagerClasses);
704     writer.writeAttribute(HtmlAttributes.SRC, image, false);
705     writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
706     writer.writeAttribute(HtmlAttributes.ALT, "", false);
707     writer.writeAttribute(DataAttributes.DISABLED, disabled);
708     writer.endElement(HtmlElements.IMG);
709   }
710 
711   // TODO sheet.getColumnLayout() liefert ggf. eine falsche Anzahl von Spalten
712   // TODO
713   // TODO
714 
715   private void renderColumnHeaders(
716       FacesContext facesContext, UISheet sheet, TobagoResponseWriter writer, ResourceManager resourceManager,
717       String contextPath, String sheetId, List<AbstractUIColumn> renderedColumnList, Measure headerWidth)
718       throws IOException {
719 
720     final Grid grid = sheet.getHeaderGrid();
721     final List<Integer> columnWidths = sheet.getWidthList();
722 
723     LOG.info("*****************************************************");
724     LOG.info("" + grid);
725     LOG.info("*****************************************************");
726 
727     writer.startElement(HtmlElements.DIV, sheet);
728     writer.writeClassAttribute(Classes.create(sheet, "headerDiv"));
729     writer.startElement(HtmlElements.TABLE, sheet);
730     writer.writeAttribute(HtmlAttributes.CELLSPACING, "0", false);
731     writer.writeAttribute(HtmlAttributes.CELLPADDING, "0", false);
732     writer.writeAttribute(HtmlAttributes.SUMMARY, "", false);
733     writer.writeClassAttribute(Classes.create(sheet, "headerTable"));
734 
735     if (columnWidths != null) {
736       writer.startElement(HtmlElements.COLGROUP, null);
737       for (Integer columnWidth : columnWidths) {
738         writer.startElement(HtmlElements.COL, null);
739         writer.writeAttribute(HtmlAttributes.WIDTH, columnWidth);
740         writer.endElement(HtmlElements.COL);
741       }
742       writer.endElement(HtmlElements.COLGROUP);
743     }
744 
745     writer.startElement(HtmlElements.TBODY, sheet);
746     for (int i = 0; i < grid.getRowCount(); i++) {
747       writer.startElement(HtmlElements.TR, null);
748       for (int j = 0; j < grid.getColumnCount(); j++) {
749         final Cell cell = grid.getCell(j, i);
750         if (cell instanceof OriginCell) {
751           writer.startElement(HtmlElements.TD, null);
752           if (cell.getColumnSpan() > 1) {
753             writer.writeAttribute(HtmlAttributes.COLSPAN, cell.getColumnSpan());
754           }
755           if (cell.getRowSpan() > 1) {
756             writer.writeAttribute(HtmlAttributes.ROWSPAN, cell.getRowSpan());
757           }
758 
759           final UIComponent cellComponent = (UIComponent) cell.getComponent();
760           final boolean pure = !(cellComponent instanceof UIOut);
761 
762           writer.startElement(HtmlElements.DIV, null);
763           writer.writeClassAttribute(Classes.create(sheet, "headerCell"));
764           writer.startElement(HtmlElements.SPAN, null);
765           Style headerStyle = new Style();
766           Measure headerHeight = Measure.valueOf(20).multiply(cell.getRowSpan());
767           if (!pure) {
768             headerHeight = headerHeight.subtract(6); // XXX todo
769           }
770           headerStyle.setHeight(headerHeight);
771           writer.writeStyleAttribute(headerStyle);
772           final AbstractUIColumn column = renderedColumnList.get(j);
773           String sorterImage = null;
774           Markup markup = Markup.NULL;
775           String tip = (String) column.getAttributes().get(Attributes.TIP);
776           if (cell.getColumnSpan() == 1) {
777             final boolean sortable = ComponentUtils.getBooleanAttribute(column, Attributes.SORTABLE);
778             if (sortable) {
779               UICommand sortCommand = (UICommand) column.getFacet(Facets.SORTER);
780               if (sortCommand == null) {
781                 final String columnId = column.getClientId(facesContext);
782                 final String sorterId = columnId.substring(columnId.lastIndexOf(":") + 1) + "_" + UISheet.SORTER_ID;
783                 sortCommand = (UICommand) CreateComponentUtils.createComponent(
784                     facesContext, UICommand.COMPONENT_TYPE, RendererTypes.LINK, sorterId);
785                 column.getFacets().put(Facets.SORTER, sortCommand);
786               }
787               final CommandMap map = new CommandMap();
788               final Command click = new Command(sortCommand.getClientId(facesContext),
789                   null, null, null, new String[]{sheet.getClientId(facesContext)}, null, null, null, null);
790               map.setClick(click);
791               writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(map), true);
792 
793               if (tip == null) {
794                 tip = "";
795               } else {
796                 tip += " - ";
797               }
798               tip += ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetTipSorting");
799 
800               markup = markup.add(Markup.SORTABLE);
801 
802               SheetState sheetState = sheet.getSheetState(facesContext);
803               if (column.getId().equals(sheetState.getSortedColumnId())) {
804                 String sortTitle;
805                 if (sheetState.isAscending()) {
806                   sorterImage = contextPath + resourceManager.getImage(facesContext, "image/ascending.gif");
807                   sortTitle = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetAscending");
808                   markup = markup.add(Markup.ASCENDING);
809                 } else {
810                   sorterImage = contextPath + resourceManager.getImage(facesContext, "image/descending.gif");
811                   sortTitle = ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", "sheetDescending");
812                   markup = markup.add(Markup.DESCENDING);
813                 }
814                 if (sortTitle != null) {
815                   tip += " - " + sortTitle;
816                 }
817               }
818             }
819           }
820 
821           if (j == 0) {
822             markup = markup.add(Markup.FIRST);
823           }
824           if (pure) {
825              markup = markup.add(Markup.PURE);
826           }
827           writer.writeClassAttribute(Classes.create(sheet, "header", markup));
828           writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
829 
830           if (column instanceof UIColumnSelector) {
831             renderColumnSelectorHeader(facesContext, writer, sheet);
832           } else {
833              RenderUtils.encode(facesContext, cellComponent);
834           }
835 
836           if (sorterImage != null) {
837             writer.startElement(HtmlElements.IMG, null);
838             writer.writeAttribute(HtmlAttributes.SRC, sorterImage, false);
839             writer.writeAttribute(HtmlAttributes.ALT, "", false);
840             writer.endElement(HtmlElements.IMG);
841           }
842 
843           writer.endElement(HtmlElements.SPAN);
844           if (renderedColumnList.get(j).isResizable()) {
845             encodeResizing(writer, sheet, j + cell.getColumnSpan() - 1);
846           }
847           writer.endElement(HtmlElements.DIV);
848 
849           writer.endElement(HtmlElements.TD);
850         }
851       }
852       // add a filler column
853       writer.startElement(HtmlElements.TD, null);
854       writer.startElement(HtmlElements.DIV, null);
855       // todo: is the filler class needed here?
856       writer.writeClassAttribute(Classes.create(sheet, "headerCell", Markup.FILLER));
857       writer.endElement(HtmlElements.DIV);
858       writer.endElement(HtmlElements.TD);
859 
860       writer.endElement(HtmlElements.TR);
861     }
862     writer.endElement(HtmlElements.TBODY);
863     writer.endElement(HtmlElements.TABLE);
864     writer.endElement(HtmlElements.DIV);
865   }
866 
867   private void encodeResizing(TobagoResponseWriter writer, AbstractUISheet sheet, int columnIndex) throws IOException {
868     writer.startElement(HtmlElements.SPAN, null);
869     writer.writeClassAttribute(Classes.create(sheet, "headerResize"));
870     writer.writeAttribute(DataAttributes.COLUMNINDEX, Integer.toString(columnIndex), false);
871     writer.write("&nbsp;&nbsp;"); // is needed for IE
872     writer.endElement(HtmlElements.SPAN);
873   }
874 
875   protected void renderColumnSelectorHeader(
876       FacesContext facesContext, TobagoResponseWriter writer, UISheet sheet)
877       throws IOException {
878 
879     final UIToolBar toolBar = createToolBar(facesContext, sheet);
880     writer.startElement(HtmlElements.DIV, null);
881     writer.writeClassAttribute(Classes.create(sheet, "toolBar"));
882 
883     if (UISheet.MULTI.equals(sheet.getSelectable())) {
884       RenderUtils.prepareRendererAll(facesContext, toolBar);
885       RenderUtils.encode(facesContext, toolBar);
886     }
887 
888     writer.endElement(HtmlElements.DIV);
889   }
890 
891   private UIToolBar createToolBar(FacesContext facesContext, UISheet sheet) {
892     final Application application = facesContext.getApplication();
893     final UICommand dropDown = (UICommand) CreateComponentUtils.createComponent(
894         facesContext, UICommand.COMPONENT_TYPE, null, "dropDown");
895     final UIMenu menu = (UIMenu) CreateComponentUtils.createComponent(
896         facesContext, UIMenu.COMPONENT_TYPE, RendererTypes.MENU, "menu");
897     FacetUtils.setDropDownMenu(dropDown, menu);
898     final String sheetId = sheet.getClientId(facesContext);
899 
900     createMenuItem(facesContext, menu, "sheetMenuSelect", Markup.SHEET_SELECT_ALL, sheetId);
901     createMenuItem(facesContext, menu, "sheetMenuUnselect", Markup.SHEET_DESELECT_ALL, sheetId);
902     createMenuItem(facesContext, menu, "sheetMenuToggleselect", Markup.SHEET_TOGGLE_ALL, sheetId);
903 
904     final UIToolBar toolBar = (UIToolBar) application.createComponent(UIToolBar.COMPONENT_TYPE);
905     toolBar.setId(facesContext.getViewRoot().createUniqueId());
906     toolBar.setRendererType("TabGroupToolBar");
907     toolBar.setTransient(true);
908     toolBar.getChildren().add(dropDown);
909     sheet.getFacets().put(Facets.TOOL_BAR, toolBar);
910     return toolBar;
911   }
912 
913   private void createMenuItem(
914       final FacesContext facesContext, UIMenu menu, String label, Markup markup, String sheetId) {
915     final String id = markup.toString();
916     final UIMenuCommand menuItem = (UIMenuCommand) CreateComponentUtils.createComponent(
917         facesContext, UIMenuCommand.COMPONENT_TYPE, RendererTypes.MENU_COMMAND, id);
918     menuItem.setLabel(ResourceManagerUtils.getPropertyNotNull(facesContext, "tobago", label));
919     menuItem.setMarkup(markup);
920     menuItem.setOnclick("/**/"); // XXX avoid submit
921     ComponentUtils.putDataAttributeWithPrefix(menuItem, DataAttributes.SHEETID, sheetId);
922     menu.getChildren().add(menuItem);
923   }
924 
925   private void writeDirectPagingLinks(
926       TobagoResponseWriter writer, FacesContext facesContext, Application application, UISheet sheet)
927       throws IOException {
928     UICommand pagerCommand = (UICommand) sheet.getFacet(Facets.PAGER_PAGE);
929     if (pagerCommand == null) {
930       pagerCommand = createPagingCommand(application, PageAction.TO_PAGE, false);
931       sheet.getFacets().put(Facets.PAGER_PAGE, pagerCommand);
932     }
933     String pagerCommandId = pagerCommand.getClientId(facesContext);
934     int linkCount = ComponentUtils.getIntAttribute(sheet, Attributes.DIRECT_LINK_COUNT);
935     linkCount--;  // current page needs no link
936     ArrayList<Integer> prevs = new ArrayList<Integer>(linkCount);
937     int page = sheet.getCurrentPage() + 1;
938     for (int i = 0; i < linkCount && page > 1; i++) {
939       page--;
940       if (page > 0) {
941         prevs.add(0, page);
942       }
943     }
944 
945     ArrayList<Integer> nexts = new ArrayList<Integer>(linkCount);
946     page = sheet.getCurrentPage() + 1;
947     final int pages = sheet.hasRowCount() || sheet.isRowsUnlimited() ? sheet.getPages() : Integer.MAX_VALUE;
948     for (int i = 0; i < linkCount && page < pages; i++) {
949       page++;
950       if (page > 1) {
951         nexts.add(page);
952       }
953     }
954 
955     if (prevs.size() > (linkCount / 2)
956         && nexts.size() > (linkCount - (linkCount / 2))) {
957       while (prevs.size() > (linkCount / 2)) {
958         prevs.remove(0);
959       }
960       while (nexts.size() > (linkCount - (linkCount / 2))) {
961         nexts.remove(nexts.size() - 1);
962       }
963     } else if (prevs.size() <= (linkCount / 2)) {
964       while (prevs.size() + nexts.size() > linkCount) {
965         nexts.remove(nexts.size() - 1);
966       }
967     } else {
968       while (prevs.size() + nexts.size() > linkCount) {
969         prevs.remove(0);
970       }
971     }
972 
973     String name;
974     int skip = prevs.size() > 0 ? prevs.get(0) : 1;
975     if (skip > 1) {
976       skip -= (linkCount - (linkCount / 2));
977       skip--;
978       name = "...";
979       if (skip < 1) {
980         skip = 1;
981         if (prevs.get(0) == 2) {
982           name = "1";
983         }
984       }
985       writeLinkElement(writer, sheet, name, Integer.toString(skip), pagerCommandId, true);
986     }
987     for (Integer prev : prevs) {
988       name = prev.toString();
989       writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
990     }
991     name = Integer.toString(sheet.getCurrentPage() + 1);
992     writeLinkElement(writer, sheet, name, name, pagerCommandId, false);
993 
994     for (Integer next : nexts) {
995       name = next.toString();
996       writeLinkElement(writer, sheet, name, name, pagerCommandId, true);
997     }
998 
999     skip = nexts.size() > 0 ? nexts.get(nexts.size() - 1) : pages;
1000     if (skip < pages) {
1001       skip += linkCount / 2;
1002       skip++;
1003       name = "...";
1004       if (skip > pages) {
1005         skip = pages;
1006         if ((nexts.get(nexts.size() - 1)) == skip - 1) {
1007           name = Integer.toString(skip);
1008         }
1009       }
1010       writeLinkElement(writer, sheet, name, Integer.toString(skip), pagerCommandId, true);
1011     }
1012   }
1013 
1014   private UICommand createPagingCommand(Application application, PageAction command, boolean disabled) {
1015     UICommand link;
1016     link = (UICommand) application.createComponent(UICommand.COMPONENT_TYPE);
1017     link.setRendererType(RendererTypes.SHEET_PAGE_COMMAND);
1018     link.setRendered(true);
1019     link.setId(command.getToken());
1020     link.getAttributes().put(Attributes.INLINE, Boolean.TRUE);
1021     link.getAttributes().put(Attributes.DISABLED, disabled);
1022     return link;
1023   }
1024 
1025   private void writeLinkElement(
1026       TobagoResponseWriter writer, UISheet sheet, String str, String skip, String id, boolean makeLink)
1027       throws IOException {
1028     String type = makeLink ? HtmlElements.A : HtmlElements.SPAN;
1029     writer.startElement(type, null);
1030     writer.writeClassAttribute(Classes.create(sheet, "pagingLink"));
1031     if (makeLink) {
1032       writer.writeIdAttribute(id + ComponentUtils.SUB_SEPARATOR + "link_" + skip);
1033       writer.writeAttribute(HtmlAttributes.HREF, "#", true);
1034     }
1035     writer.flush();
1036     writer.write(str);
1037     writer.endElement(type);
1038   }
1039 
1040   private Measure getContentBorder(FacesContext facesContext, UISheet data) {
1041     return getBorderLeft(facesContext, data).add(getBorderRight(facesContext, data));
1042   }
1043 
1044   @Override
1045   public Measure getPreferredHeight(FacesContext facesContext, Configurable component) {
1046     final UISheet sheet = (UISheet) component;
1047     final Measure headerHeight = getHeaderHeight(facesContext, sheet);
1048     final Measure rowHeight = getRowHeight(facesContext, sheet);
1049     final Measure footerHeight = getFooterHeight(facesContext, sheet);
1050     int rows = sheet.getRows();
1051     if (rows == 0) {
1052       rows = sheet.getRowCount();
1053     }
1054     if (rows == -1) {
1055       rows = 10; // estimating something to get a valid value...
1056     }
1057 
1058     if (LOG.isDebugEnabled()) {
1059       LOG.debug(headerHeight + " " + footerHeight + " " + rowHeight + " " + rows);
1060     }
1061 
1062     return headerHeight.add(rowHeight.multiply(rows)).add(footerHeight);
1063   }
1064 
1065   @Override
1066   public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
1067     // DO Nothing
1068   }
1069 
1070   @Override
1071   public boolean getPrepareRendersChildren() {
1072     return true;
1073   }
1074 
1075   @Override
1076   public void prepareRendersChildren(FacesContext facesContext, UIComponent component) throws IOException {
1077 /*
1078     UISheet sheet = (UISheet) component;
1079     for (UIColumn column : sheet.getRenderedColumns()) {
1080       RenderUtils.prepareRendererAll(facesContext, column);
1081     }
1082 */
1083   }
1084 }