You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InternalWorkbook.java 92KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506
  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. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hssf.model;
  16. import java.security.AccessControlException;
  17. import java.util.ArrayList;
  18. import java.util.Iterator;
  19. import java.util.LinkedHashMap;
  20. import java.util.List;
  21. import java.util.Locale;
  22. import java.util.Map;
  23. import java.util.Map.Entry;
  24. import org.apache.poi.ddf.EscherBSERecord;
  25. import org.apache.poi.ddf.EscherBoolProperty;
  26. import org.apache.poi.ddf.EscherContainerRecord;
  27. import org.apache.poi.ddf.EscherDgRecord;
  28. import org.apache.poi.ddf.EscherDggRecord;
  29. import org.apache.poi.ddf.EscherOptRecord;
  30. import org.apache.poi.ddf.EscherProperties;
  31. import org.apache.poi.ddf.EscherRGBProperty;
  32. import org.apache.poi.ddf.EscherRecord;
  33. import org.apache.poi.ddf.EscherSimpleProperty;
  34. import org.apache.poi.ddf.EscherSpRecord;
  35. import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
  36. import org.apache.poi.hssf.record.BOFRecord;
  37. import org.apache.poi.hssf.record.BackupRecord;
  38. import org.apache.poi.hssf.record.BookBoolRecord;
  39. import org.apache.poi.hssf.record.BoundSheetRecord;
  40. import org.apache.poi.hssf.record.CodepageRecord;
  41. import org.apache.poi.hssf.record.CountryRecord;
  42. import org.apache.poi.hssf.record.DSFRecord;
  43. import org.apache.poi.hssf.record.DateWindow1904Record;
  44. import org.apache.poi.hssf.record.DrawingGroupRecord;
  45. import org.apache.poi.hssf.record.EOFRecord;
  46. import org.apache.poi.hssf.record.EscherAggregate;
  47. import org.apache.poi.hssf.record.ExtSSTRecord;
  48. import org.apache.poi.hssf.record.ExtendedFormatRecord;
  49. import org.apache.poi.hssf.record.ExternSheetRecord;
  50. import org.apache.poi.hssf.record.FileSharingRecord;
  51. import org.apache.poi.hssf.record.FnGroupCountRecord;
  52. import org.apache.poi.hssf.record.FontRecord;
  53. import org.apache.poi.hssf.record.FormatRecord;
  54. import org.apache.poi.hssf.record.HideObjRecord;
  55. import org.apache.poi.hssf.record.HyperlinkRecord;
  56. import org.apache.poi.hssf.record.InterfaceEndRecord;
  57. import org.apache.poi.hssf.record.InterfaceHdrRecord;
  58. import org.apache.poi.hssf.record.MMSRecord;
  59. import org.apache.poi.hssf.record.NameCommentRecord;
  60. import org.apache.poi.hssf.record.NameRecord;
  61. import org.apache.poi.hssf.record.PaletteRecord;
  62. import org.apache.poi.hssf.record.PasswordRecord;
  63. import org.apache.poi.hssf.record.PasswordRev4Record;
  64. import org.apache.poi.hssf.record.PrecisionRecord;
  65. import org.apache.poi.hssf.record.ProtectRecord;
  66. import org.apache.poi.hssf.record.ProtectionRev4Record;
  67. import org.apache.poi.hssf.record.RecalcIdRecord;
  68. import org.apache.poi.hssf.record.Record;
  69. import org.apache.poi.hssf.record.RefreshAllRecord;
  70. import org.apache.poi.hssf.record.SSTRecord;
  71. import org.apache.poi.hssf.record.StyleRecord;
  72. import org.apache.poi.hssf.record.SupBookRecord;
  73. import org.apache.poi.hssf.record.TabIdRecord;
  74. import org.apache.poi.hssf.record.UseSelFSRecord;
  75. import org.apache.poi.hssf.record.WindowOneRecord;
  76. import org.apache.poi.hssf.record.WindowProtectRecord;
  77. import org.apache.poi.hssf.record.WriteAccessRecord;
  78. import org.apache.poi.hssf.record.WriteProtectRecord;
  79. import org.apache.poi.hssf.record.common.UnicodeString;
  80. import org.apache.poi.hssf.util.HSSFColor;
  81. import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
  82. import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
  83. import org.apache.poi.ss.formula.FormulaShifter;
  84. import org.apache.poi.ss.formula.ptg.Area3DPtg;
  85. import org.apache.poi.ss.formula.ptg.NameXPtg;
  86. import org.apache.poi.ss.formula.ptg.OperandPtg;
  87. import org.apache.poi.ss.formula.ptg.Ptg;
  88. import org.apache.poi.ss.formula.ptg.Ref3DPtg;
  89. import org.apache.poi.ss.formula.udf.UDFFinder;
  90. import org.apache.poi.ss.usermodel.BuiltinFormats;
  91. import org.apache.poi.util.Internal;
  92. import org.apache.poi.util.POILogFactory;
  93. import org.apache.poi.util.POILogger;
  94. /**
  95. * Low level model implementation of a Workbook. Provides creational methods
  96. * for settings and objects contained in the workbook object.
  97. * <P>
  98. * This file contains the low level binary records starting at the workbook's BOF and
  99. * ending with the workbook's EOF. Use HSSFWorkbook for a high level representation.
  100. * <P>
  101. * The structures of the highlevel API use references to this to perform most of their
  102. * operations. Its probably unwise to use these low level structures directly unless you
  103. * really know what you're doing. I recommend you read the Microsoft Excel 97 Developer's
  104. * Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
  105. * before even attempting to use this.
  106. *
  107. *
  108. * @author Luc Girardin (luc dot girardin at macrofocus dot com)
  109. * @author Sergei Kozello (sergeikozello at mail.ru)
  110. * @author Shawn Laubach (slaubach at apache dot org) (Data Formats)
  111. * @author Andrew C. Oliver (acoliver at apache dot org)
  112. * @author Brian Sanders (bsanders at risklabs dot com) - custom palette
  113. * @author Dan Sherman (dsherman at isisph.com)
  114. * @author Glen Stampoultzis (glens at apache.org)
  115. * @see org.apache.poi.hssf.usermodel.HSSFWorkbook
  116. */
  117. @Internal
  118. public final class InternalWorkbook {
  119. /**
  120. * Excel silently truncates long sheet names to 31 chars.
  121. * This constant is used to ensure uniqueness in the first 31 chars
  122. */
  123. private static final int MAX_SENSITIVE_SHEET_NAME_LEN = 31;
  124. private static final POILogger log = POILogFactory.getLogger(InternalWorkbook.class);
  125. private static final int DEBUG = POILogger.DEBUG;
  126. /**
  127. * constant used to set the "codepage" wherever "codepage" is set in records
  128. * (which is duplicated in more than one record)
  129. */
  130. private final static short CODEPAGE = 0x04B0;
  131. /**
  132. * this contains the Worksheet record objects
  133. */
  134. private final WorkbookRecordList records;
  135. /**
  136. * this contains a reference to the SSTRecord so that new stings can be added
  137. * to it.
  138. */
  139. protected SSTRecord sst;
  140. private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
  141. /**
  142. * holds the "boundsheet" records (aka bundlesheet) so that they can have their
  143. * reference to their "BOF" marker
  144. */
  145. private final List<BoundSheetRecord> boundsheets;
  146. private final List<FormatRecord> formats;
  147. private final List<HyperlinkRecord> hyperlinks;
  148. /** the number of extended format records */
  149. private int numxfs;
  150. /** the number of font records */
  151. private int numfonts;
  152. /** holds the max format id */
  153. private int maxformatid;
  154. /** whether 1904 date windowing is being used */
  155. private boolean uses1904datewindowing;
  156. private DrawingManager2 drawingManager;
  157. private List<EscherBSERecord> escherBSERecords;
  158. private WindowOneRecord windowOne;
  159. private FileSharingRecord fileShare;
  160. private WriteAccessRecord writeAccess;
  161. private WriteProtectRecord writeProtect;
  162. /**
  163. * Hold the {@link NameCommentRecord}s indexed by the name of the {@link NameRecord} to which they apply.
  164. */
  165. private final Map<String, NameCommentRecord> commentRecords;
  166. private InternalWorkbook() {
  167. records = new WorkbookRecordList();
  168. boundsheets = new ArrayList<BoundSheetRecord>();
  169. formats = new ArrayList<FormatRecord>();
  170. hyperlinks = new ArrayList<HyperlinkRecord>();
  171. numxfs = 0;
  172. numfonts = 0;
  173. maxformatid = -1;
  174. uses1904datewindowing = false;
  175. escherBSERecords = new ArrayList<EscherBSERecord>();
  176. commentRecords = new LinkedHashMap<String, NameCommentRecord>();
  177. }
  178. /**
  179. * read support for low level
  180. * API. Pass in an array of Record objects, A Workbook
  181. * object is constructed and passed back with all of its initialization set
  182. * to the passed in records and references to those records held. Unlike Sheet
  183. * workbook does not use an offset (its assumed to be 0) since its first in a file.
  184. * If you need an offset then construct a new array with a 0 offset or write your
  185. * own ;-p.
  186. *
  187. * @param recs an array of Record objects
  188. * @return Workbook object
  189. */
  190. public static InternalWorkbook createWorkbook(List<Record> recs) {
  191. if (log.check( POILogger.DEBUG ))
  192. log.log(DEBUG, "Workbook (readfile) created with reclen=",
  193. Integer.valueOf(recs.size()));
  194. InternalWorkbook retval = new InternalWorkbook();
  195. List<Record> records = new ArrayList<Record>(recs.size() / 3);
  196. retval.records.setRecords(records);
  197. int k;
  198. for (k = 0; k < recs.size(); k++) {
  199. Record rec = recs.get(k);
  200. if (rec.getSid() == EOFRecord.sid) {
  201. records.add(rec);
  202. if (log.check( POILogger.DEBUG ))
  203. log.log(DEBUG, "found workbook eof record at " + k);
  204. break;
  205. }
  206. switch (rec.getSid()) {
  207. case BoundSheetRecord.sid :
  208. if (log.check( POILogger.DEBUG ))
  209. log.log(DEBUG, "found boundsheet record at " + k);
  210. retval.boundsheets.add((BoundSheetRecord) rec);
  211. retval.records.setBspos( k );
  212. break;
  213. case SSTRecord.sid :
  214. if (log.check( POILogger.DEBUG ))
  215. log.log(DEBUG, "found sst record at " + k);
  216. retval.sst = ( SSTRecord ) rec;
  217. break;
  218. case FontRecord.sid :
  219. if (log.check( POILogger.DEBUG ))
  220. log.log(DEBUG, "found font record at " + k);
  221. retval.records.setFontpos( k );
  222. retval.numfonts++;
  223. break;
  224. case ExtendedFormatRecord.sid :
  225. if (log.check( POILogger.DEBUG ))
  226. log.log(DEBUG, "found XF record at " + k);
  227. retval.records.setXfpos( k );
  228. retval.numxfs++;
  229. break;
  230. case TabIdRecord.sid :
  231. if (log.check( POILogger.DEBUG ))
  232. log.log(DEBUG, "found tabid record at " + k);
  233. retval.records.setTabpos( k );
  234. break;
  235. case ProtectRecord.sid :
  236. if (log.check( POILogger.DEBUG ))
  237. log.log(DEBUG, "found protect record at " + k);
  238. retval.records.setProtpos( k );
  239. break;
  240. case BackupRecord.sid :
  241. if (log.check( POILogger.DEBUG ))
  242. log.log(DEBUG, "found backup record at " + k);
  243. retval.records.setBackuppos( k );
  244. break;
  245. case ExternSheetRecord.sid :
  246. throw new RuntimeException("Extern sheet is part of LinkTable");
  247. case NameRecord.sid :
  248. case SupBookRecord.sid :
  249. // LinkTable can start with either of these
  250. if (log.check( POILogger.DEBUG ))
  251. log.log(DEBUG, "found SupBook record at " + k);
  252. retval.linkTable = new LinkTable(recs, k, retval.records, retval.commentRecords);
  253. k+=retval.linkTable.getRecordCount() - 1;
  254. continue;
  255. case FormatRecord.sid :
  256. if (log.check( POILogger.DEBUG ))
  257. log.log(DEBUG, "found format record at " + k);
  258. retval.formats.add((FormatRecord) rec);
  259. retval.maxformatid = retval.maxformatid >= ((FormatRecord)rec).getIndexCode() ? retval.maxformatid : ((FormatRecord)rec).getIndexCode();
  260. break;
  261. case DateWindow1904Record.sid :
  262. if (log.check( POILogger.DEBUG ))
  263. log.log(DEBUG, "found datewindow1904 record at " + k);
  264. retval.uses1904datewindowing = ((DateWindow1904Record)rec).getWindowing() == 1;
  265. break;
  266. case PaletteRecord.sid:
  267. if (log.check( POILogger.DEBUG ))
  268. log.log(DEBUG, "found palette record at " + k);
  269. retval.records.setPalettepos( k );
  270. break;
  271. case WindowOneRecord.sid:
  272. if (log.check( POILogger.DEBUG ))
  273. log.log(DEBUG, "found WindowOneRecord at " + k);
  274. retval.windowOne = (WindowOneRecord) rec;
  275. break;
  276. case WriteAccessRecord.sid:
  277. if (log.check( POILogger.DEBUG ))
  278. log.log(DEBUG, "found WriteAccess at " + k);
  279. retval.writeAccess = (WriteAccessRecord) rec;
  280. break;
  281. case WriteProtectRecord.sid:
  282. if (log.check( POILogger.DEBUG ))
  283. log.log(DEBUG, "found WriteProtect at " + k);
  284. retval.writeProtect = (WriteProtectRecord) rec;
  285. break;
  286. case FileSharingRecord.sid:
  287. if (log.check( POILogger.DEBUG ))
  288. log.log(DEBUG, "found FileSharing at " + k);
  289. retval.fileShare = (FileSharingRecord) rec;
  290. break;
  291. case NameCommentRecord.sid:
  292. final NameCommentRecord ncr = (NameCommentRecord) rec;
  293. if (log.check( POILogger.DEBUG ))
  294. log.log(DEBUG, "found NameComment at " + k);
  295. retval.commentRecords.put(ncr.getNameText(), ncr);
  296. default :
  297. }
  298. records.add(rec);
  299. }
  300. //What if we dont have any ranges and supbooks
  301. // if (retval.records.supbookpos == 0) {
  302. // retval.records.supbookpos = retval.records.bspos + 1;
  303. // retval.records.namepos = retval.records.supbookpos + 1;
  304. // }
  305. // Look for other interesting values that
  306. // follow the EOFRecord
  307. for ( ; k < recs.size(); k++) {
  308. Record rec = recs.get(k);
  309. switch (rec.getSid()) {
  310. case HyperlinkRecord.sid:
  311. retval.hyperlinks.add((HyperlinkRecord)rec);
  312. break;
  313. }
  314. }
  315. if (retval.windowOne == null) {
  316. retval.windowOne = createWindowOne();
  317. }
  318. if (log.check( POILogger.DEBUG ))
  319. log.log(DEBUG, "exit create workbook from existing file function");
  320. return retval;
  321. }
  322. /**
  323. * Creates an empty workbook object with three blank sheets and all the empty
  324. * fields. Use this to create a workbook from scratch.
  325. */
  326. public static InternalWorkbook createWorkbook()
  327. {
  328. if (log.check( POILogger.DEBUG ))
  329. log.log( DEBUG, "creating new workbook from scratch" );
  330. InternalWorkbook retval = new InternalWorkbook();
  331. List<Record> records = new ArrayList<Record>( 30 );
  332. retval.records.setRecords(records);
  333. List<FormatRecord> formats = retval.formats;
  334. records.add(createBOF());
  335. records.add(new InterfaceHdrRecord(CODEPAGE));
  336. records.add(createMMS());
  337. records.add(InterfaceEndRecord.instance);
  338. records.add(createWriteAccess());
  339. records.add(createCodepage());
  340. records.add(createDSF());
  341. records.add(createTabId());
  342. retval.records.setTabpos(records.size() - 1);
  343. records.add(createFnGroupCount());
  344. records.add(createWindowProtect());
  345. records.add(createProtect());
  346. retval.records.setProtpos(records.size() - 1);
  347. records.add(createPassword());
  348. records.add(createProtectionRev4());
  349. records.add(createPasswordRev4());
  350. retval.windowOne = createWindowOne();
  351. records.add(retval.windowOne);
  352. records.add(createBackup());
  353. retval.records.setBackuppos(records.size() - 1);
  354. records.add(createHideObj());
  355. records.add(createDateWindow1904());
  356. records.add(createPrecision());
  357. records.add(createRefreshAll());
  358. records.add(createBookBool());
  359. records.add(createFont());
  360. records.add(createFont());
  361. records.add(createFont());
  362. records.add(createFont());
  363. retval.records.setFontpos( records.size() - 1 ); // last font record position
  364. retval.numfonts = 4;
  365. // set up format records
  366. for (int i = 0; i <= 7; i++) {
  367. FormatRecord rec = createFormat(i);
  368. retval.maxformatid = retval.maxformatid >= rec.getIndexCode() ? retval.maxformatid : rec.getIndexCode();
  369. formats.add(rec);
  370. records.add(rec);
  371. }
  372. for (int k = 0; k < 21; k++) {
  373. records.add(retval.createExtendedFormat(k));
  374. retval.numxfs++;
  375. }
  376. retval.records.setXfpos( records.size() - 1 );
  377. for (int k = 0; k < 6; k++) {
  378. records.add(retval.createStyle(k));
  379. }
  380. records.add(retval.createUseSelFS());
  381. int nBoundSheets = 1; // now just do 1
  382. for (int k = 0; k < nBoundSheets; k++) {
  383. BoundSheetRecord bsr = createBoundSheet(k);
  384. records.add(bsr);
  385. retval.boundsheets.add(bsr);
  386. retval.records.setBspos(records.size() - 1);
  387. }
  388. records.add( retval.createCountry() );
  389. for ( int k = 0; k < nBoundSheets; k++ ) {
  390. retval.getOrCreateLinkTable().checkExternSheet(k);
  391. }
  392. retval.sst = new SSTRecord();
  393. records.add(retval.sst);
  394. records.add(retval.createExtendedSST());
  395. records.add(EOFRecord.instance);
  396. if (log.check( POILogger.DEBUG ))
  397. log.log( DEBUG, "exit create new workbook from scratch" );
  398. return retval;
  399. }
  400. /**Retrieves the Builtin NameRecord that matches the name and index
  401. * There shouldn't be too many names to make the sequential search too slow
  402. * @param name byte representation of the builtin name to match
  403. * @param sheetNumber 1-based sheet number
  404. * @return null if no builtin NameRecord matches
  405. */
  406. public NameRecord getSpecificBuiltinRecord(byte name, int sheetNumber)
  407. {
  408. return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetNumber);
  409. }
  410. /**
  411. * Removes the specified Builtin NameRecord that matches the name and index
  412. * @param name byte representation of the builtin to match
  413. * @param sheetIndex zero-based sheet reference
  414. */
  415. public void removeBuiltinRecord(byte name, int sheetIndex) {
  416. linkTable.removeBuiltinRecord(name, sheetIndex);
  417. // TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}?
  418. }
  419. public int getNumRecords() {
  420. return records.size();
  421. }
  422. /**
  423. * gets the font record at the given index in the font table. Remember
  424. * "There is No Four" (someone at M$ must have gone to Rocky Horror one too
  425. * many times)
  426. *
  427. * @param idx the index to look at (0 or greater but NOT 4)
  428. * @return FontRecord located at the given index
  429. */
  430. public FontRecord getFontRecordAt(int idx) {
  431. int index = idx;
  432. if (index > 4) {
  433. index -= 1; // adjust for "There is no 4"
  434. }
  435. if (index > (numfonts - 1)) {
  436. throw new ArrayIndexOutOfBoundsException(
  437. "There are only " + numfonts
  438. + " font records, you asked for " + idx);
  439. }
  440. FontRecord retval =
  441. ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + index);
  442. return retval;
  443. }
  444. /**
  445. * Retrieves the index of the given font
  446. */
  447. public int getFontIndex(FontRecord font) {
  448. for(int i=0; i<=numfonts; i++) {
  449. FontRecord thisFont =
  450. ( FontRecord ) records.get((records.getFontpos() - (numfonts - 1)) + i);
  451. if(thisFont == font) {
  452. // There is no 4!
  453. if(i > 3) {
  454. return (i+1);
  455. }
  456. return i;
  457. }
  458. }
  459. throw new IllegalArgumentException("Could not find that font!");
  460. }
  461. /**
  462. * creates a new font record and adds it to the "font table". This causes the
  463. * boundsheets to move down one, extended formats to move down (so this function moves
  464. * those pointers as well)
  465. *
  466. * @return FontRecord that was just created
  467. */
  468. public FontRecord createNewFont() {
  469. FontRecord rec = createFont();
  470. records.add(records.getFontpos()+1, rec);
  471. records.setFontpos( records.getFontpos() + 1 );
  472. numfonts++;
  473. return rec;
  474. }
  475. /**
  476. * Removes the given font record from the
  477. * file's list. This will make all
  478. * subsequent font indicies drop by one,
  479. * so you'll need to update those yourself!
  480. */
  481. public void removeFontRecord(FontRecord rec) {
  482. records.remove(rec); // this updates FontPos for us
  483. numfonts--;
  484. }
  485. /**
  486. * gets the number of font records
  487. *
  488. * @return number of font records in the "font table"
  489. */
  490. public int getNumberOfFontRecords() {
  491. return numfonts;
  492. }
  493. /**
  494. * Sets the BOF for a given sheet
  495. *
  496. * @param sheetIndex the number of the sheet to set the positing of the bof for
  497. * @param pos the actual bof position
  498. */
  499. public void setSheetBof(int sheetIndex, int pos) {
  500. if (log.check( POILogger.DEBUG ))
  501. log.log(DEBUG, "setting bof for sheetnum =", Integer.valueOf(sheetIndex),
  502. " at pos=", Integer.valueOf(pos));
  503. checkSheets(sheetIndex);
  504. getBoundSheetRec(sheetIndex)
  505. .setPositionOfBof(pos);
  506. }
  507. private BoundSheetRecord getBoundSheetRec(int sheetIndex) {
  508. return boundsheets.get(sheetIndex);
  509. }
  510. /**
  511. * Returns the position of the backup record.
  512. */
  513. public BackupRecord getBackupRecord() {
  514. return ( BackupRecord ) records.get(records.getBackuppos());
  515. }
  516. /**
  517. * sets the name for a given sheet. If the boundsheet record doesn't exist and
  518. * its only one more than we have, go ahead and create it. If it's > 1 more than
  519. * we have, except
  520. *
  521. * @param sheetnum the sheet number (0 based)
  522. * @param sheetname the name for the sheet
  523. */
  524. public void setSheetName(int sheetnum, String sheetname) {
  525. checkSheets(sheetnum);
  526. // YK: Mimic Excel and silently truncate sheet names longer than 31 characters
  527. if(sheetname.length() > 31) sheetname = sheetname.substring(0, 31);
  528. BoundSheetRecord sheet = boundsheets.get(sheetnum);
  529. sheet.setSheetname(sheetname);
  530. }
  531. /**
  532. * Determines whether a workbook contains the provided sheet name. For the purpose of
  533. * comparison, long names are truncated to 31 chars.
  534. *
  535. * @param name the name to test (case insensitive match)
  536. * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check.
  537. * @return true if the sheet contains the name, false otherwise.
  538. */
  539. public boolean doesContainsSheetName(String name, int excludeSheetIdx) {
  540. String aName = name;
  541. if (aName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  542. aName = aName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  543. }
  544. for (int i = 0; i < boundsheets.size(); i++) {
  545. BoundSheetRecord boundSheetRecord = getBoundSheetRec(i);
  546. if (excludeSheetIdx == i) {
  547. continue;
  548. }
  549. String bName = boundSheetRecord.getSheetname();
  550. if (bName.length() > MAX_SENSITIVE_SHEET_NAME_LEN) {
  551. bName = bName.substring(0, MAX_SENSITIVE_SHEET_NAME_LEN);
  552. }
  553. if (aName.equalsIgnoreCase(bName)) {
  554. return true;
  555. }
  556. }
  557. return false;
  558. }
  559. /**
  560. * sets the order of appearance for a given sheet.
  561. *
  562. * @param sheetname the name of the sheet to reorder
  563. * @param pos the position that we want to insert the sheet into (0 based)
  564. */
  565. public void setSheetOrder(String sheetname, int pos ) {
  566. int sheetNumber = getSheetIndex(sheetname);
  567. //remove the sheet that needs to be reordered and place it in the spot we want
  568. boundsheets.add(pos, boundsheets.remove(sheetNumber));
  569. }
  570. /**
  571. * gets the name for a given sheet.
  572. *
  573. * @param sheetIndex the sheet number (0 based)
  574. * @return sheetname the name for the sheet
  575. */
  576. public String getSheetName(int sheetIndex) {
  577. return getBoundSheetRec(sheetIndex).getSheetname();
  578. }
  579. /**
  580. * Gets the hidden flag for a given sheet.
  581. * Note that a sheet could instead be
  582. * set to be very hidden, which is different
  583. * ({@link #isSheetVeryHidden(int)})
  584. *
  585. * @param sheetnum the sheet number (0 based)
  586. * @return True if sheet is hidden
  587. */
  588. public boolean isSheetHidden(int sheetnum) {
  589. return getBoundSheetRec(sheetnum).isHidden();
  590. }
  591. /**
  592. * Gets the very hidden flag for a given sheet.
  593. * This is different from the normal
  594. * hidden flag
  595. * ({@link #isSheetHidden(int)})
  596. *
  597. * @param sheetnum the sheet number (0 based)
  598. * @return True if sheet is very hidden
  599. */
  600. public boolean isSheetVeryHidden(int sheetnum) {
  601. return getBoundSheetRec(sheetnum).isVeryHidden();
  602. }
  603. /**
  604. * Hide or unhide a sheet
  605. *
  606. * @param sheetnum The sheet number
  607. * @param hidden True to mark the sheet as hidden, false otherwise
  608. */
  609. public void setSheetHidden(int sheetnum, boolean hidden) {
  610. getBoundSheetRec(sheetnum).setHidden(hidden);
  611. }
  612. /**
  613. * Hide or unhide a sheet.
  614. * 0 = not hidden
  615. * 1 = hidden
  616. * 2 = very hidden.
  617. *
  618. * @param sheetnum The sheet number
  619. * @param hidden 0 for not hidden, 1 for hidden, 2 for very hidden
  620. */
  621. public void setSheetHidden(int sheetnum, int hidden) {
  622. BoundSheetRecord bsr = getBoundSheetRec(sheetnum);
  623. boolean h = false;
  624. boolean vh = false;
  625. if(hidden == 0) {
  626. } else if(hidden == 1) {
  627. h = true;
  628. } else if(hidden == 2) {
  629. vh = true;
  630. } else {
  631. throw new IllegalArgumentException("Invalid hidden flag " + hidden + " given, must be 0, 1 or 2");
  632. }
  633. bsr.setHidden(h);
  634. bsr.setVeryHidden(vh);
  635. }
  636. /**
  637. * get the sheet's index
  638. * @param name sheet name
  639. * @return sheet index or -1 if it was not found.
  640. */
  641. public int getSheetIndex(String name) {
  642. int retval = -1;
  643. for (int k = 0; k < boundsheets.size(); k++) {
  644. String sheet = getSheetName(k);
  645. if (sheet.equalsIgnoreCase(name)) {
  646. retval = k;
  647. break;
  648. }
  649. }
  650. return retval;
  651. }
  652. /**
  653. * if we're trying to address one more sheet than we have, go ahead and add it! if we're
  654. * trying to address >1 more than we have throw an exception!
  655. */
  656. private void checkSheets(int sheetnum) {
  657. if ((boundsheets.size()) <= sheetnum) { // if we're short one add another..
  658. if ((boundsheets.size() + 1) <= sheetnum) {
  659. throw new RuntimeException("Sheet number out of bounds!");
  660. }
  661. BoundSheetRecord bsr = createBoundSheet(sheetnum);
  662. records.add(records.getBspos()+1, bsr);
  663. records.setBspos( records.getBspos() + 1 );
  664. boundsheets.add(bsr);
  665. getOrCreateLinkTable().checkExternSheet(sheetnum);
  666. fixTabIdRecord();
  667. }
  668. }
  669. /**
  670. * @param sheetIndex zero based sheet index
  671. */
  672. public void removeSheet(int sheetIndex) {
  673. if (boundsheets.size() > sheetIndex) {
  674. records.remove(records.getBspos() - (boundsheets.size() - 1) + sheetIndex);
  675. boundsheets.remove(sheetIndex);
  676. fixTabIdRecord();
  677. }
  678. // Within NameRecords, it's ok to have the formula
  679. // part point at deleted sheets. It's also ok to
  680. // have the ExternSheetNumber point at deleted
  681. // sheets.
  682. // However, the sheet index must be adjusted, or
  683. // excel will break. (Sheet index is either 0 for
  684. // global, or 1 based index to sheet)
  685. int sheetNum1Based = sheetIndex + 1;
  686. for(int i=0; i<getNumNames(); i++) {
  687. NameRecord nr = getNameRecord(i);
  688. if(nr.getSheetNumber() == sheetNum1Based) {
  689. // Excel re-writes these to point to no sheet
  690. nr.setSheetNumber(0);
  691. } else if(nr.getSheetNumber() > sheetNum1Based) {
  692. // Bump down by one, so still points
  693. // at the same sheet
  694. nr.setSheetNumber(nr.getSheetNumber()-1);
  695. }
  696. }
  697. }
  698. /**
  699. * make the tabid record look like the current situation.
  700. *
  701. * @return number of bytes written in the TabIdRecord
  702. */
  703. private int fixTabIdRecord() {
  704. TabIdRecord tir = ( TabIdRecord ) records.get(records.getTabpos());
  705. int sz = tir.getRecordSize();
  706. short[] tia = new short[ boundsheets.size() ];
  707. for (short k = 0; k < tia.length; k++) {
  708. tia[ k ] = k;
  709. }
  710. tir.setTabIdArray(tia);
  711. return tir.getRecordSize() - sz;
  712. }
  713. /**
  714. * returns the number of boundsheet objects contained in this workbook.
  715. *
  716. * @return number of BoundSheet records
  717. */
  718. public int getNumSheets() {
  719. if (log.check( POILogger.DEBUG ))
  720. log.log(DEBUG, "getNumSheets=", Integer.valueOf(boundsheets.size()));
  721. return boundsheets.size();
  722. }
  723. /**
  724. * get the number of ExtendedFormat records contained in this workbook.
  725. *
  726. * @return int count of ExtendedFormat records
  727. */
  728. public int getNumExFormats() {
  729. if (log.check( POILogger.DEBUG ))
  730. log.log(DEBUG, "getXF=", Integer.valueOf(numxfs));
  731. return numxfs;
  732. }
  733. /**
  734. * gets the ExtendedFormatRecord at the given 0-based index
  735. *
  736. * @param index of the Extended format record (0-based)
  737. * @return ExtendedFormatRecord at the given index
  738. */
  739. public ExtendedFormatRecord getExFormatAt(int index) {
  740. int xfptr = records.getXfpos() - (numxfs - 1);
  741. xfptr += index;
  742. ExtendedFormatRecord retval =
  743. ( ExtendedFormatRecord ) records.get(xfptr);
  744. return retval;
  745. }
  746. /**
  747. * Removes the given ExtendedFormatRecord record from the
  748. * file's list. This will make all
  749. * subsequent font indicies drop by one,
  750. * so you'll need to update those yourself!
  751. */
  752. public void removeExFormatRecord(ExtendedFormatRecord rec) {
  753. records.remove(rec); // this updates XfPos for us
  754. numxfs--;
  755. }
  756. /**
  757. * Removes ExtendedFormatRecord record with given index from the
  758. * file's list. This will make all
  759. * subsequent font indicies drop by one,
  760. * so you'll need to update those yourself!
  761. * @param index of the Extended format record (0-based)
  762. */
  763. public void removeExFormatRecord(int index) {
  764. int xfptr = records.getXfpos() - (numxfs - 1) + index;
  765. records.remove(xfptr); // this updates XfPos for us
  766. numxfs--;
  767. }
  768. /**
  769. * creates a new Cell-type Extneded Format Record and adds it to the end of
  770. * ExtendedFormatRecords collection
  771. *
  772. * @return ExtendedFormatRecord that was created
  773. */
  774. public ExtendedFormatRecord createCellXF() {
  775. ExtendedFormatRecord xf = createExtendedFormat();
  776. records.add(records.getXfpos()+1, xf);
  777. records.setXfpos( records.getXfpos() + 1 );
  778. numxfs++;
  779. return xf;
  780. }
  781. /**
  782. * Returns the StyleRecord for the given
  783. * xfIndex, or null if that ExtendedFormat doesn't
  784. * have a Style set.
  785. */
  786. public StyleRecord getStyleRecord(int xfIndex) {
  787. // Style records always follow after
  788. // the ExtendedFormat records
  789. for(int i=records.getXfpos(); i<records.size(); i++) {
  790. Record r = records.get(i);
  791. if(r instanceof ExtendedFormatRecord) {
  792. continue;
  793. }
  794. if(!(r instanceof StyleRecord)) {
  795. continue;
  796. }
  797. StyleRecord sr = (StyleRecord)r;
  798. if(sr.getXFIndex() == xfIndex) {
  799. return sr;
  800. }
  801. }
  802. return null;
  803. }
  804. /**
  805. * Creates a new StyleRecord, for the given Extended
  806. * Format index, and adds it onto the end of the
  807. * records collection
  808. */
  809. public StyleRecord createStyleRecord(int xfIndex) {
  810. // Style records always follow after
  811. // the ExtendedFormat records
  812. StyleRecord newSR = new StyleRecord();
  813. newSR.setXFIndex(xfIndex);
  814. // Find the spot
  815. int addAt = -1;
  816. for(int i=records.getXfpos(); i<records.size() &&
  817. addAt == -1; i++) {
  818. Record r = records.get(i);
  819. if(r instanceof ExtendedFormatRecord ||
  820. r instanceof StyleRecord) {
  821. // Keep going
  822. } else {
  823. addAt = i;
  824. }
  825. }
  826. if(addAt == -1) {
  827. throw new IllegalStateException("No XF Records found!");
  828. }
  829. records.add(addAt, newSR);
  830. return newSR;
  831. }
  832. /**
  833. * Adds a string to the SST table and returns its index (if its a duplicate
  834. * just returns its index and update the counts) ASSUMES compressed unicode
  835. * (meaning 8bit)
  836. *
  837. * @param string the string to be added to the SSTRecord
  838. *
  839. * @return index of the string within the SSTRecord
  840. */
  841. public int addSSTString(UnicodeString string) {
  842. if (log.check( POILogger.DEBUG ))
  843. log.log(DEBUG, "insert to sst string='", string);
  844. if (sst == null) {
  845. insertSST();
  846. }
  847. return sst.addString(string);
  848. }
  849. /**
  850. * given an index into the SST table, this function returns the corresponding String value
  851. * @return String containing the SST String
  852. */
  853. public UnicodeString getSSTString(int str) {
  854. if (sst == null) {
  855. insertSST();
  856. }
  857. UnicodeString retval = sst.getString(str);
  858. if (log.check( POILogger.DEBUG ))
  859. log.log(DEBUG, "Returning SST for index=", Integer.valueOf(str),
  860. " String= ", retval);
  861. return retval;
  862. }
  863. /**
  864. * use this function to add a Shared String Table to an existing sheet (say
  865. * generated by a different java api) without an sst....
  866. * @see #createExtendedSST()
  867. * @see org.apache.poi.hssf.record.SSTRecord
  868. */
  869. public void insertSST() {
  870. if (log.check( POILogger.DEBUG ))
  871. log.log(DEBUG, "creating new SST via insertSST!");
  872. sst = new SSTRecord();
  873. records.add(records.size() - 1, createExtendedSST());
  874. records.add(records.size() - 2, sst);
  875. }
  876. /**
  877. * Serializes all records int the worksheet section into a big byte array. Use
  878. * this to write the Workbook out.
  879. *
  880. * @return byte array containing the HSSF-only portions of the POIFS file.
  881. */
  882. // GJS: Not used so why keep it.
  883. // public byte [] serialize() {
  884. // log.log(DEBUG, "Serializing Workbook!");
  885. // byte[] retval = null;
  886. //
  887. //// ArrayList bytes = new ArrayList(records.size());
  888. // int arraysize = getSize();
  889. // int pos = 0;
  890. //
  891. // retval = new byte[ arraysize ];
  892. // for (int k = 0; k < records.size(); k++) {
  893. //
  894. // Record record = records.get(k);
  895. //// Let's skip RECALCID records, as they are only use for optimization
  896. // if(record.getSid() != RecalcIdRecord.sid || ((RecalcIdRecord)record).isNeeded()) {
  897. // pos += record.serialize(pos, retval); // rec.length;
  898. // }
  899. // }
  900. // log.log(DEBUG, "Exiting serialize workbook");
  901. // return retval;
  902. // }
  903. /**
  904. * Serializes all records int the worksheet section into a big byte array. Use
  905. * this to write the Workbook out.
  906. * @param offset of the data to be written
  907. * @param data array of bytes to write this to
  908. */
  909. public int serialize( int offset, byte[] data )
  910. {
  911. if (log.check( POILogger.DEBUG ))
  912. log.log( DEBUG, "Serializing Workbook with offsets" );
  913. int pos = 0;
  914. SSTRecord sst = null;
  915. int sstPos = 0;
  916. boolean wroteBoundSheets = false;
  917. for ( int k = 0; k < records.size(); k++ )
  918. {
  919. Record record = records.get( k );
  920. int len = 0;
  921. if (record instanceof SSTRecord)
  922. {
  923. sst = (SSTRecord)record;
  924. sstPos = pos;
  925. }
  926. if (record.getSid() == ExtSSTRecord.sid && sst != null)
  927. {
  928. record = sst.createExtSSTRecord(sstPos + offset);
  929. }
  930. if (record instanceof BoundSheetRecord) {
  931. if(!wroteBoundSheets) {
  932. for (int i = 0; i < boundsheets.size(); i++) {
  933. len+= getBoundSheetRec(i)
  934. .serialize(pos+offset+len, data);
  935. }
  936. wroteBoundSheets = true;
  937. }
  938. } else {
  939. len = record.serialize( pos + offset, data );
  940. }
  941. ///// DEBUG BEGIN /////
  942. // if (len != record.getRecordSize())
  943. // throw new IllegalStateException("Record size does not match serialized bytes. Serialized size = " + len + " but getRecordSize() returns " + record.getRecordSize());
  944. ///// DEBUG END /////
  945. pos += len; // rec.length;
  946. }
  947. if (log.check( POILogger.DEBUG ))
  948. log.log( DEBUG, "Exiting serialize workbook" );
  949. return pos;
  950. }
  951. /**
  952. * Perform any work necessary before the workbook is about to be serialized.
  953. *
  954. * Include in it ant code that modifies the workbook record stream and affects its size.
  955. */
  956. public void preSerialize(){
  957. // Ensure we have enough tab IDs
  958. // Can be a few short if new sheets were added
  959. if(records.getTabpos() > 0) {
  960. TabIdRecord tir = ( TabIdRecord ) records.get(records.getTabpos());
  961. if(tir._tabids.length < boundsheets.size()) {
  962. fixTabIdRecord();
  963. }
  964. }
  965. }
  966. public int getSize()
  967. {
  968. int retval = 0;
  969. SSTRecord sst = null;
  970. for ( int k = 0; k < records.size(); k++ )
  971. {
  972. Record record = records.get( k );
  973. if (record instanceof SSTRecord)
  974. sst = (SSTRecord)record;
  975. if (record.getSid() == ExtSSTRecord.sid && sst != null)
  976. retval += sst.calcExtSSTRecordSize();
  977. else
  978. retval += record.getRecordSize();
  979. }
  980. return retval;
  981. }
  982. private static BOFRecord createBOF() {
  983. BOFRecord retval = new BOFRecord();
  984. retval.setVersion(( short ) 0x600);
  985. retval.setType(BOFRecord.TYPE_WORKBOOK);
  986. retval.setBuild(( short ) 0x10d3);
  987. retval.setBuildYear(( short ) 1996);
  988. retval.setHistoryBitMask(0x41); // was c1 before verify
  989. retval.setRequiredVersion(0x6);
  990. return retval;
  991. }
  992. private static MMSRecord createMMS() {
  993. MMSRecord retval = new MMSRecord();
  994. retval.setAddMenuCount(( byte ) 0);
  995. retval.setDelMenuCount(( byte ) 0);
  996. return retval;
  997. }
  998. /**
  999. * creates the WriteAccess record containing the logged in user's name
  1000. */
  1001. private static WriteAccessRecord createWriteAccess() {
  1002. WriteAccessRecord retval = new WriteAccessRecord();
  1003. String defaultUserName = "POI";
  1004. try {
  1005. String username = System.getProperty("user.name");
  1006. // Google App engine returns null for user.name, see Bug 53974
  1007. if(username == null) username = defaultUserName;
  1008. retval.setUsername(username);
  1009. } catch (AccessControlException e) {
  1010. // AccessControlException can occur in a restricted context
  1011. // (client applet/jws application or restricted security server)
  1012. retval.setUsername(defaultUserName);
  1013. }
  1014. return retval;
  1015. }
  1016. private static CodepageRecord createCodepage() {
  1017. CodepageRecord retval = new CodepageRecord();
  1018. retval.setCodepage(CODEPAGE);
  1019. return retval;
  1020. }
  1021. private static DSFRecord createDSF() {
  1022. return new DSFRecord(false); // we don't even support double stream files
  1023. }
  1024. /**
  1025. * creates the TabId record containing an array
  1026. */
  1027. private static TabIdRecord createTabId() {
  1028. return new TabIdRecord();
  1029. }
  1030. /**
  1031. * creates the FnGroupCount record containing the Magic number constant of 14.
  1032. */
  1033. private static FnGroupCountRecord createFnGroupCount() {
  1034. FnGroupCountRecord retval = new FnGroupCountRecord();
  1035. retval.setCount(( short ) 14);
  1036. return retval;
  1037. }
  1038. /**
  1039. * @return a new WindowProtect record with protect set to false.
  1040. */
  1041. private static WindowProtectRecord createWindowProtect() {
  1042. // by default even when we support it we won't
  1043. // want it to be protected
  1044. return new WindowProtectRecord(false);
  1045. }
  1046. /**
  1047. * @return a new Protect record with protect set to false.
  1048. */
  1049. private static ProtectRecord createProtect() {
  1050. // by default even when we support it we won't
  1051. // want it to be protected
  1052. return new ProtectRecord(false);
  1053. }
  1054. /**
  1055. * @return a new Password record with password set to 0x0000 (no password).
  1056. */
  1057. private static PasswordRecord createPassword() {
  1058. return new PasswordRecord(0x0000); // no password by default!
  1059. }
  1060. /**
  1061. * @return a new ProtectionRev4 record with protect set to false.
  1062. */
  1063. private static ProtectionRev4Record createProtectionRev4() {
  1064. return new ProtectionRev4Record(false);
  1065. }
  1066. /**
  1067. * @return a new PasswordRev4 record with password set to 0.
  1068. */
  1069. private static PasswordRev4Record createPasswordRev4() {
  1070. return new PasswordRev4Record(0x0000);
  1071. }
  1072. /**
  1073. * creates the WindowOne record with the following magic values: <P>
  1074. * horizontal hold - 0x168 <P>
  1075. * vertical hold - 0x10e <P>
  1076. * width - 0x3a5c <P>
  1077. * height - 0x23be <P>
  1078. * options - 0x38 <P>
  1079. * selected tab - 0 <P>
  1080. * displayed tab - 0 <P>
  1081. * num selected tab- 0 <P>
  1082. * tab width ratio - 0x258 <P>
  1083. */
  1084. private static WindowOneRecord createWindowOne() {
  1085. WindowOneRecord retval = new WindowOneRecord();
  1086. retval.setHorizontalHold(( short ) 0x168);
  1087. retval.setVerticalHold(( short ) 0x10e);
  1088. retval.setWidth(( short ) 0x3a5c);
  1089. retval.setHeight(( short ) 0x23be);
  1090. retval.setOptions(( short ) 0x38);
  1091. retval.setActiveSheetIndex( 0x0);
  1092. retval.setFirstVisibleTab(0x0);
  1093. retval.setNumSelectedTabs(( short ) 1);
  1094. retval.setTabWidthRatio(( short ) 0x258);
  1095. return retval;
  1096. }
  1097. /**
  1098. * creates the Backup record with backup set to 0. (loose the data, who cares)
  1099. */
  1100. private static BackupRecord createBackup() {
  1101. BackupRecord retval = new BackupRecord();
  1102. retval.setBackup(( short ) 0); // by default DONT save backups of files...just loose data
  1103. return retval;
  1104. }
  1105. /**
  1106. * creates the HideObj record with hide object set to 0. (don't hide)
  1107. */
  1108. private static HideObjRecord createHideObj() {
  1109. HideObjRecord retval = new HideObjRecord();
  1110. retval.setHideObj(( short ) 0); // by default set hide object off
  1111. return retval;
  1112. }
  1113. /**
  1114. * creates the DateWindow1904 record with windowing set to 0. (don't window)
  1115. */
  1116. private static DateWindow1904Record createDateWindow1904() {
  1117. DateWindow1904Record retval = new DateWindow1904Record();
  1118. retval.setWindowing(( short ) 0); // don't EVER use 1904 date windowing...tick tock..
  1119. return retval;
  1120. }
  1121. /**
  1122. * creates the Precision record with precision set to true. (full precision)
  1123. */
  1124. private static PrecisionRecord createPrecision() {
  1125. PrecisionRecord retval = new PrecisionRecord();
  1126. retval.setFullPrecision(true); // always use real numbers in calculations!
  1127. return retval;
  1128. }
  1129. /**
  1130. * @return a new RefreshAll record with refreshAll set to false. (do not refresh all calcs)
  1131. */
  1132. private static RefreshAllRecord createRefreshAll() {
  1133. return new RefreshAllRecord(false);
  1134. }
  1135. /**
  1136. * creates the BookBool record with saveLinkValues set to 0. (don't save link values)
  1137. */
  1138. private static BookBoolRecord createBookBool() {
  1139. BookBoolRecord retval = new BookBoolRecord();
  1140. retval.setSaveLinkValues(( short ) 0);
  1141. return retval;
  1142. }
  1143. /**
  1144. * creates a Font record with the following magic values: <P>
  1145. * fontheight = 0xc8<P>
  1146. * attributes = 0x0<P>
  1147. * color palette index = 0x7fff<P>
  1148. * bold weight = 0x190<P>
  1149. * Font Name Length = 5 <P>
  1150. * Font Name = Arial <P>
  1151. */
  1152. private static FontRecord createFont() {
  1153. FontRecord retval = new FontRecord();
  1154. retval.setFontHeight(( short ) 0xc8);
  1155. retval.setAttributes(( short ) 0x0);
  1156. retval.setColorPaletteIndex(( short ) 0x7fff);
  1157. retval.setBoldWeight(( short ) 0x190);
  1158. retval.setFontName("Arial");
  1159. return retval;
  1160. }
  1161. /**
  1162. * Creates a FormatRecord object
  1163. * @param id the number of the format record to create (meaning its position in
  1164. * a file as M$ Excel would create it.)
  1165. */
  1166. private static FormatRecord createFormat(int id) {
  1167. // we'll need multiple editions for
  1168. // the different formats
  1169. switch (id) {
  1170. case 0: return new FormatRecord(5, BuiltinFormats.getBuiltinFormat(5));
  1171. case 1: return new FormatRecord(6, BuiltinFormats.getBuiltinFormat(6));
  1172. case 2: return new FormatRecord(7, BuiltinFormats.getBuiltinFormat(7));
  1173. case 3: return new FormatRecord(8, BuiltinFormats.getBuiltinFormat(8));
  1174. case 4: return new FormatRecord(0x2a, BuiltinFormats.getBuiltinFormat(0x2a));
  1175. case 5: return new FormatRecord(0x29, BuiltinFormats.getBuiltinFormat(0x29));
  1176. case 6: return new FormatRecord(0x2c, BuiltinFormats.getBuiltinFormat(0x2c));
  1177. case 7: return new FormatRecord(0x2b, BuiltinFormats.getBuiltinFormat(0x2b));
  1178. }
  1179. throw new IllegalArgumentException("Unexpected id " + id);
  1180. }
  1181. /**
  1182. * Creates an ExtendedFormatRecord object
  1183. * @param id the number of the extended format record to create (meaning its position in
  1184. * a file as MS Excel would create it.)
  1185. */
  1186. private static ExtendedFormatRecord createExtendedFormat(int id) { // we'll need multiple editions
  1187. ExtendedFormatRecord retval = new ExtendedFormatRecord();
  1188. switch (id) {
  1189. case 0 :
  1190. retval.setFontIndex(( short ) 0);
  1191. retval.setFormatIndex(( short ) 0);
  1192. retval.setCellOptions(( short ) 0xfffffff5);
  1193. retval.setAlignmentOptions(( short ) 0x20);
  1194. retval.setIndentionOptions(( short ) 0);
  1195. retval.setBorderOptions(( short ) 0);
  1196. retval.setPaletteOptions(( short ) 0);
  1197. retval.setAdtlPaletteOptions(( short ) 0);
  1198. retval.setFillPaletteOptions(( short ) 0x20c0);
  1199. break;
  1200. case 1 :
  1201. retval.setFontIndex(( short ) 1);
  1202. retval.setFormatIndex(( short ) 0);
  1203. retval.setCellOptions(( short ) 0xfffffff5);
  1204. retval.setAlignmentOptions(( short ) 0x20);
  1205. retval.setIndentionOptions(( short ) 0xfffff400);
  1206. retval.setBorderOptions(( short ) 0);
  1207. retval.setPaletteOptions(( short ) 0);
  1208. retval.setAdtlPaletteOptions(( short ) 0);
  1209. retval.setFillPaletteOptions(( short ) 0x20c0);
  1210. break;
  1211. case 2 :
  1212. retval.setFontIndex(( short ) 1);
  1213. retval.setFormatIndex(( short ) 0);
  1214. retval.setCellOptions(( short ) 0xfffffff5);
  1215. retval.setAlignmentOptions(( short ) 0x20);
  1216. retval.setIndentionOptions(( short ) 0xfffff400);
  1217. retval.setBorderOptions(( short ) 0);
  1218. retval.setPaletteOptions(( short ) 0);
  1219. retval.setAdtlPaletteOptions(( short ) 0);
  1220. retval.setFillPaletteOptions(( short ) 0x20c0);
  1221. break;
  1222. case 3 :
  1223. retval.setFontIndex(( short ) 2);
  1224. retval.setFormatIndex(( short ) 0);
  1225. retval.setCellOptions(( short ) 0xfffffff5);
  1226. retval.setAlignmentOptions(( short ) 0x20);
  1227. retval.setIndentionOptions(( short ) 0xfffff400);
  1228. retval.setBorderOptions(( short ) 0);
  1229. retval.setPaletteOptions(( short ) 0);
  1230. retval.setAdtlPaletteOptions(( short ) 0);
  1231. retval.setFillPaletteOptions(( short ) 0x20c0);
  1232. break;
  1233. case 4 :
  1234. retval.setFontIndex(( short ) 2);
  1235. retval.setFormatIndex(( short ) 0);
  1236. retval.setCellOptions(( short ) 0xfffffff5);
  1237. retval.setAlignmentOptions(( short ) 0x20);
  1238. retval.setIndentionOptions(( short ) 0xfffff400);
  1239. retval.setBorderOptions(( short ) 0);
  1240. retval.setPaletteOptions(( short ) 0);
  1241. retval.setAdtlPaletteOptions(( short ) 0);
  1242. retval.setFillPaletteOptions(( short ) 0x20c0);
  1243. break;
  1244. case 5 :
  1245. retval.setFontIndex(( short ) 0);
  1246. retval.setFormatIndex(( short ) 0);
  1247. retval.setCellOptions(( short ) 0xfffffff5);
  1248. retval.setAlignmentOptions(( short ) 0x20);
  1249. retval.setIndentionOptions(( short ) 0xfffff400);
  1250. retval.setBorderOptions(( short ) 0);
  1251. retval.setPaletteOptions(( short ) 0);
  1252. retval.setAdtlPaletteOptions(( short ) 0);
  1253. retval.setFillPaletteOptions(( short ) 0x20c0);
  1254. break;
  1255. case 6 :
  1256. retval.setFontIndex(( short ) 0);
  1257. retval.setFormatIndex(( short ) 0);
  1258. retval.setCellOptions(( short ) 0xfffffff5);
  1259. retval.setAlignmentOptions(( short ) 0x20);
  1260. retval.setIndentionOptions(( short ) 0xfffff400);
  1261. retval.setBorderOptions(( short ) 0);
  1262. retval.setPaletteOptions(( short ) 0);
  1263. retval.setAdtlPaletteOptions(( short ) 0);
  1264. retval.setFillPaletteOptions(( short ) 0x20c0);
  1265. break;
  1266. case 7 :
  1267. retval.setFontIndex(( short ) 0);
  1268. retval.setFormatIndex(( short ) 0);
  1269. retval.setCellOptions(( short ) 0xfffffff5);
  1270. retval.setAlignmentOptions(( short ) 0x20);
  1271. retval.setIndentionOptions(( short ) 0xfffff400);
  1272. retval.setBorderOptions(( short ) 0);
  1273. retval.setPaletteOptions(( short ) 0);
  1274. retval.setAdtlPaletteOptions(( short ) 0);
  1275. retval.setFillPaletteOptions(( short ) 0x20c0);
  1276. break;
  1277. case 8 :
  1278. retval.setFontIndex(( short ) 0);
  1279. retval.setFormatIndex(( short ) 0);
  1280. retval.setCellOptions(( short ) 0xfffffff5);
  1281. retval.setAlignmentOptions(( short ) 0x20);
  1282. retval.setIndentionOptions(( short ) 0xfffff400);
  1283. retval.setBorderOptions(( short ) 0);
  1284. retval.setPaletteOptions(( short ) 0);
  1285. retval.setAdtlPaletteOptions(( short ) 0);
  1286. retval.setFillPaletteOptions(( short ) 0x20c0);
  1287. break;
  1288. case 9 :
  1289. retval.setFontIndex(( short ) 0);
  1290. retval.setFormatIndex(( short ) 0);
  1291. retval.setCellOptions(( short ) 0xfffffff5);
  1292. retval.setAlignmentOptions(( short ) 0x20);
  1293. retval.setIndentionOptions(( short ) 0xfffff400);
  1294. retval.setBorderOptions(( short ) 0);
  1295. retval.setPaletteOptions(( short ) 0);
  1296. retval.setAdtlPaletteOptions(( short ) 0);
  1297. retval.setFillPaletteOptions(( short ) 0x20c0);
  1298. break;
  1299. case 10 :
  1300. retval.setFontIndex(( short ) 0);
  1301. retval.setFormatIndex(( short ) 0);
  1302. retval.setCellOptions(( short ) 0xfffffff5);
  1303. retval.setAlignmentOptions(( short ) 0x20);
  1304. retval.setIndentionOptions(( short ) 0xfffff400);
  1305. retval.setBorderOptions(( short ) 0);
  1306. retval.setPaletteOptions(( short ) 0);
  1307. retval.setAdtlPaletteOptions(( short ) 0);
  1308. retval.setFillPaletteOptions(( short ) 0x20c0);
  1309. break;
  1310. case 11 :
  1311. retval.setFontIndex(( short ) 0);
  1312. retval.setFormatIndex(( short ) 0);
  1313. retval.setCellOptions(( short ) 0xfffffff5);
  1314. retval.setAlignmentOptions(( short ) 0x20);
  1315. retval.setIndentionOptions(( short ) 0xfffff400);
  1316. retval.setBorderOptions(( short ) 0);
  1317. retval.setPaletteOptions(( short ) 0);
  1318. retval.setAdtlPaletteOptions(( short ) 0);
  1319. retval.setFillPaletteOptions(( short ) 0x20c0);
  1320. break;
  1321. case 12 :
  1322. retval.setFontIndex(( short ) 0);
  1323. retval.setFormatIndex(( short ) 0);
  1324. retval.setCellOptions(( short ) 0xfffffff5);
  1325. retval.setAlignmentOptions(( short ) 0x20);
  1326. retval.setIndentionOptions(( short ) 0xfffff400);
  1327. retval.setBorderOptions(( short ) 0);
  1328. retval.setPaletteOptions(( short ) 0);
  1329. retval.setAdtlPaletteOptions(( short ) 0);
  1330. retval.setFillPaletteOptions(( short ) 0x20c0);
  1331. break;
  1332. case 13 :
  1333. retval.setFontIndex(( short ) 0);
  1334. retval.setFormatIndex(( short ) 0);
  1335. retval.setCellOptions(( short ) 0xfffffff5);
  1336. retval.setAlignmentOptions(( short ) 0x20);
  1337. retval.setIndentionOptions(( short ) 0xfffff400);
  1338. retval.setBorderOptions(( short ) 0);
  1339. retval.setPaletteOptions(( short ) 0);
  1340. retval.setAdtlPaletteOptions(( short ) 0);
  1341. retval.setFillPaletteOptions(( short ) 0x20c0);
  1342. break;
  1343. case 14 :
  1344. retval.setFontIndex(( short ) 0);
  1345. retval.setFormatIndex(( short ) 0);
  1346. retval.setCellOptions(( short ) 0xfffffff5);
  1347. retval.setAlignmentOptions(( short ) 0x20);
  1348. retval.setIndentionOptions(( short ) 0xfffff400);
  1349. retval.setBorderOptions(( short ) 0);
  1350. retval.setPaletteOptions(( short ) 0);
  1351. retval.setAdtlPaletteOptions(( short ) 0);
  1352. retval.setFillPaletteOptions(( short ) 0x20c0);
  1353. break;
  1354. // cell records
  1355. case 15 :
  1356. retval.setFontIndex(( short ) 0);
  1357. retval.setFormatIndex(( short ) 0);
  1358. retval.setCellOptions(( short ) 0x1);
  1359. retval.setAlignmentOptions(( short ) 0x20);
  1360. retval.setIndentionOptions(( short ) 0x0);
  1361. retval.setBorderOptions(( short ) 0);
  1362. retval.setPaletteOptions(( short ) 0);
  1363. retval.setAdtlPaletteOptions(( short ) 0);
  1364. retval.setFillPaletteOptions(( short ) 0x20c0);
  1365. break;
  1366. // style
  1367. case 16 :
  1368. retval.setFontIndex(( short ) 1);
  1369. retval.setFormatIndex(( short ) 0x2b);
  1370. retval.setCellOptions(( short ) 0xfffffff5);
  1371. retval.setAlignmentOptions(( short ) 0x20);
  1372. retval.setIndentionOptions(( short ) 0xfffff800);
  1373. retval.setBorderOptions(( short ) 0);
  1374. retval.setPaletteOptions(( short ) 0);
  1375. retval.setAdtlPaletteOptions(( short ) 0);
  1376. retval.setFillPaletteOptions(( short ) 0x20c0);
  1377. break;
  1378. case 17 :
  1379. retval.setFontIndex(( short ) 1);
  1380. retval.setFormatIndex(( short ) 0x29);
  1381. retval.setCellOptions(( short ) 0xfffffff5);
  1382. retval.setAlignmentOptions(( short ) 0x20);
  1383. retval.setIndentionOptions(( short ) 0xfffff800);
  1384. retval.setBorderOptions(( short ) 0);
  1385. retval.setPaletteOptions(( short ) 0);
  1386. retval.setAdtlPaletteOptions(( short ) 0);
  1387. retval.setFillPaletteOptions(( short ) 0x20c0);
  1388. break;
  1389. case 18 :
  1390. retval.setFontIndex(( short ) 1);
  1391. retval.setFormatIndex(( short ) 0x2c);
  1392. retval.setCellOptions(( short ) 0xfffffff5);
  1393. retval.setAlignmentOptions(( short ) 0x20);
  1394. retval.setIndentionOptions(( short ) 0xfffff800);
  1395. retval.setBorderOptions(( short ) 0);
  1396. retval.setPaletteOptions(( short ) 0);
  1397. retval.setAdtlPaletteOptions(( short ) 0);
  1398. retval.setFillPaletteOptions(( short ) 0x20c0);
  1399. break;
  1400. case 19 :
  1401. retval.setFontIndex(( short ) 1);
  1402. retval.setFormatIndex(( short ) 0x2a);
  1403. retval.setCellOptions(( short ) 0xfffffff5);
  1404. retval.setAlignmentOptions(( short ) 0x20);
  1405. retval.setIndentionOptions(( short ) 0xfffff800);
  1406. retval.setBorderOptions(( short ) 0);
  1407. retval.setPaletteOptions(( short ) 0);
  1408. retval.setAdtlPaletteOptions(( short ) 0);
  1409. retval.setFillPaletteOptions(( short ) 0x20c0);
  1410. break;
  1411. case 20 :
  1412. retval.setFontIndex(( short ) 1);
  1413. retval.setFormatIndex(( short ) 0x9);
  1414. retval.setCellOptions(( short ) 0xfffffff5);
  1415. retval.setAlignmentOptions(( short ) 0x20);
  1416. retval.setIndentionOptions(( short ) 0xfffff800);
  1417. retval.setBorderOptions(( short ) 0);
  1418. retval.setPaletteOptions(( short ) 0);
  1419. retval.setAdtlPaletteOptions(( short ) 0);
  1420. retval.setFillPaletteOptions(( short ) 0x20c0);
  1421. break;
  1422. // unused from this point down
  1423. case 21 :
  1424. retval.setFontIndex(( short ) 5);
  1425. retval.setFormatIndex(( short ) 0x0);
  1426. retval.setCellOptions(( short ) 0x1);
  1427. retval.setAlignmentOptions(( short ) 0x20);
  1428. retval.setIndentionOptions(( short ) 0x800);
  1429. retval.setBorderOptions(( short ) 0);
  1430. retval.setPaletteOptions(( short ) 0);
  1431. retval.setAdtlPaletteOptions(( short ) 0);
  1432. retval.setFillPaletteOptions(( short ) 0x20c0);
  1433. break;
  1434. case 22 :
  1435. retval.setFontIndex(( short ) 6);
  1436. retval.setFormatIndex(( short ) 0x0);
  1437. retval.setCellOptions(( short ) 0x1);
  1438. retval.setAlignmentOptions(( short ) 0x20);
  1439. retval.setIndentionOptions(( short ) 0x5c00);
  1440. retval.setBorderOptions(( short ) 0);
  1441. retval.setPaletteOptions(( short ) 0);
  1442. retval.setAdtlPaletteOptions(( short ) 0);
  1443. retval.setFillPaletteOptions(( short ) 0x20c0);
  1444. break;
  1445. case 23 :
  1446. retval.setFontIndex(( short ) 0);
  1447. retval.setFormatIndex(( short ) 0x31);
  1448. retval.setCellOptions(( short ) 0x1);
  1449. retval.setAlignmentOptions(( short ) 0x20);
  1450. retval.setIndentionOptions(( short ) 0x5c00);
  1451. retval.setBorderOptions(( short ) 0);
  1452. retval.setPaletteOptions(( short ) 0);
  1453. retval.setAdtlPaletteOptions(( short ) 0);
  1454. retval.setFillPaletteOptions(( short ) 0x20c0);
  1455. break;
  1456. case 24 :
  1457. retval.setFontIndex(( short ) 0);
  1458. retval.setFormatIndex(( short ) 0x8);
  1459. retval.setCellOptions(( short ) 0x1);
  1460. retval.setAlignmentOptions(( short ) 0x20);
  1461. retval.setIndentionOptions(( short ) 0x5c00);
  1462. retval.setBorderOptions(( short ) 0);
  1463. retval.setPaletteOptions(( short ) 0);
  1464. retval.setAdtlPaletteOptions(( short ) 0);
  1465. retval.setFillPaletteOptions(( short ) 0x20c0);
  1466. break;
  1467. case 25 :
  1468. retval.setFontIndex(( short ) 6);
  1469. retval.setFormatIndex(( short ) 0x8);
  1470. retval.setCellOptions(( short ) 0x1);
  1471. retval.setAlignmentOptions(( short ) 0x20);
  1472. retval.setIndentionOptions(( short ) 0x5c00);
  1473. retval.setBorderOptions(( short ) 0);
  1474. retval.setPaletteOptions(( short ) 0);
  1475. retval.setAdtlPaletteOptions(( short ) 0);
  1476. retval.setFillPaletteOptions(( short ) 0x20c0);
  1477. break;
  1478. }
  1479. return retval;
  1480. }
  1481. /**
  1482. * creates an default cell type ExtendedFormatRecord object.
  1483. * @return ExtendedFormatRecord with intial defaults (cell-type)
  1484. */
  1485. private static ExtendedFormatRecord createExtendedFormat() {
  1486. ExtendedFormatRecord retval = new ExtendedFormatRecord();
  1487. retval.setFontIndex(( short ) 0);
  1488. retval.setFormatIndex(( short ) 0x0);
  1489. retval.setCellOptions(( short ) 0x1);
  1490. retval.setAlignmentOptions(( short ) 0x20);
  1491. retval.setIndentionOptions(( short ) 0);
  1492. retval.setBorderOptions(( short ) 0);
  1493. retval.setPaletteOptions(( short ) 0);
  1494. retval.setAdtlPaletteOptions(( short ) 0);
  1495. retval.setFillPaletteOptions(( short ) 0x20c0);
  1496. retval.setTopBorderPaletteIdx(HSSFColor.BLACK.index);
  1497. retval.setBottomBorderPaletteIdx(HSSFColor.BLACK.index);
  1498. retval.setLeftBorderPaletteIdx(HSSFColor.BLACK.index);
  1499. retval.setRightBorderPaletteIdx(HSSFColor.BLACK.index);
  1500. return retval;
  1501. }
  1502. /**
  1503. * Creates a StyleRecord object
  1504. * @param id the number of the style record to create (meaning its position in
  1505. * a file as MS Excel would create it.
  1506. */
  1507. private static StyleRecord createStyle(int id) { // we'll need multiple editions
  1508. StyleRecord retval = new StyleRecord();
  1509. switch (id) {
  1510. case 0 :
  1511. retval.setXFIndex(0x010);
  1512. retval.setBuiltinStyle(3);
  1513. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1514. break;
  1515. case 1 :
  1516. retval.setXFIndex(0x011);
  1517. retval.setBuiltinStyle(6);
  1518. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1519. break;
  1520. case 2 :
  1521. retval.setXFIndex(0x012);
  1522. retval.setBuiltinStyle(4);
  1523. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1524. break;
  1525. case 3 :
  1526. retval.setXFIndex(0x013);
  1527. retval.setBuiltinStyle(7);
  1528. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1529. break;
  1530. case 4 :
  1531. retval.setXFIndex(0x000);
  1532. retval.setBuiltinStyle(0);
  1533. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1534. break;
  1535. case 5 :
  1536. retval.setXFIndex(0x014);
  1537. retval.setBuiltinStyle(5);
  1538. retval.setOutlineStyleLevel(( byte ) 0xffffffff);
  1539. break;
  1540. }
  1541. return retval;
  1542. }
  1543. /**
  1544. * Creates a palette record initialized to the default palette
  1545. */
  1546. private static PaletteRecord createPalette() {
  1547. return new PaletteRecord();
  1548. }
  1549. /**
  1550. * @return a new UseSelFS object with the use natural language flag set to 0 (false)
  1551. */
  1552. private static UseSelFSRecord createUseSelFS() {
  1553. return new UseSelFSRecord(false);
  1554. }
  1555. /**
  1556. * create a "bound sheet" or "bundlesheet" (depending who you ask) record
  1557. * Always sets the sheet's bof to 0. You'll need to set that yourself.
  1558. * @param id either sheet 0,1 or 2.
  1559. * @return record containing a BoundSheetRecord
  1560. * @see org.apache.poi.hssf.record.BoundSheetRecord
  1561. * @see org.apache.poi.hssf.record.Record
  1562. */
  1563. private static BoundSheetRecord createBoundSheet(int id) {
  1564. return new BoundSheetRecord("Sheet" + (id+1));
  1565. }
  1566. /**
  1567. * Creates the Country record with the default country set to 1
  1568. * and current country set to 7 in case of russian locale ("ru_RU") and 1 otherwise
  1569. */
  1570. private static CountryRecord createCountry() {
  1571. CountryRecord retval = new CountryRecord();
  1572. retval.setDefaultCountry(( short ) 1);
  1573. // from Russia with love ;)
  1574. if ( Locale.getDefault().toString().equals( "ru_RU" ) ) {
  1575. retval.setCurrentCountry(( short ) 7);
  1576. }
  1577. else {
  1578. retval.setCurrentCountry(( short ) 1);
  1579. }
  1580. return retval;
  1581. }
  1582. /**
  1583. * Creates the ExtendedSST record with numstrings per bucket set to 0x8. HSSF
  1584. * doesn't yet know what to do with this thing, but we create it with nothing in
  1585. * it hardly just to make Excel happy and our sheets look like Excel's
  1586. */
  1587. private static ExtSSTRecord createExtendedSST() {
  1588. ExtSSTRecord retval = new ExtSSTRecord();
  1589. retval.setNumStringsPerBucket(( short ) 0x8);
  1590. return retval;
  1591. }
  1592. /**
  1593. * lazy initialization
  1594. * Note - creating the link table causes creation of 1 EXTERNALBOOK and 1 EXTERNALSHEET record
  1595. */
  1596. private LinkTable getOrCreateLinkTable() {
  1597. if(linkTable == null) {
  1598. linkTable = new LinkTable((short) getNumSheets(), records);
  1599. }
  1600. return linkTable;
  1601. }
  1602. /** finds the sheet name by his extern sheet index
  1603. * @param externSheetIndex extern sheet index
  1604. * @return sheet name.
  1605. */
  1606. public String findSheetNameFromExternSheet(int externSheetIndex){
  1607. int indexToSheet = linkTable.getIndexToInternalSheet(externSheetIndex);
  1608. if (indexToSheet < 0) {
  1609. // TODO - what does '-1' mean here?
  1610. //error check, bail out gracefully!
  1611. return "";
  1612. }
  1613. if (indexToSheet >= boundsheets.size()) {
  1614. // Not sure if this can ever happen (See bug 45798)
  1615. return ""; // Seems to be what excel would do in this case
  1616. }
  1617. return getSheetName(indexToSheet);
  1618. }
  1619. public ExternalSheet getExternalSheet(int externSheetIndex) {
  1620. String[] extNames = linkTable.getExternalBookAndSheetName(externSheetIndex);
  1621. if (extNames == null) {
  1622. return null;
  1623. }
  1624. return new ExternalSheet(extNames[0], extNames[1]);
  1625. }
  1626. public ExternalName getExternalName(int externSheetIndex, int externNameIndex) {
  1627. String nameName = linkTable.resolveNameXText(externSheetIndex, externNameIndex);
  1628. if(nameName == null) {
  1629. return null;
  1630. }
  1631. int ix = linkTable.resolveNameXIx(externSheetIndex, externNameIndex);
  1632. return new ExternalName(nameName, externNameIndex, ix);
  1633. }
  1634. /**
  1635. * Finds the sheet index for a particular external sheet number.
  1636. * @param externSheetNumber The external sheet number to convert
  1637. * @return The index to the sheet found.
  1638. */
  1639. public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
  1640. {
  1641. return linkTable.getSheetIndexFromExternSheetIndex(externSheetNumber);
  1642. }
  1643. /** returns the extern sheet number for specific sheet number ,
  1644. * if this sheet doesn't exist in extern sheet , add it
  1645. * @param sheetNumber sheet number
  1646. * @return index to extern sheet
  1647. */
  1648. public short checkExternSheet(int sheetNumber){
  1649. return (short)getOrCreateLinkTable().checkExternSheet(sheetNumber);
  1650. }
  1651. public int getExternalSheetIndex(String workbookName, String sheetName) {
  1652. return getOrCreateLinkTable().getExternalSheetIndex(workbookName, sheetName);
  1653. }
  1654. /** gets the total number of names
  1655. * @return number of names
  1656. */
  1657. public int getNumNames(){
  1658. if(linkTable == null) {
  1659. return 0;
  1660. }
  1661. return linkTable.getNumNames();
  1662. }
  1663. /** gets the name record
  1664. * @param index name index
  1665. * @return name record
  1666. */
  1667. public NameRecord getNameRecord(int index){
  1668. return linkTable.getNameRecord(index);
  1669. }
  1670. /** gets the name comment record
  1671. * @param nameRecord name record who's comment is required.
  1672. * @return name comment record or <code>null</code> if there isn't one for the given name.
  1673. */
  1674. public NameCommentRecord getNameCommentRecord(final NameRecord nameRecord){
  1675. return commentRecords.get(nameRecord.getNameText());
  1676. }
  1677. /** creates new name
  1678. * @return new name record
  1679. */
  1680. public NameRecord createName(){
  1681. return addName(new NameRecord());
  1682. }
  1683. /** creates new name
  1684. * @return new name record
  1685. */
  1686. public NameRecord addName(NameRecord name)
  1687. {
  1688. LinkTable linkTable = getOrCreateLinkTable();
  1689. linkTable.addName(name);
  1690. return name;
  1691. }
  1692. /**
  1693. * Generates a NameRecord to represent a built-in region
  1694. * @return a new NameRecord
  1695. */
  1696. public NameRecord createBuiltInName(byte builtInName, int sheetNumber) {
  1697. if (sheetNumber < 0 || sheetNumber+1 > Short.MAX_VALUE) {
  1698. throw new IllegalArgumentException("Sheet number ["+sheetNumber+"]is not valid ");
  1699. }
  1700. NameRecord name = new NameRecord(builtInName, sheetNumber);
  1701. if(linkTable.nameAlreadyExists(name)) {
  1702. throw new RuntimeException("Builtin (" + builtInName
  1703. + ") already exists for sheet (" + sheetNumber + ")");
  1704. }
  1705. addName(name);
  1706. return name;
  1707. }
  1708. /** removes the name
  1709. * @param nameIndex name index
  1710. */
  1711. public void removeName(int nameIndex){
  1712. if (linkTable.getNumNames() > nameIndex) {
  1713. int idx = findFirstRecordLocBySid(NameRecord.sid);
  1714. records.remove(idx + nameIndex);
  1715. linkTable.removeName(nameIndex);
  1716. }
  1717. }
  1718. /**
  1719. * If a {@link NameCommentRecord} is added or the name it references
  1720. * is renamed, then this will update the lookup cache for it.
  1721. */
  1722. public void updateNameCommentRecordCache(final NameCommentRecord commentRecord) {
  1723. if(commentRecords.containsValue(commentRecord)) {
  1724. for(Entry<String,NameCommentRecord> entry : commentRecords.entrySet()) {
  1725. if(entry.getValue().equals(commentRecord)) {
  1726. commentRecords.remove(entry.getKey());
  1727. break;
  1728. }
  1729. }
  1730. }
  1731. commentRecords.put(commentRecord.getNameText(), commentRecord);
  1732. }
  1733. /**
  1734. * Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat.
  1735. * @param format the format string
  1736. * @param createIfNotFound creates a new format if format not found
  1737. * @return the format id of a format that matches or -1 if none found and createIfNotFound
  1738. */
  1739. public short getFormat(String format, boolean createIfNotFound) {
  1740. for (FormatRecord r : formats) {
  1741. if (r.getFormatString().equals(format)) {
  1742. return (short)r.getIndexCode();
  1743. }
  1744. }
  1745. if (createIfNotFound) {
  1746. return (short)createFormat(format);
  1747. }
  1748. return -1;
  1749. }
  1750. /**
  1751. * Returns the list of FormatRecords in the workbook.
  1752. * @return ArrayList of FormatRecords in the notebook
  1753. */
  1754. public List<FormatRecord> getFormats() {
  1755. return formats;
  1756. }
  1757. /**
  1758. * Creates a FormatRecord, inserts it, and returns the index code.
  1759. * @param formatString the format string
  1760. * @return the index code of the format record.
  1761. * @see org.apache.poi.hssf.record.FormatRecord
  1762. * @see org.apache.poi.hssf.record.Record
  1763. */
  1764. public int createFormat(String formatString) {
  1765. maxformatid = maxformatid >= 0xa4 ? maxformatid + 1 : 0xa4; //Starting value from M$ empircal study.
  1766. FormatRecord rec = new FormatRecord(maxformatid, formatString);
  1767. int pos = 0;
  1768. while ( pos < records.size() && records.get( pos ).getSid() != FormatRecord.sid )
  1769. pos++;
  1770. pos += formats.size();
  1771. formats.add( rec );
  1772. records.add( pos, rec );
  1773. return maxformatid;
  1774. }
  1775. /**
  1776. * Returns the first occurance of a record matching a particular sid.
  1777. */
  1778. public Record findFirstRecordBySid(short sid) {
  1779. for (Record record : records) {
  1780. if (record.getSid() == sid) {
  1781. return record;
  1782. }
  1783. }
  1784. return null;
  1785. }
  1786. /**
  1787. * Returns the index of a record matching a particular sid.
  1788. * @param sid The sid of the record to match
  1789. * @return The index of -1 if no match made.
  1790. */
  1791. public int findFirstRecordLocBySid(short sid) {
  1792. int index = 0;
  1793. for (Record record : records) {
  1794. if (record.getSid() == sid) {
  1795. return index;
  1796. }
  1797. index ++;
  1798. }
  1799. return -1;
  1800. }
  1801. /**
  1802. * Returns the next occurance of a record matching a particular sid.
  1803. */
  1804. public Record findNextRecordBySid(short sid, int pos) {
  1805. int matches = 0;
  1806. for (Record record : records) {
  1807. if (record.getSid() == sid) {
  1808. if (matches++ == pos)
  1809. return record;
  1810. }
  1811. }
  1812. return null;
  1813. }
  1814. public List<HyperlinkRecord> getHyperlinks()
  1815. {
  1816. return hyperlinks;
  1817. }
  1818. public List<Record> getRecords() {
  1819. return records.getRecords();
  1820. }
  1821. /**
  1822. * Whether date windowing is based on 1/2/1904 or 1/1/1900.
  1823. * Some versions of Excel (Mac) can save workbooks using 1904 date windowing.
  1824. *
  1825. * @return true if using 1904 date windowing
  1826. */
  1827. public boolean isUsing1904DateWindowing() {
  1828. return uses1904datewindowing;
  1829. }
  1830. /**
  1831. * Returns the custom palette in use for this workbook; if a custom palette record
  1832. * does not exist, then it is created.
  1833. */
  1834. public PaletteRecord getCustomPalette()
  1835. {
  1836. PaletteRecord palette;
  1837. int palettePos = records.getPalettepos();
  1838. if (palettePos != -1) {
  1839. Record rec = records.get(palettePos);
  1840. if (rec instanceof PaletteRecord) {
  1841. palette = (PaletteRecord) rec;
  1842. } else throw new RuntimeException("InternalError: Expected PaletteRecord but got a '"+rec+"'");
  1843. }
  1844. else
  1845. {
  1846. palette = createPalette();
  1847. //Add the palette record after the bof which is always the first record
  1848. records.add(1, palette);
  1849. records.setPalettepos(1);
  1850. }
  1851. return palette;
  1852. }
  1853. /**
  1854. * Finds the primary drawing group, if one already exists
  1855. */
  1856. public DrawingManager2 findDrawingGroup() {
  1857. if(drawingManager != null) {
  1858. // We already have it!
  1859. return drawingManager;
  1860. }
  1861. // Need to find a DrawingGroupRecord that
  1862. // contains a EscherDggRecord
  1863. for(Record r : records) {
  1864. if(r instanceof DrawingGroupRecord) {
  1865. DrawingGroupRecord dg = (DrawingGroupRecord)r;
  1866. dg.processChildRecords();
  1867. EscherContainerRecord cr =
  1868. dg.getEscherContainer();
  1869. if(cr == null) {
  1870. continue;
  1871. }
  1872. EscherDggRecord dgg = null;
  1873. EscherContainerRecord bStore = null;
  1874. for(Iterator<EscherRecord> it = cr.getChildIterator(); it.hasNext();) {
  1875. EscherRecord er = it.next();
  1876. if(er instanceof EscherDggRecord) {
  1877. dgg = (EscherDggRecord)er;
  1878. } else if (er.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER) {
  1879. bStore = (EscherContainerRecord) er;
  1880. }
  1881. }
  1882. if(dgg != null) {
  1883. drawingManager = new DrawingManager2(dgg);
  1884. if(bStore != null){
  1885. for(EscherRecord bs : bStore.getChildRecords()){
  1886. if(bs instanceof EscherBSERecord) escherBSERecords.add((EscherBSERecord)bs);
  1887. }
  1888. }
  1889. return drawingManager;
  1890. }
  1891. }
  1892. }
  1893. // Look for the DrawingGroup record
  1894. int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
  1895. // If there is one, does it have a EscherDggRecord?
  1896. if(dgLoc != -1) {
  1897. DrawingGroupRecord dg = (DrawingGroupRecord)records.get(dgLoc);
  1898. EscherDggRecord dgg = null;
  1899. EscherContainerRecord bStore = null;
  1900. for(EscherRecord er : dg.getEscherRecords()) {
  1901. if (er instanceof EscherDggRecord) {
  1902. dgg = (EscherDggRecord) er;
  1903. } else if (er.getRecordId() == EscherContainerRecord.BSTORE_CONTAINER) {
  1904. bStore = (EscherContainerRecord) er;
  1905. }
  1906. }
  1907. if(dgg != null) {
  1908. drawingManager = new DrawingManager2(dgg);
  1909. if(bStore != null){
  1910. for(EscherRecord bs : bStore.getChildRecords()){
  1911. if(bs instanceof EscherBSERecord) escherBSERecords.add((EscherBSERecord)bs);
  1912. }
  1913. }
  1914. }
  1915. }
  1916. return drawingManager;
  1917. }
  1918. /**
  1919. * Creates a primary drawing group record. If it already
  1920. * exists then it's modified.
  1921. */
  1922. public void createDrawingGroup() {
  1923. if (drawingManager == null) {
  1924. EscherContainerRecord dggContainer = new EscherContainerRecord();
  1925. EscherDggRecord dgg = new EscherDggRecord();
  1926. EscherOptRecord opt = new EscherOptRecord();
  1927. EscherSplitMenuColorsRecord splitMenuColors = new EscherSplitMenuColorsRecord();
  1928. dggContainer.setRecordId((short) 0xF000);
  1929. dggContainer.setOptions((short) 0x000F);
  1930. dgg.setRecordId(EscherDggRecord.RECORD_ID);
  1931. dgg.setOptions((short)0x0000);
  1932. dgg.setShapeIdMax(1024);
  1933. dgg.setNumShapesSaved(0);
  1934. dgg.setDrawingsSaved(0);
  1935. dgg.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {} );
  1936. drawingManager = new DrawingManager2(dgg);
  1937. EscherContainerRecord bstoreContainer = null;
  1938. if (escherBSERecords.size() > 0)
  1939. {
  1940. bstoreContainer = new EscherContainerRecord();
  1941. bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER );
  1942. bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) );
  1943. for (EscherRecord escherRecord : escherBSERecords) {
  1944. bstoreContainer.addChildRecord( escherRecord );
  1945. }
  1946. }
  1947. opt.setRecordId((short) 0xF00B);
  1948. opt.setOptions((short) 0x0033);
  1949. opt.addEscherProperty( new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296) );
  1950. opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 0x08000041) );
  1951. opt.addEscherProperty( new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, 134217792) );
  1952. splitMenuColors.setRecordId((short) 0xF11E);
  1953. splitMenuColors.setOptions((short) 0x0040);
  1954. splitMenuColors.setColor1(0x0800000D);
  1955. splitMenuColors.setColor2(0x0800000C);
  1956. splitMenuColors.setColor3(0x08000017);
  1957. splitMenuColors.setColor4(0x100000F7);
  1958. dggContainer.addChildRecord(dgg);
  1959. if (bstoreContainer != null)
  1960. dggContainer.addChildRecord( bstoreContainer );
  1961. dggContainer.addChildRecord(opt);
  1962. dggContainer.addChildRecord(splitMenuColors);
  1963. int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
  1964. if (dgLoc == -1) {
  1965. DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
  1966. drawingGroup.addEscherRecord(dggContainer);
  1967. int loc = findFirstRecordLocBySid(CountryRecord.sid);
  1968. getRecords().add(loc+1, drawingGroup);
  1969. } else {
  1970. DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
  1971. drawingGroup.addEscherRecord(dggContainer);
  1972. getRecords().set(dgLoc, drawingGroup);
  1973. }
  1974. }
  1975. }
  1976. public WindowOneRecord getWindowOne() {
  1977. return windowOne;
  1978. }
  1979. public EscherBSERecord getBSERecord(int pictureIndex) {
  1980. return escherBSERecords.get(pictureIndex-1);
  1981. }
  1982. public int addBSERecord(EscherBSERecord e) {
  1983. createDrawingGroup();
  1984. // maybe we don't need that as an instance variable anymore
  1985. escherBSERecords.add( e );
  1986. int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
  1987. DrawingGroupRecord drawingGroup = (DrawingGroupRecord) getRecords().get( dgLoc );
  1988. EscherContainerRecord dggContainer = (EscherContainerRecord) drawingGroup.getEscherRecord( 0 );
  1989. EscherContainerRecord bstoreContainer;
  1990. if (dggContainer.getChild( 1 ).getRecordId() == EscherContainerRecord.BSTORE_CONTAINER )
  1991. {
  1992. bstoreContainer = (EscherContainerRecord) dggContainer.getChild( 1 );
  1993. }
  1994. else
  1995. {
  1996. bstoreContainer = new EscherContainerRecord();
  1997. bstoreContainer.setRecordId( EscherContainerRecord.BSTORE_CONTAINER );
  1998. List<EscherRecord> childRecords = dggContainer.getChildRecords();
  1999. childRecords.add(1, bstoreContainer);
  2000. dggContainer.setChildRecords(childRecords);
  2001. }
  2002. bstoreContainer.setOptions( (short) ( (escherBSERecords.size() << 4) | 0xF ) );
  2003. bstoreContainer.addChildRecord( e );
  2004. return escherBSERecords.size();
  2005. }
  2006. public DrawingManager2 getDrawingManager()
  2007. {
  2008. return drawingManager;
  2009. }
  2010. public WriteProtectRecord getWriteProtect() {
  2011. if (writeProtect == null) {
  2012. writeProtect = new WriteProtectRecord();
  2013. int i = 0;
  2014. for (i = 0;
  2015. i < records.size() && !(records.get(i) instanceof BOFRecord);
  2016. i++) {
  2017. }
  2018. records.add(i+1, writeProtect);
  2019. }
  2020. return this.writeProtect;
  2021. }
  2022. public WriteAccessRecord getWriteAccess() {
  2023. if (writeAccess == null) {
  2024. writeAccess = createWriteAccess();
  2025. int i = 0;
  2026. for (i = 0;
  2027. i < records.size() && !(records.get(i) instanceof InterfaceEndRecord);
  2028. i++) {
  2029. }
  2030. records.add(i+1, writeAccess);
  2031. }
  2032. return writeAccess;
  2033. }
  2034. public FileSharingRecord getFileSharing() {
  2035. if (fileShare == null) {
  2036. fileShare = new FileSharingRecord();
  2037. int i = 0;
  2038. for (i = 0;
  2039. i < records.size() && !(records.get(i) instanceof WriteAccessRecord);
  2040. i++) {
  2041. }
  2042. records.add(i+1, fileShare);
  2043. }
  2044. return fileShare;
  2045. }
  2046. /**
  2047. * is the workbook protected with a password (not encrypted)?
  2048. */
  2049. public boolean isWriteProtected() {
  2050. if (fileShare == null) {
  2051. return false;
  2052. }
  2053. FileSharingRecord frec = getFileSharing();
  2054. return frec.getReadOnly() == 1;
  2055. }
  2056. /**
  2057. * protect a workbook with a password (not encypted, just sets writeprotect
  2058. * flags and the password.
  2059. * @param password to set
  2060. */
  2061. public void writeProtectWorkbook( String password, String username ) {
  2062. int protIdx = -1;
  2063. FileSharingRecord frec = getFileSharing();
  2064. WriteAccessRecord waccess = getWriteAccess();
  2065. WriteProtectRecord wprotect = getWriteProtect();
  2066. frec.setReadOnly((short)1);
  2067. frec.setPassword(FileSharingRecord.hashPassword(password));
  2068. frec.setUsername(username);
  2069. waccess.setUsername(username);
  2070. }
  2071. /**
  2072. * removes the write protect flag
  2073. */
  2074. public void unwriteProtectWorkbook() {
  2075. records.remove(fileShare);
  2076. records.remove(writeProtect);
  2077. fileShare = null;
  2078. writeProtect = null;
  2079. }
  2080. /**
  2081. * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
  2082. * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
  2083. * @return the string representation of the defined or external name
  2084. */
  2085. public String resolveNameXText(int refIndex, int definedNameIndex) {
  2086. return linkTable.resolveNameXText(refIndex, definedNameIndex);
  2087. }
  2088. /**
  2089. *
  2090. * @param name the name of an external function, typically a name of a UDF
  2091. * @param udf locator of user-defiend functions to resolve names of VBA and Add-In functions
  2092. * @return the external name or null
  2093. */
  2094. public NameXPtg getNameXPtg(String name, UDFFinder udf) {
  2095. LinkTable lnk = getOrCreateLinkTable();
  2096. NameXPtg xptg = lnk.getNameXPtg(name);
  2097. if(xptg == null && udf.findFunction(name) != null) {
  2098. // the name was not found in the list of external names
  2099. // check if the Workbook's UDFFinder is aware about it and register the name if it is
  2100. xptg = lnk.addNameXPtg(name);
  2101. }
  2102. return xptg;
  2103. }
  2104. /**
  2105. * Check if the cloned sheet has drawings. If yes, then allocate a new drawing group ID and
  2106. * re-generate shape IDs
  2107. *
  2108. * @param sheet the cloned sheet
  2109. */
  2110. public void cloneDrawings(InternalSheet sheet){
  2111. findDrawingGroup();
  2112. if(drawingManager == null) {
  2113. //this workbook does not have drawings
  2114. return;
  2115. }
  2116. //check if the cloned sheet has drawings
  2117. int aggLoc = sheet.aggregateDrawingRecords(drawingManager, false);
  2118. if(aggLoc != -1) {
  2119. EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
  2120. EscherContainerRecord escherContainer = agg.getEscherContainer();
  2121. if (escherContainer == null) {
  2122. return;
  2123. }
  2124. EscherDggRecord dgg = drawingManager.getDgg();
  2125. //register a new drawing group for the cloned sheet
  2126. int dgId = drawingManager.findNewDrawingGroupId();
  2127. dgg.addCluster( dgId, 0 );
  2128. dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1);
  2129. EscherDgRecord dg = null;
  2130. for(Iterator<EscherRecord> it = escherContainer.getChildIterator(); it.hasNext();) {
  2131. EscherRecord er = it.next();
  2132. if(er instanceof EscherDgRecord) {
  2133. dg = (EscherDgRecord)er;
  2134. //update id of the drawing in the cloned sheet
  2135. dg.setOptions( (short) ( dgId << 4 ) );
  2136. } else if (er instanceof EscherContainerRecord){
  2137. // iterate over shapes and re-generate shapeId
  2138. EscherContainerRecord cp = (EscherContainerRecord)er;
  2139. for(Iterator<EscherRecord> spIt = cp.getChildRecords().iterator(); spIt.hasNext();) {
  2140. EscherContainerRecord shapeContainer = (EscherContainerRecord)spIt.next();
  2141. for(EscherRecord shapeChildRecord : shapeContainer.getChildRecords()) {
  2142. int recordId = shapeChildRecord.getRecordId();
  2143. if (recordId == EscherSpRecord.RECORD_ID){
  2144. EscherSpRecord sp = (EscherSpRecord)shapeChildRecord;
  2145. int shapeId = drawingManager.allocateShapeId((short)dgId, dg);
  2146. //allocateShapeId increments the number of shapes. roll back to the previous value
  2147. dg.setNumShapes(dg.getNumShapes()-1);
  2148. sp.setShapeId(shapeId);
  2149. } else if (recordId == EscherOptRecord.RECORD_ID){
  2150. EscherOptRecord opt = (EscherOptRecord)shapeChildRecord;
  2151. EscherSimpleProperty prop = (EscherSimpleProperty)opt.lookup(
  2152. EscherProperties.BLIP__BLIPTODISPLAY );
  2153. if (prop != null){
  2154. int pictureIndex = prop.getPropertyValue();
  2155. // increment reference count for pictures
  2156. EscherBSERecord bse = getBSERecord(pictureIndex);
  2157. bse.setRef(bse.getRef() + 1);
  2158. }
  2159. }
  2160. }
  2161. }
  2162. }
  2163. }
  2164. }
  2165. }
  2166. public NameRecord cloneFilter(int filterDbNameIndex, int newSheetIndex){
  2167. NameRecord origNameRecord = getNameRecord(filterDbNameIndex);
  2168. // copy original formula but adjust 3D refs to the new external sheet index
  2169. int newExtSheetIx = checkExternSheet(newSheetIndex);
  2170. Ptg[] ptgs = origNameRecord.getNameDefinition();
  2171. for (int i=0; i< ptgs.length; i++) {
  2172. Ptg ptg = ptgs[i];
  2173. if (ptg instanceof Area3DPtg) {
  2174. Area3DPtg a3p = (Area3DPtg) ((OperandPtg) ptg).copy();
  2175. a3p.setExternSheetIndex(newExtSheetIx);
  2176. ptgs[i] = a3p;
  2177. } else if (ptg instanceof Ref3DPtg) {
  2178. Ref3DPtg r3p = (Ref3DPtg) ((OperandPtg) ptg).copy();
  2179. r3p.setExternSheetIndex(newExtSheetIx);
  2180. ptgs[i] = r3p;
  2181. }
  2182. }
  2183. NameRecord newNameRecord = createBuiltInName(NameRecord.BUILTIN_FILTER_DB, newSheetIndex+1);
  2184. newNameRecord.setNameDefinition(ptgs);
  2185. newNameRecord.setHidden(true);
  2186. return newNameRecord;
  2187. }
  2188. /**
  2189. * Updates named ranges due to moving of cells
  2190. */
  2191. public void updateNamesAfterCellShift(FormulaShifter shifter) {
  2192. for (int i = 0 ; i < getNumNames() ; ++i){
  2193. NameRecord nr = getNameRecord(i);
  2194. Ptg[] ptgs = nr.getNameDefinition();
  2195. if (shifter.adjustFormula(ptgs, nr.getSheetNumber())) {
  2196. nr.setNameDefinition(ptgs);
  2197. }
  2198. }
  2199. }
  2200. /**
  2201. * Get or create RecalcIdRecord
  2202. *
  2203. * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#setForceFormulaRecalculation(boolean)
  2204. */
  2205. public RecalcIdRecord getRecalcId(){
  2206. RecalcIdRecord record = (RecalcIdRecord)findFirstRecordBySid(RecalcIdRecord.sid);
  2207. if(record == null){
  2208. record = new RecalcIdRecord();
  2209. // typically goes after the Country record
  2210. int pos = findFirstRecordLocBySid(CountryRecord.sid);
  2211. records.add(pos + 1, record);
  2212. }
  2213. return record;
  2214. }
  2215. /**
  2216. * Changes an external referenced file to another file.
  2217. * A formular in Excel which refers a cell in another file is saved in two parts:
  2218. * The referenced file is stored in an reference table. the row/cell information is saved separate.
  2219. * This method invokation will only change the reference in the lookup-table itself.
  2220. * @param oldUrl The old URL to search for and which is to be replaced
  2221. * @param newUrl The URL replacement
  2222. * @return true if the oldUrl was found and replaced with newUrl. Otherwise false
  2223. */
  2224. public boolean changeExternalReference(String oldUrl, String newUrl) {
  2225. return linkTable.changeExternalReference(oldUrl, newUrl);
  2226. }
  2227. }