View Javadoc
1   /*
2    * SmartSprites Project
3    *
4    * Copyright (C) 2007-2009, Stanisław Osiński.
5    * All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without modification,
8    * are permitted provided that the following conditions are met:
9    *
10   * - Redistributions of  source code must  retain the above  copyright notice, this
11   *   list of conditions and the following disclaimer.
12   *
13   * - Redistributions in binary form must reproduce the above copyright notice, this
14   *   list of conditions and the following  disclaimer in  the documentation  and/or
15   *   other materials provided with the distribution.
16   *
17   * - Neither the name of the SmartSprites Project nor the names of its contributors
18   *   may  be used  to endorse  or  promote  products derived   from  this  software
19   *   without specific prior written permission.
20   *
21   * - We kindly request that you include in the end-user documentation provided with
22   *   the redistribution and/or in the software itself an acknowledgement equivalent
23   *   to  the  following: "This product includes software developed by the SmartSprites
24   *   Project."
25   *
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  AND
27   * ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED  TO, THE IMPLIED
28   * WARRANTIES  OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR  PURPOSE   ARE
29   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE  FOR
30   * ANY DIRECT, INDIRECT, INCIDENTAL,  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
31   * (INCLUDING, BUT  NOT LIMITED  TO, PROCUREMENT  OF SUBSTITUTE  GOODS OR SERVICES;
32   * LOSS OF USE, DATA, OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  ON
33   * ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY,  OR TORT
34   * (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY  OUT OF THE USE  OF THIS
35   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36   */
37  package org.carrot2.util;
38  
39  import java.io.File;
40  import java.io.IOException;
41  import java.util.ArrayList;
42  import java.util.regex.Pattern;
43  
44  import org.apache.commons.io.FilenameUtils;
45  
46  /**
47   * Various utility methods for working with {@link File}s.
48   */
49  public class FileUtils {
50  
51      /**
52       * Instantiates a new file utils.
53       */
54      private FileUtils() {
55          // Prevent Instantiation
56      }
57  
58      /**
59       * Creates a new {@link File} from the provided path and attempts to execute {@link File#getCanonicalFile()}. In
60       * case of a failure, returns the result of {@link File#getAbsoluteFile()}.
61       *
62       * @param path
63       *            the path
64       *
65       * @return the canonical or absolute file
66       */
67      public static File getCanonicalOrAbsoluteFile(String path) {
68          File file = new File(path);
69          try {
70              return file.getCanonicalFile();
71          } catch (final IOException e) {
72              return file.getAbsoluteFile();
73          }
74      }
75  
76      /**
77       * Changes the root directory of a file. For example, file is /a/b/c/d/e and oldRoot is /a/b/c, and newRoot is /x/y,
78       * the result will be /x/y/d/e.
79       *
80       * @param file
81       *            the file
82       * @param oldRoot
83       *            the old root
84       * @param newRoot
85       *            the new root
86       *
87       * @return the string
88       */
89      public static String changeRoot(String file, String oldRoot, String newRoot) {
90          // File is assumed to be a subpath of oldRoot, so PathUtils.getRelativeFilePath()
91          // shouldn't return null here.
92          final String relativePath = PathUtils.getRelativeFilePath(oldRoot, file);
93          return FilenameUtils.concat(newRoot, relativePath);
94      }
95  
96      /**
97       * Removes useless segments in relative paths, e.g. replaces <code>../path/../other/file.css</code> with
98       * <code>../other/file.css</code>
99       *
100      * @param path
101      *            the path
102      * @param separator
103      *            the separator
104      *
105      * @return the string
106      */
107     public static String canonicalize(String path, String separator) {
108         String replaced = path;
109         String toReplace = null;
110         final String separatorEscaped = Pattern.quote(separator);
111         final Pattern pattern = Pattern
112                 .compile("[^" + separatorEscaped + "\\.]+" + separatorEscaped + "\\.\\." + separatorEscaped + "?");
113         while (!replaced.equals(toReplace)) {
114             toReplace = replaced;
115             replaced = pattern.matcher(toReplace).replaceFirst("");
116         }
117         return replaced;
118     }
119 
120     /**
121      * Attempts to delete the provided files and throws an {@link IOException} in case {@link File#delete()} returns
122      * <code>false</code> for any of them.
123      *
124      * @param files
125      *            the files
126      *
127      * @throws IOException
128      *             Signals that an I/O exception has occurred.
129      */
130     public static void deleteThrowingExceptions(File... files) throws IOException {
131         if (files == null) {
132             return;
133         }
134 
135         final ArrayList<String> undeletedFiles = new ArrayList<>();
136         for (File file : files) {
137             if (file == null) {
138                 continue;
139             }
140 
141             if (file.exists() && !file.delete()) {
142                 undeletedFiles.add(file.getPath());
143             }
144         }
145 
146         if (!undeletedFiles.isEmpty()) {
147             throw new IOException("Unable to delete files: " + undeletedFiles.toString());
148         }
149     }
150 
151     /**
152      * Calls {@link File#mkdirs()} on the provided argument and throws an {@link IOException} if the call returns
153      * <code>false</code>.
154      *
155      * @param dirs
156      *            the dirs
157      *
158      * @throws IOException
159      *             Signals that an I/O exception has occurred.
160      */
161     public static void mkdirsThrowingExceptions(File dirs) throws IOException {
162         if (dirs.exists()) {
163             return;
164         }
165 
166         if (!dirs.mkdirs()) {
167             throw new IOException("Unable to create directories: " + dirs.getPath());
168         }
169     }
170 
171     /**
172      * Returns <code>true</code> if file is contained in the parent directory or any parent of the parent directory.
173      *
174      * @param file
175      *            the file
176      * @param parent
177      *            the parent
178      *
179      * @return true, if is file in parent
180      */
181     public static boolean isFileInParent(File file, File parent) {
182         final File fileParent = file.getParentFile();
183         if (fileParent == null) {
184             return false;
185         }
186 
187         if (fileParent.equals(parent)) {
188             return true;
189         }
190 
191         return isFileInParent(fileParent, parent);
192     }
193 }