Bug 354549 timing problems in ChartView
diff --git a/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.chart/src/org/eclipse/amp/agf/chart/ChartEditPart.java b/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.chart/src/org/eclipse/amp/agf/chart/ChartEditPart.java
index d706c3a..c207c9c 100644
--- a/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.chart/src/org/eclipse/amp/agf/chart/ChartEditPart.java
+++ b/org.eclipse.amp.agf/plugins/org.eclipse.amp.agf.chart/src/org/eclipse/amp/agf/chart/ChartEditPart.java
@@ -49,6 +49,7 @@
 import org.eclipse.draw2d.geometry.Dimension;

 import org.eclipse.draw2d.geometry.Point;

 import org.eclipse.emf.common.util.EList;

+import org.eclipse.gef.EditPart;

 import org.eclipse.gef.editparts.AbstractGraphicalEditPart;

 import org.eclipse.jface.viewers.IColorProvider;

 import org.eclipse.swt.events.DisposeEvent;

@@ -90,6 +91,7 @@
 	 * Instantiates a new chart edit part.

 	 */

 	public ChartEditPart() {

+		chartStrategy = getInitStrategie();

 		resourceManager = new ResourceManager();

 		PlatformConfig config = new PlatformConfig();

 		try {

@@ -102,6 +104,10 @@
 		genericPalette.shift(0);

 	}

 

+	IChartDesignStrategy getInitStrategie() {

+		return ChartType.TIME_SERIES.createStrategy();

+	}

+

 	/**

 	 * The listener interface for receiving chartModel events. The class that is

 	 * interested in processing a chartModel event implements this interface, and

@@ -113,6 +119,7 @@
 	 * @see ChartModelEvent

 	 */

 	class ChartModelListener extends SWTAsyncModelListener {

+

 		/**

 		 * Instantiates a new chart model listener.

 		 */

@@ -122,15 +129,7 @@
 

 		@Override

 		public void observing(IObservationProvider observed) {

-			// wait for data source to be available from model set..

-			while (getDataProvider() == null) {

-				try {

-					Thread.sleep(25);

-				} catch (InterruptedException e) {

-					//

-				}

-			}

-			setChartStrategy(ChartType.TIME_SERIES.createStrategy());

+			setChartStrategy(getInitStrategie());

 		}

 

 		@Override

@@ -201,6 +200,7 @@
 		chartListener.setWidget(getViewer().getControl());

 

 		final Figure newFigure = new Figure() {

+

 			@Override

 			public void paintFigure(Graphics graphics) {

 				if (getResourceManager().getImage() != null) {

@@ -212,6 +212,7 @@
 			}

 		};

 		newFigure.setLayoutManager(new AbstractLayout() {

+

 			@Override

 			public Dimension getMinimumSize(IFigure container, int hintWidth, int hintHeight) {

 				return new Dimension(hintWidth < 1 ? 1 : hintWidth, hintHeight < 1 ? 1 : hintHeight);

@@ -227,6 +228,7 @@
 			}

 		});

 		newFigure.addFigureListener(new FigureListener() {

+

 			public void figureMoved(IFigure source) {

 				regenerateChart();

 			}

@@ -242,12 +244,7 @@
 

 	@Override

 	public void refresh() {

-		new Thread() {

-			@Override

-			public void run() {

-				regenerateChart(true);

-			}

-		}.run();

+		regenerateChart(true);

 	}

 

 	@Override

@@ -261,7 +258,9 @@
 		Object provider = adapterManager.loadAdapter(this.getModel(), IDataProvider.ID);

 

 		if (!(provider instanceof IDataProvider)) {

-			throw new RuntimeException("Couldn't find data provider for chart model. Please ensure that a data provider adapter has been defined for the class: " + this.getModel().getClass());

+			throw new RuntimeException(

+					"Couldn't find data provider for chart model. Please ensure that a data provider adapter has been defined for the class: "

+							+ this.getModel().getClass());

 		}

 

 		setDataProvider((IDataProvider) provider);

@@ -306,7 +305,8 @@
 				chartListener.endPainting();

 			}

 		} catch (ChartException ce) {

-			StatusManager.getManager().handle(new Status(Status.WARNING, AGFChartPlugin.PLUGIN_ID, "Couldn't generate chart.", ce));

+			StatusManager.getManager().handle(

+					new Status(Status.WARNING, AGFChartPlugin.PLUGIN_ID, "Couldn't generate chart.", ce));

 			chartListener.endPainting();

 		}

 	}

@@ -325,14 +325,6 @@
 	}

 

 	/**

-	 * Creates the chart.

-	 */

-	public synchronized void createChart() {

-		chart = chartStrategy.createChart(dataProvider, dataSource);

-		chart.getTitle().getLabel().setVisible(false);

-	}

-

-	/**

 	 * Update chart series.

 	 */

 	public synchronized void updateChartSelection() {

@@ -387,8 +379,7 @@
 

 	/**

 	 * 

-	 * @see org.eclipse.birt.chart.device.ICallBackNotifier#callback(java.lang.Object,

-	 *      java.lang.Object,

+	 * @see org.eclipse.birt.chart.device.ICallBackNotifier#callback(java.lang.Object, java.lang.Object,

 	 *      org.eclipse.birt.chart.model.attribute.CallBackValue)

 	 */

 	public void callback(Object arg0, Object arg1, CallBackValue arg2) {

@@ -447,10 +438,12 @@
 		this.dataProvider = dataProvider;

 		dataSource = dataProvider.getDataSource(getModel());

 		dataListener = new IDataSelectionListener() {

+

 			public void selectionChanged(Object dataSet) {

 				if (getParent() != null && getRoot() != null && getViewer() != null && getChart() != null) {

 					updateChartSelection();

 					getViewer().getControl().getDisplay().asyncExec(new Runnable() {

+

 						public void run() {

 							regenerateChart(true);

 						}

@@ -459,14 +452,24 @@
 			}

 		};

 		dataProvider.addListener(dataSource, dataListener);

+		createAndInitChart();

 	}

 

 	public void setChartStrategy(IChartDesignStrategy chartStrategy) {

 		this.chartStrategy = chartStrategy;

-		createChart();

+		createAndInitChart();

+	}

+

+	private void createAndInitChart() {

+		if(dataProvider == null){

+			return;

+		}

+		chart = chartStrategy.createChart(dataProvider, dataSource);

+		chart.getTitle().getLabel().setVisible(false);

 		updateChartSelection();

-		if (getViewer().getControl().getDisplay() != null) {

+		if (getViewer() != null && getViewer().getControl() != null && !getViewer().getControl().isDisposed()) {

 			getViewer().getControl().getDisplay().asyncExec(new Runnable() {

+

 				public void run() {

 					repaintChart();

 				}

@@ -493,6 +496,7 @@
 	 * @author fei

 	 */

 	public class ResourceManager implements DisposeListener {

+

 		private GC gc;

 

 		private Image image;

@@ -530,7 +534,8 @@
 

 		private void addDisposeListener() {

 			removeDisposeListener();

-			if (disposeEventProvider == null && getViewer() != null && getViewer().getControl() != null && !getViewer().getControl().isDisposed()) {

+			if (disposeEventProvider == null && getViewer() != null && getViewer().getControl() != null

+					&& !getViewer().getControl().isDisposed()) {

 				disposeEventProvider = getViewer().getControl();

 				disposeEventProvider.addDisposeListener(this);

 			}

diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/view/ScaleGranularityControl.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/view/ScaleGranularityControl.java
index a4cbeb8..28ed94c 100644
--- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/view/ScaleGranularityControl.java
+++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.ide/src/org/eclipse/amp/axf/ide/view/ScaleGranularityControl.java
@@ -64,7 +64,12 @@
 			}

 		});

 

-		ModelViewManager.getInstance().getManagerListeners().addModelManagerListener(ScaleGranularityControl.this);

+		IModel activeModel = ModelViewManager.getInstance().getActiveModel();

+		if (activeModel != null) {

+			engine = activeModel.getEngine();

+		}

+

+		ModelViewManager.getInstance().getManagerListeners().addModelManagerListener(this);

 		return scale;

 	}

 

diff --git a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.views/src/org/eclipse/amp/axf/view/SWTAsyncModelListener.java b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.views/src/org/eclipse/amp/axf/view/SWTAsyncModelListener.java
index cb57ee0..ab6c150 100644
--- a/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.views/src/org/eclipse/amp/axf/view/SWTAsyncModelListener.java
+++ b/org.eclipse.amp.axf/plugins/org.eclipse.amp.axf.views/src/org/eclipse/amp/axf/view/SWTAsyncModelListener.java
@@ -15,6 +15,9 @@
  */
 package org.eclipse.amp.axf.view;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.eclipse.amp.axf.core.IModel;
 import org.eclipse.amp.axf.core.LifeCycleState;
 import org.eclipse.amp.axf.time.TimeGranularity;
@@ -33,155 +36,161 @@
  */
 public abstract class SWTAsyncModelListener extends SWTThreadModelListener {
 
-    private final class ReportingRunner implements Runnable {
-        private final LifeCycleState key;
-        private final IModel model;
-        Exception t;
+	private List<ReportingRunner> reportingRunners;
 
-        public ReportingRunner(LifeCycleState key, IModel model) {
-            this.key = key;
-            this.model = model;
-        }
+	private boolean waitForPaint = true;
 
-        public void run() {
-            try {
-                doUpdate(key, model);
-            } catch (Exception e) {
-                t = e;
-            } finally {
-                updating = false;
-                if (t != null) {
-                    painting = false;
-                    t.printStackTrace();
-                    throw new RuntimeException("Problem in view updating for " + getName(), t);
-                }
-            }
-        }
-    }
+	private long minTimeBetweenUpdates = 10000;
 
-    private boolean waitForPaint = true;
+	protected boolean updating = false;
 
-    private long minTimeBetweenUpdates = 10000;
+	private boolean painting = false;
 
-    protected boolean updating = false;
+	private long lastUpdate;
 
-    private boolean painting = false;
+	/**
+	 * Instantiates a new sWT async model listener.
+	 * 
+	 * @param widget the widget
+	 * @param name the name
+	 */
+	public SWTAsyncModelListener(Control widget, String name) {
+		super(widget, name);
+		reportingRunners = new ArrayList<SWTAsyncModelListener.ReportingRunner>();
+	}
 
-    private long lastUpdate;
+	/**
+	 * Instantiates a new sWT async model listener.
+	 * 
+	 * @param widget the widget
+	 * @param name the name
+	 * @param updatePeriod the update period
+	 */
+	public SWTAsyncModelListener(Control widget, String name, long updatePeriod) {
+		this(widget, name);
+		this.minTimeBetweenUpdates = updatePeriod;
+	}
 
-    /**
-     * Instantiates a new sWT async model listener.
-     * 
-     * @param widget the widget
-     * @param name the name
-     */
-    public SWTAsyncModelListener(Control widget, String name) {
-        super(widget, name);
-    }
+	List<ReportingRunner> getReportingRunners() {
+		return reportingRunners;
+	}
 
-    /**
-     * Instantiates a new sWT async model listener.
-     * 
-     * @param widget the widget
-     * @param name the name
-     * @param updatePeriod the update period
-     */
-    public SWTAsyncModelListener(Control widget, String name, long updatePeriod) {
-        super(widget, name);
-        this.minTimeBetweenUpdates = updatePeriod;
-    }
+	/**
+	 * @param key
+	 * @param observed
+	 * @see org.eclipse.amp.axf.core.AbstractLifecycleListener#stateChange(java.lang.Object, java.lang.Object)
+	 */
+	@Override
+	public void stateChange(Object key, Object observed) {
+		IModel model = (IModel) observed;
+		if (model.getEngine().isRunning()
+				&& (System.currentTimeMillis() > lastUpdate + minTimeBetweenUpdates || model.getPeriod()
+						% ((TimeGranularity) model.getEngine().getUpdateGranularity()).getUpdateFrequency() == 0)) {
+			lastUpdate = System.currentTimeMillis();
+			updating = true;
+			if (AXFViewPlugin.getDefault() != null) {
+				Display display = AXFViewPlugin.getDefault().getWorkbench().getDisplay();
+				if (!display.isDisposed()) {
+					ReportingRunner runnable = new ReportingRunner((LifeCycleState) key, model);
+					reportingRunners.add(runnable);
+					display.asyncExec(runnable);
 
-    /**
-     * @param key
-     * @param observed
-     * @see org.eclipse.amp.axf.core.AbstractLifecycleListener#stateChange(java.lang.Object, java.lang.Object)
-     */
-    @Override
-    public void stateChange(Object key, Object observed) {
-        IModel model = (IModel) observed;
-        if (model.getEngine().isRunning()
-                && (System.currentTimeMillis() > lastUpdate + minTimeBetweenUpdates || model.getPeriod()
-                        % ((TimeGranularity) model.getEngine().getUpdateGranularity()).getUpdateFrequency() == 0)) {
-            lastUpdate = System.currentTimeMillis();
-            updating = true;
-            ReportingRunner runnable = new ReportingRunner((LifeCycleState) key, model);
-            if (AXFViewPlugin.getDefault() != null) {
-                Display display = AXFViewPlugin.getDefault().getWorkbench().getDisplay();
-                if (!display.isDisposed()) {
-                    display.asyncExec(runnable);
-                    while ((updating || painting) && isWaitForUpdate()
-                            && (getWidget() == null || !getWidget().isDisposed())) {
-                        try {
-                            // Assume best case of 60fps + allow compute
-                            Thread.sleep(5);
-                        } catch (InterruptedException e) {
-                        }
-                    }
-                }
-            }
-            if (runnable.t != null) {
-                runnable.t.printStackTrace();
-            }
-        } else if (key == LifeCycleState.OBSERVED || key == LifeCycleState.END) {
-            SWTAsyncModelListener.super.stateChange(key, model);
-        }
-        // Even though model is not null on method entrance (state change), it could become null during execution of
-        // reporting runner
-        if (model != null) {
-            forceModelNotify(model);
-        }
-    }
+				}
+			}
+		} else if (key == LifeCycleState.OBSERVED || key == LifeCycleState.END) {
+			SWTAsyncModelListener.super.stateChange(key, model);
+		}
+		// Even though model is not null on method entrance (state change), it could become null during execution of
+		// reporting runner
+		if (model != null) {
+			forceModelNotify(model);
+		}
+	}
 
-    private void doUpdate(Object key, final IModel model) {
-        // Have to check visibility within UI thread
-        if (getWidget() == null || !getWidget().isDisposed() && getWidget().isVisible()) {
-            update(model);
-            getListener().stateChange(key, model);
-        }
-        // If the update method hasn't explicitly indicated beginPainting, we are not waiting for the
-        // actual update or it has already occurred.
-        if (!painting) {
-        }
-    }
+	private void doUpdate(Object key, final IModel model) {
+		// Have to check visibility within UI thread
+		if (getWidget() == null || !getWidget().isDisposed() && getWidget().isVisible()) {
+			update(model);
+			getListener().stateChange(key, model);
+		}
+		// If the update method hasn't explicitly indicated beginPainting, we are not waiting for the
+		// actual update or it has already occurred.
+		if (!painting) {
+		}
+	}
 
-    /**
-     * Force model notify.
-     * 
-     * @param model the model
-     */
-    public void forceModelNotify(IModel model) {
-        model.getEngine().observationComplete(this);
-    }
+	/**
+	 * Force model notify.
+	 * 
+	 * @param model the model
+	 */
+	public void forceModelNotify(IModel model) {
+		model.getEngine().observationComplete(this);
+	}
 
-    /**
-     * Begin painting.
-     */
-    public void beginPainting() {
-        this.painting = true;
-    }
+	/**
+	 * Begin painting.
+	 */
+	public void beginPainting() {
+		this.painting = true;
+	}
 
-    /**
-     * End painting.
-     */
-    public void endPainting() {
-        this.painting = false;
-    }
+	/**
+	 * End painting.
+	 */
+	public void endPainting() {
+		this.painting = false;
+	}
 
-    /**
-     * Sets the wait for paint.
-     * 
-     * @param waitForPaint the new wait for paint
-     */
-    public void setWaitForUpdate(boolean waitForPaint) {
-        this.waitForPaint = waitForPaint;
-    }
+	/**
+	 * Sets the wait for paint.
+	 * 
+	 * @param waitForPaint the new wait for paint
+	 */
+	public void setWaitForUpdate(boolean waitForPaint) {
+		this.waitForPaint = waitForPaint;
+	}
 
-    /**
-     * Checks if is wait for paint.
-     * 
-     * @return true, if is wait for paint
-     */
-    public boolean isWaitForUpdate() {
-        return waitForPaint;
-    }
+	/**
+	 * Checks if is wait for paint.
+	 * 
+	 * @return true, if is wait for paint
+	 */
+	public boolean isWaitForUpdate() {
+		return waitForPaint;
+	}
+
+	private final class ReportingRunner implements Runnable {
+
+		private final LifeCycleState key;
+
+		private final IModel model;
+
+		Exception t;
+
+		public ReportingRunner(LifeCycleState key, IModel model) {
+			this.key = key;
+			this.model = model;
+		}
+
+		public void run() {
+			try {
+				if (reportingRunners.size() > 1) {
+					return;
+				}
+				doUpdate(key, model);
+			} catch (Exception e) {
+				t = e;
+			} finally {
+				reportingRunners.remove(this);
+				updating = false;
+				if (t != null) {
+					painting = false;
+					t.printStackTrace();
+					throw new RuntimeException("Problem in view updating for " + getName(), t);
+				}
+			}
+		}
+	}
+
 }
diff --git a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java
index 022d4af..e8c2d09 100644
--- a/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java
+++ b/org.eclipse.amp.escape/plugins/org.eclipse.amp.escape.ascape/src/org/eclipse/amp/escape/ascape/wrap/ScapeWrapperModel.java
@@ -71,17 +71,12 @@
 	}
 
 	public void addModelListener(final ILifeCycleListener listener) {
-		new Thread() {
-			@Override
-			public void run() {
-				listeners.add(listener);
-				if (!(listener instanceof ScapeWrapperModelListener)) {
-					ModelWrapperScapeListener wrapper = new ModelWrapperScapeListener(ScapeWrapperModel.this, getScape(), listener);
-					getScape().addView(wrapper);
-					wrapperForListener.put(listener, wrapper);
-				}
-			}
-		}.start();
+		listeners.add(listener);
+		if (!(listener instanceof ScapeWrapperModelListener)) {
+			ModelWrapperScapeListener wrapper = new ModelWrapperScapeListener(ScapeWrapperModel.this, getScape(), listener);
+			getScape().addView(wrapper);
+			wrapperForListener.put(listener, wrapper);
+		}
 	}
 
 	public Collection<ILifeCycleListener> getModelListeners() {
@@ -89,16 +84,11 @@
 	}
 
 	public void removeModelListener(final ILifeCycleListener listener) {
-		new Thread() {
-			@Override
-			public void run() {
-				listeners.remove(listener);
-				if (listener instanceof ScapeWrapperModelListener) {
-					getScape().removeScapeListener(((ScapeWrapperModelListener) listener).getWrapped());
-				}
-				listener.observationEnd(ScapeWrapperModel.this);
-			}
-		}.start();
+		listeners.remove(listener);
+		if (listener instanceof ScapeWrapperModelListener) {
+			getScape().removeScapeListener(((ScapeWrapperModelListener) listener).getWrapped());
+		}
+		listener.observationEnd(ScapeWrapperModel.this);
 	}
 
 	public boolean isInitialized() {
diff --git a/org.eclipse.amp.escape/tests/org.eclipse.amp.escape.ide.test/src/org/eclipse/amp/escape/ide/ProjectLoaderTest.java b/org.eclipse.amp.escape/tests/org.eclipse.amp.escape.ide.test/src/org/eclipse/amp/escape/ide/ProjectLoaderTest.java
index 9c3ac80..e4e07e1 100644
--- a/org.eclipse.amp.escape/tests/org.eclipse.amp.escape.ide.test/src/org/eclipse/amp/escape/ide/ProjectLoaderTest.java
+++ b/org.eclipse.amp.escape/tests/org.eclipse.amp.escape.ide.test/src/org/eclipse/amp/escape/ide/ProjectLoaderTest.java
@@ -6,8 +6,12 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.util.List;
 
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Platform;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
@@ -31,7 +35,7 @@
 	 */
 	@Test
 	public void testReadDependencies() throws IllegalStateException, FileNotFoundException, BundleException, IOException {
-		List<Bundle> dependencies = ProjectLoader.readDependencies(new FileInputStream("testFiles/defaultManifest.MF"));
+		List<Bundle> dependencies = ProjectLoader.readDependencies(getInputStream("defaultManifest.MF"));
 		assertEquals(8, dependencies.size());
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.resources")));
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.runtime")));
@@ -53,7 +57,7 @@
 	 */
 	@Test
 	public void testReadDependencies_optionalBundles() throws IllegalStateException, FileNotFoundException, BundleException, IOException {
-		List<Bundle> dependencies = ProjectLoader.readDependencies(new FileInputStream("testFiles/optionalBundles.MF"));
+		List<Bundle> dependencies = ProjectLoader.readDependencies(getInputStream("optionalBundles.MF"));
 		assertEquals(8, dependencies.size());
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.resources")));
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.runtime")));
@@ -77,7 +81,7 @@
 	 */
 	@Test
 	public void testReadDependencies_bundleUnavalilableButOptional() throws IllegalStateException, FileNotFoundException, BundleException, IOException {
-		List<Bundle> dependencies = ProjectLoader.readDependencies(new FileInputStream("testFiles/bundleUnavalilableButOptional.MF"));
+		List<Bundle> dependencies = ProjectLoader.readDependencies(getInputStream("bundleUnavalilableButOptional.MF"));
 		assertEquals(8, dependencies.size());
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.resources")));
 		assertTrue(dependencies.contains(Platform.getBundle("org.eclipse.core.runtime")));
@@ -101,7 +105,12 @@
 	 */
 	@Test(expected = IllegalStateException.class)
 	public void testReadDependencies_bundleUnavailableButNecessary() throws IllegalStateException, FileNotFoundException, BundleException, IOException {
-		ProjectLoader.readDependencies(new FileInputStream("testFiles/bundleUnavailableButNecessary.MF"));
+		ProjectLoader.readDependencies(getInputStream("bundleUnavailableButNecessary.MF"));
 	}
 
+	private InputStream getInputStream(String fileName) throws IOException{
+		URL[] url = FileLocator.findEntries(EscapeIDEPlugin.getDefault().getBundle(), new Path("testFiles/"+fileName));
+		assert url.length == 1 : "Es wurde nicht genau einen Pfad gefunden. Erwartet wurde der Pfad für das:testFiles/" + fileName;
+		return url[0].openStream();
+	}
 }