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    }