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: Table.java 556248 2007-07-14 08:52:28Z adelmelle $ */
019:
020: package org.apache.fop.fo.flow;
021:
022: import java.util.BitSet;
023: import java.util.List;
024:
025: import org.xml.sax.Locator;
026:
027: import org.apache.fop.apps.FOPException;
028: import org.apache.fop.datatypes.Length;
029: import org.apache.fop.datatypes.ValidationPercentBaseContext;
030: import org.apache.fop.fo.FONode;
031: import org.apache.fop.fo.FObj;
032: import org.apache.fop.fo.PropertyList;
033: import org.apache.fop.fo.StaticPropertyList;
034: import org.apache.fop.fo.ValidationException;
035: import org.apache.fop.fo.properties.CommonAccessibility;
036: import org.apache.fop.fo.properties.CommonAural;
037: import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
038: import org.apache.fop.fo.properties.CommonMarginBlock;
039: import org.apache.fop.fo.properties.CommonRelativePosition;
040: import org.apache.fop.fo.properties.KeepProperty;
041: import org.apache.fop.fo.properties.LengthPairProperty;
042: import org.apache.fop.fo.properties.LengthRangeProperty;
043:
044: /**
045: * Class modelling the fo:table object.
046: */
047: public class Table extends TableFObj {
048:
049: /** properties */
050: private CommonBorderPaddingBackground commonBorderPaddingBackground;
051: private CommonMarginBlock commonMarginBlock;
052: private LengthRangeProperty blockProgressionDimension;
053: private int borderCollapse;
054: private LengthPairProperty borderSeparation;
055: private int breakAfter;
056: private int breakBefore;
057: private LengthRangeProperty inlineProgressionDimension;
058: private KeepProperty keepTogether;
059: private KeepProperty keepWithNext;
060: private KeepProperty keepWithPrevious;
061: private int tableLayout;
062: private int tableOmitFooterAtBreak;
063: private int tableOmitHeaderAtBreak;
064: // Unused but valid items, commented out for performance:
065: // private CommonAccessibility commonAccessibility;
066: // private CommonAural commonAural;
067: // private CommonRelativePosition commonRelativePosition;
068: // private int intrusionDisplace;
069: // private int writingMode;
070:
071: /** extension properties */
072: private Length widowContentLimit;
073: private Length orphanContentLimit;
074:
075: private static final int MINCOLWIDTH = 10000; // 10pt
076:
077: /** collection of columns in this table */
078: protected List columns = null;
079:
080: /** helper variables for implicit column-numbering */
081: private int columnIndex = 1;
082: private BitSet usedColumnIndices = new BitSet();
083:
084: /** the table-header and -footer */
085: private TableBody tableHeader = null;
086: private TableBody tableFooter = null;
087:
088: /** used for validation */
089: private boolean tableColumnFound = false;
090: private boolean tableHeaderFound = false;
091: private boolean tableFooterFound = false;
092: private boolean tableBodyFound = false;
093:
094: /**
095: * The table's property list. Used in case the table has
096: * no explicit columns, as a parent property list to
097: * internally generated TableColumns
098: */
099: private PropertyList propList;
100:
101: /**
102: * @param parent FONode that is the parent of this object
103: */
104: public Table(FONode parent) {
105: super (parent);
106: }
107:
108: /**
109: * @see org.apache.fop.fo.FObj#bind(PropertyList)
110: */
111: public void bind(PropertyList pList) throws FOPException {
112: super .bind(pList);
113: commonBorderPaddingBackground = pList
114: .getBorderPaddingBackgroundProps();
115: commonMarginBlock = pList.getMarginBlockProps();
116: blockProgressionDimension = pList.get(
117: PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange();
118: borderCollapse = pList.get(PR_BORDER_COLLAPSE).getEnum();
119: borderSeparation = pList.get(PR_BORDER_SEPARATION)
120: .getLengthPair();
121: breakAfter = pList.get(PR_BREAK_AFTER).getEnum();
122: breakBefore = pList.get(PR_BREAK_BEFORE).getEnum();
123: inlineProgressionDimension = pList.get(
124: PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
125: keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep();
126: keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
127: keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
128: tableLayout = pList.get(PR_TABLE_LAYOUT).getEnum();
129: tableOmitFooterAtBreak = pList.get(
130: PR_TABLE_OMIT_FOOTER_AT_BREAK).getEnum();
131: tableOmitHeaderAtBreak = pList.get(
132: PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum();
133:
134: //Bind extension properties
135: widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT)
136: .getLength();
137: orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT)
138: .getLength();
139:
140: if (!blockProgressionDimension.getOptimum(null).isAuto()) {
141: attributeWarning("only a value of \"auto\" for block-progression-dimension has a well-specified"
142: + " behavior on fo:table. Falling back to \"auto\"");
143: // Anyway, the bpd of a table is not used by the layout code
144: }
145: if (tableLayout == EN_AUTO) {
146: attributeWarning("table-layout=\"auto\" is currently not supported by FOP");
147: }
148: if (!isSeparateBorderModel()
149: && getCommonBorderPaddingBackground()
150: .hasPadding(
151: ValidationPercentBaseContext
152: .getPseudoContext())) {
153: //See "17.6.2 The collapsing border model" in CSS2
154: attributeWarning("In collapsing border model a table does not have padding"
155: + " (see http://www.w3.org/TR/REC-CSS2/tables.html#collapsing-borders)"
156: + ", but a non-zero value for padding was found. The padding will be ignored.");
157: }
158:
159: /* Store reference to the property list, so
160: * new lists can be created in case the table has no
161: * explicit columns
162: * (see addDefaultColumn())
163: */
164: this .propList = pList;
165: }
166:
167: /**
168: * @see org.apache.fop.fo.FONode#startOfNode
169: */
170: protected void startOfNode() throws FOPException {
171: super .startOfNode();
172: getFOEventHandler().startTable(this );
173: }
174:
175: /**
176: * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
177: * XSL Content Model: (marker*,table-column*,table-header?,table-footer?,table-body+)
178: */
179: protected void validateChildNode(Locator loc, String nsURI,
180: String localName) throws ValidationException {
181: if (FO_URI.equals(nsURI)) {
182: if ("marker".equals(localName)) {
183: if (tableColumnFound || tableHeaderFound
184: || tableFooterFound || tableBodyFound) {
185: nodesOutOfOrderError(loc, "fo:marker",
186: "(table-column*,table-header?,table-footer?,table-body+)");
187: }
188: } else if ("table-column".equals(localName)) {
189: tableColumnFound = true;
190: if (tableHeaderFound || tableFooterFound
191: || tableBodyFound) {
192: nodesOutOfOrderError(loc, "fo:table-column",
193: "(table-header?,table-footer?,table-body+)");
194: }
195: } else if ("table-header".equals(localName)) {
196: if (tableHeaderFound) {
197: tooManyNodesError(loc, "table-header");
198: } else {
199: tableHeaderFound = true;
200: if (tableFooterFound || tableBodyFound) {
201: nodesOutOfOrderError(loc, "fo:table-header",
202: "(table-footer?,table-body+)");
203: }
204: }
205: } else if ("table-footer".equals(localName)) {
206: if (tableFooterFound) {
207: tooManyNodesError(loc, "table-footer");
208: } else {
209: tableFooterFound = true;
210: if (tableBodyFound
211: && getUserAgent().validateStrictly()) {
212: nodesOutOfOrderError(loc, "fo:table-footer",
213: "(table-body+)");
214: }
215: }
216: } else if ("table-body".equals(localName)) {
217: tableBodyFound = true;
218: } else {
219: invalidChildError(loc, nsURI, localName);
220: }
221: } else {
222: invalidChildError(loc, nsURI, localName);
223: }
224: }
225:
226: /**
227: * @see org.apache.fop.fo.FONode#endOfNode
228: */
229: protected void endOfNode() throws FOPException {
230:
231: if (!tableBodyFound) {
232: missingChildElementError("(marker*,table-column*,table-header?,table-footer?"
233: + ",table-body+)");
234: }
235: if (!inMarker()) {
236: /* clean up */
237: for (int i = columns.size(); --i >= 0;) {
238: TableColumn col = (TableColumn) columns.get(i);
239: if (col != null) {
240: col.releasePropertyList();
241: }
242: }
243: this .propList = null;
244: }
245: getFOEventHandler().endTable(this );
246:
247: }
248:
249: /**
250: * @see org.apache.fop.fo.FONode#addChildNode(FONode)
251: */
252: protected void addChildNode(FONode child) throws FOPException {
253:
254: int childId = child.getNameId();
255:
256: switch (childId) {
257: case FO_TABLE_COLUMN:
258: if (columns == null) {
259: columns = new java.util.ArrayList();
260: }
261: if (!inMarker()) {
262: addColumnNode((TableColumn) child);
263: } else {
264: columns.add((TableColumn) child);
265: }
266: return;
267: case FO_MARKER:
268: super .addChildNode(child);
269: return;
270: default:
271: switch (childId) {
272: case FO_TABLE_FOOTER:
273: tableFooter = (TableBody) child;
274: break;
275: case FO_TABLE_HEADER:
276: tableHeader = (TableBody) child;
277: break;
278: default:
279: super .addChildNode(child);
280: }
281: }
282: }
283:
284: /**
285: * Adds a default column to the columns list (called from
286: * TableBody.addChildNode() when the table has no explicit
287: * columns, and if processing the first row)
288: *
289: * @param colWidth the column's width (null if the default should be used)
290: * @param colNr the column-number from the cell
291: * @throws FOPException if there was an error creating the property list
292: */
293: protected void addDefaultColumn(Length colWidth, int colNr)
294: throws FOPException {
295: TableColumn defaultColumn = new TableColumn(this , true);
296: PropertyList pList = new StaticPropertyList(defaultColumn,
297: this .propList);
298: pList.setWritingMode();
299: defaultColumn.bind(pList);
300: if (colWidth != null) {
301: defaultColumn.setColumnWidth(colWidth);
302: }
303: if (colNr != 0) {
304: defaultColumn.setColumnNumber(colNr);
305: }
306: addColumnNode(defaultColumn);
307: }
308:
309: /**
310: * Adds a column to the columns List, and updates the columnIndex
311: * used for determining initial values for column-number
312: *
313: * @param col the column to add
314: * @throws FOPException
315: */
316: private void addColumnNode(TableColumn col) {
317:
318: int colNumber = col.getColumnNumber();
319: int colRepeat = col.getNumberColumnsRepeated();
320:
321: if (columns.size() < colNumber) {
322: /* add nulls for non-occupied indices between
323: /* the last column up to and including the current one
324: */
325: while (columns.size() < colNumber) {
326: columns.add(null);
327: }
328: }
329:
330: /* replace the null-value with the actual column */
331: columns.set(colNumber - 1, col);
332:
333: if (colRepeat > 1) {
334: //in case column is repeated:
335: //for the time being, add the same column
336: //(colRepeat - 1) times to the columns list
337: //TODO: need to force the column-number (?)
338: for (int i = colRepeat - 1; --i >= 0;) {
339: columns.add(col);
340: }
341: }
342: //flag column indices used by this column
343: int startIndex = columnIndex - 1;
344: int endIndex = startIndex + colRepeat;
345: flagColumnIndices(startIndex, endIndex);
346: }
347:
348: /** @return true of table-layout="auto" */
349: public boolean isAutoLayout() {
350: return (tableLayout == EN_AUTO);
351: }
352:
353: /**
354: * Returns the list of table-column elements.
355: *
356: * @return a list of {@link TableColumn} elements, may contain null elements
357: */
358: public List getColumns() {
359: return columns;
360: }
361:
362: /** @return the body for the table-header. */
363: public TableBody getTableHeader() {
364: return tableHeader;
365: }
366:
367: /** @return the body for the table-footer. */
368: public TableBody getTableFooter() {
369: return tableFooter;
370: }
371:
372: /** @return true if the table-header should be omitted at breaks */
373: public boolean omitHeaderAtBreak() {
374: return (this .tableOmitHeaderAtBreak == EN_TRUE);
375: }
376:
377: /** @return true if the table-footer should be omitted at breaks */
378: public boolean omitFooterAtBreak() {
379: return (this .tableOmitFooterAtBreak == EN_TRUE);
380: }
381:
382: /**
383: * @return the "inline-progression-dimension" property.
384: */
385: public LengthRangeProperty getInlineProgressionDimension() {
386: return inlineProgressionDimension;
387: }
388:
389: /**
390: * @return the "block-progression-dimension" property.
391: */
392: public LengthRangeProperty getBlockProgressionDimension() {
393: return blockProgressionDimension;
394: }
395:
396: /**
397: * @return the Common Margin Properties-Block.
398: */
399: public CommonMarginBlock getCommonMarginBlock() {
400: return commonMarginBlock;
401: }
402:
403: /**
404: * @return the Common Border, Padding, and Background Properties.
405: */
406: public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
407: return commonBorderPaddingBackground;
408: }
409:
410: /** @return the "break-after" property. */
411: public int getBreakAfter() {
412: return breakAfter;
413: }
414:
415: /** @return the "break-before" property. */
416: public int getBreakBefore() {
417: return breakBefore;
418: }
419:
420: /** @return the "keep-with-next" property. */
421: public KeepProperty getKeepWithNext() {
422: return keepWithNext;
423: }
424:
425: /** @return the "keep-with-previous" property. */
426: public KeepProperty getKeepWithPrevious() {
427: return keepWithPrevious;
428: }
429:
430: /** @return the "keep-together" property. */
431: public KeepProperty getKeepTogether() {
432: return keepTogether;
433: }
434:
435: /**
436: * Convenience method to check if a keep-together constraint is specified.
437: * @return true if keep-together is active.
438: */
439: public boolean mustKeepTogether() {
440: return !getKeepTogether().getWithinPage().isAuto()
441: || !getKeepTogether().getWithinColumn().isAuto();
442: }
443:
444: /** @return the "border-collapse" property. */
445: public int getBorderCollapse() {
446: return borderCollapse;
447: }
448:
449: /** @return true if the separate border model is active */
450: public boolean isSeparateBorderModel() {
451: return (getBorderCollapse() == EN_SEPARATE);
452: }
453:
454: /** @return the "border-separation" property. */
455: public LengthPairProperty getBorderSeparation() {
456: return borderSeparation;
457: }
458:
459: /** @return the "fox:widow-content-limit" extension property */
460: public Length getWidowContentLimit() {
461: return widowContentLimit;
462: }
463:
464: /** @return the "fox:orphan-content-limit" extension property */
465: public Length getOrphanContentLimit() {
466: return orphanContentLimit;
467: }
468:
469: /** @see org.apache.fop.fo.FONode#getLocalName() */
470: public String getLocalName() {
471: return "table";
472: }
473:
474: /** @see org.apache.fop.fo.FObj#getNameId() */
475: public int getNameId() {
476: return FO_TABLE;
477: }
478:
479: /**
480: * Returns the current column index of the Table
481: *
482: * @return the next column number to use
483: */
484: public int getCurrentColumnIndex() {
485: return columnIndex;
486: }
487:
488: /**
489: * Checks if a certain column-number is already occupied
490: *
491: * @param colNr the column-number to check
492: * @return true if column-number is already in use
493: */
494: public boolean isColumnNumberUsed(int colNr) {
495: return usedColumnIndices.get(colNr - 1);
496: }
497:
498: /**
499: * Sets the current column index of the given Table
500: * (used by ColumnNumberPropertyMaker.make() in case the column-number
501: * was explicitly specified)
502: *
503: * @param newIndex the new value for column index
504: */
505: public void setCurrentColumnIndex(int newIndex) {
506: columnIndex = newIndex;
507: }
508:
509: /**
510: * @see org.apache.fop.fo.flow.TableFObj#flagColumnIndices(int, int)
511: */
512: protected void flagColumnIndices(int start, int end) {
513: for (int i = start; i < end; i++) {
514: usedColumnIndices.set(i);
515: }
516: //set index for the next column to use
517: while (usedColumnIndices.get(columnIndex - 1)) {
518: columnIndex++;
519: }
520: }
521:
522: /**
523: * @see org.apache.fop.fo.FONode#clone(FONode, boolean)
524: */
525: public FONode clone(FONode parent, boolean removeChildren)
526: throws FOPException {
527: FObj fobj = (FObj) super .clone(parent, removeChildren);
528: if (removeChildren) {
529: Table t = (Table) fobj;
530: t.columns = null;
531: t.tableHeader = null;
532: t.tableFooter = null;
533: }
534: return fobj;
535: }
536: }
|