content: // This file is part of AceWiki.
  // Copyright 2008-2013, AceWiki developers.
  // 
  // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
  // Lesser General Public License as published by the Free Software Foundation, either version 3 of
  // the License, or (at your option) any later version.
  // 
  // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
  // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  // Lesser General Public License for more details.
  // 
  // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
  // not, see http://www.gnu.org/licenses/.
  
  package ch.uzh.ifi.attempto.echocomp;
  
  import java.util.HashMap;
  import java.util.Map;
  
  import nextapp.echo.app.ApplicationInstance;
  import nextapp.echo.app.Column;
  import nextapp.echo.app.Component;
  import nextapp.echo.app.TaskQueueHandle;
  
  /**
   * This abstract class can be used to create components that are initialized asynchronously in the background.
   * This is makes sense for components that require some time to create themselves (e.g. because of time
   * expensive calculations). The possibly time consuming creation of the component can be done in a synchronized
   * way so that at most one such creation process (per application instance) is running at a time.
   * 
   * @author Tobias Kuhn
   */
  public abstract class DelayedComponent extends Column {
  	
  	private static final long serialVersionUID = -3479950976442367130L;
  	
  	private static Map<String, TaskQueueHandle> taskQueues =
  		new HashMap<String, TaskQueueHandle>();
  	
  	/**
  	 * Creates a new delayed component which shows the temporary component until the real component is
  	 * ready.
  	 * 
  	 * @param tempComponent The temporary component.
  	 * @param synchronize Defines whether the calculation should be performed in a synchronized way.
  	 */
  	public DelayedComponent(Component tempComponent, boolean synchronize) {
  		if (tempComponent != null) {
  			add(tempComponent);
  		}
  		
  		final ApplicationInstance application = EchoThread.getActiveApplication();
  		TaskQueueHandle taskQueueTemp = taskQueues.get(application.toString());
  		if (taskQueueTemp == null) {
  			taskQueueTemp = application.createTaskQueue();
  			taskQueues.put(application.toString(), taskQueueTemp);
  		}
  		final TaskQueueHandle taskQueue = taskQueueTemp;
  		
  		if (synchronize) {
  			
  			EchoThread thread = new EchoThread() {
  				
  				public ApplicationInstance getApplication() {
  					return application;
  				}
  				
  				public void run() {
  					synchronized (application) {
  						final Component c = initComponent();
  						application.enqueueTask(
  							taskQueue,
  							new Runnable() {
  								public void run() {
  									DelayedComponent.this.removeAll();
  									DelayedComponent.this.add(c);
  									finalizeAction();
  								}
  							}
  						);
  						try {
  							sleep(500);
  						} catch (InterruptedException ex) {}
  					}
  				}
  				
  			};
  			thread.setPriority(Thread.MIN_PRIORITY);
  			thread.start();
  			
  		} else {
  			
  			EchoThread thread = new EchoThread() {
  				
  				public ApplicationInstance getApplication() {
  					return application;
  				}
  				
  				public void run() {
  					final Component c = initComponent();
  					application.enqueueTask(
  						taskQueue,
  						new Runnable() {
  							public void run() {
  								DelayedComponent.this.removeAll();
  								DelayedComponent.this.add(c);
  								finalizeAction();
  							}
  						}
  					);
  				}
  				
  			};
  			thread.start();
  			
  		}
  		
  	}
  	
  	
  	/**
  	 * Creates a new delayed component with the given temporary component. The calculation is
  	 * not synchronized.
  	 * 
  	 * @param tempComponent The temporary component.
  	 */
  	public DelayedComponent(Component tempComponent) {
  		this(tempComponent, false);
  	}
  	
  	
  	/**
  	 * Creates a new delayed component with no temporary component.
  	 * 
  	 * @param synchronize Defines whether the calculation should be performed in a synchronized way.
  	 */
  	public DelayedComponent(boolean synchronize) {
  		this(null, synchronize);
  	}
  	
  	
  	/**
  	 * Creates a new delayed component with no temporary component. The calculation is
  	 * not synchronized.
  	 */
  	public DelayedComponent() {
  		this(null, false);
  	}
  	
  	/**
  	 * This method should contain the (possibly time-consuming) operations to create the actual GUI
  	 * component. This operation will be performed asynchronously. As soon as it is finished, the
  	 * temporary component (if present) is replaced by the component this method returns.
  	 * 
  	 * @return The GUI component.
  	 */
  	public abstract Component initComponent();
  	
  	/**
  	 * Override this method to run code in the appliction context (e.g. for GUI changes) after the
  	 * asynchronous operation has finished.
  	 */
  	public void finalizeAction() {
  	}
  
  }
  