001: /*
002: * Copyright 2003-2004 Michael Franken, Zilverline.
003: *
004: * The contents of this file, or the files included with this file, are subject to
005: * the current version of ZILVERLINE Collaborative Source License for the
006: * Zilverline Search Engine (the "License"); You may not use this file except in
007: * compliance with the License.
008: *
009: * You may obtain a copy of the License at
010: *
011: * http://www.zilverline.org.
012: *
013: * See the License for the rights, obligations and
014: * limitations governing use of the contents of the file.
015: *
016: * The Original and Upgraded Code is the Zilverline Search Engine. The developer of
017: * the Original and Upgraded Code is Michael Franken. Michael Franken owns the
018: * copyrights in the portions it created. All Rights Reserved.
019: *
020: */
021:
022: package org.zilverline.util;
023:
024: import java.io.BufferedInputStream;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.IOException;
028: import java.security.MessageDigest;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: /**
034: * A Bunch of utilities for File manipulation.
035: *
036: * @author Michael Franken
037: * @version $Revision: 1.28 $
038: */
039: public final class FileUtils {
040: /** logger for Commons logging. */
041: private static Log log = LogFactory.getLog(FileUtils.class);
042:
043: /**
044: * Hidden default constructor.
045: */
046: private FileUtils() {
047: }
048:
049: /**
050: * Determine whether a file or directory is actually a symbolic link.
051: *
052: * @param file the file or directory to check
053: * @return true if so
054: */
055: public static boolean isLink(final File file) {
056: try {
057: String os = System.getProperty("os.name");
058: if (os.indexOf("Windows") >= 0) {
059: return false;
060: }
061: if (file == null || !file.exists()) {
062: return false;
063: } else {
064: String cnnpath = file.getCanonicalPath();
065: String abspath = file.getAbsolutePath();
066: log.debug("comparing " + cnnpath + " and " + abspath);
067: return !abspath.equals(cnnpath);
068: }
069: } catch (IOException e) {
070: log
071: .warn("could not determine whether "
072: + file.getAbsolutePath()
073: + " is a symbolic link", e);
074: return false;
075: }
076: }
077:
078: /**
079: * Recursively remove a directory.
080: *
081: * @param sourceDir the Directory to be removed
082: *
083: * @return true on success, false otherwise.
084: * <p>
085: */
086: public static boolean removeDir(final File sourceDir) {
087: // try {
088: // org.apache.commons.io.FileUtils.deleteDirectory(sourceDir);
089: // } catch (IOException e) {
090: // log.warn("could not delete " + sourceDir, e);
091: // return false;
092: // }
093: // log.debug("Succesfully removed directory: " + sourceDir);
094: // return true;
095:
096: if (sourceDir == null) {
097: return false;
098: }
099:
100: boolean allsuccess = true;
101: boolean success = true;
102: int nrOfFilesDeleted = 0;
103: int nrOfDirsDeleted = 0;
104:
105: if (sourceDir.isDirectory()) {
106: File[] files = sourceDir.listFiles();
107:
108: // I've seen listFiles return null, so be carefull, guess dir names too long for OS
109: if (files == null) {
110: log.warn("Something funny with '" + sourceDir
111: + "'. Name or path too long?");
112: log.warn("Could not delete '" + sourceDir
113: + "' from cache");
114:
115: // see whether we can rename the dir
116: if (sourceDir.renameTo(new File(sourceDir.getParent(),
117: "1"))) {
118: log.warn("Renamed '" + sourceDir + "'");
119:
120: return removeDir(sourceDir); // try again
121: } else {
122: log.warn("Could not rename '" + sourceDir
123: + "' to '" + sourceDir.getParent() + "1'");
124: }
125:
126: return false;
127: }
128:
129: log.debug(sourceDir + ": is a directory with "
130: + files.length + " docs");
131:
132: for (int i = 0; i < files.length; i++) {
133: log.debug("removing " + files[i]);
134:
135: if (files[i].isDirectory()) {
136: success = removeDir(files[i]);
137: } else {
138: success = files[i].delete();
139: }
140:
141: if (!success) {
142: log.warn("could not delete " + files[i]
143: + " from cache");
144: } else {
145: nrOfFilesDeleted++;
146: }
147:
148: allsuccess = allsuccess && success;
149: }
150:
151: log.debug("removing " + sourceDir);
152: success = sourceDir.delete();
153:
154: if (!success) {
155: log.warn("could not delete " + sourceDir
156: + " from cache");
157: } else {
158: nrOfDirsDeleted++;
159: }
160:
161: allsuccess = allsuccess && success;
162: }
163:
164: // TODO: make this info at outer level of recursion
165: log.debug("Deleted: " + nrOfDirsDeleted + " directories and "
166: + nrOfFilesDeleted + " files from " + sourceDir);
167: log.debug("Exiting removeDir for: " + sourceDir + ", "
168: + allsuccess);
169:
170: return allsuccess;
171: }
172:
173: /**
174: * Determine whether File is somewhere within Directory.
175: *
176: * @param file the File.
177: * @param dir the Directory.
178: *
179: * @return true, if so.
180: */
181: public static boolean isIn(final File file, final File dir) {
182: if ((file == null) || !file.isFile()) {
183: return false;
184: }
185:
186: if ((dir == null) || !dir.isDirectory()) {
187: return false;
188: }
189:
190: String fileString;
191: String directoryString;
192:
193: try {
194: directoryString = dir.getCanonicalPath();
195: fileString = file.getCanonicalPath();
196:
197: return fileString.startsWith(directoryString);
198: } catch (IOException e) {
199: log.error("Can't determine whether file is in Dir", e);
200: }
201:
202: return false;
203: }
204:
205: /**
206: * Get the casesensitive extension (without the '.') of a file.
207: *
208: * @param sourceFile the File the extension is extracted from.
209: *
210: * @return extension, empty string if no extension.
211: */
212: public static String getExtension(final File sourceFile) {
213: if (sourceFile == null) {
214: return "";
215: }
216:
217: // get the extension of the source file
218: int index = sourceFile.getName().lastIndexOf('.');
219:
220: if (index != -1) {
221: return sourceFile.getName().substring(index + 1);
222: }
223:
224: return "";
225: }
226:
227: /**
228: * Create a new directory in the given directory, with prefix and postfix.
229: *
230: * @param sourceFile the sourceFile to use for the new directory
231: * @param dir the (existing) directory to create the directory in.
232: *
233: * @return newly created Directory or null.
234: * @throws IOException directory can't be created
235: */
236: public static File createTempDir(final File sourceFile,
237: final File dir) throws IOException {
238: File unZipDestinationDirectory = null;
239:
240: try {
241: // get the full path (not just the name, since we could have recursed into newly created directory)
242: String destinationDirectory = sourceFile.getCanonicalPath();
243:
244: log.debug("destinationDirectory: " + destinationDirectory);
245:
246: // change extension into _
247: int index = destinationDirectory.lastIndexOf('.');
248: String extension;
249:
250: if (index != -1) {
251: extension = destinationDirectory.substring(index + 1);
252: destinationDirectory = destinationDirectory.substring(
253: 0, index)
254: + '_' + extension;
255: }
256:
257: // actually create the directory
258: unZipDestinationDirectory = new File(destinationDirectory);
259: boolean canCreate = unZipDestinationDirectory.mkdirs();
260:
261: if (!canCreate) {
262: log.warn("Could not create: "
263: + unZipDestinationDirectory);
264: }
265:
266: log.debug("Created: " + unZipDestinationDirectory
267: + " from File: " + sourceFile);
268: } catch (Exception e) {
269: log.error("error creating directory from file: "
270: + sourceFile, e);
271: }
272:
273: return unZipDestinationDirectory;
274: }
275:
276: /**
277: * Get the casesensitive basename (without the '.') of a file.
278: *
279: * @param sourceFile the File the basename is extracted from.
280: *
281: * @return basename, entire name if no extension.
282: */
283: public static String getBasename(final File sourceFile) {
284: if (sourceFile == null) {
285: return "";
286: }
287:
288: // get the basename of the source file
289: int index = sourceFile.getName().lastIndexOf('.');
290:
291: if (index != -1) {
292: return sourceFile.getName().substring(0, index);
293: }
294:
295: return sourceFile.getName();
296: }
297:
298: /**
299: * Get the MD5 hash (unique identifier based on contents) of a file.
300: *
301: * <p>
302: * N.B. This is an expensive operation, since the entire file is read.
303: * </p>
304: *
305: * @param sourceFile the File the MD5 hash is created from, can take null or not a normalFile
306: *
307: * @return MD5 hash of file as a String, null if it can't create a hash.
308: */
309: public static String getMD5Hash(final File sourceFile) {
310: log.debug("Getting MD5 hash for " + sourceFile);
311:
312: final char[] HEX = "0123456789abcdef".toCharArray();
313:
314: if (sourceFile == null || !sourceFile.isFile()) {
315: log.error("Error creating MD5 Hash for " + sourceFile);
316: return null;
317: }
318: BufferedInputStream bis = null;
319: try {
320: MessageDigest md = MessageDigest.getInstance("MD5");
321: // IMessageDigest md = HashFactory.getInstance("MD5");
322: if (md == null) {
323: log.error("Error creating MessageDigest for "
324: + sourceFile);
325: return null;
326: }
327:
328: bis = new BufferedInputStream(new FileInputStream(
329: sourceFile));
330: md.reset();
331: int len = 0;
332: byte[] buffer = new byte[8192];
333: while ((len = bis.read(buffer)) > -1) {
334: md.update(buffer, 0, len);
335: }
336:
337: byte[] bytes = md.digest();
338: if (bytes == null) {
339: log.error("MessageDigest has no bytes for "
340: + sourceFile);
341:
342: return null;
343: }
344:
345: // base64? encode the digest
346: StringBuffer sb = new StringBuffer(bytes.length * 2);
347: int b;
348: for (int i = 0; i < bytes.length; i++) {
349: b = bytes[i] & 0xFF;
350: sb.append(HEX[b >>> 4]);
351: sb.append(HEX[b & 0x0F]);
352: }
353:
354: log.debug("MD5 hash for " + sourceFile + " is " + sb);
355: return sb.toString();
356: } catch (Exception e) {
357: log.error("Can't determine MD5 hash for " + sourceFile, e);
358:
359: return null;
360: } finally {
361: if (bis != null) {
362: try {
363: bis.close();
364: } catch (IOException e) {
365: log.warn("Can't close stream for " + sourceFile, e);
366: }
367: }
368: }
369: }
370: }
|