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: TableBody.java 567294 2007-08-18 17:00:50Z vhennebert $ */
019:
020: package org.apache.fop.fo.flow;
021:
022: // Java
023: import java.util.BitSet;
024: import java.util.List;
025:
026: import org.xml.sax.Attributes;
027: import org.xml.sax.Locator;
028:
029: import org.apache.fop.apps.FOPException;
030: import org.apache.fop.datatypes.Length;
031: import org.apache.fop.fo.FONode;
032: import org.apache.fop.fo.FObj;
033: import org.apache.fop.fo.PropertyList;
034: import org.apache.fop.fo.ValidationException;
035: import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
036:
037: /**
038: * Class modelling the fo:table-body object.
039: */
040: public class TableBody extends TableFObj {
041: // The value of properties relevant for fo:table-body.
042: private CommonBorderPaddingBackground commonBorderPaddingBackground;
043: // Unused but valid items, commented out for performance:
044: // private CommonAccessibility commonAccessibility;
045: // private CommonAural commonAural;
046: // private CommonRelativePosition commonRelativePosition;
047: // private int visibility;
048: // End of property values
049:
050: private PropertyList savedPropertyList;
051:
052: /**
053: * used for validation
054: */
055: protected boolean tableRowsFound = false;
056: protected boolean tableCellsFound = false;
057:
058: /**
059: * used for initial values of column-number property
060: */
061: protected List pendingSpans;
062: protected BitSet usedColumnIndices;
063: private int columnIndex = 1;
064: protected boolean firstRow = true;
065:
066: /**
067: * @param parent FONode that is the parent of the object
068: */
069: public TableBody(FONode parent) {
070: super (parent);
071: }
072:
073: /**
074: * @see FObj#bind(PropertyList)
075: */
076: public void bind(PropertyList pList) throws FOPException {
077: commonBorderPaddingBackground = pList
078: .getBorderPaddingBackgroundProps();
079: super .bind(pList);
080: //Used by convertCellsToRows()
081: savedPropertyList = pList;
082: }
083:
084: /**
085: * @see org.apache.fop.fo.FONode#processNode(String, Locator, Attributes, PropertyList)
086: */
087: public void processNode(String elementName, Locator locator,
088: Attributes attlist, PropertyList pList) throws FOPException {
089: if (!inMarker()) {
090: if (getTable().columns != null) {
091: int cap = getTable().columns.size();
092: pendingSpans = new java.util.ArrayList(cap);
093: usedColumnIndices = new java.util.BitSet(cap);
094: } else {
095: pendingSpans = new java.util.ArrayList();
096: usedColumnIndices = new java.util.BitSet();
097: }
098: setNextColumnIndex();
099: }
100: super .processNode(elementName, locator, attlist, pList);
101: }
102:
103: /**
104: * @see org.apache.fop.fo.FONode#startOfNode
105: */
106: protected void startOfNode() throws FOPException {
107: getFOEventHandler().startBody(this );
108: }
109:
110: /**
111: * @see org.apache.fop.fo.FONode#endOfNode
112: */
113: protected void endOfNode() throws FOPException {
114:
115: if (!inMarker()) {
116: // clean up
117: savedPropertyList = null;
118: pendingSpans = null;
119: usedColumnIndices = null;
120: }
121:
122: getFOEventHandler().endBody(this );
123:
124: if (!(tableRowsFound || tableCellsFound)) {
125: if (getUserAgent().validateStrictly()) {
126: missingChildElementError("marker* (table-row+|table-cell+)");
127: } else {
128: log.error("fo:table-body must not be empty. "
129: + "Expected: marker* (table-row+|table-cell+)");
130: getParent().removeChild(this );
131: }
132: }
133:
134: /*
135: if (tableCellsFound) {
136: convertCellsToRows();
137: }
138: */
139: }
140:
141: /**
142: * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
143: * XSL Content Model: marker* (table-row+|table-cell+)
144: */
145: protected void validateChildNode(Locator loc, String nsURI,
146: String localName) throws ValidationException {
147: if (FO_URI.equals(nsURI)) {
148: if (localName.equals("marker")) {
149: if (tableRowsFound || tableCellsFound) {
150: nodesOutOfOrderError(loc, "fo:marker",
151: "(table-row+|table-cell+)");
152: }
153: } else if (localName.equals("table-row")) {
154: tableRowsFound = true;
155: if (tableCellsFound) {
156: invalidChildError(
157: loc,
158: nsURI,
159: localName,
160: "Either fo:table-rows"
161: + " or fo:table-cells may be children of an "
162: + getName() + " but not both");
163: }
164: } else if (localName.equals("table-cell")) {
165: tableCellsFound = true;
166: if (tableRowsFound) {
167: invalidChildError(loc, nsURI, localName,
168: "Either fo:table-rows or fo:table-cells "
169: + "may be children of an "
170: + getName() + " but not both");
171: }
172: } else {
173: invalidChildError(loc, nsURI, localName);
174: }
175: } else {
176: invalidChildError(loc, nsURI, localName);
177: }
178: }
179:
180: /**
181: * @see org.apache.fop.fo.FONode#addChildNode(FONode)
182: */
183: protected void addChildNode(FONode child) throws FOPException {
184: if (!inMarker()) {
185: if (firstRow) {
186: Table t = getTable();
187:
188: if (t.columns == null) {
189: t.columns = new java.util.ArrayList();
190: }
191:
192: switch (child.getNameId()) {
193: case FO_TABLE_ROW:
194: firstRow = false;
195: break;
196: case FO_TABLE_CELL:
197: TableCell cell = (TableCell) child;
198: int colNr = cell.getColumnNumber();
199: int colSpan = cell.getNumberColumnsSpanned();
200: Length colWidth = null;
201:
202: if (cell.getWidth().getEnum() != EN_AUTO
203: && colSpan == 1) {
204: colWidth = cell.getWidth();
205: }
206:
207: for (int i = colNr; i < colNr + colSpan; ++i) {
208: if (t.columns.size() < i
209: || t.columns.get(i - 1) == null) {
210: t.addDefaultColumn(colWidth,
211: i == colNr ? cell.getColumnNumber()
212: : 0);
213: } else {
214: TableColumn col = (TableColumn) t.columns
215: .get(i - 1);
216: if (!col.isDefaultColumn()
217: && colWidth != null) {
218: col.setColumnWidth(colWidth);
219: }
220: }
221: }
222: break;
223: default:
224: //nop
225: }
226: }
227: }
228: super .addChildNode(child);
229: }
230:
231: /**
232: * @return the Common Border, Padding, and Background Properties.
233: */
234: public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
235: return commonBorderPaddingBackground;
236: }
237:
238: /** @see org.apache.fop.fo.FONode#getLocalName() */
239: public String getLocalName() {
240: return "table-body";
241: }
242:
243: /**
244: * @see org.apache.fop.fo.FObj#getNameId()
245: */
246: public int getNameId() {
247: return FO_TABLE_BODY;
248: }
249:
250: /**
251: * @param obj table row in question
252: * @return true if the given table row is the first row of this body.
253: */
254: public boolean isFirst(TableRow obj) {
255: return (firstChild == null || firstChild == obj);
256: }
257:
258: /**
259: * Initializes list of pending row-spans; used for correctly
260: * assigning initial value for column-number for the
261: * cells of following rows
262: * (note: not literally mentioned in the Rec, but it is assumed
263: * that, if the first cell in a given row spans two rows, then
264: * the first cell of the following row will have an initial
265: * column-number of 2, since the first column is already
266: * occupied...)
267: */
268: protected void initPendingSpans(FONode child) {
269: if (child.getNameId() == FO_TABLE_ROW) {
270: pendingSpans = ((TableRow) child).pendingSpans;
271: } else if (pendingSpans == null) {
272: if (getTable().columns != null) {
273: List tableCols = getTable().columns;
274: pendingSpans = new java.util.ArrayList(tableCols.size());
275: for (int i = tableCols.size(); --i >= 0;) {
276: pendingSpans.add(null);
277: }
278: } else {
279: pendingSpans = new java.util.ArrayList();
280: }
281: }
282: }
283:
284: /**
285: * Returns the current column index of the TableBody
286: *
287: * @return the next column number to use
288: */
289: public int getCurrentColumnIndex() {
290: return columnIndex;
291: }
292:
293: /**
294: * Sets the current column index to a specific value
295: * (used by ColumnNumberPropertyMaker.make() in case the
296: * column-number was explicitly specified on the cell)
297: *
298: * @param newIndex the new column index
299: */
300: public void setCurrentColumnIndex(int newIndex) {
301: columnIndex = newIndex;
302: }
303:
304: /**
305: * Resets the current column index for the TableBody
306: *
307: */
308: public void resetColumnIndex() {
309: columnIndex = 1;
310: for (int i = usedColumnIndices.length(); --i >= 0;) {
311: usedColumnIndices.clear(i);
312: }
313:
314: PendingSpan pSpan;
315: for (int i = pendingSpans.size(); --i >= 0;) {
316: pSpan = (PendingSpan) pendingSpans.get(i);
317: if (pSpan != null) {
318: pSpan.rowsLeft--;
319: if (pSpan.rowsLeft == 0) {
320: pendingSpans.set(i, null);
321: } else {
322: usedColumnIndices.set(i);
323: }
324: }
325: }
326: if (!firstRow) {
327: setNextColumnIndex();
328: }
329: }
330:
331: /**
332: * Increases columnIndex to the next available value
333: *
334: */
335: protected void setNextColumnIndex() {
336: while (usedColumnIndices.get(columnIndex - 1)) {
337: //increment columnIndex
338: columnIndex++;
339: }
340: //if the table has explicit columns, and
341: //the index is not assigned to any
342: //column, increment further until the next
343: //index occupied by a column...
344: if (getTable().columns != null) {
345: while (columnIndex <= getTable().columns.size()
346: && !getTable().isColumnNumberUsed(columnIndex)) {
347: columnIndex++;
348: }
349: }
350: }
351:
352: /**
353: * Checks whether the previous cell had 'ends-row="true"'
354: *
355: * @return true if:
356: * a) there is a previous cell, which
357: * had ends-row="true"
358: * b) there is no previous cell (implicit
359: * start of row)
360: */
361: public boolean previousCellEndedRow() {
362: if (firstChild != null) {
363: FONode prevNode = getChildNodes().lastNode();
364: if (prevNode.getNameId() == FO_TABLE_CELL) {
365: return ((TableCell) prevNode).endsRow();
366: }
367: }
368: return true;
369: }
370:
371: /**
372: * Checks whether a given column-number is already in use
373: * for the current row;
374: *
375: * @param colNr the column-number to check
376: * @return true if column-number is already occupied
377: */
378: public boolean isColumnNumberUsed(int colNr) {
379: return usedColumnIndices.get(colNr - 1);
380: }
381:
382: /**
383: * @see org.apache.fop.fo.flow.TableFObj#flagColumnIndices(int, int)
384: */
385: protected void flagColumnIndices(int start, int end) {
386: for (int i = start; i < end; i++) {
387: usedColumnIndices.set(i);
388: }
389: setNextColumnIndex();
390: }
391: }
|