platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java
changeset 40 eb3c938c7fef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java	Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ *******************************************************************************/
+package org.eclipse.core.internal.resources;
+
+import org.eclipse.core.internal.utils.FileUtil;
+
+import java.io.*;
+import org.eclipse.core.filesystem.*;
+import org.eclipse.core.internal.preferences.EclipsePreferences;
+import org.eclipse.core.internal.utils.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * The standard implementation of {@link IFile}.
+ */
+public class File extends Resource implements IFile {
+
+	protected File(IPath path, Workspace container) {
+		super(path, container);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#appendContents(InputStream, int, IProgressMonitor)
+	 */
+	public void appendContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+		monitor = Policy.monitorFor(monitor);
+		try {
+			String message = NLS.bind(Messages.resources_settingContents, getFullPath());
+			monitor.beginTask(message, Policy.totalWork);
+			Assert.isNotNull(content, "Content cannot be null."); //$NON-NLS-1$
+			if (workspace.shouldValidate)
+				workspace.validateSave(this);
+			final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+			try {
+				workspace.prepareOperation(rule, monitor);
+				ResourceInfo info = getResourceInfo(false, false);
+				checkAccessible(getFlags(info));
+				workspace.beginOperation(true);
+				IFileInfo fileInfo = getStore().fetchInfo();
+				internalSetContents(content, fileInfo, updateFlags, true, Policy.subMonitorFor(monitor, Policy.opWork));
+			} catch (OperationCanceledException e) {
+				workspace.getWorkManager().operationCanceled();
+				throw e;
+			} finally {
+				workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+			}
+		} finally {
+			monitor.done();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#appendContents(InputStream, boolean, boolean, IProgressMonitor)
+	 */
+	public void appendContents(InputStream content, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+		// funnel all operations to central method
+		int updateFlags = force ? IResource.FORCE : IResource.NONE;
+		updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+		appendContents(content, updateFlags, monitor);
+	}
+
+	/**
+	 * Changes this file to be a folder in the resource tree and returns
+	 * the newly created folder.  All related
+	 * properties are deleted.  It is assumed that on disk the resource is
+	 * already a folder/directory so no action is taken to delete the disk
+	 * contents.
+	 * <p>
+	 * <b>This method is for the exclusive use of the local resource manager</b>
+	 */
+	public IFolder changeToFolder() throws CoreException {
+		getPropertyManager().deleteProperties(this, IResource.DEPTH_ZERO);
+		IFolder result = workspace.getRoot().getFolder(path);
+		if (isLinked()) {
+			IPath location = getRawLocation();
+			delete(IResource.NONE, null);
+			result.createLink(location, IResource.ALLOW_MISSING_LOCAL, null);
+		} else {
+			workspace.deleteResource(this);
+			workspace.createResource(result, false);
+		}
+		return result;
+	}
+
+	/**
+	 * Checks that this resource is synchronized with the local file system.
+	 */
+	private void checkSynchronized() throws CoreException {
+		if (!isSynchronized(IResource.DEPTH_ZERO)) {
+			String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, getFullPath());
+			throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, getFullPath(), message, null);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#create(InputStream, int, IProgressMonitor)
+	 */
+	public void create(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+		final boolean monitorNull = monitor == null;
+		monitor = Policy.monitorFor(monitor);
+		try {
+			String message = monitorNull ? "" : NLS.bind(Messages.resources_creating, getFullPath()); //$NON-NLS-1$
+			monitor.beginTask(message, Policy.totalWork);
+			checkValidPath(path, FILE, true);
+			final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
+			try {
+				workspace.prepareOperation(rule, monitor);
+				checkDoesNotExist();
+				Container parent = (Container) getParent();
+				ResourceInfo info = parent.getResourceInfo(false, false);
+				parent.checkAccessible(getFlags(info));
+
+				workspace.beginOperation(true);
+				IFileStore store = getStore();
+				IFileInfo localInfo = store.fetchInfo();
+				if (BitMask.isSet(updateFlags, IResource.FORCE)) {
+					if (!Workspace.caseSensitive) {
+						if (localInfo.exists()) {
+							String name = getLocalManager().getLocalName(store);
+							if (name == null || localInfo.getName().equals(name)) {
+								delete(true, null);
+							} else {
+								// The file system is not case sensitive and there is already a file
+								// under this location.
+								message = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+								throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), message, null);
+							}
+						}
+					}
+				} else {
+					if (localInfo.exists()) {
+						//return an appropriate error message for case variant collisions
+						if (!Workspace.caseSensitive) {
+							String name = getLocalManager().getLocalName(store);
+							if (name != null && !localInfo.getName().equals(name)) {
+								message = NLS.bind(Messages.resources_existsLocalDifferentCase, new Path(store.toString()).removeLastSegments(1).append(name).toOSString());
+								throw new ResourceException(IResourceStatus.CASE_VARIANT_EXISTS, getFullPath(), message, null);
+							}
+						}
+						message = NLS.bind(Messages.resources_fileExists, store.toString());
+						throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, getFullPath(), message, null);
+					}
+				}
+				monitor.worked(Policy.opWork * 40 / 100);
+
+				info = workspace.createResource(this, updateFlags);
+				boolean local = content != null;
+				if (local) {
+					try {
+						internalSetContents(content, localInfo, updateFlags, false, Policy.subMonitorFor(monitor, Policy.opWork * 60 / 100));
+					} catch (CoreException e) {
+						// a problem happened creating the file on disk, so delete from the workspace and disk
+						workspace.deleteResource(this);
+						store.delete(EFS.NONE, null);
+						throw e; // rethrow
+					}
+				}
+				internalSetLocal(local, DEPTH_ZERO);
+				if (!local)
+					getResourceInfo(true, true).clearModificationStamp();
+			} catch (OperationCanceledException e) {
+				workspace.getWorkManager().operationCanceled();
+				throw e;
+			} finally {
+				workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+			}
+		} finally {
+			monitor.done();
+			FileUtil.safeClose(content);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#create(InputStream, boolean, IProgressMonitor)
+	 */
+	public void create(InputStream content, boolean force, IProgressMonitor monitor) throws CoreException {
+		// funnel all operations to central method
+		create(content, (force ? IResource.FORCE : IResource.NONE), monitor);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getCharset()
+	 */
+	public String getCharset() throws CoreException {
+		return getCharset(true);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getCharset(boolean)
+	 */
+	public String getCharset(boolean checkImplicit) throws CoreException {
+		// non-existing resources default to parent's charset
+		ResourceInfo info = getResourceInfo(false, false);
+		int flags = getFlags(info);
+		if (!exists(flags, false))
+			return checkImplicit ? workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true) : null;
+		checkLocal(flags, DEPTH_ZERO);
+		return internalGetCharset(checkImplicit, info);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getCharsetFor(Reader)
+	 */
+	public String getCharsetFor(Reader contents) throws CoreException {
+		String charset;
+		ResourceInfo info = getResourceInfo(false, false);
+		int flags = getFlags(info);
+		if (exists(flags, true))
+			// the file exists, look for user setting
+			if ((charset = workspace.getCharsetManager().getCharsetFor(getFullPath(), false)) != null)
+				// if there is a file-specific user setting, use it
+				return charset;
+		// tries to obtain a description from the contents provided
+		IContentDescription description;
+		try {
+			// TODO need to take project specific settings into account
+			IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+			description = contentTypeManager.getDescriptionFor(contents, getName(), new QualifiedName[] {IContentDescription.CHARSET});
+		} catch (IOException e) {
+			String message = NLS.bind(Messages.resources_errorContentDescription, getFullPath());
+			throw new ResourceException(IResourceStatus.FAILED_DESCRIBING_CONTENTS, getFullPath(), message, e);
+		}
+		if (description != null)
+			if ((charset = description.getCharset()) != null)
+				// the description contained charset info, we are done 
+				return charset;
+		// could not find out the encoding based on the contents... default to parent's
+		return workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true);
+	}
+
+	private String internalGetCharset(boolean checkImplicit, ResourceInfo info) throws CoreException {
+		// if there is a file-specific user setting, use it
+		String charset = workspace.getCharsetManager().getCharsetFor(getFullPath(), false);
+		if (charset != null || !checkImplicit)
+			return charset;
+		// tries to obtain a description for the file contents
+		IContentDescription description = workspace.getContentDescriptionManager().getDescriptionFor(this, info);
+		if (description != null) {
+			String contentCharset = description.getCharset();
+			if (contentCharset != null)
+				return contentCharset;
+		}
+		// could not find out the encoding based on the contents... default to parent's
+		return workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true);
+	}
+
+	/*
+	 *  (non-Javadoc)
+	 * @see org.eclipse.core.resources.IFile#getContentDescription()
+	 */
+	public IContentDescription getContentDescription() throws CoreException {
+		ResourceInfo info = getResourceInfo(false, false);
+		int flags = getFlags(info);
+		checkAccessible(flags);
+		checkSynchronized();
+		checkLocal(flags, DEPTH_ZERO);
+		return workspace.getContentDescriptionManager().getDescriptionFor(this, info);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getContents()
+	 */
+	public InputStream getContents() throws CoreException {
+		return getContents(false);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getContents(boolean)
+	 */
+	public InputStream getContents(boolean force) throws CoreException {
+		ResourceInfo info = getResourceInfo(false, false);
+		int flags = getFlags(info);
+		checkAccessible(flags);
+		checkLocal(flags, DEPTH_ZERO);
+		return getLocalManager().read(this, force, null);
+	}
+
+	/**
+	 * @see IFile#getEncoding()
+	 * @deprecated
+	 */
+	public int getEncoding() throws CoreException {
+		ResourceInfo info = getResourceInfo(false, false);
+		int flags = getFlags(info);
+		checkAccessible(flags);
+		checkLocal(flags, DEPTH_ZERO);
+		return getLocalManager().getEncoding(this);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#getHistory(IProgressMonitor)
+	 */
+	public IFileState[] getHistory(IProgressMonitor monitor) {
+		return getLocalManager().getHistoryStore().getStates(getFullPath(), monitor);
+	}
+
+	/* (non-Javadoc)
+	 * @see IResource#getType()
+	 */
+	public int getType() {
+		return FILE;
+	}
+
+	protected void internalSetContents(InputStream content, IFileInfo fileInfo, int updateFlags, boolean append, IProgressMonitor monitor) throws CoreException {
+		if (content == null)
+			content = new ByteArrayInputStream(new byte[0]);
+		getLocalManager().write(this, content, fileInfo, updateFlags, append, monitor);
+		updateMetadataFiles();
+		workspace.getAliasManager().updateAliases(this, getStore(), IResource.DEPTH_ZERO, monitor);
+	}
+
+	/**
+	 * Optimized refreshLocal for files.  This implementation does not block the workspace
+	 * for the common case where the file exists both locally and on the file system, and
+	 * is in sync.  For all other cases, it defers to the super implementation.
+	 */
+	public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException {
+		if (!getLocalManager().fastIsSynchronized(this))
+			super.refreshLocal(IResource.DEPTH_ZERO, monitor);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#setContents(IFileState, int, IProgressMonitor)
+	 */
+	public void setContents(IFileState content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+		setContents(content.getContents(), updateFlags, monitor);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#setContents(InputStream, int, IProgressMonitor)
+	 */
+	public void setContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
+		monitor = Policy.monitorFor(monitor);
+		try {
+			String message = NLS.bind(Messages.resources_settingContents, getFullPath());
+			monitor.beginTask(message, Policy.totalWork);
+			if (workspace.shouldValidate)
+				workspace.validateSave(this);
+			final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
+			try {
+				workspace.prepareOperation(rule, monitor);
+				ResourceInfo info = getResourceInfo(false, false);
+				checkAccessible(getFlags(info));
+				workspace.beginOperation(true);
+				IFileInfo fileInfo = getStore().fetchInfo();
+				internalSetContents(content, fileInfo, updateFlags, false, Policy.subMonitorFor(monitor, Policy.opWork));
+			} catch (OperationCanceledException e) {
+				workspace.getWorkManager().operationCanceled();
+				throw e;
+			} finally {
+				workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+			}
+		} finally {
+			monitor.done();
+			FileUtil.safeClose(content);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see IResource#setLocalTimeStamp(long)
+	 */
+	public long setLocalTimeStamp(long value) throws CoreException {
+		//override to handle changing timestamp on project description file
+		long result = super.setLocalTimeStamp(value);
+		if (path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
+			//handle concurrent project deletion
+			ResourceInfo projectInfo = ((Project) getProject()).getResourceInfo(false, false);
+			if (projectInfo != null)
+				getLocalManager().updateLocalSync(projectInfo, result);
+		}
+		return result;
+	}
+
+	/**
+	 * Treat the file specially if it represents a metadata file, which includes:
+	 * - project description file (.project)
+	 * - project preferences files (*.prefs)
+	 *  
+	 * This method is called whenever it is discovered that a file has
+	 * been modified (added, removed, or changed).
+	 */
+	public void updateMetadataFiles() throws CoreException {
+		int count = path.segmentCount();
+		String name = path.segment(1);
+		// is this a project description file?
+		if (count == 2 && name.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
+			((Project) getProject()).updateDescription();
+			return;
+		}
+		// check to see if we are in the .settings directory
+		if (count == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(name)) {
+			ProjectPreferences.updatePreferences(this);
+			return;
+		}
+	}
+
+	/** (non-Javadoc)
+	 * @see IFile#setCharset(String)
+	 * @deprecated Replaced by {@link #setCharset(String, IProgressMonitor)} which 
+	 * 	is a workspace operation and reports changes in resource deltas.
+	 */
+	public void setCharset(String newCharset) throws CoreException {
+		ResourceInfo info = getResourceInfo(false, false);
+		checkAccessible(getFlags(info));
+		workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#setCharset(String, IProgressMonitor)
+	 */
+	public void setCharset(String newCharset, IProgressMonitor monitor) throws CoreException {
+		monitor = Policy.monitorFor(monitor);
+		try {
+			String message = NLS.bind(Messages.resources_settingCharset, getFullPath());
+			monitor.beginTask(message, Policy.totalWork);
+			// need to get the project as a scheduling rule because we might be creating a new folder/file to
+			// hold the project settings
+			final ISchedulingRule rule = workspace.getRuleFactory().charsetRule(this);
+			try {
+				workspace.prepareOperation(rule, monitor);
+				ResourceInfo info = getResourceInfo(false, false);
+				checkAccessible(getFlags(info));
+				workspace.beginOperation(true);
+				workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
+				info = getResourceInfo(false, true);
+				info.incrementCharsetGenerationCount();
+				monitor.worked(Policy.opWork);
+			} catch (OperationCanceledException e) {
+				workspace.getWorkManager().operationCanceled();
+				throw e;
+			} finally {
+				workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
+			}
+		} finally {
+			monitor.done();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#setContents(InputStream, boolean, boolean, IProgressMonitor)
+	 */
+	public void setContents(InputStream content, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+		// funnel all operations to central method
+		int updateFlags = force ? IResource.FORCE : IResource.NONE;
+		updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+		setContents(content, updateFlags, monitor);
+	}
+
+	/* (non-Javadoc)
+	 * @see IFile#setContents(IFileState, boolean, boolean, IProgressMonitor)
+	 */
+	public void setContents(IFileState source, boolean force, boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+		// funnel all operations to central method
+		int updateFlags = force ? IResource.FORCE : IResource.NONE;
+		updateFlags |= keepHistory ? IResource.KEEP_HISTORY : IResource.NONE;
+		setContents(source.getContents(), updateFlags, monitor);
+	}
+	
+}