PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/components/forks/poi/src/loci/poi/hssf/eventmodel/EventRecordFactory.java

http://github.com/openmicroscopy/bioformats
Java | 505 lines | 338 code | 48 blank | 119 comment | 50 complexity | 468caa59753e7c2d27a76f3903f70894 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception
  1. /*
  2. * #%L
  3. * Fork of Apache Jakarta POI.
  4. * %%
  5. * Copyright (C) 2008 - 2013 Open Microscopy Environment:
  6. * - Board of Regents of the University of Wisconsin-Madison
  7. * - Glencoe Software, Inc.
  8. * - University of Dundee
  9. * %%
  10. * Licensed under the Apache License, Version 2.0 (the "License");
  11. * you may not use this file except in compliance with the License.
  12. * You may obtain a copy of the License at
  13. *
  14. * http://www.apache.org/licenses/LICENSE-2.0
  15. *
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. * #L%
  22. */
  23. /* ====================================================================
  24. Licensed to the Apache Software Foundation (ASF) under one or more
  25. contributor license agreements. See the NOTICE file distributed with
  26. this work for additional information regarding copyright ownership.
  27. The ASF licenses this file to You under the Apache License, Version 2.0
  28. (the "License"); you may not use this file except in compliance with
  29. the License. You may obtain a copy of the License at
  30. http://www.apache.org/licenses/LICENSE-2.0
  31. Unless required by applicable law or agreed to in writing, software
  32. distributed under the License is distributed on an "AS IS" BASIS,
  33. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  34. See the License for the specific language governing permissions and
  35. limitations under the License.
  36. ==================================================================== */
  37. package loci.poi.hssf.eventmodel;
  38. import java.io.InputStream;
  39. import java.lang.reflect.Constructor;
  40. import java.util.ArrayList;
  41. import java.util.HashMap;
  42. import java.util.Iterator;
  43. import java.util.List;
  44. import java.util.Map;
  45. import loci.poi.hssf.record.BOFRecord;
  46. import loci.poi.hssf.record.BackupRecord;
  47. import loci.poi.hssf.record.BlankRecord;
  48. import loci.poi.hssf.record.BookBoolRecord;
  49. import loci.poi.hssf.record.BoolErrRecord;
  50. import loci.poi.hssf.record.BottomMarginRecord;
  51. import loci.poi.hssf.record.BoundSheetRecord;
  52. import loci.poi.hssf.record.CalcCountRecord;
  53. import loci.poi.hssf.record.CalcModeRecord;
  54. import loci.poi.hssf.record.CodepageRecord;
  55. import loci.poi.hssf.record.ColumnInfoRecord;
  56. import loci.poi.hssf.record.ContinueRecord;
  57. import loci.poi.hssf.record.CountryRecord;
  58. import loci.poi.hssf.record.DBCellRecord;
  59. import loci.poi.hssf.record.DSFRecord;
  60. import loci.poi.hssf.record.DateWindow1904Record;
  61. import loci.poi.hssf.record.DefaultColWidthRecord;
  62. import loci.poi.hssf.record.DefaultRowHeightRecord;
  63. import loci.poi.hssf.record.DeltaRecord;
  64. import loci.poi.hssf.record.DimensionsRecord;
  65. import loci.poi.hssf.record.EOFRecord;
  66. import loci.poi.hssf.record.ExtSSTRecord;
  67. import loci.poi.hssf.record.ExtendedFormatRecord;
  68. import loci.poi.hssf.record.ExternSheetRecord;
  69. import loci.poi.hssf.record.FnGroupCountRecord;
  70. import loci.poi.hssf.record.FontRecord;
  71. import loci.poi.hssf.record.FooterRecord;
  72. import loci.poi.hssf.record.FormatRecord;
  73. import loci.poi.hssf.record.GridsetRecord;
  74. import loci.poi.hssf.record.GutsRecord;
  75. import loci.poi.hssf.record.HCenterRecord;
  76. import loci.poi.hssf.record.HeaderRecord;
  77. import loci.poi.hssf.record.HideObjRecord;
  78. import loci.poi.hssf.record.IndexRecord;
  79. import loci.poi.hssf.record.InterfaceEndRecord;
  80. import loci.poi.hssf.record.InterfaceHdrRecord;
  81. import loci.poi.hssf.record.IterationRecord;
  82. import loci.poi.hssf.record.LabelRecord;
  83. import loci.poi.hssf.record.LabelSSTRecord;
  84. import loci.poi.hssf.record.LeftMarginRecord;
  85. import loci.poi.hssf.record.MMSRecord;
  86. import loci.poi.hssf.record.MergeCellsRecord;
  87. import loci.poi.hssf.record.MulBlankRecord;
  88. import loci.poi.hssf.record.MulRKRecord;
  89. import loci.poi.hssf.record.NameRecord;
  90. import loci.poi.hssf.record.NumberRecord;
  91. import loci.poi.hssf.record.PaneRecord;
  92. import loci.poi.hssf.record.PaletteRecord;
  93. import loci.poi.hssf.record.PasswordRecord;
  94. import loci.poi.hssf.record.PasswordRev4Record;
  95. import loci.poi.hssf.record.PrecisionRecord;
  96. import loci.poi.hssf.record.PrintGridlinesRecord;
  97. import loci.poi.hssf.record.PrintHeadersRecord;
  98. import loci.poi.hssf.record.PrintSetupRecord;
  99. import loci.poi.hssf.record.ProtectRecord;
  100. import loci.poi.hssf.record.ProtectionRev4Record;
  101. import loci.poi.hssf.record.RKRecord;
  102. import loci.poi.hssf.record.Record;
  103. import loci.poi.hssf.record.RecordFormatException;
  104. import loci.poi.hssf.record.RecordInputStream;
  105. import loci.poi.hssf.record.RefModeRecord;
  106. import loci.poi.hssf.record.RefreshAllRecord;
  107. import loci.poi.hssf.record.RightMarginRecord;
  108. import loci.poi.hssf.record.RowRecord;
  109. import loci.poi.hssf.record.SSTRecord;
  110. import loci.poi.hssf.record.SaveRecalcRecord;
  111. import loci.poi.hssf.record.SelectionRecord;
  112. import loci.poi.hssf.record.SharedFormulaRecord;
  113. import loci.poi.hssf.record.StringRecord;
  114. import loci.poi.hssf.record.StyleRecord;
  115. import loci.poi.hssf.record.TabIdRecord;
  116. import loci.poi.hssf.record.TopMarginRecord;
  117. import loci.poi.hssf.record.UnknownRecord;
  118. import loci.poi.hssf.record.UseSelFSRecord;
  119. import loci.poi.hssf.record.VCenterRecord;
  120. import loci.poi.hssf.record.WSBoolRecord;
  121. import loci.poi.hssf.record.WindowOneRecord;
  122. import loci.poi.hssf.record.WindowProtectRecord;
  123. import loci.poi.hssf.record.WindowTwoRecord;
  124. import loci.poi.hssf.record.WriteAccessRecord;
  125. import loci.poi.hssf.record.WriteProtectRecord;
  126. import loci.poi.hssf.record.FilePassRecord;
  127. import loci.poi.hssf.record.NoteRecord;
  128. /**
  129. * Event-based record factory. As opposed to RecordFactory
  130. * this refactored version throws record events as it comes
  131. * accross the records. I throws the "lazily" one record behind
  132. * to ensure that ContinueRecords are processed first.
  133. *
  134. * @author Andrew C. Oliver (acoliver@apache.org) - probably to blame for the bugs (so yank his chain on the list)
  135. * @author Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory
  136. * @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory
  137. * @author Csaba Nagy (ncsaba at yahoo dot com)
  138. */
  139. public class EventRecordFactory
  140. {
  141. /**
  142. * contains the classes for all the records we want to parse.
  143. */
  144. private static final Class[] records;
  145. static {
  146. records = new Class[]
  147. {
  148. BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class,
  149. InterfaceEndRecord.class, WriteAccessRecord.class,
  150. CodepageRecord.class, DSFRecord.class, TabIdRecord.class,
  151. FnGroupCountRecord.class, WindowProtectRecord.class,
  152. ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class,
  153. PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class,
  154. HideObjRecord.class, DateWindow1904Record.class,
  155. PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class,
  156. FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class,
  157. StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class,
  158. CountryRecord.class, SSTRecord.class, ExtSSTRecord.class,
  159. EOFRecord.class, IndexRecord.class, CalcModeRecord.class,
  160. CalcCountRecord.class, RefModeRecord.class, IterationRecord.class,
  161. DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class,
  162. PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class,
  163. DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class,
  164. FooterRecord.class, HCenterRecord.class, VCenterRecord.class,
  165. PrintSetupRecord.class, DefaultColWidthRecord.class,
  166. DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class,
  167. RKRecord.class, NumberRecord.class, DBCellRecord.class,
  168. WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class,
  169. LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class,
  170. MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class,
  171. BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
  172. LeftMarginRecord.class, RightMarginRecord.class,
  173. TopMarginRecord.class, BottomMarginRecord.class,
  174. PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class,
  175. WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
  176. NoteRecord.class
  177. };
  178. }
  179. /**
  180. * cache of the recordsToMap();
  181. */
  182. private static Map recordsMap = recordsToMap(records);
  183. /**
  184. * cache of the return of getAllKnownSids so that we don't have to
  185. * expensively get them every time.
  186. */
  187. private static short[] sidscache;
  188. /**
  189. * List of the listners that are registred. should all be ERFListener
  190. */
  191. private List listeners;
  192. /**
  193. * instance is abortable or not
  194. */
  195. private boolean abortable;
  196. /**
  197. * Construct an abortable EventRecordFactory.
  198. * The same as calling new EventRecordFactory(true)
  199. * @see #EventRecordFactory(boolean)
  200. */
  201. public EventRecordFactory() {
  202. this(true);
  203. }
  204. /**
  205. * Create an EventRecordFactory
  206. * @param abortable specifies whether the return from the listener
  207. * handler functions are obeyed. False means they are ignored. True
  208. * means the event loop exits on error.
  209. */
  210. public EventRecordFactory(boolean abortable) {
  211. this.abortable = abortable;
  212. listeners = new ArrayList(recordsMap.size());
  213. if (sidscache == null) {
  214. sidscache = getAllKnownRecordSIDs();
  215. }
  216. }
  217. /**
  218. * Register a listener for records. These can be for all records
  219. * or just a subset.
  220. *
  221. * @param sids an array of Record.sid values identifying the records
  222. * the listener will work with. Alternatively if this is "null" then
  223. * all records are passed.
  224. */
  225. public void registerListener(ERFListener listener, short[] sids) {
  226. if (sids == null)
  227. sids = sidscache;
  228. ERFListener wrapped = new ListenerWrapper(listener, sids, abortable);
  229. listeners.add(wrapped);
  230. }
  231. /**
  232. * used for unit tests to test the registration of record listeners.
  233. * @return Iterator of ERFListeners
  234. */
  235. protected Iterator listeners() {
  236. return listeners.iterator();
  237. }
  238. /**
  239. * sends the record event to all registered listeners.
  240. * @param record the record to be thrown.
  241. * @return boolean abort. If exitability is turned on this aborts
  242. * out of the event loop should any listener specify to do so.
  243. */
  244. private boolean throwRecordEvent(Record record)
  245. {
  246. boolean result = true;
  247. Iterator i = listeners.iterator();
  248. while (i.hasNext()) {
  249. result = ((ERFListener) i.next()).processRecord(record);
  250. if (abortable == true && result == false) {
  251. break;
  252. }
  253. }
  254. return result;
  255. }
  256. /**
  257. * Create an array of records from an input stream
  258. *
  259. * @param in the InputStream from which the records will be
  260. * obtained
  261. *
  262. * @exception RecordFormatException on error processing the
  263. * InputStream
  264. */
  265. public void processRecords(InputStream in)
  266. throws RecordFormatException
  267. {
  268. Record last_record = null;
  269. RecordInputStream recStream = new RecordInputStream(in);
  270. while (recStream.hasNextRecord()) {
  271. recStream.nextRecord();
  272. Record[] recs = createRecord(recStream); // handle MulRK records
  273. if (recs.length > 1)
  274. {
  275. for (int k = 0; k < recs.length; k++)
  276. {
  277. if ( last_record != null ) {
  278. if (throwRecordEvent(last_record) == false && abortable == true) {
  279. last_record = null;
  280. break;
  281. }
  282. }
  283. last_record =
  284. recs[ k ]; // do to keep the algorythm homogenous...you can't
  285. } // actually continue a number record anyhow.
  286. }
  287. else
  288. {
  289. Record record = recs[ 0 ];
  290. if (record != null)
  291. {
  292. if (last_record != null) {
  293. if (throwRecordEvent(last_record) == false && abortable == true) {
  294. last_record = null;
  295. break;
  296. }
  297. }
  298. last_record = record;
  299. }
  300. }
  301. }
  302. if (last_record != null) {
  303. throwRecordEvent(last_record);
  304. }
  305. }
  306. /**
  307. * create a record, if there are MUL records than multiple records
  308. * are returned digested into the non-mul form.
  309. */
  310. public static Record [] createRecord(RecordInputStream in)
  311. {
  312. Record retval = null;
  313. Record[] realretval = null;
  314. try
  315. {
  316. Constructor constructor =
  317. ( Constructor ) recordsMap.get(new Short(in.getSid()));
  318. if (constructor != null)
  319. {
  320. retval = ( Record ) constructor.newInstance(new Object[]
  321. {
  322. in
  323. });
  324. }
  325. else
  326. {
  327. retval = new UnknownRecord(in);
  328. }
  329. }
  330. catch (Exception introspectionException)
  331. {
  332. throw new RecordFormatException("Unable to construct record instance" , introspectionException);
  333. }
  334. if (retval instanceof RKRecord)
  335. {
  336. RKRecord rk = ( RKRecord ) retval;
  337. NumberRecord num = new NumberRecord();
  338. num.setColumn(rk.getColumn());
  339. num.setRow(rk.getRow());
  340. num.setXFIndex(rk.getXFIndex());
  341. num.setValue(rk.getRKNumber());
  342. retval = num;
  343. }
  344. else if (retval instanceof DBCellRecord)
  345. {
  346. retval = null;
  347. }
  348. else if (retval instanceof MulRKRecord)
  349. {
  350. MulRKRecord mrk = ( MulRKRecord ) retval;
  351. realretval = new Record[ mrk.getNumColumns() ];
  352. for (int k = 0; k < mrk.getNumColumns(); k++)
  353. {
  354. NumberRecord nr = new NumberRecord();
  355. nr.setColumn(( short ) (k + mrk.getFirstColumn()));
  356. nr.setRow(mrk.getRow());
  357. nr.setXFIndex(mrk.getXFAt(k));
  358. nr.setValue(mrk.getRKNumberAt(k));
  359. realretval[ k ] = nr;
  360. }
  361. }
  362. else if (retval instanceof MulBlankRecord)
  363. {
  364. MulBlankRecord mb = ( MulBlankRecord ) retval;
  365. realretval = new Record[ mb.getNumColumns() ];
  366. for (int k = 0; k < mb.getNumColumns(); k++)
  367. {
  368. BlankRecord br = new BlankRecord();
  369. br.setColumn(( short ) (k + mb.getFirstColumn()));
  370. br.setRow(mb.getRow());
  371. br.setXFIndex(mb.getXFAt(k));
  372. realretval[ k ] = br;
  373. }
  374. }
  375. if (realretval == null)
  376. {
  377. realretval = new Record[ 1 ];
  378. realretval[ 0 ] = retval;
  379. }
  380. return realretval;
  381. }
  382. /**
  383. * @return an array of all the SIDS for all known records
  384. */
  385. public static short [] getAllKnownRecordSIDs()
  386. {
  387. short[] results = new short[ recordsMap.size() ];
  388. int i = 0;
  389. for (Iterator iterator = recordsMap.keySet().iterator();
  390. iterator.hasNext(); )
  391. {
  392. Short sid = ( Short ) iterator.next();
  393. results[ i++ ] = sid.shortValue();
  394. }
  395. return results;
  396. }
  397. /**
  398. * gets the record constructors and sticks them in the map by SID
  399. * @return map of SIDs to short,short,byte[] constructors for Record classes
  400. * most of loci.poi.hssf.record.*
  401. */
  402. private static Map recordsToMap(Class [] records)
  403. {
  404. Map result = new HashMap();
  405. Constructor constructor;
  406. for (int i = 0; i < records.length; i++)
  407. {
  408. Class record = null;
  409. short sid = 0;
  410. record = records[ i ];
  411. try
  412. {
  413. sid = record.getField("sid").getShort(null);
  414. constructor = record.getConstructor(new Class[]
  415. {
  416. RecordInputStream.class
  417. });
  418. }
  419. catch (Exception illegalArgumentException)
  420. {
  421. throw new RecordFormatException(
  422. "Unable to determine record types");
  423. }
  424. result.put(new Short(sid), constructor);
  425. }
  426. return result;
  427. }
  428. }
  429. /**
  430. * ListenerWrapper just wraps an ERFListener and adds support for throwing
  431. * the event to multiple SIDs
  432. */
  433. class ListenerWrapper implements ERFListener {
  434. private ERFListener listener;
  435. private short[] sids;
  436. private boolean abortable;
  437. ListenerWrapper(ERFListener listener, short[] sids, boolean abortable) {
  438. this.listener = listener;
  439. this.sids = sids;
  440. this.abortable = abortable;
  441. }
  442. public boolean processRecord(Record rec)
  443. {
  444. boolean result = true;
  445. for (int k = 0; k < sids.length; k++) {
  446. if (sids[k] == rec.getSid()) {
  447. result = listener.processRecord(rec);
  448. if (abortable == true && result == false) {
  449. break;
  450. }
  451. }
  452. }
  453. return result;
  454. }
  455. }