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