introduce a new datastructure to represent the whole table grid
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridArea.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridArea.java
new file mode 100644
index 0000000..ef8e75e
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridArea.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Florian Thienel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author Florian Thienel
+ */
+public class GridArea {
+
+ public final int startRow;
+ public final int startColumn;
+ public final int endRow;
+ public final int endColumn;
+
+ public GridArea(final GridPosition position) {
+ this(position.row, position.column, position.row, position.column);
+ }
+
+ public GridArea(final GridPosition startPosition, final GridPosition endPosition) {
+ this(startPosition.row, startPosition.column, endPosition.row, endPosition.column);
+ }
+
+ public GridArea(final int row, final int column) {
+ this(row, column, row, column);
+ }
+
+ public GridArea(final int startRow, final int startColumn, final int endRow, final int endColumn) {
+ this.startRow = Math.min(startRow, endRow);
+ this.startColumn = Math.min(startColumn, endColumn);
+ this.endRow = Math.max(startRow, endRow);
+ this.endColumn = Math.max(startColumn, endColumn);
+ }
+
+ public boolean contains(final int row, final int column) {
+ return row >= startRow && row <= endRow && column >= startColumn && column <= endColumn;
+ }
+
+ public Collection<GridPosition> positions() {
+ final ArrayList<GridPosition> positions = new ArrayList<GridPosition>();
+ for (int row = startRow; row <= endRow; row += 1) {
+ for (int column = startColumn; column <= endColumn; column += 1) {
+ positions.add(new GridPosition(row, column));
+ }
+ }
+ return positions;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + endColumn;
+ result = prime * result + endRow;
+ result = prime * result + startColumn;
+ result = prime * result + startRow;
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final GridArea other = (GridArea) obj;
+ if (endColumn != other.endColumn) {
+ return false;
+ }
+ if (endRow != other.endRow) {
+ return false;
+ }
+ if (startColumn != other.startColumn) {
+ return false;
+ }
+ if (startRow != other.startRow) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GridArea [startRow=" + startRow + ", startColumn=" + startColumn + ", endRow=" + endRow + ", endColumn=" + endColumn + "]";
+ }
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridPosition.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridPosition.java
new file mode 100644
index 0000000..b1e04e4
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/GridPosition.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Florian Thienel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+/**
+ * @author Florian Thienel
+ */
+public class GridPosition {
+
+ public final int row;
+ public final int column;
+
+ public GridPosition(final int row, final int column) {
+ this.row = row;
+ this.column = column;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + column;
+ result = prime * result + row;
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final GridPosition other = (GridPosition) obj;
+ if (column != other.column) {
+ return false;
+ }
+ if (row != other.row) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GridPosition [row=" + row + ", column=" + column + "]";
+ }
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Table.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Table.java
index 03f61d8..057f591 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Table.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/Table.java
@@ -30,6 +30,7 @@
private final ArrayList<IStructuralBox> children = new ArrayList<IStructuralBox>();
private TableColumnLayout columnLayout = new TableColumnLayout();
+ private TableLayoutGrid layoutGrid = new TableLayoutGrid();
@Override
public void setParent(final IBox parent) {
@@ -152,6 +153,18 @@
return columnLayout;
}
+ public TableLayoutGrid getLayoutGrid() {
+ return layoutGrid;
+ }
+
+ public void setLayoutGrid(final TableLayoutGrid layoutGrid) {
+ if (layoutGrid == null) {
+ this.layoutGrid = new TableLayoutGrid();
+ } else {
+ this.layoutGrid = layoutGrid;
+ }
+ }
+
public void layout(final Graphics graphics) {
layoutColumns(graphics);
layoutChildren(graphics);
@@ -173,6 +186,7 @@
columnLayout = new TableColumnLayout(columnLayout.getParentLayout());
}
TableColumnLayout.addColumnLayoutInformationForChildren(graphics, this, columnLayout);
+ TableLayoutGrid.setupLayoutGrid(graphics, this, layoutGrid);
}
@Override
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableCell.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableCell.java
index cdfad2d..bc583ad 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableCell.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableCell.java
@@ -34,6 +34,9 @@
private String columnName;
private String startColumnName;
private String endColumnName;
+ private int verticalSpan = 1;
+
+ private GridArea gridArea;
private int naturalHeight;
@@ -194,6 +197,22 @@
this.endColumnName = endColumnName;
}
+ public int getVerticalSpan() {
+ return verticalSpan;
+ }
+
+ public void setVerticalSpan(final int verticalSpan) {
+ this.verticalSpan = verticalSpan;
+ }
+
+ public GridArea getGridArea() {
+ return gridArea;
+ }
+
+ public void setGridArea(final GridArea gridArea) {
+ this.gridArea = gridArea;
+ }
+
public int calculateNaturalHeight(final Graphics graphics, final int width) {
naturalHeight = 0;
for (int i = 0; i < children.size(); i += 1) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableLayoutGrid.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableLayoutGrid.java
new file mode 100644
index 0000000..ff07257
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableLayoutGrid.java
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Florian Thienel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Florian Thienel - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.vex.core.internal.boxes;
+
+import java.util.HashMap;
+
+import org.eclipse.vex.core.internal.core.Graphics;
+
+/**
+ * @author Florian Thienel
+ */
+public class TableLayoutGrid {
+
+ private final HashMap<GridPosition, TableCell> grid = new HashMap<GridPosition, TableCell>();
+ private int currentRow = 0;
+ private int nextColumn = 1;
+ private int maxColumn = 0;
+
+ public static void setupLayoutGrid(final Graphics graphics, final IStructuralBox parent, final TableLayoutGrid layoutGrid) {
+ // TODO merge with TableColumnLayout
+ parent.accept(new DepthFirstBoxTraversal<Object>() {
+ @Override
+ public Object visit(final Table box) {
+ if (box == parent) {
+ traverseChildren(box);
+ } else {
+ box.setLayoutGrid(layoutGrid);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final TableRowGroup box) {
+ if (box == parent) {
+ traverseChildren(box);
+ } else {
+ box.setLayoutGrid(layoutGrid);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final TableColumnSpec box) {
+ // TODO Auto-generated method stub
+ return super.visit(box);
+ }
+
+ @Override
+ public Object visit(final TableRow box) {
+ if (box == parent) {
+ layoutGrid.addNextRow();
+ traverseChildren(box);
+ } else {
+ box.setLayoutGrid(layoutGrid);
+ }
+ return null;
+ }
+
+ @Override
+ public Object visit(final TableCell box) {
+ if (box.getStartColumnIndex() > 0 && box.getEndColumnIndex() > 0) {
+ layoutGrid.addCellOnCurrentRow(box, box.getStartColumnIndex(), box.getEndColumnIndex());
+ } else {
+ layoutGrid.addNextCellOnCurrentRow(box);
+ }
+ return null;
+ }
+ });
+ }
+
+ public int getRows() {
+ return currentRow;
+ }
+
+ public int getColumns() {
+ return maxColumn;
+ }
+
+ public int addNextRow() {
+ currentRow += 1;
+ nextColumn = 1;
+ return currentRow;
+ }
+
+ public boolean addCellOnCurrentRow(final TableCell cell, final int column) {
+ final GridArea area = new GridArea(currentRow, column, currentRow + cell.getVerticalSpan() - 1, column);
+ if (isOccupied(area)) {
+ return false;
+ }
+ occupy(area, cell);
+ cell.setGridArea(area);
+ updateNextColumn();
+ return true;
+ }
+
+ public boolean addCellOnCurrentRow(final TableCell cell, final int startColumn, final int endColumn) {
+ final GridArea area = new GridArea(currentRow, startColumn, currentRow + cell.getVerticalSpan() - 1, endColumn);
+ if (isOccupied(area)) {
+ return false;
+ }
+ occupy(area, cell);
+ cell.setGridArea(area);
+ updateNextColumn();
+ return true;
+ }
+
+ public void addNextCellOnCurrentRow(final TableCell cell) {
+ final GridArea area = new GridArea(currentRow, nextColumn, currentRow + cell.getVerticalSpan() - 1, nextColumn);
+ occupy(area, cell);
+ cell.setGridArea(area);
+ updateNextColumn();
+ }
+
+ private void updateNextColumn() {
+ while (isOccupied(new GridPosition(currentRow, nextColumn))) {
+ nextColumn += 1;
+ }
+ }
+
+ private void occupy(final GridArea area, final TableCell cell) {
+ for (final GridPosition position : area.positions()) {
+ grid.put(position, cell);
+ maxColumn = Math.max(maxColumn, position.column);
+ }
+ }
+
+ private boolean isOccupied(final GridArea area) {
+ for (final GridPosition position : area.positions()) {
+ if (isOccupied(position)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isOccupied(final GridPosition position) {
+ return grid.containsKey(position);
+ }
+
+ public TableCell getCell(final GridPosition position) {
+ return grid.get(position);
+ }
+
+ public IStructuralBox getRowChild(final GridPosition position, final TableRow parentRow) {
+ final TableCell cell = grid.get(position);
+ if (cell == null) {
+ return null;
+ }
+
+ return cell.accept(new ParentTraversal<IStructuralBox>() {
+ @Override
+ public IStructuralBox visit(final HorizontalBar box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final List box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final ListItem box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final Paragraph box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final StructuralFrame box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final StructuralNodeReference box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final Table box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final TableCell box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final TableColumnSpec box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final TableRow box) {
+ return null;
+ }
+
+ @Override
+ public IStructuralBox visit(final TableRowGroup box) {
+ return visitStructuralBox(box);
+ }
+
+ @Override
+ public IStructuralBox visit(final VerticalBlock box) {
+ return visitStructuralBox(box);
+ }
+
+ private IStructuralBox visitStructuralBox(final IStructuralBox box) {
+ if (box.getParent() == parentRow) {
+ return box;
+ }
+ return box.getParent().accept(this);
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ for (int row = 1; row <= currentRow; row += 1) {
+ final StringBuilder top = new StringBuilder();
+ final StringBuilder middle = new StringBuilder();
+ final StringBuilder bottom = new StringBuilder();
+ for (int column = 1; column <= maxColumn; column += 1) {
+ final GridPosition position = new GridPosition(row, column);
+ final TableCell cell = grid.get(position);
+ if (cell == null) {
+ top.append(" ");
+ middle.append(" ");
+ bottom.append(" ");
+ } else {
+ final GridArea gridArea = cell.getGridArea();
+ if (row == gridArea.startRow) {
+ top.append("-----");
+ } else {
+ top.append(" ");
+ }
+ if (row == gridArea.endRow) {
+ bottom.append("-----");
+ } else {
+ bottom.append(" ");
+ }
+ if (column == gridArea.startColumn) {
+ middle.append("|");
+ } else {
+ middle.append(" ");
+ }
+ middle.append(Long.toString(cell.hashCode()).substring(0, 3));
+ if (column == gridArea.endColumn) {
+ middle.append("|");
+ } else {
+ middle.append(" ");
+ }
+ }
+ }
+ builder.append(top.toString()).append("\n");
+ builder.append(middle.toString()).append("\n");
+ builder.append(bottom.toString()).append("\n");
+ }
+ return builder.toString();
+ }
+}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRow.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRow.java
index b67c49b..03c7759 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRow.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRow.java
@@ -30,6 +30,7 @@
private final ArrayList<IStructuralBox> children = new ArrayList<IStructuralBox>();
private TableColumnLayout columnLayout = new TableColumnLayout();
+ private TableLayoutGrid layoutGrid = new TableLayoutGrid();
@Override
public void setParent(final IBox parent) {
@@ -152,8 +153,21 @@
}
}
+ public TableLayoutGrid getLayoutGrid() {
+ return layoutGrid;
+ }
+
+ public void setLayoutGrid(final TableLayoutGrid layoutGrid) {
+ if (layoutGrid == null) {
+ this.layoutGrid = new TableLayoutGrid();
+ } else {
+ this.layoutGrid = layoutGrid;
+ }
+ }
+
public void layout(final Graphics graphics) {
TableColumnLayout.addColumnLayoutInformationForChildren(graphics, this, columnLayout);
+ TableLayoutGrid.setupLayoutGrid(graphics, this, layoutGrid);
int cellHeight = 0;
int columnIndex = 1;
for (int i = 0; i < children.size(); i += 1) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRowGroup.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRowGroup.java
index cce9f7d..59cdf10 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRowGroup.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/boxes/TableRowGroup.java
@@ -30,6 +30,7 @@
private final ArrayList<IStructuralBox> children = new ArrayList<IStructuralBox>();
private TableColumnLayout columnLayout = new TableColumnLayout();
+ private TableLayoutGrid layoutGrid = new TableLayoutGrid();
@Override
public void setParent(final IBox parent) {
@@ -148,6 +149,18 @@
this.columnLayout = columnLayout;
}
+ public TableLayoutGrid getLayoutGrid() {
+ return layoutGrid;
+ }
+
+ public void setLayoutGrid(final TableLayoutGrid layoutGrid) {
+ if (layoutGrid == null) {
+ this.layoutGrid = new TableLayoutGrid();
+ } else {
+ this.layoutGrid = layoutGrid;
+ }
+ }
+
public void layout(final Graphics graphics) {
layoutColumns(graphics);
layoutChildren(graphics);
@@ -158,6 +171,7 @@
columnLayout = new TableColumnLayout(columnLayout.getParentLayout());
}
TableColumnLayout.addColumnLayoutInformationForChildren(graphics, this, columnLayout);
+ TableLayoutGrid.setupLayoutGrid(graphics, this, layoutGrid);
}
private void layoutChildren(final Graphics graphics) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/visualization/CssBasedBoxModelBuilder.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/visualization/CssBasedBoxModelBuilder.java
index d9e835d..4c338fb 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/visualization/CssBasedBoxModelBuilder.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/visualization/CssBasedBoxModelBuilder.java
@@ -332,6 +332,9 @@
cell.setStartColumnName(nameStart.getValue());
cell.setEndColumnName(nameEnd.getValue());
}
+
+ final IAttribute moreRows = element.getAttribute("morerows");
+ cell.setVerticalSpan(1 + toInt(moreRows));
} else if ("th".equals(element.getLocalName()) || "td".equals(element.getLocalName())) {
// TODO HTML table
}