/*******************************************************************************
 * Copyright (c) 2015 Pivotal Software, Inc.
 * 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:
 *    Pivotal Software, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.livexp.core;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

/**
 * Like a LiveExpression but ensures that its refresh method is always called
 * in a background job.
 * <p>
 * With a regular LiveExp the refresh will be called on the same thread
 * as the change event it reacts to. So in general you have little control
 * over what thread that might be.
 * <p>
 * {@link AsyncLiveExpression} is when the 'compute' method called during refresh
 * does something that you might not want to just execute, for example, on the UI thread.
 * <p>
 * It is also useful in that when refreshes might be 'lengthy' operations, bursty events
 * triggering refreshes will only causes limited refreshes as only a single Job is
 * being scheduled and rescheduled.
 *
 * @author Kris De Volder
 */
public abstract class AsyncLiveExpression<T> extends LiveExpression<T> {

	private Job refreshJob;
	private long refreshDelay = 0;

	public AsyncLiveExpression(T initialValue) {
		this(initialValue, "AsyncLiveExpression refresh");
	}

	public AsyncLiveExpression(T initialValue, String jobName) {
		super(initialValue);
		refreshJob = new Job(jobName) {
			protected IStatus run(IProgressMonitor monitor) {
				syncRefresh();
				return Status.OK_STATUS;
			};
		};
	}

	@Override
	public void refresh() {
		refreshJob.schedule(refreshDelay);
	}

	private void syncRefresh() {
		super.refresh();
	}

	public long getRefreshDelay() {
		return refreshDelay;
	}

	public void setRefreshDelay(long refreshDelay) {
		this.refreshDelay = refreshDelay;
	}

}

@@ -795,16 +795,33 @@
 ion but 
+has an option to 
 ensures 
@@ -836,16 +836,19 @@
  refresh
+%0A *
  method 
@@ -863,19 +863,16 @@
 s called
-%0A *
  in a ba
@@ -1575,16 +1575,59 @@
 n%3CT%3E %7B%0A%0A
+%09public enum AsyncMode %7B%0A%09%09SYNC, ASYNC%0A%09%7D%0A%0A
 %09private
@@ -1742,16 +1742,145 @@
 alValue,
+ AsyncMode.ASYNC);%0A%09%7D%0A%0A%09public AsyncLiveExpression(T initialValue, AsyncMode mode) %7B%0A%09%09this(initialValue, mode==AsyncMode.ASYNC ?
  %22AsyncL
@@ -1901,23 +1901,260 @@
 refresh%22
+: null
 );%0A%09%7D%0A%0A
+%09/**%0A%09 * Create AsyncLiveExpression. If Job name is passed in then%0A%09 * this expression will refresh itself asynchronously.%0A%09 * %3Cp%3E%0A%09 * If the jobName is null then it behaves like a plain (i.e.%0A%09 * synchronous) LiveExpression.%0A%09 */%0A
 %09public 
@@ -2230,16 +2230,40 @@
 Value);%0A
+%09%09if (jobName!=null) %7B%0A%09
 %09%09refres
@@ -2288,16 +2288,17 @@
 Name) %7B%0A
+%09
 %09%09%09prote
@@ -2346,16 +2346,17 @@
 ) %7B%0A%09%09%09%09
+%09
 syncRefr
@@ -2362,16 +2362,17 @@
 resh();%0A
+%09
 %09%09%09%09retu
@@ -2399,20 +2399,219 @@
 %0A%09%09%09
+%09
 %7D;%0A
+%09
 %09%09%7D;%0A
+%09%09%7D%0A
 %09%7D%0A%0A
+%09/**%0A%09 * This method is final, if you are overriding this, then you probably should%0A%09 * be overriding 'syncRefresh' instead. Otherwise you probably are breaking%0A%09 * async refresh support.%0A%09 */%0A
 %09@Ov
@@ -2621,24 +2621,30 @@
 ide%0A%09public 
+final 
 void refresh
@@ -2644,24 +2644,51 @@
 refresh() %7B%0A
+%09%09if (refreshJob!=null) %7B%0A%09
 %09%09refreshJob
@@ -2713,27 +2713,62 @@
 elay);%0A%09
-%7D%0A%0A%09private
+%09%7D else %7B%0A%09%09%09syncRefresh();%0A%09%09%7D%0A%09%7D%0A%0A%09protected
  void sy

Generalize 'AsyncLiveExpression' so async behavior is optional.