PageRenderTime 250ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/components/forks/poi/src/loci/poi/poifs/filesystem/POIFSFileSystem.java

http://github.com/openmicroscopy/bioformats
Java | 521 lines | 258 code | 75 blank | 188 comment | 17 complexity | 821ddc8beda41ba4d7ad809592451995 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.poifs.filesystem;
  38. import java.io.*;
  39. import java.util.*;
  40. import loci.common.*;
  41. import loci.poi.poifs.common.POIFSConstants;
  42. import loci.poi.poifs.dev.POIFSViewable;
  43. import loci.poi.poifs.property.DirectoryProperty;
  44. import loci.poi.poifs.property.DocumentProperty;
  45. import loci.poi.poifs.property.Property;
  46. import loci.poi.poifs.property.PropertyTable;
  47. import loci.poi.poifs.storage.BATBlock;
  48. import loci.poi.poifs.storage.BlockAllocationTableReader;
  49. import loci.poi.poifs.storage.BlockAllocationTableWriter;
  50. import loci.poi.poifs.storage.BlockList;
  51. import loci.poi.poifs.storage.BlockWritable;
  52. import loci.poi.poifs.storage.HeaderBlockReader;
  53. import loci.poi.poifs.storage.HeaderBlockWriter;
  54. import loci.poi.poifs.storage.RawDataBlock;
  55. import loci.poi.poifs.storage.RawDataBlockList;
  56. import loci.poi.poifs.storage.SmallBlockTableReader;
  57. import loci.poi.poifs.storage.SmallBlockTableWriter;
  58. import loci.poi.poifs.storage.SmallDocumentBlock;
  59. import loci.poi.poifs.storage.ListManagedBlock;
  60. import loci.poi.poifs.storage.SmallDocumentBlockList;
  61. import loci.poi.util.*;
  62. /**
  63. * This is the main class of the POIFS system; it manages the entire
  64. * life cycle of the filesystem.
  65. *
  66. * @author Marc Johnson (mjohnson at apache dot org)
  67. */
  68. public class POIFSFileSystem
  69. implements POIFSViewable
  70. {
  71. private PropertyTable _property_table;
  72. private List _documents;
  73. private DirectoryNode _root;
  74. private int bigBlockSize;
  75. private RandomAccessInputStream stream;
  76. /**
  77. * Constructor, intended for writing
  78. */
  79. public POIFSFileSystem()
  80. {
  81. _property_table = new PropertyTable();
  82. _documents = new ArrayList();
  83. _root = null;
  84. }
  85. /**
  86. * Create a POIFSFileSystem from an InputStream
  87. *
  88. * @param stream the InputStream from which to read the data
  89. *
  90. * @exception IOException on errors reading, or on invalid data
  91. */
  92. public POIFSFileSystem(final RandomAccessInputStream stream, int size)
  93. throws IOException
  94. {
  95. this();
  96. this.stream = stream;
  97. // read the header block from the stream
  98. HeaderBlockReader header_block_reader = new HeaderBlockReader(stream,
  99. size);
  100. // read the rest of the stream into blocks
  101. RawDataBlockList data_blocks = new RawDataBlockList(stream,
  102. size);
  103. // set up the block allocation table (necessary for the
  104. // data_blocks to be manageable
  105. new BlockAllocationTableReader(header_block_reader.getBATCount(),
  106. header_block_reader.getBATArray(),
  107. header_block_reader.getXBATCount(),
  108. header_block_reader.getXBATIndex(),
  109. data_blocks, size);
  110. // get property table from the document
  111. PropertyTable properties =
  112. new PropertyTable(header_block_reader.getPropertyStart(),
  113. data_blocks);
  114. // init documents
  115. BlockList small = SmallBlockTableReader.getSmallDocumentBlocks(
  116. data_blocks, properties.getRoot(),
  117. header_block_reader.getSBATStart());
  118. processProperties(small, data_blocks,
  119. properties.getRoot().getChildren(), null);
  120. }
  121. /**
  122. * Create a new document to be added to the root directory
  123. *
  124. * @param stream the InputStream from which the document's data
  125. * will be obtained
  126. * @param name the name of the new POIFSDocument
  127. *
  128. * @return the new DocumentEntry
  129. *
  130. * @exception IOException on error creating the new POIFSDocument
  131. */
  132. public DocumentEntry createDocument(final RandomAccessInputStream stream,
  133. final String name)
  134. throws IOException
  135. {
  136. return getRoot().createDocument(name, stream);
  137. }
  138. /**
  139. * create a new DocumentEntry in the root entry; the data will be
  140. * provided later
  141. *
  142. * @param name the name of the new DocumentEntry
  143. * @param size the size of the new DocumentEntry
  144. * @param writer the writer of the new DocumentEntry
  145. *
  146. * @return the new DocumentEntry
  147. *
  148. * @exception IOException
  149. */
  150. public DocumentEntry createDocument(final String name, final int size,
  151. final POIFSWriterListener writer)
  152. throws IOException
  153. {
  154. return getRoot().createDocument(name, size, writer);
  155. }
  156. /**
  157. * create a new DirectoryEntry in the root directory
  158. *
  159. * @param name the name of the new DirectoryEntry
  160. *
  161. * @return the new DirectoryEntry
  162. *
  163. * @exception IOException on name duplication
  164. */
  165. public DirectoryEntry createDirectory(final String name)
  166. throws IOException
  167. {
  168. return getRoot().createDirectory(name);
  169. }
  170. /**
  171. * Write the filesystem out
  172. *
  173. * @param stream the OutputStream to which the filesystem will be
  174. * written
  175. *
  176. * @exception IOException thrown on errors writing to the stream
  177. */
  178. public void writeFilesystem(final OutputStream stream)
  179. throws IOException
  180. {
  181. // get the property table ready
  182. _property_table.preWrite();
  183. // create the small block store, and the SBAT
  184. SmallBlockTableWriter sbtw =
  185. new SmallBlockTableWriter(_documents, _property_table.getRoot());
  186. // create the block allocation table
  187. BlockAllocationTableWriter bat =
  188. new BlockAllocationTableWriter();
  189. // create a list of BATManaged objects: the documents plus the
  190. // property table and the small block table
  191. List bm_objects = new ArrayList();
  192. bm_objects.addAll(_documents);
  193. bm_objects.add(_property_table);
  194. bm_objects.add(sbtw);
  195. bm_objects.add(sbtw.getSBAT());
  196. // walk the list, allocating space for each and assigning each
  197. // a starting block number
  198. Iterator iter = bm_objects.iterator();
  199. while (iter.hasNext())
  200. {
  201. BATManaged bmo = ( BATManaged ) iter.next();
  202. int block_count = bmo.countBlocks();
  203. if (block_count != 0)
  204. {
  205. bmo.setStartBlock(bat.allocateSpace(block_count));
  206. }
  207. else
  208. {
  209. // Either the BATManaged object is empty or its data
  210. // is composed of SmallBlocks; in either case,
  211. // allocating space in the BAT is inappropriate
  212. }
  213. }
  214. // allocate space for the block allocation table and take its
  215. // starting block
  216. int batStartBlock = bat.createBlocks(512);
  217. // get the extended block allocation table blocks
  218. HeaderBlockWriter header_block_writer = new HeaderBlockWriter(512);
  219. BATBlock[] xbat_blocks =
  220. header_block_writer.setBATBlocks(bat.countBlocks(),
  221. batStartBlock, 512);
  222. // set the property table start block
  223. header_block_writer.setPropertyStart(_property_table.getStartBlock());
  224. // set the small block allocation table start block
  225. header_block_writer.setSBATStart(sbtw.getSBAT().getStartBlock());
  226. // set the small block allocation table block count
  227. header_block_writer.setSBATBlockCount(sbtw.getSBATBlockCount());
  228. // the header is now properly initialized. Make a list of
  229. // writers (the header block, followed by the documents, the
  230. // property table, the small block store, the small block
  231. // allocation table, the block allocation table, and the
  232. // extended block allocation table blocks)
  233. List writers = new ArrayList();
  234. writers.add(header_block_writer);
  235. writers.addAll(_documents);
  236. writers.add(_property_table);
  237. writers.add(sbtw);
  238. writers.add(sbtw.getSBAT());
  239. writers.add(bat);
  240. for (int j = 0; j < xbat_blocks.length; j++)
  241. {
  242. writers.add(xbat_blocks[ j ]);
  243. }
  244. // now, write everything out
  245. iter = writers.iterator();
  246. while (iter.hasNext())
  247. {
  248. BlockWritable writer = ( BlockWritable ) iter.next();
  249. writer.writeBlocks(stream);
  250. }
  251. }
  252. /**
  253. * read in a file and write it back out again
  254. *
  255. * @param args names of the files; arg[ 0 ] is the input file,
  256. * arg[ 1 ] is the output file
  257. *
  258. * @exception IOException
  259. */
  260. public static void main(String args[])
  261. throws IOException
  262. {
  263. if (args.length != 2)
  264. {
  265. System.err.println(
  266. "two arguments required: input filename and output filename");
  267. System.exit(1);
  268. }
  269. FileOutputStream ostream = new FileOutputStream(args[ 1 ]);
  270. RandomAccessInputStream in = new RandomAccessInputStream(args[0]);
  271. new POIFSFileSystem(in, 512).writeFilesystem(ostream);
  272. in.close();
  273. ostream.close();
  274. }
  275. /**
  276. * get the root entry
  277. *
  278. * @return the root entry
  279. */
  280. public DirectoryEntry getRoot()
  281. {
  282. if (_root == null)
  283. {
  284. _root = new DirectoryNode(_property_table.getRoot(), this, null);
  285. }
  286. return _root;
  287. }
  288. /**
  289. * open a document in the root entry's list of entries
  290. *
  291. * @param documentName the name of the document to be opened
  292. *
  293. * @return a newly opened DocumentInputStream
  294. *
  295. * @exception IOException if the document does not exist or the
  296. * name is that of a DirectoryEntry
  297. */
  298. public DocumentInputStream createDocumentInputStream(
  299. final String documentName)
  300. throws IOException
  301. {
  302. Entry document = getRoot().getEntry(documentName);
  303. if (!document.isDocumentEntry())
  304. {
  305. throw new IOException("Entry '" + documentName
  306. + "' is not a DocumentEntry");
  307. }
  308. return new DocumentInputStream(( DocumentEntry ) document, stream);
  309. }
  310. /**
  311. * add a new POIFSDocument
  312. *
  313. * @param document the POIFSDocument being added
  314. */
  315. void addDocument(final POIFSDocument document)
  316. {
  317. _documents.add(document);
  318. _property_table.addProperty(document.getDocumentProperty());
  319. }
  320. /**
  321. * add a new DirectoryProperty
  322. *
  323. * @param directory the DirectoryProperty being added
  324. */
  325. void addDirectory(final DirectoryProperty directory)
  326. {
  327. _property_table.addProperty(directory);
  328. }
  329. /**
  330. * remove an entry
  331. *
  332. * @param entry to be removed
  333. */
  334. void remove(EntryNode entry)
  335. {
  336. _property_table.removeProperty(entry.getProperty());
  337. if (entry.isDocumentEntry())
  338. {
  339. _documents.remove((( DocumentNode ) entry).getDocument());
  340. }
  341. }
  342. private void processProperties(final BlockList small_blocks,
  343. final BlockList big_blocks,
  344. final Iterator properties,
  345. final DirectoryNode dir)
  346. throws IOException
  347. {
  348. while (properties.hasNext())
  349. {
  350. Property property = ( Property ) properties.next();
  351. String name = property.getName();
  352. DirectoryNode parent = (dir == null)
  353. ? (( DirectoryNode ) getRoot())
  354. : dir;
  355. if (property.isDirectory())
  356. {
  357. DirectoryNode new_dir =
  358. ( DirectoryNode ) parent.createDirectory(name);
  359. new_dir.setStorageClsid( property.getStorageClsid() );
  360. processProperties(
  361. small_blocks, big_blocks,
  362. (( DirectoryProperty ) property).getChildren(), new_dir);
  363. }
  364. else
  365. {
  366. int startBlock = property.getStartBlock();
  367. int size = property.getSize();
  368. POIFSDocument document = null;
  369. if (property.shouldUseSmallBlocks())
  370. {
  371. document =
  372. new POIFSDocument(name, small_blocks
  373. .fetchBlocks(startBlock), size, bigBlockSize);
  374. }
  375. else
  376. {
  377. document =
  378. new POIFSDocument(name,
  379. big_blocks.fetchBlocks(startBlock),
  380. size, bigBlockSize);
  381. }
  382. parent.createDocument(document);
  383. }
  384. }
  385. }
  386. /* ********** START begin implementation of POIFSViewable ********** */
  387. /**
  388. * Get an array of objects, some of which may implement
  389. * POIFSViewable
  390. *
  391. * @return an array of Object; may not be null, but may be empty
  392. */
  393. public Object [] getViewableArray()
  394. {
  395. if (preferArray())
  396. {
  397. return (( POIFSViewable ) getRoot()).getViewableArray();
  398. }
  399. else
  400. {
  401. return new Object[ 0 ];
  402. }
  403. }
  404. /**
  405. * Get an Iterator of objects, some of which may implement
  406. * POIFSViewable
  407. *
  408. * @return an Iterator; may not be null, but may have an empty
  409. * back end store
  410. */
  411. public Iterator getViewableIterator()
  412. {
  413. if (!preferArray())
  414. {
  415. return (( POIFSViewable ) getRoot()).getViewableIterator();
  416. }
  417. else
  418. {
  419. return Collections.EMPTY_LIST.iterator();
  420. }
  421. }
  422. /**
  423. * Give viewers a hint as to whether to call getViewableArray or
  424. * getViewableIterator
  425. *
  426. * @return true if a viewer should call getViewableArray, false if
  427. * a viewer should call getViewableIterator
  428. */
  429. public boolean preferArray()
  430. {
  431. return (( POIFSViewable ) getRoot()).preferArray();
  432. }
  433. /**
  434. * Provides a short description of the object, to be used when a
  435. * POIFSViewable object has not provided its contents.
  436. *
  437. * @return short description
  438. */
  439. public String getShortDescription()
  440. {
  441. return "POIFS FileSystem";
  442. }
  443. /* ********** END begin implementation of POIFSViewable ********** */
  444. } // end public class POIFSFileSystem