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, final int columnSpan, final int rowSpan) {
69  
70      assert columnSpan > 0;
71      assert rowSpan > 0;
72  
73      int iterator = columnSpan;
74      boolean error = false;
75  
76      if (iterator + columnCursor > columnCount) {
77        LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
78            + "columnSpan='" + iterator + "' columnCursor='" + columnCursor + "' columnCount='" + columnCount + "'");
79        iterator = columnCount - columnCursor;
80        error = true;
81      }
82  
83      cell.setColumnSpan(iterator);
84      cell.setRowSpan(rowSpan);
85  
86      for (int i = 1; i < iterator; i++) {
87        if (getCell(i + columnCursor, rowCursor) != null) {
88          LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
89              + "columnSpan='" + iterator + "' columnCursor='" + columnCursor + "' columnCount='" + columnCount + "'");
90          iterator = i - 1;
91          error = true;
92        }
93      }
94  
95      for (int j = 0; j < rowSpan; j++) {
96        for (int i = 0; i < iterator; i++) {
97          final Cell actualCell;
98          if (i == 0 && j == 0) {
99            actualCell = cell;
100         } else {
101           actualCell = new SpanCell(cell, i == 0, j == 0);
102         }
103         assert getCell(i + columnCursor, j + rowCursor) == null : "Position in the cell must be free.";
104         setCell(i + columnCursor, j + rowCursor, actualCell);
105         if (error) {
106           addError(i + columnCursor, j + rowCursor);
107         }
108       }
109     }
110 
111     findNextFreeCell();
112   }
113 
114   public Cell getCell(final int column, final int row) {
115     assert column >= 0 && column < columnCount : "column=" + column + " columnCount=" + columnCount;
116     assert row >= 0 : "row=" + row;
117 
118     if (row >= rowCount) {
119       return null;
120     } else {
121       return cells.get(column + row * columnCount);
122     }
123   }
124 
125   public void setCell(final int column, final int row, final Cell cell) {
126     if (row >= rowCount) {
127       enlarge(row - rowCount + 1);
128     }
129     cells.set(column + row * columnCount, cell);
130   }
131 
132   private void findNextFreeCell() {
133     for (; rowCursor < rowCount; rowCursor++) {
134       for (; columnCursor < columnCount; columnCursor++) {
135         if (getCell(columnCursor, rowCursor) == null) {
136           return;
137         }
138       }
139       columnCursor = 0;
140     }
141   }
142 
143   public LayoutTokens getColumns() {
144     return columns;
145   }
146 
147   public LayoutTokens getRows() {
148     return rows;
149   }
150 
151   private void enlarge(final int newRows) {
152     
153     // process cells
154     for (int i = 0; i < newRows; i++) {
155       for (int j = 0; j < columnCount; j++) {
156         cells.add(null);
157       }
158     }
159 
160     // process heads
161     for (int i = rowCount; i < rowCount + newRows; i++) {
162       rows.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
163     }
164 
165     rowCount += newRows;
166   }
167 
168   public void addError(final int i, final int j) {
169     if (errorIndexes == null) {
170       errorIndexes = new ArrayList<Integer>();
171     }
172     errorIndexes.add(j * columnCount + i);
173   }
174 
175   public boolean hasError(final int i, final int j) {
176     if (errorIndexes == null) {
177       return false;
178     }
179     return errorIndexes.contains(j * columnCount + i);
180   }
181 
182   public int getColumnCount() {
183     return columnCount;
184   }
185 
186   public int getRowCount() {
187     return rowCount;
188   }
189 
190   /**
191    * Prints the state of the grid as an Unicode shape like this:
192    * ┏━┳━┳━┳━┯━┓
193    * ┃█┃█┃█┃█│➞┃
194    * ┠─╂─╊━╇━┿━┫
195    * ┃⬇┃⬇┃█│➞│➞┃
196    * ┣━╉─╊━╈━╈━┫
197    * ┃█┃⬇┃█┃█┃█┃
198    * ┣━╇━╇━╉─╊━┩
199    * ┃█│➞│➞┃⬇┃◌│
200    * ┡━┿━┿━╉─╂─┤
201    * │◌│◌│◌┃⬇┃◌│
202    * └─┴─┴─┺━┹─┘
203    */
204   public String gridAsString() {
205 
206     final StringBuilder builder = new StringBuilder();
207 
208     // top of grid
209     for (int i = 0; i < columnCount; i++) {
210       if (i == 0) {
211         if (getCell(i, 0) != null) {
212           builder.append("┏");
213         } else {
214           builder.append("┌");
215         }
216       } else {
217         final Cell c = getCell(i - 1, 0);
218         final Cell d = getCell(i, 0);
219         if (c == null && d == null) {
220           builder.append("┬");
221         } else {
222           if (connected(c, d)) {
223             builder.append("┯");
224           } else {
225             if (c == null) {
226               builder.append("┲");
227             } else if (d == null) {
228               builder.append("┱");
229             } else {
230               builder.append("┳");
231             }
232           }
233         }
234       }
235       if (getCell(i, 0) != null) {
236         builder.append("━");
237       } else {
238         builder.append("─");
239       }
240 
241     }
242     if (getCell(columnCount - 1, 0) != null) {
243       builder.append("┓");
244     } else {
245       builder.append("┐");
246     }
247     builder.append("\n");
248 
249     for (int j = 0; j < rowCount; j++) {
250 
251       // between the cells
252       if (j != 0) {
253         for (int i = 0; i < columnCount; i++) {
254           if (i == 0) {
255             final Cell b = getCell(0, j - 1);
256             final Cell d = getCell(0, j);
257             if (b == null && d == null) {
258               builder.append("├");
259             } else {
260               if (connected(b, d)) {
261                 builder.append("┠");
262               } else {
263                 if (b == null) {
264                   builder.append("┢");
265                 } else if (d == null) {
266                   builder.append("┡");
267                 } else {
268                   builder.append("┣");
269                 }
270               }
271             }
272           } else {
273             final Cell a = getCell(i - 1, j - 1);
274             final Cell b = getCell(i, j - 1);
275             final Cell c = getCell(i - 1, j);
276             final Cell d = getCell(i, j);
277 //            a│b
278 //            ─┼─
279 //            c│d
280             if (connected(a, b)) {
281               if (connected(c, d)) {
282                 if (connected(a, c)) {
283                   builder.append("┼");
284                 } else {
285                   builder.append("┿");
286                 }
287               } else {
288                 builder.append("╈");
289               }
290             } else {
291               if (connected(c, d)) {
292                 if (connected(a, c)) {
293                   builder.append("╄");
294                 } else if (connected(b, d)) {
295                   builder.append("╃");
296                 } else {
297                   builder.append("╇");
298                 }
299               } else {
300                 if (connected(a, c)) {
301                   if (connected(b, d)) {
302                     builder.append("╂");
303                   } else {
304                     builder.append("╊");
305                   }
306                 } else {
307                   if (connected(b, d)) {
308                     builder.append("╉");
309                   } else {
310                     builder.append("╋");
311                   }
312                 }
313               }
314             }
315           }
316           final Cell a = getCell(i, j - 1);
317           final Cell c = getCell(i, j);
318           if (connected(a, c)) {
319             builder.append("─");
320           } else {
321             builder.append("━");
322           }
323         }
324         final Cell a = getCell(columnCount - 1, j - 1);
325         final Cell c = getCell(columnCount - 1, j);
326         if (a == null && c == null) {
327           builder.append("┤");
328         } else {
329           if (connected(a, c)) {
330             builder.append("┨");
331           } else {
332             if (a == null) {
333               builder.append("┪");
334             } else if (c == null) {
335               builder.append("┩");
336             } else {
337               builder.append("┫");
338             }
339           }
340         }
341         builder.append("\n");
342       }
343 
344       // cell
345       for (int i = 0; i < columnCount; i++) {
346         if (i == 0) {
347           if (getCell(i, j) != null) {
348             builder.append("┃");
349           } else {
350             builder.append("│");
351           }
352         } else {
353           final Cell c = getCell(i - 1, j);
354           final Cell d = getCell(i, j);
355           if (connected(c, d)) {
356             builder.append("│");
357           } else {
358             builder.append("┃");
359           }
360         }
361         if (hasError(i, j)) {
362           builder.append("✖"); //↯
363         } else {
364           if (getCell(i, j) instanceof OriginCell) {
365             builder.append("█");
366           } else if (getCell(i, j) instanceof SpanCell) {
367             if (j == 0) {
368               builder.append("➞");
369             } else {
370               final Cell a = getCell(i, j - 1);
371               final Cell c = getCell(i, j);
372               if (connected(a, c)) {
373                 builder.append("⬇");
374               } else {
375                 builder.append("➞");
376               }
377             }
378           } else {
379             builder.append("◌");
380           }
381         }
382       }
383       if (getCell(columnCount - 1, j) != null) {
384         builder.append("┃");
385       } else {
386         builder.append("│");
387       }
388       builder.append("\n");
389     }
390 
391     //last bottom
392     for (int i = 0; i < columnCount; i++) {
393       if (i == 0) {
394         if (getCell(0, rowCount - 1) != null) {
395           builder.append("┗");
396         } else {
397           builder.append("└");
398         }
399       } else {
400         final Cell a = getCell(i - 1, rowCount - 1);
401         final Cell b = getCell(i, rowCount - 1);
402         if (a == null && b == null) {
403           builder.append("┴");
404         } else {
405           if (connected(a, b)) {
406             builder.append("┷");
407           } else {
408             if (a == null) {
409               builder.append("┺");
410             } else if (b == null) {
411               builder.append("┹");
412             } else {
413               builder.append("┻");
414             }
415           }
416         }
417       }
418       if (getCell(i, rowCount - 1) != null) {
419         builder.append("━");
420       } else {
421         builder.append("─");
422       }
423     }
424     if (getCell(columnCount - 1, rowCount - 1) != null) {
425       builder.append("┛");
426     } else {
427       builder.append("┘");
428     }
429     builder.append("\n");
430 
431     return builder.toString();
432   }
433 
434   @Override
435   public String toString() {
436     return gridAsString() + "columns=" + columns + '\n' + "rows=" + rows + "\n";
437   }
438 
439   private boolean connected(final Cell a, final Cell b) {
440     if (a == null && b == null) {
441       return true;
442     }
443     if (a == null || b == null) {
444       return false;
445     }
446     return a.getOrigin().equals(b.getOrigin());
447   }
448 }