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 }