1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.xmerge.converter.xml.sxc;
25 
26 import java.awt.Color;
27 
28 import org.w3c.dom.NodeList;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NamedNodeMap;
31 import org.w3c.dom.Element;
32 
33 import java.io.IOException;
34 import java.util.Vector;
35 import java.util.Enumeration;
36 
37 import org.openoffice.xmerge.Document;
38 import org.openoffice.xmerge.ConvertData;
39 import org.openoffice.xmerge.ConvertException;
40 import org.openoffice.xmerge.DocumentSerializer;
41 
42 import org.openoffice.xmerge.converter.xml.OfficeConstants;
43 import org.openoffice.xmerge.converter.xml.sxc.SxcDocument;
44 import org.openoffice.xmerge.converter.xml.sxc.CellStyle;
45 import org.openoffice.xmerge.converter.xml.StyleCatalog;
46 
47 import org.openoffice.xmerge.util.Debug;
48 import org.openoffice.xmerge.util.XmlUtil;
49 
50 /**
51  *  <p>General spreadsheet implementation of <code>DocumentSerializer</code>
52  *  for the {@link
53  *  org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory
54  *  SxcPluginFactory}.  Used with SXC <code>Document</code> objects.</p>
55  *
56  *  <p>The <code>serialize</code> method traverses the DOM
57  *  <code>Document</code> from the given <code>Document</code> object.
58  *  It uses a <code>DocEncoder</code> object for the actual conversion
59  *  of contents to the device spreadsheet format.</p>
60  *
61  * @author      Paul Rank
62  * @author      Mark Murnane
63  */
64 public abstract class SxcDocumentSerializer implements OfficeConstants,
65     DocumentSerializer {
66 
67     /**  The cell foreground <code>Color</code>. */
68     private Color foreground = Color.black;
69 
70     /**  The cell background <code>Color</code>. */
71     private Color background = Color.white;
72 
73     /**  The cell format. */
74     private long format = 0;
75 
76     /**  <code>Format</code> object describing the cell. */
77     private Format fmt = null;
78 
79     /**  The row number. */
80     private int rowID = 1;
81 
82     /**  The column number. */
83     private int colID = 1;
84 
85     /**  The number of times the current row is repeated. */
86     private int rowsRepeated = 1;
87 
88     /**  The number of times the current column is repeated. */
89     private int colsRepeated = 1;
90 
91     /**  The number of times the current column is repeated. */
92 	private StyleCatalog styleCat = null;
93     /**
94      *  An array of column widths of the current worksheet.  Width is
95      *  measured in number of characters.
96      */
97     private Vector ColumnRowList;
98 
99     /**  Width, in characters, of the current cell display data. */
100     private int displayWidth = 0;
101 
102     /**
103      *  A <code>SpreadsheetEncoder</code> object for encoding to
104      *  appropriate format.
105      */
106     protected SpreadsheetEncoder encoder = null;
107 
108     /**  <code>SxcDocument</code> object that this converter processes. */
109     protected SxcDocument sxcDoc = null;
110 
111 
112     /**
113      *  Constructor.
114      *
115      *  @param  document  Input <code>SxcDocument</code>
116      *                    <code>Document</code>.
117      */
SxcDocumentSerializer(Document document)118     public SxcDocumentSerializer(Document document) {
119         fmt = new Format();
120         sxcDoc = (SxcDocument) document;
121     }
122 
123 
124     /**
125      *  <p>Method to convert a DOM <code>Document</code> into
126      *  &quot;Device&quot; <code>Document</code> objects.</p>
127      *
128      *  <p>This method is not thread safe for performance reasons.
129      *  This method should not be called from within two threads.
130      *  It would be best to call this method only once per object
131      *  instance.</p>
132      *
133      *  @return  <code>ConvertData</code> containing &quot;Device&quot;
134      *           <code>Document</code> objects.
135      *
136      *  @throws  ConvertException  If any conversion error occurs.
137      *  @throws  IOException       If any I/O error occurs.
138      */
serialize()139     public abstract ConvertData serialize() throws ConvertException,
140         IOException;
141 
142 
143     /**
144      *  This method traverses <i>office:settings</i> <code>Element</code>.
145      *
146      *  @param  node  <i>office:settings</i> <code>Node</code>.
147      *
148      *  @throws  IOException  If any I/O error occurs.
149      */
traverseSettings(Node node)150 	public void traverseSettings(Node node) throws IOException {
151 		if (node.hasChildNodes()) {
152 
153 			NodeList nodeList = node.getChildNodes();
154             int len = nodeList.getLength();
155             for (int i = 0; i < len; i++) {
156 				Node child = nodeList.item(i);
157 
158                 if (child.getNodeType() == Node.ELEMENT_NODE) {
159                     String nodeName = child.getNodeName();
160 
161                     if (nodeName.equals(TAG_CONFIG_ITEM_SET)) {
162 
163                         traverseSettings(child);
164 
165                     } else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_INDEXED)) {
166 
167                         traverseSettings(child);
168 
169  					} else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_ENTRY)) {
170 
171 						BookSettings bs = new BookSettings(child);
172 						encoder.addSettings(bs);
173 
174                     } else {
175 
176                         Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />");
177                     }
178                 }
179             }
180 		}
181 	}
182 
183     /*
184      * Handles the loading of defined styles from the style.xml file as well
185      * as automatic styles from the content.xml file.
186      *
187      * Any change to a defined style, such as a short bold section, falls into
188      * the latter category.
189      */
loadStyles(SxcDocument sxcDoc)190     protected void loadStyles(SxcDocument sxcDoc) {
191 
192         styleCat = new StyleCatalog(25);
193         NodeList nl = null;
194         String families[] = new String[] {	SxcConstants.COLUMN_STYLE_FAMILY,
195 											SxcConstants.ROW_STYLE_FAMILY,
196 											SxcConstants.TABLE_CELL_STYLE_FAMILY };
197         Class classes[]   = new Class[] {	ColumnStyle.class,
198 											RowStyle.class,
199 											CellStyle.class};
200         /*
201          * Process the content XML for any other style info.
202          */
203         org.w3c.dom.Document contentDom = sxcDoc.getContentDOM();
204         nl = contentDom.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES);
205         if (nl.getLength() != 0) {
206         	styleCat.add(nl.item(0), families, classes, null, false);
207         }
208 
209         org.w3c.dom.Document stylesDom = sxcDoc.getStyleDOM();
210         nl = stylesDom.getElementsByTagName(TAG_OFFICE_STYLES);
211         if (nl.getLength() != 0) {
212         	styleCat.add(nl.item(0), families, classes, null, false);
213         }
214     }
215 
216     /**
217      *  This method traverses <i>office:body</i> <code>Element</code>.
218      *
219      *  @param  node  <i>office:body</i> <code>Node</code>.
220      *
221      *  @throws  IOException  If any I/O error occurs.
222      */
traverseBody(Node node)223     protected void traverseBody(Node node) throws IOException {
224 
225         Debug.log(Debug.TRACE, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
226         Debug.log(Debug.TRACE, "<DEBUGLOG>");
227 
228         if (node.hasChildNodes()) {
229 
230             NodeList nodeList = node.getChildNodes();
231             int len = nodeList.getLength();
232 
233             for (int i = 0; i < len; i++) {
234 				Node searchNode = nodeList.item(i);
235                 if (searchNode.getNodeType() == Node.ELEMENT_NODE) {
236 
237                     String nodeName = searchNode.getNodeName();
238 
239                     if (nodeName.equals(TAG_NAMED_EXPRESSIONS)) {
240 
241 						traverseNamedExpressions(searchNode);
242 
243                     } else {
244 
245                         Debug.log(Debug.TRACE, "Skipping " + XmlUtil.getNodeInfo(searchNode) + " />");
246                     }
247                 }
248 			}
249 
250             for (int i = 0; i < len; i++) {
251                 Node child = nodeList.item(i);
252 
253                 if (child.getNodeType() == Node.ELEMENT_NODE) {
254                     String nodeName = child.getNodeName();
255 
256                     if (nodeName.equals(TAG_TABLE)) {
257 
258                         traverseTable(child);
259 
260                     } else {
261 
262                         Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />");
263                     }
264                 }
265             }
266         }
267 
268         Debug.log(Debug.TRACE, "</DEBUGLOG>");
269     }
270 
271 
272     /**
273      *  This method traverses the <i>table:table</i> element
274      *  <code>Node</code>.
275      *
276      *  @param  node  A <i>table:table</i> <code>Node</code>.
277      *
278      *  @throws  IOException  If any I/O error occurs.
279      */
traverseNamedExpressions(Node node)280     protected void traverseNamedExpressions(Node node) throws IOException {
281 
282         Debug.log(Debug.TRACE, "<NAMED:EXPRESSIONS>");
283 
284         NamedNodeMap att = node.getAttributes();
285 
286         if (node.hasChildNodes()) {
287 
288             NodeList nodeList = node.getChildNodes();
289             int len = nodeList.getLength();
290 
291             for (int i = 0; i < len; i++) {
292                 Node child = nodeList.item(i);
293 
294                 if (child.getNodeType() == Node.ELEMENT_NODE) {
295 					NameDefinition nd = new NameDefinition(child);
296 					encoder.setNameDefinition(nd);
297                 }
298             }
299         }
300 
301         Debug.log(Debug.TRACE, "</NAMED:EXPRESSIONS>");
302     }
303 
304     /**
305      *  This method traverses the <i>table:table</i> element
306      *  <code>Node</code>.
307      *
308      *  @param  node  A <i>table:table</i> <code>Node</code>.
309      *
310      *  @throws  IOException  If any I/O error occurs.
311      */
traverseTable(Node node)312     protected void traverseTable(Node node) throws IOException {
313 
314         Debug.log(Debug.TRACE, "<TABLE>");
315 
316         ColumnRowList = new Vector();
317 
318         // Get table attributes
319         // TODO - extract style from attribute
320 
321         NamedNodeMap att = node.getAttributes();
322 
323         String tableName =
324             att.getNamedItem(ATTRIBUTE_TABLE_NAME).getNodeValue();
325 
326         rowID = 1;
327 
328         encoder.createWorksheet(tableName);
329 
330         if (node.hasChildNodes()) {
331 
332             NodeList nodeList = node.getChildNodes();
333             int len = nodeList.getLength();
334 
335             for (int i = 0; i < len; i++) {
336                 Node child = nodeList.item(i);
337 
338                 if (child.getNodeType() == Node.ELEMENT_NODE) {
339                     String nodeName = child.getNodeName();
340 
341                     if (nodeName.equals(TAG_TABLE_ROW)) {
342                         // TODO - handle all the possible rows
343                         // spelled out in the entities
344 
345                         traverseTableRow(child);
346 
347                     } else if (nodeName.equals(TAG_TABLE_COLUMN)) {
348 
349                         traverseTableColumn(child);
350 
351                     } else if (nodeName.equals(TAG_TABLE_SCENARIO)) {
352 
353                         // TODO
354 
355                     } else {
356 
357                         Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />");
358                     }
359                 }
360             }
361         }
362 
363         // Add column width info to the current sheet
364         encoder.setColumnRows(ColumnRowList);
365 
366         Debug.log(Debug.TRACE, "</TABLE>");
367     }
368 
369     /**
370      *  This method traverses the <i>table:table-row</i> element
371      *  <code>Node</code>.
372      *
373      *  @param  node  A <i>table:table-row</i> <code>Node</code>.
374      *
375      *  @throws  IOException  If any I/O error occurs.
376      */
traverseTableRow(Node node)377     protected void traverseTableRow(Node node) throws IOException {
378 
379         // Get the attributes of the row
380         NamedNodeMap cellAtt = node.getAttributes();
381 
382         if (cellAtt != null) {
383 
384 			Node rowStyle =
385                 cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME);
386 
387 			Node tableNumRowRepeatingNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
388 			int repeatedRows = 1;
389 
390 			if(tableNumRowRepeatingNode!=null) {
391 				String repeatStr = tableNumRowRepeatingNode.getNodeValue();
392 				Debug.log(Debug.TRACE, "traverseTableRow() repeated-rows : " + repeatStr);
393 				repeatedRows = Integer.parseInt(repeatStr);
394 			}
395 
396 			String styleName = new String("");
397 
398 			if ( rowStyle != null) {
399 				styleName = rowStyle.getNodeValue();
400 			}
401 			if(styleName.equalsIgnoreCase("Default") || styleName.length()==0) {
402 
403 				Debug.log(Debug.TRACE, "No defined Row Style Attribute was found");
404 
405 			} else {
406 
407         		RowStyle rStyle = (	RowStyle)styleCat.lookup(styleName,
408 										SxcConstants.ROW_STYLE_FAMILY, null,
409 										RowStyle.class);
410 
411 				int rowHeight = rStyle.getRowHeight();
412 
413 				Debug.log(Debug.TRACE, "traverseTableRow() Row Height : " + rowHeight);
414 				ColumnRowInfo ri = new ColumnRowInfo(	rowHeight,
415 															repeatedRows,
416 															ColumnRowInfo.ROW,
417 															rowHeight!=0);
418 				ColumnRowList.add(ri);
419 			}
420 
421             // Get the attribute representing the number of rows repeated
422             Node rowsRepeatedNode =
423                 cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
424 
425             // There is a number of rows repeated attribute:
426             if (rowsRepeatedNode != null) {
427 
428                 // Get the number of times the row is repeated
429                 String rowsRepeatedString = rowsRepeatedNode.getNodeValue();
430 
431                 Integer rowsRepeatedInt = new Integer(rowsRepeatedString);
432 
433                 rowsRepeated = rowsRepeatedInt.intValue();
434 
435             } else {
436 
437                 // The row is not repeated
438                 rowsRepeated = 1;
439             }
440         }
441 
442         Debug.log(Debug.TRACE, "<TR>");
443 
444         if (node.hasChildNodes()) {
445 
446             NodeList nodeList = node.getChildNodes();
447             int len = nodeList.getLength();
448 
449             for (int i = 0; i < len; i++) {
450                 Node child = nodeList.item(i);
451 
452                 if (child.getNodeType() == Node.ELEMENT_NODE) {
453                     String nodeName = child.getNodeName();
454 
455                     if (nodeName.equals(TAG_TABLE_CELL)) {
456 
457                         traverseCell(child);
458 
459                     } else {
460 
461                         Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />");
462                     }
463                 }
464             }
465         }
466 
467         // Increase the row counter by the number of rows which are repeated
468         rowID += rowsRepeated;
469 
470         // Re-initialize number of rows repeated before processing the next
471         // row data.
472         rowsRepeated = 1;
473 
474         // When starting a new row, set the column counter back to the
475         // first column.
476         colID = 1;
477 
478         // Re-initialize number of columns repeated before processing
479         // the next row data.
480         colsRepeated = 1;
481 
482         Debug.log(Debug.TRACE, "</TR>");
483     }
484 
485 
486     /**
487      *  This method traverses the <i>table:table-column</i>
488      *  <code>Node</code>.  Not yet implemented.
489      *
490      *  @param  node  A <i>table:table-column</i> <code>Node</code>.
491      *
492      *  @throws  IOException  If any I/O error occurs.
493      */
traverseTableColumn(Node node)494     protected void traverseTableColumn(Node node) throws IOException {
495 
496 		Debug.log(Debug.TRACE, "traverseColumn() : ");
497 		NamedNodeMap cellAtt = node.getAttributes();
498 		Node tableStyleNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME);
499 		Node tableNumColRepeatingNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
500 		Node tableDefaultCellStyle = cellAtt.getNamedItem(ATTRIBUTE_DEFAULT_CELL_STYLE);
501 
502 		int repeatedColumns = 1;
503 		int columnWidth = 0;
504 		ColumnRowInfo col = new ColumnRowInfo(ColumnRowInfo.COLUMN);
505 
506 		if(tableNumColRepeatingNode!=null) {
507 			Debug.log(Debug.TRACE, "traverseColumn() repeated-cols : " + tableNumColRepeatingNode.getNodeValue());
508 			repeatedColumns = Integer.parseInt(tableNumColRepeatingNode.getNodeValue());
509 			col.setRepeated(repeatedColumns);
510 		}
511 
512 		String cellStyleName = new String("");
513 
514 		if(tableDefaultCellStyle!=null) {
515 			cellStyleName = tableDefaultCellStyle.getNodeValue();
516 
517 			Debug.log(Debug.TRACE, "traverseColumn() default-cell-style : " + cellStyleName);
518 		}
519 
520 		if(cellStyleName.equalsIgnoreCase("Default") || cellStyleName.length()==0) {
521 
522 			Debug.log(Debug.TRACE, "No default cell Style Attribute was found");
523 
524 		} else {
525 
526         	CellStyle cellStyle = (CellStyle)styleCat.lookup(cellStyleName,
527                                 SxcConstants.TABLE_CELL_STYLE_FAMILY, null,
528                                 CellStyle.class);
529 			Format defaultFmt = new Format(cellStyle.getFormat());
530 			col.setFormat(defaultFmt);
531 		}
532 
533 		String styleName = new String("");
534 
535 		if(tableStyleNode!=null) {
536 			styleName = tableStyleNode.getNodeValue();
537 		}
538 
539 		if(styleName.equalsIgnoreCase("Default") || styleName.length()==0) {
540 
541 			Debug.log(Debug.TRACE, "No defined Style Attribute was found");
542 
543 		} else {
544 
545         	ColumnStyle cStyle = (ColumnStyle)styleCat.lookup(styleName,
546                                 SxcConstants.COLUMN_STYLE_FAMILY, null,
547                                 ColumnStyle.class);
548 
549 			columnWidth = cStyle.getColWidth();
550 			col.setSize(columnWidth);
551 			Debug.log(Debug.TRACE, "traverseColumn() Column Width : " + columnWidth);
552 
553 		}
554 		ColumnRowList.add(col);
555     }
556 
557     /**
558      *  This method traverses a <i>table:table-cell</i> element
559      *  <code>Node</code>.
560      *
561      *  @param  node  a <i>table:table-cell</i> <code>Node</code>.
562      *
563      *  @throws  IOException  if any I/O error occurs.
564      */
traverseCell(Node node)565     protected void traverseCell(Node node) throws IOException {
566 
567         NamedNodeMap cellAtt = node.getAttributes();
568 
569         int debug_i=0;
570         Node debug_attrib = null;
571 		fmt.clearFormatting();
572         if (cellAtt == null || cellAtt.item(0) == null)
573         {
574            Debug.log(Debug.INFO, "No Cell Attributes\n");
575 		   // return;
576         }
577         else
578         {
579            while ((debug_attrib = cellAtt.item(debug_i++)) != null)
580            {
581               Debug.log(Debug.INFO, "Cell Attribute " + debug_i +
582                  ": " + debug_attrib.getNodeName() + " : " +
583                  debug_attrib.getNodeValue() + "\n");
584            }
585         }
586 
587         // Get the type of data in the cell
588         Node tableValueTypeNode =
589             cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE_TYPE);
590 
591         // Get the number of columns this cell is repeated
592         Node colsRepeatedNode =
593             cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
594 
595 		// Get the style type
596 		Node tableStyleNode =
597 			cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME);
598 
599 		String styleName = new String("");
600 
601 		if(tableStyleNode!=null) {
602 			styleName = tableStyleNode.getNodeValue();
603 		}
604 
605 		if(styleName.equalsIgnoreCase("Default")) {
606 
607 			Debug.log(Debug.TRACE, "No defined Style Attribute was found");
608 
609 		} else if(styleName.length()!=0) {
610 
611         	CellStyle cStyle = (CellStyle)styleCat.lookup(styleName,
612                                 SxcConstants.TABLE_CELL_STYLE_FAMILY, null,
613                                 CellStyle.class);
614 
615 			Format definedFormat = cStyle.getFormat();
616 			fmt = new Format(definedFormat);
617 		}
618 
619         // There is a number of cols repeated attribute
620         if (colsRepeatedNode != null) {
621 
622             // Get the number of times the cell is repeated
623             String colsRepeatedString = colsRepeatedNode.getNodeValue();
624 
625             Integer colsRepeatedInt = new Integer(colsRepeatedString);
626             colsRepeated = colsRepeatedInt.intValue();
627         } else {
628 
629             // The cell is not repeated
630             colsRepeated = 1;
631         }
632 
633 
634 		// if there is no style we need to check to see if there is a default
635 		// cell style defined in the table-column's
636 
637 		if (fmt.isDefault() && styleName.length()==0) {
638 			int index = 1;
639 			for(Enumeration e = ColumnRowList.elements();e.hasMoreElements();) {
640 				ColumnRowInfo cri = (ColumnRowInfo) e.nextElement();
641 				if(cri.isColumn()) {
642 					if(colID>=index && colID<(index+cri.getRepeated())) {
643 						fmt = new Format(cri.getFormat());
644 					}
645 					index += cri.getRepeated();
646 				}
647 			}
648 		}
649 
650 
651 		// for (int j = 0; j < colsRepeated; j++) {
652 
653 
654         if (tableValueTypeNode != null) {
655 
656             // Make sure we initialize to 0 the width of the current cell
657             displayWidth = 0;
658 
659             String cellType =
660                 tableValueTypeNode.getNodeValue();
661 
662             if (cellType.equalsIgnoreCase(CELLTYPE_STRING)) {
663 
664                 // has text:p tag
665                 fmt.setCategory(CELLTYPE_STRING);
666 				Node tableStringValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STRING_VALUE);
667 				Debug.log(Debug.TRACE,"Cell Type String :  " + tableStringValueNode);
668 				if(tableStringValueNode != null) {
669 					fmt.setValue(tableStringValueNode.getNodeValue());
670 				}
671 
672             } else if (cellType.equalsIgnoreCase(CELLTYPE_FLOAT)) {
673 
674                 // has table:value attribute
675                 // has text:p tag
676 
677                 // Determine the number of decimal places StarCalc
678                 // is displaying for this floating point output.
679                 fmt.setCategory(CELLTYPE_FLOAT);
680                 fmt.setDecimalPlaces(getDecimalPlaces(node));
681               	Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE);
682 				fmt.setValue(tableValueNode.getNodeValue());
683 
684 
685             } else if (cellType.equalsIgnoreCase(CELLTYPE_TIME)) {
686 
687                 // has table:time-value attribute
688                 // has text:p tag - which is the value we convert
689 
690                 fmt.setCategory(CELLTYPE_TIME);
691 				Node tableTimeNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_TIME_VALUE);
692 				fmt.setValue(tableTimeNode.getNodeValue());
693 
694             } else if (cellType.equalsIgnoreCase(CELLTYPE_DATE)) {
695 
696                 // has table:date-value attribute
697                 // has text:p tag - which is the value we convert
698 
699                 fmt.setCategory(CELLTYPE_DATE);
700 				Node tableDateNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_DATE_VALUE);
701 				fmt.setValue(tableDateNode.getNodeValue());
702 
703             } else if (cellType.equalsIgnoreCase(CELLTYPE_CURRENCY)) {
704 
705                 // has table:currency
706                 // has table:value attribute
707                 // has text:p tag
708 
709                 fmt.setCategory(CELLTYPE_CURRENCY);
710                 fmt.setDecimalPlaces(getDecimalPlaces(node));
711 				Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE);
712 				fmt.setValue(tableValueNode.getNodeValue());
713 
714             } else if (cellType.equalsIgnoreCase(CELLTYPE_BOOLEAN)) {
715 
716                 // has table:boolean-value attribute
717                 // has text:p tag - which is the value we convert
718 
719                 fmt.setCategory(CELLTYPE_BOOLEAN);
720 				Node tableBooleanNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_BOOLEAN_VALUE);
721 				fmt.setValue(tableBooleanNode.getNodeValue());
722 
723             } else if (cellType.equalsIgnoreCase(CELLTYPE_PERCENT)) {
724 
725                 // has table:value attribute
726                 // has text:p tag
727 
728                 fmt.setCategory(CELLTYPE_PERCENT);
729                 fmt.setDecimalPlaces(getDecimalPlaces(node));
730 				Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE);
731 				fmt.setValue(tableValueNode.getNodeValue());
732 
733             } else {
734 
735 				Debug.log(Debug.TRACE,"No defined value type" + cellType);
736                 // Should never get here
737 
738             }
739         }
740 
741         Node tableFormulaNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_FORMULA);
742 
743 		if(tableFormulaNode != null)
744 		{
745 			if(tableValueTypeNode == null) {			// If there is no value-type Node we must assume string-value
746 				fmt.setCategory(CELLTYPE_STRING);
747 				Node tableStringValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STRING_VALUE);
748 				fmt.setValue(tableStringValueNode.getNodeValue());
749 			}
750 			String cellFormula = tableFormulaNode.getNodeValue();
751 			addCell(cellFormula);
752 		} else {
753 
754 			// Text node, Date node, or Time node
755 			//
756 			Debug.log(Debug.INFO,
757 	        "TextNode, DateNode, TimeNode or BooleanNode\n");
758 			// This handles the case where we have style information but no content
759     	    if (node.hasChildNodes()) {
760 				NodeList childList = node.getChildNodes();
761 				int len = childList.getLength();
762 
763 				for (int i = 0; i < len; i++) {
764 					Node child = childList.item(i);
765 					if (child.getNodeType() == Node.ELEMENT_NODE) {
766 						String childName = child.getNodeName();
767 						if (childName.equals(TAG_PARAGRAPH)) {
768 							traverseParagraph(child);
769 						}
770 					}
771 				}
772 			} else if(!fmt.isDefault()) {
773 				addCell("");
774 			}
775 		}
776 
777         // Clear out format for current cell after it is written
778         format = 0;
779 
780         // Increase the column counter by the number of times the
781         // last cell was repeated.
782         colID += colsRepeated;
783 
784         // Re-initialize the number of columns repeated before processing
785         // the next cell data.
786         colsRepeated = 1;
787 
788     }
789 
790 
791     /**
792      *  This method traverses the <i>text:p</i> element <code>Node</code>.
793      *
794      *  @param  node  A <i>text:p</i> <code>Node</code>.
795      *
796      *  @throws  IOException  If any I/O error occurs.
797      */
traverseParagraph(Node node)798     protected void traverseParagraph(Node node) throws IOException {
799 
800         NamedNodeMap cellAtt = node.getAttributes();
801 
802         int debug_i=0;
803         Node debug_attrib = null;
804         if (cellAtt == null || cellAtt.item(0) == null)
805         {
806            Debug.log(Debug.INFO, "No Paragraph Attributes\n");
807         }
808         else
809         {
810            while ((debug_attrib = cellAtt.item(debug_i++)) != null)
811            {
812               Debug.log(Debug.INFO, "Paragraph Attribute " + debug_i +
813                  ": " + debug_attrib.getNodeName() + " : " +
814                  debug_attrib.getNodeValue() + "\n");
815            }
816         }
817 
818         if (node.hasChildNodes()) {
819 
820             NodeList nodeList = node.getChildNodes();
821 
822             int len = nodeList.getLength();
823 
824             StringBuffer buffer = new StringBuffer();
825 
826             for (int i = 0; i < len; i++) {
827 
828                 Node child = nodeList.item(i);
829 
830                 // TODO: need to handle space/tabs/newline nodes later
831                 short nodeType = child.getNodeType();
832 
833                 switch (nodeType) {
834 
835                     case Node.TEXT_NODE:
836                         buffer.append(child.getNodeValue());
837                         break;
838 
839                     case Node.ENTITY_REFERENCE_NODE:
840 
841                         NodeList nodeList2 = child.getChildNodes();
842                         int len2 = nodeList2.getLength();
843 
844                         for (int j = 0; j < len2; j++) {
845                             Node child2 = nodeList2.item(j);
846 
847                             if (child2.getNodeType() == Node.TEXT_NODE) {
848                                 buffer.append(child2.getNodeValue());
849                             }
850                         }
851 
852                         break;
853                 }
854             }
855 
856             String s = buffer.toString();
857             // displayWidth = calculateContentWidth(s);
858             addCell(s);
859 
860         }
861     }
862 
863 
864     /**
865      *  This method will take the input cell value and add
866      *  it to the spreadsheet <code>Document</code> we are currently
867      *  encoding.  This method correctly handles cells that are
868      *  repeated in either the row, cell, or both directions.
869      *
870      *  @param  cellValue  The contents of the cell we want to add
871      *                     to the spreadsheet <code>Document</code>.
872      *
873      *  @throws  IOException  If any I/O error occurs.
874      */
addCell(String cellValue)875     protected void addCell(String cellValue) throws IOException {
876 
877         int col = colID;
878         int row = rowID;
879 
880         for (int i = 0; i < rowsRepeated; i++) {
881 
882             // Log the columns when there are rowsRepeated.
883             if (i > 0) {
884                 Debug.log(Debug.TRACE, "</TR>");
885                 Debug.log(Debug.TRACE, "<TR>");
886             }
887 
888             col = colID;
889 
890             for (int j = 0; j < colsRepeated; j++) {
891 
892                 Debug.log(Debug.TRACE, "<TD>");
893 
894 
895                 // Add the cell data to the encoded spreadsheet document
896                 encoder.addCell(row, col, fmt, cellValue);
897 
898                 Debug.log(Debug.TRACE, cellValue);
899                 Debug.log(Debug.TRACE, "</TD>");
900 
901                 col++;
902             }
903 
904             row++;
905 
906         }
907 
908     }
909 
910 
911 
912     /**
913      *  This method takes a <i>table:table-cell</i> <code>Node</code>
914      *  and traverses down to the <i>text:p</i> tag.  The value is
915      *  extracted from the <i>text:p</i> tag and the number of decimal
916      *  places is calculated.
917      *
918      *  @param  node  A <i>table:table-cell</i> <code>Node</code>.
919      *
920      *  @return  The number of decimal places in the display
921      *           string of the data in the input <code>Node</code>.
922      */
getDecimalPlaces(Node node)923     protected int getDecimalPlaces(Node node) {
924 
925         int decimals = 0;
926 
927         Element element = null;
928 
929         // cast org.w3c.dom.Node to org.w3c.dom.Element
930         if (node instanceof Element) {
931             element = (Element) node;
932         } else {
933             return decimals;
934         }
935 
936         //  Traverse to the text:p element, there should only be one.
937         NodeList list = element.getElementsByTagName(TAG_PARAGRAPH);
938 
939         if (list.getLength() != 1) {
940             return decimals;
941         }
942 
943         Node paragraph = list.item(0);
944 
945         if (paragraph.hasChildNodes()) {
946 
947             NodeList nodeList = paragraph.getChildNodes();
948 
949             int len = nodeList.getLength();
950 
951             for (int j = 0; j < len; j++) {
952 
953                 Node child = nodeList.item(j);
954 
955                 if (child.getNodeType() == Node.TEXT_NODE) {
956 
957                     String s = child.getNodeValue();
958 
959                     // displayWidth = calculateContentWidth(s);
960 
961                     int k = s.lastIndexOf(".");
962                     if (k > 0) {
963                         s = s.substring(k+1);
964                         decimals = s.length();
965                     }
966                 }
967             }
968         }
969 
970         return decimals;
971     }
972 
973     /*
974      * Utility method to retrieve a Node attribute.
975      */
getAttribute(Node node, String attribute)976     private String getAttribute (Node node, String attribute) {
977         NamedNodeMap attrNodes = node.getAttributes();
978 
979         if (attrNodes != null) {
980             Node attr = attrNodes.getNamedItem(attribute);
981             if (attr != null) {
982                 return attr.getNodeValue();
983             }
984         }
985         return null;
986     }
987 
988 }
989 
990