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