View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.internal.layout;
21  
22  import org.apache.myfaces.tobago.layout.LayoutTokens;
23  import org.apache.myfaces.tobago.layout.RelativeLayoutToken;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  public class Grid {
31  
32    private static final Logger LOG = LoggerFactory.getLogger(Grid.class);
33  
34    // TODO: check if it is faster with arrays.
35    /**
36     * The rectangular data as a 1-dim list
37     */
38    private List<Cell> cells;
39  
40    private LayoutTokens columns;
41    private LayoutTokens rows;
42  
43    private int columnCount;
44    private int rowCount;
45  
46    private int columnCursor;
47    private int rowCursor;
48  
49    private List<Integer> errorIndexes;
50  
51    public Grid(final LayoutTokens columns, final LayoutTokens rows) {
52      assert columns.getSize() > 0;
53      assert rows.getSize() > 0;
54  
55      this.columnCount = columns.getSize();
56      this.rowCount = rows.getSize();
57  
58      this.columns = columns;
59      this.rows = rows;
60  
61      final int size = columnCount * rowCount;
62      this.cells = new ArrayList<Cell>(size);
63      for (int i = 0; i < size; i++) {
64        this.cells.add(null);
65      }
66    }
67  
68    public void add(final OriginCell cell, int columnSpan, final int rowSpan) {
69  
70      assert columnSpan > 0;
71      assert rowSpan > 0;
72  
73      boolean error = false;
74  
75      if (columnSpan + columnCursor > columnCount) {
76        LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
77            + "columnSpan='" + columnSpan + "' columnCursor='" + columnCursor + "' columnCount='" + columnCount + "'");
78        columnSpan = columnCount - columnCursor;
79        error = true;
80      }
81  
82      cell.setColumnSpan(columnSpan);
83      cell.setRowSpan(rowSpan);
84  
85      for (int i = 1; i < columnSpan; i++) {
86        if (getCell(i + columnCursor, rowCursor) != null) {
87          LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
88              + "columnSpan='" + columnSpan + "' columnCursor='" + columnCursor + "' columnCount='" + columnCount + "'");
89          columnSpan = i - 1;
90          error = true;
91        }
92      }
93  
94      for (int j = 0; j < rowSpan; j++) {
95        for (int i = 0; i < columnSpan; i++) {
96          final Cell actualCell;
97          if (i == 0 && j == 0) {
98            actualCell = cell;
99          } else {
100           actualCell = new SpanCell(cell, i == 0, j == 0);
101         }
102         assert getCell(i + columnCursor, j + rowCursor) == null : "Position in the cell must be free.";
103         setCell(i + columnCursor, j + rowCursor, actualCell);
104         if (error) {
105           addError(i + columnCursor, j + rowCursor);
106         }
107       }
108     }
109 
110     findNextFreeCell();
111   }
112 
113   public Cell getCell(final int column, final int row) {
114     assert column >= 0 && column < columnCount : "column=" + column + " columnCount=" + columnCount;
115     assert row >= 0 : "row=" + row;
116 
117     if (row >= rowCount) {
118       return null;
119     } else {
120       return cells.get(column + row * columnCount);
121     }
122   }
123 
124   public void setCell(final int column, final int row, final Cell cell) {
125     if (row >= rowCount) {
126       enlarge(row - rowCount + 1);
127     }
128     cells.set(column + row * columnCount, cell);
129   }
130 
131   private void findNextFreeCell() {
132     for (; rowCursor < rowCount; rowCursor++) {
133       for (; columnCursor < columnCount; columnCursor++) {
134         if (getCell(columnCursor, rowCursor) == null) {
135           return;
136         }
137       }
138       columnCursor = 0;
139     }
140   }
141 
142   public LayoutTokens getColumns() {
143     return columns;
144   }
145 
146   public LayoutTokens getRows() {
147     return rows;
148   }
149 
150   private void enlarge(final int newRows) {
151     
152     // process cells
153     for (int i = 0; i < newRows; i++) {
154       for (int j = 0; j < columnCount; j++) {
155         cells.add(null);
156       }
157     }
158 
159     // process heads
160     for (int i = rowCount; i < rowCount + newRows; i++) {
161       rows.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
162     }
163 
164     rowCount += newRows;
165   }
166 
167   public void addError(final int i, final int j) {
168     if (errorIndexes == null) {
169       errorIndexes = new ArrayList<Integer>();
170     }
171     errorIndexes.add(j * columnCount + i);
172   }
173 
174   public boolean hasError(final int i, final int j) {
175     if (errorIndexes == null) {
176       return false;
177     }
178     return errorIndexes.contains(j * columnCount + i);
179   }
180 
181   public int getColumnCount() {
182     return columnCount;
183   }
184 
185   public int getRowCount() {
186     return rowCount;
187   }
188 
189   /**
190    * Prints the state of the grid as an Unicode shape like this:
191    * ┏━┳━┳━┳━┯━┓
192    * ┃█┃█┃█┃█│➞┃
193    * ┠─╂─╊━╇━┿━┫
194    * ┃⬇┃⬇┃█│➞│➞┃
195    * ┣━╉─╊━╈━╈━┫
196    * ┃█┃⬇┃█┃█┃█┃
197    * ┣━╇━╇━╉─╊━┩
198    * ┃█│➞│➞┃⬇┃◌│
199    * ┡━┿━┿━╉─╂─┤
200    * │◌│◌│◌┃⬇┃◌│
201    * └─┴─┴─┺━┹─┘
202    */
203   public String gridAsString() {
204 
205     final StringBuilder builder = new StringBuilder();
206 
207     // top of grid
208     for (int i = 0; i < columnCount; i++) {
209       if (i == 0) {
210         if (getCell(i, 0) != null) {
211           builder.append("┏");
212         } else {
213           builder.append("┌");
214         }
215       } else {
216         final Cell c = getCell(i - 1, 0);
217         final Cell d = getCell(i, 0);
218         if (c == null && d == null) {
219           builder.append("┬");
220         } else {
221           if (connected(c, d)) {
222             builder.append("┯");
223           } else {
224             if (c == null) {
225               builder.append("┲");
226             } else if (d == null) {
227               builder.append("┱");
228             } else {
229               builder.append("┳");
230             }
231           }
232         }
233       }
234       if (getCell(i, 0) != null) {
235         builder.append("━");
236       } else {
237         builder.append("─");
238       }
239 
240     }
241     if (getCell(columnCount - 1, 0) != null) {
242       builder.append("┓");
243     } else {
244       builder.append("┐");
245     }
246     builder.append("\n");
247 
248     for (int j = 0; j < rowCount; j++) {
249 
250       // between the cells
251       if (j != 0) {
252         for (int i = 0; i < columnCount; i++) {
253           if (i == 0) {
254             final Cell b = getCell(0, j - 1);
255             final Cell d = getCell(0, j);
256             if (b == null && d == null) {
257               builder.append("├");
258             } else {
259               if (connected(b, d)) {
260                 builder.append("┠");
261               } else {
262                 if (b == null) {
263                   builder.append("┢");
264                 } else if (d == null) {
265                   builder.append("┡");
266                 } else {
267                   builder.append("┣");
268                 }
269               }
270             }
271           } else {
272             final Cell a = getCell(i - 1, j - 1);
273             final Cell b = getCell(i, j - 1);
274             final Cell c = getCell(i - 1, j);
275             final Cell d = getCell(i, j);
276 //            a│b
277 //            ─┼─
278 //            c│d
279             if (connected(a, b)) {
280               if (connected(c, d)) {
281                 if (connected(a, c)) {
282                   builder.append("┼");
283                 } else {
284                   builder.append("┿");
285                 }
286               } else {
287                 builder.append("╈");
288               }
289             } else {
290               if (connected(c, d)) {
291                 if (connected(a, c)) {
292                   builder.append("╄");
293                 } else if (connected(b, d)) {
294                   builder.append("╃");
295                 } else {
296                   builder.append("╇");
297                 }
298               } else {
299                 if (connected(a, c)) {
300                   if (connected(b, d)) {
301                     builder.append("╂");
302                   } else {
303                     builder.append("╊");
304                   }
305                 } else {
306                   if (connected(b, d)) {
307                     builder.append("╉");
308                   } else {
309                     builder.append("╋");
310                   }
311                 }
312               }
313             }
314           }
315           final Cell a = getCell(i, j - 1);
316           final Cell c = getCell(i, j);
317           if (connected(a, c)) {
318             builder.append("─");
319           } else {
320             builder.append("━");
321           }
322         }
323         final Cell a = getCell(columnCount - 1, j - 1);
324         final Cell c = getCell(columnCount - 1, j);
325         if (a == null && c == null) {
326           builder.append("┤");
327         } else {
328           if (connected(a, c)) {
329             builder.append("┨");
330           } else {
331             if (a == null) {
332               builder.append("┪");
333             } else if (c == null) {
334               builder.append("┩");
335             } else {
336               builder.append("┫");
337             }
338           }
339         }
340         builder.append("\n");
341       }
342 
343       // cell
344       for (int i = 0; i < columnCount; i++) {
345         if (i == 0) {
346           if (getCell(i, j) != null) {
347             builder.append("┃");
348           } else {
349             builder.append("│");
350           }
351         } else {
352           final Cell c = getCell(i - 1, j);
353           final Cell d = getCell(i, j);
354           if (connected(c, d)) {
355             builder.append("│");
356           } else {
357             builder.append("┃");
358           }
359         }
360         if (hasError(i, j)) {
361           builder.append("✖"); //↯
362         } else {
363           if (getCell(i, j) instanceof OriginCell) {
364             builder.append("█");
365           } else if (getCell(i, j) instanceof SpanCell) {
366             if (j == 0) {
367               builder.append("➞");
368             } else {
369               final Cell a = getCell(i, j - 1);
370               final Cell c = getCell(i, j);
371               if (connected(a, c)) {
372                 builder.append("⬇");
373               } else {
374                 builder.append("➞");
375               }
376             }
377           } else {
378             builder.append("◌");
379           }
380         }
381       }
382       if (getCell(columnCount - 1, j) != null) {
383         builder.append("┃");
384       } else {
385         builder.append("│");
386       }
387       builder.append("\n");
388     }
389 
390     //last bottom
391     for (int i = 0; i < columnCount; i++) {
392       if (i == 0) {
393         if (getCell(0, rowCount - 1) != null) {
394           builder.append("┗");
395         } else {
396           builder.append("└");
397         }
398       } else {
399         final Cell a = getCell(i - 1, rowCount - 1);
400         final Cell b = getCell(i, rowCount - 1);
401         if (a == null && b == null) {
402           builder.append("┴");
403         } else {
404           if (connected(a, b)) {
405             builder.append("┷");
406           } else {
407             if (a == null) {
408               builder.append("┺");
409             } else if (b == null) {
410               builder.append("┹");
411             } else {
412               builder.append("┻");
413             }
414           }
415         }
416       }
417       if (getCell(i, rowCount - 1) != null) {
418         builder.append("━");
419       } else {
420         builder.append("─");
421       }
422     }
423     if (getCell(columnCount - 1, rowCount - 1) != null) {
424       builder.append("┛");
425     } else {
426       builder.append("┘");
427     }
428     builder.append("\n");
429 
430     return builder.toString();
431   }
432 
433   @Override
434   public String toString() {
435     return gridAsString() + "columns=" + columns + '\n' + "rows=" + rows + "\n";
436   }
437 
438   private boolean connected(final Cell a, final Cell b) {
439     if (a == null && b == null) {
440       return true;
441     }
442     if (a == null || b == null) {
443       return false;
444     }
445     return a.getOrigin().equals(b.getOrigin());
446   }
447 }