001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.util;
019:
020: import java.io.BufferedReader;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.InputStreamReader;
027: import java.io.OutputStream;
028: import java.io.PrintWriter;
029: import java.io.StringWriter;
030: import java.net.URL;
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Collections;
034: import java.util.List;
035:
036: import org.apache.ivy.util.url.URLHandlerRegistry;
037:
038: /**
039: * Utility class used to deal with file related operations, like copy, full reading, symlink, ...
040: */
041: public final class FileUtil {
042:
043: private FileUtil() {
044: //Utility class
045: }
046:
047: // according to tests by users, 64kB seems to be a good value for the buffer used during copy
048: // further improvements could be obtained using NIO API
049: private static final int BUFFER_SIZE = 64 * 1024;
050:
051: private static final byte[] EMPTY_BUFFER = new byte[0];
052:
053: public static void symlink(File src, File dest,
054: CopyProgressListener l, boolean overwrite)
055: throws IOException {
056: try {
057: if (dest.exists()) {
058: if (!dest.isFile()) {
059: throw new IOException(
060: "impossible to copy: destination is not a file: "
061: + dest);
062: }
063: if (!overwrite) {
064: Message.verbose(dest
065: + " already exists, nothing done");
066: return;
067: }
068: }
069: if (dest.getParentFile() != null) {
070: dest.getParentFile().mkdirs();
071: }
072:
073: Runtime runtime = Runtime.getRuntime();
074: Message.verbose("executing 'ln -s -f "
075: + src.getAbsolutePath() + " " + dest.getPath()
076: + "'");
077: Process process = runtime.exec(new String[] { "ln", "-s",
078: "-f", src.getAbsolutePath(), dest.getPath() });
079:
080: if (process.waitFor() != 0) {
081: InputStream errorStream = process.getErrorStream();
082: InputStreamReader isr = new InputStreamReader(
083: errorStream);
084: BufferedReader br = new BufferedReader(isr);
085:
086: StringBuffer error = new StringBuffer();
087: String line;
088: while ((line = br.readLine()) != null) {
089: error.append(line);
090: error.append('\n');
091: }
092:
093: throw new IOException("error symlinking " + src
094: + " to " + dest + ":\n" + error);
095: }
096: if (!dest.exists()) {
097: throw new IOException("error symlinking " + dest
098: + " doesn't exists");
099: }
100: } catch (IOException x) {
101: Message.verbose("symlink failed; falling back to copy");
102: StringWriter buffer = new StringWriter();
103: x.printStackTrace(new PrintWriter(buffer));
104: Message.debug(buffer.toString());
105: copy(src, dest, l, overwrite);
106: } catch (InterruptedException x) {
107: Thread.currentThread().interrupt();
108: }
109: }
110:
111: public static boolean copy(File src, File dest,
112: CopyProgressListener l) throws IOException {
113: return copy(src, dest, l, false);
114: }
115:
116: public static boolean copy(File src, File dest,
117: CopyProgressListener l, boolean overwrite)
118: throws IOException {
119: if (dest.exists()) {
120: if (!dest.isFile()) {
121: throw new IOException(
122: "impossible to copy: destination is not a file: "
123: + dest);
124: }
125: if (overwrite) {
126: if (!dest.canWrite()) {
127: dest.delete();
128: } // if dest is writable, the copy will overwrite it without requiring a delete
129: } else {
130: Message.verbose(dest + " already exists, nothing done");
131: return false;
132: }
133: }
134: copy(new FileInputStream(src), dest, l);
135: long srcLen = src.length();
136: long destLen = dest.length();
137: if (srcLen != destLen) {
138: dest.delete();
139: throw new IOException("size of source file "
140: + src.toString() + "(" + srcLen
141: + ") differs from size of dest file "
142: + dest.toString() + "(" + destLen
143: + ") - please retry");
144: }
145: dest.setLastModified(src.lastModified());
146: return true;
147: }
148:
149: public static void copy(URL src, File dest, CopyProgressListener l)
150: throws IOException {
151: URLHandlerRegistry.getDefault().download(src, dest, l);
152: }
153:
154: public static void copy(InputStream src, File dest,
155: CopyProgressListener l) throws IOException {
156: if (dest.getParentFile() != null) {
157: dest.getParentFile().mkdirs();
158: }
159: copy(src, new FileOutputStream(dest), l);
160: }
161:
162: public static void copy(InputStream src, OutputStream dest,
163: CopyProgressListener l) throws IOException {
164: CopyProgressEvent evt = null;
165: if (l != null) {
166: evt = new CopyProgressEvent();
167: }
168: try {
169: byte[] buffer = new byte[BUFFER_SIZE];
170: int c;
171: long total = 0;
172:
173: if (l != null) {
174: l.start(evt);
175: }
176: while ((c = src.read(buffer)) != -1) {
177: if (Thread.currentThread().isInterrupted()) {
178: throw new IOException("transfer interrupted");
179: }
180: dest.write(buffer, 0, c);
181: total += c;
182: if (l != null) {
183: l.progress(evt.update(buffer, c, total));
184: }
185: }
186:
187: if (l != null) {
188: evt.update(EMPTY_BUFFER, 0, total);
189: }
190:
191: // close the streams
192: src.close();
193: dest.close();
194: } finally {
195: try {
196: src.close();
197: } catch (IOException ex) {
198: // ignore
199: }
200: try {
201: dest.close();
202: } catch (IOException ex) {
203: // ignore
204: }
205: }
206:
207: if (l != null) {
208: l.end(evt);
209: }
210: }
211:
212: /**
213: * Reads the whole BufferedReader line by line, using \n as line separator for each line.
214: * <p>
215: * Note that this method will add a final \n to the last line even though there is no new line
216: * character at the end of last line in the original reader.
217: * </p>
218: * <p>
219: * The BufferedReader is closed when this method returns.
220: * </p>
221: *
222: * @param in
223: * the {@link BufferedReader} to read from
224: * @return a String with the whole content read from the {@link BufferedReader}
225: * @throws IOException
226: * if an IO problems occur during reading
227: */
228: public static String readEntirely(BufferedReader in)
229: throws IOException {
230: try {
231: StringBuffer buf = new StringBuffer();
232:
233: String line = in.readLine();
234: while (line != null) {
235: buf.append(line + "\n");
236: line = in.readLine();
237: }
238: return buf.toString();
239: } finally {
240: in.close();
241: }
242: }
243:
244: /**
245: * Reads the entire content of the file and returns it as a String.
246: *
247: * @param f
248: * the file to read from
249: * @return a String with the file content
250: * @throws IOException
251: * if an IO problems occurs during reading
252: */
253: public static String readEntirely(File f) throws IOException {
254: return readEntirely(new FileInputStream(f));
255: }
256:
257: /**
258: * Reads the entire content of the {@link InputStream} and returns it as a String.
259: * <p>
260: * The input stream is closed when this method returns.
261: * </p>
262: *
263: * @param is
264: * the {@link InputStream} to read from
265: * @return a String with the input stream content
266: * @throws IOException
267: * if an IO problems occurs during reading
268: */
269: public static String readEntirely(InputStream is)
270: throws IOException {
271: try {
272: StringBuffer sb = new StringBuffer();
273: byte[] buffer = new byte[BUFFER_SIZE];
274: int c;
275:
276: while ((c = is.read(buffer)) != -1) {
277: sb.append(new String(buffer, 0, c));
278: }
279: return sb.toString();
280: } finally {
281: is.close();
282: }
283: }
284:
285: public static String concat(String dir, String file) {
286: return dir + "/" + file;
287: }
288:
289: public static void forceDelete(File f) {
290: if (f.isDirectory()) {
291: File[] sub = f.listFiles();
292: for (int i = 0; i < sub.length; i++) {
293: forceDelete(sub[i]);
294: }
295: }
296: f.delete();
297: }
298:
299: /**
300: * Returns a list of Files composed of all directories being parent of file and child of root +
301: * file and root themselves. Example: getPathFiles(new File("test"), new
302: * File("test/dir1/dir2/file.txt")) => {new File("test/dir1"), new File("test/dir1/dir2"), new
303: * File("test/dir1/dir2/file.txt") } Note that if root is not an ancester of file, or if root is
304: * null, all directories from the file system root will be returned.
305: */
306: public static List getPathFiles(File root, File file) {
307: List ret = new ArrayList();
308: while (file != null
309: && !file.getAbsolutePath().equals(
310: root.getAbsolutePath())) {
311: ret.add(file);
312: file = file.getParentFile();
313: }
314: if (root != null) {
315: ret.add(root);
316: }
317: Collections.reverse(ret);
318: return ret;
319: }
320:
321: /**
322: * Returns a collection of all Files being contained in the given directory, recursively,
323: * including directories.
324: *
325: * @param dir The directory from which all files, including files in subdirectory)
326: * are extracted.
327: * @return A collectoin containing all the files of the given directory and it's
328: * subdirectories.
329: */
330: public static Collection listAll(File dir) {
331: return listAll(dir, new ArrayList());
332: }
333:
334: private static Collection listAll(File file, Collection list) {
335: if (file.exists()) {
336: list.add(file);
337: }
338: if (file.isDirectory()) {
339: File[] files = file.listFiles();
340: for (int i = 0; i < files.length; i++) {
341: listAll(files[i], list);
342: }
343: }
344: return list;
345: }
346:
347: }
|