You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZipEntry.java 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /*
  2. * Copyright 2001-2005 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.zip;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.util.Vector;
  21. import java.util.zip.ZipException;
  22. /**
  23. * Extension that adds better handling of extra fields and provides
  24. * access to the internal and external file attributes.
  25. *
  26. */
  27. public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
  28. private static final int PLATFORM_UNIX = 3;
  29. private static final int PLATFORM_FAT = 0;
  30. private int internalAttributes = 0;
  31. private int platform = PLATFORM_FAT;
  32. private long externalAttributes = 0;
  33. private Vector extraFields = new Vector();
  34. private String name = null;
  35. /**
  36. * Creates a new zip entry with the specified name.
  37. * @param name the name of the entry
  38. * @since 1.1
  39. */
  40. public ZipEntry(String name) {
  41. super(name);
  42. }
  43. /**
  44. * Creates a new zip entry with fields taken from the specified zip entry.
  45. * @param entry the entry to get fields from
  46. * @since 1.1
  47. * @throws ZipException on error
  48. */
  49. public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
  50. /*
  51. * REVISIT: call super(entry) instead of this stuff in Ant2,
  52. * "copy constructor" has not been available in JDK 1.1
  53. */
  54. super(entry.getName());
  55. setComment(entry.getComment());
  56. setMethod(entry.getMethod());
  57. setTime(entry.getTime());
  58. long size = entry.getSize();
  59. if (size > 0) {
  60. setSize(size);
  61. }
  62. long cSize = entry.getCompressedSize();
  63. if (cSize > 0) {
  64. setComprSize(cSize);
  65. }
  66. long crc = entry.getCrc();
  67. if (crc > 0) {
  68. setCrc(crc);
  69. }
  70. byte[] extra = entry.getExtra();
  71. if (extra != null) {
  72. setExtraFields(ExtraFieldUtils.parse(extra));
  73. } else {
  74. // initializes extra data to an empty byte array
  75. setExtra();
  76. }
  77. }
  78. /**
  79. * Creates a new zip entry with fields taken from the specified zip entry.
  80. * @param entry the entry to get fields from
  81. * @throws ZipException on error
  82. * @since 1.1
  83. */
  84. public ZipEntry(ZipEntry entry) throws ZipException {
  85. this((java.util.zip.ZipEntry) entry);
  86. setInternalAttributes(entry.getInternalAttributes());
  87. setExternalAttributes(entry.getExternalAttributes());
  88. setExtraFields(entry.getExtraFields());
  89. }
  90. /**
  91. * @since 1.9
  92. */
  93. protected ZipEntry() {
  94. super("");
  95. }
  96. /**
  97. * Overwrite clone.
  98. * @return a cloned copy of this ZipEntry
  99. * @since 1.1
  100. */
  101. public Object clone() {
  102. try {
  103. ZipEntry e = (ZipEntry) super.clone();
  104. e.setName(getName());
  105. e.setComment(getComment());
  106. e.setMethod(getMethod());
  107. e.setTime(getTime());
  108. long size = getSize();
  109. if (size > 0) {
  110. e.setSize(size);
  111. }
  112. long cSize = getCompressedSize();
  113. if (cSize > 0) {
  114. e.setComprSize(cSize);
  115. }
  116. long crc = getCrc();
  117. if (crc > 0) {
  118. e.setCrc(crc);
  119. }
  120. e.extraFields = (Vector) extraFields.clone();
  121. e.setInternalAttributes(getInternalAttributes());
  122. e.setExternalAttributes(getExternalAttributes());
  123. e.setExtraFields(getExtraFields());
  124. return e;
  125. } catch (Throwable t) {
  126. // in JDK 1.1 ZipEntry is not Cloneable, so super.clone declares
  127. // to throw CloneNotSupported - since JDK 1.2 it is overridden to
  128. // not throw that exception
  129. return null;
  130. }
  131. }
  132. /**
  133. * Retrieves the internal file attributes.
  134. * @return the internal file attributes
  135. * @since 1.1
  136. */
  137. public int getInternalAttributes() {
  138. return internalAttributes;
  139. }
  140. /**
  141. * Sets the internal file attributes.
  142. * @param value an <code>int</code> value
  143. * @since 1.1
  144. */
  145. public void setInternalAttributes(int value) {
  146. internalAttributes = value;
  147. }
  148. /**
  149. * Retrieves the external file attributes.
  150. * @return the external file attributes
  151. * @since 1.1
  152. */
  153. public long getExternalAttributes() {
  154. return externalAttributes;
  155. }
  156. /**
  157. * Sets the external file attributes.
  158. * @param value an <code>long</code> value
  159. * @since 1.1
  160. */
  161. public void setExternalAttributes(long value) {
  162. externalAttributes = value;
  163. }
  164. /**
  165. * Sets Unix permissions in a way that is understood by Info-Zip's
  166. * unzip command.
  167. * @param mode an <code>int</code> value
  168. * @since Ant 1.5.2
  169. */
  170. public void setUnixMode(int mode) {
  171. setExternalAttributes((mode << 16)
  172. // MS-DOS read-only attribute
  173. | ((mode & 0200) == 0 ? 1 : 0)
  174. // MS-DOS directory flag
  175. | (isDirectory() ? 0x10 : 0));
  176. platform = PLATFORM_UNIX;
  177. }
  178. /**
  179. * Unix permission.
  180. * @return the unix permissions
  181. * @since Ant 1.6
  182. */
  183. public int getUnixMode() {
  184. return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
  185. }
  186. /**
  187. * Platform specification to put into the &quot;version made
  188. * by&quot; part of the central file header.
  189. *
  190. * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode}
  191. * has been called, in which case 3 (Unix) will be returned.
  192. *
  193. * @since Ant 1.5.2
  194. */
  195. public int getPlatform() {
  196. return platform;
  197. }
  198. /**
  199. * Set the platform (UNIX or FAT).
  200. * @param platform an <code>int</code> value - 0 is FAT, 3 is UNIX
  201. * @since 1.9
  202. */
  203. protected void setPlatform(int platform) {
  204. this.platform = platform;
  205. }
  206. /**
  207. * Replaces all currently attached extra fields with the new array.
  208. * @param fields an array of extra fields
  209. * @since 1.1
  210. */
  211. public void setExtraFields(ZipExtraField[] fields) {
  212. extraFields.removeAllElements();
  213. for (int i = 0; i < fields.length; i++) {
  214. extraFields.addElement(fields[i]);
  215. }
  216. setExtra();
  217. }
  218. /**
  219. * Retrieves extra fields.
  220. * @return an array of the extra fields
  221. * @since 1.1
  222. */
  223. public ZipExtraField[] getExtraFields() {
  224. ZipExtraField[] result = new ZipExtraField[extraFields.size()];
  225. extraFields.copyInto(result);
  226. return result;
  227. }
  228. /**
  229. * Adds an extra fields - replacing an already present extra field
  230. * of the same type.
  231. * @param ze an extra field
  232. * @since 1.1
  233. */
  234. public void addExtraField(ZipExtraField ze) {
  235. ZipShort type = ze.getHeaderId();
  236. boolean done = false;
  237. for (int i = 0, fieldsSize = extraFields.size(); !done && i < fieldsSize; i++) {
  238. if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
  239. extraFields.setElementAt(ze, i);
  240. done = true;
  241. }
  242. }
  243. if (!done) {
  244. extraFields.addElement(ze);
  245. }
  246. setExtra();
  247. }
  248. /**
  249. * Remove an extra fields.
  250. * @param type the type of extra field to remove
  251. * @since 1.1
  252. */
  253. public void removeExtraField(ZipShort type) {
  254. boolean done = false;
  255. for (int i = 0, fieldsSize = extraFields.size(); !done && i < fieldsSize; i++) {
  256. if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
  257. extraFields.removeElementAt(i);
  258. done = true;
  259. }
  260. }
  261. if (!done) {
  262. throw new java.util.NoSuchElementException();
  263. }
  264. setExtra();
  265. }
  266. /**
  267. * Throws an Exception if extra data cannot be parsed into extra fields.
  268. * @param extra an array of bytes to be parsed into extra fields
  269. * @throws RuntimeException if the bytes cannot be parsed
  270. * @since 1.1
  271. * @throws RuntimeException on error
  272. */
  273. public void setExtra(byte[] extra) throws RuntimeException {
  274. try {
  275. setExtraFields(ExtraFieldUtils.parse(extra));
  276. } catch (Exception e) {
  277. throw new RuntimeException(e.getMessage());
  278. }
  279. }
  280. /**
  281. * Unfortunately {@link java.util.zip.ZipOutputStream
  282. * java.util.zip.ZipOutputStream} seems to access the extra data
  283. * directly, so overriding getExtra doesn't help - we need to
  284. * modify super's data directly.
  285. *
  286. * @since 1.1
  287. */
  288. protected void setExtra() {
  289. super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
  290. }
  291. /**
  292. * Retrieves the extra data for the local file data.
  293. * @return the extra data for local file
  294. * @since 1.1
  295. */
  296. public byte[] getLocalFileDataExtra() {
  297. byte[] extra = getExtra();
  298. return extra != null ? extra : new byte[0];
  299. }
  300. /**
  301. * Retrieves the extra data for the central directory.
  302. * @return the central directory extra data
  303. * @since 1.1
  304. */
  305. public byte[] getCentralDirectoryExtra() {
  306. return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
  307. }
  308. /**
  309. * Helper for JDK 1.1 <-> 1.2 incompatibility.
  310. *
  311. * @since 1.2
  312. */
  313. private Long compressedSize = null;
  314. /**
  315. * Make this class work in JDK 1.1 like a 1.2 class.
  316. *
  317. * <p>This either stores the size for later usage or invokes
  318. * setCompressedSize via reflection.</p>
  319. * @param size the size to use
  320. * @since 1.2
  321. */
  322. public void setComprSize(long size) {
  323. if (haveSetCompressedSize()) {
  324. performSetCompressedSize(this, size);
  325. } else {
  326. compressedSize = new Long(size);
  327. }
  328. }
  329. /**
  330. * Override to make this class work in JDK 1.1 like a 1.2 class.
  331. * @return the compressed size
  332. * @since 1.2
  333. */
  334. public long getCompressedSize() {
  335. if (compressedSize != null) {
  336. // has been set explicitly and we are running in a 1.1 VM
  337. return compressedSize.longValue();
  338. }
  339. return super.getCompressedSize();
  340. }
  341. /**
  342. * Get the name of the entry.
  343. * @return the entry name
  344. * @since 1.9
  345. */
  346. public String getName() {
  347. return name == null ? super.getName() : name;
  348. }
  349. /**
  350. * Is this entry a directory?
  351. * @return true if the entry is a directory
  352. * @since 1.10
  353. */
  354. public boolean isDirectory() {
  355. return getName().endsWith("/");
  356. }
  357. /**
  358. * Set the name of the entry.
  359. * @param name the name to use
  360. */
  361. protected void setName(String name) {
  362. this.name = name;
  363. }
  364. /**
  365. * Get the hashCode of the entry.
  366. * This uses the name as the hashcode.
  367. * @return a hashcode.
  368. * @since Ant 1.7
  369. */
  370. public int hashCode() {
  371. // this method has severe consequences on performance. We cannot rely
  372. // on the super.hashCode() method since super.getName() always return
  373. // the empty string in the current implemention (there's no setter)
  374. // so it is basically draining the performance of a hashmap lookup
  375. return getName().hashCode();
  376. }
  377. /**
  378. * The equality method. In this case, the implementation returns 'this == o'
  379. * which is basically the equals method of the Object class.
  380. * @param o the object to compare to
  381. * @return true if this object has the same name as <code>o</code>
  382. * @since Ant 1.7
  383. */
  384. public boolean equals(Object o) {
  385. return (this == o);
  386. }
  387. /**
  388. * Helper for JDK 1.1
  389. *
  390. * @since 1.2
  391. */
  392. private static Method setCompressedSizeMethod = null;
  393. /**
  394. * Helper for JDK 1.1
  395. *
  396. * @since 1.2
  397. */
  398. private static Object lockReflection = new Object();
  399. /**
  400. * Helper for JDK 1.1
  401. *
  402. * @since 1.2
  403. */
  404. private static boolean triedToGetMethod = false;
  405. /**
  406. * Are we running JDK 1.2 or higher?
  407. *
  408. * @since 1.2
  409. */
  410. private static boolean haveSetCompressedSize() {
  411. checkSCS();
  412. return setCompressedSizeMethod != null;
  413. }
  414. /**
  415. * Invoke setCompressedSize via reflection.
  416. *
  417. * @since 1.2
  418. */
  419. private static void performSetCompressedSize(ZipEntry ze, long size) {
  420. Long[] s = {new Long(size)};
  421. try {
  422. setCompressedSizeMethod.invoke(ze, (Object[]) s);
  423. } catch (InvocationTargetException ite) {
  424. Throwable nested = ite.getTargetException();
  425. String msg = getDisplayableMessage(nested);
  426. if (msg == null) {
  427. msg = getDisplayableMessage(ite);
  428. }
  429. if (nested != null) {
  430. nested.printStackTrace();
  431. } else {
  432. ite.printStackTrace();
  433. }
  434. throw new RuntimeException("InvocationTargetException setting the "
  435. + "compressed size of " + ze + ": "
  436. + msg);
  437. } catch (Exception other) {
  438. throw new RuntimeException("Exception setting the compressed size "
  439. + "of " + ze + ": "
  440. + getDisplayableMessage(other));
  441. }
  442. }
  443. /**
  444. * Try to get a handle to the setCompressedSize method.
  445. *
  446. * @since 1.2
  447. */
  448. private static void checkSCS() {
  449. if (!triedToGetMethod) {
  450. synchronized (lockReflection) {
  451. triedToGetMethod = true;
  452. try {
  453. setCompressedSizeMethod =
  454. java.util.zip.ZipEntry.class.getMethod("setCompressedSize",
  455. new Class[] {Long.TYPE});
  456. } catch (NoSuchMethodException nse) {
  457. // Ignore the exception
  458. }
  459. }
  460. }
  461. }
  462. /**
  463. * try to get as much single-line information out of the exception
  464. * as possible.
  465. */
  466. private static String getDisplayableMessage(Throwable e) {
  467. String msg = null;
  468. if (e != null) {
  469. if (e.getMessage() != null) {
  470. msg = e.getClass().getName() + ": " + e.getMessage();
  471. } else {
  472. msg = e.getClass().getName();
  473. }
  474. }
  475. return msg;
  476. }
  477. }