Copyright (c) 2000, 2015 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
Serge Beauchamp (Freescale Semiconductor) - [229633] Group and Project Path Variable Support
James Blackburn (Broadcom Corp.) - ongoing development
Sergey Prigogin (Google) - [464072] Refresh on Access ignored during text search
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Serge Beauchamp (Freescale Semiconductor) - [229633] Group and Project Path Variable Support
* James Blackburn (Broadcom Corp.) - ongoing development
* Sergey Prigogin (Google) - [464072] Refresh on Access ignored during text search
*******************************************************************************/
package org.eclipse.core.internal.resources;
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 IFile
. /**
* The standard implementation of {@link IFile}.
*/
public class File extends Resource implements IFile {
protected File(IPath path, Workspace container) {
super(path, container);
}
@Override
public void appendContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
String message = NLS.bind(Messages.resources_settingContents, getFullPath());
SubMonitor subMonitor = SubMonitor.convert(monitor, message, 100);
try {
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, subMonitor.newChild(1));
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
workspace.beginOperation(true);
IFileInfo fileInfo = getStore().fetchInfo();
internalSetContents(content, fileInfo, updateFlags, true, subMonitor.newChild(99));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true);
}
} finally {
subMonitor.done();
FileUtil.safeClose(content);
}
}
@Override
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.
This method is for the exclusive use of the local resource manager
/**
* 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;
}
@Override
public void create(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
String message = NLS.bind(Messages.resources_creating, getFullPath());
SubMonitor subMonitor = SubMonitor.convert(monitor, message, 100);
try {
checkValidPath(path, FILE, true);
final ISchedulingRule rule = workspace.getRuleFactory().createRule(this);
try {
workspace.prepareOperation(rule, subMonitor.newChild(1));
checkDoesNotExist();
Container parent = (Container) getParent();
ResourceInfo info = parent.getResourceInfo(false, false);
parent.checkAccessible(getFlags(info));
checkValidGroupContainer(parent, false, false);
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);
}
}
subMonitor.worked(40);
info = workspace.createResource(this, updateFlags);
boolean local = content != null;
if (local) {
try {
internalSetContents(content, localInfo, updateFlags, false, subMonitor.newChild(59));
} catch (CoreException | OperationCanceledException e) {
// CoreException when a problem happened creating the file on disk
// OperationCanceledException when the operation of setting contents has been
// canceled
// In either case delete from the workspace and disk
workspace.deleteResource(this);
store.delete(EFS.NONE, null);
throw e;
}
}
internalSetLocal(local, DEPTH_ZERO);
if (!local)
getResourceInfo(true, true).clearModificationStamp();
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true);
}
} finally {
subMonitor.done();
FileUtil.safeClose(content);
}
}
@Override
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);
}
@Override
public String getCharset() throws CoreException {
return getCharset(true);
}
@Override
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);
try {
return internalGetCharset(checkImplicit, info);
} catch (CoreException e) {
if (e.getStatus().getCode() == IResourceStatus.RESOURCE_NOT_FOUND) {
return checkImplicit ? workspace.getCharsetManager().getCharsetFor(getFullPath().removeLastSegments(1), true) : null;
}
throw e;
}
}
@Override
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, true);
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);
}
@Override
public IContentDescription getContentDescription() throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
boolean isSynchronized = isSynchronized(IResource.DEPTH_ZERO);
// Throw an exception if out-of-sync and not auto-refresh enabled
if (!isSynchronized && !getLocalManager().isLightweightAutoRefreshEnabled()) {
String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, getFullPath());
throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, getFullPath(), message, null);
}
return workspace.getContentDescriptionManager().getDescriptionFor(this, info, isSynchronized);
}
@Override
public InputStream getContents() throws CoreException {
return getContents(getLocalManager().isLightweightAutoRefreshEnabled());
}
@Override
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);
}
@Deprecated
@Override
public int getEncoding() throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
int flags = getFlags(info);
checkAccessible(flags);
checkLocal(flags, DEPTH_ZERO);
return getLocalManager().getEncoding(this);
}
@Override
public IFileState[] getHistory(IProgressMonitor monitor) {
return getLocalManager().getHistoryStore().getStates(getFullPath(), monitor);
}
@Override
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.
/**
* 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.
*/
@Override
public void refreshLocal(int depth, IProgressMonitor monitor) throws CoreException {
if (!getLocalManager().fastIsSynchronized(this))
super.refreshLocal(IResource.DEPTH_ZERO, monitor);
}
@Override
public void setContents(IFileState content, int updateFlags, IProgressMonitor monitor) throws CoreException {
setContents(content.getContents(), updateFlags, monitor);
}
@Override
public void setContents(InputStream content, int updateFlags, IProgressMonitor monitor) throws CoreException {
String message = NLS.bind(Messages.resources_settingContents, getFullPath());
SubMonitor subMonitor = SubMonitor.convert(monitor, message, 100);
try {
if (workspace.shouldValidate)
workspace.validateSave(this);
final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(this);
try {
workspace.prepareOperation(rule, subMonitor.newChild(1));
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
workspace.beginOperation(true);
IFileInfo fileInfo = getStore().fetchInfo();
internalSetContents(content, fileInfo, updateFlags, false, subMonitor.newChild(99));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, true);
}
} finally {
subMonitor.done();
FileUtil.safeClose(content);
}
}
@Override
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).
/**
* 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 project = (Project) getProject();
project.updateDescription();
// Discard stale project natures on ProjectInfo
ProjectInfo projectInfo = (ProjectInfo) project.getResourceInfo(false, true);
projectInfo.discardNatures();
return;
}
// check to see if we are in the .settings directory
if (count == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(name)) {
ProjectPreferences.updatePreferences(this);
return;
}
}
@Deprecated
@Override
public void setCharset(String newCharset) throws CoreException {
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
}
@Override
public void setCharset(String newCharset, IProgressMonitor monitor) throws CoreException {
String message = NLS.bind(Messages.resources_settingCharset, getFullPath());
SubMonitor subMonitor = SubMonitor.convert(monitor, message, 100);
// 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, subMonitor.newChild(1));
ResourceInfo info = getResourceInfo(false, false);
checkAccessible(getFlags(info));
workspace.beginOperation(true);
workspace.getCharsetManager().setCharsetFor(getFullPath(), newCharset);
info = getResourceInfo(false, true);
info.incrementCharsetGenerationCount();
subMonitor.worked(99);
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
subMonitor.done();
workspace.endOperation(rule, true);
}
}
@Override
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);
}
@Override
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);
}
}