source: osm/applications/editors/josm/plugins/opendata/includes/org/apache/poi/hssf/model/InternalWorkbook.java@ 28000

Last change on this file since 28000 was 28000, checked in by donvip, 12 years ago

Import new "opendata" JOSM plugin

File size: 17.6 KB
Line 
1/* ====================================================================
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16==================================================================== */
17
18package org.apache.poi.hssf.model;
19
20import java.util.ArrayList;
21import java.util.LinkedHashMap;
22import java.util.List;
23import java.util.Map;
24
25import org.apache.poi.hssf.record.BoundSheetRecord;
26import org.apache.poi.hssf.record.DateWindow1904Record;
27import org.apache.poi.hssf.record.EOFRecord;
28import org.apache.poi.hssf.record.ExtSSTRecord;
29import org.apache.poi.hssf.record.ExtendedFormatRecord;
30import org.apache.poi.hssf.record.ExternSheetRecord;
31import org.apache.poi.hssf.record.FormatRecord;
32import org.apache.poi.hssf.record.HyperlinkRecord;
33import org.apache.poi.hssf.record.NameCommentRecord;
34import org.apache.poi.hssf.record.NameRecord;
35import org.apache.poi.hssf.record.Record;
36import org.apache.poi.hssf.record.SSTRecord;
37import org.apache.poi.hssf.record.SupBookRecord;
38import org.apache.poi.hssf.record.TabIdRecord;
39import org.apache.poi.hssf.record.WindowOneRecord;
40import org.apache.poi.hssf.record.common.UnicodeString;
41import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
42import org.apache.poi.util.Internal;
43import org.apache.poi.util.POILogFactory;
44import org.apache.poi.util.POILogger;
45
46/**
47 * Low level model implementation of a Workbook. Provides creational methods
48 * for settings and objects contained in the workbook object.
49 * <P>
50 * This file contains the low level binary records starting at the workbook's BOF and
51 * ending with the workbook's EOF. Use HSSFWorkbook for a high level representation.
52 * <P>
53 * The structures of the highlevel API use references to this to perform most of their
54 * operations. Its probably unwise to use these low level structures directly unless you
55 * really know what you're doing. I recommend you read the Microsoft Excel 97 Developer's
56 * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
57 * before even attempting to use this.
58 *
59 *
60 * @author Luc Girardin (luc dot girardin at macrofocus dot com)
61 * @author Sergei Kozello (sergeikozello at mail.ru)
62 * @author Shawn Laubach (slaubach at apache dot org) (Data Formats)
63 * @author Andrew C. Oliver (acoliver at apache dot org)
64 * @author Brian Sanders (bsanders at risklabs dot com) - custom palette
65 * @author Dan Sherman (dsherman at isisph.com)
66 * @author Glen Stampoultzis (glens at apache.org)
67 * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
68 */
69@Internal
70public final class InternalWorkbook {
71
72
73 private static final POILogger log = POILogFactory.getLogger(InternalWorkbook.class);
74 private static final int DEBUG = POILogger.DEBUG;
75
76 /**
77 * this contains the Worksheet record objects
78 */
79 private final WorkbookRecordList records;
80
81 /**
82 * this contains a reference to the SSTRecord so that new stings can be added
83 * to it.
84 */
85 protected SSTRecord sst;
86
87
88 private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
89
90 /**
91 * holds the "boundsheet" records (aka bundlesheet) so that they can have their
92 * reference to their "BOF" marker
93 */
94 private final List<BoundSheetRecord> boundsheets;
95 private final List<FormatRecord> formats;
96 private final List<HyperlinkRecord> hyperlinks;
97
98 /** the number of extended format records */
99 private int numxfs;
100 /** holds the max format id */
101 private int maxformatid;
102 /** whether 1904 date windowing is being used */
103 private boolean uses1904datewindowing;
104 private WindowOneRecord windowOne;
105
106 /**
107 * Hold the {@link NameCommentRecord}s indexed by the name of the {@link NameRecord} to which they apply.
108 */
109 private final Map<String, NameCommentRecord> commentRecords;
110
111 private InternalWorkbook() {
112 records = new WorkbookRecordList();
113
114 boundsheets = new ArrayList<BoundSheetRecord>();
115 formats = new ArrayList<FormatRecord>();
116 hyperlinks = new ArrayList<HyperlinkRecord>();
117 numxfs = 0;
118 maxformatid = -1;
119 uses1904datewindowing = false;
120 commentRecords = new LinkedHashMap<String, NameCommentRecord>();
121 }
122
123 /**
124 * read support for low level
125 * API. Pass in an array of Record objects, A Workbook
126 * object is constructed and passed back with all of its initialization set
127 * to the passed in records and references to those records held. Unlike Sheet
128 * workbook does not use an offset (its assumed to be 0) since its first in a file.
129 * If you need an offset then construct a new array with a 0 offset or write your
130 * own ;-p.
131 *
132 * @param recs an array of Record objects
133 * @return Workbook object
134 */
135 public static InternalWorkbook createWorkbook(List<Record> recs) {
136 if (log.check( POILogger.DEBUG ))
137 log.log(DEBUG, "Workbook (readfile) created with reclen=",
138 Integer.valueOf(recs.size()));
139 InternalWorkbook retval = new InternalWorkbook();
140 List<Record> records = new ArrayList<Record>(recs.size() / 3);
141 retval.records.setRecords(records);
142
143 int k;
144 for (k = 0; k < recs.size(); k++) {
145 Record rec = recs.get(k);
146
147 if (rec.getSid() == EOFRecord.sid) {
148 records.add(rec);
149 if (log.check( POILogger.DEBUG ))
150 log.log(DEBUG, "found workbook eof record at " + k);
151 break;
152 }
153 switch (rec.getSid()) {
154
155 case BoundSheetRecord.sid :
156 if (log.check( POILogger.DEBUG ))
157 log.log(DEBUG, "found boundsheet record at " + k);
158 retval.boundsheets.add((BoundSheetRecord) rec);
159 retval.records.setBspos( k );
160 break;
161
162 case SSTRecord.sid :
163 if (log.check( POILogger.DEBUG ))
164 log.log(DEBUG, "found sst record at " + k);
165 retval.sst = ( SSTRecord ) rec;
166 break;
167
168 case ExtendedFormatRecord.sid :
169 if (log.check( POILogger.DEBUG ))
170 log.log(DEBUG, "found XF record at " + k);
171 retval.records.setXfpos( k );
172 retval.numxfs++;
173 break;
174
175 case TabIdRecord.sid :
176 if (log.check( POILogger.DEBUG ))
177 log.log(DEBUG, "found tabid record at " + k);
178 retval.records.setTabpos( k );
179 break;
180
181 case ExternSheetRecord.sid :
182 throw new RuntimeException("Extern sheet is part of LinkTable");
183 case NameRecord.sid :
184 case SupBookRecord.sid :
185 // LinkTable can start with either of these
186 if (log.check( POILogger.DEBUG ))
187 log.log(DEBUG, "found SupBook record at " + k);
188 retval.linkTable = new LinkTable(recs, k, retval.records, retval.commentRecords);
189 k+=retval.linkTable.getRecordCount() - 1;
190 continue;
191 case FormatRecord.sid :
192 if (log.check( POILogger.DEBUG ))
193 log.log(DEBUG, "found format record at " + k);
194 retval.formats.add((FormatRecord) rec);
195 retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode();
196 break;
197 case DateWindow1904Record.sid :
198 if (log.check( POILogger.DEBUG ))
199 log.log(DEBUG, "found datewindow1904 record at " + k);
200 retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1;
201 break;
202 case WindowOneRecord.sid:
203 if (log.check( POILogger.DEBUG ))
204 log.log(DEBUG, "found WindowOneRecord at " + k);
205 retval.windowOne = (WindowOneRecord) rec;
206 break;
207 case NameCommentRecord.sid:
208 final NameCommentRecord ncr = (NameCommentRecord) rec;
209 if (log.check( POILogger.DEBUG ))
210 log.log(DEBUG, "found NameComment at " + k);
211 retval.commentRecords.put(ncr.getNameText(), ncr);
212 default :
213 }
214 records.add(rec);
215 }
216 //What if we dont have any ranges and supbooks
217 // if (retval.records.supbookpos == 0) {
218 // retval.records.supbookpos = retval.records.bspos + 1;
219 // retval.records.namepos = retval.records.supbookpos + 1;
220 // }
221
222 // Look for other interesting values that
223 // follow the EOFRecord
224 for ( ; k < recs.size(); k++) {
225 Record rec = recs.get(k);
226 switch (rec.getSid()) {
227 case HyperlinkRecord.sid:
228 retval.hyperlinks.add((HyperlinkRecord)rec);
229 break;
230 }
231 }
232
233 if (retval.windowOne == null) {
234 retval.windowOne = createWindowOne();
235 }
236 if (log.check( POILogger.DEBUG ))
237 log.log(DEBUG, "exit create workbook from existing file function");
238 return retval;
239 }
240
241 public int getNumRecords() {
242 return records.size();
243 }
244
245
246 private BoundSheetRecord getBoundSheetRec(int sheetIndex) {
247 return boundsheets.get(sheetIndex);
248 }
249
250
251 /**
252 * gets the name for a given sheet.
253 *
254 * @param sheetIndex the sheet number (0 based)
255 * @return sheetname the name for the sheet
256 */
257 public String getSheetName(int sheetIndex) {
258 return getBoundSheetRec(sheetIndex).getSheetname();
259 }
260
261 /**
262 * gets the ExtendedFormatRecord at the given 0-based index
263 *
264 * @param index of the Extended format record (0-based)
265 * @return ExtendedFormatRecord at the given index
266 */
267
268 public ExtendedFormatRecord getExFormatAt(int index) {
269 int xfptr = records.getXfpos() - (numxfs - 1);
270
271 xfptr += index;
272 ExtendedFormatRecord retval =
273 ( ExtendedFormatRecord ) records.get(xfptr);
274
275 return retval;
276 }
277
278 /**
279 * Adds a string to the SST table and returns its index (if its a duplicate
280 * just returns its index and update the counts) ASSUMES compressed unicode
281 * (meaning 8bit)
282 *
283 * @param string the string to be added to the SSTRecord
284 *
285 * @return index of the string within the SSTRecord
286 */
287
288 public int addSSTString(UnicodeString string) {
289 if (log.check( POILogger.DEBUG ))
290 log.log(DEBUG, "insert to sst string='", string);
291 if (sst == null) {
292 insertSST();
293 }
294 return sst.addString(string);
295 }
296
297 /**
298 * given an index into the SST table, this function returns the corresponding String value
299 * @return String containing the SST String
300 */
301
302 public UnicodeString getSSTString(int str) {
303 if (sst == null) {
304 insertSST();
305 }
306 UnicodeString retval = sst.getString(str);
307
308 if (log.check( POILogger.DEBUG ))
309 log.log(DEBUG, "Returning SST for index=", Integer.valueOf(str),
310 " String= ", retval);
311 return retval;
312 }
313
314 /**
315 * use this function to add a Shared String Table to an existing sheet (say
316 * generated by a different java api) without an sst....
317 * @see #createExtendedSST()
318 * @see org.apache.poi.hssf.record.SSTRecord
319 */
320
321 public void insertSST() {
322 if (log.check( POILogger.DEBUG ))
323 log.log(DEBUG, "creating new SST via insertSST!");
324 sst = new SSTRecord();
325 records.add(records.size() - 1, createExtendedSST());
326 records.add(records.size() - 2, sst);
327 }
328
329 /**
330 * Serializes all records int the worksheet section into a big byte array. Use
331 * this to write the Workbook out.
332 *
333 * @return byte array containing the HSSF-only portions of the POIFS file.
334 */
335 // GJS: Not used so why keep it.
336// public byte [] serialize() {
337// log.log(DEBUG, "Serializing Workbook!");
338// byte[] retval = null;
339//
340//// ArrayList bytes = new ArrayList(records.size());
341// int arraysize = getSize();
342// int pos = 0;
343//
344// retval = new byte[ arraysize ];
345// for (int k = 0; k < records.size(); k++) {
346//
347// Record record = records.get(k);
348//// Let's skip RECALCID records, as they are only use for optimization
349// if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
350// pos += record.serialize(pos, retval); // rec.length;
351// }
352// }
353// log.log(DEBUG, "Exiting serialize workbook");
354// return retval;
355// }
356
357 /**
358 * creates the WindowOne record with the following magic values: <P>
359 * horizontal hold - 0x168 <P>
360 * vertical hold - 0x10e <P>
361 * width - 0x3a5c <P>
362 * height - 0x23be <P>
363 * options - 0x38 <P>
364 * selected tab - 0 <P>
365 * displayed tab - 0 <P>
366 * num selected tab- 0 <P>
367 * tab width ratio - 0x258 <P>
368 */
369 private static WindowOneRecord createWindowOne() {
370 WindowOneRecord retval = new WindowOneRecord();
371
372 retval.setHorizontalHold(( short ) 0x168);
373 retval.setVerticalHold(( short ) 0x10e);
374 retval.setWidth(( short ) 0x3a5c);
375 retval.setHeight(( short ) 0x23be);
376 retval.setOptions(( short ) 0x38);
377 retval.setActiveSheetIndex( 0x0);
378 retval.setFirstVisibleTab(0x0);
379 retval.setNumSelectedTabs(( short ) 1);
380 retval.setTabWidthRatio(( short ) 0x258);
381 return retval;
382 }
383
384 /**
385 * Creates the ExtendedSST record with numstrings per bucket set to 0x8. HSSF
386 * doesn't yet know what to do with this thing, but we create it with nothing in
387 * it hardly just to make Excel happy and our sheets look like Excel's
388 */
389 private static ExtSSTRecord createExtendedSST() {
390 ExtSSTRecord retval = new ExtSSTRecord();
391 retval.setNumStringsPerBucket(( short ) 0x8);
392 return retval;
393 }
394
395 /** finds the sheet name by his extern sheet index
396 * @param externSheetIndex extern sheet index
397 * @return sheet name.
398 */
399 public String findSheetNameFromExternSheet(int externSheetIndex){
400
401 int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
402 if (indexToSheet < 0) {
403 // TODO - what does '-1' mean here?
404 //error check, bail out gracefully!
405 return "";
406 }
407 if (indexToSheet >= boundsheets.size()) {
408 // Not sure if this can ever happen (See bug 45798)
409 return ""; // Seems to be what excel would do in this case
410 }
411 return getSheetName(indexToSheet);
412 }
413 public ExternalSheet getExternalSheet(int externSheetIndex) {
414 String[] extNames = linkTable.getExternalBookAndSheetName(externSheetIndex);
415 if (extNames == null) {
416 return null;
417 }
418 return new ExternalSheet(extNames[0], extNames[1]);
419 }
420
421 /** gets the total number of names
422 * @return number of names
423 */
424 public int getNumNames(){
425 if(linkTable == null) {
426 return 0;
427 }
428 return linkTable.getNumNames();
429 }
430
431 /** gets the name record
432 * @param index name index
433 * @return name record
434 */
435 public NameRecord getNameRecord(int index){
436 return linkTable.getNameRecord(index);
437 }
438
439
440
441 /**
442 * Returns the list of FormatRecords in the workbook.
443 * @return ArrayList of FormatRecords in the notebook
444 */
445 public List<FormatRecord> getFormats() {
446 return formats;
447 }
448
449 /**
450 * Whether date windowing is based on 1/2/1904 or 1/1/1900.
451 * Some versions of Excel (Mac) can save workbooks using 1904 date windowing.
452 *
453 * @return true if using 1904 date windowing
454 */
455 public boolean isUsing1904DateWindowing() {
456 return uses1904datewindowing;
457 }
458
459 /**
460 * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
461 * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
462 * @return the string representation of the defined or external name
463 */
464 public String resolveNameXText(int refIndex, int definedNameIndex) {
465 return linkTable.resolveNameXText(refIndex, definedNameIndex);
466 }
467}
Note: See TracBrowser for help on using the repository browser.