package gov.nasa.jpl.mbee.mdk.docgen.docbook; import java.util.ArrayList; import java.util.List; /** * A docbook table<br/> * Body and headers are essentially 2d matrix (headers can be multi-line)<br/> * Cells in body and header must be instances of other DocumentElement. If you * only want some simple text in a cell, use DBText.<br/> * If you want complex elements in a cell, use DBTableEntry which allows a list * of DocumentElements as its content. DBTableEntry needs to be used if you want * your cell to span.<br/> * If a cell is not a DBTableEntry, it'll automatically add the docbook entry * tags, so use DBTableEntry only if you have a complex cell.<br/> * <br/> * A list of DBColSpecs can also be set, these need to be filled in if any cell * has a span.<br/> * The rows of the header and body 2d matrix doesn't all have to have the same * amount of "columns".<br/> * If you have cells that span, the spanning info will take care of the * alignments, do not put in null or empty things if the cell's spanned by * something else.<br/> * Ex. if your first header row has a cell that spans two rows, your second * header row would have 1 less cell than the first, because one cell is * "covered" by the spanning cell in the first row. * * @author dlam */ public class DBTable extends DocumentElement { private List<List<DocumentElement>> body; private String caption; private String style; private List<List<DocumentElement>> headers; private List<DBColSpec> colspecs; private int cols; private boolean transpose; private boolean hideHeaders; private boolean showIfEmpty; public List<List<DocumentElement>> getBody() { return body; } public String getCaption() { return caption; } public List<List<DocumentElement>> getHeaders() { return headers; } public List<DBColSpec> getColspecs() { return colspecs; } public int getCols() { return cols; } public String getStyle() { return style; } public boolean isShowIfEmpty() { return showIfEmpty; } public void setShowIfEmpty(boolean showIfEmpty) { this.showIfEmpty = showIfEmpty; } /** * This must be set * * @param body */ public void setBody(List<List<DocumentElement>> body) { this.body = body; } public void setCaption(String caption) { this.caption = caption; } /** * This must be set * * @param headers */ public void setHeaders(List<List<DocumentElement>> headers) { this.headers = headers; } public void setColspecs(List<DBColSpec> colspecs) { this.colspecs = colspecs; } /** * this must be set (the cols is the max number of cols in your table) * * @param cols */ public void setCols(int cols) { this.cols = cols; } public void setStyle(String style) { this.style = style; } public boolean isTranspose() { return transpose; } public void setTranspose(boolean transpose) { this.transpose = transpose; } public boolean isHideHeaders() { return hideHeaders; } public void setHideHeaders(final boolean hideHeaders) { this.hideHeaders = hideHeaders; } @Override public void accept(IDBVisitor v) { v.visit(this); } /* * transpose the table (this is actually rotate 90 degrees counterclockwise */ public void transpose() { //this whole thing looks complicated because of transposing colspans and rowspans //the new table wouldn't have a header since the new header would be part of the body removeAllNulls(); //every cell should be a document element, this is to remove any inconsistent user set nulls addNulls(); //go through the current table and add in nulls according to colspans and rowspans //so the table is truly m x n, every cell should be either null or a document element //do the transpose, if i is index of the col and j is index of the row in the old table, //the new table's rows would be i and cols would be j List<List<DocumentElement>> newbody = new ArrayList<List<DocumentElement>>(); if (headers != null && headers.size() > 0) { for (int i = headers.get(0).size() - 1; i >= 0; i--) { List<DocumentElement> newrow = new ArrayList<DocumentElement>(); newbody.add(newrow); for (int j = 0; j < headers.size(); j++) { DocumentElement oldcell = headers.get(j).get(i); if (oldcell instanceof DBTableEntry) { DBTableEntry oldcelll = ((DBTableEntry) oldcell); String namest = oldcelll.getNamest(); String nameend = oldcelll.getNameend(); int morerows = oldcelll.getMorerows(); oldcelll.setMorerows(0); oldcelll.setNamest(null); oldcelll.setNameend(null); if (namest != null && nameend != null) { int oldnamest = Integer.parseInt(namest); int oldnameend = Integer.parseInt(nameend); int diff = oldnameend - oldnamest; oldcelll.setMorerows(diff); } if (morerows > 0) { String newnamest = Integer.toString(newrow.size() + 1); String newnameend = Integer.toString(newrow.size() + 1 + morerows); oldcelll.setNamest(newnamest); oldcelll.setNameend(newnameend); } newrow.add(oldcelll); } else { newrow.add(oldcell); } } } } if (body != null && body.size() > 0) { for (int i = body.get(0).size() - 1; i >= 0; i--) { List<DocumentElement> newrow = newbody.get(newbody.size() - i - 1); for (int j = 0; j < body.size(); j++) { DocumentElement oldcell = body.get(j).get(i); if (oldcell instanceof DBTableEntry) { DBTableEntry oldcelll = ((DBTableEntry) oldcell); String namest = oldcelll.getNamest(); String nameend = oldcelll.getNameend(); int morerows = oldcelll.getMorerows(); oldcelll.setMorerows(0); oldcelll.setNamest(null); oldcelll.setNameend(null); if (namest != null && nameend != null) { int oldnamest = Integer.parseInt(namest); int oldnameend = Integer.parseInt(nameend); int diff = oldnameend - oldnamest; oldcelll.setMorerows(diff); } if (morerows > 0) { String newnamest = Integer.toString(newrow.size() + 1); String newnameend = Integer.toString(newrow.size() + 1 + morerows); oldcelll.setNamest(newnamest); oldcelll.setNameend(newnameend); } newrow.add(oldcelll); } else { newrow.add(oldcell); } } } } this.body = newbody; this.headers = null; this.cols = !newbody.isEmpty() ? newbody.get(0).size() : 0; List<DBColSpec> newcolspecs = new ArrayList<DBColSpec>(); for (int i = 1; i <= cols; i++) { newcolspecs.add(new DBColSpec(i)); } this.colspecs = newcolspecs; } private void removeAllNulls() { if (headers != null) { for (List<DocumentElement> row : headers) { while (row.remove(null)) { } } } if (body != null) { for (List<DocumentElement> row : body) { while (row.remove(null)) { } } } } private void addNulls() { //handle old colspans first for each row handleColspan(headers); handleColspan(body); handleRowspan(headers); handleRowspan(body); } private void handleColspan(List<List<DocumentElement>> body) { if (body == null) { return; } for (List<DocumentElement> row : body) { List<DocumentElement> copy = new ArrayList<DocumentElement>(row); for (int i = copy.size() - 1; i >= 0; i--) { DocumentElement cell = copy.get(i); if (cell instanceof DBTableEntry) { String namest = ((DBTableEntry) cell).getNamest(); String nameend = ((DBTableEntry) cell).getNameend(); if (namest != null && nameend != null) { try { int start = Integer.parseInt(namest); int end = Integer.parseInt(nameend); int morecols = end - start; while (morecols > 0) { row.add(i, null); morecols--; } } catch (Exception e) { } } } } } } private void handleRowspan(List<List<DocumentElement>> body) { if (body == null) { return; } //int j = 0; //need to handle old rowspans bottom up because of stuff int i = 0; for (int j = body.size() - 1; j >= 0; j--) { //List<DocumentElement> row: headers) { List<DocumentElement> row = body.get(j); for (DocumentElement cell : row) { if (cell instanceof DBTableEntry && ((DBTableEntry) cell).getMorerows() > 0) { int length = ((DBTableEntry) cell).getMorerows(); for (int k = j; k < j + length; k++) { body.get(k + 1).add(i, null); } } i++; } i = 0; //j++; } } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(super.toString()); int pos = sb.lastIndexOf(")"); sb.insert(pos, ", " + getBody()); return sb.toString(); } }