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 recrod 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 	 * @return 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 	 * @return the name of the <code>Worksheet</code> to be added
376 	 */
addCell(int row,int col, Format fmt, String cellContents)377 	public void addCell(int row,int col, Format fmt, String cellContents)
378 	throws IOException {
379 
380 		Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1);
381 		int ixfe = addExtendedFormat(fmt);
382 
383 		String category = fmt.getCategory();
384 
385 		// Now the formatting is out of the way add the cell
386 		Debug.log(Debug.TRACE,"Cell Format: " + fmt);
387 		Debug.log(Debug.TRACE,"Row : " + row);
388 		Debug.log(Debug.TRACE,"Col : " + col);
389 		if(cellContents.startsWith("=")) {
390 			try {
391 				Formula f = new Formula(row, col, cellContents, ixfe, fmt, this);
392 				currentWS.addCell(f);
393 				if(category.equalsIgnoreCase(CELLTYPE_STRING)) {
394 					StringValue	sv = new StringValue(fmt.getValue());
395 					currentWS.addCell(sv);
396 				}
397 			} catch(Exception e) {
398 				Debug.log(Debug.TRACE, "Parsing Exception thrown : " + e.getMessage());
399 				BoolErrCell errorCell = new BoolErrCell(row, col, ixfe, 0x2A, 1);
400 				currentWS.addCell(errorCell);
401 			}
402 		} else if(category.equalsIgnoreCase(OfficeConstants.CELLTYPE_FLOAT)) {
403 			try {
404 				FloatNumber num = new FloatNumber(row, col, cellContents, ixfe);
405 				currentWS.addCell(num);
406 			} catch(Exception e) {
407 				Debug.log(Debug.TRACE,"Error could not parse Float " + cellContents);
408 				LabelCell lc = new LabelCell(row, col, cellContents, ixfe);
409 				currentWS.addCell(lc);
410 			}
411 		} else {
412 			if(cellContents.length()==0) {
413 				Debug.log(Debug.TRACE, "Blank Cell");
414 				BlankCell b = new BlankCell(row, col, ixfe);
415 				currentWS.addCell(b);
416 			} else {
417 				Debug.log(Debug.TRACE, "Label Cell : " + cellContents);
418 				LabelCell lc = new LabelCell(row, col, cellContents, ixfe);
419 				currentWS.addCell(lc); 	// three because we assume the last three
420 										// Records in any worksheet is the selection,
421 										// window2 and eof Records
422 			}
423 		}
424 	}
425 
426 	/**
427  	 * Will create a number of ColInfo records based on the column widths
428 	 * based in.
429 	 *
430  	 * @param	columnRows <code>Vector</code> of <code>ColumnRowInfo</code>
431  	 */
addColInfo(Vector columnRows)432 	public void addColInfo(Vector columnRows) throws IOException {
433 
434 		Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1);
435 
436 		int nCols = 0;
437 		int nRows = 0;
438 
439 		Debug.log(Debug.TRACE,"Workbook: addColInfo()");
440 		for(Enumeration e = columnRows.elements();e.hasMoreElements();) {
441 			ColumnRowInfo cri =(ColumnRowInfo) e.nextElement();
442 			int ixfe = 0;
443 			int size = cri.getSize();
444 			int repeated = cri.getRepeated();
445 			if(cri.isColumn()) {
446 				Debug.log(Debug.TRACE,"Workbook: adding ColInfo width = " + size);
447 				ColInfo newColInfo = new ColInfo(	nCols,
448 													nCols+repeated-1,
449 													size, ixfe);
450 				currentWS.addCol(newColInfo);
451 				nCols += repeated;
452 			} else if(cri.isRow()) {
453 
454 				Debug.log(Debug.TRACE,"Workbook: adding Row Height = " + size);
455 				if(!cri.isDefaultSize()) {
456 					for(int i=0;i<repeated;i++) {
457 						Row newRow = new Row(nRows++, size, cri.isUserDefined());
458 						currentWS.addRow(newRow);
459 					}
460 				} else {
461 					// If it is the Default Row we don't need to add it
462 					nRows += repeated;
463 				}
464 
465 			}
466 		}
467 	}
468 
addNameDefinition(NameDefinition nameDefinition)469 	public void addNameDefinition(NameDefinition nameDefinition) throws IOException {
470 
471 		DefinedName dn = new DefinedName(nameDefinition, this);
472 		definedNames.add(dn);
473 	}
474 
475 	/**
476  	 * Adds the <code>BookSettings</code> for this workbook.
477 	 *
478  	 * @param book the <code>BookSettings</code> to add
479  	 */
addSettings(BookSettings book)480 	public void addSettings(BookSettings book) throws IOException {
481 
482 		int index = 0;
483 		Vector sheetSettings = book.getSheetSettings();
484 		String activeSheetName = book.getActiveSheet();
485 
486 		for(Enumeration e = worksheets.elements();e.hasMoreElements();) {
487 			Worksheet ws = (Worksheet) e.nextElement();
488 			String name = getSheetName(index++);
489 			if(activeSheetName.equals(name)) {
490 				win1.setActiveSheet(index-1);
491 			}
492 			for(Enumeration eSettings = sheetSettings.elements();eSettings.hasMoreElements();) {
493 				SheetSettings s = (SheetSettings) eSettings.nextElement();
494 				if(name.equals(s.getSheetName())) {
495 					ws.addSettings(s);
496 				}
497 			}
498 		}
499 	}
500 
501 	/**
502      * Return the filename of the pxl document without the file extension
503 	 *
504 	 * @return filename without the file extension
505 	 */
getName()506 	public String getName() {
507 
508 		// We have to strip off the file extension
509 		int end = fileName.lastIndexOf(".");
510 		String name;
511 		if( end >= 0)	// check in case the filename is already stripped
512 			name = fileName.substring(0, end);
513 		else
514 			name = fileName;
515 
516 		return name;
517 	}
518 
519 	/**
520      * Returns the filename of the pxl document with the file extension
521 	 *
522 	 * @return filename with the file extension
523 	 */
getFileName()524 	public String getFileName() {
525 
526 		return fileName;
527 	}
528 
529 }
530