1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.layout;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 import org.apache.myfaces.tobago.layout.LayoutTokens;
25 import org.apache.myfaces.tobago.layout.Orientation;
26 import org.apache.myfaces.tobago.layout.RelativeLayoutToken;
27
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31
32 public class Grid {
33
34 private static final Logger LOG = LoggerFactory.getLogger(Grid.class);
35
36
37
38
39
40 private List<Cell> cells;
41
42 private BankHead[] columnHeads;
43 private BankHead[] rowHeads;
44
45 private int columnCount;
46 private int rowCount;
47
48 private int columnCursor;
49 private int rowCursor;
50
51 private boolean columnOverflow;
52 private boolean rowOverflow;
53
54 private List<Integer> errorIndexes;
55
56 public Grid(LayoutTokens columns, LayoutTokens rows) {
57 assert columns.getSize() > 0;
58 assert rows.getSize() > 0;
59
60 this.columnCount = columns.getSize();
61 this.rowCount = rows.getSize();
62
63 this.columnHeads = new BankHead[columnCount];
64 for (int i = 0; i < columnCount; i++) {
65 columnHeads[i] = new BankHead(columns.get(i));
66 }
67 this.rowHeads = new BankHead[rowCount];
68 for (int i = 0; i < rowCount; i++) {
69 rowHeads[i] = new BankHead(rows.get(i));
70 }
71 int size = columnCount * rowCount;
72 this.cells = new ArrayList<Cell>(size);
73 for (int i = 0; i < size; i++) {
74 this.cells.add(null);
75 }
76 }
77
78 public void add(OriginCell cell, int columnSpan, int rowSpan) {
79
80 assert columnSpan > 0;
81 assert rowSpan > 0;
82
83 boolean error = false;
84
85 if (columnSpan + columnCursor > columnCount) {
86 LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
87 + "columnSpan=" + columnSpan + " columnCursor=" + columnCursor + " columnCount=" + columnCount);
88 columnSpan = columnCount - columnCursor;
89 error = true;
90 }
91
92 cell.setColumnSpan(columnSpan);
93 cell.setRowSpan(rowSpan);
94
95 for (int i = 1; i < columnSpan; i++) {
96 if (getCell(i + columnCursor, rowCursor) != null) {
97 LOG.warn("The columnSpan is to large for the actual position in the grid. Will be fixed. "
98 + "columnSpan=" + columnSpan + " columnCursor=" + columnCursor + " columnCount=" + columnCount);
99 columnSpan = i - 1;
100 error = true;
101 }
102 }
103
104 for (int j = 0; j < rowSpan; j++) {
105 for (int i = 0; i < columnSpan; i++) {
106 Cell actualCell;
107 if (i == 0 && j == 0) {
108 actualCell = cell;
109 } else {
110 actualCell = new SpanCell(cell, i == 0, j == 0);
111 }
112 assert getCell(i + columnCursor, j + rowCursor) == null : "Position in the cell must be free.";
113 setCell(i + columnCursor, j + rowCursor, actualCell);
114 if (error) {
115 addError(i + columnCursor, j + rowCursor);
116 }
117 }
118 }
119
120 findNextFreeCell();
121 }
122
123 public Cell getCell(int i, int j, Orientation orientation) {
124 return orientation == Orientation.HORIZONTAL ? getCell(i, j) : getCell(j, i);
125 }
126
127 public Cell getCell(int column, int row) {
128 assert column >= 0 && column < columnCount : "column=" + column + " columnCount=" + columnCount;
129 assert row >= 0 : "row=" + row;
130
131 if (row >= rowCount) {
132 return null;
133 } else {
134 return cells.get(column + row * columnCount);
135 }
136 }
137
138 public void setCell(int column, int row, Cell cell) {
139 if (row >= rowCount) {
140 enlarge(row - rowCount + 1);
141 }
142 cells.set(column + row * columnCount, cell);
143 }
144
145 private void findNextFreeCell() {
146 for (; rowCursor < rowCount; rowCursor++) {
147 for (; columnCursor < columnCount; columnCursor++) {
148 if (getCell(columnCursor, rowCursor) == null) {
149 return;
150 }
151 }
152 columnCursor = 0;
153 }
154 }
155
156 public BankHead[] getBankHeads(Orientation orientation) {
157 return orientation == Orientation.HORIZONTAL ? columnHeads : rowHeads;
158 }
159
160 private void enlarge(int newRows) {
161
162
163 for (int i = 0; i < newRows; i++) {
164 for (int j = 0; j < columnCount; j++) {
165 cells.add(null);
166 }
167 }
168
169
170 BankHead[] newRowHeads = new BankHead[rowCount + newRows];
171 System.arraycopy(rowHeads, 0, newRowHeads, 0, rowHeads.length);
172 rowHeads = newRowHeads;
173
174
175 for (int i = rowCount; i < rowCount + newRows; i++) {
176 rowHeads[i] = new BankHead(RelativeLayoutToken.DEFAULT_INSTANCE);
177 }
178
179 rowCount += newRows;
180 }
181
182 public boolean isOverflow(Orientation orientation) {
183 return orientation == Orientation.HORIZONTAL ? columnOverflow : rowOverflow;
184 }
185
186 public void setOverflow(boolean overflow, Orientation orientation) {
187 if (orientation == Orientation.HORIZONTAL) {
188 this.columnOverflow = overflow;
189 } else {
190 this.rowOverflow = overflow;
191 }
192 }
193
194 public boolean isOverflow() {
195 return columnOverflow;
196 }
197
198 public void setColumnOverflow(boolean columnOverflow) {
199 this.columnOverflow = columnOverflow;
200 }
201
202 public boolean isRowOverflow() {
203 return rowOverflow;
204 }
205
206 public void setRowOverflow(boolean rowOverflow) {
207 this.rowOverflow = rowOverflow;
208 }
209
210 public void addError(int i, int j) {
211 if (errorIndexes == null) {
212 errorIndexes = new ArrayList<Integer>();
213 }
214 errorIndexes.add(j * columnCount + i);
215 }
216
217 public boolean hasError(int i, int j) {
218 if (errorIndexes == null) {
219 return false;
220 }
221 return errorIndexes.contains(j * columnCount + i);
222 }
223
224 public int getColumnCount() {
225 return columnCount;
226 }
227
228 public int getRowCount() {
229 return rowCount;
230 }
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 public String gridAsString() {
247
248 StringBuilder builder = new StringBuilder();
249
250
251 for (int i = 0; i < columnCount; i++) {
252 if (i == 0) {
253 if (getCell(i, 0) != null) {
254 builder.append("┏");
255 } else {
256 builder.append("┌");
257 }
258 } else {
259 Cell c = getCell(i - 1, 0);
260 Cell d = getCell(i, 0);
261 if (c == null && d == null) {
262 builder.append("┬");
263 } else {
264 if (connected(c, d)) {
265 builder.append("┯");
266 } else {
267 if (c == null) {
268 builder.append("┲");
269 } else if (d == null) {
270 builder.append("┱");
271 } else {
272 builder.append("┳");
273 }
274 }
275 }
276 }
277 if (getCell(i, 0) != null) {
278 builder.append("━");
279 } else {
280 builder.append("─");
281 }
282
283 }
284 if (getCell(columnCount - 1, 0) != null) {
285 builder.append("┓");
286 } else {
287 builder.append("┐");
288 }
289 builder.append("\n");
290
291 for (int j = 0; j < rowCount; j++) {
292
293
294 if (j != 0) {
295 for (int i = 0; i < columnCount; i++) {
296 if (i == 0) {
297 Cell b = getCell(0, j - 1);
298 Cell d = getCell(0, j);
299 if (b == null && d == null) {
300 builder.append("├");
301 } else {
302 if (connected(b, d)) {
303 builder.append("┠");
304 } else {
305 if (b == null) {
306 builder.append("┢");
307 } else if (d == null) {
308 builder.append("┡");
309 } else {
310 builder.append("┣");
311 }
312 }
313 }
314 } else {
315 Cell a = getCell(i - 1, j - 1);
316 Cell b = getCell(i, j - 1);
317 Cell c = getCell(i - 1, j);
318 Cell d = getCell(i, j);
319
320
321
322 if (connected(a, b)) {
323 if (connected(c, d)) {
324 if (connected(a, c)) {
325 builder.append("┼");
326 } else {
327 builder.append("┿");
328 }
329 } else {
330 builder.append("╈");
331 }
332 } else {
333 if (connected(c, d)) {
334 if (connected(a, c)) {
335 builder.append("╄");
336 } else if (connected(b, d)) {
337 builder.append("╃");
338 } else {
339 builder.append("╇");
340 }
341 } else {
342 if (connected(a, c)) {
343 if (connected(b, d)) {
344 builder.append("╂");
345 } else {
346 builder.append("╊");
347 }
348 } else {
349 if (connected(b, d)) {
350 builder.append("╉");
351 } else {
352 builder.append("╋");
353 }
354 }
355 }
356 }
357 }
358 Cell a = getCell(i, j - 1);
359 Cell c = getCell(i, j);
360 if (connected(a, c)) {
361 builder.append("─");
362 } else {
363 builder.append("━");
364 }
365 }
366 Cell a = getCell(columnCount - 1, j - 1);
367 Cell c = getCell(columnCount - 1, j);
368 if (a == null && c == null) {
369 builder.append("┤");
370 } else {
371 if (connected(a, c)) {
372 builder.append("┨");
373 } else {
374 if (a == null) {
375 builder.append("┪");
376 } else if (c == null) {
377 builder.append("┩");
378 } else {
379 builder.append("┫");
380 }
381 }
382 }
383 builder.append("\n");
384 }
385
386
387 for (int i = 0; i < columnCount; i++) {
388 if (i == 0) {
389 if (getCell(i, j) != null) {
390 builder.append("┃");
391 } else {
392 builder.append("│");
393 }
394 } else {
395 Cell c = getCell(i - 1, j);
396 Cell d = getCell(i, j);
397 if (connected(c, d)) {
398 builder.append("│");
399 } else {
400 builder.append("┃");
401 }
402 }
403 if (hasError(i, j)) {
404 builder.append("✖");
405 } else {
406 if (getCell(i, j) instanceof OriginCell) {
407 builder.append("█");
408 } else if (getCell(i, j) instanceof SpanCell) {
409 if (j == 0) {
410 builder.append("➞");
411 } else {
412 Cell a = getCell(i, j - 1);
413 Cell c = getCell(i, j);
414 if (connected(a, c)) {
415 builder.append("⬇");
416 } else {
417 builder.append("➞");
418 }
419 }
420 } else {
421 builder.append("◌");
422 }
423 }
424 }
425 if (getCell(columnCount - 1, j) != null) {
426 builder.append("┃");
427 } else {
428 builder.append("│");
429 }
430 builder.append("\n");
431 }
432
433
434 for (int i = 0; i < columnCount; i++) {
435 if (i == 0) {
436 if (getCell(0, rowCount - 1) != null) {
437 builder.append("┗");
438 } else {
439 builder.append("└");
440 }
441 } else {
442 Cell a = getCell(i - 1, rowCount - 1);
443 Cell b = getCell(i, rowCount - 1);
444 if (a == null && b == null) {
445 builder.append("┴");
446 } else {
447 if (connected(a, b)) {
448 builder.append("┷");
449 } else {
450 if (a == null) {
451 builder.append("┺");
452 } else if (b == null) {
453 builder.append("┹");
454 } else {
455 builder.append("┻");
456 }
457 }
458 }
459 }
460 if (getCell(i, rowCount - 1) != null) {
461 builder.append("━");
462 } else {
463 builder.append("─");
464 }
465 }
466 if (getCell(columnCount - 1, rowCount - 1) != null) {
467 builder.append("┛");
468 } else {
469 builder.append("┘");
470 }
471 builder.append("\n");
472
473 return builder.toString();
474 }
475
476 @Override
477 public String toString() {
478 StringBuilder builder = new StringBuilder();
479 builder.append(gridAsString());
480 builder.append("columnHeads=");
481 builder.append(Arrays.toString(columnHeads));
482 builder.append("\n");
483 builder.append("rowHeads=");
484 builder.append(Arrays.toString(rowHeads));
485 builder.append("\n");
486 return builder.toString();
487 }
488
489 private boolean connected(Cell a, Cell b) {
490 if (a == null && b == null) {
491 return true;
492 }
493 if (a == null || b == null) {
494 return false;
495 }
496 return a.getOrigin().equals(b.getOrigin());
497 }
498 }