1 package org.apache.maven.shared.utils;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.util.Date;
29 import java.util.zip.ZipEntry;
30 import java.util.zip.ZipInputStream;
31
32 import org.apache.maven.shared.utils.io.FileUtils;
33 import org.apache.maven.shared.utils.io.IOUtil;
34
35 /**
36 * Expand will unpack the given zip archive.
37 *
38 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
39 */
40 class Expand
41 {
42 /**
43 * Source file which should get expanded
44 */
45 private File source;
46
47 /**
48 * destination directory
49 */
50 private File dest;
51
52 /**
53 * if the unpackaging should get performed if the destination already exists.
54 */
55 private boolean overwrite = false;
56
57 private static final int BUFFER_SIZE = 2 ^ 16;
58
59
60 /**
61 * The zip archive which should get expanded.
62 *
63 * @param sourceArchive
64 */
65 public void setSrc( File sourceArchive )
66 {
67 this.source = sourceArchive;
68 }
69
70 /**
71 * Set the destination directory into which the archive should get expanded.
72 * The directory will get created if it doesn't yet exist
73 * while executing the expand.
74 *
75 * @param destinationDirectory
76 */
77 public void setDest( File destinationDirectory )
78 {
79 this.dest = destinationDirectory;
80 }
81
82 /**
83 * If the destination directory should get overwritten if the content
84 * already exists. If <code>false</code> we will only overwrite if the local
85 * file or directory is older than the one in the archive.
86 *
87 * @param overwrite
88 */
89 public void setOverwrite( boolean overwrite )
90 {
91 this.overwrite = overwrite;
92 }
93
94 /**
95 * Actually perform the unpacking of the source archive
96 * into the destination directory.
97 *
98 * @throws Exception
99 */
100 public void execute()
101 throws Exception
102 {
103 expandFile( source, dest );
104 }
105
106 /**
107 * <p>It is intended to be overwritten when implementing an own unarchiver</p>
108 * <p/>
109 * <p><b>Note:</b> we kept this protected method for the sake of backward compatibility!</p>
110 *
111 * @param srcFile The source file.
112 * @param destination The destination.
113 * @throws Exception In case of failure.
114 */
115 void expandFile( File srcFile, File destination )
116 throws Exception
117 {
118 if ( source == null )
119 {
120 throw new NullPointerException( "Source Archive must not be null!" );
121 }
122
123 File destDir = destination;
124 if ( destDir == null )
125 {
126 destDir = new File( System.getProperty( "user.dir" ) );
127 }
128
129 ZipInputStream in = null;
130 try
131 {
132 in = new ZipInputStream( new FileInputStream( srcFile ) );
133
134 for ( ZipEntry zipEntry = in.getNextEntry(); zipEntry != null; zipEntry = in.getNextEntry() )
135 {
136 String zipEntryName = zipEntry.getName();
137 Date zipEntryDate = new Date( zipEntry.getTime() );
138
139 extractFile( source, destDir, in, zipEntryName, zipEntryDate, zipEntry.isDirectory() );
140 }
141
142 in.close();
143 in = null;
144 }
145 finally
146 {
147 IOUtil.close( in );
148 }
149 }
150
151 /**
152 * Extract a single ZipEntry.
153 * <p/>
154 * <p><b>Note:</b> we kept this protected method for the sake of backward compatibility!</p>
155 *
156 * @param archive the archive to unpack
157 * @param destDir the destination dirctory
158 * @param compressedInputStream
159 * @param entryName
160 * @param entryDate
161 * @param isDirectory
162 * @throws Exception
163 */
164 void extractFile( File archive, File destDir, InputStream compressedInputStream, String entryName,
165 Date entryDate, boolean isDirectory )
166 throws Exception
167 {
168 File targetFile = new File( destDir, entryName );
169
170 if ( !targetFile.getAbsolutePath().startsWith( destDir.getAbsolutePath() ) )
171 {
172 throw new IOException( "Entry '" + entryName + "' outside the target directory." );
173 }
174
175 // if overwrite is specified and the file type
176 // of the existing file does not match, then delete it
177 if ( overwrite && targetFile.exists() && targetFile.isDirectory() != isDirectory )
178 {
179 deleteFileOrDir( targetFile );
180 }
181
182 if ( !targetFile.exists() || overwrite || targetFile.lastModified() <= entryDate.getTime() )
183 {
184 if ( isDirectory )
185 {
186 targetFile.mkdirs();
187 }
188 else
189 {
190 byte[] buffer = new byte[BUFFER_SIZE];
191 OutputStream out = null;
192 try
193 {
194 out = new FileOutputStream( targetFile );
195
196 int len;
197 while ( ( len = compressedInputStream.read( buffer ) ) >= 0 )
198 {
199 out.write( buffer, 0, len );
200 }
201
202 out.close();
203 out = null;
204 }
205 finally
206 {
207 IOUtil.close( out );
208 }
209 targetFile.setLastModified( entryDate.getTime() );
210 }
211 }
212 }
213
214 /**
215 * small helper method who deletes the given directory or file.
216 *
217 * @param targetFile
218 * @throws IOException
219 */
220 private void deleteFileOrDir( File targetFile )
221 throws IOException
222 {
223 if ( targetFile.isDirectory() )
224 {
225 FileUtils.deleteDirectory( targetFile );
226 }
227 else
228 {
229 FileUtils.delete( targetFile );
230 }
231
232 }
233 }