/** * Copyright (C) 2005 - 2014 Eric Van Dewoestine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.eclim.util.file; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.Enumeration; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.naming.CompositeName; import org.apache.commons.lang.StringUtils; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemManager; import org.apache.commons.vfs.VFS; import org.eclim.util.IOUtils; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * Utilities for working w/ files and commons vfs. * * @author Eric Van Dewoestine */ public class FileUtils { public static final String JAR_PREFIX = "jar://"; public static final String ZIP_PREFIX = "zip://"; public static final String JAR_EXT = ".jar"; public static final String ZIP_EXT = ".zip"; public static final char UNIX_SEPARATOR = '/'; public static final char WINDOWS_SEPARATOR = '\\'; public static final String UTF8 = "utf-8"; /** * Converts the supplied byte offset in the specified file to the * corresponding char offset for that file using the supplied file encoding. * * @param filename The absolute path to the file. * @param byteOffset The byte offset to be converted. * @param encoding The encoding of the file. If null, defaults to utf-8. * * @return The char offset. */ public static int byteOffsetToCharOffset( String filename, int byteOffset, String encoding) throws Exception { FileSystemManager fsManager = VFS.getManager(); FileObject file = fsManager.resolveFile(filename.replace("%", "%25")); return byteOffsetToCharOffset( file.getContent().getInputStream(), byteOffset, encoding); } /** * Converts the supplied byte offset in the specified file to the * corresponding char offset for that file using the supplied file encoding. * * @param in InputStream for the file contents. * @param byteOffset The byte offset to be converted. * @param encoding The encoding of the file. If null, defaults to utf-8. * * @return The char offset. */ public static int byteOffsetToCharOffset( InputStream in, int byteOffset, String encoding) throws Exception { if (encoding == null){ encoding = UTF8; } BufferedInputStream bin = null; try{ byte[] bytes = new byte[byteOffset]; bin = new BufferedInputStream(in); bin.read(bytes, 0, bytes.length); String value = new String(bytes, encoding); return value.length(); }finally{ IOUtils.closeQuietly(bin); } } /** * Converts the supplied char offset into an int array where the first element * is the line number and the second is the column number. * * @param filename The file to translate the offset for. * @param offset The offset. * @return The line and column int array. */ public static int[] offsetToLineColumn(String filename, int offset) throws Exception { FileOffsets offsets = FileOffsets.compile(filename); return offsets.offsetToLineColumn(offset); } /** * Converts the supplied char offset into an int array where the first element * is the line number and the second is the column number. * * @param in The InputStream to compile a list of offsets for. * @param offset The offset. * @return The line and column int array. */ public static int[] offsetToLineColumn(InputStream in, int offset) throws Exception { FileOffsets offsets = FileOffsets.compile(in); return offsets.offsetToLineColumn(offset); } /** * Obtains a matcher to run the supplied pattern on the specified file. * * @param pattern The regex pattern * @param file The path to the file. * @return The Matcher. */ public static Matcher matcher(Pattern pattern, String file) throws Exception { FileInputStream is = null; try{ is = new FileInputStream(file); String contents = IOUtils.toString(is); return pattern.matcher(contents); }finally{ IOUtils.closeQuietly(is); } } /** * Translates a file name that does not conform to the standard url file * format. * <p/> * Main purpose is to convert paths like:<br/> * <code>/opt/sun-jdk-1.5.0.05/src.zip/javax/swing/Spring.java</code><br/> * to<br/> * <code>zip:file:///opt/sun-jdk-1.5.0.05/src.zip!/javax/swing/Spring.java</code> * * @param file The file to translate. * @return The translated file. */ public static String toUrl(String file) { file = file.replace('\\', '/'); // if the path points to a real file, return it. if(new File(file).exists()){ return file; } // already an url. if(file.startsWith(JAR_PREFIX) || file.startsWith(ZIP_PREFIX)){ return file; } // otherwise do some conversion. StringBuffer buffer = new StringBuffer(); try{ CompositeName fileName = new CompositeName(file); Enumeration<String> names = fileName.getAll(); while(names.hasMoreElements()){ String name = names.nextElement(); if(name.indexOf("$") != -1){ name = name.substring(0, name.indexOf("$")) + '.' + getExtension(name); } if(name.length() != 0){ buffer.append('/').append(name); if(!new File(buffer.toString()).exists()){ String path = getFullPath(buffer.toString()); if(path.endsWith("/") || path.endsWith("\\")){ path = path.substring(0, path.length() - 1); } if(path.endsWith(JAR_EXT)){ buffer = new StringBuffer(JAR_PREFIX) .append(path).append('!').append('/').append(name); }else if(path.endsWith(ZIP_EXT)){ buffer = new StringBuffer(ZIP_PREFIX) .append(path).append('!').append('/').append(name); } } } } }catch(Exception e){ throw new RuntimeException(e); } return buffer.toString(); } /** * Concatenates the given head path with the supplied tail path to form a new * path. * * @param head The head path. * @param parts The path parts to concat. * @return The concatenated paths. */ public static String concat(String head, String... parts) { IPath path = Path.fromOSString(head); for (String part : parts){ path = path.append(part); } return path.toOSString().replace('\\', '/'); } /** * Gets the base name of the supplied path. * <pre> * FileUtils.getBaseName("/a/b/c/") : "c" * FileUtils.getBaseName("/a/b/c") : "c" * FileUtils.getBaseName("/a/b/c.txt") : "c.txt" * </pre> * * @param path The path. * @return The base name of the path. */ public static String getBaseName(String path) { IPath p = Path.fromOSString(path); return p.segment(p.segmentCount() - 1); } /** * Gets the file extension from the supplied path. * <pre> * FileUtils.getExtension("/a/b/c/") : "" * FileUtils.getExtension("/a/b/c") : "" * FileUtils.getExtension("/a/b/c.txt") : "txt" * </pre> * * @param path The path. * @return The file extension. */ public static String getExtension(String path) { String ext = Path.fromOSString(path).getFileExtension(); if (ext == null){ return StringUtils.EMPTY; } return ext; } /** * */ public static String getPath(String path) { IPath p = null; int index = path.indexOf(':'); if (index != -1){ p = new Path(path.substring(index + 1)); }else{ p = new Path(path); } if (!p.hasTrailingSeparator()){ p = p.uptoSegment(p.segmentCount() - 1); } return p.makeRelative().addTrailingSeparator().toOSString(); } /** * Gets the full path from the supplied path by removing any trailing path * segments that do not have a trailing separator. * <pre> * FileUtils.getFullPath("/a/b/c/") : "/a/b/c/" * FileUtils.getFullPath("/a/b/c") : "/a/b/" * FileUtils.getFullPath("/a/b/c.txt") : "/a/b/" * </pre> * * @param path The path. * @return The full path. */ public static String getFullPath(String path) { IPath p = Path.fromOSString(path); if (!p.hasTrailingSeparator()){ p = p.uptoSegment(p.segmentCount() - 1); } return p.addTrailingSeparator().toOSString(); } /** * Gets the directory path from the supplied path by removing the last path * segment. * <pre> * FileUtils.getDirName("/a/b/c/") : "/a/b/" * FileUtils.getDirName("/a/b/c") : "/a/b/" * FileUtils.getDirName("/a/b/c.txt") : "/a/b/" * </pre> * * @param path The path. * @return The full path. */ public static String getDirName(String path) { IPath p = Path.fromOSString(path); p = p.uptoSegment(p.segmentCount() - 1); return p.addTrailingSeparator().toOSString(); } /** * Gets the file name minus the extension from the supplied path. * <pre> * FileUtils.getFileName("/a/b/c/") : "" * FileUtils.getFileName("/a/b/c") : "c" * FileUtils.getFileName("/a/b/c.txt") : "c" * </pre> * * @param path The path. * @return The file name without the extension. */ public static String getFileName(String path) { IPath p = Path.fromOSString(path); if (p.hasTrailingSeparator()){ return StringUtils.EMPTY; } return p.removeFileExtension().segment(p.segmentCount() - 1); } /** * Removes the extension from the supplied file name. * <pre> * FileUtils.removeExtension("/a/b/c/") : "/a/b/c/" * FileUtils.removeExtension("/a/b/c") : "/a/b/c" * FileUtils.removeExtension("/a/b/c.txt") : "/a/b/c" * </pre> * * @param path The path. * @return The path with the extension removed. */ public static String removeExtension(String path) { return Path.fromOSString(path).removeFileExtension().toOSString(); } /** * Adds the trailing slash if it doesn't exists. * <pre> * FileUtils.addTrailingSlash("/a/b/c/") : "/a/b/c/" * FileUtils.addTrailingSlash("/a/b/c") : "/a/b/c/" * </pre> * * @param path The path. * @return The path with a trailing slash. */ public static String addTrailingSlash(String path) { if (!path.endsWith("/")){ return path += '/'; } return path; } /** * Removes the trailing slash if it exists. * <pre> * FileUtils.removeTrailingSlash("/a/b/c/") : "/a/b/c" * FileUtils.removeTrailingSlash("/a/b/c") : "/a/b/c" * </pre> * * @param path The path. * @return The path with any trialing slash removed. */ public static String removeTrailingSlash(String path) { if (path.endsWith("/")){ return path.substring(0, path.length() - 1); } return path; } /** * Convert all path separators for the given path to unix style separators. * * @param path The path. * @return The updated path. */ public static String separatorsToUnix(String path) { if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) { return path; } return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR); } /** * Deletes a directory and all of its contents. * * @param dir The directory to delete. */ public static void deleteDirectory(File dir) { if(!dir.exists()){ return; } for(File f : dir.listFiles()){ if(f.isDirectory()){ deleteDirectory(f); }else{ f.delete(); } } dir.delete(); } }