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 }