/******************************************************************************* * Copyright (c) 2005, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Martin Oberhuber (Wind River) - [44107] Add symbolic links to ResourceAttributes API * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.che.core.internal.resources; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.internal.resources.ResourceException; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.osgi.util.NLS; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; /** * Static utility methods for manipulating Files and URIs. */ public class FileUtil { /** * Singleton buffer created to prevent buffer creations in the * transferStreams method. Used as an optimization, based on the assumption * that multiple writes won't happen in a given instance of FileStore. */ private static final byte[] buffer = new byte[8192]; // /** // * Converts a ResourceAttributes object into an IFileInfo object. // * @param attributes The resource attributes // * @return The file info // */ // public static IFileInfo attributesToFileInfo(ResourceAttributes attributes) { // IFileInfo fileInfo = EFS.createFileInfo(); // fileInfo.setAttribute(EFS.ATTRIBUTE_READ_ONLY, attributes.isReadOnly()); // fileInfo.setAttribute(EFS.ATTRIBUTE_EXECUTABLE, attributes.isExecutable()); // fileInfo.setAttribute(EFS.ATTRIBUTE_ARCHIVE, attributes.isArchive()); // fileInfo.setAttribute(EFS.ATTRIBUTE_HIDDEN, attributes.isHidden()); // fileInfo.setAttribute(EFS.ATTRIBUTE_SYMLINK, attributes.isSymbolicLink()); // fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_READ, attributes.isSet(EFS.ATTRIBUTE_GROUP_READ)); // fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_WRITE, attributes.isSet(EFS.ATTRIBUTE_GROUP_WRITE)); // fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE, attributes.isSet(EFS.ATTRIBUTE_GROUP_EXECUTE)); // fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_READ, attributes.isSet(EFS.ATTRIBUTE_OTHER_READ)); // fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_WRITE, attributes.isSet(EFS.ATTRIBUTE_OTHER_WRITE)); // fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE, attributes.isSet(EFS.ATTRIBUTE_OTHER_EXECUTE)); // return fileInfo; // } /** * Converts an IPath into its canonical form for the local file system. */ public static IPath canonicalPath(IPath path) { if (path == null) return null; try { final String pathString = path.toOSString(); final String canonicalPath = new java.io.File(pathString).getCanonicalPath(); //only create a new path if necessary if (canonicalPath.equals(pathString)) return path; return new Path(canonicalPath); } catch (IOException e) { return path; } } /** * Converts a URI into its canonical form. */ public static URI canonicalURI(URI uri) { if (uri == null) return null; if (EFS.SCHEME_FILE.equals(uri.getScheme())) { //only create a new URI if it is different final IPath inputPath = URIUtil.toPath(uri); final IPath canonicalPath = canonicalPath(inputPath); if (inputPath == canonicalPath) return uri; return URIUtil.toURI(canonicalPath); } return uri; } /** * Returns true if the given file system locations overlap. If "bothDirections" is true, * this means they are the same, or one is a proper prefix of the other. If "bothDirections" * is false, this method only returns true if the locations are the same, or the first location * is a prefix of the second. Returns false if the locations do not overlap * Does the right thing with respect to case insensitive platforms. */ private static boolean computeOverlap(IPath location1, IPath location2, boolean bothDirections) { IPath one = location1; IPath two = location2; // If we are on a case-insensitive file system then convert to all lower case. if (!Workspace.caseSensitive) { one = new Path(location1.toOSString().toLowerCase()); two = new Path(location2.toOSString().toLowerCase()); } return one.isPrefixOf(two) || (bothDirections && two.isPrefixOf(one)); } // /** // * Returns true if the given file system locations overlap. If "bothDirections" is true, // * this means they are the same, or one is a proper prefix of the other. If "bothDirections" // * is false, this method only returns true if the locations are the same, or the first location // * is a prefix of the second. Returns false if the locations do not overlap // */ // private static boolean computeOverlap(URI location1, URI location2, boolean bothDirections) { // if (location1.equals(location2)) // return true; // String scheme1 = location1.getScheme(); // String scheme2 = location2.getScheme(); // if (scheme1 == null ? scheme2 != null : !scheme1.equals(scheme2)) // return false; // if (EFS.SCHEME_FILE.equals(scheme1) && EFS.SCHEME_FILE.equals(scheme2)) // return computeOverlap(URIUtil.toPath(location1), URIUtil.toPath(location2), bothDirections); // IFileSystem system = null; // try { // system = EFS.getFileSystem(scheme1); // } catch (CoreException e) { // //handled below // } // if (system == null) { // //we are stuck with string comparison // String string1 = location1.toString(); // String string2 = location2.toString(); // return string1.startsWith(string2) || (bothDirections && string2.startsWith(string1)); // } // IFileStore store1 = system.getStore(location1); // IFileStore store2 = system.getStore(location2); // return store1.equals(store2) || store1.isParentOf(store2) || (bothDirections && store2.isParentOf(store1)); // } // // /** // * Converts an IFileInfo object into a ResourceAttributes object. // * @param fileInfo The file info // * @return The resource attributes // */ // public static ResourceAttributes fileInfoToAttributes(IFileInfo fileInfo) { // ResourceAttributes attributes = new ResourceAttributes(); // attributes.setReadOnly(fileInfo.getAttribute(EFS.ATTRIBUTE_READ_ONLY)); // attributes.setArchive(fileInfo.getAttribute(EFS.ATTRIBUTE_ARCHIVE)); // attributes.setExecutable(fileInfo.getAttribute(EFS.ATTRIBUTE_EXECUTABLE)); // attributes.setHidden(fileInfo.getAttribute(EFS.ATTRIBUTE_HIDDEN)); // attributes.setSymbolicLink(fileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK)); // attributes.set(EFS.ATTRIBUTE_GROUP_READ, fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_READ)); // attributes.set(EFS.ATTRIBUTE_GROUP_WRITE, fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_WRITE)); // attributes.set(EFS.ATTRIBUTE_GROUP_EXECUTE, fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE)); // attributes.set(EFS.ATTRIBUTE_OTHER_READ, fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_READ)); // attributes.set(EFS.ATTRIBUTE_OTHER_WRITE, fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_WRITE)); // attributes.set(EFS.ATTRIBUTE_OTHER_EXECUTE, fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE)); // return attributes; // } // // private static String getLineSeparatorFromPreferences(Preferences node) { // try { // // be careful looking up for our node so not to create any nodes as side effect // if (node.nodeExists(Platform.PI_RUNTIME)) // return node.node(Platform.PI_RUNTIME).get(Platform.PREF_LINE_SEPARATOR, null); // } catch (BackingStoreException e) { // // ignore // } // return null; // } // // /** // * Returns line separator appropriate for the given file. The returned value // * will be the first available value from the list below: // * <ol> // * <li> Line separator currently used in that file. // * <li> Line separator defined in project preferences. // * <li> Line separator defined in instance preferences. // * <li> Line separator defined in default preferences. // * <li> Operating system default line separator. // * </ol> // * @param file the file for which line separator should be returned // * @return line separator for the given file // */ // public static String getLineSeparator(IFile file) { // if (file.exists()) { // InputStream input = null; // try { // input = file.getContents(); // int c = input.read(); // while (c != -1 && c != '\r' && c != '\n') // c = input.read(); // if (c == '\n') // return "\n"; //$NON-NLS-1$ // if (c == '\r') { // if (input.read() == '\n') // return "\r\n"; //$NON-NLS-1$ // return "\r"; //$NON-NLS-1$ // } // } catch (CoreException e) { // // ignore // } catch (IOException e) { // // ignore // } finally { // safeClose(input); // } // } // Preferences rootNode = Platform.getPreferencesService().getRootNode(); // String value = null; // // if the file does not exist or has no content yet, try with project preferences // value = getLineSeparatorFromPreferences(rootNode.node(ProjectScope.SCOPE).node(file.getProject().getName())); // if (value != null) // return value; // // try with instance preferences // value = getLineSeparatorFromPreferences(rootNode.node(InstanceScope.SCOPE)); // if (value != null) // return value; // // try with default preferences // value = getLineSeparatorFromPreferences(rootNode.node(DefaultScope.SCOPE)); // if (value != null) // return value; // // if there is no preference set, fall back to OS default value // return System.getProperty("line.separator"); //$NON-NLS-1$ // } // // /** // * Returns true if the given file system locations overlap, and false otherwise. // * Overlap means the locations are the same, or one is a proper prefix of the other. // */ // public static boolean isOverlapping(URI location1, URI location2) { // return computeOverlap(location1, location2, true); // } // // /** // * Returns true if location1 is the same as, or a proper prefix of, location2. // * Returns false otherwise. // */ // public static boolean isPrefixOf(IPath location1, IPath location2) { // return computeOverlap(location1, location2, false); // } // // /** // * Returns true if location1 is the same as, or a proper prefix of, location2. // * Returns false otherwise. // */ // public static boolean isPrefixOf(URI location1, URI location2) { // return computeOverlap(location1, location2, false); // } /** * Closes a stream and ignores any resulting exception. This is useful * when doing stream cleanup in a finally block where secondary exceptions * are not worth logging. * *<p> * <strong>WARNING:</strong> * If the API contract requires notifying clients of I/O problems, then you <strong>must</strong> * explicitly close() output streams outside of safeClose(). * Some OutputStreams will defer an IOException from write() to close(). So * while the writes may 'succeed', ignoring the IOExcpetion will result in silent * data loss. * </p> * <p> * This method should only be used as a fail-safe to ensure resources are not * leaked. * </p> * See also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=332543 */ public static void safeClose(Closeable stream) { try { if (stream != null) stream.close(); } catch (IOException e) { //ignore } } /** * Converts a URI to an IPath. Returns null if the URI cannot be represented * as an IPath. * <p> * Note this method differs from URIUtil in its handling of relative URIs * as being relative to path variables. */ public static IPath toPath(URI uri) { if (uri == null) return null; final String scheme = uri.getScheme(); // null scheme represents path variable if (scheme == null || EFS.SCHEME_FILE.equals(scheme)) return new Path(uri.getSchemeSpecificPart()); return null; } public static final void transferStreams(InputStream source, OutputStream destination, String path, IProgressMonitor monitor) throws CoreException { // monitor = Policy.monitorFor(monitor); try { /* * Note: although synchronizing on the buffer is thread-safe, * it may result in slower performance in the future if we want * to allow concurrent writes. */ synchronized (buffer) { while (true) { int bytesRead = -1; try { bytesRead = source.read(buffer); } catch (IOException e) { String msg = NLS.bind(Messages.localstore_failedReadDuringWrite, path); throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, new Path(path), msg, e); } try { if (bytesRead == -1) { // Bug 332543 - ensure we don't ignore failures on close() destination.close(); break; } destination.write(buffer, 0, bytesRead); } catch (IOException e) { String msg = NLS.bind(Messages.localstore_couldNotWrite, path); throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, new Path(path), msg, e); } // monitor.worked(1); } } } finally { safeClose(source); safeClose(destination); } } /** * Not intended for instantiation. */ private FileUtil() { super(); } }