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.nio.file.Path;
42  import java.util.ArrayList;
43  import java.util.regex.Pattern;
44  
45  import org.apache.commons.io.FilenameUtils;
46  
47  /**
48   * Various utility methods for working with {@link File}s.
49   */
50  public class FileUtils {
51  
52      /**
53       * Instantiates a new file utils.
54       */
55      private FileUtils() {
56          // Prevent Instantiation
57      }
58  
59      /**
60       * Creates a new {@link File} from the provided path and attempts to execute {@link File#getCanonicalFile()}. In
61       * case of a failure, returns the result of {@link File#getAbsoluteFile()}.
62       *
63       * @param path
64       *            the path
65       *
66       * @return the canonical or absolute file
67       */
68      public static File getCanonicalOrAbsoluteFile(String path) {
69          File file = Path.of(path).toFile();
70          try {
71              return file.getCanonicalFile();
72          } catch (final IOException e) {
73              return file.getAbsoluteFile();
74          }
75      }
76  
77      /**
78       * 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,
79       * the result will be /x/y/d/e.
80       *
81       * @param file
82       *            the file
83       * @param oldRoot
84       *            the old root
85       * @param newRoot
86       *            the new root
87       *
88       * @return the string
89       */
90      public static String changeRoot(String file, String oldRoot, String newRoot) {
91          // File is assumed to be a subpath of oldRoot, so PathUtils.getRelativeFilePath()
92          // shouldn't return null here.
93          final String relativePath = PathUtils.getRelativeFilePath(oldRoot, file);
94          return FilenameUtils.concat(newRoot, relativePath);
95      }
96  
97      /**
98       * Removes useless segments in relative paths, e.g. replaces <code>../path/../other/file.css</code> with
99       * <code>../other/file.css</code>
100      *
101      * @param path
102      *            the path
103      * @param separator
104      *            the separator
105      *
106      * @return the string
107      */
108     public static String canonicalize(String path, String separator) {
109         String replaced = path;
110         String toReplace = null;
111         final String separatorEscaped = Pattern.quote(separator);
112         final Pattern pattern = Pattern
113                 .compile("[^" + separatorEscaped + "\\.]+" + separatorEscaped + "\\.\\." + separatorEscaped + "?");
114         while (!replaced.equals(toReplace)) {
115             toReplace = replaced;
116             replaced = pattern.matcher(toReplace).replaceFirst("");
117         }
118         return replaced;
119     }
120 
121     /**
122      * Attempts to delete the provided files and throws an {@link IOException} in case {@link File#delete()} returns
123      * <code>false</code> for any of them.
124      *
125      * @param files
126      *            the files
127      *
128      * @throws IOException
129      *             Signals that an I/O exception has occurred.
130      */
131     public static void deleteThrowingExceptions(File... files) throws IOException {
132         if (files == null) {
133             return;
134         }
135 
136         final ArrayList<String> undeletedFiles = new ArrayList<>();
137         for (File file : files) {
138             if (file == null) {
139                 continue;
140             }
141 
142             if (file.exists() && !file.delete()) {
143                 undeletedFiles.add(file.getPath());
144             }
145         }
146 
147         if (!undeletedFiles.isEmpty()) {
148             throw new IOException("Unable to delete files: " + undeletedFiles.toString());
149         }
150     }
151 
152     /**
153      * Calls {@link File#mkdirs()} on the provided argument and throws an {@link IOException} if the call returns
154      * <code>false</code>.
155      *
156      * @param dirs
157      *            the dirs
158      *
159      * @throws IOException
160      *             Signals that an I/O exception has occurred.
161      */
162     public static void mkdirsThrowingExceptions(File dirs) throws IOException {
163         if (dirs.exists()) {
164             return;
165         }
166 
167         if (!dirs.mkdirs()) {
168             throw new IOException("Unable to create directories: " + dirs.getPath());
169         }
170     }
171 
172     /**
173      * Returns <code>true</code> if file is contained in the parent directory or any parent of the parent directory.
174      *
175      * @param file
176      *            the file
177      * @param parent
178      *            the parent
179      *
180      * @return true, if is file in parent
181      */
182     public static boolean isFileInParent(File file, File parent) {
183         final File fileParent = file.getParentFile();
184         if (fileParent == null) {
185             return false;
186         }
187 
188         if (fileParent.equals(parent)) {
189             return true;
190         }
191 
192         return isFileInParent(fileParent, parent);
193     }
194 }