Bug 506332 - Revise working copy related APIs
diff --git a/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyNotificationTest.java b/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyNotificationTest.java
index 6baad1b..8ce8104 100644
--- a/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyNotificationTest.java
+++ b/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyNotificationTest.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.handly.internal.examples.basic.ui.model;
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import static org.eclipse.handly.model.IElementDeltaConstants.F_CONTENT;
import static org.eclipse.handly.model.IElementDeltaConstants.F_UNDERLYING_RESOURCE;
import static org.eclipse.handly.model.IElementDeltaConstants.F_WORKING_COPY;
@@ -18,9 +19,7 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.handly.buffer.BufferChange;
-import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.buffer.SaveMode;
-import org.eclipse.handly.buffer.TextFileBuffer;
import org.eclipse.handly.examples.basic.ui.model.FooModelCore;
import org.eclipse.handly.examples.basic.ui.model.IFooDef;
import org.eclipse.handly.examples.basic.ui.model.IFooElement;
@@ -44,7 +43,6 @@
extends WorkspaceTestCase
{
private FooFile workingCopy;
- private IBuffer buffer;
private IFooModel fooModel = FooModelCore.getFooModel();
private FooModelListener listener = new FooModelListener();
@@ -55,14 +53,11 @@
IFooProject fooProject = FooModelCore.create(setUpProject("Test002"));
workingCopy = (FooFile)fooProject.getFooFile("test.foo");
fooModel.addElementChangeListener(listener);
- buffer = TextFileBuffer.forFile(workingCopy.getFile());
}
@Override
protected void tearDown() throws Exception
{
- if (buffer != null)
- buffer.release();
fooModel.removeElementChangeListener(listener);
super.tearDown();
}
@@ -109,7 +104,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "g")); // rename f() to g()
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertDelta(null, listener.delta);
@@ -148,7 +144,8 @@
BufferChange change = new BufferChange(new DeleteEdit(
r.getOffset(), r.getLength())); // delete 'var y;'
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertDelta(null, listener.delta);
@@ -165,7 +162,8 @@
change = // insert 'var y;' before 'var x;'
new BufferChange(new InsertEdit(r.getOffset(), varYText));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertDelta(null, listener.delta);
@@ -197,7 +195,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "def f(y) {}")); // instead of 'def f(x) {}'
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertDelta(null, listener.delta);
@@ -212,14 +211,14 @@
private void doWithWorkingCopy(IWorkspaceRunnable runnable)
throws CoreException
{
- workingCopy.hBecomeWorkingCopy(buffer, null);
+ workingCopy.hBecomeWorkingCopy(EMPTY_CONTEXT, null);
try
{
runnable.run(null);
}
finally
{
- workingCopy.hDiscardWorkingCopy();
+ workingCopy.hReleaseWorkingCopy();
}
}
diff --git a/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyTest.java b/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyTest.java
index 9c144a3..6b18725 100644
--- a/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyTest.java
+++ b/org.eclipse.handly.examples.basic.ui.tests/src/org/eclipse/handly/internal/examples/basic/ui/model/FooWorkingCopyTest.java
@@ -10,13 +10,16 @@
*******************************************************************************/
package org.eclipse.handly.internal.examples.basic.ui.model;
+import static org.eclipse.handly.context.Contexts.*;
+
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.handly.buffer.Buffer;
import org.eclipse.handly.buffer.BufferChange;
import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.buffer.SaveMode;
-import org.eclipse.handly.buffer.TextFileBuffer;
+import org.eclipse.handly.context.IContext;
import org.eclipse.handly.examples.basic.ui.model.FooModelCore;
import org.eclipse.handly.examples.basic.ui.model.IFooDef;
import org.eclipse.handly.examples.basic.ui.model.IFooProject;
@@ -24,6 +27,7 @@
import org.eclipse.handly.junit.WorkspaceTestCase;
import org.eclipse.handly.model.ISourceElementInfo;
import org.eclipse.handly.model.impl.Element;
+import org.eclipse.handly.model.impl.SourceFile;
import org.eclipse.handly.model.impl.WorkingCopyInfo;
import org.eclipse.handly.util.TextRange;
import org.eclipse.text.edits.DeleteEdit;
@@ -37,7 +41,6 @@
extends WorkspaceTestCase
{
private FooFile workingCopy;
- private IBuffer buffer;
@Override
protected void setUp() throws Exception
@@ -45,20 +48,11 @@
super.setUp();
IFooProject fooProject = FooModelCore.create(setUpProject("Test002"));
workingCopy = (FooFile)fooProject.getFooFile("test.foo");
- buffer = TextFileBuffer.forFile(workingCopy.getFile());
- }
-
- @Override
- protected void tearDown() throws Exception
- {
- if (buffer != null)
- buffer.release();
- super.tearDown();
}
public void test1() throws Exception
{
- doWithWorkingCopy(new IWorkspaceRunnable()
+ doWithWorkingCopy(EMPTY_CONTEXT, new IWorkspaceRunnable()
{
@Override
public void run(IProgressMonitor monitor) throws CoreException
@@ -73,7 +67,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "g"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
defs = workingCopy.getDefs();
assertEquals(3, defs.length);
@@ -92,7 +87,7 @@
public void test2() throws Exception
{
- doWithWorkingCopy(new IWorkspaceRunnable()
+ doWithWorkingCopy(EMPTY_CONTEXT, new IWorkspaceRunnable()
{
@Override
public void run(IProgressMonitor monitor) throws CoreException
@@ -112,7 +107,8 @@
BufferChange change = new BufferChange(new DeleteEdit(
r.getOffset(), r.getLength()));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
vars = workingCopy.getVars();
assertEquals(2, vars.length);
@@ -132,7 +128,8 @@
change = new BufferChange(new InsertEdit(r.getOffset(),
var2Text));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
vars = workingCopy.getVars();
assertEquals(1, vars.length);
@@ -151,20 +148,27 @@
{
// working copy for a non-existing source file
workingCopy.getParent().getResource().delete(true, null);
- doWithWorkingCopy(new IWorkspaceRunnable()
+ try (
+ IBuffer buffer = new Buffer(
+ "var x; var y; def f() {} def f(x) {} def f(x, y) {}"))
{
- @Override
- public void run(IProgressMonitor monitor) throws CoreException
- {
- assertFalse(workingCopy.getParent().exists());
- assertTrue(workingCopy.exists());
+ doWithWorkingCopy(of(SourceFile.WORKING_COPY_BUFFER, buffer),
+ new IWorkspaceRunnable()
+ {
+ @Override
+ public void run(IProgressMonitor monitor)
+ throws CoreException
+ {
+ assertFalse(workingCopy.getParent().exists());
+ assertTrue(workingCopy.exists());
- IFooVar[] vars = workingCopy.getVars();
- assertEquals(2, vars.length);
- IFooDef[] defs = workingCopy.getDefs();
- assertEquals(3, defs.length);
- }
- });
+ IFooVar[] vars = workingCopy.getVars();
+ assertEquals(2, vars.length);
+ IFooDef[] defs = workingCopy.getDefs();
+ assertEquals(3, defs.length);
+ }
+ });
+ }
}
public void testBug479623() throws Exception
@@ -179,7 +183,8 @@
{
while (!stop[0])
{
- WorkingCopyInfo info = workingCopy.hAcquireWorkingCopy();
+ WorkingCopyInfo info =
+ workingCopy.hAcquireExistingWorkingCopy(null);
if (info != null)
{
try
@@ -192,7 +197,7 @@
}
finally
{
- workingCopy.hDiscardWorkingCopy();
+ workingCopy.hReleaseWorkingCopy();
}
}
}
@@ -201,7 +206,7 @@
thread.start();
try
{
- doWithWorkingCopy(new IWorkspaceRunnable()
+ doWithWorkingCopy(EMPTY_CONTEXT, new IWorkspaceRunnable()
{
@Override
public void run(IProgressMonitor monitor) throws CoreException
@@ -221,7 +226,7 @@
public void testBug480397_2() throws Exception
{
// working copy cannot be closed
- doWithWorkingCopy(new IWorkspaceRunnable()
+ doWithWorkingCopy(EMPTY_CONTEXT, new IWorkspaceRunnable()
{
@Override
public void run(IProgressMonitor monitor) throws CoreException
@@ -253,21 +258,21 @@
}
};
// non-openable elements cannot be closed, in working copy or not
- doWithWorkingCopy(testRunnable);
+ doWithWorkingCopy(EMPTY_CONTEXT, testRunnable);
testRunnable.run(null);
}
- private void doWithWorkingCopy(IWorkspaceRunnable runnable)
- throws CoreException
+ private void doWithWorkingCopy(IContext context,
+ IWorkspaceRunnable runnable) throws CoreException
{
- workingCopy.hBecomeWorkingCopy(buffer, null);
+ workingCopy.hBecomeWorkingCopy(context, null);
try
{
runnable.run(null);
}
finally
{
- workingCopy.hDiscardWorkingCopy();
+ workingCopy.hReleaseWorkingCopy();
}
}
}
diff --git a/org.eclipse.handly.examples.basic.ui/src/org/eclipse/handly/internal/examples/basic/ui/model/FooFile.java b/org.eclipse.handly.examples.basic.ui/src/org/eclipse/handly/internal/examples/basic/ui/model/FooFile.java
index c05c23d..9a446bb 100644
--- a/org.eclipse.handly.examples.basic.ui/src/org/eclipse/handly/internal/examples/basic/ui/model/FooFile.java
+++ b/org.eclipse.handly.examples.basic.ui/src/org/eclipse/handly/internal/examples/basic/ui/model/FooFile.java
@@ -91,12 +91,27 @@
}
@Override
- protected void hBuildStructure(Object ast, IContext context,
- IProgressMonitor monitor)
+ protected void hBuildStructure(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
Map<IElement, Object> newElements = context.get(NEW_ELEMENTS);
SourceElementBody body = new SourceElementBody();
- XtextResource resource = (XtextResource)ast;
+
+ XtextResource resource = (XtextResource)context.get(SOURCE_AST);
+ if (resource == null)
+ {
+ try
+ {
+ resource = parse(context.get(SOURCE_CONTENTS),
+ getFile().getCharset());
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(Activator.createErrorStatus(
+ e.getMessage(), e));
+ }
+ }
+
IParseResult parseResult = resource.getParseResult();
if (parseResult != null)
{
@@ -108,32 +123,8 @@
builder.buildStructure(this, body, (Module)root, monitor);
}
}
- newElements.put(this, body);
- }
- /**
- * Returns a new <code>XtextResource</code> loaded from the given source
- * string. The resource is created in a new <code>ResourceSet</code>
- * obtained from the <code>IResourceSetProvider</code> corresponding to
- * this file.
- *
- * @return the new <code>XtextResource</code> loaded from the given source
- * string (never <code>null</code>)
- * @throws CoreException if resource loading failed
- */
- @Override
- protected XtextResource hCreateAst(String source, IContext context,
- IProgressMonitor monitor) throws CoreException
- {
- try
- {
- return parse(source, getFile().getCharset());
- }
- catch (IOException e)
- {
- throw new CoreException(Activator.createErrorStatus(e.getMessage(),
- e));
- }
+ newElements.put(this, body);
}
/**
diff --git a/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyNotificationTest.java b/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyNotificationTest.java
index f07eb20..71cacd3 100644
--- a/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyNotificationTest.java
+++ b/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyNotificationTest.java
@@ -10,15 +10,15 @@
*******************************************************************************/
package org.eclipse.handly.internal.examples.javamodel;
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
+
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.handly.buffer.BufferChange;
-import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.buffer.SaveMode;
-import org.eclipse.handly.buffer.TextFileBuffer;
import org.eclipse.handly.examples.javamodel.ICompilationUnit;
import org.eclipse.handly.examples.javamodel.IField;
import org.eclipse.handly.examples.javamodel.IMethod;
@@ -37,7 +37,6 @@
extends WorkspaceTestCase
{
private CompilationUnit workingCopy;
- private IBuffer buffer;
private JavaModelListener listener = new JavaModelListener();
@Override
@@ -47,7 +46,6 @@
IProject project = setUpProject("Test010");
workingCopy = (CompilationUnit)JavaModelCore.createCompilationUnitFrom(
project.getFile(new Path("src/X.java")));
- buffer = TextFileBuffer.forFile(workingCopy.getFile());
workingCopy.getJavaModel().addElementChangeListener(listener);
}
@@ -56,8 +54,6 @@
{
if (workingCopy != null)
workingCopy.getJavaModel().removeElementChangeListener(listener);
- if (buffer != null)
- buffer.release();
super.tearDown();
}
@@ -115,7 +111,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "Y"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertNull(listener.delta);
@@ -145,7 +142,8 @@
BufferChange change = new BufferChange(new DeleteEdit(
r.getOffset(), r.getLength()));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertNull(listener.delta);
@@ -164,7 +162,8 @@
change = new BufferChange(new InsertEdit(r.getOffset(),
"int y;"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertNull(listener.delta);
@@ -195,7 +194,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "void f() {}"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertNull(listener.delta);
@@ -227,7 +227,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "void f(int y) {}")); // renamed arg
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
assertNull(listener.delta);
@@ -247,14 +248,14 @@
private void doWithWorkingCopy(IWorkspaceRunnable runnable)
throws CoreException
{
- workingCopy.hBecomeWorkingCopy(buffer, null);
+ workingCopy.hBecomeWorkingCopy(EMPTY_CONTEXT, null);
try
{
runnable.run(null);
}
finally
{
- workingCopy.hDiscardWorkingCopy();
+ workingCopy.hReleaseWorkingCopy();
}
}
}
diff --git a/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyTest.java b/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyTest.java
index 7c9185e..914b40c 100644
--- a/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyTest.java
+++ b/org.eclipse.handly.examples.javamodel.tests/src/org/eclipse/handly/internal/examples/javamodel/WorkingCopyTest.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.handly.internal.examples.javamodel;
+import static org.eclipse.handly.context.Contexts.of;
+
import java.util.ArrayList;
import java.util.List;
@@ -22,13 +24,14 @@
import org.eclipse.handly.buffer.ChildBuffer;
import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.buffer.SaveMode;
-import org.eclipse.handly.buffer.TextFileBuffer;
+import org.eclipse.handly.context.IContext;
import org.eclipse.handly.examples.javamodel.ICompilationUnit;
import org.eclipse.handly.examples.javamodel.IField;
import org.eclipse.handly.examples.javamodel.IMethod;
import org.eclipse.handly.examples.javamodel.IType;
import org.eclipse.handly.examples.javamodel.JavaModelCore;
import org.eclipse.handly.junit.WorkspaceTestCase;
+import org.eclipse.handly.model.impl.SourceFile;
import org.eclipse.handly.util.TextRange;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.WorkingCopyOwner;
@@ -50,7 +53,6 @@
private static final int AST_LEVEL = AST.JLS8;
private CompilationUnit workingCopy;
- private IBuffer buffer;
private List<IProblem> problems;
private IProblemRequestor problemRequestor = new ProblemRequestor();
@@ -61,18 +63,9 @@
IProject project = setUpProject("Test010");
workingCopy = (CompilationUnit)JavaModelCore.createCompilationUnitFrom(
project.getFile(new Path("src/X.java")));
- buffer = TextFileBuffer.forFile(workingCopy.getFile());
problems = new ArrayList<IProblem>();
}
- @Override
- protected void tearDown() throws Exception
- {
- if (buffer != null)
- buffer.release();
- super.tearDown();
- }
-
public void test001() throws Exception
{
doWithWorkingCopy(new IWorkspaceRunnable()
@@ -89,7 +82,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "Y"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
types = workingCopy.getTypes();
assertEquals(1, types.length);
@@ -123,7 +117,8 @@
BufferChange change = new BufferChange(new DeleteEdit(
r.getOffset(), r.getLength()));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
fields = typeX.getFields();
assertEquals(1, fields.length);
@@ -138,7 +133,8 @@
change = new BufferChange(new InsertEdit(r.getOffset(),
"int y;"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
fields = typeX.getFields();
assertEquals(0, fields.length);
@@ -169,7 +165,8 @@
BufferChange change = new BufferChange(new ReplaceEdit(
r.getOffset(), r.getLength(), "void f() {}"));
change.setSaveMode(SaveMode.LEAVE_UNSAVED);
- buffer.applyChange(change, null);
+ workingCopy.hWorkingCopyInfo().getBuffer().applyChange(change,
+ null);
methods = typeX.getMethods();
assertEquals(1, methods.length);
@@ -313,9 +310,12 @@
{
});
assertFalse(privateCopy.equals(workingCopy));
- try (IBuffer privateBuffer = new ChildBuffer(buffer))
+ try (
+ IBuffer buffer = workingCopy.getBuffer();
+ IBuffer privateBuffer = new ChildBuffer(buffer))
{
- doWithWorkingCopy(privateCopy, privateBuffer, null,
+ doWithWorkingCopy(privateCopy, of(
+ SourceFile.WORKING_COPY_BUFFER, privateBuffer),
new IWorkspaceRunnable()
{
public void run(IProgressMonitor monitor)
@@ -362,22 +362,21 @@
private void doWithWorkingCopy(IWorkspaceRunnable runnable)
throws CoreException
{
- doWithWorkingCopy(workingCopy, buffer, problemRequestor, runnable);
+ doWithWorkingCopy(workingCopy, of(IProblemRequestor.class,
+ problemRequestor), runnable);
}
- private static void doWithWorkingCopy(CompilationUnit workingCopy,
- IBuffer buffer, IProblemRequestor problemRequestor,
+ private static void doWithWorkingCopy(CompilationUnit cu, IContext context,
IWorkspaceRunnable runnable) throws CoreException
{
- workingCopy.hBecomeWorkingCopy(buffer, (IBuffer b) ->
- new JavaWorkingCopyInfo(b, problemRequestor), null);
+ cu.hBecomeWorkingCopy(context, null);
try
{
runnable.run(null);
}
finally
{
- workingCopy.hDiscardWorkingCopy();
+ cu.hReleaseWorkingCopy();
}
}
diff --git a/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/CompilatonUnitDocumentProvider.java b/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/CompilatonUnitDocumentProvider.java
index bc7b965..5d00c63 100644
--- a/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/CompilatonUnitDocumentProvider.java
+++ b/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/CompilatonUnitDocumentProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015 1C-Soft LLC.
+ * Copyright (c) 2015, 2016 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
@@ -10,13 +10,12 @@
*******************************************************************************/
package org.eclipse.handly.internal.examples.javamodel.ui.editor;
-import org.eclipse.handly.buffer.IBuffer;
-import org.eclipse.handly.internal.examples.javamodel.CompilationUnit;
-import org.eclipse.handly.internal.examples.javamodel.JavaWorkingCopyInfo;
+import org.eclipse.handly.examples.javamodel.ICompilationUnit;
import org.eclipse.handly.internal.examples.javamodel.ui.JavaInputElementProvider;
-import org.eclipse.handly.model.impl.IWorkingCopyInfoFactory;
-import org.eclipse.handly.model.impl.SourceFile;
+import org.eclipse.handly.model.IElement;
+import org.eclipse.handly.model.ISourceFile;
import org.eclipse.handly.ui.texteditor.SourceFileDocumentProvider;
+import org.eclipse.ui.IEditorInput;
/**
* Compilation unit document provider.
@@ -24,24 +23,15 @@
public class CompilatonUnitDocumentProvider
extends SourceFileDocumentProvider
{
- public CompilatonUnitDocumentProvider()
- {
- super(JavaInputElementProvider.INSTANCE);
- }
-
@Override
- protected SourceFile getSourceFile(Object element)
+ protected ISourceFile getSourceFile(Object input)
{
- SourceFile sourceFile = super.getSourceFile(element);
- if (!(sourceFile instanceof CompilationUnit))
+ if (!(input instanceof IEditorInput))
return null;
- return sourceFile;
- }
-
- @Override
- protected IWorkingCopyInfoFactory getWorkingCopyInfoFactory(
- SourceFile sourceFile, Object element, FileInfo fileInfo)
- {
- return (IBuffer buffer) -> new JavaWorkingCopyInfo(buffer, null);
+ IElement element = JavaInputElementProvider.INSTANCE.getElement(
+ (IEditorInput)input);
+ if (!(element instanceof ICompilationUnit))
+ return null;
+ return (ICompilationUnit)element;
}
}
diff --git a/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/JavaReconciler.java b/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/JavaReconciler.java
index a7f956e..cae9c41 100644
--- a/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/JavaReconciler.java
+++ b/org.eclipse.handly.examples.javamodel.ui/src/org/eclipse/handly/internal/examples/javamodel/ui/editor/JavaReconciler.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015 1C-Soft LLC.
+ * Copyright (c) 2015, 2016 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
@@ -13,14 +13,14 @@
import org.eclipse.handly.examples.javamodel.JavaModelCore;
import org.eclipse.handly.model.IElementChangeListener;
import org.eclipse.handly.ui.IWorkingCopyManager;
-import org.eclipse.handly.ui.text.reconciler.HandlyReconciler;
+import org.eclipse.handly.ui.text.reconciler.EditorWorkingCopyReconciler;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Reconciler for a Java specific text editor.
*/
public class JavaReconciler
- extends HandlyReconciler
+ extends EditorWorkingCopyReconciler
{
/**
* Creates a new Java reconciler.
diff --git a/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/CompilationUnit.java b/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/CompilationUnit.java
index d7e072c..ea578ae 100644
--- a/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/CompilationUnit.java
+++ b/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/CompilationUnit.java
@@ -11,6 +11,8 @@
package org.eclipse.handly.internal.examples.javamodel;
import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
+import static org.eclipse.handly.context.Contexts.of;
+import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.model.Elements.FORCE_RECONCILING;
import java.util.Map;
@@ -30,7 +32,6 @@
import org.eclipse.handly.examples.javamodel.IType;
import org.eclipse.handly.model.IElement;
import org.eclipse.handly.model.impl.SourceElementBody;
-import org.eclipse.handly.model.impl.WorkingCopyInfo;
import org.eclipse.handly.model.impl.WorkspaceSourceFile;
import org.eclipse.handly.snapshot.ISnapshot;
import org.eclipse.handly.util.Property;
@@ -237,8 +238,7 @@
CompilationUnit.class.getName() + ".ignoreMethodBodies", //$NON-NLS-1$
Boolean.class).withDefault(false);
- @Override
- protected org.eclipse.jdt.core.dom.CompilationUnit hCreateAst(String source,
+ org.eclipse.jdt.core.dom.CompilationUnit createAst(String source,
IContext context, IProgressMonitor monitor) throws CoreException
{
ASTParser parser = ASTParser.newParser(context.getOrDefault(AST_LEVEL));
@@ -259,19 +259,32 @@
}
@Override
- protected void hBuildStructure(Object ast, IContext context,
- IProgressMonitor monitor)
+ protected void hBuildStructure(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
Map<IElement, Object> newElements = context.get(NEW_ELEMENTS);
SourceElementBody body = new SourceElementBody();
+
+ org.eclipse.jdt.core.dom.CompilationUnit cu =
+ (org.eclipse.jdt.core.dom.CompilationUnit)context.get(SOURCE_AST);
+ if (cu == null)
+ cu = createAst(context.get(SOURCE_CONTENTS), context, monitor);
+
CompilatonUnitStructureBuilder builder =
new CompilatonUnitStructureBuilder(newElements);
- builder.buildStructure(this, body,
- (org.eclipse.jdt.core.dom.CompilationUnit)ast);
+ builder.buildStructure(this, body, cu);
+
newElements.put(this, body);
}
@Override
+ protected IContext hWorkingCopyContext(IContext context)
+ {
+ return of(IProblemRequestor.class, context.get(
+ IProblemRequestor.class));
+ }
+
+ @Override
protected ReconcileOperation hReconcileOperation()
{
return new CuReconcileOperation();
@@ -281,13 +294,19 @@
extends NotifyingReconcileOperation
{
@Override
- protected void reconcile(Object ast, IContext context,
- IProgressMonitor monitor) throws CoreException
+ protected void reconcile(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
- super.reconcile(ast, context, monitor);
-
org.eclipse.jdt.core.dom.CompilationUnit cu =
- (org.eclipse.jdt.core.dom.CompilationUnit)ast;
+ (org.eclipse.jdt.core.dom.CompilationUnit)context.get(
+ SOURCE_AST);
+ if (cu == null)
+ {
+ cu = createAst(context.get(SOURCE_CONTENTS), context, monitor);
+ context = with(of(SOURCE_AST, cu), context);
+ }
+
+ super.reconcile(context, monitor);
reportProblems(cu.getProblems());
@@ -300,12 +319,8 @@
{
if (problems == null || problems.length == 0)
return;
- WorkingCopyInfo info = hPeekAtWorkingCopyInfo();
- if (info instanceof JavaWorkingCopyInfo)
- {
- reportProblems(((JavaWorkingCopyInfo)info).problemRequestor,
- problems);
- }
+ reportProblems(hWorkingCopyInfo().getContext().get(
+ IProblemRequestor.class), problems);
}
private void reportProblems(IProblemRequestor requestor,
diff --git a/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/JavaWorkingCopyInfo.java b/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/JavaWorkingCopyInfo.java
deleted file mode 100644
index ea2d849..0000000
--- a/org.eclipse.handly.examples.javamodel/src/org/eclipse/handly/internal/examples/javamodel/JavaWorkingCopyInfo.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015, 2016 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.internal.examples.javamodel;
-
-import org.eclipse.handly.buffer.IBuffer;
-import org.eclipse.handly.model.impl.DefaultWorkingCopyInfo;
-import org.eclipse.jdt.core.IProblemRequestor;
-
-/**
- * Model-specific extension of {@link DefaultWorkingCopyInfo}.
- */
-public class JavaWorkingCopyInfo
- extends DefaultWorkingCopyInfo
-{
- final IProblemRequestor problemRequestor;
-
- public JavaWorkingCopyInfo(IBuffer buffer,
- IProblemRequestor problemRequestor)
- {
- super(buffer);
- this.problemRequestor = problemRequestor;
- }
-}
diff --git a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/SimpleSourceFile.java b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/SimpleSourceFile.java
index a2635dd..511ade4 100644
--- a/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/SimpleSourceFile.java
+++ b/org.eclipse.handly.tests/src/org/eclipse/handly/model/impl/SimpleSourceFile.java
@@ -11,7 +11,6 @@
package org.eclipse.handly.model.impl;
import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.handly.context.IContext;
import org.eclipse.handly.model.IModel;
@@ -53,15 +52,7 @@
}
@Override
- protected Object hCreateAst(String source, IContext context,
- IProgressMonitor monitor) throws CoreException
- {
- return new Object();
- }
-
- @Override
- protected void hBuildStructure(Object ast, IContext context,
- IProgressMonitor monitor)
+ protected void hBuildStructure(IContext context, IProgressMonitor monitor)
{
}
}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyManager.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyManager.java
index 2493471..5e077e0 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyManager.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015 1C-Soft LLC.
+ * Copyright (c) 2015, 2016 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
@@ -10,28 +10,78 @@
*******************************************************************************/
package org.eclipse.handly.ui;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.handly.model.ISourceFile;
-import org.eclipse.ui.IEditorInput;
+import org.eclipse.jface.text.IDocument;
/**
* Interface for accessing working copies of source files.
- * The original source file is only given indirectly by means
- * of an <code>IEditorInput</code>.
- *
- * @noimplement This interface is not intended to be implemented by clients.
- * @noextend This interface is not intended to be extended by clients.
+ * The life cycle is as follows:
+ * <ul>
+ * <li>
+ * {@link #connect} attempts to acquire a working copy for the given element
+ * </li>
+ * <li>
+ * {@link #getWorkingCopy} returns the working copy acquired on {@code connect}
+ * </li>
+ * <li>
+ * {@link #disconnect} releases the working copy acquired on {@code connect}
+ * </li>
+ * </ul>
+ * <p>
+ * Implementations are generally not expected to be thread safe and, if not
+ * mentioned otherwise, may only be called from the user-interface thread.
+ * </p>
*/
public interface IWorkingCopyManager
{
/**
- * Returns the working copy remembered for the source file corresponding to
- * the given editor input.
+ * Connects the given element to this manager. Attempts to acquire a
+ * working copy for the given element.
*
- * @param editorInput the editor input (may be <code>null</code>)
- * @return the working copy remembered for the source file corresponding
- * to the given editor input, or <code>null</code> if there is no source
- * file corresponding to the input or if there is no working copy
- * remembered for the corresponding source file
+ * @param element the element (not <code>null</code>)
+ * @throws CoreException if working copy could not be acquired successfully
*/
- ISourceFile getWorkingCopy(IEditorInput editorInput);
+ void connect(Object element) throws CoreException;
+
+ /**
+ * Disconnects the given element from this manager. Releases the working copy
+ * acquired on {@link #connect}.
+ *
+ * @param element the element (not <code>null</code>)
+ */
+ void disconnect(Object element);
+
+ /**
+ * Returns the working copy managed for the given element.
+ *
+ * @param element the element for which to find the working copy,
+ * or <code>null</code>
+ * @return the working copy managed for the given element,
+ * or <code>null</code> if none
+ */
+ ISourceFile getWorkingCopy(Object element);
+
+ /**
+ * Returns the working copy managed for the given document.
+ * <p>
+ * <b>Note:</b> An implementation may go through the list of working copies and
+ * test whether the working copy buffer's document is equal to the given one.
+ * Therefore, this method should not be used in performance critical code.
+ * </p>
+ *
+ * @param document the document for which to find the working copy,
+ * or <code>null</code>
+ * @return the working copy managed for the given document,
+ * or <code>null</code> if none
+ */
+ ISourceFile getWorkingCopy(IDocument document);
+
+ /**
+ * Returns all working copies that are currently managed by this manager.
+ *
+ * @return the working copies currently managed by this manager
+ * (never <code>null</code>)
+ */
+ ISourceFile[] getWorkingCopies();
}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyProvider.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyProvider.java
deleted file mode 100644
index 476c141..0000000
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/IWorkingCopyProvider.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 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.ui;
-
-import org.eclipse.handly.model.ISourceFile;
-
-/**
- * An object capable of providing a working copy.
- * This interface may be implemented by clients.
- */
-public interface IWorkingCopyProvider
-{
- /**
- * Returns a working copy according to the provider strategy.
- * The result may or may not be the same each time this method is called
- * on the provider.
- *
- * @return the provided working copy, or <code>null</code>
- * if no working copy can be provided
- */
- ISourceFile getWorkingCopy();
-}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/WorkingCopyProvider.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/WorkingCopyProvider.java
deleted file mode 100644
index 2ecb3af..0000000
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/WorkingCopyProvider.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 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.ui;
-
-import org.eclipse.handly.model.ISourceFile;
-import org.eclipse.ui.IEditorPart;
-
-/**
- * Provides the working copy associated with the given editor
- * via the given working copy manager.
- */
-public final class WorkingCopyProvider
- implements IWorkingCopyProvider
-{
- private final IEditorPart editor;
- private final IWorkingCopyManager manager;
-
- /**
- * Creates a new working copy provider for the given editor
- * and the given working copy manager.
- *
- * @param editor the editor (not <code>null</code>)
- * @param manager the working copy manager (not <code>null</code>)
- */
- public WorkingCopyProvider(IEditorPart editor, IWorkingCopyManager manager)
- {
- if (editor == null)
- throw new IllegalArgumentException();
- if (manager == null)
- throw new IllegalArgumentException();
- this.editor = editor;
- this.manager = manager;
- }
-
- @Override
- public ISourceFile getWorkingCopy()
- {
- return manager.getWorkingCopy(editor.getEditorInput());
- }
-}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/HandlyReconciler.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/EditorWorkingCopyReconciler.java
similarity index 83%
rename from org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/HandlyReconciler.java
rename to org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/EditorWorkingCopyReconciler.java
index 3cfc42d..2aca2ca 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/HandlyReconciler.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/EditorWorkingCopyReconciler.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015 1C-Soft LLC.
+ * Copyright (c) 2015, 2016 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
@@ -11,23 +11,22 @@
package org.eclipse.handly.ui.text.reconciler;
import org.eclipse.handly.ui.IWorkingCopyManager;
-import org.eclipse.handly.ui.WorkingCopyProvider;
import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.texteditor.ITextEditor;
/**
* An abstract base class of a working copy reconciler that is activated on
* editor activation and forces reconciling on a significant change in the
* underlying model.
*/
-public abstract class HandlyReconciler
- extends BaseReconciler
+public abstract class EditorWorkingCopyReconciler
+ extends WorkingCopyReconciler
{
- protected final ITextEditor editor;
+ private final IEditorPart editor;
private final IPartListener partListener = new IPartListener()
{
public void partActivated(IWorkbenchPart part)
@@ -58,11 +57,12 @@
* with the given text editor.
*
* @param editor the editor (not <code>null</code>)
- * @param manager the working copy manager (not <code>null</code>)
+ * @param workingCopyManager the working copy manager (not <code>null</code>)
*/
- public HandlyReconciler(ITextEditor editor, IWorkingCopyManager manager)
+ public EditorWorkingCopyReconciler(IEditorPart editor,
+ IWorkingCopyManager workingCopyManager)
{
- super(new WorkingCopyProvider(editor, manager));
+ super(workingCopyManager);
this.editor = editor;
}
@@ -91,4 +91,9 @@
{
return editor;
}
+
+ protected final IEditorPart getEditor()
+ {
+ return editor;
+ }
}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/BaseReconciler.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconciler.java
similarity index 92%
rename from org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/BaseReconciler.java
rename to org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconciler.java
index 7568d8a..e37f752 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/BaseReconciler.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconciler.java
@@ -25,7 +25,7 @@
import org.eclipse.handly.model.IElementChangeListener;
import org.eclipse.handly.model.IElementDelta;
import org.eclipse.handly.model.ISourceFile;
-import org.eclipse.handly.ui.IWorkingCopyProvider;
+import org.eclipse.handly.ui.IWorkingCopyManager;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
@@ -45,11 +45,10 @@
* viewer activation and forces reconciling on a significant change in the
* underlying model.
*/
-public abstract class BaseReconciler
+public abstract class WorkingCopyReconciler
extends AbstractReconciler
- implements IWorkingCopyProvider
{
- private IWorkingCopyProvider provider;
+ private IWorkingCopyManager workingCopyManager;
private IReconcilingStrategy strategy;
private volatile ISourceFile workingCopy;
private volatile boolean active = true;
@@ -65,26 +64,27 @@
return;
if (isAffectedBy(event))
- BaseReconciler.this.elementChanged(event);
+ WorkingCopyReconciler.this.elementChanged(event);
}
};
private ShellListener activationListener;
/**
* Creates a new reconciler that reconciles the working copy provided
- * by the given provider.
+ * by the given manager.
*
- * @param provider the working copy provider (not <code>null</code>)
+ * @param workingCopyManager the working copy manager (not <code>null</code>)
*/
- public BaseReconciler(IWorkingCopyProvider provider)
+ public WorkingCopyReconciler(IWorkingCopyManager workingCopyManager)
{
- if (provider == null)
+ if (workingCopyManager == null)
throw new IllegalArgumentException();
- this.provider = provider;
+ this.workingCopyManager = workingCopyManager;
// Just some reasonable defaults that can be overwritten:
setIsIncrementalReconciler(false);
setIsAllowedToModifyDocument(false);
- setReconcilingStrategy(new WorkingCopyReconcilingStrategy(this));
+ setReconcilingStrategy(new WorkingCopyReconcilingStrategy(
+ workingCopyManager));
}
/**
@@ -122,7 +122,8 @@
{
super.install(textViewer);
- setWorkingCopy(provider.getWorkingCopy());
+ setWorkingCopy(workingCopyManager.getWorkingCopy(
+ textViewer.getDocument()));
addElementChangeListener(elementChangeListener);
@@ -141,6 +142,8 @@
removeElementChangeListener(elementChangeListener);
+ setWorkingCopy(null);
+
super.uninstall();
}
@@ -151,12 +154,6 @@
}
@Override
- public ISourceFile getWorkingCopy()
- {
- return workingCopy;
- }
-
- @Override
protected void initialProcess()
{
synchronized (getReconcilerLock())
@@ -196,7 +193,7 @@
@Override
protected void reconcilerDocumentChanged(IDocument newDocument)
{
- setWorkingCopy(provider.getWorkingCopy());
+ setWorkingCopy(workingCopyManager.getWorkingCopy(newDocument));
strategy.setDocument(newDocument);
}
@@ -209,7 +206,7 @@
*/
protected Object getReconcilerLock()
{
- return this; // Null Object
+ return this;
}
/**
@@ -328,13 +325,23 @@
this.active = active;
if (Display.getCurrent() == null)
throw new AssertionError(
- "This method can only be executed by the UI thread"); //$NON-NLS-1$
+ "This method may only be executed by the user-interface thread"); //$NON-NLS-1$
if (!active)
setModelChanged(false);
else if (hasModelChanged())
forceReconciling();
}
+ private ISourceFile getWorkingCopy()
+ {
+ return workingCopy;
+ }
+
+ private void setWorkingCopy(ISourceFile workingCopy)
+ {
+ this.workingCopy = workingCopy;
+ }
+
private boolean hasModelChanged()
{
return modelChanged;
@@ -345,11 +352,6 @@
this.modelChanged = modelChanged;
}
- private void setWorkingCopy(ISourceFile workingCopy)
- {
- this.workingCopy = workingCopy;
- }
-
private class ActivationListener
extends ShellAdapter
{
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconcilingStrategy.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconcilingStrategy.java
index 414082d..e80cb9e 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconcilingStrategy.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/text/reconciler/WorkingCopyReconcilingStrategy.java
@@ -10,15 +10,14 @@
*******************************************************************************/
package org.eclipse.handly.ui.text.reconciler;
-import static org.eclipse.handly.context.Contexts.of;
-
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.handly.model.Elements;
import org.eclipse.handly.model.ISourceFile;
-import org.eclipse.handly.ui.IWorkingCopyProvider;
+import org.eclipse.handly.ui.IWorkingCopyManager;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.reconciler.DirtyRegion;
@@ -31,20 +30,28 @@
public class WorkingCopyReconcilingStrategy
implements IReconcilingStrategy, IReconcilingStrategyExtension
{
- private final IWorkingCopyProvider provider;
- private IProgressMonitor monitor;
+ private final IWorkingCopyManager workingCopyManager;
+ private volatile ISourceFile workingCopy;
+ private volatile IProgressMonitor monitor;
/**
* Creates a new working copy reconciling strategy
- * with the given working copy provider.
+ * with the given working copy manager.
*
- * @param provider the working copy provider (not <code>null</code>)
+ * @param workingCopyManager the working copy manager (not <code>null</code>)
*/
- public WorkingCopyReconcilingStrategy(IWorkingCopyProvider provider)
+ public WorkingCopyReconcilingStrategy(
+ IWorkingCopyManager workingCopyManager)
{
- if (provider == null)
+ if (workingCopyManager == null)
throw new IllegalArgumentException();
- this.provider = provider;
+ this.workingCopyManager = workingCopyManager;
+ }
+
+ @Override
+ public void setDocument(IDocument document)
+ {
+ setWorkingCopy(workingCopyManager.getWorkingCopy(document));
}
@Override
@@ -60,11 +67,6 @@
}
@Override
- public void setDocument(IDocument document)
- {
- }
-
- @Override
public final void reconcile(DirtyRegion dirtyRegion, IRegion subRegion)
{
reconcile(false);
@@ -81,42 +83,44 @@
*
* @param workingCopy the given working copy (never <code>null</code>)
* @param initialReconcile <code>true</code> if this is the initial reconcile
- * @throws CoreException if the given working copy cannot be reconciled
+ * @param monitor a progress monitor, or <code>null</code>
+ * if progress reporting is not desired
+ * @throws CoreException if the working copy cannot be reconciled
+ * @throws OperationCanceledException if this method is canceled
* @see #initialReconcile()
*/
- protected void reconcile(ISourceFile workingCopy, boolean initialReconcile)
- throws CoreException
+ protected void reconcile(ISourceFile workingCopy, boolean initialReconcile,
+ IProgressMonitor monitor) throws CoreException
{
- Elements.reconcile(workingCopy, of(Elements.FORCE_RECONCILING, true),
- getProgressMonitor());
+ Elements.reconcile(workingCopy, monitor);
}
- /**
- * @return the progress monitor set for this strategy,
- * or <code>null</code> if none
- */
- protected final IProgressMonitor getProgressMonitor()
+ private void reconcile(boolean initialReconcile)
{
- return monitor;
- }
-
- private void reconcile(final boolean initialReconcile)
- {
- final ISourceFile workingCopy = provider.getWorkingCopy();
- if (workingCopy != null)
+ ISourceFile workingCopy = getWorkingCopy();
+ if (workingCopy == null)
+ return;
+ SafeRunner.run(new ISafeRunnable()
{
- SafeRunner.run(new ISafeRunnable()
+ public void run() throws Exception
{
- public void run() throws Exception
- {
- reconcile(workingCopy, initialReconcile);
- }
+ reconcile(workingCopy, initialReconcile, monitor);
+ }
- public void handleException(Throwable exception)
- {
- // already logged by Platform
- }
- });
- }
+ public void handleException(Throwable exception)
+ {
+ // already logged by Platform
+ }
+ });
+ }
+
+ private void setWorkingCopy(ISourceFile workingCopy)
+ {
+ this.workingCopy = workingCopy;
+ }
+
+ private ISourceFile getWorkingCopy()
+ {
+ return workingCopy;
}
}
diff --git a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/texteditor/SourceFileDocumentProvider.java b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/texteditor/SourceFileDocumentProvider.java
index 60aa622..da3ac6b 100644
--- a/org.eclipse.handly.ui/src/org/eclipse/handly/ui/texteditor/SourceFileDocumentProvider.java
+++ b/org.eclipse.handly.ui/src/org/eclipse/handly/ui/texteditor/SourceFileDocumentProvider.java
@@ -10,208 +10,190 @@
*******************************************************************************/
package org.eclipse.handly.ui.texteditor;
-import java.text.MessageFormat;
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
-import org.eclipse.core.resources.IFile;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.handly.buffer.TextFileBuffer;
-import org.eclipse.handly.internal.ui.Activator;
-import org.eclipse.handly.model.IElement;
+import org.eclipse.handly.buffer.IBuffer;
+import org.eclipse.handly.model.Elements;
import org.eclipse.handly.model.ISourceFile;
-import org.eclipse.handly.model.impl.IWorkingCopyInfoFactory;
import org.eclipse.handly.model.impl.SourceFile;
-import org.eclipse.handly.ui.IInputElementProvider;
import org.eclipse.handly.ui.IWorkingCopyManager;
-import org.eclipse.ui.IEditorInput;
+import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.texteditor.IDocumentProvider;
/**
* Subclass of {@link TextFileDocumentProvider} specialized for
- * working copy management of {@link SourceFile}s.
- * <p>
- * Clients can use this class as it stands or subclass it
- * as circumstances warrant.
- * </p>
+ * working copy management of source files.
*/
-public class SourceFileDocumentProvider
+public abstract class SourceFileDocumentProvider
extends TextFileDocumentProvider
implements IWorkingCopyManager
{
- protected final IInputElementProvider inputElementProvider;
+ private static final ISourceFile[] NO_WORKING_COPIES = new ISourceFile[0];
/**
* Creates a new source file document provider with no parent.
- * <p>
- * The given input element provider is used in the default implementation
- * of {@link #getSourceFile(Object)}.
- * </p>
- *
- * @param inputElementProvider the input element provider
*/
- public SourceFileDocumentProvider(
- IInputElementProvider inputElementProvider)
+ public SourceFileDocumentProvider()
{
- this(null, inputElementProvider);
+ this(null);
}
/**
* Creates a new source file document provider with the given parent.
- * <p>
- * The given input element provider is used in the default implementation
- * of {@link #getSourceFile(Object)}.
- * </p>
*
* @param parent the parent document provider
- * @param inputElementProvider the input element provider
*/
- public SourceFileDocumentProvider(IDocumentProvider parent,
- IInputElementProvider inputElementProvider)
+ public SourceFileDocumentProvider(IDocumentProvider parent)
{
super(parent);
- this.inputElementProvider = inputElementProvider;
}
@Override
- public ISourceFile getWorkingCopy(IEditorInput editorInput)
+ public ISourceFile getWorkingCopy(Object element)
{
- FileInfo info = getFileInfo(editorInput);
- if (info instanceof SourceFileInfo)
+ SourceFileInfo info = (SourceFileInfo)getFileInfo(element);
+ if (info == null)
+ return null;
+ return info.workingCopy;
+ }
+
+ @Override
+ public ISourceFile getWorkingCopy(IDocument document)
+ {
+ Iterator<?> it = getFileInfosIterator();
+ while (it.hasNext())
{
- return ((SourceFileInfo)info).workingCopy;
+ SourceFileInfo info = (SourceFileInfo)it.next();
+ if (info.fTextFileBuffer.getDocument().equals(document))
+ return info.workingCopy;
}
return null;
}
@Override
+ public ISourceFile[] getWorkingCopies()
+ {
+ List<ISourceFile> result = new ArrayList<>();
+ Iterator<?> it = getFileInfosIterator();
+ while (it.hasNext())
+ {
+ SourceFileInfo info = (SourceFileInfo)it.next();
+ if (info.workingCopy != null)
+ result.add(info.workingCopy);
+ }
+ return result.toArray(NO_WORKING_COPIES);
+ }
+
+ /**
+ * Returns the source file for the given element.
+ *
+ * @param element the element
+ * @return the source file for the given element,
+ * or <code>null</code> if none
+ */
+ protected abstract ISourceFile getSourceFile(Object element);
+
+ @Override
protected FileInfo createEmptyFileInfo()
{
return new SourceFileInfo();
}
- /*
- * Subclasses may extend this method.
- */
@Override
protected FileInfo createFileInfo(Object element) throws CoreException
{
+ SourceFileInfo info = (SourceFileInfo)super.createFileInfo(element);
+ if (info == null)
+ return null;
boolean f = false;
- FileInfo info = super.createFileInfo(element);
try
{
- if (!(info instanceof SourceFileInfo))
- return null;
- SourceFile sourceFile = getSourceFile(element);
- if (sourceFile == null)
- return null;
- IFile file = sourceFile.hFile();
- if (file == null)
- return null;
- try (TextFileBuffer buffer = TextFileBuffer.forFile(file))
+ ISourceFile workingCopy = acquireWorkingCopy(element, info);
+ if (workingCopy != null)
{
- if (sourceFile.hBecomeWorkingCopy(buffer, // will addRef() the buffer
- getWorkingCopyInfoFactory(sourceFile, element, info),
- null) != null)
+ if (!Elements.isWorkingCopy(workingCopy))
+ throw new AssertionError();
+ try (IBuffer buffer = Elements.getBuffer(workingCopy))
{
- sourceFile.hDiscardWorkingCopy();
-
- throw new CoreException(Activator.createErrorStatus(
- MessageFormat.format(
- Messages.SourceFileDocumentProvider_Working_copy_already_exists__0,
- sourceFile), null));
+ if (buffer.getDocument() != info.fTextFileBuffer.getDocument())
+ {
+ releaseWorkingCopy(workingCopy, element, info);
+ throw new AssertionError();
+ }
}
+ info.workingCopy = workingCopy;
}
- ((SourceFileInfo)info).workingCopy = sourceFile;
f = true;
return info;
}
finally
{
- if (!f && info != null)
+ if (!f)
super.disposeFileInfo(element, info);
}
}
- /*
- * Subclasses may extend this method.
- */
@Override
protected void disposeFileInfo(Object element, FileInfo info)
{
- if (info instanceof SourceFileInfo)
+ try
{
- ((SourceFileInfo)info).workingCopy.hDiscardWorkingCopy();
+ ISourceFile workingCopy = ((SourceFileInfo)info).workingCopy;
+ if (workingCopy != null)
+ releaseWorkingCopy(workingCopy, element, info);
}
- super.disposeFileInfo(element, info);
+ finally
+ {
+ super.disposeFileInfo(element, info);
+ }
}
/**
- * Returns the source file corresponding to the given element.
- * <p>
- * The resulting source file will be switched to working copy mode
- * and associated with the file info object for the given element
- * in {@link #createFileInfo(Object)}.
- * </p>
- * <p>
- * If the given element is an <code>IEditorInput</code>, this implementation
- * uses the {@link IInputElementProvider} specified in the constructor to get
- * the input element for the editor input. If the provided input element is
- * a {@link SourceFile}, it is returned. Otherwise, <code>null</code> is
- * returned.
- * </p>
- * <p>
- * Subclasses may extend this method or override it completely.
- * </p>
+ * Attempts to acquire a working copy for the given element. A working copy
+ * acquired by this method <b>must</b> be released eventually via a call to
+ * {@link #releaseWorkingCopy(ISourceFile, Object, FileInfo)}.
*
- * @param element the element from which to compute the source file
- * @return the source file for the given element,
- * or <code>null</code> if none
+ * @param element the element
+ * @param info the element info
+ * @return an acquired working copy, or <code>null</code> if no working copy
+ * can be acquired for the given element
+ * @throws CoreException if working copy could not be acquired successfully
*/
- protected SourceFile getSourceFile(Object element)
+ protected ISourceFile acquireWorkingCopy(Object element, FileInfo info)
+ throws CoreException
{
- if (!(element instanceof IEditorInput))
- return null;
- IElement inputElement = inputElementProvider.getElement(
- (IEditorInput)element);
- if (!(inputElement instanceof SourceFile))
- return null;
- return (SourceFile)inputElement;
- }
-
- /**
- * Returns the working copy info factory for the given source file,
- * or <code>null</code> if a default factory is to be used.
- * <p>
- * This implementation returns <code>null</code>. Subclasses may override.
- * </p>
- *
- * @param sourceFile the source file corresponding to the given element
- * (never <code>null</code>)
- * @param element the element (never <code>null</code>)
- * @param fileInfo the file info for the given element
- * (never <code>null</code>)
- * @return the working copy info factory for the given source file,
- * or <code>null</code> if a default factory is to be used
- */
- protected IWorkingCopyInfoFactory getWorkingCopyInfoFactory(
- SourceFile sourceFile, Object element, FileInfo fileInfo)
- {
+ ISourceFile sourceFile = getSourceFile(element);
+ if (sourceFile instanceof SourceFile)
+ {
+ ((SourceFile)sourceFile).hBecomeWorkingCopy(EMPTY_CONTEXT, null);
+ return sourceFile;
+ }
return null;
}
/**
- * Bundle of all required information to allow working copy management.
- * <p>
- * Can be used as it stands or extended in subclasses as circumstances
- * warrant.
- * </p>
+ * Releases the working copy acquired via a call to {@link
+ * #acquireWorkingCopy(Object, FileInfo)}.
+ *
+ * @param workingCopy the working copy to release
+ * @param element the element
+ * @param info the element info
*/
+ protected void releaseWorkingCopy(ISourceFile workingCopy, Object element,
+ FileInfo info)
+ {
+ ((SourceFile)workingCopy).hReleaseWorkingCopy();
+ }
+
protected static class SourceFileInfo
extends FileInfo
{
- /**
- * A source file in working copy mode.
- */
- SourceFile workingCopy;
+ ISourceFile workingCopy;
}
}
diff --git a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextDocument.java b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextDocument.java
index 908cf61..4a596c4 100644
--- a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextDocument.java
+++ b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextDocument.java
@@ -138,16 +138,6 @@
return reconciledSnapshot.getWrappedSnapshot();
}
- public void addReconcilingListener(IReconcilingListener listener)
- {
- reconcilingListeners.add(listener);
- }
-
- public void removeReconcilingListener(IReconcilingListener listener)
- {
- reconcilingListeners.remove(listener);
- }
-
@Override
public boolean needsReconciling()
{
@@ -225,6 +215,16 @@
this.dirtyStateEditorSupport = dirtyStateEditorSupport;
}
+ void addReconcilingListener(IReconcilingListener listener)
+ {
+ reconcilingListeners.add(listener);
+ }
+
+ void removeReconcilingListener(IReconcilingListener listener)
+ {
+ reconcilingListeners.remove(listener);
+ }
+
private PendingChange getAndResetPendingChange()
{
final PendingChange result;
@@ -325,7 +325,7 @@
/**
* Document reconciling listener protocol.
*/
- public interface IReconcilingListener
+ interface IReconcilingListener
{
/**
* Called just after a reconciling operation has been performed. Informs
diff --git a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextEditorCallback.java b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextEditorCallback.java
index 6f9d6d1..e8b77f7 100644
--- a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextEditorCallback.java
+++ b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/HandlyXtextEditorCallback.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.handly.xtext.ui.editor;
+import static org.eclipse.handly.context.Contexts.of;
+import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.model.Elements.exists;
import static org.eclipse.handly.model.Elements.getSourceElementAt;
import static org.eclipse.handly.model.Elements.getSourceElementInfo;
@@ -18,7 +20,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
@@ -28,8 +29,10 @@
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.handly.buffer.IBuffer;
import org.eclipse.handly.internal.xtext.ui.Activator;
+import org.eclipse.handly.model.Elements;
import org.eclipse.handly.model.IElement;
import org.eclipse.handly.model.ISourceElement;
+import org.eclipse.handly.model.ISourceFile;
import org.eclipse.handly.model.impl.SourceFile;
import org.eclipse.handly.ui.IInputElementProvider;
import org.eclipse.handly.ui.texteditor.TextEditorBuffer;
@@ -58,9 +61,11 @@
/**
* Integrates Xtext editor with Handly working copy management facility.
- * Creates a working copy when a source file is opened in Xtext editor.
- * Discards the working copy when the editor is being disposed. Also,
- * sets the editor highlight range for the currently selected element.
+ * <p>
+ * Multiple Xtext editor instances may simultaneously be open for a given
+ * source file, each with its own underlying document, but only one of them
+ * (the most recently used one) is connected to the source file's working copy.
+ * </p>
* <p>
* Note that this class relies on a language-specific implementation of
* {@link IInputElementProvider} being available through injection.
@@ -98,16 +103,11 @@
{
private IInputElementProvider inputElementProvider;
- private Map<XtextEditor, WorkingCopyInfo> workingCopies =
- new HashMap<XtextEditor, WorkingCopyInfo>();
+ private Map<IEditorInput, WorkingCopyEditorInfo> workingCopyEditors =
+ new HashMap<>();
private Map<MultiPageEditorPart, Set<XtextEditor>> nestedEditors =
- new HashMap<MultiPageEditorPart, Set<XtextEditor>>();
- private Map<XtextEditor, IPartListener> partListeners =
- new HashMap<XtextEditor, IPartListener>();
- private Map<XtextEditor, ISelectionChangedListener> selectionListeners =
- new HashMap<XtextEditor, ISelectionChangedListener>();
- private Map<XtextEditor, HighlightRangeJob> highlightRangeJobs =
- new HashMap<XtextEditor, HighlightRangeJob>();
+ new HashMap<>();
+ private Map<XtextEditor, EditorInfo> editorInfoMap = new HashMap<>();
@Inject
public void setInputElementProvider(IInputElementProvider provider)
@@ -118,19 +118,16 @@
@Override
public void afterCreatePartControl(XtextEditor editor)
{
+ connect(editor);
registerContainer(editor);
- registerPartListener(editor);
- registerSelectionListener(editor);
}
@Override
public void beforeDispose(XtextEditor editor)
{
- deregisterPartListener(editor);
- deregisterSelectionListener(editor);
disconnectWorkingCopy(editor);
- disposeHighlightRangeJob(editor);
deregisterContainer(editor);
+ disconnect(editor);
}
@Override
@@ -149,34 +146,56 @@
protected void afterSelectionChange(XtextEditor editor,
ISelection selection)
{
- setHighlightRange(editor, selection);
+ if (selection != null)
+ setHighlightRange(editor, selection);
}
protected void setHighlightRange(XtextEditor editor, ISelection selection)
{
- if (selection == null)
- return;
- SourceFile sourceFile = getWorkingCopy(editor);
- if (sourceFile == null)
- return;
- scheduleHighlightRangeJob(editor, sourceFile, selection);
+ scheduleHighlightRangeJob(editor, selection);
}
- protected SourceFile getSourceFile(XtextEditor editor)
+ protected ISourceFile getSourceFile(XtextEditor editor)
{
IElement inputElement = inputElementProvider.getElement(
editor.getEditorInput());
- if (!(inputElement instanceof SourceFile))
+ if (!(inputElement instanceof ISourceFile))
return null;
- return (SourceFile)inputElement;
+ return (ISourceFile)inputElement;
}
- protected final SourceFile getWorkingCopy(XtextEditor editor)
+ protected final ISourceFile getWorkingCopy(XtextEditor editor)
{
- WorkingCopyInfo workingCopyInfo = workingCopies.get(editor);
- if (workingCopyInfo == null || !workingCopyInfo.success)
+ WorkingCopyEditorInfo info = workingCopyEditors.get(
+ editor.getEditorInput());
+ if (info == null || info.editor != editor)
return null;
- return workingCopyInfo.sourceFile;
+ return info.workingCopy;
+ }
+
+ protected ISourceFile acquireWorkingCopy(XtextEditor editor)
+ throws CoreException
+ {
+ ISourceFile sourceFile = getSourceFile(editor);
+ if (sourceFile instanceof SourceFile)
+ {
+ try (TextEditorBuffer buffer = new TextEditorBuffer(editor))
+ {
+ ((SourceFile)sourceFile).hBecomeWorkingCopy(with(of(
+ SourceFile.WORKING_COPY_BUFFER, buffer), of(
+ SourceFile.WORKING_COPY_INFO_FACTORY, (SourceFile sf,
+ IBuffer b) -> new XtextWorkingCopyInfo(sf, b))),
+ null);
+ return sourceFile;
+ }
+ }
+ return null;
+ }
+
+ protected void releaseWorkingCopy(XtextEditor editor,
+ ISourceFile workingCopy)
+ {
+ ((SourceFile)workingCopy).hReleaseWorkingCopy();
}
private boolean isActive(XtextEditor editor)
@@ -229,9 +248,9 @@
}
}
- private void registerPartListener(final XtextEditor editor)
+ private void connect(XtextEditor editor)
{
- IPartListener listener = new IPartListener()
+ IPartListener partListener = new IPartListener()
{
public void partActivated(IWorkbenchPart part)
{
@@ -252,137 +271,140 @@
// @formatter:on
};
editor.getSite().getWorkbenchWindow().getPartService().addPartListener(
- listener);
- partListeners.put(editor, listener);
- }
+ partListener);
- private void deregisterPartListener(XtextEditor editor)
- {
- IPartListener listener = partListeners.remove(editor);
- if (listener != null)
- {
- editor.getSite().getWorkbenchWindow().getPartService().removePartListener(
- listener);
- }
- }
-
- private void registerSelectionListener(final XtextEditor editor)
- {
- ISelectionChangedListener listener = new ISelectionChangedListener()
- {
- @Override
- public void selectionChanged(SelectionChangedEvent event)
+ ISelectionChangedListener selectionChangedListener =
+ new ISelectionChangedListener()
{
- afterSelectionChange(editor, event.getSelection());
- }
- };
+ @Override
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ afterSelectionChange(editor, event.getSelection());
+ }
+ };
ISelectionProvider selectionProvider = editor.getSelectionProvider();
if (selectionProvider instanceof IPostSelectionProvider)
((IPostSelectionProvider)selectionProvider).addPostSelectionChangedListener(
- listener);
+ selectionChangedListener);
else
- selectionProvider.addSelectionChangedListener(listener);
- selectionListeners.put(editor, listener);
+ selectionProvider.addSelectionChangedListener(
+ selectionChangedListener);
+
+ EditorInfo info = new EditorInfo();
+ info.partListener = partListener;
+ info.selectionChangedListener = selectionChangedListener;
+ info.highlightRangeJob = new HighlightRangeJob(editor);
+ editorInfoMap.put(editor, info);
}
- private void deregisterSelectionListener(XtextEditor editor)
+ private void disconnect(XtextEditor editor)
{
- ISelectionChangedListener listener = selectionListeners.remove(editor);
- if (listener != null)
- {
- ISelectionProvider selectionProvider =
- editor.getSelectionProvider();
- if (selectionProvider instanceof IPostSelectionProvider)
- ((IPostSelectionProvider)selectionProvider).removePostSelectionChangedListener(
- listener);
- else
- selectionProvider.removeSelectionChangedListener(listener);
- }
+ EditorInfo info = editorInfoMap.remove(editor);
+ if (info == null)
+ return;
+
+ editor.getSite().getWorkbenchWindow().getPartService().removePartListener(
+ info.partListener);
+
+ ISelectionProvider selectionProvider = editor.getSelectionProvider();
+ if (selectionProvider instanceof IPostSelectionProvider)
+ ((IPostSelectionProvider)selectionProvider).removePostSelectionChangedListener(
+ info.selectionChangedListener);
+ else
+ selectionProvider.removeSelectionChangedListener(
+ info.selectionChangedListener);
+
+ HighlightRangeJob highlightRangeJob = info.highlightRangeJob;
+ highlightRangeJob.cancel();
+ highlightRangeJob.setArgs(null);
}
private void connectWorkingCopy(XtextEditor editor)
{
- SourceFile sourceFile = getSourceFile(editor);
- if (sourceFile == null)
- return;
-
- XtextEditor workingCopyEditor = getWorkingCopyEditor(sourceFile);
+ XtextEditor workingCopyEditor = getWorkingCopyEditor(
+ editor.getEditorInput());
if (editor != workingCopyEditor)
{
if (workingCopyEditor != null)
- discardWorkingCopy(workingCopyEditor);
+ disconnectWorkingCopy0(workingCopyEditor);
- createWorkingCopy(sourceFile, editor);
+ connectWorkingCopy0(editor);
}
}
private void disconnectWorkingCopy(XtextEditor editor)
{
- SourceFile sourceFile = discardWorkingCopy(editor);
- if (sourceFile == null)
+ if (!disconnectWorkingCopy0(editor))
return;
XtextEditor mruClone = findMruClone(editor);
if (mruClone != null)
{
- createWorkingCopy(sourceFile, mruClone);
+ connectWorkingCopy0(mruClone);
}
}
- private void createWorkingCopy(SourceFile sourceFile, XtextEditor editor)
+ private void connectWorkingCopy0(XtextEditor editor)
{
- try (TextEditorBuffer buffer = new TextEditorBuffer(editor))
+ ISourceFile workingCopy = null;
+ try
{
- if (sourceFile.hBecomeWorkingCopy(buffer, // will addRef() the buffer
- (IBuffer b) -> new XtextWorkingCopyInfo(b), null) != null)
- {
- sourceFile.hDiscardWorkingCopy();
-
- throw new IllegalStateException("Already a working copy: " //$NON-NLS-1$
- + sourceFile);
- }
-
- workingCopies.put(editor, new WorkingCopyInfo(sourceFile, true));
-
- setHighlightRange(editor,
- editor.getSelectionProvider().getSelection());
+ workingCopy = acquireWorkingCopy(editor);
}
catch (CoreException e)
{
- workingCopies.put(editor, new WorkingCopyInfo(sourceFile, false));
-
- editor.resetHighlightRange();
-
if (!editor.getEditorInput().exists())
; // this is considered normal
else
Activator.log(e.getStatus());
}
+ if (workingCopy != null)
+ {
+ if (!Elements.isWorkingCopy(workingCopy))
+ throw new AssertionError();
+ try (IBuffer buffer = Elements.getBuffer(workingCopy))
+ {
+ if (buffer.getDocument() != editor.getDocument())
+ {
+ releaseWorkingCopy(editor, workingCopy);
+ throw new AssertionError();
+ }
+ }
+ catch (CoreException e)
+ {
+ Activator.log(e.getStatus());
+ }
+ }
+ workingCopyEditors.put(editor.getEditorInput(),
+ new WorkingCopyEditorInfo(editor, workingCopy));
+ if (workingCopy != null)
+ setHighlightRange(editor,
+ editor.getSelectionProvider().getSelection());
+ else
+ editor.resetHighlightRange();
}
- private SourceFile discardWorkingCopy(XtextEditor editor)
+ private boolean disconnectWorkingCopy0(XtextEditor editor)
{
- WorkingCopyInfo workingCopyInfo = workingCopies.remove(editor);
- if (workingCopyInfo == null)
- return null;
- if (workingCopyInfo.success)
+ WorkingCopyEditorInfo info = workingCopyEditors.get(
+ editor.getEditorInput());
+ if (info == null || info.editor != editor)
+ return false;
+ workingCopyEditors.remove(editor.getEditorInput());
+ if (info.workingCopy != null)
{
- workingCopyInfo.sourceFile.hDiscardWorkingCopy();
+ releaseWorkingCopy(editor, info.workingCopy);
editor.resetHighlightRange();
}
- return workingCopyInfo.sourceFile;
+ return true;
}
- private XtextEditor getWorkingCopyEditor(SourceFile sourceFile)
+ private XtextEditor getWorkingCopyEditor(IEditorInput editorInput)
{
- Set<Entry<XtextEditor, WorkingCopyInfo>> entrySet =
- workingCopies.entrySet();
- for (Entry<XtextEditor, WorkingCopyInfo> entry : entrySet)
- {
- if (entry.getValue().sourceFile.equals(sourceFile))
- return entry.getKey();
- }
- return null;
+ WorkingCopyEditorInfo info = workingCopyEditors.get(editorInput);
+ if (info == null)
+ return null;
+ return info.editor;
}
private XtextEditor findMruClone(XtextEditor editor)
@@ -419,43 +441,34 @@
}
private void scheduleHighlightRangeJob(XtextEditor editor,
- SourceFile sourceFile, ISelection selection)
+ ISelection selection)
{
- HighlightRangeJob highlightRangeJob = highlightRangeJobs.get(editor);
- if (highlightRangeJob == null)
- {
- highlightRangeJob = new HighlightRangeJob(editor);
- highlightRangeJobs.put(editor, highlightRangeJob);
- }
+ ISourceFile workingCopy = getWorkingCopy(editor);
+ if (workingCopy == null)
+ return;
+ EditorInfo info = editorInfoMap.get(editor);
+ if (info == null)
+ return;
+ HighlightRangeJob highlightRangeJob = info.highlightRangeJob;
highlightRangeJob.cancel();
- highlightRangeJob.setArgs(new HighlightArgs(sourceFile, selection));
+ highlightRangeJob.setArgs(new HighlightArgs(workingCopy, selection));
highlightRangeJob.schedule();
}
- private void disposeHighlightRangeJob(XtextEditor editor)
- {
- HighlightRangeJob highlightRangeJob = highlightRangeJobs.remove(editor);
- if (highlightRangeJob != null)
- {
- highlightRangeJob.cancel();
- highlightRangeJob.setArgs(null);
- }
- }
-
private class HighlightRangeJob
extends Job
{
private final XtextEditor editor;
private volatile HighlightArgs args;
- public HighlightRangeJob(XtextEditor editor)
+ HighlightRangeJob(XtextEditor editor)
{
super(""); //$NON-NLS-1$
setSystem(true);
this.editor = editor;
}
- public void setArgs(HighlightArgs args)
+ void setArgs(HighlightArgs args)
{
this.args = args;
}
@@ -466,7 +479,7 @@
HighlightArgs args = this.args;
if (args == null)
return Status.OK_STATUS;
- SourceFile sourceFile = args.sourceFile;
+ ISourceFile sourceFile = args.sourceFile;
ISelection selection = args.selection;
ISourceElement selectedElement = null;
if (selection instanceof ITextSelection)
@@ -524,8 +537,8 @@
return Status.OK_STATUS;
}
- private void setEditorHighlightRange(final HighlightArgs args,
- final int offset, final int length)
+ private void setEditorHighlightRange(HighlightArgs args, int offset,
+ int length)
{
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
{
@@ -537,7 +550,7 @@
});
}
- private void resetEditorHighlightRange(final HighlightArgs args)
+ private void resetEditorHighlightRange(HighlightArgs args)
{
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
{
@@ -559,45 +572,40 @@
private static class HighlightArgs
{
- public final SourceFile sourceFile;
- public final ISelection selection;
+ final ISourceFile sourceFile;
+ final ISelection selection;
/*
* @param sourceFile not null
* @param selection not null
*/
- public HighlightArgs(SourceFile sourceFile, ISelection selection)
+ HighlightArgs(ISourceFile sourceFile, ISelection selection)
{
this.sourceFile = sourceFile;
this.selection = selection;
}
}
- /*
- * Multiple XtextEditor instances may simultaneously be opened on a given
- * source file, each with its own underlying document, but only one of them
- * can be designated the working copy editor and connected to the source
- * file's working copy. This class is used for tracking the source file's
- * working copy editor. The success flag indicates whether the working copy
- * of the source file was created successfully by the working copy editor.
- * (A common reason for failure is that the editor input doesn't exist.)
- *
- * @see #getWorkingCopyEditor(SourceFile)
- */
- private static class WorkingCopyInfo
+ private static class EditorInfo
{
- public final SourceFile sourceFile;
- public final boolean success;
+ IPartListener partListener;
+ ISelectionChangedListener selectionChangedListener;
+ HighlightRangeJob highlightRangeJob;
+ }
+
+ private static class WorkingCopyEditorInfo
+ {
+ final XtextEditor editor;
+ final ISourceFile workingCopy;
/*
- * @param sourceFile not null
- * @param success whether sourceFile.becomeWorkingCopy was successful,
- * so sourceFile.discardWorkingCopy() is to be called
+ * @param editor not null
+ * @param workingCopy may be null
*/
- public WorkingCopyInfo(SourceFile sourceFile, boolean success)
+ WorkingCopyEditorInfo(XtextEditor editor, ISourceFile workingCopy)
{
- this.sourceFile = sourceFile;
- this.success = success;
+ this.editor = editor;
+ this.workingCopy = workingCopy;
}
}
}
diff --git a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/XtextWorkingCopyInfo.java b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/XtextWorkingCopyInfo.java
index 5919361..66c3f2f 100644
--- a/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/XtextWorkingCopyInfo.java
+++ b/org.eclipse.handly.xtext.ui/src/org/eclipse/handly/xtext/ui/editor/XtextWorkingCopyInfo.java
@@ -19,6 +19,7 @@
import org.eclipse.handly.context.Context;
import org.eclipse.handly.context.IContext;
import org.eclipse.handly.internal.xtext.ui.Activator;
+import org.eclipse.handly.model.impl.SourceFile;
import org.eclipse.handly.model.impl.WorkingCopyInfo;
import org.eclipse.handly.snapshot.NonExpiringSnapshot;
import org.eclipse.jface.text.IDocument;
@@ -46,20 +47,25 @@
context.bind(SOURCE_CONTENTS).to(snapshot.getContents());
context.bind(SOURCE_SNAPSHOT).to(snapshot.getWrappedSnapshot());
context.bind(RECONCILING_FORCED).to(forced);
- basicReconcile(context, monitor);
+ reconcile0(context, monitor);
}
};
/**
* Constructs a new working copy info and associates it with the given
- * buffer; the buffer is NOT <code>addRef</code>'ed.
+ * source file and buffer. Does not <code>addRef</code> the given buffer.
+ * <p>
+ * Clients should explicitly {@link #dispose} the working copy info
+ * after it is no longer needed.
+ * </p>
*
- * @param buffer the working copy buffer (not <code>null</code>,
+ * @param sourceFile the working copy's source file (not <code>null</code>)
+ * @param buffer the working copy's buffer (not <code>null</code>,
* must provide a <code>HandlyXtextDocument</code>)
*/
- public XtextWorkingCopyInfo(IBuffer buffer)
+ public XtextWorkingCopyInfo(SourceFile sourceFile, IBuffer buffer)
{
- super(buffer);
+ super(sourceFile, buffer);
IDocument document = buffer.getDocument();
if (!(document instanceof HandlyXtextDocument))
throw new IllegalArgumentException();
@@ -80,13 +86,13 @@
}
@Override
- protected boolean needsReconciling()
+ protected final boolean needsReconciling()
{
return getDocument().needsReconciling();
}
@Override
- protected void reconcile(IContext context, IProgressMonitor monitor)
+ protected final void reconcile(IContext context, IProgressMonitor monitor)
throws CoreException
{
try
@@ -105,7 +111,7 @@
}
}
- protected HandlyXtextDocument getDocument()
+ protected final HandlyXtextDocument getDocument()
{
return (HandlyXtextDocument)getBuffer().getDocument();
}
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/ElementDeltas.java b/org.eclipse.handly/src/org/eclipse/handly/model/ElementDeltas.java
index ef90293..0cd39db 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/ElementDeltas.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/ElementDeltas.java
@@ -42,8 +42,8 @@
* on delta A will return the handle for B. The delta for B will have status
* <code>ADDED</code>, with change flag <code>F_MOVED_FROM</code>, and {@link
* #getMovedFromElement} on delta B will return the handle for A. (Note,
- * the handle to A in this case represents an element that no longer exists).
- * Note that the move change flags only describe the changes to a single element,
+ * the handle to A in this case represents an element that no longer exists.)
+ * Move change flags only describe changes to a single element,
* they do not imply anything about the parent or children of the element.
* </p>
* <p>
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/Elements.java b/org.eclipse.handly/src/org/eclipse/handly/model/Elements.java
index b72dda1..2d6b618 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/Elements.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/Elements.java
@@ -582,6 +582,11 @@
* be shared by multiple clients, so the returned buffer may have unsaved
* changes if it has been modified by another client.
* <p>
+ * For working copies, the relationship between a source file and its buffer
+ * does not change over the lifetime of a working copy. Otherwise, a new
+ * buffer may be returned each time this method is invoked.
+ * </p>
+ * <p>
* The client takes (potentially shared) ownership of the returned buffer
* and is responsible for releasing it when finished. The buffer will be
* disposed only after it is released by every owner. The buffer must not
@@ -604,6 +609,11 @@
* be shared by multiple clients, so the returned buffer may have unsaved
* changes if it has been modified by another client.
* <p>
+ * For working copies, the relationship between a source file and its buffer
+ * does not change over the lifetime of a working copy. Otherwise, a new
+ * buffer may be returned each time this method is invoked.
+ * </p>
+ * <p>
* The client takes (potentially shared) ownership of the returned buffer
* and is responsible for releasing it when finished. The buffer will be
* disposed only after it is released by every owner. The buffer must not
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/ISourceFileExtension.java b/org.eclipse.handly/src/org/eclipse/handly/model/ISourceFileExtension.java
index 1c00693..173069e 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/ISourceFileExtension.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/ISourceFileExtension.java
@@ -90,6 +90,11 @@
* be shared by multiple clients, so the returned buffer may have unsaved
* changes if it has been modified by another client.
* <p>
+ * For working copies, the relationship between a source file and its buffer
+ * does not change over the lifetime of a working copy. Otherwise, a new
+ * buffer may be returned each time this method is invoked.
+ * </p>
+ * <p>
* The client takes (potentially shared) ownership of the returned buffer
* and is responsible for releasing it when finished. The buffer will be
* disposed only after it is released by every owner. The buffer must not
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/DefaultWorkingCopyInfo.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/DefaultWorkingCopyInfo.java
index 11c5f82..d7d3115 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/DefaultWorkingCopyInfo.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/DefaultWorkingCopyInfo.java
@@ -38,17 +38,22 @@
/**
* Constructs a new working copy info and associates it with the given
- * buffer; the buffer is NOT <code>addRef</code>'ed.
+ * source file and buffer. Does not <code>addRef</code> the given buffer.
+ * <p>
+ * Clients should explicitly {@link #dispose} the working copy info
+ * after it is no longer needed.
+ * </p>
*
- * @param buffer the working copy buffer (not <code>null</code>)
+ * @param sourceFile the working copy's source file (not <code>null</code>)
+ * @param buffer the working copy's buffer (not <code>null</code>)
*/
- public DefaultWorkingCopyInfo(IBuffer buffer)
+ public DefaultWorkingCopyInfo(SourceFile sourceFile, IBuffer buffer)
{
- super(buffer);
+ super(sourceFile, buffer);
}
@Override
- protected boolean needsReconciling()
+ protected final boolean needsReconciling()
{
return !getBuffer().getSnapshot().isEqualTo(reconciledSnapshot);
}
@@ -58,7 +63,7 @@
throws CoreException
{
if (context.containsKey(SOURCE_AST))
- throw new IllegalArgumentException(); // just to be safe that we don't pass SOURCE_AST to #basicReconcile accidentally
+ throw new IllegalArgumentException(); // just to be safe that we don't pass SOURCE_AST to #reconcile0 accidentally
synchronized (reconcilingLock)
{
@@ -67,8 +72,8 @@
{
NonExpiringSnapshot snapshot = new NonExpiringSnapshot(
getBuffer());
- basicReconcile(with(of(SOURCE_CONTENTS, snapshot.getContents()),
- of(SOURCE_SNAPSHOT, snapshot.getWrappedSnapshot()), of(
+ reconcile0(with(of(SOURCE_CONTENTS, snapshot.getContents()), of(
+ SOURCE_SNAPSHOT, snapshot.getWrappedSnapshot()), of(
RECONCILING_FORCED, !needsReconciling), context),
monitor);
reconciledSnapshot = snapshot.getWrappedSnapshot();
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/Element.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/Element.java
index 646f059..6fc1222 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/Element.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/Element.java
@@ -572,7 +572,7 @@
if (monitor.isCanceled())
throw new OperationCanceledException();
- hBuildStructure(context, new SubProgressMonitor(monitor, 1));
+ hBuildStructure0(context, new SubProgressMonitor(monitor, 1));
Object body = context.get(NEW_ELEMENTS).get(this);
if (body == null)
@@ -589,6 +589,12 @@
}
}
+ void hBuildStructure0(IContext context, IProgressMonitor monitor)
+ throws CoreException
+ {
+ hBuildStructure(context, monitor);
+ }
+
/**
* Closes this element, removing any previously registered handle/body
* relationships for it and its existing descendants.
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementManager.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementManager.java
index f0d6f7f..030689c 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementManager.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ElementManager.java
@@ -36,8 +36,7 @@
private ThreadLocal<Map<IElement, Object>> temporaryCache =
new ThreadLocal<>();
- private Map<ISourceFile, WorkingCopyInfo> workingCopyInfos =
- new HashMap<>();
+ private Map<SourceFile, WorkingCopyInfo> workingCopyInfos = new HashMap<>();
/**
* Constructs an element manager with the given body cache.
@@ -54,7 +53,15 @@
}
/**
- * Returns the source files that have corresponding working copy info.
+ * Returns the source files that are currently in working copy mode.
+ * Performs atomically.
+ * <p>
+ * Note that the result may immediately become stale if other threads can
+ * create or destroy working copies that are managed by this manager.
+ * </p>
+ *
+ * @return the source files that are currently in working copy mode
+ * (never <code>null</code>)
*/
public final synchronized ISourceFile[] getWorkingCopies()
{
@@ -62,42 +69,19 @@
}
/**
- * A handle/body relationship is going to be removed from the body cache
- * associated with this manager. Do any necessary cleanup.
+ * Attempts to close the given element. If the current state of the element
+ * does not permit closing (e.g., a working copy), it will stay open. Closing
+ * of an element usually involves closing its children and removal of its body
+ * from the cache.
* <p>
- * This method is called internally; it is not intended to be called by clients.
+ * This method is called internally; it is not intended to be invoked by clients.
* </p>
*
- * @param element the element whose body is going to be removed
- * (never <code>null</code>)
- * @param body the corresponding body that is going to be removed
- * (never <code>null</code>)
+ * @param element the element that needs closing (never <code>null</code>)
*/
- protected void removing(IElement element, Object body)
+ protected void close(IElement element)
{
- ((Element)element).hRemoving(body);
- }
-
- /**
- * Given a body, closes the children of the given element. If the current
- * state of a child element does not permit closing (e.g., a working copy),
- * it will stay open. Closing of an element usually involves closing its
- * children and removal of its body from the cache.
- * <p>
- * This method is called internally; it is not intended to be called by clients.
- * </p>
- *
- * @param element the element whose children need to be closed
- * (never <code>null</code>)
- * @param body the body corresponding to the given element
- * (never <code>null</code>)
- */
- protected void closeChildren(IElement element, Object body)
- {
- for (IElement child : (((Element)element).hChildren(body)))
- {
- ((Element)child).hClose(false);
- }
+ ((Element)element).hClose(false);
}
/**
@@ -113,7 +97,7 @@
* @return the corresponding body for the given element, or
* <code>null</code> if no body is registered for the element
*/
- synchronized Object get(IElement element)
+ synchronized Object get(Element element)
{
Map<IElement, Object> tempCache = temporaryCache.get();
if (tempCache != null)
@@ -139,7 +123,7 @@
* @return the corresponding body for the given element, or
* <code>null</code> if no body is registered for the element
*/
- synchronized Object peek(IElement element)
+ synchronized Object peek(Element element)
{
Map<IElement, Object> tempCache = temporaryCache.get();
if (tempCache != null)
@@ -160,16 +144,16 @@
* to be stored in the body cache (not <code>null</code>). At a minimum,
* it must contain a body for the given element
*/
- synchronized void put(IElement element, Map<IElement, Object> newElements)
+ synchronized void put(Element element, Map<IElement, Object> newElements)
{
// remove existing children as they are replaced with the new children contained in newElements
remove(element);
cache.putAll(newElements);
- if (element instanceof ISourceFile)
+ if (element instanceof SourceFile)
{
- WorkingCopyInfo info = workingCopyInfos.get((ISourceFile)element);
+ WorkingCopyInfo info = workingCopyInfos.get((SourceFile)element);
if (info != null && !info.created) // case of wc creation
info.created = true;
}
@@ -187,7 +171,7 @@
* @return the previous body for the given element, or <code>null</code>
* if the body cache did not previously contain a body for the element
*/
- synchronized Object putIfAbsent(IElement element,
+ synchronized Object putIfAbsent(Element element,
Map<IElement, Object> newElements)
{
Object existingBody = cache.peek(element);
@@ -204,16 +188,18 @@
* contained no body for the element. Performs atomically.
*
* @param element the element whose body is to be removed from the body cache
- * @see #removing(IElement, Object)
- * @see #closeChildren(IElement, Object)
+ * @see #close(IElement)
*/
- synchronized void remove(IElement element)
+ synchronized void remove(Element element)
{
Object body = cache.peek(element);
if (body != null)
{
- removing(element, body);
- closeChildren(element, body);
+ element.hRemoving(body);
+ for (IElement child : element.hChildren(body))
+ {
+ close(child);
+ }
cache.remove(element);
}
}
@@ -268,7 +254,7 @@
* Performs atomically.
* <p>
* Each successful call to this method must ultimately be followed
- * by exactly one call to <code>discardWorkingCopyInfo</code>.
+ * by exactly one call to <code>releaseWorkingCopyInfo</code>.
* </p>
*
* @param sourceFile the source file with which a working copy info
@@ -279,10 +265,10 @@
* @return the previous working copy info associated with the given
* source file, or <code>null</code> if there was no working copy info
* for the source file
- * @see #discardWorkingCopyInfo(ISourceFile)
+ * @see #releaseWorkingCopyInfo(SourceFile)
*/
- WorkingCopyInfo putWorkingCopyInfoIfAbsent(ISourceFile sourceFile,
- IBuffer buffer, IWorkingCopyInfoFactory factory)
+ WorkingCopyInfo putWorkingCopyInfoIfAbsent(SourceFile sourceFile,
+ IBuffer buffer, WorkingCopyInfo.Factory factory)
{
if (sourceFile == null)
throw new IllegalArgumentException();
@@ -291,11 +277,13 @@
final WorkingCopyInfo info;
if (factory == null)
- info = new DefaultWorkingCopyInfo(buffer);
+ info = new DefaultWorkingCopyInfo(sourceFile, buffer);
else
- info = factory.createWorkingCopyInfo(buffer);
+ info = factory.newWorkingCopyInfo(sourceFile, buffer);
if (info.refCount != 0)
throw new AssertionError();
+ if (info.getSourceFile() != sourceFile)
+ throw new AssertionError();
boolean disposeInfo = true;
boolean releaseBuffer = false;
try
@@ -340,15 +328,15 @@
* <p>
* Each successful call to this method that did not return
* <code>null</code> must ultimately be followed by exactly
- * one call to <code>discardWorkingCopyInfo</code>.
+ * one call to <code>releaseWorkingCopyInfo</code>.
* </p>
*
* @param sourceFile the source file whose working copy info is to be returned
* @return the working copy info for the given source file,
* or <code>null</code> if the source file has no working copy info
- * @see #discardWorkingCopyInfo(ISourceFile)
+ * @see #releaseWorkingCopyInfo(SourceFile)
*/
- synchronized WorkingCopyInfo getWorkingCopyInfo(ISourceFile sourceFile)
+ synchronized WorkingCopyInfo getWorkingCopyInfo(SourceFile sourceFile)
{
WorkingCopyInfo info = workingCopyInfos.get(sourceFile);
if (info != null)
@@ -364,7 +352,7 @@
* @return the working copy info for the given source file,
* or <code>null</code> if the source file has no working copy info
*/
- synchronized WorkingCopyInfo peekAtWorkingCopyInfo(ISourceFile sourceFile)
+ synchronized WorkingCopyInfo peekAtWorkingCopyInfo(SourceFile sourceFile)
{
return workingCopyInfos.get(sourceFile);
}
@@ -375,11 +363,11 @@
* working copy info and releases the working copy buffer. Has no effect if
* there was no working copy info for the source file. Performs atomically.
*
- * @param sourceFile the source file whose working copy info is to be discarded
+ * @param sourceFile the source file whose working copy info is to be released
* @return the working copy info for the given source file,
* or <code>null</code> if the source file had no working copy info
*/
- WorkingCopyInfo discardWorkingCopyInfo(ISourceFile sourceFile)
+ WorkingCopyInfo releaseWorkingCopyInfo(SourceFile sourceFile)
{
WorkingCopyInfo infoToDispose = null;
try
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/IBodyCache.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/IBodyCache.java
index cfcb3f3..cadd4b6 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/IBodyCache.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/IBodyCache.java
@@ -63,9 +63,9 @@
* @param elementBodies handle/body relationships to be stored in the cache
* (not <code>null</code>)
*/
- default void putAll(Map<IElement, Object> elementBodies)
+ default void putAll(Map<? extends IElement, Object> elementBodies)
{
- for (Map.Entry<IElement, Object> entry : elementBodies.entrySet())
+ for (Map.Entry<? extends IElement, Object> entry : elementBodies.entrySet())
{
put(entry.getKey(), entry.getValue());
}
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ISourceFileImpl.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ISourceFileImpl.java
index 9c25458..bbc67ee 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/ISourceFileImpl.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/ISourceFileImpl.java
@@ -89,6 +89,11 @@
* be shared by multiple clients, so the returned buffer may have unsaved
* changes if it has been modified by another client.
* <p>
+ * For working copies, the relationship between a source file and its buffer
+ * does not change over the lifetime of a working copy. Otherwise, a new
+ * buffer may be returned each time this method is invoked.
+ * </p>
+ * <p>
* The client takes (potentially shared) ownership of the returned buffer
* and is responsible for releasing it when finished. The buffer will be
* disposed only after it is released by every owner. The buffer must not
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/IWorkingCopyInfoFactory.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/IWorkingCopyInfoFactory.java
deleted file mode 100644
index a323dda..0000000
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/IWorkingCopyInfoFactory.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015, 2016 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.model.impl;
-
-import org.eclipse.handly.buffer.IBuffer;
-
-/**
- * A factory of working copy info.
- */
-public interface IWorkingCopyInfoFactory
-{
- /**
- * Returns a new working copy info associated with the given buffer;
- * the buffer is NOT <code>addRef</code>'ed. The created working copy
- * info must be explicitly disposed after it is no longer needed.
- *
- * @param buffer the buffer to be associated with the
- * created working copy info (not <code>null</code>)
- * @return the created working copy info (never <code>null</code>)
- */
- WorkingCopyInfo createWorkingCopyInfo(IBuffer buffer);
-}
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
index 693c8e4..037ec5a 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/SourceFile.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.handly.model.impl;
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import static org.eclipse.handly.context.Contexts.of;
import static org.eclipse.handly.context.Contexts.with;
import static org.eclipse.handly.model.IElementDeltaConstants.F_WORKING_COPY;
@@ -72,113 +73,111 @@
public final IBuffer hBuffer(IContext context, IProgressMonitor monitor)
throws CoreException
{
- WorkingCopyInfo info = hAcquireWorkingCopy();
- if (info == null)
+ if (monitor == null)
+ monitor = new NullProgressMonitor();
+ monitor.beginTask("", 100); //$NON-NLS-1$
+ try
{
- return hFileBuffer(context, monitor);
+ WorkingCopyInfo info = hAcquireExistingWorkingCopy(
+ new SubProgressMonitor(monitor, 10));
+ if (info == null)
+ {
+ return hFileBuffer(context, new SubProgressMonitor(monitor,
+ 90));
+ }
+ else
+ {
+ try
+ {
+ IBuffer buffer = info.getBuffer();
+ buffer.addRef();
+ return buffer;
+ }
+ finally
+ {
+ hReleaseWorkingCopy();
+ }
+ }
}
- else
+ finally
{
- try
- {
- IBuffer buffer = info.getBuffer();
- buffer.addRef();
- return buffer;
- }
- finally
- {
- hDiscardWorkingCopy();
- }
+ monitor.done();
}
}
/**
* If this source file is not already in working copy mode, switches it
- * into a working copy, associates it with the given buffer, and acquires
- * an independent ownership of the working copy and its buffer. Performs
- * atomically.
+ * into a working copy, associates it with a working copy buffer via a new
+ * working copy info, and acquires an independent ownership of the working
+ * copy (and, hence, of the working copy buffer). Performs atomically.
* <p>
- * Switching to working copy means that the source file's structure and
- * properties will no longer correspond to the underlying resource contents
- * and will no longer be updated by a resource delta processor. Instead,
- * those structure and properties can be explicitly {@link #hReconcile(
- * IContext, IProgressMonitor) reconciled} with the current contents of
- * the working copy buffer.
+ * In working copy mode the source file's structure and properties
+ * shall no longer correspond to the underlying resource contents
+ * and must no longer be updated by a resource delta processor.
+ * Instead, the source file's structure and properties can be explicitly
+ * {@link #hReconcile(IContext, IProgressMonitor) reconciled} with the
+ * current contents of the working copy buffer.
+ * </p>
+ * <p>
+ * This method supports the following options, which may be specified
+ * in the given context:
+ * </p>
+ * <ul>
+ * <li>
+ * {@link #WORKING_COPY_BUFFER} - Specifies the working copy buffer.
+ * If not set, a default buffer is associated with the working copy.
+ * </li>
+ * <li>
+ * {@link #WORKING_COPY_INFO_FACTORY} - Specifies the working copy info factory.
+ * If not set, a default factory is used for obtaining a new working copy info.
+ * </li>
+ * </ul>
* </p>
* <p>
* If the source file was already in working copy mode, this method acquires
* a new independent ownership of the working copy by incrementing an internal
* counter and returns the info associated with the working copy; the given
- * buffer is ignored.
+ * context is ignored. The returned info is owned by the working copy and
+ * must not be explicitly disposed by the client.
* </p>
* <p>
* Each successful call to this method must ultimately be followed
- * by exactly one call to <code>hDiscardWorkingCopy</code>.
+ * by exactly one call to <code>hReleaseWorkingCopy</code>.
* </p>
*
- * @param buffer the working copy buffer (not <code>null</code>)
+ * @param context the operation context (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired
* @return the working copy info previously associated with
* this source file, or <code>null</code> if there was no
* working copy info for this source file
- * @throws CoreException if the working copy cannot be created
+ * @throws CoreException if the working copy could not be created successfully
* @throws OperationCanceledException if this method is canceled
- * @see #hDiscardWorkingCopy()
+ * @see #hReleaseWorkingCopy()
+ * @see #hAcquireExistingWorkingCopy(IProgressMonitor)
*/
- public final WorkingCopyInfo hBecomeWorkingCopy(IBuffer buffer,
+ public final WorkingCopyInfo hBecomeWorkingCopy(IContext context,
IProgressMonitor monitor) throws CoreException
{
- return hBecomeWorkingCopy(buffer, null, monitor);
- }
-
- /**
- * If this source file is not already in working copy mode, switches it
- * into a working copy, associates it with the given buffer via a new
- * working copy info obtained from the given factory, and acquires an
- * independent ownership of the working copy and its buffer. Performs
- * atomically.
- * <p>
- * Switching to working copy means that the source file's structure and
- * properties will no longer correspond to the underlying resource contents
- * and will no longer be updated by a resource delta processor. Instead,
- * those structure and properties can be explicitly {@link #hReconcile(
- * IContext, IProgressMonitor) reconciled} with the current contents of
- * the working copy buffer.
- * </p>
- * <p>
- * If the source file was already in working copy mode, this method acquires
- * a new independent ownership of the working copy by incrementing an internal
- * counter and returns the info associated with the working copy; the given
- * buffer and factory are ignored.
- * </p>
- * <p>
- * Each successful call to this method must ultimately be followed
- * by exactly one call to <code>hDiscardWorkingCopy</code>.
- * </p>
- *
- * @param buffer the working copy buffer (not <code>null</code>)
- * @param factory the working copy info factory, or <code>null</code>
- * if a default factory is to be used
- * @param monitor a progress monitor, or <code>null</code>
- * if progress reporting is not desired
- * @return the working copy info previously associated with
- * this source file, or <code>null</code> if there was no
- * working copy info for this source file
- * @throws CoreException if the working copy cannot be created
- * @throws OperationCanceledException if this method is canceled
- * @see #hDiscardWorkingCopy()
- */
- public final WorkingCopyInfo hBecomeWorkingCopy(IBuffer buffer,
- IWorkingCopyInfoFactory factory, IProgressMonitor monitor)
- throws CoreException
- {
+ if (context == null)
+ throw new IllegalArgumentException();
+ IBuffer buffer = context.get(WORKING_COPY_BUFFER);
+ if (buffer == null)
+ {
+ try (IBuffer defaultBuffer = hFileBuffer(context, monitor))
+ {
+ return hBecomeWorkingCopy(with(of(WORKING_COPY_BUFFER,
+ defaultBuffer), context), monitor);
+ }
+ }
WorkingCopyProvider provider = new WorkingCopyProvider()
{
@Override
protected WorkingCopyInfo doAcquireWorkingCopy()
{
- return putWorkingCopyInfoIfAbsent(buffer, factory);
+ return hElementManager().putWorkingCopyInfoIfAbsent(
+ SourceFile.this, buffer, context.get(
+ WORKING_COPY_INFO_FACTORY));
}
@Override
@@ -195,42 +194,68 @@
boolean success = false;
try
{
- WorkingCopyInfo newInfo = hPeekAtWorkingCopyInfo();
- newInfo.workingCopy = this;
- newInfo.initTask.execute(monitor);
+ WorkingCopyInfo newInfo =
+ hElementManager().peekAtWorkingCopyInfo(this);
+ newInfo.initTask.execute(context, monitor);
success = true;
}
finally
{
if (!success)
- hDiscardWorkingCopy();
+ hReleaseWorkingCopy();
}
}
return oldInfo;
}
/**
+ * Specifies the working copy buffer.
+ * @see #hBecomeWorkingCopy(IContext, IProgressMonitor)
+ */
+ public static final Property<IBuffer> WORKING_COPY_BUFFER = Property.get(
+ SourceFile.class.getName() + ".workingCopyBuffer", IBuffer.class); //$NON-NLS-1$
+ /**
+ * Specifies the working copy info factory.
+ * @see #hBecomeWorkingCopy(IContext, IProgressMonitor)
+ */
+ public static final Property<WorkingCopyInfo.Factory> WORKING_COPY_INFO_FACTORY =
+ Property.get(SourceFile.class.getName() + ".workingCopyInfoFactory", //$NON-NLS-1$
+ WorkingCopyInfo.Factory.class);
+
+ /**
* If this source file is in working copy mode, acquires a new independent
* ownership of the working copy by incrementing an internal counter and
- * returns the info associated with the working copy. Returns <code>null</code>
- * if this source file is not a working copy. Performs atomically.
+ * returns the info associated with the working copy. The returned info is
+ * owned by the working copy and must not be explicitly disposed by the client.
+ * Returns <code>null</code> if this source file is not a working copy.
+ * Performs atomically.
* <p>
* Each successful call to this method that did not return <code>null</code>
- * must ultimately be followed by exactly one call to <code>hDiscardWorkingCopy</code>.
+ * must ultimately be followed by exactly one call to <code>hReleaseWorkingCopy</code>.
* </p>
*
* @return the working copy info for this source file,
* or <code>null</code> if this source file is not a working copy
- * @see #hDiscardWorkingCopy()
+ * @see #hReleaseWorkingCopy()
+ * @see #hBecomeWorkingCopy(IContext, IProgressMonitor)
*/
- public final WorkingCopyInfo hAcquireWorkingCopy()
+ public final WorkingCopyInfo hAcquireExistingWorkingCopy(
+ IProgressMonitor monitor)
{
WorkingCopyProvider provider = new WorkingCopyProvider()
{
@Override
protected WorkingCopyInfo doAcquireWorkingCopy()
{
- return getWorkingCopyInfo();
+ return hElementManager().getWorkingCopyInfo(SourceFile.this);
+ }
+
+ @Override
+ protected boolean isCanceled()
+ {
+ if (monitor == null)
+ return false;
+ return monitor.isCanceled();
}
};
return provider.acquireWorkingCopy();
@@ -238,23 +263,22 @@
/**
* Relinquishes an independent ownership of the working copy by decrementing
- * an internal counter. If there are no remaining independent owners of the
- * working copy, switches this source file from working copy mode back to
- * its original mode and releases the working copy buffer. Performs
- * atomically.
+ * an internal counter. If there are no remaining owners of the working copy,
+ * switches this source file from working copy mode back to its original mode
+ * and releases the working copy buffer. Performs atomically.
* <p>
* Each independent ownership of the working copy must ultimately end
- * with exactly one call to this method. If a client is not an independent
- * owner of the working copy, it must not call this method.
+ * with exactly one call to this method. Clients that do not own the
+ * working copy must not call this method.
* </p>
*
* @return <code>true</code> if this source file was switched from
* working copy mode back to its original mode, <code>false</code>
* otherwise
*/
- public final boolean hDiscardWorkingCopy()
+ public final boolean hReleaseWorkingCopy()
{
- WorkingCopyInfo info = hElementManager().discardWorkingCopyInfo(this);
+ WorkingCopyInfo info = hElementManager().releaseWorkingCopyInfo(this);
if (info == null)
throw new IllegalStateException("Not a working copy: " + hToString( //$NON-NLS-1$
of(FORMAT_STYLE, MEDIUM)));
@@ -266,24 +290,45 @@
return false;
}
+ /**
+ * If this source file is in working copy mode, returns the working copy info
+ * without acquiring an independent ownership of the working copy. The
+ * returned info is owned by the working copy and must not be explicitly
+ * disposed by the client. Returns <code>null</code> if this source file
+ * is not a working copy.
+ * <p>
+ * Note that if this method is invoked by a client that does not own the
+ * working copy, the returned info may get disposed from another thread.
+ * </p>
+ *
+ * @return the working copy info for this source file,
+ * or <code>null</code> if this source file is not a working copy
+ * @see #hAcquireExistingWorkingCopy(IProgressMonitor)
+ * @see #hBecomeWorkingCopy(IContext, IProgressMonitor)
+ */
+ public final WorkingCopyInfo hWorkingCopyInfo()
+ {
+ WorkingCopyInfo info = hElementManager().peekAtWorkingCopyInfo(this);
+ if (info == null)
+ return null;
+ if (info.created)
+ return info;
+ // special case: wc creation is in progress on the current thread
+ if (this.equals(CURRENTLY_RECONCILED.get()))
+ return info;
+ return null;
+ }
+
@Override
public final boolean hIsWorkingCopy()
{
- WorkingCopyInfo info = hPeekAtWorkingCopyInfo();
- if (info == null)
- return false;
- if (info.created)
- return true;
- // special case: wc creation is in progress on the current thread
- if (this.equals(CURRENTLY_RECONCILED.get()))
- return true;
- return false;
+ return hWorkingCopyInfo() != null;
}
@Override
public final boolean hNeedsReconciling()
{
- WorkingCopyInfo info = hAcquireWorkingCopy();
+ WorkingCopyInfo info = hAcquireExistingWorkingCopy(null);
if (info == null)
return false;
else
@@ -294,7 +339,7 @@
}
finally
{
- hDiscardWorkingCopy();
+ hReleaseWorkingCopy();
}
}
}
@@ -303,22 +348,31 @@
public final void hReconcile(IContext context, IProgressMonitor monitor)
throws CoreException
{
- WorkingCopyInfo info = hAcquireWorkingCopy();
- if (info == null)
- return; // not a working copy
- else
+ if (monitor == null)
+ monitor = new NullProgressMonitor();
+ monitor.beginTask("", 100); //$NON-NLS-1$
+ try
{
- try
+ WorkingCopyInfo info = hAcquireExistingWorkingCopy(
+ new SubProgressMonitor(monitor, 10));
+ if (info == null)
+ return; // not a working copy
+ else
{
- if (monitor == null)
- monitor = new NullProgressMonitor();
-
- info.reconcile(context, monitor);
+ try
+ {
+ info.reconcile(context, new SubProgressMonitor(monitor,
+ 90));
+ }
+ finally
+ {
+ hReleaseWorkingCopy();
+ }
}
- finally
- {
- hDiscardWorkingCopy();
- }
+ }
+ finally
+ {
+ monitor.done();
}
}
@@ -345,17 +399,19 @@
}
/**
- * If this source file is in working copy mode, returns the working copy info
- * without acquiring an independent ownership of the working copy. Returns
- * <code>null</code> if this source file is not a working copy.
+ * Returns a context to be associated with a new working copy of this
+ * source file. The given operation context is propagated from the
+ * {@link #hBecomeWorkingCopy} method.
+ * <p>
+ * This implementation returns an empty context. Subclasses may override.
+ * </p>
*
- * @return the working copy info for this source file,
- * or <code>null</code> if this source file is not a working copy
- * @see #hAcquireWorkingCopy()
+ * @param context the operation context (never <code>null</code>)
+ * @return the working copy context (not <code>null</code>)
*/
- protected final WorkingCopyInfo hPeekAtWorkingCopyInfo()
+ protected IContext hWorkingCopyContext(IContext context)
{
- return hElementManager().peekAtWorkingCopyInfo(this);
+ return EMPTY_CONTEXT;
}
/**
@@ -404,65 +460,51 @@
}
@Override
- protected final void hBuildStructure(IContext context,
- IProgressMonitor monitor) throws CoreException
+ void hBuildStructure0(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
- int ticks = 2;
- monitor.beginTask("", ticks); //$NON-NLS-1$
- try
+ if (!context.containsKey(SOURCE_CONTENTS) && !context.containsKey(
+ SOURCE_AST))
{
- Object ast = context.get(SOURCE_AST);
-
- if (ast == null) // not a working copy
+ if (hIsWorkingCopy())
+ throw new AssertionError();
+ // NOTE: source files that are not working copies must reflect
+ // the structure of the underlying file rather than the buffer
+ NonExpiringSnapshot snapshot;
+ try (ISnapshotProvider provider = hFileSnapshotProvider())
{
- // NOTE: AST is created from the underlying file contents,
- // not from the buffer contents, since source files that are not
- // working copies must reflect the structure of the underlying file
- NonExpiringSnapshot snapshot;
- try (
- ISnapshotProvider provider = hFileSnapshotProvider(context))
+ try
{
- try
- {
- snapshot = new NonExpiringSnapshot(provider);
- }
- catch (IllegalStateException e)
- {
- Throwable cause = e.getCause();
- if (cause instanceof CoreException)
- throw (CoreException)cause;
- throw new CoreException(Activator.createErrorStatus(
- e.getMessage(), e));
- }
+ snapshot = new NonExpiringSnapshot(provider);
}
- ast = hCreateAst(snapshot.getContents(), context,
- new SubProgressMonitor(monitor, 1));
- context = with(of(SOURCE_CONTENTS, snapshot.getContents()), of(
- SOURCE_SNAPSHOT, snapshot.getWrappedSnapshot()), context);
- --ticks;
+ catch (IllegalStateException e)
+ {
+ Throwable cause = e.getCause();
+ if (cause instanceof CoreException)
+ throw (CoreException)cause;
+ throw new CoreException(Activator.createErrorStatus(
+ e.getMessage(), e));
+ }
}
-
- hBuildStructure(ast, context, new SubProgressMonitor(monitor,
- ticks));
-
- Map<IElement, Object> newElements = context.get(NEW_ELEMENTS);
- Object body = newElements.get(this);
- if (body instanceof SourceElementBody)
- {
- SourceElementBody thisBody = (SourceElementBody)body;
-
- String source = context.get(SOURCE_CONTENTS);
- if (source != null)
- thisBody.setFullRange(new TextRange(0, source.length()));
-
- ISnapshot snapshot = context.get(SOURCE_SNAPSHOT);
- if (snapshot != null)
- setSnapshot(thisBody, snapshot, newElements);
- }
+ context = with(of(SOURCE_CONTENTS, snapshot.getContents()), of(
+ SOURCE_SNAPSHOT, snapshot.getWrappedSnapshot()), context);
}
- finally
+
+ super.hBuildStructure0(context, monitor);
+
+ Map<IElement, Object> newElements = context.get(NEW_ELEMENTS);
+ Object body = newElements.get(this);
+ if (body instanceof SourceElementBody)
{
- monitor.done();
+ SourceElementBody thisBody = (SourceElementBody)body;
+
+ String source = context.get(SOURCE_CONTENTS);
+ if (source != null)
+ thisBody.setFullRange(new TextRange(0, source.length()));
+
+ ISnapshot snapshot = context.get(SOURCE_SNAPSHOT);
+ if (snapshot != null)
+ setSnapshot(thisBody, snapshot, newElements);
}
}
@@ -483,13 +525,11 @@
* be accessed by clients that don't own it.
* </p>
*
- * @param context the operation context (never <code>null</code>)
* @return a snapshot provider for the underlying file's stored contents
* (not <code>null</code>)
* @see ISnapshotProvider
*/
- protected abstract ISnapshotProvider hFileSnapshotProvider(
- IContext context);
+ protected abstract ISnapshotProvider hFileSnapshotProvider();
/**
* Returns the buffer opened for the underlying file of this source file.
@@ -518,8 +558,7 @@
* @return the buffer opened for the underlying file of this source file,
* or <code>null</code> if <code>CREATE_BUFFER == false</code> and
* there is currently no buffer opened for that file
- * @throws CoreException if the underlying file does not exist
- * or its contents cannot be accessed
+ * @throws CoreException if the buffer could not be opened successfully
* @throws OperationCanceledException if this method is canceled
* @see IBuffer
*/
@@ -527,31 +566,28 @@
IProgressMonitor monitor) throws CoreException;
/**
- * Returns a new AST created from the given source string. Unless otherwise
- * indicated by options specified in the given context, the AST may contain
- * just enough information for computing the structure and properties of
- * this element and each of its descendant elements.
- *
- * @param source the source string to parse (not <code>null</code>)
- * @param context the operation context (not <code>null</code>)
- * @param monitor a progress monitor (not <code>null</code>)
- * @return the AST created from the given source string (never <code>null</code>)
- * @throws CoreException if the AST could not be created
- * @throws OperationCanceledException if this method is canceled
- */
- protected abstract Object hCreateAst(String source, IContext context,
- IProgressMonitor monitor) throws CoreException;
-
- /**
* Creates and initializes bodies for this element and for each
- * of its descendant elements using information in the given AST.
- * Uses the {@link #NEW_ELEMENTS} map in the given context to associate
- * the created bodies with their respective elements.
+ * of its descendant elements according to options specified in the
+ * given context. Uses the {@link #NEW_ELEMENTS} map in the given context
+ * to associate the created bodies with their respective elements.
* <p>
- * The AST is safe to read in the dynamic context of this method call,
- * but must not be modified. In general, implementations should not keep
- * references to any part of the AST or the context outside the dynamic
- * scope of the invocation of this method.
+ * The following context options influence how the structure is built and,
+ * if simultaneously present, must be mutually consistent:
+ * </p>
+ * <ul>
+ * <li>
+ * {@link #SOURCE_AST} - Specifies the AST to use when building the structure.
+ * The AST is safe to read in the dynamic context of this method call, but
+ * must not be modified.
+ * </li>
+ * <li>
+ * {@link #SOURCE_CONTENTS} - Specifies the source string to use when
+ * building the structure.
+ * </li>
+ * </ul>
+ * <p>
+ * At least one of <code>SOURCE_AST</code> or <code>SOURCE_CONTENTS</code>
+ * will have a non-null value in the given context.
* </p>
* <p>
* The given context may provide additional data that this method can use,
@@ -559,34 +595,36 @@
* </p>
* <ul>
* <li>
- * {@link #SOURCE_CONTENTS} - Specifies the source string from which the
- * given AST was created.
- * </li>
- * <li>
- * {@link #SOURCE_SNAPSHOT} - Specifies the source snapshot from which the
- * given AST was created. The snapshot may expire. Implementations may
- * keep references to the snapshot outside the dynamic scope of the
- * invocation of this method.
+ * {@link #SOURCE_SNAPSHOT} - Specifies the source snapshot from which
+ * <code>SOURCE_AST</code> was created or <code>SOURCE_CONTENTS</code>
+ * was obtained. The snapshot may expire.
* </li>
* </ul>
*
- * @param ast the AST (never <code>null</code>)
* @param context the operation context (never <code>null</code>)
* @param monitor a progress monitor (never <code>null</code>)
+ * @throws CoreException if this method fails
* @throws OperationCanceledException if this method is canceled
*/
- protected abstract void hBuildStructure(Object ast, IContext context,
- IProgressMonitor monitor);
+ @Override
+ protected abstract void hBuildStructure(IContext context,
+ IProgressMonitor monitor) throws CoreException;
/**
+ * Specifies the source AST.
+ * @see #hBuildStructure(IContext, IProgressMonitor)
+ */
+ protected static final Property<Object> SOURCE_AST = Property.get(
+ SourceFile.class.getName() + ".sourceAst", Object.class); //$NON-NLS-1$
+ /**
* Specifies the source string.
- * @see #hBuildStructure(Object, IContext, IProgressMonitor)
+ * @see #hBuildStructure(IContext, IProgressMonitor)
*/
protected static final Property<String> SOURCE_CONTENTS = Property.get(
SourceFile.class.getName() + ".sourceContents", String.class); //$NON-NLS-1$
/**
* Specifies the source snapshot.
- * @see #hBuildStructure(Object, IContext, IProgressMonitor)
+ * @see #hBuildStructure(IContext, IProgressMonitor)
*/
protected static final Property<ISnapshot> SOURCE_SNAPSHOT = Property.get(
SourceFile.class.getName() + ".sourceSnapshot", ISnapshot.class); //$NON-NLS-1$
@@ -631,22 +669,10 @@
}
}
- private WorkingCopyInfo putWorkingCopyInfoIfAbsent(IBuffer buffer,
- IWorkingCopyInfoFactory factory)
- {
- return hElementManager().putWorkingCopyInfoIfAbsent(this, buffer,
- factory);
- }
-
- private WorkingCopyInfo getWorkingCopyInfo()
- {
- return hElementManager().getWorkingCopyInfo(this);
- }
-
/**
* Indicates whether the structure should be rebuilt when reconciling
* is forced.
- * @see ReconcileOperation#reconcile(Object, IContext, IProgressMonitor)
+ * @see ReconcileOperation#reconcile(IContext, IProgressMonitor)
*/
protected static final Property<Boolean> REBUILD_STRUCTURE_IF_FORCED =
Property.get(SourceFile.class.getName() + ".rebuildStructureIfForced", //$NON-NLS-1$
@@ -654,16 +680,12 @@
/**
* Indicates whether reconciling was forced, i.e. the working copy buffer
* has not been modified since the last time it was reconciled.
- * @see ReconcileOperation#reconcile(Object, IContext, IProgressMonitor)
+ * @see ReconcileOperation#reconcile(IContext, IProgressMonitor)
*/
static final Property<Boolean> RECONCILING_FORCED = Property.get(
SourceFile.class.getName() + ".reconcilingForced", //$NON-NLS-1$
Boolean.class).withDefault(false);
- private static final Property<Object> SOURCE_AST = Property.get(
- SourceFile.class.getName() + ".sourceAst", //$NON-NLS-1$
- Object.class); // the AST to use when building the source file structure
-
private static final ThreadLocal<SourceFile> CURRENTLY_RECONCILED =
new ThreadLocal<SourceFile>(); // the source file being reconciled
@@ -683,40 +705,48 @@
protected class ReconcileOperation
{
/**
- * Reconciles this working copy according to the given AST and
- * additional data provided in the context.
- * <p>
- * The AST is safe to read in the dynamic context of this method call,
- * but must not be modified. In general, implementations should not keep
- * references to any part of the AST or the context outside the dynamic
- * scope of the invocation of this method.
- * </p>
+ * Reconciles this working copy according to options specified
+ * in the given context.
* <p>
* The following context options can influence whether the structure
* of the working copy gets rebuilt:
* </p>
* <ul>
* <li>
- * {@link SourceFile#REBUILD_STRUCTURE_IF_FORCED REBUILD_STRUCTURE_IF_FORCED} -
- * Indicates whether the structure should be rebuilt even if reconciling
- * was forced, i.e. the working copy buffer has not been modified since
- * the last time it was reconciled.
+ * {@link #REBUILD_STRUCTURE_IF_FORCED} - Indicates whether the structure
+ * should be rebuilt even if reconciling was forced, i.e. the working copy
+ * buffer has not been modified since the last time it was reconciled.
* </li>
* </ul>
* <p>
+ * The following context options influence rebuilding of the structure
+ * of the working copy and, if simultaneously present, must be mutually
+ * consistent:
+ * </p>
+ * <ul>
+ * <li>
+ * {@link #SOURCE_AST} - Specifies the AST to use when building the
+ * structure. The AST is safe to read in the dynamic context of this
+ * method call, but must not be modified.
+ * </li>
+ * <li>
+ * {@link #SOURCE_CONTENTS} - Specifies the source string to use when
+ * building the structure.
+ * </li>
+ * </ul>
+ * <p>
+ * At least one of <code>SOURCE_AST</code> or <code>SOURCE_CONTENTS</code>
+ * must have a non-null value in the given context.
+ * </p>
+ * <p>
* The given context may provide additional data that this method can use,
* including the following:
* </p>
* <ul>
* <li>
- * {@link #SOURCE_CONTENTS} - Specifies the source string from which the
- * given AST was created.
- * </li>
- * <li>
* {@link #SOURCE_SNAPSHOT} - Specifies the source snapshot from which
- * the given AST was created. The snapshot may expire. Implementations
- * may keep references to the snapshot outside the dynamic scope of the
- * invocation of this method.
+ * <code>SOURCE_AST</code> was created or <code>SOURCE_CONTENTS</code>
+ * was obtained. The snapshot may expire.
* </li>
* </ul>
* <p>
@@ -724,21 +754,22 @@
* implementation.
* </p>
*
- * @param ast the working copy AST (not <code>null</code>)
* @param context the operation context (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired
* @throws CoreException if the working copy cannot be reconciled
* @throws OperationCanceledException if this method is canceled
*/
- protected void reconcile(Object ast, IContext context,
- IProgressMonitor monitor) throws CoreException
+ protected void reconcile(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
- if (ast == null)
+ if (context.get(SOURCE_AST) == null && context.get(
+ SOURCE_CONTENTS) == null)
+ {
throw new IllegalArgumentException();
- if (context == null)
- throw new IllegalArgumentException();
- WorkingCopyInfo info = hPeekAtWorkingCopyInfo();
+ }
+ WorkingCopyInfo info = hElementManager().peekAtWorkingCopyInfo(
+ SourceFile.this);
boolean create = !info.created; // case of wc creation
if (create || !context.getOrDefault(RECONCILING_FORCED)
|| context.getOrDefault(REBUILD_STRUCTURE_IF_FORCED))
@@ -748,8 +779,7 @@
CURRENTLY_RECONCILED.set(SourceFile.this);
try
{
- hOpen(with(of(SOURCE_AST, ast), of(FORCE_OPEN, true),
- context), monitor);
+ hOpen(with(of(FORCE_OPEN, true), context), monitor);
}
finally
{
@@ -777,8 +807,8 @@
extends ReconcileOperation
{
@Override
- protected void reconcile(Object ast, IContext context,
- IProgressMonitor monitor) throws CoreException
+ protected void reconcile(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
ElementDelta.Factory deltaFactory = hModel().getModelContext().get(
ElementDelta.Factory.class);
@@ -788,7 +818,7 @@
ElementDifferencer differ = new ElementDifferencer(
new ElementDelta.Builder(rootDelta));
- doReconcile(ast, context, monitor);
+ doReconcile(context, monitor);
differ.buildDelta();
if (!differ.isEmptyDelta())
@@ -802,24 +832,23 @@
}
/**
- * This implementation calls {@link ReconcileOperation#reconcile(Object,
+ * This implementation calls {@link ReconcileOperation#reconcile(
* IContext, IProgressMonitor) super.reconcile(..)}.
* <p>
* Subclasses may override this method, but must call its <b>super</b>
* implementation.
* </p>
*
- * @param ast the working copy AST (not <code>null</code>)
* @param context the operation context (not <code>null</code>)
* @param monitor a progress monitor, or <code>null</code>
* if progress reporting is not desired
* @throws CoreException if the working copy cannot be reconciled
* @throws OperationCanceledException if this method is canceled
*/
- protected void doReconcile(Object ast, IContext context,
- IProgressMonitor monitor) throws CoreException
+ protected void doReconcile(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
- super.reconcile(ast, context, monitor);
+ super.reconcile(context, monitor);
}
}
@@ -842,7 +871,7 @@
finally
{
if (!success)
- hDiscardWorkingCopy();
+ hReleaseWorkingCopy();
}
if (success)
return info;
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkingCopyInfo.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkingCopyInfo.java
index ea0678f..3ec2b1b 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkingCopyInfo.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkingCopyInfo.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.handly.model.impl;
+import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import static org.eclipse.handly.context.Contexts.of;
import static org.eclipse.handly.model.Elements.FORCE_RECONCILING;
@@ -29,37 +30,58 @@
import org.eclipse.handly.util.Property;
/**
- * Holds information related to a working copy.
+ * Holds information related to a working copy of a {@link SourceFile}.
* <p>
- * Concrete implementations of this abstract class are expected to be safe
+ * Concrete subclasses of this abstract class are expected to be safe
* for use by multiple threads.
* </p>
+ *
+ * @noextend This class is not intended to be directly extended by clients.
+ * However, clients may extend concrete subclasses of this class.
*/
public abstract class WorkingCopyInfo
{
+ private final SourceFile sourceFile;
private final IBuffer buffer;
+ private IContext context = EMPTY_CONTEXT;
final InitTask initTask = new InitTask();
- SourceFile workingCopy;
volatile boolean created; // whether wc was created (from the model POV)
int refCount;
/**
* Constructs a new working copy info and associates it with the given
- * buffer; the buffer is NOT <code>addRef</code>'ed.
+ * source file and buffer. Does not <code>addRef</code> the given buffer.
+ * <p>
+ * Clients should explicitly {@link #dispose} the working copy info
+ * after it is no longer needed.
+ * </p>
*
- * @param buffer the working copy buffer (not <code>null</code>)
+ * @param sourceFile the working copy's source file (not <code>null</code>)
+ * @param buffer the working copy's buffer (not <code>null</code>)
*/
- public WorkingCopyInfo(IBuffer buffer)
+ public WorkingCopyInfo(SourceFile sourceFile, IBuffer buffer)
{
+ if ((this.sourceFile = sourceFile) == null)
+ throw new IllegalArgumentException();
if ((this.buffer = buffer) == null)
throw new IllegalArgumentException();
}
/**
- * Returns the buffer associated with this working copy info;
- * does NOT <code>addRef</code> the buffer.
+ * Returns the source file associated with this working copy info.
*
- * @return the working copy buffer (never <code>null</code>)
+ * @return the working copy's source file (never <code>null</code>)
+ */
+ public final SourceFile getSourceFile()
+ {
+ return sourceFile;
+ }
+
+ /**
+ * Returns the buffer associated with this working copy info.
+ * Does not <code>addRef</code> the buffer.
+ *
+ * @return the working copy's buffer (never <code>null</code>)
*/
public final IBuffer getBuffer()
{
@@ -67,6 +89,16 @@
}
/**
+ * Returns the context associated with this working copy info.
+ *
+ * @return the working copy's context (never <code>null</code>)
+ */
+ public IContext getContext()
+ {
+ return context;
+ }
+
+ /**
* Disposes of this working copy info. Does nothing if the working copy info
* is already disposed.
*
@@ -123,6 +155,7 @@
*
* @return <code>true</code> if the working copy needs reconciling,
* <code>false</code> otherwise
+ * @noreference This method is for internal use only.
*/
protected abstract boolean needsReconciling();
@@ -141,9 +174,9 @@
* </li>
* </ul>
* <p>
- * An implementation of this method is supposed to call {@link
- * #basicReconcile} with an appropriately augmented context while
- * providing the necessary synchronization guarantees.
+ * An implementation of this method is supposed to call {@link #reconcile0}
+ * with an appropriately augmented context while providing the necessary
+ * synchronization guarantees.
* </p>
*
* @param context the operation context (not <code>null</code>)
@@ -151,6 +184,7 @@
* if progress reporting is not desired
* @throws CoreException if the working copy cannot be reconciled
* @throws OperationCanceledException if this method is canceled
+ * @noreference This method is for internal use only.
*/
protected abstract void reconcile(IContext context,
IProgressMonitor monitor) throws CoreException;
@@ -212,39 +246,39 @@
* if progress reporting is not desired
* @throws CoreException if the working copy cannot be reconciled
* @throws OperationCanceledException if this method is canceled
+ * @noreference This method is for internal use only.
*/
- protected final void basicReconcile(IContext context,
- IProgressMonitor monitor) throws CoreException
+ protected final void reconcile0(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
- Object ast = context.get(SOURCE_AST);
- if (ast == null)
- ast = workingCopy.hCreateAst(context.get(SOURCE_CONTENTS), context,
- monitor);
- workingCopy.hReconcileOperation().reconcile(ast, context, monitor);
+ sourceFile.hReconcileOperation().reconcile(context, monitor);
}
/**
* Specifies the source AST for reconciling.
- * @see #basicReconcile(IContext, IProgressMonitor)
+ * @see #reconcile0(IContext, IProgressMonitor)
+ * @noreference This property is for internal use only.
*/
- protected static final Property<Object> SOURCE_AST = Property.get(
- WorkingCopyInfo.class.getName() + ".sourceAst", Object.class); //$NON-NLS-1$
+ protected static final Property<Object> SOURCE_AST = SourceFile.SOURCE_AST;
/**
* Specifies the source string for reconciling.
- * @see #basicReconcile(IContext, IProgressMonitor)
+ * @see #reconcile0(IContext, IProgressMonitor)
+ * @noreference This property is for internal use only.
*/
protected static final Property<String> SOURCE_CONTENTS =
SourceFile.SOURCE_CONTENTS;
/**
* Specifies the source snapshot for reconciling.
- * @see #basicReconcile(IContext, IProgressMonitor)
+ * @see #reconcile0(IContext, IProgressMonitor)
+ * @noreference This property is for internal use only.
*/
protected static final Property<ISnapshot> SOURCE_SNAPSHOT =
SourceFile.SOURCE_SNAPSHOT;
/**
* Indicates whether reconciling was forced, i.e. the working copy buffer
* has not been modified since the last time it was reconciled.
- * @see #basicReconcile(IContext, IProgressMonitor)
+ * @see #reconcile0(IContext, IProgressMonitor)
+ * @noreference This property is for internal use only.
*/
protected static final Property<Boolean> RECONCILING_FORCED =
SourceFile.RECONCILING_FORCED;
@@ -253,7 +287,7 @@
* Clients should not be exposed to working copy info if it has not been
* initialized.
*
- * @noreference For internal use only.
+ * @noreference This method is for internal use only.
*/
public final boolean isInitialized()
{
@@ -271,8 +305,33 @@
}
}
+ /**
+ * A factory of working copy info.
+ */
+ public interface Factory
+ {
+ /**
+ * Returns a new working copy info associated with the given source file
+ * and buffer. This method is not expected to <code>addRef</code> the
+ * given buffer.
+ * <p>
+ * Clients should explicitly {@link WorkingCopyInfo#dispose() dispose}
+ * the working copy info after it is no longer needed.
+ * </p>
+ *
+ * @param sourceFile the source file to be associated with the
+ * created working copy info (not <code>null</code>)
+ * @param buffer the buffer to be associated with the
+ * created working copy info (not <code>null</code>)
+ * @return a new working copy info (never <code>null</code>)
+ */
+ WorkingCopyInfo newWorkingCopyInfo(SourceFile sourceFile,
+ IBuffer buffer);
+ }
+
class InitTask
{
+ private IContext creationContext;
private IProgressMonitor monitor;
private final FutureTask<?> futureTask = new FutureTask<Object>(
new Callable<Object>()
@@ -285,13 +344,16 @@
}
});
- void execute(IProgressMonitor monitor) throws CoreException
+ void execute(IContext context, IProgressMonitor monitor)
+ throws CoreException
{
+ this.creationContext = context;
if (monitor == null)
monitor = new NullProgressMonitor();
this.monitor = monitor;
futureTask.run();
this.monitor = null;
+ this.creationContext = null;
try
{
futureTask.get();
@@ -329,6 +391,7 @@
private void run() throws CoreException
{
+ context = sourceFile.hWorkingCopyContext(creationContext);
onInit();
reconcile(of(FORCE_RECONCILING, true), monitor);
if (!created)
diff --git a/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkspaceSourceFile.java b/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkspaceSourceFile.java
index 48e8ecb..c509945 100644
--- a/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkspaceSourceFile.java
+++ b/org.eclipse.handly/src/org/eclipse/handly/model/impl/WorkspaceSourceFile.java
@@ -102,7 +102,7 @@
}
@Override
- protected final ISnapshotProvider hFileSnapshotProvider(IContext context)
+ protected final ISnapshotProvider hFileSnapshotProvider()
{
return () ->
{