001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.myfaces.tobago.component;
021    
022    import org.apache.commons.logging.Log;
023    import org.apache.commons.logging.LogFactory;
024    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_COLUMNS;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SPAN_X;
026    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SPAN_Y;
027    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_BORDER;
028    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_CELLSPACING;
029    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARGIN;
030    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARGIN_BOTTOM;
031    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARGIN_LEFT;
032    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARGIN_RIGHT;
033    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_MARGIN_TOP;
034    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ROWS;
035    import org.apache.myfaces.tobago.util.LayoutUtil;
036    
037    import javax.faces.component.UIComponent;
038    import javax.faces.context.FacesContext;
039    import javax.faces.el.ValueBinding;
040    import java.io.IOException;
041    import java.io.Serializable;
042    import java.util.ArrayList;
043    import java.util.List;
044    
045    public class UIGridLayout extends UILayout {
046    
047      private static final Log LOG = LogFactory.getLog(UIGridLayout.class);
048    
049      public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.GridLayout";
050      public static final String COMPONENT_FAMILY = "org.apache.myfaces.tobago.GridLayout";
051    
052      public static final Marker FREE = new Marker("free");
053      public static final String USED = "used";
054    
055      private String border;
056      private String cellspacing;
057    
058      private String margin;
059      private String marginTop;
060      private String marginRight;
061      private String marginBottom;
062      private String marginLeft;
063      private String columns;
064      private String rows;
065      private boolean ignoreFree;
066      private transient LayoutTokens columnLayout;
067      private transient LayoutTokens rowLayout;
068    
069      private List<Row> layoutRows;
070    
071      public LayoutTokens getRowLayout() {
072        if (rowLayout == null) {
073          rowLayout = LayoutTokens.parse(getRows());
074        }
075        return rowLayout;
076      }
077    
078      public LayoutTokens getColumnLayout() {
079        if (columnLayout == null) {
080          columnLayout = LayoutTokens.parse(getColumns());
081        }
082        return columnLayout;
083      }
084    
085      public String getMarginTop() {
086        if (marginTop != null) {
087          return marginTop;
088        }
089        ValueBinding vb = getValueBinding(ATTR_MARGIN_TOP);
090        if (vb != null) {
091          return (String) vb.getValue(getFacesContext());
092        } else {
093          return getMargin();
094        }
095      }
096    
097      public String getMarginRight() {
098        if (marginRight != null) {
099          return marginRight;
100        }
101        ValueBinding vb = getValueBinding(ATTR_MARGIN_RIGHT);
102        if (vb != null) {
103          return (String) vb.getValue(getFacesContext());
104        } else {
105          return getMargin();
106        }
107      }
108    
109      public String getMarginBottom() {
110        if (marginBottom != null) {
111          return marginBottom;
112        }
113        ValueBinding vb = getValueBinding(ATTR_MARGIN_BOTTOM);
114        if (vb != null) {
115          return (String) vb.getValue(getFacesContext());
116        } else {
117          return getMargin();
118        }
119      }
120    
121      public String getMarginLeft() {
122        if (marginLeft != null) {
123          return marginLeft;
124        }
125        ValueBinding vb = getValueBinding(ATTR_MARGIN_LEFT);
126        if (vb != null) {
127          return (String) vb.getValue(getFacesContext());
128        } else {
129          return getMargin();
130        }
131      }
132    
133      public String getMargin() {
134        if (margin != null) {
135          return margin;
136        }
137        ValueBinding vb = getValueBinding(ATTR_MARGIN);
138        if (vb != null) {
139          return (String) vb.getValue(getFacesContext());
140        } else {
141          return margin;
142        }
143      }
144    
145      public String getRows() {
146        if (rows != null) {
147          return rows;
148        }
149        ValueBinding vb = getValueBinding(ATTR_ROWS);
150        if (vb != null) {
151          return (String) vb.getValue(getFacesContext());
152        } else {
153          return "1*";
154        }
155      }
156    
157      public String getColumns() {
158        if (columns != null) {
159          return columns;
160        }
161        ValueBinding vb = getValueBinding(ATTR_COLUMNS);
162        if (vb != null) {
163          return (String) vb.getValue(getFacesContext());
164        } else {
165          return "1*";
166        }
167      }
168    
169      public String getCellspacing() {
170        if (cellspacing != null) {
171          return cellspacing;
172        }
173        ValueBinding vb = getValueBinding(ATTR_CELLSPACING);
174        if (vb != null) {
175          return (String) vb.getValue(getFacesContext());
176        } else {
177          return cellspacing;
178        }
179      }
180    
181      public String getBorder() {
182        if (border != null) {
183          return border;
184        }
185        ValueBinding vb = getValueBinding(ATTR_BORDER);
186        if (vb != null) {
187          return (String) vb.getValue(getFacesContext());
188        } else {
189          return border;
190        }
191      }
192    
193      public void setBorder(String border) {
194        this.border = border;
195      }
196    
197      public void setCellspacing(String cellspacing) {
198        this.cellspacing = cellspacing;
199      }
200    
201      public void setMargin(String margin) {
202        this.margin = margin;
203      }
204    
205      public void setMarginTop(String marginTop) {
206        this.marginTop = marginTop;
207      }
208    
209      public void setMarginRight(String marginRight) {
210        this.marginRight = marginRight;
211      }
212    
213      public void setMarginBottom(String marginBottom) {
214        this.marginBottom = marginBottom;
215      }
216    
217      public void setMarginLeft(String marginLeft) {
218        this.marginLeft = marginLeft;
219      }
220    
221      public void setColumns(String columns) {
222        this.columns = columns;
223      }
224    
225      public void setRows(String rows) {
226        this.rows = rows;
227      }
228    
229      public Object saveState(FacesContext context) {
230        clearRows();
231        // fix for jsf 1.1.01
232        columnLayout = null;
233        rowLayout = null;
234        Object[] saveState = new Object[10];
235        saveState[0] = super.saveState(context);
236        saveState[1] = rows;
237        saveState[2] = columns;
238        saveState[3] = margin;
239        saveState[4] = marginLeft;
240        saveState[5] = marginRight;
241        saveState[6] = marginTop;
242        saveState[7] = marginBottom;
243        saveState[8] = border;
244        saveState[9] = cellspacing;
245        return saveState;
246      }
247    
248      public void restoreState(FacesContext context, Object savedState) {
249        Object[] values = (Object[]) savedState;
250        super.restoreState(context, values[0]);
251        rows = (String) values[1];
252        columns = (String) values[2];
253        margin = (String) values[3];
254        marginLeft = (String) values[4];
255        marginRight = (String) values[5];
256        marginTop = (String) values[6];
257        marginBottom = (String) values[7];
258        border = (String) values[8];
259        cellspacing = (String) values[9];
260      }
261    
262      @Override
263      public String getFamily() {
264        return COMPONENT_FAMILY;
265      }
266    
267      @Override
268      public boolean getRendersChildren() {
269        return false;
270      }
271    
272      @Override
273      public void encodeChildren(FacesContext context)
274          throws IOException {
275        // do nothing here
276      }
277    
278      @Override
279      public void encodeChildrenOfComponent(
280          FacesContext facesContext, UIComponent component) throws IOException {
281        super.encodeChildrenOfComponent(facesContext, component);
282        clearRows();
283        // fix for jsf 1.1.01
284        columnLayout = null;
285        rowLayout = null;
286      }
287    
288      private void clearRows() {
289        layoutRows = null;
290      }
291    
292      public int getColumnCount() {
293        return getColumnLayout().getSize();
294      }
295    
296      public List<Row> ensureRows() {
297        if (layoutRows == null) {
298          layoutRows = createRows();
299        }
300        return layoutRows;
301      }
302    
303      private List<Row> createRows() {
304        List<Row> rows = new ArrayList<Row>();
305        int columnCount = getColumnCount();
306        List<UIComponent> children
307            = LayoutUtil.addChildren(new ArrayList<UIComponent>(), getParent());
308    
309        for (UIComponent component : children) {
310          int spanX = getSpanX(component);
311          int spanY = getSpanY(component);
312    
313          int r = nextFreeRow(rows);
314          if (r == rows.size()) {
315            rows.add(new Row(columnCount));
316          }
317          int c = rows.get(r).nextFreeColumn();
318          rows.get(r).addControl(component, spanX);
319          rows.get(r).fill(c + 1, c + spanX, component.isRendered());
320    
321          for (int i = r + 1; i < r + spanY; i++) {
322    
323            if (i == rows.size()) {
324              rows.add(new Row(columnCount));
325            }
326            rows.get(i).fill(c, c + spanX, component.isRendered());
327          }
328        }
329        return rows;
330      }
331    
332      private int nextFreeRow(List rows) {
333        int i = 0;
334        for (; i < rows.size(); i++) {
335          if (((Row) rows.get(i)).nextFreeColumn() != -1) {
336            return i;
337          }
338        }
339        return i;
340      }
341    
342      public static int getSpanX(UIComponent component) {
343        return ComponentUtil.getIntAttribute(
344            component, ATTR_SPAN_X, 1);
345      }
346    
347      public static int getSpanY(UIComponent component) {
348        return ComponentUtil.getIntAttribute(
349            component, ATTR_SPAN_Y, 1);
350      }
351    
352      public boolean isIgnoreFree() {
353        return ignoreFree;
354      }
355    
356      public void setIgnoreFree(boolean ignoreFree) {
357        this.ignoreFree = ignoreFree;
358      }
359    
360      public static class Row implements Serializable {
361        private static final long serialVersionUID = 1511693677519052045L;
362        private int columns;
363        private List cells;
364        private boolean hidden;
365    
366        public Row(int columns) {
367          setColumns(columns);
368        }
369    
370        private void addControl(UIComponent component, int spanX) {
371    
372          int i = nextFreeColumn();
373    
374          cells.set(i, component);
375          fill(i + 1, i + spanX, component.isRendered());
376        }
377    
378        private void fill(int start, int end, boolean rendered) {
379    
380          if (end > columns) {
381            LOG.error("Error in Jsp (end > columns). "
382                + "Try to insert more spanX as possible.");
383            LOG.error("start:   " + start);
384            LOG.error("end:     " + end);
385            LOG.error("columns: " + columns);
386            LOG.error("Actual cells:");
387            for (Object component : cells) {
388              if (component instanceof UIComponent) {
389                LOG.error("Cell-ID: " + ((UIComponent) component).getId()
390                    + " " + ((UIComponent) component).getRendererType());
391              } else {
392                LOG.error("Cell:    " + component); // e.g. marker
393              }
394            }
395    
396            end = columns; // fix the "end" parameter to continue the processing.
397          }
398    
399          for (int i = start; i < end; i++) {
400            cells.set(i, new Marker(USED, rendered));
401          }
402        }
403    
404        private int nextFreeColumn() {
405          for (int i = 0; i < columns; i++) {
406            if (FREE.equals(cells.get(i))) {
407              return i;
408            }
409          }
410          return -1;
411        }
412    
413        public List getElements() {
414          return cells;
415        }
416    
417        public int getColumns() {
418          return columns;
419        }
420    
421        private void setColumns(int columns) {
422          this.columns = columns;
423          cells = new ArrayList(columns);
424          for (int i = 0; i < columns; i++) {
425            cells.add(FREE);
426          }
427        }
428    
429        public boolean isHidden() {
430          return hidden;
431        }
432    
433        public void setHidden(boolean hidden) {
434          this.hidden = hidden;
435        }
436      }
437    
438      public static class Marker implements Serializable {
439        private static final long serialVersionUID = 2505999420762504893L;
440        private final String name;
441        private boolean rendered;
442    
443        private Marker(String name) {
444          this.name = name;
445        }
446    
447        public Marker(String name, boolean rendered) {
448          this.name = name;
449          this.rendered = rendered;
450        }
451    
452        @Override
453        public String toString() {
454          return name;
455        }
456    
457        public boolean isRendered() {
458          return rendered;
459        }
460      }
461    }