### Eclipse Workspace Patch 1.0 #P org.eclipse.core.resources Index: src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java,v retrieving revision 1.130 diff -u -r1.130 FileSystemResourceManager.java --- src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java 12 Jan 2011 09:38:29 -0000 1.130 +++ src/org/eclipse/core/internal/localstore/FileSystemResourceManager.java 25 Mar 2011 12:36:10 -0000 @@ -18,18 +18,22 @@ import java.util.*; import org.eclipse.core.filesystem.*; import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.internal.refresh.RefreshManager; import org.eclipse.core.internal.resources.*; import org.eclipse.core.internal.resources.File; import org.eclipse.core.internal.utils.*; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.eclipse.core.runtime.preferences.*; import org.eclipse.osgi.util.NLS; import org.xml.sax.InputSource; /** * Manages the synchronization between the workspace's view and the file system. */ -public class FileSystemResourceManager implements ICoreConstants, IManager { +public class FileSystemResourceManager implements ICoreConstants, IManager, IPreferenceChangeListener { /** * The history store is initialized lazily - always use the accessor method @@ -37,6 +41,8 @@ protected IHistoryStore _historyStore; protected Workspace workspace; + private volatile boolean lightweightAutoRefreshEnabled; + public FileSystemResourceManager(Workspace workspace) { this.workspace = workspace; } @@ -124,6 +130,15 @@ return results; } + /** + * Asynchronously auto-refresh the requested resource if Auto-Refresh is enabled + * @param target + */ + private void asyncRefresh(IResource target) { + if (lightweightAutoRefreshEnabled) + workspace.getRefreshManager().refresh(target); + } + private void findLinkedResourcesPaths(URI inputLocation, final ArrayList results) throws CoreException { IPath suffix = null; IFileStore fileStore = EFS.getStore(inputLocation); @@ -621,10 +636,12 @@ return projectInfo.getLocalSyncInfo() == getStore(descriptionFile).fetchInfo().getLastModified(); } - /* (non-Javadoc) + /** * Returns true if the given resource is synchronized with the file system * to the given depth. Returns false otherwise. * + * Any discovered out-of-sync resources are scheduled to be brought back in sync. + * * @see IResource#isSynchronized(int) */ public boolean isSynchronized(IResource target, int depth) { @@ -661,12 +678,25 @@ Policy.log(e); return false; } catch (IsSynchronizedVisitor.ResourceChangedException e) { + // Ask refresh manager to bring out-of-sync resource back into sync when convenient + asyncRefresh(e.target); //visitor throws an exception if out of sync return false; } return true; } + /** + * Check whether the preference {@link RefreshManager#PREF_LIGHTWEIGHT_AUTO_REFRESH} is + * enabled. When this preference is true the Resources plugin automatically refreshes + * resources which are known to be out-of-sync, and may install lightweight filesystem + * notification hooks. + * @return whether this FSRM is automatically refreshing discovered out-of-sync resources + */ + public boolean isLightweightAutoRefreshEnabled() { + return lightweightAutoRefreshEnabled; + } + public void link(Resource target, URI location, IFileInfo fileInfo) throws CoreException { initializeStore(target, location); ResourceInfo info = target.getResourceInfo(false, true); @@ -716,18 +746,27 @@ return null; } + public void preferenceChange(PreferenceChangeEvent event) { + if (RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH.equals(event.getKey())) + lightweightAutoRefreshEnabled = Boolean.valueOf((String)event.getNewValue()); + } + public InputStream read(IFile target, boolean force, IProgressMonitor monitor) throws CoreException { IFileStore store = getStore(target); - if (!force) { - final IFileInfo fileInfo = store.fetchInfo(); - if (!fileInfo.exists()) { + final IFileInfo fileInfo = store.fetchInfo(); + if (!fileInfo.exists()) { + asyncRefresh(target); + if (!force) { String message = NLS.bind(Messages.localstore_fileNotFound, store.toString()); throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, target.getFullPath(), message, null); } - ResourceInfo info = ((Resource) target).getResourceInfo(true, false); - int flags = ((Resource) target).getFlags(info); - ((Resource) target).checkExists(flags, true); - if (fileInfo.getLastModified() != info.getLocalSyncInfo()) { + } + ResourceInfo info = ((Resource) target).getResourceInfo(true, false); + int flags = ((Resource) target).getFlags(info); + ((Resource) target).checkExists(flags, true); + if (fileInfo.getLastModified() != info.getLocalSyncInfo()) { + asyncRefresh(target); + if (!force) { String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath()); throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null); } @@ -962,10 +1001,13 @@ public void shutdown(IProgressMonitor monitor) throws CoreException { if (_historyStore != null) _historyStore.shutdown(monitor); + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).removePreferenceChangeListener(this); } public void startup(IProgressMonitor monitor) throws CoreException { - //nothing to do + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).addPreferenceChangeListener(this); + lightweightAutoRefreshEnabled = Platform.getPreferencesService().getBoolean( + ResourcesPlugin.PI_RESOURCES, RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, PreferenceInitializer.PREF_LIGHTWEIGHT_AUTO_REFRESH, null); } /** @@ -1006,10 +1048,12 @@ ResourceInfo info = ((Resource) target).getResourceInfo(true, false); // test if timestamp is the same since last synchronization if (lastModified != info.getLocalSyncInfo()) { + asyncRefresh(target); String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath()); throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null); } if (!fileInfo.exists()) { + asyncRefresh(target); String message = NLS.bind(Messages.localstore_resourceDoesNotExist, target.getFullPath()); throw new ResourceException(IResourceStatus.NOT_FOUND_LOCAL, target.getFullPath(), message, null); } Index: src/org/eclipse/core/internal/localstore/IsSynchronizedVisitor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/localstore/IsSynchronizedVisitor.java,v retrieving revision 1.9 diff -u -r1.9 IsSynchronizedVisitor.java --- src/org/eclipse/core/internal/localstore/IsSynchronizedVisitor.java 4 Apr 2006 20:53:47 -0000 1.9 +++ src/org/eclipse/core/internal/localstore/IsSynchronizedVisitor.java 25 Mar 2011 12:36:10 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -7,25 +7,30 @@ * * Contributors: * IBM Corporation - initial API and implementation + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.localstore; import org.eclipse.core.internal.resources.Resource; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; /** * Visits a unified tree, and throws a ResourceChangedException on the first * node that is discovered to be out of sync. The exception that is thrown - * will not have any meaningful status, message, or stack trace. Nodes - * discovered to be out of sync are not brought to be in sync with the workspace. + * will not have any meaningful status, message, or stack trace. However it + * does contain the target resource which can be used to bring the Resource + * back into sync. */ public class IsSynchronizedVisitor extends CollectSyncStatusVisitor { static class ResourceChangedException extends RuntimeException { private static final long serialVersionUID = 1L; + public final IResource target; + public ResourceChangedException(IResource target) { + this.target = target; + } } - protected static ResourceChangedException exception = new ResourceChangedException(); - /** * Creates a new IsSynchronizedVisitor. */ @@ -36,7 +41,19 @@ /** * @see CollectSyncStatusVisitor#changed(Resource) */ + @Override protected void changed(Resource target) { - throw exception; + throw new ResourceChangedException(target); + } + + @Override + protected void fileToFolder(UnifiedTreeNode node, Resource target) { + changed((Resource)workspace.getRoot().getFolder(target.getFullPath())); + } + + @Override + protected void folderToFile(UnifiedTreeNode node, Resource target) { + // Pass correct gender to changed for notification and async-refresh + changed((Resource)workspace.getRoot().getFile(target.getFullPath())); } } Index: src/org/eclipse/core/internal/refresh/RefreshManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/refresh/RefreshManager.java,v retrieving revision 1.19 diff -u -r1.19 RefreshManager.java --- src/org/eclipse/core/internal/refresh/RefreshManager.java 21 Mar 2011 12:11:43 -0000 1.19 +++ src/org/eclipse/core/internal/refresh/RefreshManager.java 25 Mar 2011 12:36:10 -0000 @@ -10,6 +10,9 @@ *******************************************************************************/ package org.eclipse.core.internal.refresh; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; + import org.eclipse.core.internal.resources.IManager; import org.eclipse.core.internal.utils.Policy; import org.eclipse.core.resources.*; @@ -27,6 +30,25 @@ * @since 3.0 */ public class RefreshManager implements IRefreshResult, IManager, Preferences.IPropertyChangeListener { + + /** + * Name of a preference for configuring whether out-of-sync resources are automatically + * asynchronously refreshed, when discovered to be out-of-sync by the workspace. + *

+ * This preference suppresses out-of-sync CoreException for some read methods, including: + * {@link IFile#getContents()} & {@link IFile#getContentDescription()}. + *

+ *

+ * In the future the workspace may enable other lightweight auto-refresh mechanisms when this + * preferece is true. (The existing {@link ResourcesPlugin#PREF_AUTO_REFRESH} will continue + * to enable filesystem hooks and the existing polling based monitor.) + *

+ * This is a is true by default. Integrators should take care when + * changing this from the default. See the discussion: https://bugs.eclipse.org/303517 + * @since 3.7 + */ + public static final String PREF_LIGHTWEIGHT_AUTO_REFRESH = "refresh.lightweight.enabled"; //$NON-NLS-1$ + public static boolean DEBUG = Policy.DEBUG_AUTO_REFRESH; public static final String DEBUG_PREFIX = "Auto-refresh: "; //$NON-NLS-1$ MonitorManager monitors; Index: src/org/eclipse/core/internal/resources/ContentDescriptionManager.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/ContentDescriptionManager.java,v retrieving revision 1.43 diff -u -r1.43 ContentDescriptionManager.java --- src/org/eclipse/core/internal/resources/ContentDescriptionManager.java 21 Mar 2011 12:11:43 -0000 1.43 +++ src/org/eclipse/core/internal/resources/ContentDescriptionManager.java 25 Mar 2011 12:36:11 -0000 @@ -300,7 +300,15 @@ return projectContentTypes.getMatcherFor(project); } - public IContentDescription getDescriptionFor(File file, ResourceInfo info) throws CoreException { + /** + * Discovers, and caches, the content description of the requested File. + * @param file to discover the content description for; result cached + * @param info ResourceInfo for the passed in file + * @param in_sync boolean flag which indicates if cache can be trusted. If false false don't trust the cache + * @return IContentDescription for the file + * @throws CoreException + */ + public IContentDescription getDescriptionFor(File file, ResourceInfo info, boolean in_sync) throws CoreException { if (ProjectContentTypes.usesContentTypePreferences(file.getFullPath().segment(0))) // caching for project containing project specific settings is not supported return readDescription(file); @@ -311,7 +319,7 @@ // the cache is not good, flush it flushJob.schedule(1000); } - if (getCacheState() != ABOUT_TO_FLUSH) { + if (in_sync && getCacheState() != ABOUT_TO_FLUSH) { // first look for the flags in the resource info to avoid looking in the cache // don't need to copy the info because the modified bits are not in the deltas if (info == null) @@ -332,12 +340,14 @@ info.clear(ICoreConstants.M_CONTENT_CACHE); } } - synchronized (this) { + if (in_sync) { // tries to get a description from the cache - Cache.Entry entry = cache.getEntry(file.getFullPath()); - if (entry != null && entry.getTimestamp() == getTimestamp(info)) - // there was a description in the cache, and it was up to date - return (IContentDescription) entry.getCached(); + synchronized (this) { + Cache.Entry entry = cache.getEntry(file.getFullPath()); + if (entry != null && entry.getTimestamp() == getTimestamp(info)) + // there was a description in the cache, and it was up to date + return (IContentDescription) entry.getCached(); + } } // either we didn't find a description in the cache, or it was not up-to-date - has to be read again @@ -347,7 +357,7 @@ synchronized (this) { // tries to get a description from the cache Cache.Entry entry = cache.getEntry(file.getFullPath()); - if (entry != null && entry.getTimestamp() == getTimestamp(info)) + if (entry != null && in_sync && entry.getTimestamp() == getTimestamp(info)) // there was a description in the cache, and it was up to date return (IContentDescription) entry.getCached(); @@ -375,7 +385,7 @@ entry = cache.addEntry(file.getFullPath(), newDescription, getTimestamp(info)); else { // just update the existing entry - entry.setTimestamp(info.getContentId()); + entry.setTimestamp(getTimestamp(info)); entry.setCached(newDescription); } return newDescription; Index: src/org/eclipse/core/internal/resources/File.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/File.java,v retrieving revision 1.94 diff -u -r1.94 File.java --- src/org/eclipse/core/internal/resources/File.java 13 May 2010 11:54:45 -0000 1.94 +++ src/org/eclipse/core/internal/resources/File.java 25 Mar 2011 12:36:11 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Serge Beauchamp (Freescale Semiconductor) - [229633] Group and Project Path Variable Support + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.resources; @@ -94,16 +95,6 @@ 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) */ @@ -252,7 +243,7 @@ if (charset != null || !checkImplicit) return charset; // tries to obtain a description for the file contents - IContentDescription description = workspace.getContentDescriptionManager().getDescriptionFor(this, info); + IContentDescription description = workspace.getContentDescriptionManager().getDescriptionFor(this, info, true); if (description != null) { String contentCharset = description.getCharset(); if (contentCharset != null) @@ -270,16 +261,21 @@ ResourceInfo info = getResourceInfo(false, false); int flags = getFlags(info); checkAccessible(flags); - checkSynchronized(); checkLocal(flags, DEPTH_ZERO); - return workspace.getContentDescriptionManager().getDescriptionFor(this, info); + 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); } /* (non-Javadoc) * @see IFile#getContents() */ public InputStream getContents() throws CoreException { - return getContents(false); + return getContents(getLocalManager().isLightweightAutoRefreshEnabled()); } /* (non-Javadoc) Index: src/org/eclipse/core/internal/resources/PreferenceInitializer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/PreferenceInitializer.java,v retrieving revision 1.12 diff -u -r1.12 PreferenceInitializer.java --- src/org/eclipse/core/internal/resources/PreferenceInitializer.java 13 Oct 2009 09:28:08 -0000 1.12 +++ src/org/eclipse/core/internal/resources/PreferenceInitializer.java 25 Mar 2011 12:36:11 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2009 IBM Corporation and others. + * Copyright (c) 2004, 2011 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 @@ -7,9 +7,12 @@ * * Contributors: * IBM Corporation - initial API and implementation + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.resources; +import org.eclipse.core.internal.refresh.RefreshManager; + import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.preferences.*; @@ -24,6 +27,7 @@ // DEFAULTS public static final boolean PREF_AUTO_REFRESH_DEFAULT = false; + public static final boolean PREF_LIGHTWEIGHT_AUTO_REFRESH = false; public static final boolean PREF_DISABLE_LINKING_DEFAULT = false; public static final String PREF_ENCODING_DEFAULT = ""; //$NON-NLS-1$ public static final boolean PREF_AUTO_BUILDING_DEFAULT = true; @@ -49,6 +53,7 @@ IEclipsePreferences node = new DefaultScope().getNode(ResourcesPlugin.PI_RESOURCES); // auto-refresh default node.putBoolean(ResourcesPlugin.PREF_AUTO_REFRESH, PREF_AUTO_REFRESH_DEFAULT); + node.putBoolean(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, PREF_LIGHTWEIGHT_AUTO_REFRESH); // linked resources default node.putBoolean(ResourcesPlugin.PREF_DISABLE_LINKING, PREF_DISABLE_LINKING_DEFAULT); Index: src/org/eclipse/core/resources/IFile.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/resources/IFile.java,v retrieving revision 1.63 diff -u -r1.63 IFile.java --- src/org/eclipse/core/resources/IFile.java 22 Jan 2010 13:25:46 -0000 1.63 +++ src/org/eclipse/core/resources/IFile.java 25 Mar 2011 12:36:11 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -7,9 +7,12 @@ * * Contributors: * IBM Corporation - initial API and implementation + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.resources; +import org.eclipse.core.internal.refresh.RefreshManager; + import java.io.InputStream; import java.io.Reader; import java.net.URI; @@ -669,7 +672,8 @@ *
  • This resource could not be read.
  • *
  • This resource is not local.
  • *
  • The workspace is not in sync with the corresponding location - * in the local file system.
  • + * in the local file system and {@link RefreshManager#PREF_LIGHTWEIGHT_AUTO_REFRESH} is + * disabled. *
  • The corresponding location in the local file system * is occupied by a directory.
  • * @@ -681,8 +685,17 @@ /** * Returns an open input stream on the contents of this file. - * This refinement of the corresponding IStorage method - * returns an open input stream on the contents of this file. + *

    + * This refinement of the corresponding {@link IStorage} method + * is a convenience method returning an open input stream. It's equivalent to: + *

    +	 *   getContents(RefreshManager#PREF_LIGHTWEIGHT_AUTO_REFRESH);
    +	 * 
    + *

    + *

    + * If lightweight auto-refresh is not enabled this method will throw a CoreException + * when opening out-of-sync resources. + *

    * The client is responsible for closing the stream when finished. * * @return an input stream containing the contents of the file @@ -690,8 +703,10 @@ * */ public InputStream getContents() throws CoreException; Index: src/org/eclipse/core/resources/ResourcesPlugin.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java,v retrieving revision 1.58 diff -u -r1.58 ResourcesPlugin.java --- src/org/eclipse/core/resources/ResourcesPlugin.java 20 Jan 2011 15:40:00 -0000 1.58 +++ src/org/eclipse/core/resources/ResourcesPlugin.java 25 Mar 2011 12:36:11 -0000 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Serge Beauchamp (Freescale Semiconductor) - [252996] add PT_FILTER_PROVIDERS * Serge Beauchamp (Freescale Semiconductor) - [229633] add PT_VARIABLE_PROVIDERS + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.resources; @@ -280,7 +281,9 @@ /** * Name of a preference for configuring whether the workspace performs auto- - * refresh. + * refresh. Auto-refresh installs a file-system listener, or performs + * periodic file-system polling to actively discover changes in the resource + * hierarchy. * @since 3.0 */ public static final String PREF_AUTO_REFRESH = "refresh.enabled"; //$NON-NLS-1$ #P org.eclipse.core.tests.resources Index: src/org/eclipse/core/tests/resources/CharsetTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/CharsetTest.java,v retrieving revision 1.37 diff -u -r1.37 CharsetTest.java --- src/org/eclipse/core/tests/resources/CharsetTest.java 28 Dec 2010 14:22:46 -0000 1.37 +++ src/org/eclipse/core/tests/resources/CharsetTest.java 25 Mar 2011 12:36:14 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 IBM Corporation and others. + * Copyright (c) 2004, 2011 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.tests.resources; @@ -14,9 +15,12 @@ import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.core.internal.preferences.EclipsePreferences; +import org.eclipse.core.internal.refresh.RefreshManager; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.*; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.service.prefs.BackingStoreException; public class CharsetTest extends ResourceTest { @@ -359,11 +363,22 @@ } } - public void testBug186984() { + /** + * Test for getting charset on an IFile: + * #getContentDescription() checks file sync state(), always returning the + * correct content description, whereas getCharset() uses the cached charset if available. + * @throws Exception + */ + public void testBug186984() throws Exception { + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putBoolean(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, false); IWorkspace workspace = getWorkspace(); IProject project = workspace.getRoot().getProject(getUniqueString()); IFile file = project.getFile("file.xml"); + // Test changing content types externally as per bug 186984 Comment 8 + String ascii = ""; + String utf = ""; + // test if we can get the charset, when the file doesn't exist in a file system try { file.getCharset(true); @@ -389,21 +404,64 @@ } catch (CoreException ex) { fail("3.0"); } - //getContentDescription checks synchronization state, so it should fail - try { - file.getContentDescription(); - fail("3.1"); - } catch (CoreException ex) { - assertEquals("3.2", IResourceStatus.OUT_OF_SYNC_LOCAL, ex.getStatus().getCode()); - } - // test if we can get the charset, when the file is refreshed - try { - file.refreshLocal(IResource.DEPTH_ZERO, null); - file.getCharset(true); - } catch (CoreException ex) { - fail("4.0"); - } + // set the content type within the XML file, ensure that #getContentDescription (which respects sync state) + // returns the correct value. + + // 1) first set the content type to ascii + file.setContents(new ByteArrayInputStream(ascii.getBytes("ascii")), IResource.FORCE, getMonitor()); + assertTrue("4.0", file.getCharset().equals("ascii")); + assertTrue("4.1", file.getContentDescription().getCharset().equals("ascii")); + + // 2) Make out of sync - Methods should still work, giving the previous value + touchInFilesystem(file); + assertTrue("4.2", file.getCharset().equals("ascii")); + try { + file.getContentDescription().getCharset().equals("ascii"); + assertTrue("4.3", false); + } catch (CoreException e) { + // expected + } + + // As we now know that #getContentDescription correctly checks sync state, just enable LIGHTWEIGHT refresh + // for the rest of the test. + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putBoolean(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, true); + assertTrue("4.4", file.getContentDescription().getCharset().equals("ascii")); + + // getContentDescription will have noticed out-of-sync + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + // Prime the cache... + assertTrue("4.5", file.getCharset().equals("ascii")); + + // 3) Change the content type of the file under eclipse's feet + FileWriter writer = new FileWriter(file.getLocation().toFile()); + writer.write(utf); + writer.close(); + touchInFilesystem(file); + // #getCharset uses the cached value (bug 209167) - doesn't check sync state + assertTrue("5.4", file.getCharset().equals("ascii")); + // #getContentDescription checks sync and discovers the real content type + assertTrue("5.5", file.getContentDescription().getCharset().equals("UTF-8")); + // getContentDescription will have noticed out-of-sync + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + // #getCharset will now have noticed that the file has changed. + assertTrue("5.6", file.getCharset().equals("UTF-8")); + + // 4) Change the content type of the file under eclipse's feet once more (to non-default). + writer = new FileWriter(file.getLocation().toFile()); + writer.write(ascii); + writer.close(); + touchInFilesystem(file); + // #getCharset uses the cached value (bug 209167) - doesn't check sync state + assertTrue("6.7", file.getCharset().equals("UTF-8")); + // #getContentDescription checks sync and discovers the real content type + assertTrue("6.8", file.getContentDescription().getCharset().equals("ascii")); + // getContentDescription will have noticed out-of-sync + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + assertTrue("6.9", file.getCharset().equals("ascii")); + + // And disable lightweight refresh again before we leave + ResourcesPlugin.getPlugin().getPluginPreferences().setValue(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, false); } public void testBug207510() { Index: src/org/eclipse/core/tests/resources/IResourceTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/IResourceTest.java,v retrieving revision 1.49 diff -u -r1.49 IResourceTest.java --- src/org/eclipse/core/tests/resources/IResourceTest.java 6 Oct 2010 19:35:14 -0000 1.49 +++ src/org/eclipse/core/tests/resources/IResourceTest.java 25 Mar 2011 12:36:15 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -18,6 +18,7 @@ import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.tests.harness.CancelingProgressMonitor; import org.eclipse.core.tests.harness.FussyProgressMonitor; @@ -192,7 +193,7 @@ } //out of sync IResource[] unsynchronized = buildResources(root, new String[] {"1/2/3/3"}); - ensureOutOfSync(unsynchronized[0]); + ensureOutOfSync((IFile) unsynchronized[0]); unsynchronizedResources.add(unsynchronized[0]); //file system only @@ -424,6 +425,13 @@ /** * Sets up the workspace and file system for this test. */ protected void setupBeforeState(IResource receiver, IResource target, int state, int depth, boolean addVerifier) throws CoreException { + // Wait for any outstanding refresh to finish + try { + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + } catch (InterruptedException e) { + fail("interrupted unexpectedly"); + } + if (addVerifier) { /* install the verifier */ if (verifier == null) { @@ -466,7 +474,7 @@ break; case S_CHANGED : ensureExistsInWorkspace(target, true); - ensureOutOfSync(target); + touchInFilesystem(target); if (addVerifier) { verifier.reset(); // we only get a delta if the receiver of refreshLocal Index: src/org/eclipse/core/tests/resources/ResourceTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/ResourceTest.java,v retrieving revision 1.29 diff -u -r1.29 ResourceTest.java --- src/org/eclipse/core/tests/resources/ResourceTest.java 24 Mar 2011 15:33:11 -0000 1.29 +++ src/org/eclipse/core/tests/resources/ResourceTest.java 25 Mar 2011 12:36:15 -0000 @@ -71,7 +71,7 @@ * test is complete * @see #getTempStore */ - private final Set storesToDelete = new HashSet(); + private final Set storesToDelete = new HashSet(); /** * Does some garbage collections to free unused resources @@ -373,7 +373,7 @@ } protected void cleanup() throws CoreException { - final IFileStore[] toDelete = (IFileStore[]) storesToDelete.toArray(new IFileStore[0]); + final IFileStore[] toDelete = storesToDelete.toArray(new IFileStore[0]); storesToDelete.clear(); getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { @@ -658,22 +658,36 @@ } /** - * Modifies the resource in the file system so that it is out of sync + * Modifies the passed in IFile in the file system so that it is out of sync * with the workspace. */ - public void ensureOutOfSync(final IResource resource) { - if (resource.getType() != IResource.FILE) - return; - IFile file = (IFile) resource; - ensureExistsInWorkspace(file, true); - while (file.isSynchronized(IResource.DEPTH_ZERO)) { - modifyInFileSystem(file); + public void ensureOutOfSync(final IFile file) { + touchInFilesystem(file); + modifyInFileSystem(file); + assertTrue("File not out of sync: " + file.getLocation().toOSString(), file.getLocation().toFile().lastModified() != file.getLocalTimeStamp()); + } + + /** + * Touch (but don't modify) the resource in the filesystem so that it's modification stamp is newer than + * the cached value in the Workspace. + */ + public void touchInFilesystem(IResource resource) { + java.io.File osFile = resource.getLocation().toFile(); + // Ensure the resource exists in the filesystem + if (!osFile.exists()) + ensureExistsInFileSystem(resource); + // Manually check that the core.resource time-stamp is out-of-sync + // with the java.io.File last modified. #isSynchronized() will schedule + // out-of-sync resources for refresh, so we don't use that here. + for (int count = 0; count < 30 && osFile.lastModified() == resource.getLocalTimeStamp(); count++) { try { Thread.sleep(100); } catch (InterruptedException e) { // ignore } + resource.getLocation().toFile().setLastModified(System.currentTimeMillis()); } + assertTrue("File not out of sync: " + resource.getLocation().toOSString(), osFile.lastModified() != resource.getLocalTimeStamp()); } private boolean existsInFileSystem(IResource resource) { Index: src/org/eclipse/core/tests/resources/regression/AllTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/AllTests.java,v retrieving revision 1.45 diff -u -r1.45 AllTests.java --- src/org/eclipse/core/tests/resources/regression/AllTests.java 12 Jan 2011 09:38:31 -0000 1.45 +++ src/org/eclipse/core/tests/resources/regression/AllTests.java 25 Mar 2011 12:36:15 -0000 @@ -58,6 +58,7 @@ suite.addTest(Bug_265810.suite()); suite.addTest(Bug_264182.suite()); suite.addTest(Bug_288315.suite()); + suite.addTest(Bug_303517.suite()); suite.addTest(Bug_329836.suite()); suite.addTest(Bug_331445.suite()); suite.addTest(Bug_332543.suite()); Index: src/org/eclipse/core/tests/resources/regression/Bug_303517.java =================================================================== RCS file: src/org/eclipse/core/tests/resources/regression/Bug_303517.java diff -N src/org/eclipse/core/tests/resources/regression/Bug_303517.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/tests/resources/regression/Bug_303517.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2011 Broadcom 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: + * James Blackburn (Broadcom Corp.) - initial API and implementation + *******************************************************************************/ +package org.eclipse.core.tests.resources.regression; + +import java.io.File; +import java.io.InputStream; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.eclipse.core.internal.refresh.RefreshManager; +import org.eclipse.core.resources.*; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.core.tests.resources.ResourceTest; + +/** + * Tests that, when the workspace discovery a resource is out-of-sync + * it brings the resource back into sync in a timely manner. + */ +public class Bug_303517 extends ResourceTest { + + public static Test suite() { + return new TestSuite(Bug_303517.class); + } + + String[] resources = new String[] {"/", "/Bug303517/", "/Bug303517/Folder/", "/Bug303517/Folder/Resource",}; + + public String[] defineHierarchy() { + return resources; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putBoolean(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, true); + } + + @Override + protected void tearDown() throws Exception { + super.setUp(); + InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES).putBoolean(RefreshManager.PREF_LIGHTWEIGHT_AUTO_REFRESH, false); + } + + /** + * Tests that file deleted is udpated after #getContents + * @throws Exception + */ + public void testExists() throws Exception { + createHierarchy(); + IFile f = getWorkspace().getRoot().getFile(new Path(resources[resources.length - 1])); + assertTrue("1.0", f.exists()); + assertTrue("1.1", f.isSynchronized(IResource.DEPTH_ONE)); + + // Touch on file-system + f.getLocation().toFile().delete(); + // Core.resources still thinks the file exists + assertTrue("1.2", f.exists()); + try { + InputStream in = f.getContents(); + in.close(); + assertTrue("1.3", false); + } catch (CoreException e) { + // File doesn't exist - expected + } + + // Wait for auto-refresh to happen + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + + // Core.resources should be aware that the file no longer exists... + assertFalse("1.4", f.exists()); + } + + /** + * Tests that file discovered out-of-sync during #getContents is updated + * @throws Exception + */ + public void testGetContents() throws Exception { + createHierarchy(); + IFile f = getWorkspace().getRoot().getFile(new Path(resources[resources.length - 1])); + assertTrue("1.0", f.exists()); + assertTrue("1.1", f.isSynchronized(IResource.DEPTH_ONE)); + + // Touch on file-system + touchInFilesystem(f); + try { + InputStream in = f.getContents(false); + in.close(); + assertTrue("1.2", false); + } catch (CoreException e) { + // File is out-of-sync, so this is good + } + + // Wait for auto-refresh to happen + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + + // File is now in sync. + try { + InputStream in = f.getContents(false); + in.close(); + } catch (CoreException e) { + // Bad, file shouldn't be out-of-sync + fail("1.3", e); + } + } + + /** + * Tests that file discovered out-of-sync during #getContents is updated + * @throws Exception + */ + public void testGetContentsTrue() throws Exception { + createHierarchy(); + IFile f = getWorkspace().getRoot().getFile(new Path(resources[resources.length - 1])); + assertTrue("1.0", f.exists()); + assertTrue("1.1", f.isSynchronized(IResource.DEPTH_ONE)); + + // Touch on file-system + touchInFilesystem(f); + try { + InputStream in = f.getContents(true); + in.close(); + } catch (CoreException e) { + // File is out-of-sync, so this is good + fail("1.2", e); + } + + // Wait for auto-refresh to happen + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + + // File is now in sync. + try { + InputStream in = f.getContents(); + in.close(); + } catch (CoreException e) { + // Bad, file shouldn't be out-of-sync + fail("1.3", e); + } + } + + /** + * Tests that resource discovered out-of-sync during #isSynchronized is updated + * @throws Exception + */ + public void testIsSynchronized() throws Exception { + createHierarchy(); + IFile f = getWorkspace().getRoot().getFile(new Path(resources[resources.length - 1])); + assertTrue("1.0", f.exists()); + assertTrue("1.1", f.isSynchronized(IResource.DEPTH_ONE)); + + // Touch on file-system + touchInFilesystem(f); + assertFalse("1.2", f.isSynchronized(IResource.DEPTH_ONE)); + + // Wait for auto-refresh to happen + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + + // File is now in sync. + assertTrue("1.3", f.isSynchronized(IResource.DEPTH_ONE)); + } + + /** + * Tests that when changing resource gender is correctly picked up. + * @throws Exception + */ + public void testChangeResourceGender() throws Exception { + createHierarchy(); + IResource f = getWorkspace().getRoot().getFile(new Path(resources[resources.length - 1])); + assertTrue("1.0", f.exists()); + assertTrue("1.1", f.isSynchronized(IResource.DEPTH_ONE)); + + // Replace the file with a folder + File osResource = f.getLocation().toFile(); + osResource.delete(); + osResource.mkdir(); + assertTrue(osResource.exists()); + File osChild = new File(osResource, "child"); + osChild.createNewFile(); + assertTrue(osChild.exists()); + + assertFalse("1.2", f.isSynchronized(IResource.DEPTH_ONE)); + + // Wait for auto-refresh to happen + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_REFRESH, getMonitor()); + + // File is no longer a file - i.e. still out-of-sync + assertFalse("1.3", f.exists()); + assertFalse("1.4", f.isSynchronized(IResource.DEPTH_ONE)); + // Folder + child are now in-sync + f = getWorkspace().getRoot().getFolder(new Path(resources[resources.length - 1])); + assertTrue("1.5", f.exists()); + assertTrue("1.6", f.isSynchronized(IResource.DEPTH_INFINITE)); + } +} Index: src/org/eclipse/core/tests/resources/regression/IProjectTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/resources/regression/IProjectTest.java,v retrieving revision 1.19 diff -u -r1.19 IProjectTest.java --- src/org/eclipse/core/tests/resources/regression/IProjectTest.java 20 May 2009 23:50:56 -0000 1.19 +++ src/org/eclipse/core/tests/resources/regression/IProjectTest.java 25 Mar 2011 12:36:17 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -241,13 +241,7 @@ try { project.create(null); project.open(null); - while (dotProject.isSynchronized(IResource.DEPTH_ZERO)) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - } - dotProject.getLocation().toFile().setLastModified(System.currentTimeMillis()); - } + touchInFilesystem(dotProject); project.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { fail("0.99", e);