View Javadoc
1   /*
2    * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg@dewire.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.util;
45  
46  import java.io.File;
47  import java.io.IOException;
48  import java.nio.file.Files;
49  import java.nio.file.LinkOption;
50  import java.nio.file.NoSuchFileException;
51  import java.nio.file.Path;
52  import java.nio.file.attribute.BasicFileAttributeView;
53  import java.nio.file.attribute.BasicFileAttributes;
54  import java.nio.file.attribute.FileTime;
55  import java.nio.file.attribute.PosixFileAttributeView;
56  import java.nio.file.attribute.PosixFileAttributes;
57  import java.nio.file.attribute.PosixFilePermission;
58  import java.text.Normalizer;
59  import java.text.Normalizer.Form;
60  
61  import org.eclipse.jgit.lib.Constants;
62  import org.eclipse.jgit.util.FS.Attributes;
63  
64  /**
65   * File utilities using Java 7 NIO2
66   */
67  public class FileUtil {
68  
69  	static class Java7BasicAttributes extends Attributes {
70  
71  		Java7BasicAttributes(FS fs, File fPath, boolean exists,
72  				boolean isDirectory, boolean isExecutable,
73  				boolean isSymbolicLink, boolean isRegularFile,
74  				long creationTime, long lastModifiedTime, long length) {
75  			super(fs, fPath, exists, isDirectory, isExecutable, isSymbolicLink,
76  					isRegularFile, creationTime, lastModifiedTime, length);
77  		}
78  	}
79  
80  	/**
81  	 * @param path
82  	 * @return target path of the symlink
83  	 * @throws IOException
84  	 */
85  	public static String readSymlink(File path) throws IOException {
86  		Path nioPath = path.toPath();
87  		Path target = Files.readSymbolicLink(nioPath);
88  		String targetString = target.toString();
89  		if (SystemReader.getInstance().isWindows())
90  			targetString = targetString.replace('\\', '/');
91  		else if (SystemReader.getInstance().isMacOS())
92  			targetString = Normalizer.normalize(targetString, Form.NFC);
93  		return targetString;
94  	}
95  
96  	/**
97  	 * @param path
98  	 *            path of the symlink to be created
99  	 * @param target
100 	 *            target of the symlink to be created
101 	 * @throws IOException
102 	 */
103 	public static void createSymLink(File path, String target)
104 			throws IOException {
105 		Path nioPath = path.toPath();
106 		if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS))
107 			Files.delete(nioPath);
108 		if (SystemReader.getInstance().isWindows())
109 			target = target.replace('/', '\\');
110 		Path nioTarget = new File(target).toPath();
111 		Files.createSymbolicLink(nioPath, nioTarget);
112 	}
113 
114 	/**
115 	 * @param path
116 	 * @return {@code true} if the passed path is a symlink
117 	 */
118 	public static boolean isSymlink(File path) {
119 		Path nioPath = path.toPath();
120 		return Files.isSymbolicLink(nioPath);
121 	}
122 
123 	/**
124 	 * @param path
125 	 * @return lastModified attribute for given path
126 	 * @throws IOException
127 	 */
128 	public static long lastModified(File path) throws IOException {
129 		Path nioPath = path.toPath();
130 		return Files.getLastModifiedTime(nioPath, LinkOption.NOFOLLOW_LINKS)
131 				.toMillis();
132 	}
133 
134 	/**
135 	 * @param path
136 	 * @param time
137 	 * @throws IOException
138 	 */
139 	public static void setLastModified(File path, long time) throws IOException {
140 		Path nioPath = path.toPath();
141 		Files.setLastModifiedTime(nioPath, FileTime.fromMillis(time));
142 	}
143 
144 	/**
145 	 * @param path
146 	 * @return {@code true} if the given path exists
147 	 */
148 	public static boolean exists(File path) {
149 		Path nioPath = path.toPath();
150 		return Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS);
151 	}
152 
153 	/**
154 	 * @param path
155 	 * @return {@code true} if the given path is hidden
156 	 * @throws IOException
157 	 */
158 	public static boolean isHidden(File path) throws IOException {
159 		Path nioPath = path.toPath();
160 		return Files.isHidden(nioPath);
161 	}
162 
163 	/**
164 	 * @param path
165 	 * @param hidden
166 	 * @throws IOException
167 	 */
168 	public static void setHidden(File path, boolean hidden) throws IOException {
169 		Path nioPath = path.toPath();
170 		Files.setAttribute(nioPath, "dos:hidden", Boolean.valueOf(hidden), //$NON-NLS-1$
171 				LinkOption.NOFOLLOW_LINKS);
172 	}
173 
174 	/**
175 	 * @param path
176 	 * @return length of the given file
177 	 * @throws IOException
178 	 */
179 	public static long getLength(File path) throws IOException {
180 		Path nioPath = path.toPath();
181 		if (Files.isSymbolicLink(nioPath))
182 			return Files.readSymbolicLink(nioPath).toString()
183 					.getBytes(Constants.CHARSET).length;
184 		return Files.size(nioPath);
185 	}
186 
187 	/**
188 	 * @param path
189 	 * @return {@code true} if the given file a directory
190 	 */
191 	public static boolean isDirectory(File path) {
192 		Path nioPath = path.toPath();
193 		return Files.isDirectory(nioPath, LinkOption.NOFOLLOW_LINKS);
194 	}
195 
196 	/**
197 	 * @param path
198 	 * @return {@code true} if the given file is a file
199 	 */
200 	public static boolean isFile(File path) {
201 		Path nioPath = path.toPath();
202 		return Files.isRegularFile(nioPath, LinkOption.NOFOLLOW_LINKS);
203 	}
204 
205 	/**
206 	 * @param path
207 	 * @return {@code true} if the given file can be executed
208 	 */
209 	public static boolean canExecute(File path) {
210 		if (!isFile(path))
211 			return false;
212 		return path.canExecute();
213 	}
214 
215 	/**
216 	 * @param path
217 	 * @throws IOException
218 	 */
219 	public static void delete(File path) throws IOException {
220 		Path nioPath = path.toPath();
221 		Files.delete(nioPath);
222 	}
223 
224 	static Attributes getFileAttributesBasic(FS fs, File path) {
225 		try {
226 			Path nioPath = path.toPath();
227 			BasicFileAttributes readAttributes = nioPath
228 					.getFileSystem()
229 					.provider()
230 					.getFileAttributeView(nioPath,
231 							BasicFileAttributeView.class,
232 							LinkOption.NOFOLLOW_LINKS).readAttributes();
233 			Attributes attributes = new FileUtil.Java7BasicAttributes(fs, path,
234 					true,
235 					readAttributes.isDirectory(),
236 					fs.supportsExecute() ? path.canExecute() : false,
237 					readAttributes.isSymbolicLink(),
238 					readAttributes.isRegularFile(), //
239 					readAttributes.creationTime().toMillis(), //
240 					readAttributes.lastModifiedTime().toMillis(),
241 					readAttributes.isSymbolicLink() ? Constants
242 							.encode(FileUtils.readSymLink(path)).length
243 							: readAttributes.size());
244 			return attributes;
245 		} catch (NoSuchFileException e) {
246 			return new FileUtil.Java7BasicAttributes(fs, path, false, false,
247 					false, false, false, 0L, 0L, 0L);
248 		} catch (IOException e) {
249 			return new Attributes(path, fs);
250 		}
251 	}
252 
253 	/**
254 	 * @param fs
255 	 * @param path
256 	 * @return file system attributes for the given file
257 	 */
258 	public static Attributes getFileAttributesPosix(FS fs, File path) {
259 		try {
260 			Path nioPath = path.toPath();
261 			PosixFileAttributes readAttributes = nioPath
262 					.getFileSystem()
263 					.provider()
264 					.getFileAttributeView(nioPath,
265 							PosixFileAttributeView.class,
266 							LinkOption.NOFOLLOW_LINKS).readAttributes();
267 			Attributes attributes = new FileUtil.Java7BasicAttributes(
268 					fs,
269 					path,
270 					true, //
271 					readAttributes.isDirectory(), //
272 					readAttributes.permissions().contains(
273 							PosixFilePermission.OWNER_EXECUTE),
274 					readAttributes.isSymbolicLink(),
275 					readAttributes.isRegularFile(), //
276 					readAttributes.creationTime().toMillis(), //
277 					readAttributes.lastModifiedTime().toMillis(),
278 					readAttributes.size());
279 			return attributes;
280 		} catch (NoSuchFileException e) {
281 			return new FileUtil.Java7BasicAttributes(fs, path, false, false,
282 					false, false, false, 0L, 0L, 0L);
283 		} catch (IOException e) {
284 			return new Attributes(path, fs);
285 		}
286 	}
287 
288 	/**
289 	 * @param file
290 	 * @return on Mac: NFC normalized {@link File}, otherwise the passed file
291 	 */
292 	public static File normalize(File file) {
293 		if (SystemReader.getInstance().isMacOS()) {
294 			// TODO: Would it be faster to check with isNormalized first
295 			// assuming normalized paths are much more common
296 			String normalized = Normalizer.normalize(file.getPath(),
297 					Normalizer.Form.NFC);
298 			return new File(normalized);
299 		}
300 		return file;
301 	}
302 
303 	/**
304 	 * @param name
305 	 * @return on Mac: NFC normalized form of given name
306 	 */
307 	public static String normalize(String name) {
308 		if (SystemReader.getInstance().isMacOS()) {
309 			if (name == null)
310 				return null;
311 			return Normalizer.normalize(name, Normalizer.Form.NFC);
312 		}
313 		return name;
314 	}
315 
316 }