/*
* SmartDoc : Ultimate document format based on XML
* Copyright (C) 1998-2001 ASAMI, Tomoharu (asami@zeomtech.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.xmlsmartdoc.SmartDoc;
import java.util.*;
import org.w3c.dom.Element;
import com.AsamiOffice.util.D2Array;
import org.xmlsmartdoc.SmartDoc.adapter.CSVAdapter;
/**
* Table
*
* @since Sep. 29, 1998
* @version Jun. 13, 2001
* @author ASAMI, Tomoharu (asami@zeomtech.com)
*/
public class Table extends FloatingObject {
protected D2Array head_ = new D2Array();
protected D2Array data_ = new D2Array();
protected D2Array foot_ = new D2Array();
protected List colgroups_ = new ArrayList(); // List<Colgroup>
protected List rowgroups_ = new ArrayList(); // List<Colgroup>
protected List cols_ = new ArrayList(); // List<Col>
protected THead thead_;
protected TFoot tfoot_;
protected TBody tbody_;
protected Tnote tnote_;
protected PTable ptable_;
transient protected int ty_ = 0;
transient protected int y_ = 0;
protected String src_;
protected String srctype_;
public Table() {
}
public Table(Element element) {
super(element);
if (sequenceNumber_ == null) {
sequenceNumber_ = new SequenceNumber("table");
}
}
// Content
public int getEntityType() {
return (ENTITY_BLOCK);
}
// Container
public void format() {
super.format();
_buildTable();
_adjustTableHead();
_adjustTableFoot();
_adjustTableData();
_rebuildTHeadTree(); // for index
_rebuildTFootTree(); // for index
_rebuildTDataTree(); // for index
_buildMetaInfo();
_setupData();
if (rowgroups_.size() > 1) {
_buildPTable();
}
}
public Rowgroup[] getRowgroups() {
Rowgroup[] rowgroups = new Rowgroup[rowgroups_.size()];
return ((Rowgroup[])rowgroups_.toArray(rowgroups));
}
public Colgroup[] getColgroups() {
Colgroup[] colgroups = new Colgroup[colgroups_.size()];
return ((Colgroup[])colgroups_.toArray(colgroups));
}
public Col[] getCols() {
Col[] cols = new Col[cols_.size()];
return ((Col[])cols_.toArray(cols));
}
public THead getTHead() {
return (thead_);
}
public TFoot getTFoot() {
return (tfoot_);
}
public TBody getTBody() {
return (tbody_);
}
/**
* This method is available after format().
*/
public int getWidth() {
return (data_.getWidth());
}
/**
* This method is available after format().
*/
public int getHeight() {
return (data_.getHeight());
}
/**
* This method is available after format().
*/
public Tnote getTnote() {
return (tnote_);
}
/**
* This method is available after format().
*/
public PTable getPTable() {
return (ptable_);
}
public D2Array getHeadData() {
if (head_.getHeight() > 0) {
return (head_);
} else {
return (null);
}
}
public D2Array getFootData() {
if (foot_.getHeight() > 0) {
return (foot_);
} else {
return (null);
}
}
public D2Array getBodyData() {
if (data_.getHeight() > 0) {
return (data_);
} else {
return (null);
}
}
/**
* @deprecated
*/
public Th getTh(int x, int y) {
if (x >= head_.getWidth()) {
return (null);
}
if (y >= head_.getHeight()) {
return (null);
}
return ((Th)head_.get(x, y));
}
/**
* @deprecated
*/
public Td getTd(int x, int y) {
if (x >= data_.getWidth()) {
return (null);
}
if (y >= data_.getHeight()) {
return (null);
}
return ((Td)data_.get(x, y));
}
public TrContent getCell(int x, int y) {
if (x >= data_.getWidth()) {
return (null);
}
if (y >= data_.getHeight()) {
return (null);
}
return ((TrContent)data_.get(x, y));
}
public String getClazz(int x, int y) {
TrContent cell = getCell(x, y);
if (cell != null) {
String type = cell.getClazz();
if (type != null) {
return (type);
}
}
Col col = (Col)cols_.get(x);
return (col.getClazz());
}
public String getAlign(int x, int y) {
TrContent cell = getCell(x, y);
if (cell != null) {
String type = cell.getAlign();
if (type != null) {
return (type);
}
}
Col col = (Col)cols_.get(x);
return (col.getAlign());
}
protected void _buildTable() {
Content[] contents = getContents();
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
content.format();
if (content instanceof Rowgroup) {
Rowgroup rowgroup = (Rowgroup)content;
rowgroup.setTable(this);
rowgroups_.add(rowgroup);
} else if (content instanceof Colgroup) {
Colgroup colgroup = (Colgroup)content;
colgroup.setTable(this);
colgroups_.add(colgroup);
} else if (content instanceof THead) {
_buildTHead((THead)content);
} else if (content instanceof TFoot) {
_buildTFoot((TFoot)content);
} else if (content instanceof TBody) {
_buildTBody((TBody)content);
} else if (content instanceof Tnote) {
_buildTnote((Tnote)content);
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
}
/*
protected void _buildTableFromSrc(String src, IAdapter adapter) {
throw (new UnsupportedOperationException());
}
protected void _buildHeadFromSrc(String src, IAdapter adapter) {
if (adapter == null) {
return;
}
SmartDocTableModel model = adapter.getTable(src, null);
_buildHeadFromTableModel(model);
}
protected void _buildHeadFromText(String text, Adapter adapter) {
if (adapter == null) {
return;
}
SmartDocTableModel model = adapter.parseTable(text, null);
_buildHeadFromTableModel(model);
}
protected void _buildHeadFromTableModel(SmartDocTableModel model) {
int width = model.getColumnCount();
int height = model.getRowCount();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
Object head = model.getValueAt(y, x);
if (head != null) {
Th th = new Th(head.toString());
head_.put(x, y, th);
}
}
ty_++;
}
}
*/
protected void _adjustTableHead() {
head_ = _adjustData(head_);
}
protected void _adjustTableFoot() {
foot_ = _adjustData(foot_);
}
protected void _adjustTableData() {
data_ = _adjustData(data_);
}
/**
* adjust position of TrContent using rowspan.
*/
protected D2Array _adjustData(D2Array data) {
D2Array newData = new D2Array();
int width = data.getWidth();
int height = data.getHeight();
int tableWidth = 0; // XXX : more precisely
for (int y = 0;y < height;y++) {
int w = 0;
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell == null) {
w++;
} else {
w += cell.getColSpan();
}
}
tableWidth = Math.max(tableWidth, w);
}
int tableHeight = 0; // XXX : more precisely
for (int x = 0;x < width;x++) {
int h = 0;
for (int y = 0;y < height;y++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell == null) {
h++;
} else {
h += cell.getRowSpan();
}
}
tableHeight = Math.max(tableHeight, h);
}
int[] currentYs = new int[tableWidth];
Arrays.fill(currentYs, 0);
for (int y = 0;y < height;y++) {
int currentX = 0;
for (int x = 0;x < width;x++) {
for (;;) {
if (currentX != tableWidth &&
currentYs[currentX] > y) {
currentX++;
} else {
break;
}
}
if (currentX == tableWidth) {
break;
}
TrContent cell = (TrContent)data.get(x, y); // XXX : TrContent
if (cell == null) {
continue;
}
int rowSpan = cell.getRowSpan();
int colSpan = cell.getColSpan();
newData.put(currentX, y, cell);
int last = currentX + colSpan;
for (int i = currentX;i < last;i++) {
currentYs[i] += rowSpan;
}
currentX += colSpan;
}
}
return (newData);
}
/*
protected void _buildDataFromSrc(String src, Adapter adapter) {
if (adapter == null) {
return;
}
SmartDocTableModel model = adapter.getTable(src, null);
_buildDataFromTableModel(model);
}
protected void _buildDataFromText(String text, Adapter adapter) {
if (adapter == null) {
return;
}
SmartDocTableModel model = adapter.parseTable(text, null);
_buildDataFromTableModel(model);
}
protected void _buildDataFromTableModel(SmartDocTableModel model) {
int width = model.getColumnCount();
int height = model.getRowCount();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
Object data = model.getValueAt(y, x);
if (data != null) {
Td td = new Td(data.toString());
data_.put(x, y, td);
}
}
y_++;
}
}
*/
/**
* adjust position of TrContent using rowspan.
*/
protected void _adjustTableData0() {
D2Array data = new D2Array();
int width = data_.getWidth();
int height = data_.getHeight();
int tableWidth = 0; // XXX : more precisely
for (int y = 0;y < height;y++) {
int w = 0;
for (int x = 0;x < width;x++) {
Td td = (Td)data_.get(x, y);
if (td == null) {
w++;
} else {
w += td.getColSpan();
}
}
tableWidth = Math.max(tableWidth, w);
}
int tableHeight = 0; // XXX : more precisely
for (int x = 0;x < width;x++) {
int h = 0;
for (int y = 0;y < height;y++) {
Td td = (Td)data_.get(x, y);
if (td == null) {
h++;
} else {
h += td.getRowSpan();
}
}
tableHeight = Math.max(tableHeight, h);
}
int[] currentYs = new int[tableWidth];
Arrays.fill(currentYs, 0);
for (int y = 0;y < height;y++) {
int currentX = 0;
for (int x = 0;x < width;x++) {
for (;;) {
if (currentX != tableWidth &&
currentYs[currentX] > y) {
currentX++;
} else {
break;
}
}
if (currentX == tableWidth) {
break;
}
Td td = (Td)data_.get(x, y); // XXX : TrContent
if (td == null) {
continue;
}
int rowSpan = td.getRowSpan();
int colSpan = td.getColSpan();
data.put(currentX, y, td);
int last = currentX + colSpan;
for (int i = currentX;i < last;i++) {
currentYs[i] += rowSpan;
}
currentX += colSpan;
}
}
data_ = data;
//System.out.println(data_);
}
protected void _rebuildTHeadTree() {
if (head_.getWidth() > 0) {
if (thead_ == null) {
thead_ = new THead();
}
_rebuildTree(thead_, head_);
}
}
protected void _rebuildTFootTree() {
if (foot_.getWidth() > 0) {
if (tfoot_ == null) {
tfoot_ = new TFoot();
}
_rebuildTree(tfoot_, foot_);
}
}
protected void _rebuildTDataTree() {
if (data_.getWidth() > 0) {
if (tbody_ == null) {
tbody_ = new TBody();
}
_rebuildTree(tbody_, data_);
}
}
protected void _rebuildTree(Container parent, D2Array data) {
if (parent == null) {
return;
}
parent.clearContents();
if (data == null) {
return;
}
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
Tr tr = null;
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell != null) {
tr = (Tr)cell.getParent();
if (tr != null) {
break;
}
}
}
if (tr == null) {
tr = new Tr();
}
tr.clearContents();
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell != null) {
tr.addContent(cell);
}
}
parent.addContent(tr);
}
}
protected void _buildTHead(THead thead) {
thead_ = thead;
D2Array data = new D2Array();
Content[] contents = thead.getContents();
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Tr) {
_buildRecord((Tr)content, data);
} else if (content instanceof Ul) {
_buildTree((Ul)content, data, Th.class);
//_debugData(data);
data = data.transpose(); // XXX : really append
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell != null) {
int colspan = cell.getColSpan();
int rowspan = cell.getRowSpan();
cell.setColSpan(rowspan);
cell.setRowSpan(colspan);
}
}
}
//_debugData(data);
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
head_ = data;
}
protected void _debugData(D2Array data) {
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell == null) {
System.out.print("null");
} else {
if (cell instanceof Th) {
System.out.print("th[");
} else if (cell instanceof Td) {
System.out.print("td[");
} else {
throw (new InternalError());
}
System.out.print(cell.getColSpan());
System.out.print("/");
System.out.print(cell.getRowSpan());
System.out.print("]");
}
System.out.print(",");
}
System.out.println();
}
}
protected void _buildRecord(Tr tr, D2Array data) {
Content[] contents = tr.getContents();
int x = 0;
int y = data.getHeight(); // append
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof TrContent) {
data.put(x++, y, content);
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
}
protected void _buildTree(Ul ul, D2Array data, Class cellType) {
_buildTree(ul, 0, data.getHeight(), data, cellType);
// adjust tree
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
TrContent prev = null;
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell == null) {
if (prev != null) {
int span = prev.getColSpan();
span += width - (x - 1 + span);
prev.setColSpan(span);
break;
}
} else {
prev = cell;
}
}
}
}
protected int _buildTree(
Ul ul,
int x,
int y,
D2Array data,
Class cellType
) {
Content[] contents = ul.getContents();
int span = 0;
TrContent cell = null;
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Li) {
cell = _buildTree((Li)content, x, y, data, cellType);
int subspan = cell.getRowSpan();
span = span + subspan;
y = y + subspan;
} else if (content instanceof Tr) {
// assume tr doesn't have ul entry.
Content[] cells = ((Tr)content).getContents();
for (int j = 0;j < cells.length;j++) {
if (cells[j] instanceof TrContent) {
cell = (TrContent)cells[j];
data.put(x + j, y, cell);
}
}
y++;
span++;
} else if (content instanceof Ul) {
// ul is appeared in ul only if followed li and li has no ul
if (y == 0) { // pre-condition
throw (new InternalError());
}
int subspan
= _buildTree((Ul)content, x + 1, y - 1, data, cellType);
cell.setRowSpan(subspan);
if (subspan > 0) {
span = span + subspan - 1;
y = y + subspan - 1;
}
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
return (span);
}
protected TrContent _buildTree(
Li li,
int x,
int y,
D2Array data,
Class cellType
) {
Content[] contents = li.getContents();
int span = 0;
TrContent cell = _getCell(cellType);
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Ul) {
int subspan
= _buildTree((Ul)content, x + 1, y, data, cellType);
cell.setRowSpan(subspan);
span = span + subspan - 1;
} else if (content instanceof CharBlock) {
cell.addContent(content);
} else if (content instanceof Sentence) {
cell.addContent(content);
} else {
_warning("bad tag : " + content.toString());
}
}
data.put(x, y, cell);
return (cell);
}
protected TrContent _getCell(Class cellType) {
try {
return ((TrContent)cellType.newInstance());
} catch (IllegalAccessException e) {
throw (new InternalError());
} catch (InstantiationException e) {
throw (new InternalError());
}
}
/*
protected int _buildHeadTree0(Ul ul, int x) { // XXX : should correct
Content[] contents = ul.getContents();
int span = 0;
Th th = null;
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Ul) {
ty_--;
int subspan = _buildHeadTree((Ul)content, x + 1);
th.setRowSpan(subspan);
span = span + subspan - 1;
} else if (content instanceof Li) {
span++;
th = new Th();
th.addContents(((Li)content).getContents());
head_.put(x, ty_++, th);
} else if (content instanceof Tr) {
span++;
// assume tr doesn't have ul entry.
Content[] ths = ((Tr)content).getContents();
for (int j = 0;j < ths.length;j++) {
if (ths[j] instanceof Th) {
th = (Th)ths[j];
head_.put(x + j, ty_, th);
}
}
ty_++;
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
return (span);
}
*/
protected void _buildTFoot(TFoot tfoot) {
tfoot_ = tfoot;
D2Array data = new D2Array();
Content[] contents = tfoot.getContents();
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Tr) {
_buildRecord((Tr)content, data);
} else if (content instanceof Ul) {
_buildTree((Ul)content, data, Th.class);
data = data.rotateLeft(); // XXX : really append
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell != null) {
int colspan = cell.getColSpan();
int rowspan = cell.getRowSpan();
cell.setColSpan(rowspan);
cell.setRowSpan(colspan);
}
}
}
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
foot_ = data;
}
protected void _buildTBody(TBody tbody) {
tbody_ = tbody;
D2Array data = new D2Array();
Content[] contents = tbody.getContents();
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Tr) {
_buildRecord((Tr)content, data);
} else if (content instanceof Ul) {
_buildTree((Ul)content, data, Td.class);
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
data_ = data;
}
// XXX : re-make for more precise rendering
protected void _buildTBody0(TBody tbody) {
tbody_ = tbody;
/* String src = tbody.getSrc();
Adapter adapter = tbody.getAdapter();
if (src != null) {
_buildDataFromSrc(src, adapter);
}*/
Content[] contents = tbody.getContents();
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Tr) {
_buildDataRecord((Tr)content);
} else if (content instanceof Ul) {
_buildDataTree((Ul)content);
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
/* String inlineText = tbody.getInlineText();
if (inlineText != null) {
_buildDataFromText(inlineText, adapter);
}*/
}
protected void _buildDataRecord(Tr tr) {
Content[] contents = tr.getContents();
int x = 0;
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Td) {
data_.put(x++, y_, content);
} else {
_warning("bad tag : " + content.toString());
}
}
y_++;
}
protected void _buildDataTree(Ul ul) {
Content[] contents = ul.getContents();
int x = 0;
Td td = null;
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Ul) {
y_--; // adjust for previous li
td.setRowSpan(_buildDataTree((Ul)content, x + 1));
} else if (content instanceof Li) {
td = new Td();
td.addContents(((Li)content).getContents());
data_.put(x, y_++, td);
} else if (content instanceof Tr) {
// assume tr doesn't have ul entry.
Content[] tds = ((Tr)content).getContents();
for (int j = 0;j < tds.length;j++) {
if (tds[j] instanceof Td) {
td = (Td)tds[j];
data_.put(x + j, y_, td);
}
}
y_++;
} else {
_warning("bad tag : " + content.toString());
}
}
}
protected int _buildDataTree(Ul ul, int x) {
Content[] contents = ul.getContents();
int span = 0;
Td td = null;
for (int i = 0;i < contents.length;i++) {
Content content = contents[i];
if (content instanceof Ul) {
y_--;
int subspan = _buildDataTree((Ul)content, x + 1);
td.setRowSpan(subspan);
span = span + subspan - 1;
} else if (content instanceof Li) {
span++;
td = new Td();
td.addContents(((Li)content).getContents());
data_.put(x, y_++, td);
} else if (content instanceof Tr) {
span++;
// assume tr doesn't have ul entry.
Content[] tds = ((Tr)content).getContents();
for (int j = 0;j < tds.length;j++) {
if (tds[j] instanceof Td) {
td = (Td)tds[j];
data_.put(x + j, y_, td);
}
}
y_++;
} else if (content instanceof CharBlock) {
// do nothing
} else {
_warning("bad tag : " + content.toString());
}
}
return (span);
}
protected void _buildTnote(Tnote tnote) {
tnote_ = tnote;
}
protected void _buildMetaInfo() {
int nColgroups = colgroups_.size();
for (int i = 0;i < nColgroups;i++) {
Colgroup colgroup = (Colgroup)colgroups_.get(i);
Col[] cols = colgroup.getCols();
for (int j = 0;j < cols.length;j++) {
cols_.add(cols[j]);
}
}
int width = Math.max(
_calcTabularWidth(data_),
_calcTabularWidth(head_)
);
width = Math.max(width, _calcTabularWidth(foot_));
int nCols = cols_.size();
if (nCols >= width) {
return;
}
Colgroup stub = new Colgroup();
stub.setTable(this);
for (int i = nCols;i < width;i++) {
Col col = new Col();
stub.addCol(col);
cols_.add(col);
}
colgroups_.add(stub);
}
private int _calcTabularWidth(D2Array tabular) {
int width = tabular.getWidth();
int height = tabular.getHeight();
int result = width;
for (int y = 0;y < height;y++) {
for (int x = width - 1;x >= 0;x--) {
TrContent cell = (TrContent)tabular.get(x, y);
if (cell != null) {
int recordWidth = x + cell.getColSpan();
result = Math.max(result, recordWidth);
break;
}
}
}
return (result);
}
protected void _setupData() {
_setupData(head_);
_setupData(foot_);
_setupData(data_);
}
protected void _setupData(D2Array data) {
int width = data.getWidth();
int height = data.getHeight();
for (int y = 0;y < height;y++) {
for (int x = 0;x < width;x++) {
TrContent cell = (TrContent)data.get(x, y);
if (cell != null) {
cell.setCol((Col)cols_.get(x));
}
}
}
}
protected void _buildPTable() {
ptable_ = new PTable(this);
}
}