org.crsh.text.ui.TableLineRenderer Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.text.ui;
import org.crsh.text.LineReader;
import org.crsh.text.LineRenderer;
import org.crsh.text.RenderAppendable;
import org.crsh.text.Style;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
class TableLineRenderer extends LineRenderer {
/** . */
final Layout columnLayout;
/** . */
final Layout rowLayout;
/** . */
final BorderStyle border;
/** . */
final BorderStyle separator;
/** . */
final Overflow overflow;
/** . */
final Style.Composite style;
/** Cell padding left. */
final int leftCellPadding;
/** Cell padding right. */
final int rightCellPadding;
/** . */
private TableRowLineRenderer head;
/** . */
private TableRowLineRenderer tail;
TableLineRenderer(TableElement table) {
this.rowLayout = table.getRowLayout();
this.columnLayout = table.getColumnLayout();
this.border = table.getBorder();
this.style = table.getStyle();
this.separator = table.getSeparator();
this.overflow = table.getOverflow();
this.leftCellPadding = table.getLeftCellPadding();
this.rightCellPadding = table.getRightCellPadding();
//
for (RowElement row : table.getRows()) {
if (head == null) {
head = tail = new TableRowLineRenderer(this, row);
} else {
tail = tail.add(new TableRowLineRenderer(this, row));
}
}
}
private int getMaxColSize() {
int n = 0;
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
n = Math.max(n, row.getColsSize());
}
return n;
}
@Override
public int getMinWidth() {
int minWidth = 0;
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
minWidth = Math.max(minWidth, row.getMinWidth());
}
return minWidth + (border != null ? 2 : 0);
}
@Override
public int getActualWidth() {
int actualWidth = 0;
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
actualWidth = Math.max(actualWidth, row.getActualWidth());
}
return actualWidth + (border != null ? 2 : 0);
}
@Override
public int getActualHeight(int width) {
if (border != null) {
width -= 2;
}
int actualHeight = 0;
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
actualHeight += row.getActualHeight(width);
}
if (border != null) {
actualHeight += 2;
}
return actualHeight;
}
@Override
public int getMinHeight(int width) {
return border != null ? 2 : 0;
}
@Override
public LineReader reader(int width) {
return reader(width, 0);
}
@Override
public LineReader reader(final int width, final int height) {
int len = getMaxColSize();
int[] eltWidths = new int[len];
int[] eltMinWidths = new int[len];
// Compute each column as is
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
for (int i = 0;i < row.getCols().size();i++) {
LineRenderer renderable = row.getCols().get(i);
eltWidths[i] = Math.max(eltWidths[i], renderable.getActualWidth() + row.row.leftCellPadding + row.row.rightCellPadding);
eltMinWidths[i] = Math.max(eltMinWidths[i], renderable.getMinWidth() + row.row.leftCellPadding + row.row.rightCellPadding);
}
}
// Note that we may have a different widths != eltWidths according to the layout algorithm
final int[] widths = columnLayout.compute(separator != null, width - (border != null ? 2 : 0), eltWidths, eltMinWidths);
//
if (widths != null) {
// Compute new widths array
final AtomicInteger effectiveWidth = new AtomicInteger();
if (border != null) {
effectiveWidth.addAndGet(2);
}
for (int i = 0;i < widths.length;i++) {
effectiveWidth.addAndGet(widths[i]);
if (separator != null) {
if (i > 0) {
effectiveWidth.addAndGet(1);
}
}
}
//
final int[] heights;
if (height > 0) {
// Apply vertical layout
int size = tail.getSize();
int[] actualHeights = new int[size];
int[] minHeights = new int[size];
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
actualHeights[row.getIndex()] = row.getActualHeight(widths);
minHeights[row.getIndex()] = row.getMinHeight(widths);
}
heights = rowLayout.compute(false, height - (border != null ? 2 : 0), actualHeights, minHeights);
if (heights == null) {
return null;
}
} else {
heights = new int[tail.getSize()];
Arrays.fill(heights, -1);
}
//
return new LineReader() {
/** . */
TableRowReader rHead = null;
/** . */
TableRowReader rTail = null;
/** . */
int index = 0;
/**
* 0 -> render top
* 1 -> render rows
* 2 -> render bottom
* 3 -> done
*/
int status = border != null ? 0 : 1;
{
// Add all rows
for (TableRowLineRenderer row = head;row != null;row = row.next()) {
if (row.getIndex() < heights.length) {
int[] what;
if (row.getColsSize() == widths.length) {
what = widths;
} else {
// I'm not sure this algorithm is great
// perhaps the space should be computed or some kind of merge
// that respect the columns should be done
// Redistribute space among columns
what = new int[row.getColsSize()];
for (int j = 0;j < widths.length;j++) {
what[j % what.length] += widths[j];
}
// Remove zero length columns to avoid issues
int end = what.length;
while (end > 0 && what[end - 1] == 0) {
end--;
}
//
if (end != what.length) {
what = Arrays.copyOf(what, end);
}
}
TableRowReader next = row.renderer(what, heights[row.getIndex()]);
if (rHead == null) {
rHead = rTail = next;
} else {
rTail = rTail.add(next);
}
} else {
break;
}
}
}
public boolean hasLine() {
switch (status) {
case 0:
case 2:
return true;
case 1:
while (rHead != null) {
if (rHead.hasLine()) {
return true;
} else {
rHead = rHead.next();
}
}
// Update status according to height
if (height > 0) {
if (border == null) {
if (index == height) {
status = 3;
}
} else {
if (index == height - 1) {
status = 2;
}
}
} else {
if (border != null) {
status = 2;
} else {
status = 3;
}
}
//
return status < 3;
default:
return false;
}
}
public void renderLine(RenderAppendable to) {
if (!hasLine()) {
throw new IllegalStateException();
}
switch (status) {
case 0:
case 2: {
to.styleOff();
to.append(border.corner);
for (int i = 0;i < widths.length;i++) {
if (widths[i] > 0) {
if (separator != null && i > 0) {
to.append(border.horizontal);
}
for (int j = 0;j < widths[i];j++) {
to.append(border.horizontal);
}
}
}
to.append(border.corner);
to.styleOn();
for (int i = width - effectiveWidth.get();i > 0;i--) {
to.append(' ');
}
status++;
break;
}
case 1: {
//
boolean sep = rHead != null && rHead.isSeparator();
if (border != null) {
to.styleOff();
to.append(sep ? border.corner : border.vertical);
to.styleOn();
}
//
if (style != null) {
to.enterStyle(style);
}
//
if (rHead != null) {
// Render row
rHead.renderLine(to);
} else {
// Vertical padding
for (int i = 0;i < widths.length;i++) {
if (separator != null && i > 0) {
to.append(separator.vertical);
}
for (int j = 0;j < widths[i];j++) {
to.append(' ');
}
}
}
//
if (style != null) {
to.leaveStyle();
}
//
if (border != null) {
to.styleOff();
to.append(sep ? border.corner : border.vertical);
to.styleOn();
}
// Padding
for (int i = width - effectiveWidth.get();i > 0;i--) {
to.append(' ');
}
break;
}
default:
throw new AssertionError();
}
// Increase vertical index
index++;
}
};
} else {
return LineRenderer.NULL.reader(width);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy