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.

Property.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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.poifs.property;
  16. import java.io.*;
  17. import java.util.*;
  18. import org.apache.poi.hpsf.ClassID;
  19. import org.apache.poi.poifs.common.POIFSConstants;
  20. import org.apache.poi.poifs.dev.POIFSViewable;
  21. import org.apache.poi.util.ByteField;
  22. import org.apache.poi.util.IntegerField;
  23. import org.apache.poi.util.LittleEndianConsts;
  24. import org.apache.poi.util.ShortField;
  25. /**
  26. * This abstract base class is the ancestor of all classes
  27. * implementing POIFS Property behavior.
  28. *
  29. * @author Marc Johnson (mjohnson at apache dot org)
  30. */
  31. public abstract class Property implements Child, POIFSViewable {
  32. static final private byte _default_fill = ( byte ) 0x00;
  33. static final private int _name_size_offset = 0x40;
  34. static final private int _max_name_length =
  35. (_name_size_offset / LittleEndianConsts.SHORT_SIZE) - 1;
  36. static final protected int _NO_INDEX = -1;
  37. // useful offsets
  38. static final private int _node_color_offset = 0x43;
  39. static final private int _previous_property_offset = 0x44;
  40. static final private int _next_property_offset = 0x48;
  41. static final private int _child_property_offset = 0x4C;
  42. static final private int _storage_clsid_offset = 0x50;
  43. static final private int _user_flags_offset = 0x60;
  44. static final private int _seconds_1_offset = 0x64;
  45. static final private int _days_1_offset = 0x68;
  46. static final private int _seconds_2_offset = 0x6C;
  47. static final private int _days_2_offset = 0x70;
  48. static final private int _start_block_offset = 0x74;
  49. static final private int _size_offset = 0x78;
  50. // node colors
  51. static final protected byte _NODE_BLACK = 1;
  52. static final protected byte _NODE_RED = 0;
  53. // documents must be at least this size to be stored in big blocks
  54. static final private int _big_block_minimum_bytes = POIFSConstants.BIG_BLOCK_MINIMUM_DOCUMENT_SIZE;
  55. private String _name;
  56. private ShortField _name_size;
  57. private ByteField _property_type;
  58. private ByteField _node_color;
  59. private IntegerField _previous_property;
  60. private IntegerField _next_property;
  61. private IntegerField _child_property;
  62. private ClassID _storage_clsid;
  63. private IntegerField _user_flags;
  64. private IntegerField _seconds_1;
  65. private IntegerField _days_1;
  66. private IntegerField _seconds_2;
  67. private IntegerField _days_2;
  68. private IntegerField _start_block;
  69. private IntegerField _size;
  70. private byte[] _raw_data;
  71. private int _index;
  72. private Child _next_child;
  73. private Child _previous_child;
  74. protected Property()
  75. {
  76. _raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
  77. Arrays.fill(_raw_data, _default_fill);
  78. _name_size = new ShortField(_name_size_offset);
  79. _property_type =
  80. new ByteField(PropertyConstants.PROPERTY_TYPE_OFFSET);
  81. _node_color = new ByteField(_node_color_offset);
  82. _previous_property = new IntegerField(_previous_property_offset,
  83. _NO_INDEX, _raw_data);
  84. _next_property = new IntegerField(_next_property_offset,
  85. _NO_INDEX, _raw_data);
  86. _child_property = new IntegerField(_child_property_offset,
  87. _NO_INDEX, _raw_data);
  88. _storage_clsid = new ClassID(_raw_data,_storage_clsid_offset);
  89. _user_flags = new IntegerField(_user_flags_offset, 0, _raw_data);
  90. _seconds_1 = new IntegerField(_seconds_1_offset, 0,
  91. _raw_data);
  92. _days_1 = new IntegerField(_days_1_offset, 0, _raw_data);
  93. _seconds_2 = new IntegerField(_seconds_2_offset, 0,
  94. _raw_data);
  95. _days_2 = new IntegerField(_days_2_offset, 0, _raw_data);
  96. _start_block = new IntegerField(_start_block_offset);
  97. _size = new IntegerField(_size_offset, 0, _raw_data);
  98. _index = _NO_INDEX;
  99. setName("");
  100. setNextChild(null);
  101. setPreviousChild(null);
  102. }
  103. /**
  104. * Constructor from byte data
  105. *
  106. * @param index index number
  107. * @param array byte data
  108. * @param offset offset into byte data
  109. */
  110. protected Property(int index, byte [] array, int offset)
  111. {
  112. _raw_data = new byte[ POIFSConstants.PROPERTY_SIZE ];
  113. System.arraycopy(array, offset, _raw_data, 0,
  114. POIFSConstants.PROPERTY_SIZE);
  115. _name_size = new ShortField(_name_size_offset, _raw_data);
  116. _property_type =
  117. new ByteField(PropertyConstants.PROPERTY_TYPE_OFFSET, _raw_data);
  118. _node_color = new ByteField(_node_color_offset, _raw_data);
  119. _previous_property = new IntegerField(_previous_property_offset,
  120. _raw_data);
  121. _next_property = new IntegerField(_next_property_offset,
  122. _raw_data);
  123. _child_property = new IntegerField(_child_property_offset,
  124. _raw_data);
  125. _storage_clsid = new ClassID(_raw_data,_storage_clsid_offset);
  126. _user_flags = new IntegerField(_user_flags_offset, 0, _raw_data);
  127. _seconds_1 = new IntegerField(_seconds_1_offset, _raw_data);
  128. _days_1 = new IntegerField(_days_1_offset, _raw_data);
  129. _seconds_2 = new IntegerField(_seconds_2_offset, _raw_data);
  130. _days_2 = new IntegerField(_days_2_offset, _raw_data);
  131. _start_block = new IntegerField(_start_block_offset, _raw_data);
  132. _size = new IntegerField(_size_offset, _raw_data);
  133. _index = index;
  134. int name_length = (_name_size.get() / LittleEndianConsts.SHORT_SIZE)
  135. - 1;
  136. if (name_length < 1)
  137. {
  138. _name = "";
  139. }
  140. else
  141. {
  142. char[] char_array = new char[ name_length ];
  143. int name_offset = 0;
  144. for (int j = 0; j < name_length; j++)
  145. {
  146. char_array[ j ] = ( char ) new ShortField(name_offset,
  147. _raw_data).get();
  148. name_offset += LittleEndianConsts.SHORT_SIZE;
  149. }
  150. _name = new String(char_array, 0, name_length);
  151. }
  152. _next_child = null;
  153. _previous_child = null;
  154. }
  155. /**
  156. * Write the raw data to an OutputStream.
  157. *
  158. * @param stream the OutputStream to which the data should be
  159. * written.
  160. *
  161. * @exception IOException on problems writing to the specified
  162. * stream.
  163. */
  164. public void writeData(OutputStream stream)
  165. throws IOException
  166. {
  167. stream.write(_raw_data);
  168. }
  169. /**
  170. * Set the start block for the document referred to by this
  171. * Property.
  172. *
  173. * @param startBlock the start block index
  174. */
  175. public void setStartBlock(int startBlock)
  176. {
  177. _start_block.set(startBlock, _raw_data);
  178. }
  179. /**
  180. * @return the start block
  181. */
  182. public int getStartBlock()
  183. {
  184. return _start_block.get();
  185. }
  186. /**
  187. * find out the document size
  188. *
  189. * @return size in bytes
  190. */
  191. public int getSize()
  192. {
  193. return _size.get();
  194. }
  195. /**
  196. * Based on the currently defined size, should this property use
  197. * small blocks?
  198. *
  199. * @return true if the size is less than _big_block_minimum_bytes
  200. */
  201. public boolean shouldUseSmallBlocks()
  202. {
  203. return Property.isSmall(_size.get());
  204. }
  205. /**
  206. * does the length indicate a small document?
  207. *
  208. * @param length length in bytes
  209. *
  210. * @return true if the length is less than
  211. * _big_block_minimum_bytes
  212. */
  213. public static boolean isSmall(int length)
  214. {
  215. return length < _big_block_minimum_bytes;
  216. }
  217. /**
  218. * Get the name of this property
  219. *
  220. * @return property name as String
  221. */
  222. public String getName()
  223. {
  224. return _name;
  225. }
  226. /**
  227. * @return true if a directory type Property
  228. */
  229. abstract public boolean isDirectory();
  230. /**
  231. * Sets the storage clsid, which is the Class ID of a COM object which
  232. * reads and writes this stream
  233. * @return storage Class ID for this property stream
  234. */
  235. public ClassID getStorageClsid()
  236. {
  237. return _storage_clsid;
  238. }
  239. /**
  240. * Set the name; silently truncates the name if it's too long.
  241. *
  242. * @param name the new name
  243. */
  244. protected void setName(String name)
  245. {
  246. char[] char_array = name.toCharArray();
  247. int limit = Math.min(char_array.length, _max_name_length);
  248. _name = new String(char_array, 0, limit);
  249. short offset = 0;
  250. int j = 0;
  251. for (; j < limit; j++)
  252. {
  253. new ShortField(offset, ( short ) char_array[ j ], _raw_data);
  254. offset += LittleEndianConsts.SHORT_SIZE;
  255. }
  256. for (; j < _max_name_length + 1; j++)
  257. {
  258. new ShortField(offset, ( short ) 0, _raw_data);
  259. offset += LittleEndianConsts.SHORT_SIZE;
  260. }
  261. // double the count, and include the null at the end
  262. _name_size
  263. .set(( short ) ((limit + 1)
  264. * LittleEndianConsts.SHORT_SIZE), _raw_data);
  265. }
  266. /**
  267. * Sets the storage class ID for this property stream. This is the Class ID
  268. * of the COM object which can read and write this property stream
  269. * @param clsidStorage Storage Class ID
  270. */
  271. public void setStorageClsid( ClassID clsidStorage)
  272. {
  273. _storage_clsid = clsidStorage;
  274. if( clsidStorage == null) {
  275. Arrays.fill( _raw_data, _storage_clsid_offset, _storage_clsid_offset + ClassID.LENGTH, (byte) 0);
  276. } else {
  277. clsidStorage.write( _raw_data, _storage_clsid_offset);
  278. }
  279. }
  280. /**
  281. * Set the property type. Makes no attempt to validate the value.
  282. *
  283. * @param propertyType the property type (root, file, directory)
  284. */
  285. protected void setPropertyType(byte propertyType)
  286. {
  287. _property_type.set(propertyType, _raw_data);
  288. }
  289. /**
  290. * Set the node color.
  291. *
  292. * @param nodeColor the node color (red or black)
  293. */
  294. protected void setNodeColor(byte nodeColor)
  295. {
  296. _node_color.set(nodeColor, _raw_data);
  297. }
  298. /**
  299. * Set the child property.
  300. *
  301. * @param child the child property's index in the Property Table
  302. */
  303. protected void setChildProperty(int child)
  304. {
  305. _child_property.set(child, _raw_data);
  306. }
  307. /**
  308. * Get the child property (its index in the Property Table)
  309. *
  310. * @return child property index
  311. */
  312. protected int getChildIndex()
  313. {
  314. return _child_property.get();
  315. }
  316. /**
  317. * Set the size of the document associated with this Property
  318. *
  319. * @param size the size of the document, in bytes
  320. */
  321. protected void setSize(int size)
  322. {
  323. _size.set(size, _raw_data);
  324. }
  325. /**
  326. * Set the index for this Property
  327. *
  328. * @param index this Property's index within its containing
  329. * Property Table
  330. */
  331. protected void setIndex(int index)
  332. {
  333. _index = index;
  334. }
  335. /**
  336. * get the index for this Property
  337. *
  338. * @return the index of this Property within its Property Table
  339. */
  340. protected int getIndex()
  341. {
  342. return _index;
  343. }
  344. /**
  345. * Perform whatever activities need to be performed prior to
  346. * writing
  347. */
  348. abstract protected void preWrite();
  349. /**
  350. * get the next sibling
  351. *
  352. * @return index of next sibling
  353. */
  354. int getNextChildIndex()
  355. {
  356. return _next_property.get();
  357. }
  358. /**
  359. * get the previous sibling
  360. *
  361. * @return index of previous sibling
  362. */
  363. int getPreviousChildIndex()
  364. {
  365. return _previous_property.get();
  366. }
  367. /**
  368. * determine whether the specified index is valid
  369. *
  370. * @param index value to be checked
  371. *
  372. * @return true if the index is valid
  373. */
  374. static boolean isValidIndex(int index)
  375. {
  376. return index != _NO_INDEX;
  377. }
  378. /**
  379. * Get the next Child, if any
  380. *
  381. * @return the next Child; may return null
  382. */
  383. public Child getNextChild()
  384. {
  385. return _next_child;
  386. }
  387. /**
  388. * Get the previous Child, if any
  389. *
  390. * @return the previous Child; may return null
  391. */
  392. public Child getPreviousChild()
  393. {
  394. return _previous_child;
  395. }
  396. /**
  397. * Set the next Child
  398. *
  399. * @param child the new 'next' child; may be null, which has the
  400. * effect of saying there is no 'next' child
  401. */
  402. public void setNextChild(Child child)
  403. {
  404. _next_child = child;
  405. _next_property.set((child == null) ? _NO_INDEX
  406. : (( Property ) child)
  407. .getIndex(), _raw_data);
  408. }
  409. /**
  410. * Set the previous Child
  411. *
  412. * @param child the new 'previous' child; may be null, which has
  413. * the effect of saying there is no 'previous' child
  414. */
  415. public void setPreviousChild(Child child)
  416. {
  417. _previous_child = child;
  418. _previous_property.set((child == null) ? _NO_INDEX
  419. : (( Property ) child)
  420. .getIndex(), _raw_data);
  421. }
  422. /**
  423. * Get an array of objects, some of which may implement
  424. * POIFSViewable
  425. *
  426. * @return an array of Object; may not be null, but may be empty
  427. */
  428. public Object [] getViewableArray()
  429. {
  430. Object[] results = new Object[ 5 ];
  431. results[ 0 ] = "Name = \"" + getName() + "\"";
  432. results[ 1 ] = "Property Type = " + _property_type.get();
  433. results[ 2 ] = "Node Color = " + _node_color.get();
  434. long time = _days_1.get();
  435. time <<= 32;
  436. time += _seconds_1.get() & 0x0000FFFFL;
  437. results[ 3 ] = "Time 1 = " + time;
  438. time = _days_2.get();
  439. time <<= 32;
  440. time += _seconds_2.get() & 0x0000FFFFL;
  441. results[ 4 ] = "Time 2 = " + time;
  442. return results;
  443. }
  444. /**
  445. * Get an Iterator of objects, some of which may implement
  446. * POIFSViewable
  447. *
  448. * @return an Iterator; may not be null, but may have an empty
  449. * back end store
  450. */
  451. public Iterator getViewableIterator()
  452. {
  453. return Collections.EMPTY_LIST.iterator();
  454. }
  455. /**
  456. * Give viewers a hint as to whether to call getViewableArray or
  457. * getViewableIterator
  458. *
  459. * @return true if a viewer should call getViewableArray, false if
  460. * a viewer should call getViewableIterator
  461. */
  462. public boolean preferArray()
  463. {
  464. return true;
  465. }
  466. /**
  467. * Provides a short description of the object, to be used when a
  468. * POIFSViewable object has not provided its contents.
  469. *
  470. * @return short description
  471. */
  472. public String getShortDescription()
  473. {
  474. StringBuffer buffer = new StringBuffer();
  475. buffer.append("Property: \"").append(getName()).append("\"");
  476. return buffer.toString();
  477. }
  478. }