1 |
/*
|
1 |
/*
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
3 |
*
|
3 |
*
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
5 |
*
|
5 |
*
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
9 |
* language governing permissions and limitations under the License.
|
9 |
* language governing permissions and limitations under the License.
|
10 |
*
|
10 |
*
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
12 |
*/
|
12 |
*/
|
13 |
|
13 |
|
14 |
package org.openconcerto.utils;
|
14 |
package org.openconcerto.utils;
|
15 |
|
15 |
|
16 |
import org.openconcerto.utils.CollectionMap2.Mode;
|
16 |
import org.openconcerto.utils.CollectionMap2.Mode;
|
17 |
import org.openconcerto.utils.OSFamily.Unix;
|
17 |
import org.openconcerto.utils.OSFamily.Unix;
|
18 |
import org.openconcerto.utils.ProcessStreams.Action;
|
18 |
import org.openconcerto.utils.ProcessStreams.Action;
|
19 |
import org.openconcerto.utils.StringUtils.Escaper;
|
19 |
import org.openconcerto.utils.StringUtils.Escaper;
|
20 |
import org.openconcerto.utils.cc.ExnTransformer;
|
20 |
import org.openconcerto.utils.cc.ExnTransformer;
|
21 |
import org.openconcerto.utils.cc.IClosure;
|
21 |
import org.openconcerto.utils.cc.IClosure;
|
22 |
|
22 |
|
23 |
import java.awt.Desktop;
|
23 |
import java.awt.Desktop;
|
24 |
import java.io.BufferedReader;
|
24 |
import java.io.BufferedReader;
|
25 |
import java.io.BufferedWriter;
|
25 |
import java.io.BufferedWriter;
|
26 |
import java.io.File;
|
26 |
import java.io.File;
|
27 |
import java.io.FileFilter;
|
27 |
import java.io.FileFilter;
|
28 |
import java.io.FileInputStream;
|
28 |
import java.io.FileInputStream;
|
29 |
import java.io.FileNotFoundException;
|
29 |
import java.io.FileNotFoundException;
|
30 |
import java.io.FileOutputStream;
|
30 |
import java.io.FileOutputStream;
|
31 |
import java.io.IOException;
|
31 |
import java.io.IOException;
|
32 |
import java.io.InputStream;
|
32 |
import java.io.InputStream;
|
33 |
import java.io.InputStreamReader;
|
33 |
import java.io.InputStreamReader;
|
34 |
import java.io.OutputStreamWriter;
|
34 |
import java.io.OutputStreamWriter;
|
35 |
import java.io.RandomAccessFile;
|
35 |
import java.io.RandomAccessFile;
|
36 |
import java.io.Reader;
|
36 |
import java.io.Reader;
|
37 |
import java.net.URI;
|
37 |
import java.net.URI;
|
38 |
import java.net.URL;
|
38 |
import java.net.URL;
|
39 |
import java.nio.channels.FileChannel;
|
39 |
import java.nio.channels.FileChannel;
|
40 |
import java.nio.charset.Charset;
|
40 |
import java.nio.charset.Charset;
|
- |
|
41 |
import java.nio.charset.StandardCharsets;
|
41 |
import java.nio.file.CopyOption;
|
42 |
import java.nio.file.CopyOption;
|
42 |
import java.nio.file.DirectoryStream;
|
43 |
import java.nio.file.DirectoryStream;
|
43 |
import java.nio.file.DirectoryStream.Filter;
|
44 |
import java.nio.file.DirectoryStream.Filter;
|
44 |
import java.nio.file.FileVisitResult;
|
45 |
import java.nio.file.FileVisitResult;
|
45 |
import java.nio.file.Files;
|
46 |
import java.nio.file.Files;
|
46 |
import java.nio.file.LinkOption;
|
47 |
import java.nio.file.LinkOption;
|
47 |
import java.nio.file.Path;
|
48 |
import java.nio.file.Path;
|
48 |
import java.nio.file.SimpleFileVisitor;
|
49 |
import java.nio.file.SimpleFileVisitor;
|
49 |
import java.nio.file.StandardCopyOption;
|
50 |
import java.nio.file.StandardCopyOption;
|
50 |
import java.nio.file.attribute.BasicFileAttributes;
|
51 |
import java.nio.file.attribute.BasicFileAttributes;
|
51 |
import java.nio.file.attribute.PosixFileAttributeView;
|
52 |
import java.nio.file.attribute.PosixFileAttributeView;
|
52 |
import java.nio.file.attribute.PosixFilePermissions;
|
53 |
import java.nio.file.attribute.PosixFilePermissions;
|
53 |
import java.util.ArrayList;
|
54 |
import java.util.ArrayList;
|
54 |
import java.util.Arrays;
|
55 |
import java.util.Arrays;
|
55 |
import java.util.Collection;
|
56 |
import java.util.Collection;
|
56 |
import java.util.Collections;
|
57 |
import java.util.Collections;
|
57 |
import java.util.EnumSet;
|
58 |
import java.util.EnumSet;
|
58 |
import java.util.HashMap;
|
59 |
import java.util.HashMap;
|
59 |
import java.util.HashSet;
|
60 |
import java.util.HashSet;
|
60 |
import java.util.List;
|
61 |
import java.util.List;
|
61 |
import java.util.Map;
|
62 |
import java.util.Map;
|
62 |
import java.util.Map.Entry;
|
63 |
import java.util.Map.Entry;
|
63 |
import java.util.Set;
|
64 |
import java.util.Set;
|
64 |
import java.util.logging.Level;
|
65 |
import java.util.logging.Level;
|
65 |
import java.util.regex.Pattern;
|
66 |
import java.util.regex.Pattern;
|
66 |
|
67 |
|
67 |
public final class FileUtils {
|
68 |
public final class FileUtils {
|
68 |
|
69 |
|
69 |
private FileUtils() {
|
70 |
private FileUtils() {
|
70 |
// all static
|
71 |
// all static
|
71 |
}
|
72 |
}
|
72 |
|
73 |
|
73 |
public static void browseFile(final File f) throws IOException {
|
74 |
public static void browseFile(final File f) throws IOException {
|
74 |
|
75 |
|
75 |
if (Desktop.isDesktopSupported()) {
|
76 |
if (Desktop.isDesktopSupported()) {
|
76 |
Desktop d = Desktop.getDesktop();
|
77 |
Desktop d = Desktop.getDesktop();
|
77 |
if (d.isSupported(Desktop.Action.BROWSE)) {
|
78 |
if (d.isSupported(Desktop.Action.BROWSE)) {
|
78 |
d.browse(f.getCanonicalFile().toURI());
|
79 |
d.browse(f.getCanonicalFile().toURI());
|
79 |
} else {
|
80 |
} else {
|
80 |
openNative(f);
|
81 |
openNative(f);
|
81 |
}
|
82 |
}
|
82 |
} else {
|
83 |
} else {
|
83 |
openNative(f);
|
84 |
openNative(f);
|
84 |
}
|
85 |
}
|
85 |
}
|
86 |
}
|
86 |
|
87 |
|
87 |
public static void browse(URI uri) throws Exception {
|
88 |
public static void browse(URI uri) throws Exception {
|
88 |
final boolean windows = System.getProperty("os.name").startsWith("Windows");
|
89 |
final boolean windows = System.getProperty("os.name").startsWith("Windows");
|
89 |
if (windows) {
|
90 |
if (windows) {
|
90 |
Desktop.getDesktop().browse(uri);
|
91 |
Desktop.getDesktop().browse(uri);
|
91 |
} else {
|
92 |
} else {
|
92 |
String[] cmdarray = new String[] { "xdg-open", uri.toString() };
|
93 |
String[] cmdarray = new String[] { "xdg-open", uri.toString() };
|
93 |
final int res = Runtime.getRuntime().exec(cmdarray).waitFor();
|
94 |
final int res = Runtime.getRuntime().exec(cmdarray).waitFor();
|
94 |
if (res != 0)
|
95 |
if (res != 0)
|
95 |
throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
|
96 |
throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
|
96 |
}
|
97 |
}
|
97 |
}
|
98 |
}
|
98 |
|
99 |
|
99 |
public static void openFile(File f) throws IOException {
|
100 |
public static void openFile(File f) throws IOException {
|
- |
|
101 |
if (!f.exists()) {
|
- |
|
102 |
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
|
- |
|
103 |
}
|
100 |
if (Desktop.isDesktopSupported()) {
|
104 |
if (Desktop.isDesktopSupported()) {
|
101 |
Desktop d = Desktop.getDesktop();
|
105 |
Desktop d = Desktop.getDesktop();
|
102 |
if (d.isSupported(Desktop.Action.OPEN)) {
|
106 |
if (d.isSupported(Desktop.Action.OPEN)) {
|
103 |
d.open(f.getCanonicalFile());
|
107 |
d.open(f.getCanonicalFile());
|
104 |
} else {
|
108 |
} else {
|
105 |
openNative(f);
|
109 |
openNative(f);
|
106 |
}
|
110 |
}
|
107 |
} else {
|
111 |
} else {
|
108 |
openNative(f);
|
112 |
openNative(f);
|
109 |
}
|
113 |
}
|
110 |
}
|
114 |
}
|
111 |
|
115 |
|
112 |
// immutable, getAbsoluteFile() required otherwise list() returns null
|
116 |
// immutable, getAbsoluteFile() required otherwise list() returns null
|
113 |
static private final File WD = new File("").getAbsoluteFile();
|
117 |
static private final File WD = new File("").getAbsoluteFile();
|
114 |
|
118 |
|
115 |
static public final File getWD() {
|
119 |
static public final File getWD() {
|
116 |
return WD;
|
120 |
return WD;
|
117 |
}
|
121 |
}
|
118 |
|
122 |
|
119 |
/**
|
123 |
/**
|
120 |
* All the files (see {@link File#isFile()}) contained in the passed dir.
|
124 |
* All the files (see {@link File#isFile()}) contained in the passed dir.
|
121 |
*
|
125 |
*
|
122 |
* @param dir the root directory to search.
|
126 |
* @param dir the root directory to search.
|
123 |
* @return a List of String.
|
127 |
* @return a List of String.
|
124 |
*/
|
128 |
*/
|
125 |
public static List<String> listR(File dir) {
|
129 |
public static List<String> listR(File dir) {
|
126 |
return listR(dir, REGULAR_FILE_FILTER);
|
130 |
return listR(dir, REGULAR_FILE_FILTER);
|
127 |
}
|
131 |
}
|
128 |
|
132 |
|
129 |
public static List<String> listR(File dir, FileFilter ff) {
|
133 |
public static List<String> listR(File dir, FileFilter ff) {
|
130 |
return listR_rec(dir, ff, ".");
|
134 |
return listR_rec(dir, ff, ".");
|
131 |
}
|
135 |
}
|
132 |
|
136 |
|
133 |
private static List<String> listR_rec(File dir, FileFilter ff, String prefix) {
|
137 |
private static List<String> listR_rec(File dir, FileFilter ff, String prefix) {
|
134 |
if (!dir.isDirectory())
|
138 |
if (!dir.isDirectory())
|
135 |
return null;
|
139 |
return null;
|
136 |
|
140 |
|
137 |
final List<String> res = new ArrayList<String>();
|
141 |
final List<String> res = new ArrayList<String>();
|
138 |
final File[] children = dir.listFiles();
|
142 |
final File[] children = dir.listFiles();
|
139 |
for (int i = 0; i < children.length; i++) {
|
143 |
for (int i = 0; i < children.length; i++) {
|
140 |
final String newPrefix = prefix + "/" + children[i].getName();
|
144 |
final String newPrefix = prefix + "/" + children[i].getName();
|
141 |
if (ff == null || ff.accept(children[i])) {
|
145 |
if (ff == null || ff.accept(children[i])) {
|
142 |
res.add(newPrefix);
|
146 |
res.add(newPrefix);
|
143 |
}
|
147 |
}
|
144 |
if (children[i].isDirectory()) {
|
148 |
if (children[i].isDirectory()) {
|
145 |
res.addAll(listR_rec(children[i], ff, newPrefix));
|
149 |
res.addAll(listR_rec(children[i], ff, newPrefix));
|
146 |
}
|
150 |
}
|
147 |
}
|
151 |
}
|
148 |
return res;
|
152 |
return res;
|
149 |
}
|
153 |
}
|
150 |
|
154 |
|
151 |
public static void walk(File dir, IClosure<File> c) {
|
155 |
public static void walk(File dir, IClosure<File> c) {
|
152 |
walk(dir, c, RecursionType.BREADTH_FIRST);
|
156 |
walk(dir, c, RecursionType.BREADTH_FIRST);
|
153 |
}
|
157 |
}
|
154 |
|
158 |
|
155 |
public static void walk(File dir, IClosure<File> c, RecursionType type) {
|
159 |
public static void walk(File dir, IClosure<File> c, RecursionType type) {
|
156 |
if (type == RecursionType.BREADTH_FIRST)
|
160 |
if (type == RecursionType.BREADTH_FIRST)
|
157 |
c.executeChecked(dir);
|
161 |
c.executeChecked(dir);
|
158 |
if (dir.isDirectory()) {
|
162 |
if (dir.isDirectory()) {
|
159 |
for (final File child : dir.listFiles()) {
|
163 |
for (final File child : dir.listFiles()) {
|
160 |
walk(child, c, type);
|
164 |
walk(child, c, type);
|
161 |
}
|
165 |
}
|
162 |
}
|
166 |
}
|
163 |
if (type == RecursionType.DEPTH_FIRST)
|
167 |
if (type == RecursionType.DEPTH_FIRST)
|
164 |
c.executeChecked(dir);
|
168 |
c.executeChecked(dir);
|
165 |
}
|
169 |
}
|
166 |
|
170 |
|
167 |
public static final List<File> list(File root, final int depth) {
|
171 |
public static final List<File> list(File root, final int depth) {
|
168 |
return list(root, depth, null);
|
172 |
return list(root, depth, null);
|
169 |
}
|
173 |
}
|
170 |
|
174 |
|
171 |
/**
|
175 |
/**
|
172 |
* Finds all files at the specified depth below <code>root</code>.
|
176 |
* Finds all files at the specified depth below <code>root</code>.
|
173 |
*
|
177 |
*
|
174 |
* @param root the base directory
|
178 |
* @param root the base directory
|
175 |
* @param depth the depth of the returned files.
|
179 |
* @param depth the depth of the returned files.
|
176 |
* @param ff a filter, can be <code>null</code>.
|
180 |
* @param ff a filter, can be <code>null</code>.
|
177 |
* @return a list of files <code>depth</code> levels beneath <code>root</code>.
|
181 |
* @return a list of files <code>depth</code> levels beneath <code>root</code>.
|
178 |
*/
|
182 |
*/
|
179 |
public static final List<File> list(File root, final int depth, final FileFilter ff) {
|
183 |
public static final List<File> list(File root, final int depth, final FileFilter ff) {
|
180 |
return list(root, depth, depth, ff);
|
184 |
return list(root, depth, depth, ff);
|
181 |
}
|
185 |
}
|
182 |
|
186 |
|
183 |
public static final List<File> list(final File root, final int minDepth, final int maxDepth, final FileFilter ff) {
|
187 |
public static final List<File> list(final File root, final int minDepth, final int maxDepth, final FileFilter ff) {
|
184 |
return list(root, minDepth, maxDepth, ff, false);
|
188 |
return list(root, minDepth, maxDepth, ff, false);
|
185 |
}
|
189 |
}
|
186 |
|
190 |
|
187 |
public static final List<File> list(final File root, final int minDepth, final int maxDepth, final FileFilter ff, final boolean sort) {
|
191 |
public static final List<File> list(final File root, final int minDepth, final int maxDepth, final FileFilter ff, final boolean sort) {
|
188 |
if (minDepth > maxDepth)
|
192 |
if (minDepth > maxDepth)
|
189 |
throw new IllegalArgumentException(minDepth + " > " + maxDepth);
|
193 |
throw new IllegalArgumentException(minDepth + " > " + maxDepth);
|
190 |
if (maxDepth < 0)
|
194 |
if (maxDepth < 0)
|
191 |
throw new IllegalArgumentException(maxDepth + " < 0");
|
195 |
throw new IllegalArgumentException(maxDepth + " < 0");
|
192 |
if (!root.exists())
|
196 |
if (!root.exists())
|
193 |
return Collections.<File> emptyList();
|
197 |
return Collections.<File> emptyList();
|
194 |
|
198 |
|
195 |
final File currentFile = accept(ff, minDepth, maxDepth, root, 0) ? root : null;
|
199 |
final File currentFile = accept(ff, minDepth, maxDepth, root, 0) ? root : null;
|
196 |
if (maxDepth == 0) {
|
200 |
if (maxDepth == 0) {
|
197 |
return currentFile == null ? Collections.<File> emptyList() : Collections.singletonList(currentFile);
|
201 |
return currentFile == null ? Collections.<File> emptyList() : Collections.singletonList(currentFile);
|
198 |
} else {
|
202 |
} else {
|
199 |
final List<File> res = new ArrayList<File>();
|
203 |
final List<File> res = new ArrayList<File>();
|
200 |
final File[] children = root.listFiles();
|
204 |
final File[] children = root.listFiles();
|
201 |
if (children == null)
|
205 |
if (children == null)
|
202 |
throw new IllegalStateException("cannot list " + root);
|
206 |
throw new IllegalStateException("cannot list " + root);
|
203 |
if (sort)
|
207 |
if (sort)
|
204 |
Arrays.sort(children);
|
208 |
Arrays.sort(children);
|
205 |
for (final File child : children) {
|
209 |
for (final File child : children) {
|
206 |
if (maxDepth > 1 && child.isDirectory()) {
|
210 |
if (maxDepth > 1 && child.isDirectory()) {
|
207 |
res.addAll(list(child, minDepth - 1, maxDepth - 1, ff, sort));
|
211 |
res.addAll(list(child, minDepth - 1, maxDepth - 1, ff, sort));
|
208 |
} else if (accept(ff, minDepth, maxDepth, child, 1)) {
|
212 |
} else if (accept(ff, minDepth, maxDepth, child, 1)) {
|
209 |
res.add(child);
|
213 |
res.add(child);
|
210 |
}
|
214 |
}
|
211 |
}
|
215 |
}
|
212 |
if (currentFile != null)
|
216 |
if (currentFile != null)
|
213 |
res.add(currentFile);
|
217 |
res.add(currentFile);
|
214 |
return res;
|
218 |
return res;
|
215 |
}
|
219 |
}
|
216 |
}
|
220 |
}
|
217 |
|
221 |
|
218 |
private static final boolean accept(final FileFilter ff, final int minDepth, final int maxDepth, final File f, final int depth) {
|
222 |
private static final boolean accept(final FileFilter ff, final int minDepth, final int maxDepth, final File f, final int depth) {
|
219 |
return minDepth <= depth && depth <= maxDepth && (ff == null || ff.accept(f));
|
223 |
return minDepth <= depth && depth <= maxDepth && (ff == null || ff.accept(f));
|
220 |
}
|
224 |
}
|
221 |
|
225 |
|
222 |
/**
|
226 |
/**
|
223 |
* Returns the relative path from one file to another in the same filesystem tree. Files are not
|
227 |
* Returns the relative path from one file to another in the same filesystem tree. Files are not
|
224 |
* required to exist, see {@link File#getCanonicalPath()}.
|
228 |
* required to exist, see {@link File#getCanonicalPath()}.
|
225 |
*
|
229 |
*
|
226 |
* @param fromDir the starting directory, eg /a/b/.
|
230 |
* @param fromDir the starting directory, eg /a/b/.
|
227 |
* @param to the file to get to, eg /a/x/y.txt.
|
231 |
* @param to the file to get to, eg /a/x/y.txt.
|
228 |
* @return the relative path, eg "../x/y.txt".
|
232 |
* @return the relative path, eg "../x/y.txt".
|
229 |
* @throws IOException if an error occurs while canonicalizing the files.
|
233 |
* @throws IOException if an error occurs while canonicalizing the files.
|
230 |
* @throws IllegalArgumentException if fromDir exists and is not directory.
|
234 |
* @throws IllegalArgumentException if fromDir exists and is not directory.
|
231 |
*/
|
235 |
*/
|
232 |
public static final String relative(File fromDir, File to) throws IOException {
|
236 |
public static final String relative(File fromDir, File to) throws IOException {
|
233 |
if (fromDir.exists() && !fromDir.isDirectory())
|
237 |
if (fromDir.exists() && !fromDir.isDirectory())
|
234 |
throw new IllegalArgumentException(fromDir + " is not a directory");
|
238 |
throw new IllegalArgumentException(fromDir + " is not a directory");
|
235 |
|
239 |
|
236 |
final File fromF = fromDir.getCanonicalFile();
|
240 |
final File fromF = fromDir.getCanonicalFile();
|
237 |
final File toF = to.getCanonicalFile();
|
241 |
final File toF = to.getCanonicalFile();
|
238 |
final List<File> toPath = getAncestors(toF);
|
242 |
final List<File> toPath = getAncestors(toF);
|
239 |
final List<File> fromPath = getAncestors(fromF);
|
243 |
final List<File> fromPath = getAncestors(fromF);
|
240 |
|
244 |
|
241 |
// no common ancestor (for example on Windows on 2 different letters)
|
245 |
// no common ancestor (for example on Windows on 2 different letters)
|
242 |
if (!toPath.get(0).equals(fromPath.get(0))) {
|
246 |
if (!toPath.get(0).equals(fromPath.get(0))) {
|
243 |
// already canonical
|
247 |
// already canonical
|
244 |
return toF.getPath();
|
248 |
return toF.getPath();
|
245 |
}
|
249 |
}
|
246 |
|
250 |
|
247 |
int commonIndex = Math.min(toPath.size(), fromPath.size()) - 1;
|
251 |
int commonIndex = Math.min(toPath.size(), fromPath.size()) - 1;
|
248 |
boolean found = false;
|
252 |
boolean found = false;
|
249 |
while (commonIndex >= 0 && !found) {
|
253 |
while (commonIndex >= 0 && !found) {
|
250 |
found = fromPath.get(commonIndex).equals(toPath.get(commonIndex));
|
254 |
found = fromPath.get(commonIndex).equals(toPath.get(commonIndex));
|
251 |
if (!found)
|
255 |
if (!found)
|
252 |
commonIndex--;
|
256 |
commonIndex--;
|
253 |
}
|
257 |
}
|
254 |
|
258 |
|
255 |
// on remonte jusqu'à l'ancêtre commun
|
259 |
// on remonte jusqu'à l'ancêtre commun
|
256 |
final List<String> complete = new ArrayList<String>(Collections.nCopies(fromPath.size() - 1 - commonIndex, ".."));
|
260 |
final List<String> complete = new ArrayList<String>(Collections.nCopies(fromPath.size() - 1 - commonIndex, ".."));
|
257 |
if (complete.isEmpty())
|
261 |
if (complete.isEmpty())
|
258 |
complete.add(".");
|
262 |
complete.add(".");
|
259 |
// puis on descend vers 'to'
|
263 |
// puis on descend vers 'to'
|
260 |
for (File f : toPath.subList(commonIndex + 1, toPath.size())) {
|
264 |
for (File f : toPath.subList(commonIndex + 1, toPath.size())) {
|
261 |
complete.add(f.getName());
|
265 |
complete.add(f.getName());
|
262 |
}
|
266 |
}
|
263 |
|
267 |
|
264 |
return CollectionUtils.join(complete, File.separator);
|
268 |
return CollectionUtils.join(complete, File.separator);
|
265 |
}
|
269 |
}
|
266 |
|
270 |
|
267 |
// return each ancestor of f (including itself)
|
271 |
// return each ancestor of f (including itself)
|
268 |
// eg [/, /folder, /folder/dir] for /folder/dir
|
272 |
// eg [/, /folder, /folder/dir] for /folder/dir
|
269 |
public final static List<File> getAncestors(File f) {
|
273 |
public final static List<File> getAncestors(File f) {
|
270 |
final List<File> path = new ArrayList<File>();
|
274 |
final List<File> path = new ArrayList<File>();
|
271 |
File currentF = f;
|
275 |
File currentF = f;
|
272 |
while (currentF != null) {
|
276 |
while (currentF != null) {
|
273 |
path.add(0, currentF);
|
277 |
path.add(0, currentF);
|
274 |
currentF = currentF.getParentFile();
|
278 |
currentF = currentF.getParentFile();
|
275 |
}
|
279 |
}
|
276 |
return path;
|
280 |
return path;
|
277 |
}
|
281 |
}
|
278 |
|
282 |
|
279 |
public final static File addSuffix(File f, String suffix) {
|
283 |
public final static File addSuffix(File f, String suffix) {
|
280 |
return new File(f.getParentFile(), f.getName() + suffix);
|
284 |
return new File(f.getParentFile(), f.getName() + suffix);
|
281 |
}
|
285 |
}
|
282 |
|
286 |
|
283 |
/**
|
287 |
/**
|
284 |
* Prepend a string to a suffix.
|
288 |
* Prepend a string to a suffix.
|
285 |
*
|
289 |
*
|
286 |
* @param f the file, e.g. "sample.xml".
|
290 |
* @param f the file, e.g. "sample.xml".
|
287 |
* @param toInsert the string to insert in the filename, e.g. "-sql".
|
291 |
* @param toInsert the string to insert in the filename, e.g. "-sql".
|
288 |
* @param suffix the suffix of <code>f</code>, e.g. ".xml".
|
292 |
* @param suffix the suffix of <code>f</code>, e.g. ".xml".
|
289 |
* @return a new file with <code>toInsert</code> prepended to <code>suffix</code>, e.g.
|
293 |
* @return a new file with <code>toInsert</code> prepended to <code>suffix</code>, e.g.
|
290 |
* "sample-sql.xml".
|
294 |
* "sample-sql.xml".
|
291 |
*/
|
295 |
*/
|
292 |
public final static File prependSuffix(File f, String toInsert, String suffix) {
|
296 |
public final static File prependSuffix(File f, String toInsert, String suffix) {
|
293 |
return new File(f.getParentFile(), removeSuffix(f.getName(), suffix) + toInsert + suffix);
|
297 |
return new File(f.getParentFile(), removeSuffix(f.getName(), suffix) + toInsert + suffix);
|
294 |
}
|
298 |
}
|
295 |
|
299 |
|
296 |
/**
|
300 |
/**
|
297 |
* Prepend a string to a suffix.
|
301 |
* Prepend a string to a suffix.
|
298 |
*
|
302 |
*
|
299 |
* @param f the file, e.g. "sample.xml".
|
303 |
* @param f the file, e.g. "sample.xml".
|
300 |
* @param toInsert the string to insert in the filename, e.g. "-sql".
|
304 |
* @param toInsert the string to insert in the filename, e.g. "-sql".
|
301 |
* @param suffix the suffix of <code>f</code>, e.g. ".xml".
|
305 |
* @param suffix the suffix of <code>f</code>, e.g. ".xml".
|
302 |
* @return a new file with <code>toInsert</code> prepended to <code>suffix</code>, e.g.
|
306 |
* @return a new file with <code>toInsert</code> prepended to <code>suffix</code>, e.g.
|
303 |
* "sample-sql.xml".
|
307 |
* "sample-sql.xml".
|
304 |
*/
|
308 |
*/
|
305 |
public final static Path prependSuffix(Path f, String toInsert, String suffix) {
|
309 |
public final static Path prependSuffix(Path f, String toInsert, String suffix) {
|
306 |
return f.resolveSibling(removeSuffix(f.getFileName().toString(), suffix) + toInsert + suffix);
|
310 |
return f.resolveSibling(removeSuffix(f.getFileName().toString(), suffix) + toInsert + suffix);
|
307 |
}
|
311 |
}
|
308 |
|
312 |
|
309 |
public final static String removeSuffix(String name, String suffix) {
|
313 |
public final static String removeSuffix(String name, String suffix) {
|
310 |
return name.endsWith(suffix) ? name.substring(0, name.length() - suffix.length()) : name;
|
314 |
return name.endsWith(suffix) ? name.substring(0, name.length() - suffix.length()) : name;
|
311 |
}
|
315 |
}
|
312 |
|
316 |
|
313 |
/**
|
317 |
/**
|
314 |
* Rename a file if necessary by finding a free name. The tested names are
|
318 |
* Rename a file if necessary by finding a free name. The tested names are
|
315 |
* <code>name + "_" + i + suffix</code>.
|
319 |
* <code>name + "_" + i + suffix</code>.
|
316 |
*
|
320 |
*
|
317 |
* @param parent the directory.
|
321 |
* @param parent the directory.
|
318 |
* @param name the base name of the file.
|
322 |
* @param name the base name of the file.
|
319 |
* @param suffix the suffix of the file, e.g. ".ods".
|
323 |
* @param suffix the suffix of the file, e.g. ".ods".
|
320 |
* @return <code>new File(parent, name + suffix)</code> (always non existing) and the new file,
|
324 |
* @return <code>new File(parent, name + suffix)</code> (always non existing) and the new file,
|
321 |
* (or <code>null</code> if no file was moved).
|
325 |
* (or <code>null</code> if no file was moved).
|
322 |
*/
|
326 |
*/
|
323 |
public final static File[] mvOut(final File parent, final String name, final String suffix) {
|
327 |
public final static File[] mvOut(final File parent, final String name, final String suffix) {
|
324 |
final File fDest = new File(parent, name + suffix);
|
328 |
final File fDest = new File(parent, name + suffix);
|
325 |
final File renamed;
|
329 |
final File renamed;
|
326 |
if (fDest.exists()) {
|
330 |
if (fDest.exists()) {
|
327 |
int i = 0;
|
331 |
int i = 0;
|
328 |
File free = fDest;
|
332 |
File free = fDest;
|
329 |
while (free.exists()) {
|
333 |
while (free.exists()) {
|
330 |
free = new File(parent, name + "_" + i + suffix);
|
334 |
free = new File(parent, name + "_" + i + suffix);
|
331 |
i++;
|
335 |
i++;
|
332 |
}
|
336 |
}
|
333 |
assert !fDest.equals(free);
|
337 |
assert !fDest.equals(free);
|
334 |
if (!fDest.renameTo(free))
|
338 |
if (!fDest.renameTo(free))
|
335 |
throw new IllegalStateException("Couldn't rename " + fDest + " to " + free);
|
339 |
throw new IllegalStateException("Couldn't rename " + fDest + " to " + free);
|
336 |
renamed = free;
|
340 |
renamed = free;
|
337 |
} else {
|
341 |
} else {
|
338 |
renamed = null;
|
342 |
renamed = null;
|
339 |
}
|
343 |
}
|
340 |
assert !fDest.exists();
|
344 |
assert !fDest.exists();
|
341 |
return new File[] { fDest, renamed };
|
345 |
return new File[] { fDest, renamed };
|
342 |
}
|
346 |
}
|
343 |
|
347 |
|
344 |
// ** shell
|
348 |
// ** shell
|
345 |
|
349 |
|
346 |
/**
|
350 |
/**
|
347 |
* Behave like the 'mv' unix utility, ie handle cross filesystems mv and <code>dest</code> being
|
351 |
* Behave like the 'mv' unix utility, ie handle cross filesystems mv and <code>dest</code> being
|
348 |
* a directory.
|
352 |
* a directory.
|
349 |
*
|
353 |
*
|
350 |
* @param f the source file.
|
354 |
* @param f the source file.
|
351 |
* @param dest the destination file or directory.
|
355 |
* @param dest the destination file or directory.
|
352 |
* @return the error or <code>null</code> if there was none.
|
356 |
* @return the error or <code>null</code> if there was none.
|
353 |
*/
|
357 |
*/
|
354 |
public static String mv(File f, File dest) {
|
358 |
public static String mv(File f, File dest) {
|
355 |
final File canonF;
|
359 |
final File canonF;
|
356 |
File canonDest;
|
360 |
File canonDest;
|
357 |
try {
|
361 |
try {
|
358 |
canonF = f.getCanonicalFile();
|
362 |
canonF = f.getCanonicalFile();
|
359 |
canonDest = dest.getCanonicalFile();
|
363 |
canonDest = dest.getCanonicalFile();
|
360 |
} catch (IOException e) {
|
364 |
} catch (IOException e) {
|
361 |
return ExceptionUtils.getStackTrace(e);
|
365 |
return ExceptionUtils.getStackTrace(e);
|
362 |
}
|
366 |
}
|
363 |
if (canonF.equals(canonDest))
|
367 |
if (canonF.equals(canonDest))
|
364 |
// nothing to do
|
368 |
// nothing to do
|
365 |
return null;
|
369 |
return null;
|
366 |
if (canonDest.isDirectory())
|
370 |
if (canonDest.isDirectory())
|
367 |
canonDest = new File(canonDest, canonF.getName());
|
371 |
canonDest = new File(canonDest, canonF.getName());
|
368 |
|
372 |
|
369 |
final File destF;
|
373 |
final File destF;
|
370 |
if (canonDest.exists())
|
374 |
if (canonDest.exists())
|
371 |
return canonDest + " exists";
|
375 |
return canonDest + " exists";
|
372 |
else if (!canonDest.getParentFile().exists())
|
376 |
else if (!canonDest.getParentFile().exists())
|
373 |
return "parent of " + canonDest + " does not exist";
|
377 |
return "parent of " + canonDest + " does not exist";
|
374 |
else
|
378 |
else
|
375 |
destF = canonDest;
|
379 |
destF = canonDest;
|
376 |
if (!canonF.renameTo(destF)) {
|
380 |
if (!canonF.renameTo(destF)) {
|
377 |
try {
|
381 |
try {
|
378 |
copyDirectory(canonF, destF);
|
382 |
copyDirectory(canonF, destF);
|
379 |
if (destF.exists())
|
383 |
if (destF.exists())
|
380 |
rm_R(canonF);
|
384 |
rm_R(canonF);
|
381 |
} catch (IOException e) {
|
385 |
} catch (IOException e) {
|
382 |
return ExceptionUtils.getStackTrace(e);
|
386 |
return ExceptionUtils.getStackTrace(e);
|
383 |
}
|
387 |
}
|
384 |
}
|
388 |
}
|
385 |
return null;
|
389 |
return null;
|
386 |
}
|
390 |
}
|
387 |
|
391 |
|
388 |
// transferTo() can be limited by a number of factors, like the number of bits of the system
|
392 |
// transferTo() can be limited by a number of factors, like the number of bits of the system
|
389 |
// if mmap is used (e.g. on Linux) or by an arbitrary magic number on Windows : 64Mb - 32Kb
|
393 |
// if mmap is used (e.g. on Linux) or by an arbitrary magic number on Windows : 64Mb - 32Kb
|
390 |
private static final int CHANNEL_MAX_COUNT = Math.min(64 * 1024 * 1024 - 32 * 1024, Integer.MAX_VALUE);
|
394 |
private static final int CHANNEL_MAX_COUNT = Math.min(64 * 1024 * 1024 - 32 * 1024, Integer.MAX_VALUE);
|
391 |
|
395 |
|
392 |
public static void copyFile(File in, File out) throws IOException {
|
396 |
public static void copyFile(File in, File out) throws IOException {
|
393 |
copyFile(in, out, CHANNEL_MAX_COUNT);
|
397 |
copyFile(in, out, CHANNEL_MAX_COUNT);
|
394 |
}
|
398 |
}
|
395 |
|
399 |
|
396 |
/**
|
400 |
/**
|
397 |
* Copy a file. It is generally not advised to use 0 for <code>maxCount</code> since various
|
401 |
* Copy a file. It is generally not advised to use 0 for <code>maxCount</code> since various
|
398 |
* implementations have size limitations, see {@link #copyFile(File, File)}.
|
402 |
* implementations have size limitations, see {@link #copyFile(File, File)}.
|
399 |
*
|
403 |
*
|
400 |
* @param in the source file.
|
404 |
* @param in the source file.
|
401 |
* @param out the destination file.
|
405 |
* @param out the destination file.
|
402 |
* @param maxCount the number of bytes to copy at a time, 0 meaning size of <code>in</code>.
|
406 |
* @param maxCount the number of bytes to copy at a time, 0 meaning size of <code>in</code>.
|
403 |
* @throws IOException if an error occurs.
|
407 |
* @throws IOException if an error occurs.
|
404 |
*/
|
408 |
*/
|
405 |
public static void copyFile(File in, File out, long maxCount) throws IOException {
|
409 |
public static void copyFile(File in, File out, long maxCount) throws IOException {
|
406 |
try (final FileInputStream sourceIn = new FileInputStream(in); final FileOutputStream sourceOut = new FileOutputStream(out);) {
|
410 |
try (final FileInputStream sourceIn = new FileInputStream(in); final FileOutputStream sourceOut = new FileOutputStream(out);) {
|
407 |
final FileChannel sourceChannel = sourceIn.getChannel();
|
411 |
final FileChannel sourceChannel = sourceIn.getChannel();
|
408 |
final long size = sourceChannel.size();
|
412 |
final long size = sourceChannel.size();
|
409 |
if (maxCount == 0)
|
413 |
if (maxCount == 0)
|
410 |
maxCount = size;
|
414 |
maxCount = size;
|
411 |
final FileChannel destinationChannel = sourceOut.getChannel();
|
415 |
final FileChannel destinationChannel = sourceOut.getChannel();
|
412 |
long position = 0;
|
416 |
long position = 0;
|
413 |
while (position < size) {
|
417 |
while (position < size) {
|
414 |
position += sourceChannel.transferTo(position, maxCount, destinationChannel);
|
418 |
position += sourceChannel.transferTo(position, maxCount, destinationChannel);
|
415 |
}
|
419 |
}
|
416 |
}
|
420 |
}
|
417 |
}
|
421 |
}
|
418 |
|
422 |
|
419 |
public static void copyFile(File in, File out, final boolean useTime) throws IOException {
|
423 |
public static void copyFile(File in, File out, final boolean useTime) throws IOException {
|
420 |
if (!useTime || in.lastModified() != out.lastModified()) {
|
424 |
if (!useTime || in.lastModified() != out.lastModified()) {
|
421 |
copyFile(in, out);
|
425 |
copyFile(in, out);
|
422 |
if (useTime)
|
426 |
if (useTime)
|
423 |
out.setLastModified(in.lastModified());
|
427 |
out.setLastModified(in.lastModified());
|
424 |
}
|
428 |
}
|
425 |
}
|
429 |
}
|
426 |
|
430 |
|
427 |
public static void copyDirectory(File in, File out) throws IOException {
|
431 |
public static void copyDirectory(File in, File out) throws IOException {
|
428 |
copyDirectory(in, out, Collections.<String> emptySet());
|
432 |
copyDirectory(in, out, Collections.<String> emptySet());
|
429 |
}
|
433 |
}
|
430 |
|
434 |
|
431 |
public static final Set<String> VersionControl = CollectionUtils.createSet(".svn", "CVS");
|
435 |
public static final Set<String> VersionControl = CollectionUtils.createSet(".svn", "CVS");
|
432 |
|
436 |
|
433 |
public static void copyDirectory(File in, File out, final Set<String> toIgnore) throws IOException {
|
437 |
public static void copyDirectory(File in, File out, final Set<String> toIgnore) throws IOException {
|
434 |
copyDirectory(in, out, toIgnore, false);
|
438 |
copyDirectory(in, out, toIgnore, false);
|
435 |
}
|
439 |
}
|
436 |
|
440 |
|
437 |
public static void copyDirectory(File in, File out, final Set<String> toIgnore, final boolean useTime) throws IOException {
|
441 |
public static void copyDirectory(File in, File out, final Set<String> toIgnore, final boolean useTime) throws IOException {
|
438 |
if (toIgnore.contains(in.getName()))
|
442 |
if (toIgnore.contains(in.getName()))
|
439 |
return;
|
443 |
return;
|
440 |
|
444 |
|
441 |
if (in.isDirectory()) {
|
445 |
if (in.isDirectory()) {
|
442 |
if (!out.exists()) {
|
446 |
if (!out.exists()) {
|
443 |
out.mkdir();
|
447 |
out.mkdir();
|
444 |
}
|
448 |
}
|
445 |
|
449 |
|
446 |
String[] children = in.list();
|
450 |
String[] children = in.list();
|
447 |
for (int i = 0; i < children.length; i++) {
|
451 |
for (int i = 0; i < children.length; i++) {
|
448 |
copyDirectory(new File(in, children[i]), new File(out, children[i]), toIgnore, useTime);
|
452 |
copyDirectory(new File(in, children[i]), new File(out, children[i]), toIgnore, useTime);
|
449 |
}
|
453 |
}
|
450 |
} else {
|
454 |
} else {
|
451 |
if (!in.getName().equals("Thumbs.db")) {
|
455 |
if (!in.getName().equals("Thumbs.db")) {
|
452 |
copyFile(in, out, useTime);
|
456 |
copyFile(in, out, useTime);
|
453 |
}
|
457 |
}
|
454 |
}
|
458 |
}
|
455 |
}
|
459 |
}
|
456 |
|
460 |
|
457 |
public static void copyDirectory(Path in, Path out) throws IOException {
|
461 |
public static void copyDirectory(Path in, Path out) throws IOException {
|
458 |
copyDirectory(in, out, false);
|
462 |
copyDirectory(in, out, false);
|
459 |
}
|
463 |
}
|
460 |
|
464 |
|
461 |
public static void copyDirectory(Path in, Path out, final boolean hardLink, final CopyOption... copyOptions) throws IOException {
|
465 |
public static void copyDirectory(Path in, Path out, final boolean hardLink, final CopyOption... copyOptions) throws IOException {
|
462 |
copyDirectory(in, out, Collections.<String> emptySet(), hardLink, false, Arrays.asList(copyOptions));
|
466 |
copyDirectory(in, out, Collections.<String> emptySet(), hardLink, false, Arrays.asList(copyOptions));
|
463 |
}
|
467 |
}
|
464 |
|
468 |
|
465 |
public static void copyDirectory(Path in, Path out, final Set<String> toIgnore, final boolean hardLink, final boolean followLinks, final List<CopyOption> copyOptions) throws IOException {
|
469 |
public static void copyDirectory(Path in, Path out, final Set<String> toIgnore, final boolean hardLink, final boolean followLinks, final List<CopyOption> copyOptions) throws IOException {
|
466 |
final LinkOption[] isDirOptions = followLinks ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
470 |
final LinkOption[] isDirOptions = followLinks ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
467 |
final Set<CopyOption> copyOptionsP = new HashSet<>(copyOptions);
|
471 |
final Set<CopyOption> copyOptionsP = new HashSet<>(copyOptions);
|
468 |
if (followLinks) {
|
472 |
if (followLinks) {
|
469 |
copyOptionsP.remove(LinkOption.NOFOLLOW_LINKS);
|
473 |
copyOptionsP.remove(LinkOption.NOFOLLOW_LINKS);
|
470 |
} else {
|
474 |
} else {
|
471 |
copyOptionsP.add(LinkOption.NOFOLLOW_LINKS);
|
475 |
copyOptionsP.add(LinkOption.NOFOLLOW_LINKS);
|
472 |
}
|
476 |
}
|
473 |
copyDirectory(in, out, toIgnore, isDirOptions, copyOptionsP.toArray(new CopyOption[copyOptionsP.size()]), hardLink);
|
477 |
copyDirectory(in, out, toIgnore, isDirOptions, copyOptionsP.toArray(new CopyOption[copyOptionsP.size()]), hardLink);
|
474 |
}
|
478 |
}
|
475 |
|
479 |
|
476 |
public static void copyDirectory(Path in, Path out, final Set<String> toIgnore, final LinkOption[] isDirOptions, final CopyOption[] copyOptions, final boolean hardLink) throws IOException {
|
480 |
public static void copyDirectory(Path in, Path out, final Set<String> toIgnore, final LinkOption[] isDirOptions, final CopyOption[] copyOptions, final boolean hardLink) throws IOException {
|
477 |
if (toIgnore.contains(in.getFileName().toString()))
|
481 |
if (toIgnore.contains(in.getFileName().toString()))
|
478 |
return;
|
482 |
return;
|
479 |
|
483 |
|
480 |
if (Files.isDirectory(in, isDirOptions)) {
|
484 |
if (Files.isDirectory(in, isDirOptions)) {
|
481 |
Files.copy(in, out, copyOptions);
|
485 |
Files.copy(in, out, copyOptions);
|
482 |
|
486 |
|
483 |
try (final DirectoryStream<Path> dirStream = Files.newDirectoryStream(in)) {
|
487 |
try (final DirectoryStream<Path> dirStream = Files.newDirectoryStream(in)) {
|
484 |
for (final Path child : dirStream) {
|
488 |
for (final Path child : dirStream) {
|
485 |
copyDirectory(child, out.resolve(child.getFileName()), toIgnore, isDirOptions, copyOptions, hardLink);
|
489 |
copyDirectory(child, out.resolve(child.getFileName()), toIgnore, isDirOptions, copyOptions, hardLink);
|
486 |
}
|
490 |
}
|
487 |
}
|
491 |
}
|
488 |
// fix up modification time of directory when done
|
492 |
// fix up modification time of directory when done
|
489 |
// (other attributes have already been copied at creation time)
|
493 |
// (other attributes have already been copied at creation time)
|
490 |
if (Arrays.asList(copyOptions).contains(StandardCopyOption.COPY_ATTRIBUTES))
|
494 |
if (Arrays.asList(copyOptions).contains(StandardCopyOption.COPY_ATTRIBUTES))
|
491 |
Files.setLastModifiedTime(out, Files.getLastModifiedTime(in));
|
495 |
Files.setLastModifiedTime(out, Files.getLastModifiedTime(in));
|
492 |
} else {
|
496 |
} else {
|
493 |
if (!in.getFileName().toString().equals("Thumbs.db")) {
|
497 |
if (!in.getFileName().toString().equals("Thumbs.db")) {
|
494 |
if (hardLink)
|
498 |
if (hardLink)
|
495 |
Files.createLink(out, in);
|
499 |
Files.createLink(out, in);
|
496 |
else
|
500 |
else
|
497 |
Files.copy(in, out, copyOptions);
|
501 |
Files.copy(in, out, copyOptions);
|
498 |
}
|
502 |
}
|
499 |
}
|
503 |
}
|
500 |
}
|
504 |
}
|
501 |
|
505 |
|
502 |
/**
|
506 |
/**
|
503 |
* Delete recursively the passed directory. If a deletion fails, the method stops attempting to
|
507 |
* Delete recursively the passed directory. If a deletion fails, the method stops attempting to
|
504 |
* delete and returns false.
|
508 |
* delete and returns false.
|
505 |
*
|
509 |
*
|
506 |
* @param dir the dir to be deleted.
|
510 |
* @param dir the dir to be deleted.
|
507 |
* @return <code>true</code> if all deletions were successful.
|
511 |
* @return <code>true</code> if all deletions were successful.
|
508 |
* @deprecated callers often forget to check return value, see also {@link #rm_R(Path)}
|
512 |
* @deprecated callers often forget to check return value, see also {@link #rm_R(Path)}
|
509 |
*/
|
513 |
*/
|
510 |
public static boolean rmR(File dir) {
|
514 |
public static boolean rmR(File dir) {
|
511 |
if (dir.isDirectory()) {
|
515 |
if (dir.isDirectory()) {
|
512 |
File[] children = dir.listFiles();
|
516 |
File[] children = dir.listFiles();
|
513 |
for (int i = 0; i < children.length; i++) {
|
517 |
for (int i = 0; i < children.length; i++) {
|
514 |
boolean success = rmR(children[i]);
|
518 |
boolean success = rmR(children[i]);
|
515 |
|
519 |
|
516 |
if (!success) {
|
520 |
if (!success) {
|
517 |
|
521 |
|
518 |
return false;
|
522 |
return false;
|
519 |
}
|
523 |
}
|
520 |
}
|
524 |
}
|
521 |
}
|
525 |
}
|
522 |
|
526 |
|
523 |
// The directory is now empty so delete it
|
527 |
// The directory is now empty so delete it
|
524 |
return dir.delete();
|
528 |
return dir.delete();
|
525 |
}
|
529 |
}
|
526 |
|
530 |
|
527 |
public static void rm_R(File dir) throws IOException {
|
531 |
public static void rm_R(File dir) throws IOException {
|
528 |
rm_R(dir.toPath());
|
532 |
rm_R(dir.toPath());
|
529 |
}
|
533 |
}
|
530 |
|
534 |
|
531 |
public static void rm_R(Path dir) throws IOException {
|
535 |
public static void rm_R(Path dir) throws IOException {
|
532 |
rm_R(dir, false);
|
536 |
rm_R(dir, false);
|
533 |
}
|
537 |
}
|
534 |
|
538 |
|
535 |
/**
|
539 |
/**
|
536 |
* Delete recursively the passed file.
|
540 |
* Delete recursively the passed file.
|
537 |
*
|
541 |
*
|
538 |
* @param dir the file to delete.
|
542 |
* @param dir the file to delete.
|
539 |
* @param mustExist what to do if <code>dir</code> is missing : <code>false</code> if it is not
|
543 |
* @param mustExist what to do if <code>dir</code> is missing : <code>false</code> if it is not
|
540 |
* an error, <code>true</code> to fail.
|
544 |
* an error, <code>true</code> to fail.
|
541 |
* @throws IOException if a deletion fails.
|
545 |
* @throws IOException if a deletion fails.
|
542 |
*/
|
546 |
*/
|
543 |
public static void rm_R(final Path dir, final boolean mustExist) throws IOException {
|
547 |
public static void rm_R(final Path dir, final boolean mustExist) throws IOException {
|
544 |
if (!mustExist && !Files.exists(dir))
|
548 |
if (!mustExist && !Files.exists(dir))
|
545 |
return;
|
549 |
return;
|
546 |
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
550 |
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
547 |
@Override
|
551 |
@Override
|
548 |
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
552 |
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
549 |
Files.delete(file);
|
553 |
Files.delete(file);
|
550 |
return FileVisitResult.CONTINUE;
|
554 |
return FileVisitResult.CONTINUE;
|
551 |
}
|
555 |
}
|
552 |
|
556 |
|
553 |
@Override
|
557 |
@Override
|
554 |
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
558 |
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
555 |
// The directory is now empty so delete it
|
559 |
// The directory is now empty so delete it
|
556 |
Files.delete(dir);
|
560 |
Files.delete(dir);
|
557 |
return FileVisitResult.CONTINUE;
|
561 |
return FileVisitResult.CONTINUE;
|
558 |
}
|
562 |
}
|
559 |
});
|
563 |
});
|
560 |
}
|
564 |
}
|
561 |
|
565 |
|
562 |
public static void rm(File f) throws IOException {
|
566 |
public static void rm(File f) throws IOException {
|
563 |
if (f.exists() && !f.delete())
|
567 |
if (f.exists() && !f.delete())
|
564 |
throw new IOException("cannot delete " + f);
|
568 |
throw new IOException("cannot delete " + f);
|
565 |
}
|
569 |
}
|
566 |
|
570 |
|
567 |
public static final File mkdir_p(File dir) throws IOException {
|
571 |
public static final File mkdir_p(File dir) throws IOException {
|
568 |
if (!dir.exists()) {
|
572 |
if (!dir.exists()) {
|
569 |
if (!dir.mkdirs()) {
|
573 |
if (!dir.mkdirs()) {
|
570 |
throw new IOException("cannot create directory " + dir);
|
574 |
throw new IOException("cannot create directory " + dir);
|
571 |
}
|
575 |
}
|
572 |
}
|
576 |
}
|
573 |
return dir;
|
577 |
return dir;
|
574 |
}
|
578 |
}
|
575 |
|
579 |
|
576 |
/**
|
580 |
/**
|
577 |
* Create all ancestors of <code>f</code>.
|
581 |
* Create all ancestors of <code>f</code>.
|
578 |
*
|
582 |
*
|
579 |
* @param f any file whose ancestors should be created.
|
583 |
* @param f any file whose ancestors should be created.
|
580 |
* @return <code>f</code>.
|
584 |
* @return <code>f</code>.
|
581 |
* @throws IOException if ancestors cannot be created.
|
585 |
* @throws IOException if ancestors cannot be created.
|
582 |
*/
|
586 |
*/
|
583 |
public static final File mkParentDirs(File f) throws IOException {
|
587 |
public static final File mkParentDirs(File f) throws IOException {
|
584 |
final File parentFile = f.getParentFile();
|
588 |
final File parentFile = f.getParentFile();
|
585 |
if (parentFile != null)
|
589 |
if (parentFile != null)
|
586 |
mkdir_p(parentFile);
|
590 |
mkdir_p(parentFile);
|
587 |
return f;
|
591 |
return f;
|
588 |
}
|
592 |
}
|
589 |
|
593 |
|
590 |
// **io
|
594 |
// **io
|
591 |
|
595 |
|
592 |
/**
|
596 |
/**
|
593 |
* Read a file line by line with the default encoding and returns the concatenation of these.
|
597 |
* Read a file line by line with the default encoding and returns the concatenation of these.
|
594 |
*
|
598 |
*
|
595 |
* @param f the file to read.
|
599 |
* @param f the file to read.
|
596 |
* @return the content of f.
|
600 |
* @return the content of f.
|
597 |
* @throws IOException if a pb occur while reading.
|
601 |
* @throws IOException if a pb occur while reading.
|
598 |
*/
|
602 |
*/
|
599 |
public static final String read(File f) throws IOException {
|
603 |
public static final String read(File f) throws IOException {
|
600 |
return read(new InputStreamReader(new FileInputStream(f)));
|
604 |
return read(new InputStreamReader(new FileInputStream(f)));
|
601 |
}
|
605 |
}
|
602 |
|
606 |
|
603 |
/**
|
607 |
/**
|
604 |
* Read a file line by line and returns the concatenation of these.
|
608 |
* Read a file line by line and returns the concatenation of these.
|
605 |
*
|
609 |
*
|
606 |
* @param f the file to read.
|
610 |
* @param f the file to read.
|
607 |
* @param charset the encoding of <code>f</code>.
|
611 |
* @param charset the encoding of <code>f</code>.
|
608 |
* @return the content of f.
|
612 |
* @return the content of f.
|
609 |
* @throws IOException if a pb occur while reading.
|
613 |
* @throws IOException if a pb occur while reading.
|
610 |
*/
|
614 |
*/
|
611 |
public static final String read(File f, String charset) throws IOException {
|
615 |
public static final String read(File f, String charset) throws IOException {
|
612 |
return read(new InputStreamReader(new FileInputStream(f), charset));
|
616 |
return read(new InputStreamReader(new FileInputStream(f), charset));
|
613 |
}
|
617 |
}
|
614 |
|
618 |
|
615 |
public static final String readUTF8(File f) throws IOException {
|
619 |
public static final String readUTF8(File f) throws IOException {
|
616 |
return readUTF8(new FileInputStream(f));
|
620 |
return readUTF8(new FileInputStream(f));
|
617 |
}
|
621 |
}
|
618 |
|
622 |
|
- |
|
623 |
public static final String readUTF8(Path p) throws IOException {
|
- |
|
624 |
return new String(Files.readAllBytes(p), StandardCharsets.UTF_8);
|
- |
|
625 |
}
|
- |
|
626 |
|
619 |
public static final String readUTF8(InputStream ins) throws IOException {
|
627 |
public static final String readUTF8(InputStream ins) throws IOException {
|
620 |
return read(ins, StringUtils.UTF8);
|
628 |
return read(ins, StringUtils.UTF8);
|
621 |
}
|
629 |
}
|
622 |
|
630 |
|
623 |
public static final String read(File f, Charset charset) throws IOException {
|
631 |
public static final String read(File f, Charset charset) throws IOException {
|
624 |
return read(new FileInputStream(f), charset);
|
632 |
return read(new FileInputStream(f), charset);
|
625 |
}
|
633 |
}
|
626 |
|
634 |
|
627 |
public static final String read(InputStream ins, Charset charset) throws IOException {
|
635 |
public static final String read(InputStream ins, Charset charset) throws IOException {
|
628 |
final Reader reader;
|
636 |
final Reader reader;
|
629 |
if (charset == null)
|
637 |
if (charset == null)
|
630 |
reader = new InputStreamReader(ins);
|
638 |
reader = new InputStreamReader(ins);
|
631 |
else
|
639 |
else
|
632 |
reader = new InputStreamReader(ins, charset);
|
640 |
reader = new InputStreamReader(ins, charset);
|
633 |
return read(reader);
|
641 |
return read(reader);
|
634 |
}
|
642 |
}
|
635 |
|
643 |
|
636 |
public static final String read(final Reader reader) throws IOException {
|
644 |
public static final String read(final Reader reader) throws IOException {
|
637 |
return read(reader, 8192);
|
645 |
return read(reader, 8192);
|
638 |
}
|
646 |
}
|
639 |
|
647 |
|
640 |
public static final String read(final Reader reader, final int bufferSize) throws IOException {
|
648 |
public static final String read(final Reader reader, final int bufferSize) throws IOException {
|
641 |
final StringBuilder sb = new StringBuilder();
|
649 |
final StringBuilder sb = new StringBuilder();
|
642 |
final char[] buffer = new char[bufferSize];
|
650 |
final char[] buffer = new char[bufferSize];
|
643 |
final BufferedReader in = new BufferedReader(reader);
|
651 |
final BufferedReader in = new BufferedReader(reader);
|
644 |
try {
|
652 |
try {
|
645 |
while (true) {
|
653 |
while (true) {
|
646 |
final int count = in.read(buffer);
|
654 |
final int count = in.read(buffer);
|
647 |
if (count == -1)
|
655 |
if (count == -1)
|
648 |
break;
|
656 |
break;
|
649 |
sb.append(buffer, 0, count);
|
657 |
sb.append(buffer, 0, count);
|
650 |
}
|
658 |
}
|
651 |
} finally {
|
659 |
} finally {
|
652 |
in.close();
|
660 |
in.close();
|
653 |
}
|
661 |
}
|
654 |
return sb.toString();
|
662 |
return sb.toString();
|
655 |
}
|
663 |
}
|
656 |
|
664 |
|
657 |
/**
|
665 |
/**
|
658 |
* Read the whole content of a file.
|
666 |
* Read the whole content of a file.
|
659 |
*
|
667 |
*
|
660 |
* @param f the file to read.
|
668 |
* @param f the file to read.
|
661 |
* @return its content.
|
669 |
* @return its content.
|
662 |
* @throws IOException if a pb occur while reading.
|
670 |
* @throws IOException if a pb occur while reading.
|
663 |
* @see Files#readAllBytes(java.nio.file.Path)
|
671 |
* @see Files#readAllBytes(java.nio.file.Path)
|
664 |
*/
|
672 |
*/
|
665 |
public static final byte[] readBytes(File f) throws IOException {
|
673 |
public static final byte[] readBytes(File f) throws IOException {
|
666 |
// works for /proc files which report 0 size
|
674 |
// works for /proc files which report 0 size
|
667 |
return Files.readAllBytes(f.toPath());
|
675 |
return Files.readAllBytes(f.toPath());
|
668 |
}
|
676 |
}
|
669 |
|
677 |
|
670 |
public static void write(String s, File f) throws IOException {
|
678 |
public static void write(String s, File f) throws IOException {
|
671 |
write(s, f, null, false);
|
679 |
write(s, f, null, false);
|
672 |
}
|
680 |
}
|
673 |
|
681 |
|
674 |
public static void write(String s, File f, Charset charset, boolean append) throws IOException {
|
682 |
public static void write(String s, File f, Charset charset, boolean append) throws IOException {
|
675 |
try (final FileOutputStream fileStream = new FileOutputStream(f, append);
|
683 |
try (final FileOutputStream fileStream = new FileOutputStream(f, append);
|
676 |
final BufferedWriter w = new BufferedWriter(charset == null ? new OutputStreamWriter(fileStream) : new OutputStreamWriter(fileStream, charset))) {
|
684 |
final BufferedWriter w = new BufferedWriter(charset == null ? new OutputStreamWriter(fileStream) : new OutputStreamWriter(fileStream, charset))) {
|
677 |
w.write(s);
|
685 |
w.write(s);
|
678 |
}
|
686 |
}
|
679 |
}
|
687 |
}
|
680 |
|
688 |
|
681 |
/**
|
689 |
/**
|
682 |
* Create a writer for the passed file, and write the XML declaration.
|
690 |
* Create a writer for the passed file, and write the XML declaration.
|
683 |
*
|
691 |
*
|
684 |
* @param f a file
|
692 |
* @param f a file
|
685 |
* @return a writer with the same encoding as the XML.
|
693 |
* @return a writer with the same encoding as the XML.
|
686 |
* @throws IOException if an error occurs.
|
694 |
* @throws IOException if an error occurs.
|
687 |
* @see StreamUtils#createXMLWriter(java.io.OutputStream)
|
695 |
* @see StreamUtils#createXMLWriter(java.io.OutputStream)
|
688 |
*/
|
696 |
*/
|
689 |
public static BufferedWriter createXMLWriter(final File f) throws IOException {
|
697 |
public static BufferedWriter createXMLWriter(final File f) throws IOException {
|
690 |
final FileOutputStream outs = new FileOutputStream(f);
|
698 |
final FileOutputStream outs = new FileOutputStream(f);
|
691 |
try {
|
699 |
try {
|
692 |
return StreamUtils.createXMLWriter(outs);
|
700 |
return StreamUtils.createXMLWriter(outs);
|
693 |
} catch (RuntimeException e) {
|
701 |
} catch (RuntimeException e) {
|
694 |
outs.close();
|
702 |
outs.close();
|
695 |
throw e;
|
703 |
throw e;
|
696 |
} catch (IOException e) {
|
704 |
} catch (IOException e) {
|
697 |
outs.close();
|
705 |
outs.close();
|
698 |
throw e;
|
706 |
throw e;
|
699 |
}
|
707 |
}
|
700 |
}
|
708 |
}
|
701 |
|
709 |
|
702 |
/**
|
710 |
/**
|
703 |
* Create an UTF-8 buffered writer.
|
711 |
* Create an UTF-8 buffered writer.
|
704 |
*
|
712 |
*
|
705 |
* @param f the file to write to.
|
713 |
* @param f the file to write to.
|
706 |
* @return a buffered writer.
|
714 |
* @return a buffered writer.
|
707 |
* @throws FileNotFoundException if the file cannot be opened.
|
715 |
* @throws FileNotFoundException if the file cannot be opened.
|
708 |
*/
|
716 |
*/
|
709 |
public static BufferedWriter createWriter(final File f) throws FileNotFoundException {
|
717 |
public static BufferedWriter createWriter(final File f) throws FileNotFoundException {
|
710 |
return createWriter(f, StringUtils.UTF8);
|
718 |
return createWriter(f, StringUtils.UTF8);
|
711 |
}
|
719 |
}
|
712 |
|
720 |
|
713 |
public static BufferedWriter createWriter(final File f, final Charset cs) throws FileNotFoundException {
|
721 |
public static BufferedWriter createWriter(final File f, final Charset cs) throws FileNotFoundException {
|
714 |
final FileOutputStream outs = new FileOutputStream(f);
|
722 |
final FileOutputStream outs = new FileOutputStream(f);
|
715 |
try {
|
723 |
try {
|
716 |
return new BufferedWriter(new OutputStreamWriter(outs, cs));
|
724 |
return new BufferedWriter(new OutputStreamWriter(outs, cs));
|
717 |
} catch (RuntimeException e) {
|
725 |
} catch (RuntimeException e) {
|
718 |
try {
|
726 |
try {
|
719 |
outs.close();
|
727 |
outs.close();
|
720 |
} catch (IOException e1) {
|
728 |
} catch (IOException e1) {
|
721 |
e1.printStackTrace();
|
729 |
e1.printStackTrace();
|
722 |
}
|
730 |
}
|
723 |
throw e;
|
731 |
throw e;
|
724 |
}
|
732 |
}
|
725 |
}
|
733 |
}
|
726 |
|
734 |
|
727 |
/**
|
735 |
/**
|
728 |
* Execute the passed transformer with the lock on the passed file.
|
736 |
* Execute the passed transformer with the lock on the passed file.
|
729 |
*
|
737 |
*
|
730 |
* @param <T> return type.
|
738 |
* @param <T> return type.
|
731 |
* @param f the file to lock.
|
739 |
* @param f the file to lock.
|
732 |
* @param transf what to do on the file.
|
740 |
* @param transf what to do on the file.
|
733 |
* @return what <code>transf</code> returns.
|
741 |
* @return what <code>transf</code> returns.
|
734 |
* @throws IOException if an error occurs while locking the file.
|
742 |
* @throws IOException if an error occurs while locking the file.
|
735 |
* @throws X if an error occurs while using the file.
|
743 |
* @throws X if an error occurs while using the file.
|
736 |
*/
|
744 |
*/
|
737 |
public static final <T, X extends Exception> T doWithLock(final File f, ExnTransformer<RandomAccessFile, T, X> transf) throws IOException, X {
|
745 |
public static final <T, X extends Exception> T doWithLock(final File f, ExnTransformer<RandomAccessFile, T, X> transf) throws IOException, X {
|
738 |
mkParentDirs(f);
|
746 |
mkParentDirs(f);
|
739 |
// don't use FileOutputStream : it truncates the file on creation
|
747 |
// don't use FileOutputStream : it truncates the file on creation
|
740 |
// we need write to obtain lock
|
748 |
// we need write to obtain lock
|
741 |
try (final RandomAccessFile out = new RandomAccessFile(f, "rw")) {
|
749 |
try (final RandomAccessFile out = new RandomAccessFile(f, "rw")) {
|
742 |
out.getChannel().lock();
|
750 |
out.getChannel().lock();
|
743 |
return transf.transformChecked(out);
|
751 |
return transf.transformChecked(out);
|
744 |
}
|
752 |
}
|
745 |
// the lock is released on close()
|
753 |
// the lock is released on close()
|
746 |
}
|
754 |
}
|
747 |
|
755 |
|
748 |
private static final Map<URL, File> files = new HashMap<URL, File>();
|
756 |
private static final Map<URL, File> files = new HashMap<URL, File>();
|
749 |
|
757 |
|
750 |
private static final File getShortCutFile() throws IOException {
|
758 |
private static final File getShortCutFile() throws IOException {
|
751 |
return getFile(FileUtils.class.getResource("shortcut.vbs"));
|
759 |
return getFile(FileUtils.class.getResource("shortcut.vbs"));
|
752 |
}
|
760 |
}
|
753 |
|
761 |
|
754 |
// windows cannot execute a string, it demands a file
|
762 |
// windows cannot execute a string, it demands a file
|
755 |
public static final File getFile(final URL url) throws IOException {
|
763 |
public static final File getFile(final URL url) throws IOException {
|
756 |
// avoid unnecessary IO if already a file
|
764 |
// avoid unnecessary IO if already a file
|
757 |
File urlFile = null;
|
765 |
File urlFile = null;
|
758 |
// inexpensive comparison before trying to convert to URI and call the File constructor
|
766 |
// inexpensive comparison before trying to convert to URI and call the File constructor
|
759 |
if ("file".equalsIgnoreCase(url.getProtocol())) {
|
767 |
if ("file".equalsIgnoreCase(url.getProtocol())) {
|
760 |
try {
|
768 |
try {
|
761 |
urlFile = new File(url.toURI());
|
769 |
urlFile = new File(url.toURI());
|
762 |
} catch (Exception e) {
|
770 |
} catch (Exception e) {
|
763 |
Log.get().log(Level.FINER, "couldn't convert to file " + url, e);
|
771 |
Log.get().log(Level.FINER, "couldn't convert to file " + url, e);
|
764 |
}
|
772 |
}
|
765 |
}
|
773 |
}
|
766 |
if (urlFile != null)
|
774 |
if (urlFile != null)
|
767 |
return urlFile;
|
775 |
return urlFile;
|
768 |
|
776 |
|
769 |
final File shortcutFile;
|
777 |
final File shortcutFile;
|
770 |
final File currentFile = files.get(url);
|
778 |
final File currentFile = files.get(url);
|
771 |
if (currentFile == null || !currentFile.exists()) {
|
779 |
if (currentFile == null || !currentFile.exists()) {
|
772 |
shortcutFile = File.createTempFile("windowsIsLame", ".vbs");
|
780 |
shortcutFile = File.createTempFile("windowsIsLame", ".vbs");
|
773 |
// ATTN if the VM is not terminated normally, the file won't be deleted
|
781 |
// ATTN if the VM is not terminated normally, the file won't be deleted
|
774 |
// perhaps a thread to delete the file after a certain amount of time
|
782 |
// perhaps a thread to delete the file after a certain amount of time
|
775 |
shortcutFile.deleteOnExit();
|
783 |
shortcutFile.deleteOnExit();
|
776 |
files.put(url, shortcutFile);
|
784 |
files.put(url, shortcutFile);
|
777 |
try (final InputStream stream = url.openStream(); final FileOutputStream out = new FileOutputStream(shortcutFile);) {
|
785 |
try (final InputStream stream = url.openStream(); final FileOutputStream out = new FileOutputStream(shortcutFile);) {
|
778 |
StreamUtils.copy(stream, out);
|
786 |
StreamUtils.copy(stream, out);
|
779 |
}
|
787 |
}
|
780 |
} else
|
788 |
} else
|
781 |
shortcutFile = currentFile;
|
789 |
shortcutFile = currentFile;
|
782 |
return shortcutFile;
|
790 |
return shortcutFile;
|
783 |
}
|
791 |
}
|
784 |
|
792 |
|
785 |
/**
|
793 |
/**
|
786 |
* Create a symbolic link from <code>link</code> to <code>target</code>.
|
794 |
* Create a symbolic link from <code>link</code> to <code>target</code>.
|
787 |
*
|
795 |
*
|
788 |
* @param target the target of the link, eg ".".
|
796 |
* @param target the target of the link, eg ".".
|
789 |
* @param link the file to create or replace, eg "l".
|
797 |
* @param link the file to create or replace, eg "l".
|
790 |
* @return the link if the creation was successfull, <code>null</code> otherwise, eg "l.LNK".
|
798 |
* @return the link if the creation was successfull, <code>null</code> otherwise, eg "l.LNK".
|
791 |
* @throws IOException if an error occurs.
|
799 |
* @throws IOException if an error occurs.
|
792 |
*/
|
800 |
*/
|
793 |
public static final File ln(final File target, final File link) throws IOException {
|
801 |
public static final File ln(final File target, final File link) throws IOException {
|
794 |
final Process ps;
|
802 |
final Process ps;
|
795 |
final File res;
|
803 |
final File res;
|
796 |
if (OSFamily.getInstance() == OSFamily.Windows) {
|
804 |
if (OSFamily.getInstance() == OSFamily.Windows) {
|
797 |
// using the .vbs since it doesn't depends on cygwin
|
805 |
// using the .vbs since it doesn't depends on cygwin
|
798 |
// and cygwin's ln is weird :
|
806 |
// and cygwin's ln is weird :
|
799 |
// 1. needs CYGWIN=winsymlinks to create a shortcut, but even then "ln -f" doesn't work
|
807 |
// 1. needs CYGWIN=winsymlinks to create a shortcut, but even then "ln -f" doesn't work
|
800 |
// since it tries to delete l instead of l.LNK
|
808 |
// since it tries to delete l instead of l.LNK
|
801 |
// 2. it sets the system flag so "dir" doesn't show the shortcut (unless you add /AS)
|
809 |
// 2. it sets the system flag so "dir" doesn't show the shortcut (unless you add /AS)
|
802 |
// 3. the shortcut is recognized as a symlink thanks to a special attribute that can get
|
810 |
// 3. the shortcut is recognized as a symlink thanks to a special attribute that can get
|
803 |
// lost (e.g. copying in eclipse)
|
811 |
// lost (e.g. copying in eclipse)
|
804 |
ps = Runtime.getRuntime().exec(new String[] { "cscript", getShortCutFile().getAbsolutePath(), link.getAbsolutePath(), target.getCanonicalPath() });
|
812 |
ps = Runtime.getRuntime().exec(new String[] { "cscript", getShortCutFile().getAbsolutePath(), link.getAbsolutePath(), target.getCanonicalPath() });
|
805 |
res = new File(link.getParentFile(), link.getName() + ".LNK");
|
813 |
res = new File(link.getParentFile(), link.getName() + ".LNK");
|
806 |
} else {
|
814 |
} else {
|
807 |
final String rel = FileUtils.relative(link.getAbsoluteFile().getParentFile(), target);
|
815 |
final String rel = FileUtils.relative(link.getAbsoluteFile().getParentFile(), target);
|
808 |
// add -f to replace existing links
|
816 |
// add -f to replace existing links
|
809 |
// add -n so that ln -sf aDir anExistantLinkToIt succeed
|
817 |
// add -n so that ln -sf aDir anExistantLinkToIt succeed
|
810 |
final String[] cmdarray = { "ln", "-sfn", rel, link.getAbsolutePath() };
|
818 |
final String[] cmdarray = { "ln", "-sfn", rel, link.getAbsolutePath() };
|
811 |
ps = Runtime.getRuntime().exec(cmdarray);
|
819 |
ps = Runtime.getRuntime().exec(cmdarray);
|
812 |
res = link;
|
820 |
res = link;
|
813 |
}
|
821 |
}
|
814 |
// no need for output, either it succeeds or it fails
|
822 |
// no need for output, either it succeeds or it fails
|
815 |
ProcessStreams.handle(ps, Action.CLOSE);
|
823 |
ProcessStreams.handle(ps, Action.CLOSE);
|
816 |
try {
|
824 |
try {
|
817 |
final int exitValue = ps.waitFor();
|
825 |
final int exitValue = ps.waitFor();
|
818 |
if (exitValue == 0)
|
826 |
if (exitValue == 0)
|
819 |
return res;
|
827 |
return res;
|
820 |
else
|
828 |
else
|
821 |
throw new IOException("Abnormal exit value: " + exitValue);
|
829 |
throw new IOException("Abnormal exit value: " + exitValue);
|
822 |
} catch (InterruptedException e) {
|
830 |
} catch (InterruptedException e) {
|
823 |
throw ExceptionUtils.createExn(IOException.class, "interrupted", e);
|
831 |
throw ExceptionUtils.createExn(IOException.class, "interrupted", e);
|
824 |
}
|
832 |
}
|
825 |
}
|
833 |
}
|
826 |
|
834 |
|
827 |
/**
|
835 |
/**
|
828 |
* Resolve a symbolic link or a windows shortcut.
|
836 |
* Resolve a symbolic link or a windows shortcut.
|
829 |
*
|
837 |
*
|
830 |
* @param link the shortcut, e.g. shortcut.lnk.
|
838 |
* @param link the shortcut, e.g. shortcut.lnk.
|
831 |
* @return the target of <code>link</code>, <code>null</code> if not found, e.g. target.txt.
|
839 |
* @return the target of <code>link</code>, <code>null</code> if not found, e.g. target.txt.
|
832 |
* @throws IOException if an error occurs.
|
840 |
* @throws IOException if an error occurs.
|
833 |
*/
|
841 |
*/
|
834 |
public static final File readlink(final File link) throws IOException {
|
842 |
public static final File readlink(final File link) throws IOException {
|
835 |
final Process ps;
|
843 |
final Process ps;
|
836 |
if (OSFamily.getInstance() == OSFamily.Windows) {
|
844 |
if (OSFamily.getInstance() == OSFamily.Windows) {
|
837 |
ps = Runtime.getRuntime().exec(new String[] { "cscript", "//NoLogo", getShortCutFile().getAbsolutePath(), link.getAbsolutePath() });
|
845 |
ps = Runtime.getRuntime().exec(new String[] { "cscript", "//NoLogo", getShortCutFile().getAbsolutePath(), link.getAbsolutePath() });
|
838 |
} else {
|
846 |
} else {
|
839 |
// add -f to canonicalize
|
847 |
// add -f to canonicalize
|
840 |
ps = Runtime.getRuntime().exec(new String[] { "readlink", "-f", link.getAbsolutePath() });
|
848 |
ps = Runtime.getRuntime().exec(new String[] { "readlink", "-f", link.getAbsolutePath() });
|
841 |
}
|
849 |
}
|
842 |
try {
|
850 |
try {
|
843 |
ps.getErrorStream().close();
|
851 |
ps.getErrorStream().close();
|
844 |
final String res;
|
852 |
final String res;
|
845 |
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(ps.getInputStream()));) {
|
853 |
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(ps.getInputStream()));) {
|
846 |
res = reader.readLine();
|
854 |
res = reader.readLine();
|
847 |
}
|
855 |
}
|
848 |
if (ps.waitFor() != 0 || res == null || res.length() == 0)
|
856 |
if (ps.waitFor() != 0 || res == null || res.length() == 0)
|
849 |
return null;
|
857 |
return null;
|
850 |
else
|
858 |
else
|
851 |
return new File(res);
|
859 |
return new File(res);
|
852 |
} catch (InterruptedException e) {
|
860 |
} catch (InterruptedException e) {
|
853 |
throw ExceptionUtils.createExn(IOException.class, "interrupted", e);
|
861 |
throw ExceptionUtils.createExn(IOException.class, "interrupted", e);
|
854 |
}
|
862 |
}
|
855 |
}
|
863 |
}
|
856 |
|
864 |
|
857 |
// from guava/src/com/google/common/io/Files.java
|
865 |
// from guava/src/com/google/common/io/Files.java
|
858 |
/** Maximum loop count when creating temp directories. */
|
866 |
/** Maximum loop count when creating temp directories. */
|
859 |
private static final int TEMP_DIR_ATTEMPTS = 10000;
|
867 |
private static final int TEMP_DIR_ATTEMPTS = 10000;
|
860 |
|
868 |
|
861 |
/**
|
869 |
/**
|
862 |
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
|
870 |
* Atomically creates a new directory somewhere beneath the system's temporary directory (as
|
863 |
* defined by the {@code java.io.tmpdir} system property), and returns its name.
|
871 |
* defined by the {@code java.io.tmpdir} system property), and returns its name.
|
864 |
*
|
872 |
*
|
865 |
* <p>
|
873 |
* <p>
|
866 |
* Use this method instead of {@link File#createTempFile(String, String)} when you wish to
|
874 |
* Use this method instead of {@link File#createTempFile(String, String)} when you wish to
|
867 |
* create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
|
875 |
* create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
|
868 |
* delete the file and create a directory in its place, but this leads a race condition which
|
876 |
* delete the file and create a directory in its place, but this leads a race condition which
|
869 |
* can be exploited to create security vulnerabilities, especially when executable files are to
|
877 |
* can be exploited to create security vulnerabilities, especially when executable files are to
|
870 |
* be written into the directory.
|
878 |
* be written into the directory.
|
871 |
*
|
879 |
*
|
872 |
* <p>
|
880 |
* <p>
|
873 |
* This method assumes that the temporary volume is writable, has free inodes and free blocks,
|
881 |
* This method assumes that the temporary volume is writable, has free inodes and free blocks,
|
874 |
* and that it will not be called thousands of times per second.
|
882 |
* and that it will not be called thousands of times per second.
|
875 |
*
|
883 |
*
|
876 |
* @param prefix the prefix string to be used in generating the directory's name.
|
884 |
* @param prefix the prefix string to be used in generating the directory's name.
|
877 |
* @return the newly-created directory.
|
885 |
* @return the newly-created directory.
|
878 |
* @throws IllegalStateException if the directory could not be created.
|
886 |
* @throws IllegalStateException if the directory could not be created.
|
879 |
*/
|
887 |
*/
|
880 |
public static File createTempDir(final String prefix) {
|
888 |
public static File createTempDir(final String prefix) {
|
881 |
final File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
889 |
final File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
882 |
final String baseName = prefix + System.currentTimeMillis() + "-";
|
890 |
final String baseName = prefix + System.currentTimeMillis() + "-";
|
883 |
|
891 |
|
884 |
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
|
892 |
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
|
885 |
final File tempDir = new File(baseDir, baseName + counter);
|
893 |
final File tempDir = new File(baseDir, baseName + counter);
|
886 |
if (tempDir.mkdir()) {
|
894 |
if (tempDir.mkdir()) {
|
887 |
return tempDir;
|
895 |
return tempDir;
|
888 |
}
|
896 |
}
|
889 |
}
|
897 |
}
|
890 |
throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
|
898 |
throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
|
891 |
}
|
899 |
}
|
892 |
|
900 |
|
893 |
/**
|
901 |
/**
|
894 |
* Tries to open the passed file as if it were graphically opened by the current user (respect
|
902 |
* Tries to open the passed file as if it were graphically opened by the current user (respect
|
895 |
* user's "open with"). If a native way to open the file can't be found, tries the passed list
|
903 |
* user's "open with"). If a native way to open the file can't be found, tries the passed list
|
896 |
* of executables.
|
904 |
* of executables.
|
897 |
*
|
905 |
*
|
898 |
* @param f the file to open.
|
906 |
* @param f the file to open.
|
899 |
* @param executables a list of executables to try, e.g. ["ooffice", "soffice"].
|
907 |
* @param executables a list of executables to try, e.g. ["ooffice", "soffice"].
|
900 |
* @throws IOException if the file can't be opened.
|
908 |
* @throws IOException if the file can't be opened.
|
901 |
*/
|
909 |
*/
|
902 |
public static final void open(File f, String[] executables) throws IOException {
|
910 |
public static final void open(File f, String[] executables) throws IOException {
|
- |
|
911 |
if (!f.exists()) {
|
- |
|
912 |
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
|
- |
|
913 |
}
|
903 |
try {
|
914 |
try {
|
904 |
openNative(f);
|
915 |
openNative(f);
|
905 |
} catch (IOException exn) {
|
916 |
} catch (IOException exn) {
|
906 |
for (int i = 0; i < executables.length; i++) {
|
917 |
for (int i = 0; i < executables.length; i++) {
|
907 |
final String executable = executables[i];
|
918 |
final String executable = executables[i];
|
908 |
try {
|
919 |
try {
|
909 |
ProcessStreams.handle(Runtime.getRuntime().exec(new String[] { executable, f.getCanonicalPath() }), Action.CLOSE);
|
920 |
ProcessStreams.handle(Runtime.getRuntime().exec(new String[] { executable, f.getCanonicalPath() }), Action.CLOSE);
|
910 |
return;
|
921 |
return;
|
911 |
} catch (IOException e) {
|
922 |
} catch (IOException e) {
|
912 |
// try the next one
|
923 |
// try the next one
|
913 |
}
|
924 |
}
|
914 |
}
|
925 |
}
|
915 |
throw ExceptionUtils.createExn(IOException.class, "unable to open " + f + " with: " + Arrays.asList(executables), exn);
|
926 |
throw ExceptionUtils.createExn(IOException.class, "unable to open " + f + " with: " + Arrays.asList(executables), exn);
|
916 |
}
|
927 |
}
|
917 |
}
|
928 |
}
|
918 |
|
929 |
|
919 |
/**
|
930 |
/**
|
920 |
* Open the passed file as if it were graphically opened by the current user (user's "open
|
931 |
* Open the passed file as if it were graphically opened by the current user (user's "open
|
921 |
* with").
|
932 |
* with").
|
922 |
*
|
933 |
*
|
923 |
* @param f the file to open.
|
934 |
* @param f the file to open.
|
924 |
* @throws IOException if f couldn't be opened.
|
935 |
* @throws IOException if f couldn't be opened.
|
925 |
*/
|
936 |
*/
|
926 |
private static final void openNative(File f) throws IOException {
|
937 |
private static final void openNative(File f) throws IOException {
|
927 |
final OSFamily os = OSFamily.getInstance();
|
938 |
final OSFamily os = OSFamily.getInstance();
|
928 |
final String[] cmdarray;
|
939 |
final String[] cmdarray;
|
929 |
if (os == OSFamily.Windows) {
|
940 |
if (os == OSFamily.Windows) {
|
930 |
cmdarray = new String[] { "cmd", "/c", "start", "\"\"", f.getCanonicalPath() };
|
941 |
cmdarray = new String[] { "cmd", "/c", "start", "\"\"", f.getCanonicalPath() };
|
931 |
} else if (os == OSFamily.Mac) {
|
942 |
} else if (os == OSFamily.Mac) {
|
932 |
cmdarray = new String[] { "open", f.getCanonicalPath() };
|
943 |
cmdarray = new String[] { "open", f.getCanonicalPath() };
|
933 |
} else if (os instanceof Unix) {
|
944 |
} else if (os instanceof Unix) {
|
934 |
cmdarray = new String[] { "xdg-open", f.getCanonicalPath() };
|
945 |
cmdarray = new String[] { "xdg-open", f.getCanonicalPath() };
|
935 |
} else {
|
946 |
} else {
|
936 |
throw new IOException("unknown way to open " + f);
|
947 |
throw new IOException("unknown way to open " + f);
|
937 |
}
|
948 |
}
|
938 |
try {
|
949 |
try {
|
939 |
final Process ps = Runtime.getRuntime().exec(cmdarray);
|
950 |
final Process ps = Runtime.getRuntime().exec(cmdarray);
|
940 |
ProcessStreams.handle(ps, Action.CLOSE);
|
951 |
ProcessStreams.handle(ps, Action.CLOSE);
|
941 |
// can wait since the command return as soon as the native application is launched
|
952 |
// can wait since the command return as soon as the native application is launched
|
942 |
// (i.e. this won't wait 30s for OpenOffice)
|
953 |
// (i.e. this won't wait 30s for OpenOffice)
|
943 |
final int res = ps.waitFor();
|
954 |
final int res = ps.waitFor();
|
944 |
if (res != 0)
|
955 |
if (res != 0)
|
945 |
throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
|
956 |
throw new IOException("error (" + res + ") executing " + Arrays.asList(cmdarray));
|
946 |
} catch (InterruptedException e) {
|
957 |
} catch (InterruptedException e) {
|
947 |
throw ExceptionUtils.createExn(IOException.class, "interrupted waiting for " + Arrays.asList(cmdarray), e);
|
958 |
throw ExceptionUtils.createExn(IOException.class, "interrupted waiting for " + Arrays.asList(cmdarray), e);
|
948 |
}
|
959 |
}
|
949 |
}
|
960 |
}
|
950 |
|
961 |
|
951 |
static final boolean gnomeRunning() {
|
962 |
static final boolean gnomeRunning() {
|
952 |
try {
|
963 |
try {
|
953 |
final Process ps = Runtime.getRuntime().exec(new String[] { "pgrep", "-u", System.getProperty("user.name"), "nautilus" });
|
964 |
final Process ps = Runtime.getRuntime().exec(new String[] { "pgrep", "-u", System.getProperty("user.name"), "nautilus" });
|
954 |
// no need for output, use exit status
|
965 |
// no need for output, use exit status
|
955 |
ProcessStreams.handle(ps, Action.CLOSE);
|
966 |
ProcessStreams.handle(ps, Action.CLOSE);
|
956 |
return ps.waitFor() == 0;
|
967 |
return ps.waitFor() == 0;
|
957 |
} catch (Exception e) {
|
968 |
} catch (Exception e) {
|
958 |
return false;
|
969 |
return false;
|
959 |
}
|
970 |
}
|
960 |
}
|
971 |
}
|
961 |
|
972 |
|
962 |
public static final String XML_TYPE = "text/xml";
|
973 |
public static final String XML_TYPE = "text/xml";
|
963 |
private static final Map<String, String> ext2mime;
|
974 |
private static final Map<String, String> ext2mime;
|
964 |
private static final SetMap<String, String> mime2ext;
|
975 |
private static final SetMap<String, String> mime2ext;
|
965 |
|
976 |
|
966 |
static {
|
977 |
static {
|
967 |
mime2ext = new SetMap<String, String>(Mode.NULL_FORBIDDEN);
|
978 |
mime2ext = new SetMap<String, String>(Mode.NULL_FORBIDDEN);
|
968 |
mime2ext.putCollection(XML_TYPE, ".xml");
|
979 |
mime2ext.putCollection(XML_TYPE, ".xml");
|
969 |
mime2ext.putCollection("image/jpeg", ".jpg", ".jpeg");
|
980 |
mime2ext.putCollection("image/jpeg", ".jpg", ".jpeg");
|
970 |
mime2ext.putCollection("image/png", ".png");
|
981 |
mime2ext.putCollection("image/png", ".png");
|
971 |
mime2ext.putCollection("image/tiff", ".tiff", ".tif");
|
982 |
mime2ext.putCollection("image/tiff", ".tiff", ".tif");
|
972 |
mime2ext.putCollection("application/pdf", ".pdf");
|
983 |
mime2ext.putCollection("application/pdf", ".pdf");
|
973 |
|
984 |
|
974 |
mime2ext.putCollection("application/vnd.oasis.opendocument.spreadsheet", ".ods");
|
985 |
mime2ext.putCollection("application/vnd.oasis.opendocument.spreadsheet", ".ods");
|
975 |
mime2ext.putCollection("application/vnd.oasis.opendocument.text", ".odt");
|
986 |
mime2ext.putCollection("application/vnd.oasis.opendocument.text", ".odt");
|
976 |
mime2ext.putCollection("application/vnd.oasis.opendocument.presentation", ".odp");
|
987 |
mime2ext.putCollection("application/vnd.oasis.opendocument.presentation", ".odp");
|
977 |
mime2ext.putCollection("application/vnd.oasis.opendocument.graphics", ".odg");
|
988 |
mime2ext.putCollection("application/vnd.oasis.opendocument.graphics", ".odg");
|
978 |
|
989 |
|
979 |
ext2mime = new HashMap<String, String>();
|
990 |
ext2mime = new HashMap<String, String>();
|
980 |
for (final Entry<String, Set<String>> e : mime2ext.entrySet()) {
|
991 |
for (final Entry<String, Set<String>> e : mime2ext.entrySet()) {
|
981 |
final String m = e.getKey();
|
992 |
final String m = e.getKey();
|
982 |
for (final String ext : e.getValue()) {
|
993 |
for (final String ext : e.getValue()) {
|
983 |
if (ext2mime.put(ext, m) != null)
|
994 |
if (ext2mime.put(ext, m) != null)
|
984 |
Log.get().info("Duplicate extension : " + ext);
|
995 |
Log.get().info("Duplicate extension : " + ext);
|
985 |
}
|
996 |
}
|
986 |
}
|
997 |
}
|
987 |
}
|
998 |
}
|
988 |
|
999 |
|
989 |
/**
|
1000 |
/**
|
990 |
* Try to guess the media type of the passed file name (see
|
1001 |
* Try to guess the media type of the passed file name (see
|
991 |
* <a href="http://www.iana.org/assignments/media-types">iana</a>).
|
1002 |
* <a href="http://www.iana.org/assignments/media-types">iana</a>).
|
992 |
*
|
1003 |
*
|
993 |
* @param fname a file name.
|
1004 |
* @param fname a file name.
|
994 |
* @return its mime type.
|
1005 |
* @return its mime type.
|
995 |
*/
|
1006 |
*/
|
996 |
public static final String findMimeType(String fname) {
|
1007 |
public static final String findMimeType(String fname) {
|
997 |
for (final Map.Entry<String, String> e : ext2mime.entrySet()) {
|
1008 |
for (final Map.Entry<String, String> e : ext2mime.entrySet()) {
|
998 |
if (fname.toLowerCase().endsWith(e.getKey()))
|
1009 |
if (fname.toLowerCase().endsWith(e.getKey()))
|
999 |
return e.getValue();
|
1010 |
return e.getValue();
|
1000 |
}
|
1011 |
}
|
1001 |
return null;
|
1012 |
return null;
|
1002 |
}
|
1013 |
}
|
1003 |
|
1014 |
|
1004 |
public static final Set<String> getExtensionsFromMimeType(final String mimetype) {
|
1015 |
public static final Set<String> getExtensionsFromMimeType(final String mimetype) {
|
1005 |
return mime2ext.get(mimetype);
|
1016 |
return mime2ext.get(mimetype);
|
1006 |
}
|
1017 |
}
|
1007 |
|
1018 |
|
1008 |
/**
|
1019 |
/**
|
1009 |
* Return the string after the last dot.
|
1020 |
* Return the string after the last dot.
|
1010 |
*
|
1021 |
*
|
1011 |
* @param fname a name, e.g. "test.odt" or "sans".
|
1022 |
* @param fname a name, e.g. "test.odt" or "sans".
|
1012 |
* @return the extension, e.g. "odt" or <code>null</code>.
|
1023 |
* @return the extension, e.g. "odt" or <code>null</code>.
|
1013 |
*/
|
1024 |
*/
|
1014 |
public static final String getExtension(String fname) {
|
1025 |
public static final String getExtension(String fname) {
|
1015 |
return getExtension(fname, false);
|
1026 |
return getExtension(fname, false);
|
1016 |
}
|
1027 |
}
|
1017 |
|
1028 |
|
1018 |
public static final String getExtension(final String fname, final boolean withDot) {
|
1029 |
public static final String getExtension(final String fname, final boolean withDot) {
|
1019 |
final int lastIndex = fname.lastIndexOf('.');
|
1030 |
final int lastIndex = fname.lastIndexOf('.');
|
1020 |
return lastIndex < 0 ? null : fname.substring(lastIndex + (withDot ? 0 : 1));
|
1031 |
return lastIndex < 0 ? null : fname.substring(lastIndex + (withDot ? 0 : 1));
|
1021 |
}
|
1032 |
}
|
1022 |
|
1033 |
|
1023 |
/**
|
1034 |
/**
|
1024 |
* Chars not valid in filenames.
|
1035 |
* Chars not valid in filenames.
|
1025 |
*/
|
1036 |
*/
|
1026 |
public static final Collection<Character> INVALID_CHARS;
|
1037 |
public static final Collection<Character> INVALID_CHARS;
|
1027 |
|
1038 |
|
1028 |
/**
|
1039 |
/**
|
1029 |
* An escaper suitable for producing valid filenames.
|
1040 |
* An escaper suitable for producing valid filenames.
|
1030 |
*/
|
1041 |
*/
|
1031 |
public static final Escaper FILENAME_ESCAPER = new StringUtils.Escaper('\'', 'Q');
|
1042 |
public static final Escaper FILENAME_ESCAPER = new StringUtils.Escaper('\'', 'Q');
|
1032 |
|
1043 |
|
1033 |
static private final String WS = "\\p{javaWhitespace}";
|
1044 |
static private final String WS = "\\p{javaWhitespace}";
|
1034 |
static private final Pattern WS_PATTERN = Pattern.compile(WS + "+");
|
1045 |
static private final Pattern WS_PATTERN = Pattern.compile(WS + "+");
|
1035 |
static private final Pattern CONTROL_PATTERN = Pattern.compile("[\\p{IsCc}\\p{IsCf}&&[^" + WS + "]]+");
|
1046 |
static private final Pattern CONTROL_PATTERN = Pattern.compile("[\\p{IsCc}\\p{IsCf}&&[^" + WS + "]]+");
|
1036 |
static private final Pattern INVALID_CHARS_PATTERN;
|
1047 |
static private final Pattern INVALID_CHARS_PATTERN;
|
1037 |
|
1048 |
|
1038 |
static {
|
1049 |
static {
|
1039 |
// from windows explorer
|
1050 |
// from windows explorer
|
1040 |
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
1051 |
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
1041 |
// Naming Files, Paths, and Namespaces
|
1052 |
// Naming Files, Paths, and Namespaces
|
1042 |
// on Mac only '/' and ':', on Linux only '/'
|
1053 |
// on Mac only '/' and ':', on Linux only '/'
|
1043 |
FILENAME_ESCAPER.add('"', 'D').add(':', 'C').add('/', 'S').add('\\', 'A');
|
1054 |
FILENAME_ESCAPER.add('"', 'D').add(':', 'C').add('/', 'S').add('\\', 'A');
|
1044 |
FILENAME_ESCAPER.add('<', 'L').add('>', 'G').add('*', 'R').add('|', 'P').add('?', 'M');
|
1055 |
FILENAME_ESCAPER.add('<', 'L').add('>', 'G').add('*', 'R').add('|', 'P').add('?', 'M');
|
1045 |
INVALID_CHARS = FILENAME_ESCAPER.getEscapedChars();
|
1056 |
INVALID_CHARS = FILENAME_ESCAPER.getEscapedChars();
|
1046 |
INVALID_CHARS_PATTERN = Pattern.compile("[" + CollectionUtils.join(INVALID_CHARS, "") + "]");
|
1057 |
INVALID_CHARS_PATTERN = Pattern.compile("[" + CollectionUtils.join(INVALID_CHARS, "") + "]");
|
1047 |
}
|
1058 |
}
|
1048 |
|
1059 |
|
1049 |
/**
|
1060 |
/**
|
1050 |
* Sanitize a name. Remove control characters, trim, and replace {@link #INVALID_CHARS} by '_'.
|
1061 |
* Sanitize a name. Remove control characters, trim, and replace {@link #INVALID_CHARS} by '_'.
|
1051 |
*
|
1062 |
*
|
1052 |
* @param name an arbitrary name.
|
1063 |
* @param name an arbitrary name.
|
1053 |
* @return a name suitable for any file system.
|
1064 |
* @return a name suitable for any file system.
|
1054 |
*/
|
1065 |
*/
|
1055 |
static public final String sanitize(String name) {
|
1066 |
static public final String sanitize(String name) {
|
1056 |
// remove control and format characters (except white spaces)
|
1067 |
// remove control and format characters (except white spaces)
|
1057 |
name = CONTROL_PATTERN.matcher(name).replaceAll("");
|
1068 |
name = CONTROL_PATTERN.matcher(name).replaceAll("");
|
1058 |
// only use one regular space (must be done after removing control characters as if they are
|
1069 |
// only use one regular space (must be done after removing control characters as if they are
|
1059 |
// between spaces we want only one space to remain)
|
1070 |
// between spaces we want only one space to remain)
|
1060 |
name = WS_PATTERN.matcher(name).replaceAll(" ");
|
1071 |
name = WS_PATTERN.matcher(name).replaceAll(" ");
|
1061 |
// leading and trailing spaces are hard to see (and illegal in Explorer)
|
1072 |
// leading and trailing spaces are hard to see (and illegal in Explorer)
|
1062 |
name = name.trim();
|
1073 |
name = name.trim();
|
1063 |
|
1074 |
|
1064 |
// replace all invalid characters with _
|
1075 |
// replace all invalid characters with _
|
1065 |
name = INVALID_CHARS_PATTERN.matcher(name).replaceAll("_");
|
1076 |
name = INVALID_CHARS_PATTERN.matcher(name).replaceAll("_");
|
1066 |
|
1077 |
|
1067 |
return name;
|
1078 |
return name;
|
1068 |
}
|
1079 |
}
|
1069 |
|
1080 |
|
1070 |
public static final FileFilter DIR_FILTER = new FileFilter() {
|
1081 |
public static final FileFilter DIR_FILTER = new FileFilter() {
|
1071 |
@Override
|
1082 |
@Override
|
1072 |
public boolean accept(File f) {
|
1083 |
public boolean accept(File f) {
|
1073 |
return f.isDirectory();
|
1084 |
return f.isDirectory();
|
1074 |
}
|
1085 |
}
|
1075 |
};
|
1086 |
};
|
1076 |
public static final FileFilter REGULAR_FILE_FILTER = new FileFilter() {
|
1087 |
public static final FileFilter REGULAR_FILE_FILTER = new FileFilter() {
|
1077 |
@Override
|
1088 |
@Override
|
1078 |
public boolean accept(File f) {
|
1089 |
public boolean accept(File f) {
|
1079 |
return f.isFile();
|
1090 |
return f.isFile();
|
1080 |
}
|
1091 |
}
|
1081 |
};
|
1092 |
};
|
1082 |
public static final Filter<Path> DIR_PATH_FILTER = new DirectoryStream.Filter<Path>() {
|
1093 |
public static final Filter<Path> DIR_PATH_FILTER = new DirectoryStream.Filter<Path>() {
|
1083 |
@Override
|
1094 |
@Override
|
1084 |
public boolean accept(Path entry) throws IOException {
|
1095 |
public boolean accept(Path entry) throws IOException {
|
1085 |
return Files.isDirectory(entry, LinkOption.NOFOLLOW_LINKS);
|
1096 |
return Files.isDirectory(entry, LinkOption.NOFOLLOW_LINKS);
|
1086 |
}
|
1097 |
}
|
1087 |
};
|
1098 |
};
|
1088 |
|
1099 |
|
1089 |
/**
|
1100 |
/**
|
1090 |
* Return a filter that select regular files ending in <code>ext</code>.
|
1101 |
* Return a filter that select regular files ending in <code>ext</code>.
|
1091 |
*
|
1102 |
*
|
1092 |
* @param ext the end of the name, eg ".xml".
|
1103 |
* @param ext the end of the name, eg ".xml".
|
1093 |
* @return the corresponding filter.
|
1104 |
* @return the corresponding filter.
|
1094 |
*/
|
1105 |
*/
|
1095 |
public static final FileFilter createEndFileFilter(final String ext) {
|
1106 |
public static final FileFilter createEndFileFilter(final String ext) {
|
1096 |
return new FileFilter() {
|
1107 |
return new FileFilter() {
|
1097 |
@Override
|
1108 |
@Override
|
1098 |
public boolean accept(File f) {
|
1109 |
public boolean accept(File f) {
|
1099 |
return f.isFile() && f.getName().endsWith(ext);
|
1110 |
return f.isFile() && f.getName().endsWith(ext);
|
1100 |
}
|
1111 |
}
|
1101 |
};
|
1112 |
};
|
1102 |
}
|
1113 |
}
|
1103 |
|
1114 |
|
1104 |
/**
|
1115 |
/**
|
1105 |
* How to merge the group and others portion of permissions for non-POSIX FS.
|
1116 |
* How to merge the group and others portion of permissions for non-POSIX FS.
|
1106 |
*
|
1117 |
*
|
1107 |
* @author sylvain
|
1118 |
* @author sylvain
|
1108 |
* @see FileUtils#setFilePermissionsFromPOSIX(File, String, GroupAndOthers)
|
1119 |
* @see FileUtils#setFilePermissionsFromPOSIX(File, String, GroupAndOthers)
|
1109 |
*/
|
1120 |
*/
|
1110 |
public static enum GroupAndOthers {
|
1121 |
public static enum GroupAndOthers {
|
1111 |
REQUIRE_SAME {
|
1122 |
REQUIRE_SAME {
|
1112 |
@Override
|
1123 |
@Override
|
1113 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1124 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1114 |
throw new IllegalArgumentException("Different permissions : " + groupPerms + " != " + otherPerms);
|
1125 |
throw new IllegalArgumentException("Different permissions : " + groupPerms + " != " + otherPerms);
|
1115 |
}
|
1126 |
}
|
1116 |
},
|
1127 |
},
|
1117 |
PERMISSIVE {
|
1128 |
PERMISSIVE {
|
1118 |
@Override
|
1129 |
@Override
|
1119 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1130 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1120 |
final EnumSet<Permission> res = EnumSet.noneOf(Permission.class);
|
1131 |
final EnumSet<Permission> res = EnumSet.noneOf(Permission.class);
|
1121 |
res.addAll(groupPerms);
|
1132 |
res.addAll(groupPerms);
|
1122 |
res.addAll(otherPerms);
|
1133 |
res.addAll(otherPerms);
|
1123 |
return res;
|
1134 |
return res;
|
1124 |
}
|
1135 |
}
|
1125 |
},
|
1136 |
},
|
1126 |
OTHERS {
|
1137 |
OTHERS {
|
1127 |
@Override
|
1138 |
@Override
|
1128 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1139 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1129 |
return otherPerms;
|
1140 |
return otherPerms;
|
1130 |
}
|
1141 |
}
|
1131 |
},
|
1142 |
},
|
1132 |
RESTRICTIVE {
|
1143 |
RESTRICTIVE {
|
1133 |
@Override
|
1144 |
@Override
|
1134 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1145 |
protected Set<Permission> getNonEqual(Set<Permission> groupPerms, Set<Permission> otherPerms) {
|
1135 |
final EnumSet<Permission> res = EnumSet.allOf(Permission.class);
|
1146 |
final EnumSet<Permission> res = EnumSet.allOf(Permission.class);
|
1136 |
res.retainAll(groupPerms);
|
1147 |
res.retainAll(groupPerms);
|
1137 |
res.retainAll(otherPerms);
|
1148 |
res.retainAll(otherPerms);
|
1138 |
return res;
|
1149 |
return res;
|
1139 |
}
|
1150 |
}
|
1140 |
};
|
1151 |
};
|
1141 |
|
1152 |
|
1142 |
public final Set<Permission> getPermissions(final Set<Permission> groupPerms, final Set<Permission> otherPerms) {
|
1153 |
public final Set<Permission> getPermissions(final Set<Permission> groupPerms, final Set<Permission> otherPerms) {
|
1143 |
if (groupPerms.equals(otherPerms)) {
|
1154 |
if (groupPerms.equals(otherPerms)) {
|
1144 |
return groupPerms;
|
1155 |
return groupPerms;
|
1145 |
} else {
|
1156 |
} else {
|
1146 |
return getNonEqual(groupPerms, otherPerms);
|
1157 |
return getNonEqual(groupPerms, otherPerms);
|
1147 |
}
|
1158 |
}
|
1148 |
}
|
1159 |
}
|
1149 |
|
1160 |
|
1150 |
public final Set<Permission> getPermissions(final String posixPerms) {
|
1161 |
public final Set<Permission> getPermissions(final String posixPerms) {
|
1151 |
final Set<Permission> groupPerms = Permission.fromString(posixPerms.substring(3, 6));
|
1162 |
final Set<Permission> groupPerms = Permission.fromString(posixPerms.substring(3, 6));
|
1152 |
final Set<Permission> otherPerms = Permission.fromString(posixPerms.substring(6, 9));
|
1163 |
final Set<Permission> otherPerms = Permission.fromString(posixPerms.substring(6, 9));
|
1153 |
return this.getPermissions(groupPerms, otherPerms);
|
1164 |
return this.getPermissions(groupPerms, otherPerms);
|
1154 |
}
|
1165 |
}
|
1155 |
|
1166 |
|
1156 |
protected abstract Set<Permission> getNonEqual(final Set<Permission> groupPerms, final Set<Permission> otherPerms);
|
1167 |
protected abstract Set<Permission> getNonEqual(final Set<Permission> groupPerms, final Set<Permission> otherPerms);
|
1157 |
|
1168 |
|
1158 |
}
|
1169 |
}
|
1159 |
|
1170 |
|
1160 |
public static final String setPermissions(final Path p, final String posixPerms) throws IOException {
|
1171 |
public static final String setPermissions(final Path p, final String posixPerms) throws IOException {
|
1161 |
return setPermissions(p, posixPerms, GroupAndOthers.RESTRICTIVE);
|
1172 |
return setPermissions(p, posixPerms, GroupAndOthers.RESTRICTIVE);
|
1162 |
}
|
1173 |
}
|
1163 |
|
1174 |
|
1164 |
/**
|
1175 |
/**
|
1165 |
* Use {@link PosixFileAttributeView#setPermissions(Set)} if possible, otherwise use
|
1176 |
* Use {@link PosixFileAttributeView#setPermissions(Set)} if possible, otherwise use
|
1166 |
* {@link #setFilePermissionsFromPOSIX(File, String, GroupAndOthers)}.
|
1177 |
* {@link #setFilePermissionsFromPOSIX(File, String, GroupAndOthers)}.
|
1167 |
*
|
1178 |
*
|
1168 |
* @param p the path to change.
|
1179 |
* @param p the path to change.
|
1169 |
* @param posixPerms the new permissions to apply.
|
1180 |
* @param posixPerms the new permissions to apply.
|
1170 |
* @param groupAndOthers only for non-POSIX FS, how to merge group and others portion.
|
1181 |
* @param groupAndOthers only for non-POSIX FS, how to merge group and others portion.
|
1171 |
* @return the permission applied, 9 characters for POSIX, 6 for non-POSIX (i.e. 3 for owner, 3
|
1182 |
* @return the permission applied, 9 characters for POSIX, 6 for non-POSIX (i.e. 3 for owner, 3
|
1172 |
* for the rest), <code>null</code> if some permissions couldn't be applied (only on
|
1183 |
* for the rest), <code>null</code> if some permissions couldn't be applied (only on
|
1173 |
* non-POSIX).
|
1184 |
* non-POSIX).
|
1174 |
* @throws IOException if permissions couldn't be applied.
|
1185 |
* @throws IOException if permissions couldn't be applied.
|
1175 |
*/
|
1186 |
*/
|
1176 |
public static final String setPermissions(final Path p, final String posixPerms, final GroupAndOthers groupAndOthers) throws IOException {
|
1187 |
public static final String setPermissions(final Path p, final String posixPerms, final GroupAndOthers groupAndOthers) throws IOException {
|
1177 |
final String res;
|
1188 |
final String res;
|
1178 |
final PosixFileAttributeView view = Files.getFileAttributeView(p, PosixFileAttributeView.class);
|
1189 |
final PosixFileAttributeView view = Files.getFileAttributeView(p, PosixFileAttributeView.class);
|
1179 |
if (view != null) {
|
1190 |
if (view != null) {
|
1180 |
view.setPermissions(PosixFilePermissions.fromString(posixPerms));
|
1191 |
view.setPermissions(PosixFilePermissions.fromString(posixPerms));
|
1181 |
res = posixPerms;
|
1192 |
res = posixPerms;
|
1182 |
} else {
|
1193 |
} else {
|
1183 |
// final Set<Permission> notOwnerPerms = setFilePermissions(p.toFile(), pfp,
|
1194 |
// final Set<Permission> notOwnerPerms = setFilePermissions(p.toFile(), pfp,
|
1184 |
// groupAndOthers);
|
1195 |
// groupAndOthers);
|
1185 |
final Set<Permission> notOwnerPerms = setFilePermissionsFromPOSIX(p.toFile(), posixPerms, groupAndOthers);
|
1196 |
final Set<Permission> notOwnerPerms = setFilePermissionsFromPOSIX(p.toFile(), posixPerms, groupAndOthers);
|
1186 |
res = notOwnerPerms == null ? null : posixPerms.substring(0, 3) + Permission.get3chars(notOwnerPerms);
|
1197 |
res = notOwnerPerms == null ? null : posixPerms.substring(0, 3) + Permission.get3chars(notOwnerPerms);
|
1187 |
}
|
1198 |
}
|
1188 |
return res;
|
1199 |
return res;
|
1189 |
}
|
1200 |
}
|
1190 |
|
1201 |
|
1191 |
public static final Set<Permission> setFilePermissionsFromPOSIX(final File f, final String posixPerms) {
|
1202 |
public static final Set<Permission> setFilePermissionsFromPOSIX(final File f, final String posixPerms) {
|
1192 |
return setFilePermissionsFromPOSIX(f, posixPerms, GroupAndOthers.RESTRICTIVE);
|
1203 |
return setFilePermissionsFromPOSIX(f, posixPerms, GroupAndOthers.RESTRICTIVE);
|
1193 |
}
|
1204 |
}
|
1194 |
|
1205 |
|
1195 |
/**
|
1206 |
/**
|
1196 |
* This method doesn't need POSIX but must merge permissions before applying them.
|
1207 |
* This method doesn't need POSIX but must merge permissions before applying them.
|
1197 |
*
|
1208 |
*
|
1198 |
* @param f the file to change.
|
1209 |
* @param f the file to change.
|
1199 |
* @param posixPerms the POSIX permissions to merge.
|
1210 |
* @param posixPerms the POSIX permissions to merge.
|
1200 |
* @param groupAndOthers how to merge.
|
1211 |
* @param groupAndOthers how to merge.
|
1201 |
* @return the merged permissions for the "not owner" portion, or <code>null</code> if some
|
1212 |
* @return the merged permissions for the "not owner" portion, or <code>null</code> if some
|
1202 |
* permissions couldn't be set.
|
1213 |
* permissions couldn't be set.
|
1203 |
* @see #setFilePermissions(File, Set, Set)
|
1214 |
* @see #setFilePermissions(File, Set, Set)
|
1204 |
*/
|
1215 |
*/
|
1205 |
public static final Set<Permission> setFilePermissionsFromPOSIX(final File f, final String posixPerms, final GroupAndOthers groupAndOthers) {
|
1216 |
public static final Set<Permission> setFilePermissionsFromPOSIX(final File f, final String posixPerms, final GroupAndOthers groupAndOthers) {
|
1206 |
if (posixPerms.length() != 9)
|
1217 |
if (posixPerms.length() != 9)
|
1207 |
throw new IllegalArgumentException("Invalid mode : " + posixPerms);
|
1218 |
throw new IllegalArgumentException("Invalid mode : " + posixPerms);
|
1208 |
final Set<Permission> ownerPerms = Permission.fromString(posixPerms.substring(0, 3));
|
1219 |
final Set<Permission> ownerPerms = Permission.fromString(posixPerms.substring(0, 3));
|
1209 |
final Set<Permission> notOwnerPerms = groupAndOthers.getPermissions(posixPerms);
|
1220 |
final Set<Permission> notOwnerPerms = groupAndOthers.getPermissions(posixPerms);
|
1210 |
assert notOwnerPerms != null;
|
1221 |
assert notOwnerPerms != null;
|
1211 |
final boolean success = setFilePermissions(f, ownerPerms, notOwnerPerms);
|
1222 |
final boolean success = setFilePermissions(f, ownerPerms, notOwnerPerms);
|
1212 |
return success ? notOwnerPerms : null;
|
1223 |
return success ? notOwnerPerms : null;
|
1213 |
}
|
1224 |
}
|
1214 |
|
1225 |
|
1215 |
/**
|
1226 |
/**
|
1216 |
* Use {@link File} methods to set permissions. This works everywhere but group and others are
|
1227 |
* Use {@link File} methods to set permissions. This works everywhere but group and others are
|
1217 |
* treated as the same.
|
1228 |
* treated as the same.
|
1218 |
*
|
1229 |
*
|
1219 |
* @param f the file to change.
|
1230 |
* @param f the file to change.
|
1220 |
* @param owner the permissions for the owner.
|
1231 |
* @param owner the permissions for the owner.
|
1221 |
* @param notOwner the permissions for not the owner.
|
1232 |
* @param notOwner the permissions for not the owner.
|
1222 |
* @return <code>true</code> if all asked permissions were set.
|
1233 |
* @return <code>true</code> if all asked permissions were set.
|
1223 |
* @see File#setReadable(boolean, boolean)
|
1234 |
* @see File#setReadable(boolean, boolean)
|
1224 |
* @see File#setWritable(boolean, boolean)
|
1235 |
* @see File#setWritable(boolean, boolean)
|
1225 |
* @see File#setExecutable(boolean, boolean)
|
1236 |
* @see File#setExecutable(boolean, boolean)
|
1226 |
*/
|
1237 |
*/
|
1227 |
public static final boolean setFilePermissions(final File f, final Set<Permission> owner, final Set<Permission> notOwner) {
|
1238 |
public static final boolean setFilePermissions(final File f, final Set<Permission> owner, final Set<Permission> notOwner) {
|
1228 |
boolean res = setFilePermissions(f, notOwner, false);
|
1239 |
boolean res = setFilePermissions(f, notOwner, false);
|
1229 |
if (!owner.equals(notOwner)) {
|
1240 |
if (!owner.equals(notOwner)) {
|
1230 |
res &= setFilePermissions(f, owner, true);
|
1241 |
res &= setFilePermissions(f, owner, true);
|
1231 |
}
|
1242 |
}
|
1232 |
return res;
|
1243 |
return res;
|
1233 |
}
|
1244 |
}
|
1234 |
|
1245 |
|
1235 |
public static final boolean setFilePermissions(final File f, final Set<Permission> perms, final boolean ownerOnly) {
|
1246 |
public static final boolean setFilePermissions(final File f, final Set<Permission> perms, final boolean ownerOnly) {
|
1236 |
boolean res = f.setReadable(perms.contains(Permission.READ), ownerOnly);
|
1247 |
boolean res = f.setReadable(perms.contains(Permission.READ), ownerOnly);
|
1237 |
res &= f.setWritable(perms.contains(Permission.WRITE), ownerOnly);
|
1248 |
res &= f.setWritable(perms.contains(Permission.WRITE), ownerOnly);
|
1238 |
res &= f.setExecutable(perms.contains(Permission.EXECUTE), ownerOnly);
|
1249 |
res &= f.setExecutable(perms.contains(Permission.EXECUTE), ownerOnly);
|
1239 |
return res;
|
1250 |
return res;
|
1240 |
}
|
1251 |
}
|
1241 |
|
1252 |
|
1242 |
public static enum Permission {
|
1253 |
public static enum Permission {
|
1243 |
READ, WRITE, EXECUTE;
|
1254 |
READ, WRITE, EXECUTE;
|
1244 |
|
1255 |
|
1245 |
public static final Permission R = READ;
|
1256 |
public static final Permission R = READ;
|
1246 |
public static final Permission W = WRITE;
|
1257 |
public static final Permission W = WRITE;
|
1247 |
public static final Permission X = EXECUTE;
|
1258 |
public static final Permission X = EXECUTE;
|
1248 |
public static final Map<String, Set<Permission>> FROM_STRING = new HashMap<>();
|
1259 |
public static final Map<String, Set<Permission>> FROM_STRING = new HashMap<>();
|
1249 |
public static final Pattern MINUS_PATTERN = Pattern.compile("-+");
|
1260 |
public static final Pattern MINUS_PATTERN = Pattern.compile("-+");
|
1250 |
static {
|
1261 |
static {
|
1251 |
putString("---", Collections.<Permission> emptySet());
|
1262 |
putString("---", Collections.<Permission> emptySet());
|
1252 |
putString("--x", Collections.singleton(EXECUTE));
|
1263 |
putString("--x", Collections.singleton(EXECUTE));
|
1253 |
putString("-w-", Collections.singleton(WRITE));
|
1264 |
putString("-w-", Collections.singleton(WRITE));
|
1254 |
putString("-wx", EnumSet.of(WRITE, EXECUTE));
|
1265 |
putString("-wx", EnumSet.of(WRITE, EXECUTE));
|
1255 |
putString("r--", Collections.singleton(READ));
|
1266 |
putString("r--", Collections.singleton(READ));
|
1256 |
putString("r-x", EnumSet.of(READ, EXECUTE));
|
1267 |
putString("r-x", EnumSet.of(READ, EXECUTE));
|
1257 |
putString("rw-", EnumSet.of(READ, WRITE));
|
1268 |
putString("rw-", EnumSet.of(READ, WRITE));
|
1258 |
putString("rwx", EnumSet.allOf(Permission.class));
|
1269 |
putString("rwx", EnumSet.allOf(Permission.class));
|
1259 |
}
|
1270 |
}
|
1260 |
|
1271 |
|
1261 |
static private final void putString(final String str, final EnumSet<Permission> set) {
|
1272 |
static private final void putString(final String str, final EnumSet<Permission> set) {
|
1262 |
putString(str, Collections.unmodifiableSet(set));
|
1273 |
putString(str, Collections.unmodifiableSet(set));
|
1263 |
}
|
1274 |
}
|
1264 |
|
1275 |
|
1265 |
static private final void putString(final String str, final Set<Permission> unmodifiableSet) {
|
1276 |
static private final void putString(final String str, final Set<Permission> unmodifiableSet) {
|
1266 |
FROM_STRING.put(str, unmodifiableSet);
|
1277 |
FROM_STRING.put(str, unmodifiableSet);
|
1267 |
FROM_STRING.put(MINUS_PATTERN.matcher(str).replaceAll(""), unmodifiableSet);
|
1278 |
FROM_STRING.put(MINUS_PATTERN.matcher(str).replaceAll(""), unmodifiableSet);
|
1268 |
}
|
1279 |
}
|
1269 |
|
1280 |
|
1270 |
static public final Set<Permission> fromString(final String str) {
|
1281 |
static public final Set<Permission> fromString(final String str) {
|
1271 |
final Set<Permission> res = FROM_STRING.get(str);
|
1282 |
final Set<Permission> res = FROM_STRING.get(str);
|
1272 |
if (res == null)
|
1283 |
if (res == null)
|
1273 |
throw new IllegalArgumentException("Invalid string : " + str);
|
1284 |
throw new IllegalArgumentException("Invalid string : " + str);
|
1274 |
return res;
|
1285 |
return res;
|
1275 |
}
|
1286 |
}
|
1276 |
|
1287 |
|
1277 |
public static final String get3chars(final Set<Permission> perms) {
|
1288 |
public static final String get3chars(final Set<Permission> perms) {
|
1278 |
return get3chars(perms.contains(READ), perms.contains(WRITE), perms.contains(EXECUTE));
|
1289 |
return get3chars(perms.contains(READ), perms.contains(WRITE), perms.contains(EXECUTE));
|
1279 |
}
|
1290 |
}
|
1280 |
|
1291 |
|
1281 |
private static final String get3chars(final boolean read, final boolean write, final boolean exec) {
|
1292 |
private static final String get3chars(final boolean read, final boolean write, final boolean exec) {
|
1282 |
final StringBuilder sb = new StringBuilder(3);
|
1293 |
final StringBuilder sb = new StringBuilder(3);
|
1283 |
sb.append(read ? 'r' : '-');
|
1294 |
sb.append(read ? 'r' : '-');
|
1284 |
sb.append(write ? 'w' : '-');
|
1295 |
sb.append(write ? 'w' : '-');
|
1285 |
sb.append(exec ? 'x' : '-');
|
1296 |
sb.append(exec ? 'x' : '-');
|
1286 |
return sb.toString();
|
1297 |
return sb.toString();
|
1287 |
}
|
1298 |
}
|
1288 |
}
|
1299 |
}
|
1289 |
}
|
1300 |
}
|