001 /*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020 package org.crsh.text.ui;
021
022 import org.crsh.text.LineReader;
023 import org.crsh.text.RenderAppendable;
024 import org.crsh.text.Renderer;
025 import org.crsh.text.Style;
026
027 import java.util.ArrayList;
028 import java.util.Arrays;
029 import java.util.LinkedList;
030 import java.util.List;
031
032 class TableRenderer extends Renderer {
033
034 /** . */
035 private final List<RowRenderer> rows;
036
037 /** . */
038 private final ColumnLayout layout;
039
040 /** . */
041 private final Border border;
042
043 /** . */
044 private final Style.Composite style;
045
046 /** . */
047 private final Integer height;
048
049 TableRenderer(TableElement table) {
050
051 //
052 List<RowRenderer> rows = new ArrayList<RowRenderer>(table.getRows().size());
053 for (RowElement row : table.getRows()) {
054 rows.add(row.renderer());
055 }
056
057 //
058 this.rows = rows;
059 this.layout = table.getLayout();
060 this.border = table.getBorder();
061 this.style = table.getStyle();
062 this.height = table.getHeight();
063 }
064
065 private int getMaxColSize() {
066 int n = 0;
067 for (RowRenderer row : rows) {
068 n = Math.max(n, row.getSize());
069 }
070 return n;
071 }
072
073 @Override
074 public int getMinWidth() {
075 int width = 0;
076 for (RowRenderer row : rows) {
077 width = Math.max(width, row.getMinWidth());
078 }
079 return width;
080 }
081
082 @Override
083 public int getActualWidth() {
084 int width = 0;
085 for (RowRenderer row : rows) {
086 width = Math.max(width, row.getActualWidth());
087 }
088 return width;
089 }
090
091 @Override
092 public LineReader renderer(final int width) {
093
094 int len = getMaxColSize();
095 int[] eltWidths = new int[len];
096 int[] eltMinWidths = new int[len];
097
098 // Compute each column as is
099 for (RowRenderer row : rows) {
100 for (int i = 0;i < row.getCols().size();i++) {
101 Renderer renderable = row.getCols().get(i);
102 eltWidths[i] = Math.max(eltWidths[i], renderable.getActualWidth());
103 eltMinWidths[i] = Math.max(eltMinWidths[i], renderable.getMinWidth());
104 }
105 }
106
107 // Note that we may have a different widths != eltWidths according to the layout algorithm
108 final int[] widths = layout.compute(border, width, eltWidths, eltMinWidths);
109
110 //
111 if (widths == null) {
112 return new LineReader() {
113 public int getWidth() {
114 return width;
115 }
116 public boolean hasLine() {
117 return false;
118 }
119 public void renderLine(RenderAppendable to) throws IllegalStateException {
120 throw new IllegalStateException();
121 }
122 };
123 } else {
124 final LinkedList<Object> renderers = new LinkedList<Object>();
125
126 // Add all rows
127 boolean prev = false;
128 for (int i = 0;i < rows.size();i++) {
129 RowRenderer row = rows.get(i);
130 if (border != null && (row.isHeader() || i == 0) && !prev) {
131 renderers.add(border);
132 }
133
134 //
135 int[] bilto = layout.compute(
136 border,
137 width,
138 Arrays.copyOf(eltWidths, row.getCols().size()),
139 Arrays.copyOf(eltMinWidths, row.getCols().size()));
140
141 //
142 renderers.add(row.renderer(bilto, width, border));
143
144 //
145 if (border != null && (row.isHeader() || i == rows.size() - 1)) {
146 renderers.add(border);
147 prev = true;
148 } else {
149 prev = false;
150 }
151 }
152
153 //
154 final int borderWidth;
155 if (border != null) {
156 int foo = 1;
157 for (int i = 0;i < widths.length;i++) {
158 if (widths[i] >= eltMinWidths[i]) {
159 foo += widths[i] + 1;
160 }
161 }
162 borderWidth = foo;
163 } else {
164 // Will not be used
165 borderWidth = 0;
166 }
167
168 //
169 return new LineReader() {
170
171 /** The current height. */
172 int height = 0;
173
174 public boolean hasLine() {
175 if (TableRenderer.this.height != null && height >= TableRenderer.this.height) {
176 return false;
177 } else {
178 while (renderers.size() > 0) {
179 Object first = renderers.peekFirst();
180 if (first instanceof LineReader) {
181 if (((LineReader)first).hasLine()) {
182 return true;
183 } else {
184 renderers.removeFirst();
185 }
186 } else {
187 return true;
188 }
189 }
190 return false;
191 }
192 }
193 public void renderLine(RenderAppendable to) {
194 if (!hasLine()) {
195 throw new IllegalStateException();
196 }
197 while (renderers.size() > 0) {
198 Object first = renderers.peek();
199 if (first instanceof LineReader) {
200 if (((LineReader)first).hasLine()) {
201 if (style != null) {
202 to.enterStyle(style);
203 ((LineReader)first).renderLine(to);
204 to.leaveStyle();
205 } else {
206 ((LineReader)first).renderLine(to);
207 }
208 break;
209 } else {
210 renderers.removeFirst();
211 }
212 } else {
213 Border border = (Border)first;
214 renderers.removeFirst();
215 to.styleOff();
216 to.append(border.corner);
217 for (int i = 0; i < borderWidth - 2;++i ) {
218 to.append(border.horizontal);
219 }
220 to.append(border.corner);
221 for (int i = borderWidth;i < width;i++) {
222 to.append(' ');
223 }
224 to.styleOn();
225 break;
226 }
227 }
228 height++;
229 }
230 };
231 }
232 }
233 }