View Javadoc

1   /**
2    * Copyright 2010-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util;
17  
18  import java.io.BufferedReader;
19  import java.io.BufferedWriter;
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.InputStreamReader;
24  import java.io.OutputStream;
25  import java.io.OutputStreamWriter;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.io.Writer;
29  import java.net.MalformedURLException;
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  import java.net.URL;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.Collections;
36  import java.util.List;
37  
38  import org.apache.commons.io.FileUtils;
39  import org.apache.commons.io.IOUtils;
40  import org.apache.commons.lang3.StringUtils;
41  import org.springframework.core.io.DefaultResourceLoader;
42  import org.springframework.core.io.Resource;
43  import org.springframework.core.io.ResourceLoader;
44  
45  public class LocationUtils {
46  
47  	private static final String FILE_PREFIX = "file:";
48  	private static final String BACK_SLASH = "\\";
49  	private static final String FORWARD_SLASH = "/";
50  	private static final String SLASH_DOT_SLASH = "/./";
51  	private static final String DOT_DOT_SLASH = "../";
52  	private static final String SLASH_DOT_DOT = "/..";
53  	private static final String CLASSPATH = "classpath:";
54  
55  	/**
56  	 * Return the text that appears after <code>classpath:</code>. Throws <code>IllegalArgumentException</code> if location does not start
57  	 * with <code>classpath:</code>
58  	 */
59  	public static final String getClasspathFilename(String location) {
60  		return getClasspathFilenames(Arrays.asList(location)).get(0);
61  	}
62  
63  	/**
64  	 * Return the text that appears after <code>classpath:</code>. Throws <code>IllegalArgumentException</code> if any locations do not
65  	 * start with <code>classpath:</code>
66  	 */
67  	public static final List<String> getClasspathFilenames(List<String> locations) {
68  		List<String> classpathFilenames = new ArrayList<String>();
69  		for (String location : locations) {
70  			if (!isClasspathLocation(location)) {
71  				throw new IllegalArgumentException(location + " must start with " + CLASSPATH);
72  			} else {
73  				classpathFilenames.add(StringUtils.substring(location, CLASSPATH.length()));
74  			}
75  		}
76  		return classpathFilenames;
77  	}
78  
79  	/**
80  	 * Return <code>true</code> if location starts with <code>classpath:</code>
81  	 */
82  	public static final boolean isClasspathLocation(String location) {
83  		return StringUtils.startsWith(location, CLASSPATH);
84  	}
85  
86  	public static final List<String> getNormalizedPathFragments(String absolutePath, boolean directory) {
87  		String normalized = getNormalizedAbsolutePath(absolutePath);
88  		String[] tokens = StringUtils.split(normalized, FORWARD_SLASH);
89  		List<String> fragments = new ArrayList<String>();
90  		StringBuilder sb = new StringBuilder();
91  		sb.append(FORWARD_SLASH);
92  		int length = directory ? tokens.length : tokens.length - 1;
93  		for (int i = 0; i < length; i++) {
94  			if (i != 0) {
95  				sb.append(FORWARD_SLASH);
96  			}
97  			sb.append(tokens[i]);
98  			fragments.add(sb.toString());
99  		}
100 		return fragments;
101 	}
102 
103 	public static final List<String> getLocations(String location, LocationType type, String encoding) {
104 		switch (type) {
105 		case LOCATION:
106 			return Collections.singletonList(location);
107 		case LOCATIONLIST:
108 			return getLocations(location, encoding);
109 		default:
110 			throw new IllegalArgumentException("Location type '" + type + "' is unknown");
111 		}
112 	}
113 
114 	public static final List<String> getLocations(String location, LocationType type) {
115 		return getLocations(location, type, null);
116 	}
117 
118 	public static final List<String> getLocations(String locationListing) {
119 		return getLocations(Collections.singletonList(locationListing), null);
120 	}
121 
122 	public static final List<String> getLocations(String locationListing, String encoding) {
123 		return getLocations(Collections.singletonList(locationListing), encoding);
124 	}
125 
126 	public static final List<String> getLocations(List<String> locationListings) {
127 		return getLocations(locationListings, null);
128 	}
129 
130 	public static final void copyLocationToFile(String location, File destination, String encoding) {
131 		Assert.notNull(location);
132 		Assert.notNull(destination);
133 		InputStream in = null;
134 		try {
135 			in = getInputStream(location);
136 			FileUtils.copyInputStreamToFile(in, destination);
137 		} catch (IOException e) {
138 			throw new IllegalStateException(e);
139 		} finally {
140 			IOUtils.closeQuietly(in);
141 		}
142 	}
143 
144 	public static final List<File> getFiles(File dir, List<String> filenames) {
145 		List<File> files = new ArrayList<File>();
146 		for (String filename : filenames) {
147 			File file = new File(dir, filename);
148 			files.add(file);
149 		}
150 		return files;
151 	}
152 
153 	public static final List<String> getFilenames(List<String> locations) {
154 		Assert.notNull(locations);
155 		List<String> filenames = new ArrayList<String>();
156 		for (String location : locations) {
157 			filenames.add(getFilename(location));
158 		}
159 		return filenames;
160 	}
161 
162 	public static final List<String> getLocations(List<String> locationListings, String encoding) {
163 		List<String> locations = new ArrayList<String>();
164 		for (String locationListing : locationListings) {
165 			List<String> lines = readLines(locationListing, encoding);
166 			locations.addAll(lines);
167 		}
168 		return locations;
169 	}
170 
171 	public static final String getCanonicalURLString(File file) {
172 		if (file == null) {
173 			return null;
174 		}
175 		String path = getCanonicalPath(file);
176 		File canonical = new File(path);
177 		return getURLString(canonical);
178 	}
179 
180 	public static final void validateNormalizedPath(String originalPath, String normalizedPath) {
181 		if (CollectionUtils.containsAny(normalizedPath, Arrays.asList(SLASH_DOT_DOT, SLASH_DOT_SLASH, DOT_DOT_SLASH))) {
182 			throw new IllegalArgumentException("[" + originalPath + "] could not be normalized. Normalized path [" + normalizedPath + "]");
183 		}
184 	}
185 
186 	/**
187 	 * Resolve and remove <code>..</code> and <code>.</code> from <code>absolutePath</code> after converting any back slashes to forward
188 	 * slashes
189 	 */
190 	public static final String getNormalizedAbsolutePath(String absolutePath) {
191 		if (absolutePath == null) {
192 			return null;
193 		}
194 		String replaced = StringUtils.replace(absolutePath, BACK_SLASH, FORWARD_SLASH);
195 		boolean absolute = StringUtils.startsWith(replaced, FORWARD_SLASH);
196 		if (!absolute) {
197 			throw new IllegalArgumentException("[" + absolutePath + "] is not an absolute path.");
198 		}
199 		String prefixed = FILE_PREFIX + replaced;
200 		try {
201 			URI rawURI = new URI(prefixed);
202 			URI normalizedURI = rawURI.normalize();
203 			URL normalizedURL = normalizedURI.toURL();
204 			String externalForm = normalizedURL.toExternalForm();
205 			String trimmed = StringUtils.substring(externalForm, FILE_PREFIX.length());
206 			validateNormalizedPath(absolutePath, trimmed);
207 			return trimmed;
208 		} catch (MalformedURLException e) {
209 			throw new IllegalArgumentException(e);
210 		} catch (URISyntaxException e) {
211 			throw new IllegalArgumentException(e);
212 		}
213 	}
214 
215 	public static final String getURLString(File file) {
216 		if (file == null) {
217 			return null;
218 		}
219 		try {
220 			URI uri = file.toURI();
221 			URL url = uri.toURL();
222 			return url.toExternalForm();
223 		} catch (MalformedURLException e) {
224 			throw new IllegalArgumentException(e);
225 		}
226 	}
227 
228 	public static final void forceMkdir(File file) {
229 		try {
230 			FileUtils.forceMkdir(file);
231 		} catch (IOException e) {
232 			throw new IllegalArgumentException("Unexpected IO error", e);
233 		}
234 	}
235 
236 	public static final void touch(File file) {
237 		try {
238 			FileUtils.touch(file);
239 		} catch (IOException e) {
240 			throw new IllegalArgumentException("Unexpected IO error", e);
241 		}
242 	}
243 
244 	public static final String getCanonicalPath(File file) {
245 		try {
246 			return file.getCanonicalPath();
247 		} catch (IOException e) {
248 			throw new IllegalArgumentException("Unexpected IO error", e);
249 		}
250 	}
251 
252 	/**
253 	 * Null safe method to unconditionally attempt to delete <code>filename</code> without throwing an exception. If <code>filename</code>
254 	 * is a directory, delete it and all sub-directories.
255 	 */
256 	public static final boolean deleteFileQuietly(String filename) {
257 		File file = getFileQuietly(filename);
258 		return FileUtils.deleteQuietly(file);
259 	}
260 
261 	/**
262 	 * Null safe method for getting a <code>File</code> handle from <code>filename</code>. If <code>filename</code> is null, null is
263 	 * returned.
264 	 */
265 	public static final File getFileQuietly(String filename) {
266 		if (filename == null) {
267 			return null;
268 		} else {
269 			return new File(filename);
270 		}
271 	}
272 
273 	/**
274 	 * Get the contents of <code>location</code> as a <code>String</code> using the platform's default character encoding.
275 	 */
276 	public static final String toString(String location) {
277 		return toString(location, null);
278 	}
279 
280 	/**
281 	 * Get the contents of <code>location</code> as a <code>String</code> using the specified character encoding.
282 	 */
283 	public static final String toString(String location, String encoding) {
284 		InputStream in = null;
285 		try {
286 			in = getInputStream(location);
287 			if (encoding == null) {
288 				return IOUtils.toString(in);
289 			} else {
290 				return IOUtils.toString(in, encoding);
291 			}
292 		} catch (IOException e) {
293 			throw new IllegalStateException("Unexpected IO error", e);
294 		} finally {
295 			IOUtils.closeQuietly(in);
296 		}
297 	}
298 
299 	/**
300 	 * Get the contents of <code>s</code> as a list of <code>String's</code> one entry per line
301 	 */
302 	public static final List<String> readLinesFromString(String s) {
303 		Reader reader = getBufferedReaderFromString(s);
304 		return readLinesAndClose(reader);
305 	}
306 
307 	public static final List<String> readLinesAndClose(InputStream in) {
308 		return readLinesAndClose(in, null);
309 	}
310 
311 	public static final List<String> readLinesAndClose(InputStream in, String encoding) {
312 		Reader reader = null;
313 		try {
314 			reader = getBufferedReader(in, encoding);
315 			return readLinesAndClose(reader);
316 		} catch (IOException e) {
317 			throw new IllegalStateException("Unexpected IO error", e);
318 		} finally {
319 			IOUtils.closeQuietly(reader);
320 		}
321 	}
322 
323 	public static final List<String> readLinesAndClose(Reader reader) {
324 		try {
325 			return IOUtils.readLines(reader);
326 		} catch (IOException e) {
327 			throw new IllegalStateException("Unexpected IO error", e);
328 		} finally {
329 			IOUtils.closeQuietly(reader);
330 		}
331 	}
332 
333 	/**
334 	 * Get the contents of <code>file</code> as a list of <code>String's</code> one entry per line using the platform default encoding
335 	 */
336 	public static final List<String> readLines(File file) {
337 		return readLines(getCanonicalPath(file));
338 	}
339 
340 	/**
341 	 * Get the contents of <code>location</code> as a list of <code>String's</code> one entry per line using the platform default encoding
342 	 */
343 	public static final List<String> readLines(String location) {
344 		return readLines(location, null);
345 	}
346 
347 	/**
348 	 * Get the contents of <code>location</code> as a list of <code>String's</code> one entry per line using the encoding indicated.
349 	 */
350 	public static final List<String> readLines(String location, String encoding) {
351 		Reader reader = null;
352 		try {
353 			reader = getBufferedReader(location, encoding);
354 			return readLinesAndClose(reader);
355 		} catch (IOException e) {
356 			throw new IllegalStateException("Unexpected IO error", e);
357 		} finally {
358 			IOUtils.closeQuietly(reader);
359 		}
360 	}
361 
362 	/**
363 	 * Return a <code>BufferedReader</code> for the location indicated using the platform default encoding.
364 	 */
365 	public static final BufferedReader getBufferedReader(String location) throws IOException {
366 		return getBufferedReader(location, null);
367 	}
368 
369 	/**
370 	 * Return a <code>BufferedReader</code> for the location indicated using the encoding indicated.
371 	 */
372 	public static final BufferedReader getBufferedReader(String location, String encoding) throws IOException {
373 		try {
374 			InputStream in = getInputStream(location);
375 			return getBufferedReader(in, encoding);
376 		} catch (IOException e) {
377 			throw new IOException("Unexpected IO error", e);
378 		}
379 	}
380 
381 	/**
382 	 * Return a <code>BufferedReader</code> that reads from <code>s</code>
383 	 */
384 	public static final BufferedReader getBufferedReaderFromString(String s) {
385 		return new BufferedReader(new StringReader(s));
386 	}
387 
388 	/**
389 	 * Return a <code>Writer</code> that writes to <code>out</code> using the indicated encoding. <code>null</code> means use the platform's
390 	 * default encoding.
391 	 */
392 	public static final Writer getWriter(OutputStream out, String encoding) throws IOException {
393 		if (encoding == null) {
394 			return new BufferedWriter(new OutputStreamWriter(out));
395 		} else {
396 			return new BufferedWriter(new OutputStreamWriter(out, encoding));
397 		}
398 	}
399 
400 	/**
401 	 * Return a <code>BufferedReader</code> that reads from <code>file</code> using the indicated encoding. <code>null</code> means use the
402 	 * platform's default encoding.
403 	 */
404 	public static final BufferedReader getBufferedReader(File file, String encoding) throws IOException {
405 		return getBufferedReader(FileUtils.openInputStream(file), encoding);
406 	}
407 
408 	/**
409 	 * Return a <code>BufferedReader</code> that reads from <code>in</code> using the indicated encoding. <code>null</code> means use the
410 	 * platform's default encoding.
411 	 */
412 	public static final BufferedReader getBufferedReader(InputStream in, String encoding) throws IOException {
413 		if (encoding == null) {
414 			return new BufferedReader(new InputStreamReader(in));
415 		} else {
416 			return new BufferedReader(new InputStreamReader(in, encoding));
417 		}
418 	}
419 
420 	/**
421 	 * Null safe method for determining if <code>location</code> is an existing file.
422 	 */
423 	public static final boolean isExistingFile(String location) {
424 		if (location == null) {
425 			return false;
426 		}
427 		File file = new File(location);
428 		return file.exists();
429 	}
430 
431 	/**
432 	 * Null safe method for determining if <code>location</code> exists.
433 	 */
434 	public static final boolean exists(String location) {
435 		if (location == null) {
436 			return false;
437 		}
438 		if (isExistingFile(location)) {
439 			return true;
440 		} else {
441 			Resource resource = getResource(location);
442 			return resource.exists();
443 		}
444 	}
445 
446 	/**
447 	 * Open an <code>InputStream</code> to <code>location</code>. If <code>location</code> is the path to an existing <code>File</code> on
448 	 * the local file system, a <code>FileInputStream</code> is returned. Otherwise Spring's resource loading framework is used to open an
449 	 * <code>InputStream</code> to <code>location</code>.
450 	 */
451 	public static final InputStream getInputStream(String location) throws IOException {
452 		if (isExistingFile(location)) {
453 			return FileUtils.openInputStream(new File(location));
454 		}
455 		Resource resource = getResource(location);
456 		return resource.getInputStream();
457 	}
458 
459 	public static final Resource getResource(String location) {
460 		if (location == null) {
461 			return null;
462 		}
463 		ResourceLoader loader = new DefaultResourceLoader();
464 		return loader.getResource(location);
465 	}
466 
467 	public static final String getFilename(String location) {
468 		if (location == null) {
469 			return null;
470 		}
471 		if (isExistingFile(location)) {
472 			return getFileQuietly(location).getName();
473 		} else {
474 			Resource resource = getResource(location);
475 			return resource.getFilename();
476 		}
477 	}
478 
479 }