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.

VariantSupport.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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.hpsf;
  16. import java.io.IOException;
  17. import java.io.OutputStream;
  18. import java.io.UnsupportedEncodingException;
  19. import java.math.BigInteger;
  20. import java.util.Date;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import org.apache.logging.log4j.LogManager;
  24. import org.apache.logging.log4j.Logger;
  25. import org.apache.poi.util.IOUtils;
  26. import org.apache.poi.util.LittleEndian;
  27. import org.apache.poi.util.LittleEndianByteArrayInputStream;
  28. import org.apache.poi.util.LittleEndianConsts;
  29. /**
  30. * Supports reading and writing of variant data.<p>
  31. *
  32. * <strong>FIXME (3):</strong> Reading and writing should be made more
  33. * uniform than it is now. The following items should be resolved:
  34. *
  35. * <ul>
  36. *
  37. * <li>Reading requires a length parameter that is 4 byte greater than the
  38. * actual data, because the variant type field is included.
  39. *
  40. * <li>Reading reads from a byte array while writing writes to an byte array
  41. * output stream.
  42. *
  43. * </ul>
  44. */
  45. public class VariantSupport extends Variant {
  46. /**
  47. * HPSF is able to read these {@link Variant} types.
  48. */
  49. public static final int[] SUPPORTED_TYPES = { Variant.VT_EMPTY,
  50. Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8,
  51. Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR,
  52. Variant.VT_CF, Variant.VT_BOOL };
  53. private static final Logger LOG = LogManager.getLogger(VariantSupport.class);
  54. //arbitrarily selected; may need to increase
  55. private static final int MAX_RECORD_LENGTH = 100_000;
  56. private static boolean logUnsupportedTypes;
  57. /**
  58. * Keeps a list of the variant types an "unsupported" message has already
  59. * been issued for.
  60. */
  61. private static List<Long> unsupportedMessage;
  62. private static final byte[] paddingBytes = new byte[3];
  63. /**
  64. * Specifies whether warnings about unsupported variant types are to be
  65. * written to {@code System.err} or not.
  66. *
  67. * @param logUnsupportedTypes If {@code true} warnings will be written,
  68. * if {@code false} they won't.
  69. */
  70. public static void setLogUnsupportedTypes(final boolean logUnsupportedTypes) {
  71. VariantSupport.logUnsupportedTypes = logUnsupportedTypes;
  72. }
  73. /**
  74. * Checks whether logging of unsupported variant types warning is turned
  75. * on or off.
  76. *
  77. * @return {@code true} if logging is turned on, else
  78. * {@code false}.
  79. */
  80. public static boolean isLogUnsupportedTypes() {
  81. return logUnsupportedTypes;
  82. }
  83. /**
  84. * Writes a warning to {@code System.err} that a variant type is
  85. * unsupported by HPSF. Such a warning is written only once for each variant
  86. * type. Log messages can be turned on or off by
  87. *
  88. * @param ex The exception to log
  89. */
  90. protected static void writeUnsupportedTypeMessage
  91. (final UnsupportedVariantTypeException ex) {
  92. if (isLogUnsupportedTypes())
  93. {
  94. if (unsupportedMessage == null) {
  95. unsupportedMessage = new LinkedList<>();
  96. }
  97. Long vt = Long.valueOf(ex.getVariantType());
  98. if (!unsupportedMessage.contains(vt))
  99. {
  100. LOG.atError().withThrowable(ex).log("Unsupported type");
  101. unsupportedMessage.add(vt);
  102. }
  103. }
  104. }
  105. /**
  106. * Checks whether HPSF supports the specified variant type. Unsupported
  107. * types should be implemented included in the {@link #SUPPORTED_TYPES}
  108. * array.
  109. *
  110. * @see Variant
  111. * @param variantType the variant type to check
  112. * @return {@code true} if HPFS supports this type, else
  113. * {@code false}
  114. */
  115. public boolean isSupportedType(final int variantType) {
  116. for (int st : SUPPORTED_TYPES) {
  117. if (variantType == st) {
  118. return true;
  119. }
  120. }
  121. return false;
  122. }
  123. /**
  124. * Reads a variant type from a byte array.
  125. *
  126. * @param src The byte array
  127. * @param offset The offset in the byte array where the variant starts
  128. * @param length The length of the variant including the variant type field
  129. * @param type The variant type to read
  130. * @param codepage The codepage to use for non-wide strings
  131. * @return A Java object that corresponds best to the variant field. For
  132. * example, a VT_I4 is returned as a {@link Long}, a VT_LPSTR as a
  133. * {@link String}.
  134. * @exception ReadingNotSupportedException if a property is to be written
  135. * who's variant type HPSF does not yet support
  136. * @exception UnsupportedEncodingException if the specified codepage is not
  137. * supported.
  138. * @see Variant
  139. */
  140. public static Object read( final byte[] src, final int offset,
  141. final int length, final long type, final int codepage )
  142. throws ReadingNotSupportedException, UnsupportedEncodingException {
  143. LittleEndianByteArrayInputStream lei = new LittleEndianByteArrayInputStream(src, offset);
  144. return read( lei, length, type, codepage );
  145. }
  146. public static Object read( LittleEndianByteArrayInputStream lei,
  147. final int length, final long type, final int codepage )
  148. throws ReadingNotSupportedException, UnsupportedEncodingException {
  149. final int offset = lei.getReadIndex();
  150. TypedPropertyValue typedPropertyValue = new TypedPropertyValue( (int) type, null );
  151. try {
  152. typedPropertyValue.readValue(lei);
  153. } catch ( UnsupportedOperationException exc ) {
  154. try {
  155. final byte[] v = IOUtils.toByteArray(lei, length, MAX_RECORD_LENGTH);
  156. throw new ReadingNotSupportedException( type, v );
  157. } catch (IOException e) {
  158. throw new RuntimeException(e);
  159. }
  160. }
  161. switch ( (int) type ) {
  162. /*
  163. * we have more property types that can be converted into Java
  164. * objects, but current API need to be preserved, and it returns
  165. * other types as byte arrays. In future major versions it shall be
  166. * changed -- sergey
  167. */
  168. case Variant.VT_EMPTY:
  169. case Variant.VT_I1:
  170. case Variant.VT_UI1:
  171. case Variant.VT_UI2:
  172. case Variant.VT_I4:
  173. case Variant.VT_UI4:
  174. case Variant.VT_I8:
  175. case Variant.VT_UI8:
  176. case Variant.VT_R4:
  177. case Variant.VT_R8:
  178. return typedPropertyValue.getValue();
  179. /*
  180. * also for backward-compatibility with prev. versions of POI
  181. * --sergey
  182. */
  183. case Variant.VT_I2:
  184. return ( (Short) typedPropertyValue.getValue() ).intValue();
  185. case Variant.VT_FILETIME:
  186. Filetime filetime = (Filetime) typedPropertyValue.getValue();
  187. return filetime.getJavaValue();
  188. case Variant.VT_LPSTR:
  189. CodePageString cpString = (CodePageString) typedPropertyValue.getValue();
  190. return cpString.getJavaValue( codepage );
  191. case Variant.VT_LPWSTR:
  192. UnicodeString uniString = (UnicodeString) typedPropertyValue.getValue();
  193. return uniString.toJavaString();
  194. // if(l1 < 0) {
  195. /*
  196. * YK: reading the ClipboardData packet (VT_CF) is not quite
  197. * correct. The size of the data is determined by the first four
  198. * bytes of the packet while the current implementation calculates
  199. * it in the Section constructor. Test files in Bugzilla 42726 and
  200. * 45583 clearly show that this approach does not always work. The
  201. * workaround below attempts to gracefully handle such cases instead
  202. * of throwing exceptions.
  203. *
  204. * August 20, 2009
  205. */
  206. // l1 = LittleEndian.getInt(src, o1); o1 += LittleEndianConts.INT_SIZE;
  207. // }
  208. // final byte[] v = new byte[l1];
  209. // System.arraycopy(src, o1, v, 0, v.length);
  210. // value = v;
  211. // break;
  212. case Variant.VT_CF:
  213. ClipboardData clipboardData = (ClipboardData) typedPropertyValue.getValue();
  214. return clipboardData.toByteArray();
  215. case Variant.VT_BOOL:
  216. VariantBool bool = (VariantBool) typedPropertyValue.getValue();
  217. return bool.getValue();
  218. /*
  219. * it is not very good, but what can do without breaking current
  220. * API? --sergey
  221. */
  222. default:
  223. final int unpadded = lei.getReadIndex()-offset;
  224. lei.setReadIndex(offset);
  225. final byte[] v = IOUtils.safelyAllocate(unpadded, MAX_RECORD_LENGTH);
  226. lei.readFully( v, 0, unpadded );
  227. throw new ReadingNotSupportedException( type, v );
  228. }
  229. }
  230. /**
  231. * Writes a variant value to an output stream. This method ensures that
  232. * always a multiple of 4 bytes is written.<p>
  233. *
  234. * @param out The stream to write the value to.
  235. * @param type The variant's type.
  236. * @param value The variant's value.
  237. * @param codepage The codepage to use to write non-wide strings
  238. * @return The number of entities that have been written. In many cases an
  239. * "entity" is a byte but this is not always the case.
  240. * @exception IOException if an I/O exceptions occurs
  241. * @exception WritingNotSupportedException if a property is to be written
  242. * who's variant type HPSF does not yet support
  243. */
  244. public static int write(final OutputStream out, final long type,
  245. final Object value, final int codepage)
  246. throws IOException, WritingNotSupportedException {
  247. int length = -1;
  248. switch ((int) type) {
  249. case Variant.VT_BOOL: {
  250. if (value instanceof Boolean) {
  251. int bb = ((Boolean)value) ? 0xff : 0x00;
  252. out.write(bb);
  253. out.write(bb);
  254. length = 2;
  255. }
  256. break;
  257. }
  258. case Variant.VT_LPSTR:
  259. if (value instanceof String) {
  260. CodePageString codePageString = new CodePageString();
  261. codePageString.setJavaValue( (String)value, codepage );
  262. length = codePageString.write( out );
  263. }
  264. break;
  265. case Variant.VT_LPWSTR:
  266. if (value instanceof String) {
  267. UnicodeString uniString = new UnicodeString();
  268. uniString.setJavaValue((String)value);
  269. length = uniString.write(out);
  270. }
  271. break;
  272. case Variant.VT_CF:
  273. if (value instanceof byte[]) {
  274. final byte[] cf = (byte[]) value;
  275. out.write(cf);
  276. length = cf.length;
  277. }
  278. break;
  279. case Variant.VT_EMPTY:
  280. LittleEndian.putUInt(Variant.VT_EMPTY, out);
  281. length = LittleEndianConsts.INT_SIZE;
  282. break;
  283. case Variant.VT_I2:
  284. if (value instanceof Number) {
  285. LittleEndian.putShort( out, ((Number)value).shortValue() );
  286. length = LittleEndianConsts.SHORT_SIZE;
  287. }
  288. break;
  289. case Variant.VT_UI2:
  290. if (value instanceof Number) {
  291. LittleEndian.putUShort( ((Number)value).intValue(), out );
  292. length = LittleEndianConsts.SHORT_SIZE;
  293. }
  294. break;
  295. case Variant.VT_I4:
  296. if (value instanceof Number) {
  297. LittleEndian.putInt( ((Number)value).intValue(), out);
  298. length = LittleEndianConsts.INT_SIZE;
  299. }
  300. break;
  301. case Variant.VT_UI4:
  302. if (value instanceof Number) {
  303. LittleEndian.putUInt( ((Number)value).longValue(), out);
  304. length = LittleEndianConsts.INT_SIZE;
  305. }
  306. break;
  307. case Variant.VT_I8:
  308. if (value instanceof Number) {
  309. LittleEndian.putLong( ((Number)value).longValue(), out);
  310. length = LittleEndianConsts.LONG_SIZE;
  311. }
  312. break;
  313. case Variant.VT_UI8: {
  314. if (value instanceof Number) {
  315. BigInteger bi = (value instanceof BigInteger) ? (BigInteger)value : BigInteger.valueOf(((Number)value).longValue());
  316. if (bi.bitLength() > 64) {
  317. throw new WritingNotSupportedException(type, value);
  318. }
  319. byte[] biBytesBE = bi.toByteArray(), biBytesLE = new byte[LittleEndianConsts.LONG_SIZE];
  320. int i=biBytesBE.length;
  321. for (byte b : biBytesBE) {
  322. if (i<=LittleEndianConsts.LONG_SIZE) {
  323. biBytesLE[i-1] = b;
  324. }
  325. i--;
  326. }
  327. out.write(biBytesLE);
  328. length = LittleEndianConsts.LONG_SIZE;
  329. }
  330. break;
  331. }
  332. case Variant.VT_R4: {
  333. if (value instanceof Number) {
  334. int floatBits = Float.floatToIntBits(((Number)value).floatValue());
  335. LittleEndian.putInt(floatBits, out);
  336. length = LittleEndianConsts.INT_SIZE;
  337. }
  338. break;
  339. }
  340. case Variant.VT_R8:
  341. if (value instanceof Number) {
  342. LittleEndian.putDouble( ((Number)value).doubleValue(), out);
  343. length = LittleEndianConsts.DOUBLE_SIZE;
  344. }
  345. break;
  346. case Variant.VT_FILETIME:
  347. Filetime filetimeValue = (value instanceof Date) ? new Filetime((Date)value) : new Filetime();
  348. length = filetimeValue.write( out );
  349. break;
  350. default:
  351. break;
  352. }
  353. /* The variant type is not supported yet. However, if the value
  354. * is a byte array we can write it nevertheless. */
  355. if (length == -1) {
  356. if (value instanceof byte[]) {
  357. final byte[] b = (byte[]) value;
  358. out.write(b);
  359. length = b.length;
  360. writeUnsupportedTypeMessage(new WritingNotSupportedException(type, value));
  361. } else {
  362. throw new WritingNotSupportedException(type, value);
  363. }
  364. }
  365. /* pad values to 4-bytes */
  366. int padding = (4-(length & 0x3)) & 0x3;
  367. out.write(paddingBytes, 0, padding);
  368. return length + padding;
  369. }
  370. }