1. /*
  2. * Copyright 2001-2004 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. * @version $Revision: 1.12.2.4 $
  27. */
  28. public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
  29. private static final int PLATFORM_UNIX = 3;
  30. private static final int PLATFORM_FAT = 0;
  31. private int internalAttributes = 0;
  32. private int platform = PLATFORM_FAT;
  33. private long externalAttributes = 0;
  34. private Vector extraFields = new Vector();
  35. private String name = null;
  36. /**
  37. * Creates a new zip entry with the specified name.
  38. *
  39. * @since 1.1
  40. */
  41. public ZipEntry(String name) {
  42. super(name);
  43. }
  44. /**
  45. * Creates a new zip entry with fields taken from the specified zip entry.
  46. *
  47. * @since 1.1
  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. *
  81. * @since 1.1
  82. */
  83. public ZipEntry(ZipEntry entry) throws ZipException {
  84. this((java.util.zip.ZipEntry) entry);
  85. setInternalAttributes(entry.getInternalAttributes());
  86. setExternalAttributes(entry.getExternalAttributes());
  87. setExtraFields(entry.getExtraFields());
  88. }
  89. /**
  90. * @since 1.9
  91. */
  92. protected ZipEntry() {
  93. super("");
  94. }
  95. /**
  96. * Overwrite clone
  97. *
  98. * @since 1.1
  99. */
  100. public Object clone() {
  101. try {
  102. ZipEntry e = (ZipEntry) super.clone();
  103. e.setName(getName());
  104. e.setComment(getComment());
  105. e.setMethod(getMethod());
  106. e.setTime(getTime());
  107. long size = getSize();
  108. if (size > 0) {
  109. e.setSize(size);
  110. }
  111. long cSize = getCompressedSize();
  112. if (cSize > 0) {
  113. e.setComprSize(cSize);
  114. }
  115. long crc = getCrc();
  116. if (crc > 0) {
  117. e.setCrc(crc);
  118. }
  119. e.extraFields = (Vector) extraFields.clone();
  120. e.setInternalAttributes(getInternalAttributes());
  121. e.setExternalAttributes(getExternalAttributes());
  122. e.setExtraFields(getExtraFields());
  123. return e;
  124. } catch (Throwable t) {
  125. // in JDK 1.1 ZipEntry is not Cloneable, so super.clone declares
  126. // to throw CloneNotSupported - since JDK 1.2 it is overridden to
  127. // not throw that exception
  128. return null;
  129. }
  130. }
  131. /**
  132. * Retrieves the internal file attributes.
  133. *
  134. * @since 1.1
  135. */
  136. public int getInternalAttributes() {
  137. return internalAttributes;
  138. }
  139. /**
  140. * Sets the internal file attributes.
  141. *
  142. * @since 1.1
  143. */
  144. public void setInternalAttributes(int value) {
  145. internalAttributes = value;
  146. }
  147. /**
  148. * Retrieves the external file attributes.
  149. *
  150. * @since 1.1
  151. */
  152. public long getExternalAttributes() {
  153. return externalAttributes;
  154. }
  155. /**
  156. * Sets the external file attributes.
  157. *
  158. * @since 1.1
  159. */
  160. public void setExternalAttributes(long value) {
  161. externalAttributes = value;
  162. }
  163. /**
  164. * Sets Unix permissions in a way that is understood by Info-Zip's
  165. * unzip command.
  166. *
  167. * @since Ant 1.5.2
  168. */
  169. public void setUnixMode(int mode) {
  170. setExternalAttributes((mode << 16)
  171. // MS-DOS read-only attribute
  172. | ((mode & 0200) == 0 ? 1 : 0)
  173. // MS-DOS directory flag
  174. | (isDirectory() ? 0x10 : 0));
  175. platform = PLATFORM_UNIX;
  176. }
  177. /**
  178. * Unix permission.
  179. *
  180. * @since Ant 1.6
  181. */
  182. public int getUnixMode() {
  183. return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
  184. }
  185. /**
  186. * Platform specification to put into the "version made
  187. * by" part of the central file header.
  188. *
  189. * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode}
  190. * has been called, in which case 3 (Unix) will be returned.
  191. *
  192. * @since Ant 1.5.2
  193. */
  194. public int getPlatform() {
  195. return platform;
  196. }
  197. /**
  198. * @since 1.9
  199. */
  200. protected void setPlatform(int platform) {
  201. this.platform = platform;
  202. }
  203. /**
  204. * Replaces all currently attached extra fields with the new array.
  205. *
  206. * @since 1.1
  207. */
  208. public void setExtraFields(ZipExtraField[] fields) {
  209. extraFields.removeAllElements();
  210. for (int i = 0; i < fields.length; i++) {
  211. extraFields.addElement(fields[i]);
  212. }
  213. setExtra();
  214. }
  215. /**
  216. * Retrieves extra fields.
  217. *
  218. * @since 1.1
  219. */
  220. public ZipExtraField[] getExtraFields() {
  221. ZipExtraField[] result = new ZipExtraField[extraFields.size()];
  222. extraFields.copyInto(result);
  223. return result;
  224. }
  225. /**
  226. * Adds an extra fields - replacing an already present extra field
  227. * of the same type.
  228. *
  229. * @since 1.1
  230. */
  231. public void addExtraField(ZipExtraField ze) {
  232. ZipShort type = ze.getHeaderId();
  233. boolean done = false;
  234. for (int i = 0; !done && i < extraFields.size(); i++) {
  235. if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
  236. extraFields.setElementAt(ze, i);
  237. done = true;
  238. }
  239. }
  240. if (!done) {
  241. extraFields.addElement(ze);
  242. }
  243. setExtra();
  244. }
  245. /**
  246. * Remove an extra fields.
  247. *
  248. * @since 1.1
  249. */
  250. public void removeExtraField(ZipShort type) {
  251. boolean done = false;
  252. for (int i = 0; !done && i < extraFields.size(); i++) {
  253. if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
  254. extraFields.removeElementAt(i);
  255. done = true;
  256. }
  257. }
  258. if (!done) {
  259. throw new java.util.NoSuchElementException();
  260. }
  261. setExtra();
  262. }
  263. /**
  264. * Throws an Exception if extra data cannot be parsed into extra fields.
  265. *
  266. * @since 1.1
  267. */
  268. public void setExtra(byte[] extra) throws RuntimeException {
  269. try {
  270. setExtraFields(ExtraFieldUtils.parse(extra));
  271. } catch (Exception e) {
  272. throw new RuntimeException(e.getMessage());
  273. }
  274. }
  275. /**
  276. * Unfortunately {@link java.util.zip.ZipOutputStream
  277. * java.util.zip.ZipOutputStream} seems to access the extra data
  278. * directly, so overriding getExtra doesn't help - we need to
  279. * modify super's data directly.
  280. *
  281. * @since 1.1
  282. */
  283. protected void setExtra() {
  284. super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
  285. }
  286. /**
  287. * Retrieves the extra data for the local file data.
  288. *
  289. * @since 1.1
  290. */
  291. public byte[] getLocalFileDataExtra() {
  292. byte[] extra = getExtra();
  293. return extra != null ? extra : new byte[0];
  294. }
  295. /**
  296. * Retrieves the extra data for the central directory.
  297. *
  298. * @since 1.1
  299. */
  300. public byte[] getCentralDirectoryExtra() {
  301. return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
  302. }
  303. /**
  304. * Helper for JDK 1.1 <-> 1.2 incompatibility.
  305. *
  306. * @since 1.2
  307. */
  308. private Long compressedSize = null;
  309. /**
  310. * Make this class work in JDK 1.1 like a 1.2 class.
  311. *
  312. * <p>This either stores the size for later usage or invokes
  313. * setCompressedSize via reflection.</p>
  314. *
  315. * @since 1.2
  316. */
  317. public void setComprSize(long size) {
  318. if (haveSetCompressedSize()) {
  319. performSetCompressedSize(this, size);
  320. } else {
  321. compressedSize = new Long(size);
  322. }
  323. }
  324. /**
  325. * Override to make this class work in JDK 1.1 like a 1.2 class.
  326. *
  327. * @since 1.2
  328. */
  329. public long getCompressedSize() {
  330. if (compressedSize != null) {
  331. // has been set explicitly and we are running in a 1.1 VM
  332. return compressedSize.longValue();
  333. }
  334. return super.getCompressedSize();
  335. }
  336. /**
  337. * @since 1.9
  338. */
  339. public String getName() {
  340. return name == null ? super.getName() : name;
  341. }
  342. /**
  343. * @since 1.10
  344. */
  345. public boolean isDirectory() {
  346. return getName().endsWith("/");
  347. }
  348. protected void setName(String name) {
  349. this.name = name;
  350. }
  351. /**
  352. * Helper for JDK 1.1
  353. *
  354. * @since 1.2
  355. */
  356. private static Method setCompressedSizeMethod = null;
  357. /**
  358. * Helper for JDK 1.1
  359. *
  360. * @since 1.2
  361. */
  362. private static Object lockReflection = new Object();
  363. /**
  364. * Helper for JDK 1.1
  365. *
  366. * @since 1.2
  367. */
  368. private static boolean triedToGetMethod = false;
  369. /**
  370. * Are we running JDK 1.2 or higher?
  371. *
  372. * @since 1.2
  373. */
  374. private static boolean haveSetCompressedSize() {
  375. checkSCS();
  376. return setCompressedSizeMethod != null;
  377. }
  378. /**
  379. * Invoke setCompressedSize via reflection.
  380. *
  381. * @since 1.2
  382. */
  383. private static void performSetCompressedSize(ZipEntry ze, long size) {
  384. Long[] s = {new Long(size)};
  385. try {
  386. setCompressedSizeMethod.invoke(ze, s);
  387. } catch (InvocationTargetException ite) {
  388. Throwable nested = ite.getTargetException();
  389. throw new RuntimeException("Exception setting the compressed size "
  390. + "of " + ze + ": "
  391. + nested.getMessage());
  392. } catch (Throwable other) {
  393. throw new RuntimeException("Exception setting the compressed size "
  394. + "of " + ze + ": "
  395. + other.getMessage());
  396. }
  397. }
  398. /**
  399. * Try to get a handle to the setCompressedSize method.
  400. *
  401. * @since 1.2
  402. */
  403. private static void checkSCS() {
  404. if (!triedToGetMethod) {
  405. synchronized (lockReflection) {
  406. triedToGetMethod = true;
  407. try {
  408. setCompressedSizeMethod =
  409. java.util.zip.ZipEntry.class.getMethod("setCompressedSize",
  410. new Class[] {Long.TYPE});
  411. } catch (NoSuchMethodException nse) {
  412. }
  413. }
  414. }
  415. }
  416. }