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.pexcel.records; 25 26 import java.io.OutputStream; 27 import java.io.InputStream; 28 import java.io.IOException; 29 import java.util.Vector; 30 import java.util.Enumeration; 31 32 import org.openoffice.xmerge.converter.xml.OfficeConstants; 33 import org.openoffice.xmerge.converter.xml.sxc.Format; 34 import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; 35 import org.openoffice.xmerge.converter.xml.sxc.BookSettings; 36 import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; 37 import org.openoffice.xmerge.util.Debug; 38 import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; 39 import org.openoffice.xmerge.converter.xml.sxc.ColumnRowInfo; 40 41 /** 42 * This class is used by <code> PxlDocument</code> to maintain pexcel 43 * workbooks. 44 * 45 * @author Martin Maher 46 */ 47 public class Workbook implements org.openoffice.xmerge.Document, 48 OfficeConstants { 49 50 private Vector fonts = new Vector(); 51 private Vector extendedFormats = new Vector(); 52 private Vector worksheets = new Vector(); 53 private Vector boundsheets = new Vector(); 54 private Vector definedNames = new Vector(); 55 private static final CodePage cp; 56 private static final Window1 win1; 57 private static final BeginningOfFile bof;; 58 private static final Eof eof; 59 private String fileName; 60 61 static { 62 cp = new CodePage(); 63 win1 = new Window1(); 64 bof = new BeginningOfFile(true); 65 eof = new Eof(); 66 } 67 68 69 /** 70 * Constructs a pocket Excel Workbook with the name of the file passed in 71 * as an argument. Also fills out a basic header block containing the 72 * minimum number of objects that can be created at this time. 73 * 74 * @param name Name of the Pocket Excel Data file. (excluding the file 75 * extension) 76 */ Workbook(String name)77 public Workbook(String name) throws IOException { 78 fileName = name + PocketExcelConstants.FILE_EXTENSION; 79 Format defaultFormat = new Format(); 80 FontDescription fd = new FontDescription(defaultFormat); 81 fonts.add(fd); 82 ExtendedFormat xf = new ExtendedFormat(0, defaultFormat); 83 extendedFormats.add(xf); 84 } 85 86 /** 87 * Constructs a pocket Excel Workbook from the 88 * <code>InputStream</code> and assigns it the document name passed in 89 * 90 * @param is InputStream containing a Pocket Excel Data file. 91 */ Workbook(String name, InputStream is)92 public Workbook(String name, InputStream is) throws IOException { 93 read(is); 94 fileName = name; 95 } 96 97 /** 98 * Writes the current workbook to the <code>Outputstream</code> 99 * 100 * @param os The destination outputstream 101 */ write(OutputStream os)102 public void write(OutputStream os) throws IOException { 103 bof.write(os); 104 cp.write(os); 105 for(Enumeration e = definedNames.elements();e.hasMoreElements();) { 106 DefinedName dn = (DefinedName) e.nextElement(); 107 dn.write(os); 108 } 109 win1.write(os); 110 for(Enumeration e = fonts.elements();e.hasMoreElements();) { 111 FontDescription fd = (FontDescription) e.nextElement(); 112 fd.write(os); 113 } 114 for(Enumeration e = extendedFormats.elements();e.hasMoreElements();) { 115 ExtendedFormat xf = (ExtendedFormat) e.nextElement(); 116 xf.write(os); 117 } 118 for(Enumeration e = boundsheets.elements();e.hasMoreElements();) { 119 BoundSheet bs = (BoundSheet) e.nextElement(); 120 bs.write(os); 121 } 122 eof.write(os); 123 124 for(Enumeration e = worksheets.elements();e.hasMoreElements();) { 125 Worksheet ws = (Worksheet) e.nextElement(); 126 ws.write(os); 127 } 128 } 129 130 /** 131 * Reads a workbook from the <code>InputStream</code> and contructs a 132 * workbook object from it 133 * 134 * @param is InputStream containing a Pocket Excel Data file. 135 */ read(InputStream is)136 public void read(InputStream is) throws IOException { 137 138 boolean done = false; 139 140 int b = 0; 141 while (!done) 142 { 143 b = is.read(); 144 if (b == -1) 145 { 146 Debug.log(Debug.TRACE,"End of file reached"); 147 break; 148 } 149 150 switch (b) 151 { 152 case PocketExcelConstants.DEFINED_NAME: 153 Debug.log(Debug.TRACE,"NAME: Defined Name (18h)"); 154 DefinedName dn = new DefinedName(is, this); 155 definedNames.add(dn); 156 break; 157 158 case PocketExcelConstants.BOF_RECORD: 159 Debug.log(Debug.TRACE,"BOF Record"); 160 bof.read(is); 161 break; 162 163 case PocketExcelConstants.EOF_MARKER: 164 Debug.log(Debug.TRACE,"EOF Marker"); 165 eof.read(is); 166 Worksheet ws = new Worksheet(this); 167 while(ws.read(is)) { 168 worksheets.add(ws); 169 ws = new Worksheet(this); 170 } 171 break; 172 173 case PocketExcelConstants.FONT_DESCRIPTION: 174 Debug.log(Debug.TRACE,"FONT: Font Description (31h)"); 175 FontDescription fd = new FontDescription(is); 176 fonts.add(fd); 177 break; 178 179 case PocketExcelConstants.WINDOW_INFO: 180 Debug.log(Debug.TRACE,"WINDOW1: Window Information (3Dh) [PXL 2.0]"); 181 win1.read(is); 182 break; 183 184 case PocketExcelConstants.CODEPAGE: 185 Debug.log(Debug.TRACE,"CODEPAGE : Codepage and unknown fields (42h)"); 186 cp.read(is); 187 break; 188 189 case PocketExcelConstants.BOUND_SHEET: 190 Debug.log(Debug.TRACE,"BOUNDSHEET: Sheet Information (85h)"); 191 BoundSheet bs = new BoundSheet(is); 192 boundsheets.add(bs); 193 break; 194 195 case PocketExcelConstants.EXTENDED_FORMAT: 196 Debug.log(Debug.TRACE,"XF: Extended Format (E0h) [PXL 2.0]"); 197 ExtendedFormat xf = new ExtendedFormat(is); 198 extendedFormats.add(xf); 199 break; 200 201 default: 202 b = is.read(); 203 break; 204 } 205 206 } 207 is.close(); 208 } 209 210 /** 211 * Adds a font record to the workbook 212 * 213 * @param f the font record to add 214 */ addFont(FontDescription f)215 public int addFont(FontDescription f) { 216 217 boolean alreadyExists = false; 218 int i = 0; 219 220 for(Enumeration e = fonts.elements();e.hasMoreElements();) { 221 FontDescription fd = (FontDescription) e.nextElement(); 222 if(fd.compareTo(f)) { 223 alreadyExists = true; 224 break; 225 } else { 226 i++; 227 } 228 } 229 230 if(!alreadyExists) 231 fonts.add(f); 232 233 return i; 234 } 235 236 /** 237 * Adds a ExtendedFormat record to the workbook 238 * 239 * @param fmt the font record to add 240 */ addExtendedFormat(Format fmt)241 public int addExtendedFormat(Format fmt) throws IOException { 242 243 FontDescription fd = new FontDescription(fmt); 244 int ixfnt = addFont(fd); 245 ExtendedFormat xf = new ExtendedFormat(ixfnt, fmt); 246 247 boolean alreadyExists = false; 248 int i = 0; 249 250 for(Enumeration e = extendedFormats.elements();e.hasMoreElements();) { 251 ExtendedFormat currentXF = (ExtendedFormat) e.nextElement(); 252 if(xf.compareTo(currentXF)) { 253 alreadyExists = true; 254 break; 255 } else if(!alreadyExists) { 256 i++; 257 } 258 } 259 260 if(!alreadyExists) 261 extendedFormats.add(xf); 262 263 return i; 264 } 265 266 /** 267 * Gets a worksheet at a particular index from mthe current workbook. 268 * 269 * @param index the index of the worksheet to retrieve 270 */ getWorksheet(int index)271 public Worksheet getWorksheet(int index) { 272 273 return ((Worksheet) worksheets.elementAt(index)); 274 } 275 276 /** 277 * Returns a FontDescription indictated by the 278 * index parameter passed in to the method 279 * 280 * @param ixfnt index to the FontDescriptions, this is a 0 based index 281 * @return FontDescription indexed by ixfe 282 */ getFontDescription(int ixfnt)283 public FontDescription getFontDescription(int ixfnt) { 284 285 return (FontDescription) fonts.elementAt(ixfnt); 286 } 287 288 /** 289 * Returns a ExtendedFormat indictated by the 290 * index parameter passed in to the method 291 * 292 * @param ixfe index to the FontDescriptions, this is a 0 based index 293 * @return FontDescription indexed by ixfe 294 */ getExtendedFormat(int ixfe)295 public ExtendedFormat getExtendedFormat(int ixfe) { 296 297 return (ExtendedFormat) extendedFormats.elementAt(ixfe); 298 } 299 300 /** 301 * Returns an enumeration of DefinedNames for this workbook 302 * 303 * @return Enumeration for the DefinedNames 304 */ getDefinedNames()305 public Enumeration getDefinedNames() { 306 307 return definedNames.elements(); 308 } 309 310 /** 311 * Returns an enumeration of <code>Settings</code> for this workbook 312 * 313 * @return Enumeration of <code>Settings</code> 314 */ getSettings()315 public BookSettings getSettings() { 316 317 Vector settingsVector = new Vector(); 318 int index = 0; 319 for(Enumeration e = worksheets.elements();e.hasMoreElements();) { 320 Worksheet ws = (Worksheet) e.nextElement(); 321 SheetSettings s = ws.getSettings(); 322 s.setSheetName(getSheetName(index++)); 323 settingsVector.add(s); 324 } 325 BookSettings bs = new BookSettings(settingsVector); 326 String activeSheetName = getSheetName(win1.getActiveSheet()); 327 bs.setActiveSheet(activeSheetName); 328 return bs; 329 } 330 331 /** 332 * Returns a <code>Vector</code> containing all the worksheet Names 333 * 334 * @return a <code>Vector</code> containing all the worksheet Names 335 */ getWorksheetNames()336 public Vector getWorksheetNames() { 337 338 Vector wsNames = new Vector(); 339 340 for(int i = 0;i < boundsheets.size();i++) { 341 wsNames.add(getSheetName(i)); 342 } 343 344 return wsNames; 345 } 346 347 /** 348 * Returns the name of the worksheet at the specified index 349 * 350 * @return a <code>String</code> containing the name of the worksheet 351 */ getSheetName(int index)352 public String getSheetName(int index) { 353 BoundSheet bs = (BoundSheet) boundsheets.elementAt(index); 354 355 return bs.getSheetName(); 356 } 357 358 /** 359 * Adds a <code>Worksheet</code> to the workbook. 360 * 361 * @param name the name of the <code>Worksheet</code> to be added 362 */ addWorksheet(String name)363 public void addWorksheet(String name) throws IOException { 364 365 BoundSheet bs = new BoundSheet(name); 366 boundsheets.add(bs); 367 368 Worksheet ws = new Worksheet(); 369 worksheets.add(ws); 370 } 371 372 /** 373 * Adds a cell to the current worksheet. 374 * 375 */ addCell(int row,int col, Format fmt, String cellContents)376 public void addCell(int row,int col, Format fmt, String cellContents) 377 throws IOException { 378 379 Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1); 380 int ixfe = addExtendedFormat(fmt); 381 382 String category = fmt.getCategory(); 383 384 // Now the formatting is out of the way add the cell 385 Debug.log(Debug.TRACE,"Cell Format: " + fmt); 386 Debug.log(Debug.TRACE,"Row : " + row); 387 Debug.log(Debug.TRACE,"Col : " + col); 388 if(cellContents.startsWith("=")) { 389 try { 390 Formula f = new Formula(row, col, cellContents, ixfe, fmt, this); 391 currentWS.addCell(f); 392 if(category.equalsIgnoreCase(CELLTYPE_STRING)) { 393 StringValue sv = new StringValue(fmt.getValue()); 394 currentWS.addCell(sv); 395 } 396 } catch(Exception e) { 397 Debug.log(Debug.TRACE, "Parsing Exception thrown : " + e.getMessage()); 398 BoolErrCell errorCell = new BoolErrCell(row, col, ixfe, 0x2A, 1); 399 currentWS.addCell(errorCell); 400 } 401 } else if(category.equalsIgnoreCase(OfficeConstants.CELLTYPE_FLOAT)) { 402 try { 403 FloatNumber num = new FloatNumber(row, col, cellContents, ixfe); 404 currentWS.addCell(num); 405 } catch(Exception e) { 406 Debug.log(Debug.TRACE,"Error could not parse Float " + cellContents); 407 LabelCell lc = new LabelCell(row, col, cellContents, ixfe); 408 currentWS.addCell(lc); 409 } 410 } else { 411 if(cellContents.length()==0) { 412 Debug.log(Debug.TRACE, "Blank Cell"); 413 BlankCell b = new BlankCell(row, col, ixfe); 414 currentWS.addCell(b); 415 } else { 416 Debug.log(Debug.TRACE, "Label Cell : " + cellContents); 417 LabelCell lc = new LabelCell(row, col, cellContents, ixfe); 418 currentWS.addCell(lc); // three because we assume the last three 419 // Records in any worksheet is the selection, 420 // window2 and eof Records 421 } 422 } 423 } 424 425 /** 426 * Will create a number of ColInfo records based on the column widths 427 * based in. 428 * 429 * @param columnRows <code>Vector</code> of <code>ColumnRowInfo</code> 430 */ addColInfo(Vector columnRows)431 public void addColInfo(Vector columnRows) throws IOException { 432 433 Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1); 434 435 int nCols = 0; 436 int nRows = 0; 437 438 Debug.log(Debug.TRACE,"Workbook: addColInfo()"); 439 for(Enumeration e = columnRows.elements();e.hasMoreElements();) { 440 ColumnRowInfo cri =(ColumnRowInfo) e.nextElement(); 441 int ixfe = 0; 442 int size = cri.getSize(); 443 int repeated = cri.getRepeated(); 444 if(cri.isColumn()) { 445 Debug.log(Debug.TRACE,"Workbook: adding ColInfo width = " + size); 446 ColInfo newColInfo = new ColInfo( nCols, 447 nCols+repeated-1, 448 size, ixfe); 449 currentWS.addCol(newColInfo); 450 nCols += repeated; 451 } else if(cri.isRow()) { 452 453 Debug.log(Debug.TRACE,"Workbook: adding Row Height = " + size); 454 if(!cri.isDefaultSize()) { 455 for(int i=0;i<repeated;i++) { 456 Row newRow = new Row(nRows++, size, cri.isUserDefined()); 457 currentWS.addRow(newRow); 458 } 459 } else { 460 // If it is the Default Row we don't need to add it 461 nRows += repeated; 462 } 463 464 } 465 } 466 } 467 addNameDefinition(NameDefinition nameDefinition)468 public void addNameDefinition(NameDefinition nameDefinition) throws IOException { 469 470 DefinedName dn = new DefinedName(nameDefinition, this); 471 definedNames.add(dn); 472 } 473 474 /** 475 * Adds the <code>BookSettings</code> for this workbook. 476 * 477 * @param book the <code>BookSettings</code> to add 478 */ addSettings(BookSettings book)479 public void addSettings(BookSettings book) throws IOException { 480 481 int index = 0; 482 Vector sheetSettings = book.getSheetSettings(); 483 String activeSheetName = book.getActiveSheet(); 484 485 for(Enumeration e = worksheets.elements();e.hasMoreElements();) { 486 Worksheet ws = (Worksheet) e.nextElement(); 487 String name = getSheetName(index++); 488 if(activeSheetName.equals(name)) { 489 win1.setActiveSheet(index-1); 490 } 491 for(Enumeration eSettings = sheetSettings.elements();eSettings.hasMoreElements();) { 492 SheetSettings s = (SheetSettings) eSettings.nextElement(); 493 if(name.equals(s.getSheetName())) { 494 ws.addSettings(s); 495 } 496 } 497 } 498 } 499 500 /** 501 * Return the filename of the pxl document without the file extension 502 * 503 * @return filename without the file extension 504 */ getName()505 public String getName() { 506 507 // We have to strip off the file extension 508 int end = fileName.lastIndexOf("."); 509 String name; 510 if( end >= 0) // check in case the filename is already stripped 511 name = fileName.substring(0, end); 512 else 513 name = fileName; 514 515 return name; 516 } 517 518 /** 519 * Returns the filename of the pxl document with the file extension 520 * 521 * @return filename with the file extension 522 */ getFileName()523 public String getFileName() { 524 525 return fileName; 526 } 527 528 } 529