Commit af1c2e03 authored by Johannes Roith's avatar Johannes Roith

- Fixed memory leaks.

- All observers now unregister properly at the controller and model.
- Some other minor changes.
parent 15f3f474
......@@ -26,11 +26,9 @@ public class Controller {
private static final int SET_CHANGED_AFTER_SECONDS = 1;
// TODO: we need to remove the editor from the list, when it gets disposed.
private static WeakHashMap<CupTextEditor, Controller> instances = new WeakHashMap<CupTextEditor, Controller>();
// The Job that's executed after a document change is detected (changeJob)
private DocumentDidChangeJob myJob = null;
private DocumentDidChangeJob job = null;
// if the changeJob takes some time, it's good to know if it has to be
// reexecuted after it finishes
......@@ -45,6 +43,11 @@ public class Controller {
private Set<IRegisterForControllerChanges> controllerObservers = Collections
.newSetFromMap(new WeakHashMap<IRegisterForControllerChanges, Boolean>());
public static void deleteForEditor(CupTextEditor editor) {
if (instances.containsKey(editor))
instances.remove(editor);
}
public static Controller getInstance(CupTextEditor edit) {
Controller instance = instances.get(edit);
if (instance != null) {
......@@ -57,8 +60,8 @@ public class Controller {
private Controller(CupTextEditor editor) {
this.editor = editor;
myJob = new DocumentDidChangeJob(editor);
myJob.setSystem(true);
job = new DocumentDidChangeJob(editor);
job.setSystem(true);
documentEvents = new LinkedList<DocumentEvent>();
Debugger.getInstance(editor.getDocument()); // TODO: why is this here?
}
......@@ -102,7 +105,7 @@ public class Controller {
public void initialRun() {
addJobToDo(JobsToDo.parseCode);
addJobToDo(JobsToDo.buildTable);
myJob.schedule(0);
job.schedule(0);
}
public void registerObserver(IRegisterForControllerChanges observer) {
......@@ -215,13 +218,13 @@ public class Controller {
}
private void runJobs(IDocument document) {
if (myJob.getState() == Job.NONE
|| (!myJob.running() && myJob.getState() == Job.SLEEPING)) {
myJob.cancel();
if (job.getState() == Job.NONE
|| (!job.running() && job.getState() == Job.SLEEPING)) {
job.cancel();
myJob.revNumber = RevisionManager.increment(document);
job.revNumber = RevisionManager.increment(document);
myJob.schedule(SET_CHANGED_AFTER_SECONDS * 1000);
job.schedule(SET_CHANGED_AFTER_SECONDS * 1000);
} else {
RevisionManager.increment(document);
changeJobReRunRequested = System.currentTimeMillis();
......@@ -279,10 +282,6 @@ public class Controller {
*/
public void notifyChangeJobFinished() {
synchronized (lock) {
/*
* System.out.println("job finished -> " + changeJobReRunRequested);
* TODO: REMOVE OUTPUT
*/
if (changeJobReRunRequested > 0) {
changeJobReRunRequested = 0;
......@@ -290,8 +289,8 @@ public class Controller {
// ???
long remainderOfDelay = (SET_CHANGED_AFTER_SECONDS * 100)
- (System.currentTimeMillis() - changeJobReRunRequested);
myJob.revNumber = RevisionManager.get(editor.getDocument());
myJob.schedule((remainderOfDelay >= 0) ? remainderOfDelay : 0);
job.revNumber = RevisionManager.get(editor.getDocument());
job.schedule((remainderOfDelay >= 0) ? remainderOfDelay : 0);
}
}
}
......
......@@ -133,12 +133,12 @@ public class DocumentDidChangeJob extends Job {
e.printStackTrace();
}
} else {
if (context != null) {
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
editor, context, revNumber);
statisticsJob.setSystem(true);
statisticsJob.schedule();
}
jobStatusList.add(new JobStatus(JobsToDo.parseCode, true));
//return Status.CANCEL_STATUS;
}
......@@ -180,12 +180,12 @@ public class DocumentDidChangeJob extends Job {
// TODO: we run the statistics job twice, here, even if we failed
// to build the parser. This should only run once.
if (context != null) {
StatisticsIfFailedJob statisticsJob = new StatisticsIfFailedJob(
editor, context, revNumber);
statisticsJob.setSystem(true);
statisticsJob.schedule();
}
jobStatusList.add(new JobStatus(JobsToDo.buildTable, true));
}
}
......@@ -236,6 +236,7 @@ public class DocumentDidChangeJob extends Job {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
Controller controller = Controller.getInstance(editor);
if (documentDidChangeJob.documentEvents == null
......@@ -253,11 +254,11 @@ public class DocumentDidChangeJob extends Job {
documentDidChangeJob.jobs.addAll(controller.popJobsToDo());
}
jobStatusList.clear();
if (editor == null)
if (editor == null || editor.hasBeenDisposed())
return Status.CANCEL_STATUS;
IDocument document = editor.getDocument();
if (revNumber != RevisionManager.get(document))
return Status.CANCEL_STATUS;
......
......@@ -12,12 +12,15 @@ import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import de.in.tum.www2.cup.Declarations;
import de.in.tum.www2.cup.ast.ParserResult;
import de.tum.in.www2.cupplugin.IDisposable;
import de.tum.in.www2.cupplugin.controller.Controller;
import de.tum.in.www2.cupplugin.model.*;
public class CupContentAssistProcessor implements IContentAssistProcessor,
ICupParserASTChangeObserver {
IDisposable, ICupParserASTChangeObserver {
private Declarations decls;
private IDocument doc;
......@@ -29,10 +32,10 @@ public class CupContentAssistProcessor implements IContentAssistProcessor,
Model model = Model.getInstanceForDocument(doc);
updateFromParserResult(model.getAstModel());
model.registerModelObserver(this);
}
// TODO: de-register on dispose!
public void dispose() {
Model.getInstanceForDocument(doc).unregisterModelObserver(this);
}
private void updateFromParserResult(ParserResult result) {
......
......@@ -6,7 +6,6 @@ import java.util.List;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
......@@ -64,8 +63,8 @@ public class CupContentOutlinePage extends ContentOutlinePage implements
@Override
public void dispose() {
System.out.println("DISPOSING OUTLINE OBSERVER!");
Model.getInstanceForDocument(doc).deRegisterModelObserver(this);
Model.getInstanceForDocument(doc).unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
super.dispose();
};
......
......@@ -2,8 +2,11 @@ package de.tum.in.www2.cupplugin.editors;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.text.BadLocationException;
......@@ -42,8 +45,10 @@ import de.in.tum.www2.cup.ast.ParserResult;
import de.in.tum.www2.cup.ast.ProductionRight;
import de.in.tum.www2.java.JavaScanner;
import de.in.tum.www2.java.JavaSymbol;
import de.tum.in.www2.cupplugin.IDisposable;
import de.tum.in.www2.cupplugin.PluginUtility;
import de.tum.in.www2.cupplugin.controller.Controller.JobsToDo;
import de.tum.in.www2.cupplugin.controller.Controller;
import de.tum.in.www2.cupplugin.controller.IRegisterForControllerChanges;
import de.tum.in.www2.cupplugin.controller.JobStatus;
import de.tum.in.www2.cupplugin.model.ICupParserASTChangeObserver;
......@@ -53,7 +58,11 @@ import de.tum.in.www2.cupplugin.syntax.JavaTokenScanner;
import de.tum.in.www2.cupplugin.syntax.SingleTokenScanner;
public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
CupTextEditor editor;
private CupTextEditor editor;
private Set<IDisposable> disposables = Collections
.newSetFromMap(new WeakHashMap<IDisposable, Boolean>());
private static final int AUTO_ACTIVATION_DELAY = 500;
......@@ -61,6 +70,12 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
this.editor = editor;
}
public void dispose() {
for (IDisposable d : disposables)
if (d != null)
d.dispose();
}
@Override
public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) {
return new MultipleHyperlinkPresenter(CupTextEditor.HYPERLINK.getRGB());
......@@ -74,21 +89,19 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
@Override
public ITextHover getTextHover(ISourceViewer sourceViewer,
String contentType) {
return new CupTextHover(editor);
CupTextHover cth = new CupTextHover(editor);
disposables.add(cth);
return cth;
}
@Override
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
ContentAssistant assistant = new ContentAssistant();
// TODO: correct to get the doc like that?
IDocument doc = editor.getDocumentProvider().getDocument(
editor.getEditorInput());
IContentAssistProcessor tagContentAssistProcessor = new CupContentAssistProcessor(
doc);
assistant.setContentAssistProcessor(tagContentAssistProcessor,
IDocument.DEFAULT_CONTENT_TYPE);
IDocument doc = editor.getDocument();
CupContentAssistProcessor tcap = new CupContentAssistProcessor(doc);
disposables.add(tcap);
ContentAssistant assistant = new ContentAssistant();
assistant.setContentAssistProcessor(tcap, IDocument.DEFAULT_CONTENT_TYPE);
assistant.enableAutoActivation(true);
assistant.setAutoActivationDelay(AUTO_ACTIVATION_DELAY);
assistant
......@@ -99,7 +112,7 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
}
public class PresentationModelObserver implements
ICupParserASTChangeObserver, IRegisterForControllerChanges {
IDisposable, ICupParserASTChangeObserver, IRegisterForControllerChanges {
private CupTokenScanner cupTokenScanner;
private JavaTokenScanner javaTokenScanner;
private ISourceViewer sourceViewer;
......@@ -113,6 +126,7 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
this.sourceViewer = sourceViewer;
Model model = editor.getModel();
Controller.getInstance(editor).registerObserver(this);
model.registerModelObserver(this);
// TODO: this model will likely be empty on first call?!
......@@ -121,6 +135,11 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
cupTokenScanner.updateDeclarations(result.findDeclarations());
}
public void dispose() {
Controller.getInstance(editor).unregisterObserver(this);
editor.getModel().unregisterModelObserver(this);
}
@Override
public EnumSet<JobsToDo> getRequiredJobs() {
return EnumSet.of(JobsToDo.parseCode);
......@@ -166,8 +185,8 @@ public class CupSourceViewerConfiguration extends SourceViewerConfiguration {
// will observe new declarations and trigger rescan of document
// also handles changes to declared cup symbols in java blocks.
new PresentationModelObserver(cupTokenScanner, javaTokenScanner,
sourceViewer);
disposables.add(new PresentationModelObserver(cupTokenScanner, javaTokenScanner,
sourceViewer));
CupDamagerRepairer cupDamagerRepairer = new CupDamagerRepairer(
cupTokenScanner);
......
......@@ -43,6 +43,7 @@ public class CupTextEditor extends TextEditor
private CupContentOutlinePage outlinePage;
private Position lastPosition;
private boolean hasRequestedPostSaveSyntaxRefresh;
private boolean disposed;
private static Color gray;
private static Color blue;
......@@ -193,15 +194,26 @@ public class CupTextEditor extends TextEditor
outlinePage.jumpToPosition(position);
}
public boolean hasBeenDisposed() {
return disposed;
}
@Override
public void dispose() {
System.out.println(">>>>>>>>>> DISPOSING EDITOR!");
Controller.getInstance(this).unregisterObserver(this);
// Note: the outline is actually disposed by eclipse.
CupSourceViewerConfiguration cvs = (CupSourceViewerConfiguration) getSourceViewerConfiguration();
cvs.dispose();
this.disposed = true;
IDocument doc = getDocument();
hasOpened.remove(doc);
Model.deleteForDocument(doc);
Controller.getInstance(this).unregisterObserver(this);
getModel().unregisterModelObserver(this);
Model.deleteForDocument(doc);
super.dispose();
}
......@@ -214,6 +226,7 @@ public class CupTextEditor extends TextEditor
return super.getAdapter(required);
}
public void addActions(){
if(ruler == null)
ruler = getVerticalRuler();
......
......@@ -17,22 +17,42 @@ import de.in.tum.www2.cup.ast.ProductionSymbolRef;
import de.in.tum.www2.cup.ast.Terminal;
import de.in.tum.www2.cup.ast.TypedNonTerminalDeclaration;
import de.in.tum.www2.cup.ast.TypedTerminalDeclaration;
import de.tum.in.www2.cupplugin.IDisposable;
import de.tum.in.www2.cupplugin.controller.Controller.JobsToDo;
import de.tum.in.www2.cupplugin.controller.Controller;
import de.tum.in.www2.cupplugin.controller.IRegisterForControllerChanges;
import de.tum.in.www2.cupplugin.controller.JobStatus;
import de.tum.in.www2.cupplugin.model.ICupParserASTChangeObserver;
import de.tum.in.www2.cupplugin.model.Model;
public class CupTextHover implements ITextHover,
ICupParserASTChangeObserver,
IRegisterForControllerChanges {
public class CupTextHover implements ITextHover, IDisposable,
ICupParserASTChangeObserver, IRegisterForControllerChanges {
private CupTextEditor editor;
private AbstractNode prevRequestedNode;
private ParserResult ast;
private boolean disposed;
public CupTextHover (CupTextEditor editor) {
Model model = Model.getInstanceForDocument(editor.getDocument());
model.registerModelObserver(this);
this.editor = editor;
Controller.getInstance(editor).registerObserver(this);
editor.getModel().registerModelObserver(this);
}
@Override
protected void finalize() throws Throwable {
// This object is released by eclipse early and we need to ensure
// everything gets released correctly.
dispose();
super.finalize();
}
public void dispose() {
if (editor == null || disposed)
return;
disposed = true;
Controller.getInstance(editor).unregisterObserver(this);
editor.getModel().unregisterModelObserver(this);
}
@Override
......
......@@ -189,17 +189,35 @@ public class MultiPageEditor extends MultiPageEditorPart implements
createGraphPage(jumper);
createActionTablePage(jumper);
createReduceTablePage(jumper);
setActivePage(previousPageIndex);
}
@Override
public void dispose() {
editor.getModel().unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
overviewView.dispose();
conflictsView.dispose();
actionTableView.dispose();
reduceTableView.dispose();
conflictGraphView.dispose();
graphReduceView.dispose();
Controller.deleteForEditor(editor);
editor = null;
graphReduceZoomContribution = null;
graphReduceMenu = null;
overviewView = null;
overviewView = null;
conflictsView = null;
actionTableView = null;
reduceTableView = null;
conflictGraphView = null;
graphReduceView = null;
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
super.dispose();
}
......
......@@ -26,7 +26,7 @@ public class Model {
private Statistics statistics;
private final Object statisticsLock = new Object();
private final Object parserModelLock = new Object();
private final Object parserModelLock = new Object(); // TODO: this is also used for the modelObservers?
private final Object lalrModellock = new Object();
private List<Object> modelObservers;
......@@ -38,7 +38,7 @@ public class Model {
instances.remove(document);
if (m != null)
m.dispose();
System.out.println(">>>>>>>> REMOVED MODEL INSTANCE !!!!!!!!!!!!!!!");
System.out.println(">>>>>>>> REMOVED MODEL INSTANCE - new length: " + instances.size());
}
}
......@@ -71,11 +71,13 @@ public class Model {
if (modelObserver == null)
return;
synchronized (parserModelLock) {
if (this.modelObservers.contains(modelObserver))
return;
this.modelObservers.add(modelObserver);
}
}
public boolean deRegisterModelObserver(Object modelObserver) {
public boolean unregisterModelObserver(Object modelObserver) {
if (modelObserver == null)
return false;
synchronized (parserModelLock) {
......
......@@ -140,8 +140,8 @@ public class CupActionTableView extends FailableView implements
}
public void dispose() {
// TODO: unregister everywhere
System.out.println("CupActionGraphView disposed!");
Controller.getInstance(editor).unregisterObserver(this);
Model.getInstanceForDocument(doc).unregisterModelObserver(this);
}
@Override
......
......@@ -100,7 +100,6 @@ public class CupConflictGraphView extends FailableView implements
private CupTextEditor editor;
public GraphViewer graphViewer;
private Model parseModel;
private ParserConflictNodeModelContentProvider nodeModel;
private Composite graphComposite;
private Composite parentComposite;
......@@ -136,8 +135,9 @@ public class CupConflictGraphView extends FailableView implements
IDocument doc = editor.getDocumentProvider().getDocument(
editor.getEditorInput());
this.parseModel = Model.getInstanceForDocument(doc);
this.parseModel.registerModelObserver(this);
Model model = Model.getInstanceForDocument(doc);
model.registerModelObserver(this);
this.parentComposite = parent;
this.conflictResolutionManager = new ConflictResolutionManager(null,
true);
......@@ -359,8 +359,8 @@ public class CupConflictGraphView extends FailableView implements
}
public void dispose() {
// TODO: unregister everywhere
System.out.println("CupConflictGraphView disposed!");
editor.getModel().unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
}
public AbstractZoomableViewer getGraphViewer() {
......@@ -643,25 +643,26 @@ public class CupConflictGraphView extends FailableView implements
nodeMap = new HashMap<Integer, ParserConflictNode>();
}
public HashMap<Integer, ParserConflictNode> getNodeMap() {
private HashMap<Integer, ParserConflictNode> getNodeMap() {
return nodeMap;
}
public List<ParserConflictConnection> getConnections() {
private List<ParserConflictConnection> getConnections() {
return connections;
}
/*
public void rebuildModel(Model model, LALRResult lalrResult,
CupContext lalrContext) {
this.lalrContext = lalrContext;
this.lalrResult = lalrResult;
}
}*/
public List<ParserConflictNode> getNodes() {
private List<ParserConflictNode> getNodes() {
return nodes;
}
public void rebuildModel(List<CupConflictState> resultList) {
private void rebuildModel(List<CupConflictState> resultList) {
connections.clear();
nodes.clear();
nodeMap.clear();
......
......@@ -397,7 +397,8 @@ public class CupConflictsView extends FailableView implements ICupEditorPageVisi
}
public void dispose() {
System.out.println("CupConflictsView disposed!");
editor.getModel().unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
}
private void handleModelUpdate(Model model) {
......@@ -417,7 +418,7 @@ public class CupConflictsView extends FailableView implements ICupEditorPageVisi
}
private void showParseTablesError() {
showError("Parse Tables currently not available.");
showError("Parse tables currently not available.");
}
@Override
......
......@@ -184,6 +184,7 @@ public class CupOverviewView extends FailableView
}
public void dispose() {
editor.getModel().unregisterModelObserver(this);
toolkit.dispose();
}
......
......@@ -102,7 +102,6 @@ public class CupReduceGraphView extends FailableView
public static final Object modelBuildLock = new Object();
public GraphViewer graphViewer;
private Model parseModel;
private ParserNodeModelContentProvider nodeModel;
private Composite graphComposite;
private Composite parentComposite;
......@@ -131,8 +130,8 @@ public class CupReduceGraphView extends FailableView
IDocument doc = editor.getDocumentProvider().getDocument(
editor.getEditorInput());
this.parseModel = Model.getInstanceForDocument(doc);
this.parseModel.registerModelObserver(this);
Model model = Model.getInstanceForDocument(doc);
model.registerModelObserver(this);
this.parentComposite = parent;
GridLayout parentLayout = new GridLayout(6, false);
......@@ -352,8 +351,8 @@ public class CupReduceGraphView extends FailableView
}
public void dispose() {
// TODO: unregister everywhere
System.out.println("CupReduceGraphView disposed!");
editor.getModel().unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
}
@Override
......
......@@ -100,7 +100,8 @@ public class CupReduceTableView extends FailableView implements
}
public void dispose() {
System.out.println("ConflictTableView disposed!");
editor.getModel().unregisterModelObserver(this);
Controller.getInstance(editor).unregisterObserver(this);
}
@Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment