1. /*
  2. * Copyright 2000-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.ant.taskdefs;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.FileNotFoundException;
  21. import java.io.InputStream;
  22. import java.io.IOException;
  23. import java.util.Date;
  24. import java.util.Enumeration;
  25. import java.util.Vector;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.DirectoryScanner;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.Task;
  30. import org.apache.tools.ant.types.FileSet;
  31. import org.apache.tools.ant.types.PatternSet;
  32. import org.apache.tools.ant.types.selectors.SelectorUtils;
  33. import org.apache.tools.ant.util.FileUtils;
  34. import org.apache.tools.zip.ZipEntry;
  35. import org.apache.tools.zip.ZipFile;
  36. /**
  37. * Unzip a file.
  38. *
  39. *
  40. * @since Ant 1.1
  41. *
  42. * @ant.task category="packaging"
  43. * name="unzip"
  44. * name="unjar"
  45. * name="unwar"
  46. */
  47. public class Expand extends Task {
  48. private File dest; //req
  49. private File source; // req
  50. private boolean overwrite = true;
  51. private Vector patternsets = new Vector();
  52. private Vector filesets = new Vector();
  53. private static final String NATIVE_ENCODING = "native-encoding";
  54. private String encoding = "UTF8";
  55. /**
  56. * Do the work.
  57. *
  58. * @exception BuildException Thrown in unrecoverable error.
  59. */
  60. public void execute() throws BuildException {
  61. if ("expand".equals(getTaskType())) {
  62. log("!! expand is deprecated. Use unzip instead. !!");
  63. }
  64. if (source == null && filesets.size() == 0) {
  65. throw new BuildException("src attribute and/or filesets must be "
  66. + "specified");
  67. }
  68. if (dest == null) {
  69. throw new BuildException(
  70. "Dest attribute must be specified");
  71. }
  72. if (dest.exists() && !dest.isDirectory()) {
  73. throw new BuildException("Dest must be a directory.", getLocation());
  74. }
  75. FileUtils fileUtils = FileUtils.newFileUtils();
  76. if (source != null) {
  77. if (source.isDirectory()) {
  78. throw new BuildException("Src must not be a directory."
  79. + " Use nested filesets instead.", getLocation());
  80. } else {
  81. expandFile(fileUtils, source, dest);
  82. }
  83. }
  84. if (filesets.size() > 0) {
  85. for (int j = 0; j < filesets.size(); j++) {
  86. FileSet fs = (FileSet) filesets.elementAt(j);
  87. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  88. File fromDir = fs.getDir(getProject());
  89. String[] files = ds.getIncludedFiles();
  90. for (int i = 0; i < files.length; ++i) {
  91. File file = new File(fromDir, files[i]);
  92. expandFile(fileUtils, file, dest);
  93. }
  94. }
  95. }
  96. }
  97. /*
  98. * This method is to be overridden by extending unarchival tasks.
  99. */
  100. protected void expandFile(FileUtils fileUtils, File srcF, File dir) {
  101. log("Expanding: " + srcF + " into " + dir, Project.MSG_INFO);
  102. ZipFile zf = null;
  103. try {
  104. zf = new ZipFile(srcF, encoding);
  105. Enumeration e = zf.getEntries();
  106. while (e.hasMoreElements()) {
  107. ZipEntry ze = (ZipEntry) e.nextElement();
  108. extractFile(fileUtils, srcF, dir, zf.getInputStream(ze),
  109. ze.getName(), new Date(ze.getTime()),
  110. ze.isDirectory());
  111. }
  112. log("expand complete", Project.MSG_VERBOSE);
  113. } catch (IOException ioe) {
  114. throw new BuildException("Error while expanding " + srcF.getPath(),
  115. ioe);
  116. } finally {
  117. if (zf != null) {
  118. try {
  119. zf.close();
  120. } catch (IOException e) {
  121. //ignore
  122. }
  123. }
  124. }
  125. }
  126. protected void extractFile(FileUtils fileUtils, File srcF, File dir,
  127. InputStream compressedInputStream,
  128. String entryName,
  129. Date entryDate, boolean isDirectory)
  130. throws IOException {
  131. if (patternsets != null && patternsets.size() > 0) {
  132. String name = entryName.replace('/', File.separatorChar)
  133. .replace('\\', File.separatorChar);
  134. boolean included = false;
  135. for (int v = 0; v < patternsets.size(); v++) {
  136. PatternSet p = (PatternSet) patternsets.elementAt(v);
  137. String[] incls = p.getIncludePatterns(getProject());
  138. if (incls == null || incls.length == 0) {
  139. // no include pattern implicitly means includes="**"
  140. incls = new String[] {"**"};
  141. }
  142. for (int w = 0; w < incls.length; w++) {
  143. String pattern = incls[w].replace('/', File.separatorChar)
  144. .replace('\\', File.separatorChar);
  145. if (pattern.endsWith(File.separator)) {
  146. pattern += "**";
  147. }
  148. included = SelectorUtils.matchPath(pattern, name);
  149. if (included) {
  150. break;
  151. }
  152. }
  153. if (!included) {
  154. break;
  155. }
  156. String[] excls = p.getExcludePatterns(getProject());
  157. if (excls != null) {
  158. for (int w = 0; w < excls.length; w++) {
  159. String pattern = excls[w]
  160. .replace('/', File.separatorChar)
  161. .replace('\\', File.separatorChar);
  162. if (pattern.endsWith(File.separator)) {
  163. pattern += "**";
  164. }
  165. included = !(SelectorUtils.matchPath(pattern, name));
  166. if (!included) {
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. if (!included) {
  173. //Do not process this file
  174. return;
  175. }
  176. }
  177. File f = fileUtils.resolveFile(dir, entryName);
  178. try {
  179. if (!overwrite && f.exists()
  180. && f.lastModified() >= entryDate.getTime()) {
  181. log("Skipping " + f + " as it is up-to-date",
  182. Project.MSG_DEBUG);
  183. return;
  184. }
  185. log("expanding " + entryName + " to " + f,
  186. Project.MSG_VERBOSE);
  187. // create intermediary directories - sometimes zip don't add them
  188. File dirF = fileUtils.getParentFile(f);
  189. if (dirF != null) {
  190. dirF.mkdirs();
  191. }
  192. if (isDirectory) {
  193. f.mkdirs();
  194. } else {
  195. byte[] buffer = new byte[1024];
  196. int length = 0;
  197. FileOutputStream fos = null;
  198. try {
  199. fos = new FileOutputStream(f);
  200. while ((length =
  201. compressedInputStream.read(buffer)) >= 0) {
  202. fos.write(buffer, 0, length);
  203. }
  204. fos.close();
  205. fos = null;
  206. } finally {
  207. if (fos != null) {
  208. try {
  209. fos.close();
  210. } catch (IOException e) {
  211. // ignore
  212. }
  213. }
  214. }
  215. }
  216. fileUtils.setFileLastModified(f, entryDate.getTime());
  217. } catch (FileNotFoundException ex) {
  218. log("Unable to expand to file " + f.getPath(), Project.MSG_WARN);
  219. }
  220. }
  221. /**
  222. * Set the destination directory. File will be unzipped into the
  223. * destination directory.
  224. *
  225. * @param d Path to the directory.
  226. */
  227. public void setDest(File d) {
  228. this.dest = d;
  229. }
  230. /**
  231. * Set the path to zip-file.
  232. *
  233. * @param s Path to zip-file.
  234. */
  235. public void setSrc(File s) {
  236. this.source = s;
  237. }
  238. /**
  239. * Should we overwrite files in dest, even if they are newer than
  240. * the corresponding entries in the archive?
  241. */
  242. public void setOverwrite(boolean b) {
  243. overwrite = b;
  244. }
  245. /**
  246. * Add a patternset
  247. */
  248. public void addPatternset(PatternSet set) {
  249. patternsets.addElement(set);
  250. }
  251. /**
  252. * Add a fileset
  253. */
  254. public void addFileset(FileSet set) {
  255. filesets.addElement(set);
  256. }
  257. /**
  258. * Sets the encoding to assume for file names and comments.
  259. *
  260. * <p>Set to <code>native-encoding</code> if you want your
  261. * platform's native encoding, defaults to UTF8.</p>
  262. *
  263. * @since Ant 1.6
  264. */
  265. public void setEncoding(String encoding) {
  266. if (NATIVE_ENCODING.equals(encoding)) {
  267. encoding = null;
  268. }
  269. this.encoding = encoding;
  270. }
  271. }