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

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