001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: RtfExtraRowSet.java 426576 2006-07-28 15:44:37Z jeremias $ */
019:
020: package org.apache.fop.render.rtf.rtflib.rtfdoc;
021:
022: /*
023: * This file is part of the RTF library of the FOP project, which was originally
024: * created by Bertrand Delacretaz <bdelacretaz@codeconsult.ch> and by other
025: * contributors to the jfor project (www.jfor.org), who agreed to donate jfor to
026: * the FOP project.
027: */
028:
029: import java.io.Writer;
030: import java.io.IOException;
031: import java.util.List;
032: import java.util.LinkedList;
033: import java.util.Iterator;
034: import java.util.Collections;
035:
036: /**
037: * Used to add extra table rows after a row that contains a nested table:
038: * <li> created by RtfTableRow before generating RTF code
039: * <li> an RtfTableCell that contains a nested table can ask this to put
040: * some of its children in extra rows that after the current row
041: * <li> once RtfTableRow is done rendering its children, it renders this,
042: * causing extra rows to be generated, with content that can come
043: * from several RtfTableCells
044: *
045: * See org.apache.fop.rtf.rtflib.testdocs.NestedTable for an example of
046: * usage.
047: * @author Bertrand Delacretaz bdelacretaz@codeconsult.ch
048: */
049:
050: public class RtfExtraRowSet extends RtfContainer {
051: // TODO what is idnum?
052: static final int DEFAULT_IDNUM = 0;
053:
054: /** Parent table context
055: * (added by Boris Poudérous on july 2002 in order to process nested tables)
056: */
057: private ITableColumnsInfo parentITableColumnsInfo = null;
058:
059: /** While a top-level RtfTableRow is being rendered, we build a list of
060: * RtfTableCells that must be rendered in extra rows.
061: * This holds a cell with positioning information
062: */
063: private final List cells = new LinkedList();
064:
065: private static class PositionedCell implements Comparable {
066: private final RtfTableCell cell;
067: private final int xOffset;
068: private final int rowIndex;
069:
070: PositionedCell(RtfTableCell c, int index, int offset) {
071: cell = c;
072: xOffset = offset;
073: rowIndex = index;
074: }
075:
076: /** debugging dump */
077: public String toString() {
078: return "PositionedCell: row " + rowIndex + ", offset "
079: + xOffset;
080: }
081:
082: /** cells need to be sorted by row index and then by x offset */
083: public int compareTo(Object o) {
084: int result = 0;
085: if (o == null) {
086: result = 1;
087: } else if (!(o instanceof PositionedCell)) {
088: result = 1;
089: } else {
090: final PositionedCell pc = (PositionedCell) o;
091: if (this .rowIndex < pc.rowIndex) {
092: result = -1;
093: } else if (this .rowIndex > pc.rowIndex) {
094: result = 1;
095: } else if (this .xOffset < pc.xOffset) {
096: result = -1;
097: } else if (this .xOffset > pc.xOffset) {
098: result = 1;
099: }
100: }
101:
102: return result;
103: }
104:
105: public boolean equals(Object o) {
106: return o != null && this .compareTo(o) == 0;
107: }
108: }
109:
110: /** our maximum row index */
111: private int maxRowIndex;
112:
113: /** an RtfExtraRowSet has no parent, it is only used temporary during
114: * generation of RTF for an RtfTableRow
115: */
116: RtfExtraRowSet(Writer w) throws IOException {
117: super (null, w);
118: }
119:
120: /** Add all cells of given Table to this set for later rendering in extra rows
121: * @return index of extra row to use for elements that follow this table in the same cell
122: * @param rowIndex index of first extra row to create to hold cells of tbl
123: * @param xOffset horizontal position of left edge of first column of tbl
124: */
125: int addTable(RtfTable tbl, int rowIndex, int xOffset) {
126: // process all rows of the table
127: for (Iterator it = tbl.getChildren().iterator(); it.hasNext();) {
128: final RtfElement e = (RtfElement) it.next();
129: if (e instanceof RtfTableRow) {
130: addRow((RtfTableRow) e, rowIndex, xOffset);
131: rowIndex++;
132: maxRowIndex = Math.max(rowIndex, maxRowIndex);
133: }
134: }
135: return rowIndex;
136: }
137:
138: /** add all cells of given row to this set */
139: private void addRow(RtfTableRow row, int rowIndex, int xOffset) {
140: for (Iterator it = row.getChildren().iterator(); it.hasNext();) {
141: final RtfElement e = (RtfElement) it.next();
142: if (e instanceof RtfTableCell) {
143: final RtfTableCell c = (RtfTableCell) e;
144: cells.add(new PositionedCell(c, rowIndex, xOffset));
145: xOffset += c.getCellWidth();
146: }
147: }
148: }
149:
150: /** create an extra cell to hold content that comes after a nested table in a cell
151: * Modified by Boris Poudérous in order to permit the extra cell to have
152: * the attributes of its parent cell
153: */
154: RtfTableCell createExtraCell(int rowIndex, int xOffset,
155: int cellWidth, RtfAttributes parentCellAttributes)
156: throws IOException {
157: final RtfTableCell c = new RtfTableCell(null, writer,
158: cellWidth, parentCellAttributes, DEFAULT_IDNUM);
159: cells.add(new PositionedCell(c, rowIndex, xOffset));
160: return c;
161: }
162:
163: /**
164: * render extra RtfTableRows containing all the extra RtfTableCells that we
165: * contain
166: * @throws IOException for I/O problems
167: */
168: protected void writeRtfContent() throws IOException {
169: // sort cells by rowIndex and xOffset
170: Collections.sort(cells);
171:
172: // process all extra cells by rendering them into extra rows
173: List rowCells = null;
174: int rowIndex = -1;
175: for (Iterator it = cells.iterator(); it.hasNext();) {
176: final PositionedCell pc = (PositionedCell) it.next();
177: if (pc.rowIndex != rowIndex) {
178: // starting a new row, render previous one
179: if (rowCells != null) {
180: writeRow(rowCells);
181: }
182: rowIndex = pc.rowIndex;
183: rowCells = new LinkedList();
184: }
185: rowCells.add(pc);
186: }
187:
188: // render last row
189: if (rowCells != null) {
190: writeRow(rowCells);
191: }
192: }
193:
194: /** write one RtfTableRow containing given PositionedCells */
195: private void writeRow(List cells) throws IOException {
196: if (allCellsEmpty(cells)) {
197: return;
198: }
199:
200: final RtfTableRow row = new RtfTableRow(null, writer,
201: DEFAULT_IDNUM);
202: int cellIndex = 0;
203:
204: // Get the context of the table that holds the nested table
205: ITableColumnsInfo parentITableColumnsInfo = getParentITableColumnsInfo();
206: parentITableColumnsInfo.selectFirstColumn();
207:
208: // X offset of the current empty cell to add
209: float xOffset = 0;
210: float xOffsetOfLastPositionedCell = 0;
211:
212: for (Iterator it = cells.iterator(); it.hasNext();) {
213: final PositionedCell pc = (PositionedCell) it.next();
214:
215: // if first cell is not at offset 0, add placeholder cell
216: // TODO should be merged with the cell that is above it
217: if (cellIndex == 0 && pc.xOffset > 0) {
218: /**
219: * Added by Boris Poudérous
220: */
221: // Add empty cells merged vertically with the cells above and with the same widths
222: // (BEFORE the cell that contains the nested table)
223: for (int i = 0; (xOffset < pc.xOffset)
224: && (i < parentITableColumnsInfo
225: .getNumberOfColumns()); i++) {
226: // Get the width of the cell above
227: xOffset += parentITableColumnsInfo.getColumnWidth();
228: // Create the empty cell merged vertically
229: row.newTableCellMergedVertically(
230: (int) parentITableColumnsInfo
231: .getColumnWidth(), pc.cell.attrib);
232: // Select next column in order to have its width
233: parentITableColumnsInfo.selectNextColumn();
234: }
235: }
236:
237: row.addChild(pc.cell);
238: // Line added by Boris Poudérous
239: xOffsetOfLastPositionedCell = pc.xOffset
240: + pc.cell.getCellWidth();
241: cellIndex++;
242: }
243:
244: /**
245: * Added by Boris Poudérous
246: */
247: // Add empty cells merged vertically with the cells above AFTER the cell
248: // that contains the nested table
249: // The cells added have the same widths than the cells above.
250: if (parentITableColumnsInfo.getColumnIndex() < (parentITableColumnsInfo
251: .getNumberOfColumns() - 1)) {
252: parentITableColumnsInfo.selectNextColumn();
253:
254: while (parentITableColumnsInfo.getColumnIndex() < parentITableColumnsInfo
255: .getNumberOfColumns()) {
256: // Create the empty cell merged vertically
257: // TODO : the new cells after the extra cell don't have its
258: // attributes as we did for the previous cells.
259: // => in fact the m_attrib below (last argument) is
260: // empty => should be the attributes of the above cells.
261: row.newTableCellMergedVertically(
262: (int) parentITableColumnsInfo.getColumnWidth(),
263: attrib);
264: // Select next column in order to have its width
265: parentITableColumnsInfo.selectNextColumn();
266: }
267: }
268:
269: row.writeRtf();
270: }
271:
272: /** true if all cells of given list are empty
273: * @param cells List of PositionedCell objects
274: */
275: private static boolean allCellsEmpty(List cells) {
276: boolean empty = true;
277: for (Iterator it = cells.iterator(); it.hasNext();) {
278: final PositionedCell pc = (PositionedCell) it.next();
279: if (pc.cell.containsText()) {
280: empty = false;
281: break;
282: }
283: }
284: return empty;
285: }
286:
287: /**
288: * As this contains cells from several rows, we say that it's empty
289: * only if we have no cells.
290: * writeRow makes the decision about rendering specific rows
291: * @return false (always)
292: */
293: public boolean isEmpty() {
294: return false;
295: }
296:
297: /**
298: * @return The table context of the parent table
299: * Added by Boris Poudérous on july 2002 in order to process nested tables
300: */
301: public ITableColumnsInfo getParentITableColumnsInfo() {
302: return this .parentITableColumnsInfo;
303: }
304:
305: /**
306: *
307: * @param parentITableColumnsInfo table context to set
308: */
309: public void setParentITableColumnsInfo(
310: ITableColumnsInfo parentITableColumnsInfo) {
311: this .parentITableColumnsInfo = parentITableColumnsInfo;
312: }
313: /** - end - */
314: }
|