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 * "Device" <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 "Device" 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