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 }