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        Object[] saveState = new Object[10];
230        saveState[0] = super.saveState(context);
231        saveState[1] = rows;
232        saveState[2] = columns;
233        saveState[3] = margin;
234        saveState[4] = marginLeft;
235        saveState[5] = marginRight;
236        saveState[6] = marginTop;
237        saveState[7] = marginBottom;
238        saveState[8] = border;
239        saveState[9] = cellspacing;
240        return saveState;
241      }
242    
243      public void restoreState(FacesContext context, Object savedState) {
244        Object[] values = (Object[]) savedState;
245        super.restoreState(context, values[0]);
246        rows = (String) values[1];
247        columns = (String) values[2];
248        margin = (String) values[3];
249        marginLeft = (String) values[4];
250        marginRight = (String) values[5];
251        marginTop = (String) values[6];
252        marginBottom = (String) values[7];
253        border = (String) values[8];
254        cellspacing = (String) values[9];
255      }
256    
257      @Override
258      public String getFamily() {
259        return COMPONENT_FAMILY;
260      }
261    
262      @Override
263      public boolean getRendersChildren() {
264        return false;
265      }
266    
267      @Override
268      public void encodeChildren(FacesContext context)
269          throws IOException {
270        // do nothing here
271      }
272    
273      @Override
274      public void encodeChildrenOfComponent(
275          FacesContext facesContext, UIComponent component) throws IOException {
276        super.encodeChildrenOfComponent(facesContext, component);
277        clearRows();
278      }
279    
280      private void clearRows() {
281        layoutRows = null;
282      }
283    
284      public int getColumnCount() {
285        return getColumnLayout().getSize();
286      }
287    
288      public List<Row> ensureRows() {
289        if (layoutRows == null) {
290          layoutRows = createRows();
291        }
292        return layoutRows;
293      }
294    
295      private List<Row> createRows() {
296        List<Row> rows = new ArrayList<Row>();
297        int columnCount = getColumnCount();
298        List<UIComponent> children
299            = LayoutUtil.addChildren(new ArrayList<UIComponent>(), getParent());
300    
301        for (UIComponent component : children) {
302          int spanX = getSpanX(component);
303          int spanY = getSpanY(component);
304    
305          int r = nextFreeRow(rows);
306          if (r == rows.size()) {
307            rows.add(new Row(columnCount));
308          }
309          int c = rows.get(r).nextFreeColumn();
310          rows.get(r).addControl(component, spanX);
311          rows.get(r).fill(c + 1, c + spanX, component.isRendered());
312    
313          for (int i = r + 1; i < r + spanY; i++) {
314    
315            if (i == rows.size()) {
316              rows.add(new Row(columnCount));
317            }
318            rows.get(i).fill(c, c + spanX, component.isRendered());
319          }
320        }
321        return rows;
322      }
323    
324      private int nextFreeRow(List rows) {
325        int i = 0;
326        for (; i < rows.size(); i++) {
327          if (((Row) rows.get(i)).nextFreeColumn() != -1) {
328            return i;
329          }
330        }
331        return i;
332      }
333    
334      public static int getSpanX(UIComponent component) {
335        return ComponentUtil.getIntAttribute(
336            component, ATTR_SPAN_X, 1);
337      }
338    
339      public static int getSpanY(UIComponent component) {
340        return ComponentUtil.getIntAttribute(
341            component, ATTR_SPAN_Y, 1);
342      }
343    
344      public boolean isIgnoreFree() {
345        return ignoreFree;
346      }
347    
348      public void setIgnoreFree(boolean ignoreFree) {
349        this.ignoreFree = ignoreFree;
350      }
351    
352      public static class Row implements Serializable {
353        private static final long serialVersionUID = 1511693677519052045L;
354        private int columns;
355        private List cells;
356        private boolean hidden;
357    
358        public Row(int columns) {
359          setColumns(columns);
360        }
361    
362        private void addControl(UIComponent component, int spanX) {
363    
364          int i = nextFreeColumn();
365    
366          cells.set(i, component);
367          fill(i + 1, i + spanX, component.isRendered());
368        }
369    
370        private void fill(int start, int end, boolean rendered) {
371    
372          if (end > columns) {
373            LOG.error("Error in Jsp (end > columns). "
374                + "Try to insert more spanX as possible.");
375            LOG.error("start:   " + start);
376            LOG.error("end:     " + end);
377            LOG.error("columns: " + columns);
378            LOG.error("Actual cells:");
379            for (Object component : cells) {
380              if (component instanceof UIComponent) {
381                LOG.error("Cell-ID: " + ((UIComponent) component).getId()
382                    + " " + ((UIComponent) component).getRendererType());
383              } else {
384                LOG.error("Cell:    " + component); // e.g. marker
385              }
386            }
387    
388            end = columns; // fix the "end" parameter to continue the processing.
389          }
390    
391          for (int i = start; i < end; i++) {
392            cells.set(i, new Marker(USED, rendered));
393          }
394        }
395    
396        private int nextFreeColumn() {
397          for (int i = 0; i < columns; i++) {
398            if (FREE.equals(cells.get(i))) {
399              return i;
400            }
401          }
402          return -1;
403        }
404    
405        public List getElements() {
406          return cells;
407        }
408    
409        public int getColumns() {
410          return columns;
411        }
412    
413        private void setColumns(int columns) {
414          this.columns = columns;
415          cells = new ArrayList(columns);
416          for (int i = 0; i < columns; i++) {
417            cells.add(FREE);
418          }
419        }
420    
421        public boolean isHidden() {
422          return hidden;
423        }
424    
425        public void setHidden(boolean hidden) {
426          this.hidden = hidden;
427        }
428      }
429    
430      public static class Marker implements Serializable {
431        private static final long serialVersionUID = 2505999420762504893L;
432        private final String name;
433        private boolean rendered;
434    
435        private Marker(String name) {
436          this.name = name;
437        }
438    
439        public Marker(String name, boolean rendered) {
440          this.name = name;
441          this.rendered = rendered;
442        }
443    
444        @Override
445        public String toString() {
446          return name;
447        }
448    
449        public boolean isRendered() {
450          return rendered;
451        }
452      }
453    }