Bug 533821 - Revise text file snapshot implementation
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotTest.java
new file mode 100644
index 0000000..4984836
--- /dev/null
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotTest.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2018 1C-Soft LLC.
+ * 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:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.snapshot;
+
+import java.io.ByteArrayInputStream;
+import java.util.function.Supplier;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.handly.junit.WorkspaceTestCase;
+
+/**
+ * <code>TextFileSnapshot</code> and <code>TextFileStoreSnapshot</code> tests.
+ */
+public class TextFileSnapshotTest
+ extends WorkspaceTestCase
+{
+ private IFile fileX;
+ private IFile fileY;
+ private IFile fileZ;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IProject p = setUpProject("Test002");
+ fileX = p.getFile("x.txt");
+ fileY = p.getFile("y.txt");
+ fileZ = p.getFile("z.txt");
+ }
+
+ public void test01() throws Exception
+ {
+ _testA(snapshotSupplier1(fileX), snapshotSupplier1(fileX));
+ _testA(snapshotSupplier1(fileX), snapshotSupplier2(fileX));
+ _testA(snapshotSupplier1(fileX), snapshotSupplier3(fileX));
+ }
+
+ public void test02() throws Exception
+ {
+ _testA(snapshotSupplier1(fileX), snapshotSupplier1(fileY));
+ _testA(snapshotSupplier1(fileX), snapshotSupplier2(fileY));
+ _testA(snapshotSupplier1(fileX), snapshotSupplier3(fileY));
+ }
+
+ public void test03() throws Exception
+ {
+ _testB(snapshotSupplier1(fileX), snapshotSupplier1(fileZ));
+ _testB(snapshotSupplier1(fileX), snapshotSupplier2(fileZ));
+ _testB(snapshotSupplier1(fileX), snapshotSupplier3(fileZ));
+ }
+
+ public void test04() throws Exception
+ {
+ _testC(snapshotSupplier1(fileX));
+ }
+
+ public void test05() throws Exception
+ {
+ _testD(snapshotSupplier1(fileX));
+ }
+
+ public void test06() throws Exception
+ {
+ _testA(snapshotSupplier2(fileX), snapshotSupplier1(fileX));
+ _testA(snapshotSupplier2(fileX), snapshotSupplier2(fileX));
+ _testA(snapshotSupplier2(fileX), snapshotSupplier3(fileX));
+ }
+
+ public void test07() throws Exception
+ {
+ _testA(snapshotSupplier2(fileX), snapshotSupplier1(fileY));
+ _testA(snapshotSupplier2(fileX), snapshotSupplier2(fileY));
+ _testA(snapshotSupplier2(fileX), snapshotSupplier3(fileY));
+ }
+
+ public void test08() throws Exception
+ {
+ _testB(snapshotSupplier2(fileX), snapshotSupplier1(fileZ));
+ _testB(snapshotSupplier2(fileX), snapshotSupplier2(fileZ));
+ _testB(snapshotSupplier2(fileX), snapshotSupplier3(fileZ));
+ }
+
+ public void test09() throws Exception
+ {
+ _testC(snapshotSupplier2(fileX));
+ }
+
+ public void test10() throws Exception
+ {
+ _testD(snapshotSupplier2(fileX));
+ }
+
+ public void test11() throws Exception
+ {
+ _testA(snapshotSupplier3(fileX), snapshotSupplier1(fileX));
+ _testA(snapshotSupplier3(fileX), snapshotSupplier2(fileX));
+ _testA(snapshotSupplier3(fileX), snapshotSupplier3(fileX));
+ }
+
+ public void test12() throws Exception
+ {
+ _testA(snapshotSupplier3(fileX), snapshotSupplier1(fileY));
+ _testA(snapshotSupplier3(fileX), snapshotSupplier2(fileY));
+ _testA(snapshotSupplier3(fileX), snapshotSupplier3(fileY));
+ }
+
+ public void test13() throws Exception
+ {
+ _testB(snapshotSupplier3(fileX), snapshotSupplier1(fileZ));
+ _testB(snapshotSupplier3(fileX), snapshotSupplier2(fileZ));
+ _testB(snapshotSupplier3(fileX), snapshotSupplier3(fileZ));
+ }
+
+ public void test14() throws Exception
+ {
+ _testC(snapshotSupplier3(fileX));
+ }
+
+ public void test15() throws Exception
+ {
+ _testD(snapshotSupplier3(fileX));
+ }
+
+ private Supplier<TextFileSnapshotBase> snapshotSupplier1(IFile file)
+ {
+ return () -> new TextFileSnapshot(file,
+ TextFileSnapshot.Layer.WORKSPACE);
+ }
+
+ private Supplier<TextFileSnapshotBase> snapshotSupplier2(IFile file)
+ {
+ return () -> new TextFileSnapshot(file,
+ TextFileSnapshot.Layer.FILESYSTEM);
+ }
+
+ private Supplier<TextFileSnapshotBase> snapshotSupplier3(IFile file)
+ {
+ return () ->
+ {
+ IFileStore fileStore;
+ try
+ {
+ fileStore = EFS.getStore(file.getLocationURI());
+ }
+ catch (CoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ return new TextFileStoreSnapshot(fileStore);
+ };
+ }
+
+ private void _testA(Supplier<TextFileSnapshotBase> supplier,
+ Supplier<TextFileSnapshotBase> otherSupplier)
+ {
+ TextFileSnapshotBase snapshot1 = supplier.get();
+ TextFileSnapshotBase snapshot2 = otherSupplier.get();
+ assertNotSame(snapshot1, snapshot2);
+ assertTrue(snapshot1.isEqualTo(snapshot2));
+ }
+
+ private void _testB(Supplier<TextFileSnapshotBase> supplier,
+ Supplier<TextFileSnapshotBase> otherSupplier)
+ {
+ TextFileSnapshotBase snapshot1 = supplier.get();
+ TextFileSnapshotBase snapshot2 = otherSupplier.get();
+ assertFalse(snapshot1.isEqualTo(snapshot2));
+ }
+
+ private void _testC(Supplier<TextFileSnapshotBase> snapshotSupplier)
+ throws Exception
+ {
+ TextFileSnapshotBase snapshot = snapshotSupplier.get();
+ assertTrue(snapshot.exists());
+ assertTrue(snapshot.getStatus().isOK());
+ assertEquals("hello", snapshot.getContents());
+ fileX.appendContents(new ByteArrayInputStream(", world!".getBytes()),
+ true, false, null);
+ TextFileSnapshotBase snapshot2 = snapshotSupplier.get();
+ assertTrue(snapshot2.exists());
+ assertTrue(snapshot2.getStatus().isOK());
+ assertEquals("hello, world!", snapshot2.getContents());
+ assertFalse(snapshot.isEqualTo(snapshot2));
+ }
+
+ private void _testD(Supplier<TextFileSnapshotBase> snapshotSupplier)
+ throws Exception
+ {
+ TextFileSnapshotBase snapshot = snapshotSupplier.get();
+ assertTrue(snapshot.exists());
+ assertTrue(snapshot.getStatus().isOK());
+ assertEquals("hello", snapshot.getContents());
+ fileX.delete(true, null);
+ assertTrue(snapshot.exists());
+ TextFileSnapshotBase snapshot2 = snapshotSupplier.get();
+ assertFalse(snapshot2.exists());
+ assertTrue(snapshot2.getStatus().isOK());
+ assertEquals("", snapshot2.getContents());
+ assertFalse(snapshot.isEqualTo(snapshot2));
+ }
+}
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotWsTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotWsTest.java
new file mode 100644
index 0000000..c5808e9
--- /dev/null
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileSnapshotWsTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2018 1C-Soft LLC.
+ * 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:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.snapshot;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.handly.junit.WorkspaceTestCase;
+
+/**
+ * <code>TextFileSnapshotWs</code> tests.
+ */
+public class TextFileSnapshotWsTest
+ extends WorkspaceTestCase
+{
+ private IFile file;
+ private TextFileSnapshotWs snapshot;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IProject p = setUpProject("Test002");
+ file = p.getFile("x.txt");
+ snapshot = new TextFileSnapshotWs(file);
+ }
+
+ public void test1() throws Exception
+ {
+ assertEquals("hello", snapshot.getContents());
+ assertEquals("hello", snapshot.getContents()); // another code path
+ file.touch(null);
+ assertNull(snapshot.getContents());
+ }
+
+ public void test2()
+ {
+ assertEquals("hello", snapshot.getContents());
+ assertTrue(file.getLocation().toFile().delete());
+ assertEquals("hello", snapshot.getContents());
+ }
+
+ public void test3()
+ {
+ assertTrue(file.getLocation().toFile().delete());
+ assertNull(snapshot.getContents());
+ assertNull(snapshot.getContents()); // another code path
+ }
+
+ public void test4()
+ {
+ assertEquals("hello", snapshot.getContents());
+ snapshot.clearContents();
+ assertEquals("hello", snapshot.getContents());
+ }
+
+ public void test5()
+ {
+ assertEquals("hello", snapshot.getContents());
+ assertTrue(file.getLocation().toFile().delete());
+ snapshot.clearContents();
+ assertNull(snapshot.getContents());
+ }
+}
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileStoreSnapshotTest.java b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileStoreSnapshotTest.java
new file mode 100644
index 0000000..538d84a
--- /dev/null
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/snapshot/TextFileStoreSnapshotTest.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 1C-Soft LLC.
+ * 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:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.snapshot;
+
+import java.nio.charset.Charset;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.handly.junit.WorkspaceTestCase;
+
+/**
+ * <code>TextFileStoreSnapshot</code> tests.
+ */
+public class TextFileStoreSnapshotTest
+ extends WorkspaceTestCase
+{
+ private IFileStore fileStore;
+ private TextFileStoreSnapshot snapshot;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ IProject p = setUpProject("Test002");
+ IFile file = p.getFile("x.txt");
+ fileStore = EFS.getStore(file.getLocationURI());
+ snapshot = new TextFileStoreSnapshot(fileStore, Charset.forName(
+ "UTF-8"));
+ }
+
+ public void test1() throws Exception
+ {
+ assertEquals("hello", snapshot.getContents());
+ IFileInfo info = fileStore.fetchInfo();
+ info.setLastModified(info.getLastModified() + 1000);
+ fileStore.putInfo(info, EFS.SET_LAST_MODIFIED, null);
+ assertNull(snapshot.getContents());
+ }
+}
diff --git a/org.eclipse.handly.tests/workspace/Test002/.project b/org.eclipse.handly.tests/workspace/Test002/.project
new file mode 100644
index 0000000..47360f5
--- /dev/null
+++ b/org.eclipse.handly.tests/workspace/Test002/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Test002</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ </buildSpec>
+ <natures>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.handly.tests/workspace/Test002/.settings/org.eclipse.core.resources.prefs b/org.eclipse.handly.tests/workspace/Test002/.settings/org.eclipse.core.resources.prefs
new file mode 100755
index 0000000..8a7cd42
--- /dev/null
+++ b/org.eclipse.handly.tests/workspace/Test002/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
+encoding//x.txt=UTF-8
diff --git a/org.eclipse.handly.tests/workspace/Test002/x.txt b/org.eclipse.handly.tests/workspace/Test002/x.txt
new file mode 100644
index 0000000..b6fc4c6
--- /dev/null
+++ b/org.eclipse.handly.tests/workspace/Test002/x.txt
@@ -0,0 +1 @@
+hello
\ No newline at end of file
diff --git a/org.eclipse.handly.tests/workspace/Test002/y.txt b/org.eclipse.handly.tests/workspace/Test002/y.txt
new file mode 100644
index 0000000..b6fc4c6
--- /dev/null
+++ b/org.eclipse.handly.tests/workspace/Test002/y.txt
@@ -0,0 +1 @@
+hello
\ No newline at end of file
diff --git a/org.eclipse.handly.tests/workspace/Test002/z.txt b/org.eclipse.handly.tests/workspace/Test002/z.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.handly.tests/workspace/Test002/z.txt
diff --git a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshot.java b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshot.java
index 9b55adf..1a66674 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshot.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshot.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014, 2016 1C-Soft LLC and others.
+ * Copyright (c) 2014, 2018 1C-Soft LLC 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
@@ -10,16 +10,13 @@
*******************************************************************************/
package org.eclipse.handly.snapshot;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.URI;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.handly.internal.Activator;
/**
@@ -28,10 +25,7 @@
public final class TextFileSnapshot
extends TextFileSnapshotBase
{
- private final IFile file;
- private final boolean fromFs;
- private final long modificationStamp;
- private String charset;
+ private final TextFileSnapshotBase delegate;
/**
* Takes a snapshot of the given text file in the workspace.
@@ -52,93 +46,43 @@
{
if (file == null)
throw new IllegalArgumentException();
- this.file = file;
- this.fromFs = layer.equals(Layer.FILESYSTEM);
- this.modificationStamp = getFileModificationStamp(file, fromFs);
+ this.delegate = createDelegate(file, layer);
+ }
+
+ @Override
+ public String getContents()
+ {
+ return delegate.getContents();
+ }
+
+ @Override
+ public IStatus getStatus()
+ {
+ return delegate.getStatus();
}
@Override
public boolean exists()
{
- if (fromFs)
- return modificationStamp != EFS.NONE;
- else
- return modificationStamp != IResource.NULL_STAMP;
+ return delegate.exists();
}
@Override
protected Boolean predictEquality(Snapshot other)
{
if (other instanceof TextFileSnapshot)
- {
- TextFileSnapshot otherSnapshot = (TextFileSnapshot)other;
- if (file.equals(otherSnapshot.file)
- && fromFs == otherSnapshot.fromFs
- && modificationStamp == otherSnapshot.modificationStamp)
- return true;
- }
-
- if (!isSynchronized())
- return false; // expired
+ return delegate.predictEquality(((TextFileSnapshot)other).delegate);
return null;
}
- @Override
- boolean isSynchronized()
+ private static TextFileSnapshotBase createDelegate(IFile file, Layer layer)
{
- return modificationStamp == getFileModificationStamp(file, fromFs)
- && getStatus().isOK();
- }
-
- @Override
- void cacheCharset() throws CoreException
- {
- if (charset != null)
- return;
-
- charset = file.getCharset(false);
- if (charset == null)
- {
- try (InputStream contents = file.getContents(fromFs))
- {
- charset = getCharset(contents, file.getName());
- }
- catch (IOException e)
- {
- throw new CoreException(Activator.createErrorStatus(
- e.getMessage(), e));
- }
- }
- if (charset == null)
- charset = file.getParent().getDefaultCharset();
- }
-
- @Override
- String readContents() throws CoreException
- {
- try (
- InputStream stream = file.getContents(fromFs);
- InputStreamReader reader = new InputStreamReader(stream, charset))
- {
- return String.valueOf(getInputStreamAsCharArray(stream, reader));
- }
- catch (IOException e)
- {
- throw new CoreException(Activator.createErrorStatus(e.getMessage(),
- e));
- }
- }
-
- private static long getFileModificationStamp(IFile file, boolean fromFs)
- {
- if (!fromFs)
- return file.getModificationStamp();
- else
+ if (layer == Layer.FILESYSTEM)
{
URI uri = file.getLocationURI();
if (uri == null)
- return EFS.NONE;
+ return NON_EXISTING;
IFileStore fileStore;
try
{
@@ -147,10 +91,11 @@
catch (CoreException e)
{
Activator.log(e.getStatus());
- return EFS.NONE;
+ return NON_EXISTING;
}
- return fileStore.fetchInfo().getLastModified();
+ return new TextFileStoreSnapshot(fileStore);
}
+ return new TextFileSnapshotWs(file);
}
/**
diff --git a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotBase.java b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotBase.java
index b8a57ce..722deee 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotBase.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotBase.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014, 2016 1C-Soft LLC and others.
+ * Copyright (c) 2014, 2018 1C-Soft LLC 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
@@ -13,16 +13,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentDescription;
-import org.eclipse.handly.internal.Activator;
/**
* Internal base for file related snapshot implementations.
@@ -30,56 +26,37 @@
abstract class TextFileSnapshotBase
extends Snapshot
{
+ static final TextFileSnapshotBase NON_EXISTING = new TextFileSnapshotBase()
+ {
+ @Override
+ public String getContents()
+ {
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public IStatus getStatus()
+ {
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean exists()
+ {
+ return false;
+ }
+ };
+
private static final int DEFAULT_READING_SIZE = 8192;
private static final char[] EMPTY_CHAR_ARRAY = new char[0];
- private Reference<String> contents;
- private volatile IStatus status = Status.OK_STATUS;
-
- @Override
- public synchronized String getContents()
- {
- if (!exists())
- return ""; //$NON-NLS-1$
-
- String result = null;
- boolean sync = isSynchronized();
- if (contents != null)
- {
- if (!sync)
- contents = null; // no need to continue holding on contents
- else
- result = contents.get();
- }
- if (result == null && sync)
- {
- try
- {
- cacheCharset();
- String currentContents = readContents();
- if (isSynchronized()) // still current
- contents = new SoftReference<String>(result =
- currentContents);
- }
- catch (CoreException e)
- {
- Activator.log(e.getStatus());
- status = e.getStatus();
- }
- }
- return result;
- }
-
/**
* Returns whether an I/O error was encountered while reading the file.
*
* @return an error status if an I/O error was encountered, or OK status
* otherwise
*/
- public IStatus getStatus()
- {
- return status;
- }
+ public abstract IStatus getStatus();
/**
* Returns whether the file existed at the moment this snapshot was taken.
@@ -89,12 +66,6 @@
*/
public abstract boolean exists();
- abstract boolean isSynchronized();
-
- abstract void cacheCharset() throws CoreException;
-
- abstract String readContents() throws CoreException;
-
static String getCharset(InputStream contents, String fileName)
throws IOException
{
diff --git a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotWs.java b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotWs.java
new file mode 100644
index 0000000..affa949
--- /dev/null
+++ b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileSnapshotWs.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2018 1C-Soft LLC.
+ * 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:
+ * Vladimir Piskarev (1C) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.handly.snapshot;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.handly.internal.Activator;
+
+/**
+ * Internal representation for a snapshot of a text file in the workspace.
+ * Thread-safe.
+ */
+final class TextFileSnapshotWs
+ extends TextFileSnapshotBase
+{
+ private final IFile file;
+ private final long modificationStamp;
+ private Reference<String> contents;
+ private String charset;
+ private volatile IStatus status = Status.OK_STATUS;
+
+ /**
+ * Takes a snapshot of the given text file in the workspace.
+ *
+ * @param file not <code>null</code>
+ */
+ public TextFileSnapshotWs(IFile file)
+ {
+ if (file == null)
+ throw new IllegalArgumentException();
+ this.file = file;
+ this.modificationStamp = file.getModificationStamp();
+ }
+
+ @Override
+ public synchronized String getContents()
+ {
+ if (!exists())
+ return ""; //$NON-NLS-1$
+
+ String result = null;
+ boolean current = isCurrent();
+ if (contents != null)
+ {
+ if (!current)
+ contents = null; // no need to continue holding on contents
+ else
+ result = contents.get();
+ }
+ if (result == null && current)
+ {
+ try
+ {
+ cacheCharset();
+ String currentContents = readContents();
+ if (isCurrent()) // still current
+ contents = new SoftReference<String>(result =
+ currentContents);
+ }
+ catch (CoreException e)
+ {
+ Activator.log(e.getStatus());
+ status = e.getStatus();
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public IStatus getStatus()
+ {
+ return status;
+ }
+
+ @Override
+ public boolean exists()
+ {
+ return modificationStamp != IResource.NULL_STAMP;
+ }
+
+ @Override
+ protected Boolean predictEquality(Snapshot other)
+ {
+ if (other instanceof TextFileSnapshotWs)
+ {
+ TextFileSnapshotWs otherSnapshot = (TextFileSnapshotWs)other;
+ if (file.equals(otherSnapshot.file)
+ && modificationStamp == otherSnapshot.modificationStamp)
+ return true;
+ }
+
+ if (!isCurrent())
+ return false; // expired
+
+ return null;
+ }
+
+ private boolean isCurrent()
+ {
+ return modificationStamp == file.getModificationStamp()
+ && status.isOK();
+ }
+
+ private void cacheCharset() throws CoreException
+ {
+ if (charset != null)
+ return;
+
+ charset = file.getCharset(false);
+ if (charset == null)
+ {
+ try (InputStream contents = file.getContents(false))
+ {
+ charset = getCharset(contents, file.getName());
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(Activator.createErrorStatus(
+ e.getMessage(), e));
+ }
+ }
+ if (charset == null)
+ charset = file.getParent().getDefaultCharset();
+ }
+
+ private String readContents() throws CoreException
+ {
+ try (
+ InputStream stream = file.getContents(false);
+ InputStreamReader reader = new InputStreamReader(stream, charset))
+ {
+ return String.valueOf(getInputStreamAsCharArray(stream, reader));
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(Activator.createErrorStatus(e.getMessage(),
+ e));
+ }
+ }
+
+ /*
+ * For testing purposes only.
+ */
+ void clearContents()
+ {
+ contents.clear();
+ }
+}
diff --git a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileStoreSnapshot.java b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileStoreSnapshot.java
index 1615bef..80755a6 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileStoreSnapshot.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/snapshot/TextFileStoreSnapshot.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016 1C-Soft LLC.
+ * Copyright (c) 2016, 2018 1C-Soft LLC.
* 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
@@ -19,6 +19,8 @@
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
import org.eclipse.handly.internal.Activator;
/**
@@ -28,8 +30,9 @@
extends TextFileSnapshotBase
{
private final IFileStore fileStore;
- private final long modificationStamp;
- private String charset;
+ private final long lastModified;
+ private final IStatus status;
+ private String contents;
/**
* Takes a snapshot of the given text file store. The snapshot may use a
@@ -40,10 +43,7 @@
*/
public TextFileStoreSnapshot(IFileStore fileStore)
{
- if (fileStore == null)
- throw new IllegalArgumentException();
- this.fileStore = fileStore;
- this.modificationStamp = getFileStoreModificationStamp(fileStore);
+ this(fileStore, (String)null);
}
/**
@@ -55,62 +55,73 @@
*/
public TextFileStoreSnapshot(IFileStore fileStore, Charset charset)
{
- this(fileStore);
- this.charset = charset.name();
+ this(fileStore, charset.name());
+ }
+
+ private TextFileStoreSnapshot(IFileStore fileStore, String charset)
+ {
+ if (fileStore == null)
+ throw new IllegalArgumentException();
+ this.fileStore = fileStore;
+ this.lastModified = getLastModified(fileStore);
+ if (this.lastModified == EFS.NONE)
+ {
+ this.status = Status.OK_STATUS;
+ this.contents = ""; //$NON-NLS-1$
+ }
+ else
+ {
+ IStatus status = Status.OK_STATUS;
+ String contents = null;
+ try
+ {
+ contents = readContents(fileStore, charset);
+ }
+ catch (CoreException e)
+ {
+ Activator.log(e.getStatus());
+ status = e.getStatus();
+ }
+ this.status = status;
+ this.contents = contents;
+ }
+ }
+
+ @Override
+ public synchronized String getContents()
+ {
+ if (contents != null && lastModified != getLastModified(fileStore))
+ contents = null;
+
+ return contents;
+ }
+
+ @Override
+ public IStatus getStatus()
+ {
+ return status;
}
@Override
public boolean exists()
{
- return modificationStamp != EFS.NONE;
+ return lastModified != EFS.NONE;
}
@Override
protected Boolean predictEquality(Snapshot other)
{
- if (other instanceof TextFileStoreSnapshot)
- {
- TextFileStoreSnapshot otherSnapshot = (TextFileStoreSnapshot)other;
- if (fileStore.equals(otherSnapshot.fileStore)
- && modificationStamp == otherSnapshot.modificationStamp)
- return true;
- }
-
- if (!isSynchronized())
+ if (lastModified != getLastModified(fileStore) || !status.isOK())
return false; // expired
return null;
}
- @Override
- boolean isSynchronized()
+ private static String readContents(IFileStore fileStore, String charset)
+ throws CoreException
{
- return modificationStamp == getFileStoreModificationStamp(fileStore)
- && getStatus().isOK();
- }
-
- @Override
- void cacheCharset() throws CoreException
- {
- if (charset != null)
- return;
-
- try (InputStream contents = fileStore.openInputStream(EFS.NONE, null))
- {
- charset = getCharset(contents, fileStore.getName());
- }
- catch (IOException e)
- {
- throw new CoreException(Activator.createErrorStatus(e.getMessage(),
- e));
- }
if (charset == null)
- charset = ITextFileBufferManager.DEFAULT.getDefaultEncoding();
- }
-
- @Override
- String readContents() throws CoreException
- {
+ charset = detectCharset(fileStore);
try (
InputStream stream = fileStore.openInputStream(EFS.NONE, null);
InputStreamReader reader = new InputStreamReader(stream, charset))
@@ -124,7 +135,25 @@
}
}
- private static long getFileStoreModificationStamp(IFileStore fileStore)
+ private static String detectCharset(IFileStore fileStore)
+ throws CoreException
+ {
+ String charset = null;
+ try (InputStream contents = fileStore.openInputStream(EFS.NONE, null))
+ {
+ charset = getCharset(contents, fileStore.getName());
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(Activator.createErrorStatus(e.getMessage(),
+ e));
+ }
+ if (charset == null)
+ charset = ITextFileBufferManager.DEFAULT.getDefaultEncoding();
+ return charset;
+ }
+
+ private static long getLastModified(IFileStore fileStore)
{
return fileStore.fetchInfo().getLastModified();
}