package jas2.swingstudio;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import jas2.util.*;
import jas2.util.tree.*;
import jas2.job.*;
import jas2.plugin.*;
import java.rmi.*;
/**
 * JASJob is that part of the "Job" which is always in the client, regardless of whether the job
 * is local or remote. The getJob() method will return the real job, which may be an instance of
 * LocalJob or JasJobAdaptor (for remote jobs).
 */

public final class JASJob implements Serializable, TreeSelectionListener, StoppableExecution, TreeObjectSource, ActionListener
{
	static final long serialVersionUID = 8830773281026894908L;

	public JASJob(String name)
	{
		m_name = name;
		m_root = new TreeNode(name,"Job");
		m_command = new JASJobCommandProcessor();
		m_treeModel = new SimpleTreeModel(m_root);
		m_treeRenderer = new JASTreeCellRenderer();
		m_listRenderer = new JASListCellRenderer();
	}
	private void writeObject(ObjectOutputStream stream) throws IOException
	{
		stream.defaultWriteObject();
	
		// Store the state of the remote job

		stream.writeInt(m_job.eventsAnalyzed());
		stream.writeBoolean(m_job.isAtEndOfData());
		
		m_changes = 0;
		m_command.forceSetChanged();

//		m_treeModel.saveState(stream);
	}
	private void readObject(ObjectInputStream stream) 
		throws OpenJobException
	{
		try
		{
			m_treeRenderer = new JASTreeCellRenderer();
			m_eventSourceModel = new ComboModel();
			m_listRenderer = new JASListCellRenderer();
			
			m_plugins = new Vector();
			m_pluginMenus = new Vector();
			m_pluginContext = new JobPluginContext(m_app);

			stream.defaultReadObject();
			m_root = new TreeNode(m_name,"Job");
			m_treeModel = new SimpleTreeModel(m_root);
			m_job.reopen(stream, this);
			m_app = JavaAnalysisStudio.getApp();
			m_changes = 0;
			( m_command = new JASJobCommandProcessor() ).setChanged();
			m_app.getCommandManager().add(m_command);
			m_stopMonitor = m_app.registerStoppable(this);

//			m_treeModel.restoreState(stream);
		}
		catch (Exception e)
		{
			throw new OpenJobException(e); 
		}
		m_changes = 0;
	}
	/**
	 * Adds a plugin to the list of active plugins 
	 * (so that they can be destroyed cleanly when the job is closed).
	 */
	private void addPlugin(IPlugin p)
	{
		m_plugins.addElement(p);
		p.setPluginContext(m_pluginContext);
	}
	void loadPlugin(String className) throws InstantiationException,IllegalAccessException,ClassNotFoundException 
	{
		Class pc = loadClientClass(className);
		IPlugin plugin = (IPlugin) pc.newInstance();
		addPlugin(plugin);
	}
	Class loadClientClass(String className) throws ClassNotFoundException
	{
		return m_job.loadClientClass(className);
	}
	void close()
	{
		if (m_job != null) m_job.close();
		if (m_command != null) m_app.getCommandManager().remove(m_command);
		m_job = null;
		m_command = null;
		if (m_stopMonitor != null) m_stopMonitor.end();

		clearTreeModel();
		clearEventSourceModel();
		m_app.setCurrentSelection(null);
		
		// unload any plugins

		Enumeration e = m_plugins.elements();
		while (e.hasMoreElements())
		{
			IPlugin p = (IPlugin) e.nextElement();
			p.destroy();
		}
		
		JMenuBar menuBar = m_app.getMenuBar();
		e = m_pluginMenus.elements();
		while (e.hasMoreElements())
		{
			JMenu menu = (JMenu) e.nextElement();
			menuBar.remove(menu);
		}
		menuBar.revalidate();

		// Now would be a great time for a garbage collection!

		Runtime rt = Runtime.getRuntime();
		rt.gc();
		rt.runFinalization();
	}
	/**
	 * Causes all classes loaded as a result of the LoadClass command to be
	 * reloaded
	 */
	void reload() throws RemoteException, LoaderException
	{
		if (m_job != null) m_job.reload();
		m_changes++;
		m_command.forceSetChanged();
	}
	/**
	 * Get the job name
	 */
	String getName()
	{
		return m_name;
	}
	/**
	 * The path to the file where this job is stored (use for Save/Restore)
	 */
	void setFilePath(String path)
	{
		m_filePath = path;
	}
	String getFilePath()
	{
		return m_filePath;
	}
	public void setJob(Job job)
	{
		m_job = job;
		m_app.getCommandManager().add(m_command);
		m_stopMonitor = m_app.registerStoppable(this);
	}
	public boolean isChanged()
	{
		return m_changes>0 || ( m_job != null && m_job.isChanged());
	}
	void setTreeModel(JTree tree)
	{
		if (m_tree != null)
      {
         // This is important to avoid memory leaks
         m_tree.getSelectionModel().removeTreeSelectionListener(this);
         m_tree.removeTreeSelectionListener((TreeSelectionListener) m_tree.getCellEditor());
      }
		m_tree = tree;
		if (tree != null)
		{
			tree.setModel(m_treeModel);
			tree.setCellRenderer(m_treeRenderer);
			tree.setCellEditor(new JASTreeCellEditor(tree,m_treeRenderer));
			tree.getSelectionModel().addTreeSelectionListener(this);
		}
	}
	private void clearTreeModel()
	{
		setTreeModel(null);
	}
	/**
	 * Used to attach the "Current Event Source" combo box to the event source model.
	 * Also installs a listener to listen for changes to the combo box
	 */
	void setEventSourceModel(JComboBox combo)
	{
		if (m_combo != null)
      {
         m_combo.removeActionListener(this);
      }
		m_combo = combo;
		if (combo != null)
		{
			combo.setModel(m_eventSourceModel);
			combo.setRenderer(m_listRenderer);
			combo.addActionListener(this);
		}
	}
	private void clearEventSourceModel()
	{
		setEventSourceModel(null);
	}
	/**
	 * The event source combo box has been changed
	 */
	public void actionPerformed(ActionEvent e)
	{
		TreeNode node = (TreeNode) m_combo.getSelectedItem();
		if (node != null) m_job.setEventSource(node.getTreeItem());
	}
	void updateTree()
	{
		//if (m_treeModel != null) m_treeModel.update();
		m_changes++;
		if (m_command != null) m_command.forceSetChanged();
	}
	void setCommandChanged()
	{
		if (m_command != null) m_command.setChanged();
	}
	Job getJob()
	{
		return m_job;
	}
	ComboBoxModel getComboModel()
	{
		return m_eventSourceModel;
	}
	ListCellRenderer getListRenderer()
	{
		return m_listRenderer;
	}
	public void stop()
	{
		m_job.stop();
		m_stopMonitor.doStopEnabling();
	}
	public boolean isRunning()
	{
		return m_job.isRunning();
	}
	void addNode(TreeItem item)
	{
		TreeNode node = (TreeNode) m_root.add(item);
		if ((item.flags & Job.EVENTSOURCE) != 0)
		{
			m_eventSourceModel.addElement(node);
		}
		node.update();
	}
	void deleteNode(TreeItem item)
	{
		TreeNode node = (TreeNode) m_root.find(item);
		deleteEventSource(node);
		node.destroy();
	}
	private void deleteEventSource(TreeNode node)
	{
		m_eventSourceModel.removeElement(node);		

		Enumeration e = node.children();
		if (e != null) while (e.hasMoreElements()) deleteEventSource((TreeNode) e.nextElement());
	}
	private Object getAssociatedObject(TreeNode node)
	{
		return m_job.getItem(getTreeItem(node));
	}
	private TreeItem getTreeItem(TreeNode node)
	{
		return node.getTreeItem(m_root);
	}
	public Object getItem(TreeItem item)
	{
		return m_job.getItem(item);
	}
	public void valueChanged(TreeSelectionEvent e)
	{
		TreeNode sel = (TreeNode) e.getPath().getLastPathComponent();
		TreeAdaptor a = sel.getTreeAdaptor();
		m_app.setCurrentSelection(a);
	}
	/**
	 * Loads a class into the Job (either local or remote)
	 */
	public void loadClass(String name)
	{
		try
		{
			m_job.loadObject(name);
			// if successful...
			m_changes++;
			setCommandChanged();
		}
		catch (jas2.job.LoaderException e)
		{
			// before throwing an exception, we expect loadObject() to do all cleanup for us
			m_app.error("Could not load class", e);
		}
	}
	boolean isLoaded(String className)
	{
		return m_job.isLoaded(className);
	}
	private String m_name;
	private Job m_job;
	private transient TreeModel m_treeModel = null;
	private transient DefaultTreeCellRenderer m_treeRenderer;
	private transient ListCellRenderer m_listRenderer;
	private transient String m_filePath = null;
	private transient int m_changes = 0;
	private transient boolean m_connected = false;
	private transient TreeNode m_root;
	private transient JASJobCommandProcessor m_command;
	private transient StoppableExecutionMonitor m_stopMonitor;
	private transient JavaAnalysisStudio m_app = JavaAnalysisStudio.getApp();
	private transient ComboModel m_eventSourceModel = new ComboModel();
	private transient Vector m_plugins = new Vector();
	private transient Vector m_pluginMenus = new Vector();
	private transient PluginContext m_pluginContext = new JobPluginContext(m_app);
	private transient JTree m_tree;
	private transient JComboBox m_combo;

	private class JobPluginContext extends JASPluginContext implements PluginContext
	{
		JobPluginContext(JavaAnalysisStudio app)
		{
			super(app);
		}
		public void addMenu(JMenu menu)
		{
			super.addMenu(menu);
			m_pluginMenus.addElement(menu);
		}
		public void removeMenu(JMenu menu)
		{
			super.removeMenu(menu);
			m_pluginMenus.removeElement(menu);
		}
		public void addRunListener(RunListener listener)
		{
			m_job.addRunListener(listener);
		}
		public void removeRunListener(RunListener listener)
		{
			m_job.removeRunListener(listener);
		}

		public hepjas.analysis.EventData getCurrentEvent() throws NoEventAvailable
		{
			return m_job.getEvent();
		}
		public void sendJob(Object message)
		{
			m_job.receiveMessage(message);
		}
		private Vector m_runListeners = new Vector();
	}
	
	private class ComboModel extends DefaultComboBoxModel
	{
		public void addElement(TreeNode node)
		{
			int index = getIndexOf(node);
			if (index < 0) super.addElement(node);
			
			if ((node.getFlags() & Job.CURRENTEVENTSOURCE) != 0 &&
				getSelectedItem() != node) 
			{
				setSelectedItem(node);
			}
		}
	}
	/**
	 * The JASJob contains a collection of TreeNodes that are mirrors of the 
	 * nodes in the Job. JASJob.TreeNodes however implement TreeAdaptorSite which
	 * makes it possible to add popup menus and other GUI functionality to them.
	 * 
	 * Currently the TreeNodes contain a lot of information which duplicates
	 * the information in the Job tree. It might be possible to merge these two trees,
	 * (when running locally), by having a tree factory which would create different
	 * types of nodes in the Job structure depending whether running locally or remotely.
	 * In this case we could make an extended version of SimpleNode which also implements
	 * TreeAdaptorSite. This is currently left as a future improvement. One complication
	 * would be that all updates to the tree in JASJob are done from the event dispatch thread,
	 * while the tree in AbstractJob can be updated from other threads.
	 * 
	 */
	class TreeNode extends OrderedNode implements TreeAdaptorSite, Serializable
	{
		TreeNode(String name, String type)
		{
			super(name,type,0,JASJob.this);
		}
		TreeNode(TreeNode parent, String name, String type, int flags)
		{
			this(parent,name,type,flags,null);
		}
		private TreeNode(TreeNode parent, String name, String type, int flags, Object object)
		{
			super(parent,name,type,flags,object);
		}
		protected SimpleNode createNode(SimpleNode parent, String name, String type, int flags, Object object)
		{
			return new TreeNode((TreeNode) parent,name,type,flags,object);
		}
		/*
		 * This routine attempts to load and return a command processor
		 * suitable for this type of object
		 */
		public TreeAdaptor getTreeAdaptor()
		{
			if (adaptor == null && getType() != null)
			{
				try
				{
					String s = getType();
					if (s.indexOf(".") < 0) s = "jas2.swingstudio.adaptor."+s.substring(0,1).toUpperCase()+s.substring(1)+"TreeAdaptor";
					else s += "TreeAdaptor";
                                        // S.Chekanov System.out.println(s);
					Class c = Class.forName(s);
					adaptor = (TreeAdaptor) c.newInstance();
					adaptor.setAdaptorSite(this);
				}
				catch (ClassNotFoundException x)
				{
					adaptor = null;
				}
				catch (InstantiationException x)
				{
					adaptor = null;
				}
				catch (IllegalAccessException x)
				{
					adaptor = null;
				}
			}
			return adaptor;
		}
		void update()
		{
			int flags = getFlags();
			if ((flags & Job.SHOWNOW) != 0 && adaptor == null) getTreeAdaptor();
			if (adaptor != null) adaptor.updateNode(flags);
		}
		public Object getAssociatedObject()
		{
			return getObject();
		}
		public PluginContext getPluginContext()
		{
			return m_pluginContext;
		}
		public TreeItem getTreeItem()
		{
			return JASJob.this.getTreeItem(this);
		}
		// Who uses this??
		public Job getJob()
		{
			return m_job;
		}
		/**
		 * @return the icon for this node, or null if no custom icon exists
		 */
		Icon getIcon()
		{
			try
			{
				return new JASIcon(this,"images/"+this.getType()+".gif");
			}
			catch (ImageException x)
			{
				return null;
			}
		}
		// Who uses this??
		public TreeAdaptorSite getParentAdaptor()
		{
			return (TreeNode) getParent();
		}
		/**
		 * Support for editing (renaming)
		 */
		boolean isEditable()
		{
			TreeAdaptor ta = getTreeAdaptor();
			return ta == null ? false : ta.isEditable();
		}
		JPopupMenu getPopupMenu()
		{
			TreeAdaptor ta = getTreeAdaptor();
			return ta == null ? null : ta.getPopupMenu();
		}
		public void startEditing()
		{
			int n;
			SimpleNode node = this;
			for (n=0; node != null; n++) node=node.getParent();
			SimpleNode[] nodes = new SimpleNode[n];
			node = this;
			for (int i=1; node != null; i++) { nodes[n-i] = node; node=node.getParent(); }
			m_tree.startEditingAtPath(new TreePath(nodes));
		}
		private TreeAdaptor adaptor;
	}
	private static class JASTreeCellRenderer extends DefaultTreeCellRenderer
	{
		 public Component getTreeCellRendererComponent(
			 JTree tree,
			 Object value,
			 boolean selected,
			 boolean expanded,
			 boolean leaf,
			 int row,
			 boolean hasFocus)
		 {
			 super.getTreeCellRendererComponent(tree,value,selected,expanded,leaf,row,hasFocus);
				
			 Icon icon = ((TreeNode) value).getIcon();
			 if (icon != null) setIcon(icon);			 
			 return this;
		 }
	}
	private class JASTreeCellEditor extends DefaultTreeCellEditor
	{
		JASTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
		{
			super(tree,renderer);
		}
		protected void determineOffset(JTree tree, Object value,
					   boolean isSelected, boolean expanded,
					   boolean leaf, int row) 
		{
			editingIcon = ((TreeNode) value).getIcon();
			if (editingIcon == null) 
			{
				super.determineOffset(tree,value,isSelected,expanded,leaf,row); 
			}
			else
			{
				offset = renderer.getIconTextGap() +
					     editingIcon.getIconWidth();
			}
		}
		/**
		 * This is invoked if a TreeCellEditor is not supplied in the constructor.
		 * It returns a TextField editor.
		 */
		protected TreeCellEditor createTreeCellEditor() 
		{
			javax.swing.border.Border aBorder = UIManager.getBorder("Tree.editorBorder");
			DefaultCellEditor editor = new DefaultCellEditor(new DefaultTextField(aBorder)) 
			{
				public boolean shouldSelectCell(EventObject event) 
				{
					boolean retValue = super.shouldSelectCell(event);
					getComponent().requestFocus();
					return retValue;
				}
				public boolean stopCellEditing()
				{
					TreeNode node = (TreeNode) m_tree.getEditingPath().getLastPathComponent();
					if (node.getTreeAdaptor().stopEditing(getCellEditorValue().toString()))
					{
						return super.stopCellEditing();
					}
					return false;
				}		
			};
			editor.setClickCountToStart(1);
			return editor;
		}
	}
	private static class JASListCellRenderer extends DefaultListCellRenderer 
	{
		 public Component getListCellRendererComponent(
			 JList list,                                              
			 Object value,
             int index,
             boolean isSelected,
             boolean cellHasFocus)
		 {
			 super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
				
			 if (value != null) setIcon(((TreeNode) value).getIcon());
			 return this;
		 }
	}
	class JASJobCommandProcessor extends JASCommandProcessor
	{
		public void onOpenDataset()
		{
			if (m_job instanceof JASJobAdaptor) // remote JOb
			{
				new JASWizard(m_app.getFrame(), "New Dataset",new OpenDataSetPage((JASJobAdaptor) m_job,m_app));
			}
			else
			{
				new JASWizard(m_app.getFrame(), "New Dataset",new LocalJobPage(new JASLocalJobBuilder((LocalJob) m_job)));
			}
		}
		public void onLoadProgram()
		{
			LoadDialog dlg = new LoadDialog(m_app.getFrame());
			if (dlg.doModal())
				loadClass(dlg.getClassName());
			// loadClass() should handle all exceptions
		}
		public void onLoadPlugin()
		{
			LoadDialog dlg = new LoadPluginDialog(m_app.getFrame());
			if (dlg.doModal())
			{
				try
				{
					loadPlugin(dlg.getClassName());
				}
				catch (Exception e)
				{
					m_app.error("Could not load plugin",e);
				}
			}
		}
		public void onUnloadAllPrograms() throws RemoteException
		{
			m_job.unloadAll();
			m_changes++;
			setChanged();
		}
		public void onSaveJob() throws IOException, JASException
		{
			if (m_app.jobSavingDisabled) throw new NotImplemented("Saving and restoring jobs");
			m_app.saveJob();
		}
	//	public void enableSaveJob(JASState state)
	//	{
	//		state.setEnabled(m_changes > 0);
	//	}
	// Right now too difficult to include all the ways the job could have changed
	// e.g. adding a function to a plot
		public void onSaveJobAs() throws IOException, JASException
		{
			if (m_app.jobSavingDisabled) throw new NotImplemented("Saving and restoring jobs");
			m_app.saveJobAs();
		}
		public void onCloseCurrentJob()
		{
			m_app.closeCurrentJob();
			m_app.setToEmptyJob();
		}
		public void enableDisconnect(JASState state)
		{
			state.setEnabled(m_job instanceof JASJobAdaptor);
		}
		public void onDisconnect() throws RemoteException
		{
			((JASJobAdaptor) m_job).disconnect();
			m_job = null;
			m_app.closeCurrentJob();
			m_app.setToEmptyJob();
		}
		public void onKill() throws RemoteException
		{
			m_job.kill();
		}
		public void onRewind() throws RemoteException
		{
			m_job.rewind();
		}
		public void onStep() throws RemoteException
		{
			m_job.go(1);
		}
		public void onGo() throws RemoteException
		{
			m_job.go(0);
		}
		//public void enableRun(JASState state)
		//{
		//	state.setEnabled(m_job.isRunnable());
		//}
		public void enableGo(JASState state)
		{
			state.setEnabled(m_job.isRunnable() && !m_job.isAtEndOfData());
		}
		public void enableStep(JASState state)
		{
			state.setEnabled(m_job.isRunnable() && !m_job.isAtEndOfData());
		}
		public void enableRewind(JASState state)
		{
			state.setEnabled(!m_job.isAtStartOfData());
		}
		public void enableKill(JASState state)
		{
			state.setEnabled(m_job.isRunning());
		}
		public void enablePersistent(JASCheckboxState state)
		{
			state.setCheckbox(m_job.isPersistent());
			state.setEnabled(true);
		}
		public void onPersistent(boolean persist) throws RemoteException
		{
			m_job.setPersistent(persist);
			m_changes++;
			setChanged();
		}
		protected void setChanged()
		{
			super.setChanged();
			if (m_stopMonitor != null)
				m_stopMonitor.doStopEnabling();
			// have the stop button enable with the rest
		}
		void forceSetChanged()
		{
			super.setChanged();
		}
	}
}


 

Ads help maintain this website.