PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/components/forks/poi/src/loci/poi/hpsf/Property.java

http://github.com/openmicroscopy/bioformats
Java | 449 lines | 215 code | 59 blank | 175 comment | 47 complexity | 8fa7785e3864553983ffce8b9a33e916 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.hpsf;
  38. import java.io.UnsupportedEncodingException;
  39. import java.util.HashMap;
  40. import java.util.Map;
  41. import loci.poi.util.HexDump;
  42. import loci.poi.util.LittleEndian;
  43. /**
  44. * <p>A property in a {@link Section} of a {@link PropertySet}.</p>
  45. *
  46. * <p>The property's <strong>ID</strong> gives the property a meaning
  47. * in the context of its {@link Section}. Each {@link Section} spans
  48. * its own name space of property IDs.</p>
  49. *
  50. * <p>The property's <strong>type</strong> determines how its
  51. * <strong>value </strong> is interpreted. For example, if the type is
  52. * {@link Variant#VT_LPSTR} (byte string), the value consists of a
  53. * DWord telling how many bytes the string contains. The bytes follow
  54. * immediately, including any null bytes that terminate the
  55. * string. The type {@link Variant#VT_I4} denotes a four-byte integer
  56. * value, {@link Variant#VT_FILETIME} some date and time (of a
  57. * file).</p>
  58. *
  59. * <p>Please note that not all {@link Variant} types yet. This might change
  60. * over time but largely depends on your feedback so that the POI team knows
  61. * which variant types are really needed. So please feel free to submit error
  62. * reports or patches for the types you need.</p>
  63. *
  64. * <p>Microsoft documentation: <a
  65. * href="http://msdn.microsoft.com/library/en-us/stg/stg/property_set_display_name_dictionary.asp?frame=true">
  66. * Property Set Display Name Dictionary</a>.
  67. *
  68. * @author Rainer Klute <a
  69. * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
  70. * @author Drew Varner (Drew.Varner InAndAround sc.edu)
  71. * @see Section
  72. * @see Variant
  73. * @version $Id: Property.java 489730 2006-12-22 19:18:16Z bayard $
  74. * @since 2002-02-09
  75. */
  76. public class Property
  77. {
  78. /** <p>The property's ID.</p> */
  79. protected long id;
  80. /**
  81. * <p>Returns the property's ID.</p>
  82. *
  83. * @return The ID value
  84. */
  85. public long getID()
  86. {
  87. return id;
  88. }
  89. /** <p>The property's type.</p> */
  90. protected long type;
  91. /**
  92. * <p>Returns the property's type.</p>
  93. *
  94. * @return The type value
  95. */
  96. public long getType()
  97. {
  98. return type;
  99. }
  100. /** <p>The property's value.</p> */
  101. protected Object value;
  102. /**
  103. * <p>Returns the property's value.</p>
  104. *
  105. * @return The property's value
  106. */
  107. public Object getValue()
  108. {
  109. return value;
  110. }
  111. /**
  112. * <p>Creates a property.</p>
  113. *
  114. * @param id the property's ID.
  115. * @param type the property's type, see {@link Variant}.
  116. * @param value the property's value. Only certain types are allowed, see {@link Variant}.
  117. */
  118. public Property(final long id, final long type, final Object value)
  119. {
  120. this.id = id;
  121. this.type = type;
  122. this.value = value;
  123. }
  124. /**
  125. * <p>Creates a {@link Property} instance by reading its bytes
  126. * from the property set stream.</p>
  127. *
  128. * @param id The property's ID.
  129. * @param src The bytes the property set stream consists of.
  130. * @param offset The property's type/value pair's offset in the
  131. * section.
  132. * @param length The property's type/value pair's length in bytes.
  133. * @param codepage The section's and thus the property's
  134. * codepage. It is needed only when reading string values.
  135. * @exception UnsupportedEncodingException if the specified codepage is not
  136. * supported.
  137. */
  138. public Property(final long id, final byte[] src, final long offset,
  139. final int length, final int codepage)
  140. throws UnsupportedEncodingException
  141. {
  142. this.id = id;
  143. /*
  144. * ID 0 is a special case since it specifies a dictionary of
  145. * property IDs and property names.
  146. */
  147. if (id == 0)
  148. {
  149. value = readDictionary(src, offset, length, codepage);
  150. return;
  151. }
  152. int o = (int) offset;
  153. type = LittleEndian.getUInt(src, o);
  154. o += LittleEndian.INT_SIZE;
  155. try
  156. {
  157. value = VariantSupport.read(src, o, length, (int) type, codepage);
  158. }
  159. catch (UnsupportedVariantTypeException ex)
  160. {
  161. VariantSupport.writeUnsupportedTypeMessage(ex);
  162. value = ex.getValue();
  163. }
  164. }
  165. /**
  166. * <p>Creates an empty property. It must be filled using the set method to
  167. * be usable.</p>
  168. */
  169. protected Property()
  170. { }
  171. /**
  172. * <p>Reads a dictionary.</p>
  173. *
  174. * @param src The byte array containing the bytes making out the dictionary.
  175. * @param offset At this offset within <var>src </var> the dictionary
  176. * starts.
  177. * @param length The dictionary contains at most this many bytes.
  178. * @param codepage The codepage of the string values.
  179. * @return The dictonary
  180. * @throws UnsupportedEncodingException if the dictionary's codepage is not
  181. * (yet) supported.
  182. */
  183. protected Map readDictionary(final byte[] src, final long offset,
  184. final int length, final int codepage)
  185. throws UnsupportedEncodingException
  186. {
  187. /* Check whether "offset" points into the "src" array". */
  188. if (offset < 0 || offset > src.length)
  189. throw new HPSFRuntimeException
  190. ("Illegal offset " + offset + " while HPSF stream contains " +
  191. length + " bytes.");
  192. int o = (int) offset;
  193. /*
  194. * Read the number of dictionary entries.
  195. */
  196. final long nrEntries = LittleEndian.getUInt(src, o);
  197. o += LittleEndian.INT_SIZE;
  198. final Map m = new HashMap((int) nrEntries, (float) 1.0);
  199. for (int i = 0; i < nrEntries; i++)
  200. {
  201. /* The key. */
  202. final Long id = new Long(LittleEndian.getUInt(src, o));
  203. o += LittleEndian.INT_SIZE;
  204. /* The value (a string). The length is the either the
  205. * number of (two-byte) characters if the character set is Unicode
  206. * or the number of bytes if the character set is not Unicode.
  207. * The length includes terminating 0x00 bytes which we have to strip
  208. * off to create a Java string. */
  209. long sLength = LittleEndian.getUInt(src, o);
  210. o += LittleEndian.INT_SIZE;
  211. /* Read the string. */
  212. final StringBuffer b = new StringBuffer();
  213. switch (codepage)
  214. {
  215. case -1:
  216. {
  217. /* Without a codepage the length is equal to the number of
  218. * bytes. */
  219. b.append(new String(src, o, (int) sLength));
  220. break;
  221. }
  222. case Constants.CP_UNICODE:
  223. {
  224. /* The length is the number of characters, i.e. the number
  225. * of bytes is twice the number of the characters. */
  226. final int nrBytes = (int) (sLength * 2);
  227. final byte[] h = new byte[nrBytes];
  228. for (int i2 = 0; i2 < nrBytes; i2 += 2)
  229. {
  230. h[i2] = src[o + i2 + 1];
  231. h[i2 + 1] = src[o + i2];
  232. }
  233. b.append(new String(h, 0, nrBytes,
  234. VariantSupport.codepageToEncoding(codepage)));
  235. break;
  236. }
  237. default:
  238. {
  239. /* For encodings other than Unicode the length is the number
  240. * of bytes. */
  241. b.append(new String(src, o, (int) sLength,
  242. VariantSupport.codepageToEncoding(codepage)));
  243. break;
  244. }
  245. }
  246. /* Strip 0x00 characters from the end of the string: */
  247. while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00)
  248. b.setLength(b.length() - 1);
  249. if (codepage == Constants.CP_UNICODE)
  250. {
  251. if (sLength % 2 == 1)
  252. sLength++;
  253. o += (sLength + sLength);
  254. }
  255. else
  256. o += sLength;
  257. m.put(id, b.toString());
  258. }
  259. return m;
  260. }
  261. /**
  262. * <p>Returns the property's size in bytes. This is always a multiple of
  263. * 4.</p>
  264. *
  265. * @return the property's size in bytes
  266. *
  267. * @exception WritingNotSupportedException if HPSF does not yet support the
  268. * property's variant type.
  269. */
  270. protected int getSize() throws WritingNotSupportedException
  271. {
  272. int length = VariantSupport.getVariantLength(type);
  273. if (length >= 0)
  274. return length; /* Fixed length */
  275. if (length == -2)
  276. /* Unknown length */
  277. throw new WritingNotSupportedException(type, null);
  278. /* Variable length: */
  279. final int PADDING = 4; /* Pad to multiples of 4. */
  280. switch ((int) type)
  281. {
  282. case Variant.VT_LPSTR:
  283. {
  284. int l = ((String) value).length() + 1;
  285. int r = l % PADDING;
  286. if (r > 0)
  287. l += PADDING - r;
  288. length += l;
  289. break;
  290. }
  291. case Variant.VT_EMPTY:
  292. break;
  293. default:
  294. throw new WritingNotSupportedException(type, value);
  295. }
  296. return length;
  297. }
  298. /**
  299. * <p>Compares two properties.</p>
  300. *
  301. * <p>Please beware that a property with ID == 0 is a special case: It does not have a type, and its value is the section's
  302. * dictionary. Another special case are strings: Two properties may have
  303. * the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;</p>
  304. *
  305. * @see Object#equals(java.lang.Object)
  306. */
  307. public boolean equals(final Object o)
  308. {
  309. if (!(o instanceof Property))
  310. return false;
  311. final Property p = (Property) o;
  312. final Object pValue = p.getValue();
  313. final long pId = p.getID();
  314. if (id != pId || (id != 0 && !typesAreEqual(type, p.getType())))
  315. return false;
  316. if (value == null && pValue == null)
  317. return true;
  318. if (value == null || pValue == null)
  319. return false;
  320. /* It's clear now that both values are non-null. */
  321. final Class valueClass = value.getClass();
  322. final Class pValueClass = pValue.getClass();
  323. if (!(valueClass.isAssignableFrom(pValueClass)) &&
  324. !(pValueClass.isAssignableFrom(valueClass)))
  325. return false;
  326. if (value instanceof byte[])
  327. return Util.equal((byte[]) value, (byte[]) pValue);
  328. return value.equals(pValue);
  329. }
  330. private boolean typesAreEqual(final long t1, final long t2)
  331. {
  332. if (t1 == t2 ||
  333. (t1 == Variant.VT_LPSTR && t2 == Variant.VT_LPWSTR) ||
  334. (t2 == Variant.VT_LPSTR && t1 == Variant.VT_LPWSTR))
  335. return true;
  336. else
  337. return false;
  338. }
  339. /**
  340. * @see Object#hashCode()
  341. */
  342. public int hashCode()
  343. {
  344. long hashCode = 0;
  345. hashCode += id;
  346. hashCode += type;
  347. if (value != null)
  348. hashCode += value.hashCode();
  349. final int returnHashCode = (int) (hashCode & 0x0ffffffffL );
  350. return returnHashCode;
  351. }
  352. /**
  353. * @see Object#toString()
  354. */
  355. public String toString()
  356. {
  357. final StringBuffer b = new StringBuffer();
  358. b.append(getClass().getName());
  359. b.append('[');
  360. b.append("id: ");
  361. b.append(getID());
  362. b.append(", type: ");
  363. b.append(getType());
  364. final Object value = getValue();
  365. b.append(", value: ");
  366. b.append(value.toString());
  367. if (value instanceof String)
  368. {
  369. final String s = (String) value;
  370. final int l = s.length();
  371. final byte[] bytes = new byte[l * 2];
  372. for (int i = 0; i < l; i++)
  373. {
  374. final char c = s.charAt(i);
  375. final byte high = (byte) ((c & 0x00ff00) >> 8);
  376. final byte low = (byte) ((c & 0x0000ff) >> 0);
  377. bytes[i * 2] = high;
  378. bytes[i * 2 + 1] = low;
  379. }
  380. final String hex = HexDump.dump(bytes, 0L, 0);
  381. b.append(" [");
  382. b.append(hex);
  383. b.append("]");
  384. }
  385. b.append(']');
  386. return b.toString();
  387. }
  388. }