1    	package acl2s.plugin;
2    	
3    	import java.io.BufferedReader;
4    	import java.io.File;
5    	import java.io.FileReader;
6    	import java.io.InputStream;
7    	import java.lang.reflect.Method;
8    	import java.util.ArrayList;
9    	import java.util.HashSet;
10   	
11   	import org.eclipse.core.resources.IFile;
12   	import org.eclipse.core.resources.ResourcesPlugin;
13   	import org.eclipse.core.runtime.CoreException;
14   	import org.eclipse.core.runtime.FileLocator;
15   	import org.eclipse.core.runtime.IExtension;
16   	import org.eclipse.core.runtime.IPath;
17   	import org.eclipse.core.runtime.IStatus;
18   	import org.eclipse.core.runtime.Platform;
19   	import org.eclipse.core.runtime.Status;
20   	import org.eclipse.jface.resource.ImageRegistry;
21   	import org.eclipse.swt.SWT;
22   	import org.eclipse.swt.graphics.Image;
23   	import org.eclipse.swt.widgets.Display;
24   	import org.eclipse.swt.widgets.MessageBox;
25   	import org.eclipse.swt.widgets.Shell;
26   	import org.eclipse.ui.IEditorInput;
27   	import org.eclipse.ui.IEditorPart;
28   	import org.eclipse.ui.IEditorReference;
29   	import org.eclipse.ui.IFileEditorInput;
30   	import org.eclipse.ui.IPathEditorInput;
31   	import org.eclipse.ui.IURIEditorInput;
32   	import org.eclipse.ui.IWorkbench;
33   	import org.eclipse.ui.IWorkbenchListener;
34   	import org.eclipse.ui.IWorkbenchPage;
35   	import org.eclipse.ui.IWorkbenchPart;
36   	import org.eclipse.ui.IWorkbenchWindow;
37   	import org.eclipse.ui.PartInitException;
38   	import org.eclipse.ui.PlatformUI;
39   	import org.eclipse.ui.part.FileEditorInput;
40   	import org.eclipse.ui.plugin.AbstractUIPlugin;
41   	import org.osgi.framework.Bundle;
42   	import org.osgi.framework.BundleContext;
43   	import org.osgi.framework.Version;
44   	import org.peterd.util.Pingable;
45   	import org.peterd.util.io.EmptyInputStream;
46   	
47   	import acl2s.lib.session.IBaseConfig;
48   	import acl2s.lib.session.ISessionConfig;
49   	import acl2s.lib.session.MyBaseConfig;
50   	import acl2s.lib.session.Versions;
51   	import acl2s.plugin.certify.CertifyAcl2sSystemConsole;
52   	import acl2s.plugin.dialogs.RemoveOldStuffDialog;
53   	import acl2s.plugin.editors.A2sEditor;
54   	import acl2s.plugin.editors.MyDocumentProvider;
55   	import acl2s.plugin.editors.lisp.LispDocumentProvider;
56   	import acl2s.plugin.editors.lisp.LispEditor;
57   	import acl2s.plugin.editors.session.SessionDocumentProvider;
58   	import acl2s.plugin.editors.session.SessionEditor;
59   	import acl2s.plugin.graphics.Acl2sGraphics;
60   	import acl2s.plugin.misc.DisplayAsyncRunnable;
61   	import acl2s.plugin.misc.PathEditorInput;
62   	import acl2s.plugin.prefs.Acl2Prefs;
63   	import acl2s.uilib_driver.IAcl2sGraphics;
64   	import acl2s.uilib_driver.IAcl2sGraphicsFactory;
65   	import acl2s.uilib_driver.IAcl2sUiLibDriver;
66   	import acl2s.uilib_driver.IAcl2sUiLibErrorHandler;
67   	import acl2s.uilib_driver.IAcl2sUiLibThreadManager;
68   	
69   	/**
70   	 * The main plugin class to be used in the desktop.
71   	 */
72   	public class Acl2sPlugin extends AbstractUIPlugin
73   	implements IAcl2sUiLibDriver, IAcl2sUiLibThreadManager, IAcl2sUiLibErrorHandler, IAcl2sGraphicsFactory, IWorkbenchListener {
74   		// PLUGIN STUFF
75   		public static final boolean DEBUG = true;
76   		
77   		protected static final String pluginIdString = "acl2s";
78   		public static final String id = pluginIdString;
79   		
80   		private static Acl2sPlugin plugin = null;
81   		
82   		protected static Display display = null;
83   		
84   		protected static MyBaseConfig baseConfig = null;
85   		
86   		protected static File hooksBundleRoot = null;
87   		
88   		protected static File[] modedirRoots = null;
89   		
90   		protected static File acl2sBundleRoot = null;
91   		
92   		protected static File supportHome = null;
93   		
94   		public Acl2sPlugin() {
95   			//if (plugin != null) throw new IllegalStateException();
96   			plugin = this;
97   			display = Display.getCurrent();
98   			if (display == null) {
99   				display = Display.getDefault();
100  				if (display == null) {
101  					throw new IllegalStateException("Attempt to get display when there is none.");
102  				}
103  			}
104  		}
105  	
106  		public static Acl2sPlugin getDefault() {
107  			return plugin;
108  		}
109  	
110  		public static Display getDisplay() {
111  			return display;
112  		}
113  		
114  		public static Shell getShell() {
115  			Display display = getDisplay();
116  			Shell shell = display.getActiveShell();
117  			if (shell == null) {
118  				IWorkbenchWindow w = getWorkbenchWindow();
119  				if (w != null) {
120  					shell = w.getShell();
121  					if (shell == null) {
122  						Shell[] shells = display.getShells();
123  						if (shells.length > 0) {
124  							shell = shells[0];
125  						}
126  					}
127  				}
128  			}
129  			return shell;
130  		}
131  			
132  		public static IWorkbenchWindow getWorkbenchWindow() {
133  			IWorkbenchWindow w = plugin.getWorkbench().getActiveWorkbenchWindow();
134  			if (w != null) {
135  				return w;
136  			}
137  			IWorkbenchWindow[] ws = plugin.getWorkbench().getWorkbenchWindows();
138  			if (ws == null || ws.length == 0) {
139  				return null;
140  			} else if (ws.length == 1) {
141  				return ws[0];
142  			} else { // ws.length > 1
143  				Shell sh = getDisplay().getActiveShell();
144  				if (sh != null) {
145  					for (int i = 0; i < ws.length; i++) {
146  						if (ws[i].getShell() == sh) {
147  							return ws[i];
148  						}
149  					}
150  				}
151  				// else 
152  				return ws[0];
153  			}
154  		}
155  		
156  		public static Image getImage(String path) {
157  			ImageRegistry reg = plugin.getImageRegistry();
158  			Image img = reg.get(path);
159  			if (img == null) {
160  				img = AbstractUIPlugin.imageDescriptorFromPlugin(id, path).createImage();
161  				reg.put(path, img);
162  			}
163  			return img;
164  		}
165  		
166  		public void start(BundleContext context) throws Exception {
167  			try {
168  				super.start(context);
169  				createDocumentProviders();
170  				Bundle hooksBundle = Platform.getBundle("acl2s_hooks");
171  				if (hooksBundle == null) {
172  					logError("Could not find acl2s_hooks bundle.", null);
173  				} else {
Event set_static: The static field "hooksBundleRoot" is being set in a non-static method.
174  					hooksBundleRoot = new File(FileLocator.resolve(hooksBundle.getEntry("/")).getPath());
175  				}
176  				acl2sBundleRoot = new File(FileLocator.resolve(context.getBundle().getEntry("/")).getPath());
177  				supportHome = new File(acl2sBundleRoot, "procwrap");
178  	
179  				ArrayList<File> modedirsFound = new ArrayList<File>();
180  				IExtension[] modedirExts = Platform.getExtensionRegistry().getExtensionPoint("acl2s.modedir").getExtensions();
181  				for (IExtension ext : modedirExts) {
182  					Bundle b = Platform.getBundle(ext.getContributor().getName());
183  					if (b != null) {
184  						modedirsFound.add(new File(FileLocator.resolve(b.getEntry("/")).getPath()));
185  					}
186  				}
187  				modedirRoots = modedirsFound.toArray(new File[modedirsFound.size()]);
188  	            
189  				//harshrc:: 3rd argument is where the cmdLine i.e acl2 image path is decided. 
190  				baseConfig = new MyBaseConfig(this, hooksBundleRoot, Acl2Prefs.getAcl2HomePref(null), modedirRoots);
191  				
192  				// Check if saved ground-zero theory file does not exist or is out of date - if yes, launch an indexer.
193  				// if no, load the ground-zero theory into IDocument null.
194  				File groundzero = new File("groundzero.def");
195  				Thread t = null;
196  				if (groundzero.exists()) {
197  					BufferedReader stream = new BufferedReader(new FileReader(groundzero));
198  					String acl2imagepath = stream.readLine();
199  					if (baseConfig.getCmdLine()[0].equals(acl2imagepath)) {
200  						t = new GroundZeroIndexer(stream);
201  					} else {
202  						t = new GroundZeroIndexer(baseConfig);
203  						stream.close();
204  					}
205  					
206  				} else {
207  					t = new GroundZeroIndexer(baseConfig);
208  				}
209  				t.start();
210  				
211  				PlatformUI.getWorkbench().addWorkbenchListener(this);
212  			} catch (RuntimeException e) {
213  				error("Exception while initializing ACL2s plugin", e);
214  			} catch (Exception e) {
215  				error("Exception while initializing ACL2s plugin", e);
216  				throw e;
217  			}
218  		}
219  	
220  		
221  		protected HashSet<Runnable> shutdownHooks = new HashSet<Runnable>();
222  		
223  		public void stop(BundleContext context) throws Exception {
224  			try {
225  				
226  			} finally {
227  				//plugin = null;
228  				super.stop(context);
229  			}
230  		}
231  		
232  		public void addShutdownHook(Runnable r) {
233  			shutdownHooks.add(r);
234  		}
235  		
236  		public void removeShutdownHook(Runnable r) {
237  			shutdownHooks.remove(r);
238  		}
239  		
240  		
241  		// PAIRING IA2sDocuments and IA2sPeerDocuments
242  		
243  		/*
244  		 * Files will look like:
245  		 *   Foo.lisp
246  		 *   Foo.acl2
247  		 *   Foo.lisp.a2s
248  		 *   Foo.acl2.a2s
249  		 */
250  	
251  		public static synchronized void createSessionFileFor(IFileEditorInput i) throws CoreException {
252  			IPath p = i.getFile().getFullPath();
253  			String ext = p.getFileExtension();
254  			if (ext.equalsIgnoreCase("lisp") || ext.equalsIgnoreCase("lsp")) {
255  				p = p.addFileExtension("a2s");
256  				IFile a2sFile = ResourcesPlugin.getWorkspace().getRoot().getFile(p);
257  				if (!a2sFile.exists()) {
258  					InputStream stream = new EmptyInputStream();
259  					a2sFile.create(stream, true, null);
260  				}
261  			} else {
262  				String msg = "Unrecognized file extension for acl2 source document.";
263  				throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
264  			}
265  		}
266  		
267  		
268  		protected HashSet<IPath> workspacePaths = new HashSet<IPath>();
269  		protected HashSet<IPath> localPaths = new HashSet<IPath>();
270  		//protected HashSet<URI> uriPaths = new HashSet<URI>();		
271  		
272  		
273  		public static synchronized void newA2sEditorInput(IEditorInput i) throws CoreException {
274  			if (i instanceof IFileEditorInput) {
275  				IPath p = ((IFileEditorInput)i).getFile().getFullPath();
276  				String ext = p.getFileExtension();
277  				if (ext.equalsIgnoreCase("a2s")) {
278  					plugin.checkWorkspacePath(p.removeFileExtension());
279  				} else {
280  					/* OK
281  					String msg = "Unrecognized file extension for acl2 session document.";
282  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
283  					*/
284  				}
285  			} else if (i instanceof IPathEditorInput) {
286  				IPath p = ((IPathEditorInput)i).getPath();
287  				String ext = p.getFileExtension();
288  				if (ext.equalsIgnoreCase("a2s")) {
289  					plugin.checkLocalPath(p.removeFileExtension());
290  				} else {
291  					/* OK
292  					String msg = "Unrecognized file extension for acl2 session document.";
293  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
294  					*/
295  				}
296  			} else if (i instanceof IURIEditorInput) {
297  				/* For now, always stand-alone
298  				URI uri = ((IURIEditorInput)i).getURI();
299  				String path = uri.getPath();
300  				int dotIdx = path.lastIndexOf('.');
301  				if (dotIdx < 0) {
302  					String msg = "Empty file extension not supported for acl2 session document.";
303  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
304  				}
305  				String ext = path.substring(dotIdx + 1);
306  				if (ext.equalsIgnoreCase("a2s")) {
307  					try {
308  						plugin.checkURIPath(new URI(uri.getScheme(),
309  												    uri.getAuthority(),
310  												    path.substring(0, dotIdx),
311  												    uri.getQuery(),
312  												    uri.getFragment()));
313  					} catch (URISyntaxException e) {
314  						String msg = "Problem processing URI (shouldn't happen).";
315  						throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,e));
316  					}
317  				} else {
318  					String msg = "Unrecognized file extension for acl2 session document.";
319  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
320  				}
321  				*/
322  			} else {
323  				String msg = "Editor input type not supported: " + i.getClass().getName();
324  				throw new CoreException(new Status(Status.ERROR,id,Status.OK,msg,null));
325  			}
326  		}
327  	
328  		public static synchronized void newPeerEditorInput(IEditorInput i) throws CoreException {
329  			if (i instanceof IFileEditorInput) {
330  				IPath p = ((IFileEditorInput)i).getFile().getFullPath();
331  				String ext = p.getFileExtension();
332  				if (ext.equalsIgnoreCase("lisp") || ext.equalsIgnoreCase("lsp")) {
333  					plugin.checkWorkspacePath(p);
334  				} else {
335  					/* OK
336  					String msg = "Unrecognized file extension for acl2 source document.";
337  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
338  					*/
339  				}
340  			} else if (i instanceof IPathEditorInput) {
341  				IPath p = ((IPathEditorInput)i).getPath();
342  				String ext = p.getFileExtension();
343  				if (ext.equalsIgnoreCase("lisp") || ext.equalsIgnoreCase("lsp")) {
344  					plugin.checkLocalPath(p);
345  				} else {
346  					/* OK
347  					String msg = "Unrecognized file extension for acl2 source document.";
348  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
349  					*/
350  				}
351  			} else if (i instanceof IURIEditorInput) {
352  				/* For now, always stand-alone
353  				URI uri = ((IURIEditorInput)i).getURI();
354  				String path = uri.getPath();
355  				int dotIdx = path.lastIndexOf('.');
356  				if (dotIdx < 0) {
357  					String msg = "Empty file extension not supported for acl2 source document.";
358  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
359  				}
360  				String ext = path.substring(dotIdx + 1);
361  				if (ext.equalsIgnoreCase("lisp") || ext.equalsIgnoreCase("lsp")) {
362  					plugin.checkURIPath(uri);
363  				} else {
364  					String msg = "Unrecognized file extension for acl2 source document.";
365  					throw new CoreException(new Status(IStatus.ERROR,id,IStatus.OK,msg,null));
366  				}
367  				*/
368  			} else {
369  				String msg = "Editor input type not supported: " + i.getClass().getName();
370  				throw new CoreException(new Status(Status.ERROR,id,Status.OK,msg,null));
371  			}
372  		}
373  		
374  		private void checkWorkspacePath(IPath basePath) throws CoreException {
375  			if (workspacePaths.contains(basePath)) return;
376  			
377  			IPath a2sPath = basePath.addFileExtension("a2s");
378  			
379  			IFile a2sFile = ResourcesPlugin.getWorkspace().getRoot().getFile(a2sPath);
380  			if (!a2sFile.exists()) return; // standalone lisp file
381  	
382  			IPath peerPath = basePath;
383  			String peerExt = basePath.getFileExtension();
384  			if (peerExt == null) return; // standalone .a2s file
385  			
386  			if (peerExt.equalsIgnoreCase("lisp") || peerExt.equalsIgnoreCase("lsp")) {
387  				IFileEditorInput a2sInput = new FileEditorInput(a2sFile);
388  				ISessionDocument a2sDoc = connectA2s(a2sInput);
389  			
390  				IFile peerFile = ResourcesPlugin.getWorkspace().getRoot().getFile(peerPath);
391  				IFileEditorInput peerInput = new FileEditorInput(peerFile);
392  				IPeerDocument peerDoc = connectPeer(peerInput);
393  	
394  				peerDoc.setA2s(a2sDoc);
395  				a2sDoc.setPeer(peerDoc);
396  			} else {
397  				throw new CoreException(new Status(Status.ERROR,id,Status.OK,"Unsupported prefix to .a2s",null));
398  			}
399  			
400  			workspacePaths.add(basePath);
401  		}
402  	
403  		private void checkLocalPath(IPath basePath) throws CoreException {
404  			if (localPaths.contains(basePath)) return;
405  			
406  			IPath a2sPath = basePath.addFileExtension("a2s");
407  			
408  			File a2sFile = a2sPath.toFile();
409  			if (!a2sFile.exists()) return; // standalone lisp file
410  	
411  			IPath peerPath = basePath;
412  			String peerExt = basePath.getFileExtension();
413  			if (peerExt == null) return; // standalone .a2s file
414  			
415  			if (peerExt.equalsIgnoreCase("lisp") || peerExt.equalsIgnoreCase("lsp")) {
416  				IPathEditorInput a2sInput = new PathEditorInput(a2sPath);
417  				ISessionDocument a2sDoc = connectA2s(a2sInput);
418  			
419  				//File peerFile = peerPath.toFile();
420  				IPathEditorInput peerInput = new PathEditorInput(peerPath);
421  				IPeerDocument peerDoc = connectPeer(peerInput);
422  	
423  				peerDoc.setA2s(a2sDoc);
424  				a2sDoc.setPeer(peerDoc);
425  			} else {
426  				throw new CoreException(new Status(Status.ERROR,id,Status.OK,"Unsupported prefix to .a2s",null));
427  			}
428  			
429  			localPaths.add(basePath);
430  		}
431  	
432  		/*
433  		private void checkURIPath(URI basePath) throws CoreException {
434  			if (uriPaths.contains(basePath)) return;
435  			
436  			URI a2sPath = new URI(basePath.getScheme(),basePath.getAuthority(),
437  								  basePath.getPath().concat(".a2s"),
438  								  basePath.getQuery(),basePath.getFragment());
439  			
440  			//File a2sFile = a2sPath.toFile();
441  			//if (!a2sFile.exists()) return; // standalone lisp file
442  	
443  			URI peerPath = basePath;
444  			String p = basePath.getPath();
445  			int dotIdx = p.lastIndexOf('.');
446  			if (dotIdx < 0) return; // standalone .a2s file
447  			String peerExt = p.substring(dotIdx + 1);
448  			
449  			if (peerExt.equalsIgnoreCase("lisp") || peerExt.equalsIgnoreCase("lsp")) {
450  				IURIEditorInput a2sInput = new ???;
451  				ISessionDocument a2sDoc = connectA2s(a2sInput);
452  			
453  				//File peerFile = peerPath.toFile();
454  				IURIEditorInput peerInput = new ???;
455  				IPeerDocument peerDoc = connectPeer(peerInput);
456  	
457  				peerDoc.setA2s(a2sDoc);
458  				a2sDoc.setPeer(peerDoc);
459  			} else {
460  				throw new CoreException(new Status(Status.ERROR,id,Status.OK,"Unsupported prefix to .a2s",null));
461  			}
462  			
463  			uriPaths.add(basePath);
464  		}
465  	    */
466  	
467  		
468  		// DOCUMENT PROVIDERS
469  		
470  		ArrayList<MyDocumentProvider> providers = new ArrayList<MyDocumentProvider>();
471  		LispDocumentProvider ldp = null;
472  		SessionDocumentProvider adp = null;
473  	
474  		public static LispDocumentProvider getLispDocumentProvider() { return plugin.ldp; }
475  	
476  		public static SessionDocumentProvider getA2sDocumentProvider() { return plugin.adp; }
477  		
478  		private void createDocumentProviders() {
479  			ldp = new LispDocumentProvider();
480  			providers.add(ldp);
481  			adp = new SessionDocumentProvider();
482  			providers.add(adp);
483  		}
484  	
485  		private ISessionDocument connectA2s(IEditorInput input) throws CoreException {
486  			adp.connect(input);
487  			return (ISessionDocument) adp.getDocument(input);
488  		}
489  	
490  		private IPeerDocument connectPeer(IEditorInput peerInput) throws CoreException {
491  			ldp.connect(peerInput);
492  			return (IPeerDocument) ldp.getDocument(peerInput);
493  		}
494  	
495  		
496  		
497  		// LOGGING
498  		
499  		public static void logError(final String message, Throwable t) {
500  			final Status status = new Status(IStatus.ERROR,pluginIdString,IStatus.OK,message,t);
501  			plugin.getLog().log(status);
502  			
503  			popupMessage("Error signalled by ACL2s Plugin", message, SWT.ICON_ERROR | SWT.OK);
504  		}
505  		
506  		public static void logWarning(final String message, Throwable t) {
507  			final Status status = new Status(IStatus.WARNING,pluginIdString,IStatus.OK,message,t); 
508  			plugin.getLog().log(status);
509  			/*
510  			getDisplay().asyncExec( new Runnable() {
511  				public void run() {
512  					MessageBox mb = new MessageBox(getShell(),SWT.ICON_WARNING | SWT.OK);
513  					mb.setText("Warning reported by ACL2s Plugin");
514  					mb.setMessage(message);
515  					mb.open();
516  				}
517  			});
518  			//*/
519  		}
520  		
521  		public static void popupMessage(final Shell sh, final String title, final String message, final int swtIcon) {
522  			getDisplay().asyncExec( new Runnable() {
523  				public void run() {
524  					Shell sh2 = sh == null ? getShell() : sh;
525  					if (sh2 == null) {
526  						sh2 = new Shell(getDisplay());
527  						sh2.open();
528  						go(sh2);
529  						sh2.close();
530  					} else {
531  						go(sh2);
532  					}
533  				}
534  				
535  				void go(Shell sh2) {
536  					MessageBox mb = new MessageBox(sh2, swtIcon | SWT.OK);
537  					mb.setText(title);
538  					mb.setMessage(message);
539  					mb.open();
540  				}
541  			});
542  		}
543  	
544  		public static void popupMessage(final String title, final String message, final int swtIcon) {
545  			popupMessage(null, title, message, swtIcon);
546  		}
547  	
548  		
549  		/* =============== Collection of loops in connected documents =============== */
550  		public static synchronized void disconnectedFrom(IPeerDocument doc) {
551  			if (doc.getSession() == null) return;  // standalone lisp
552  			
553  			if (plugin.getDocumentRefCount(doc) == 1 &&
554  					plugin.getDocumentRefCount(doc.getSession()) == 1) {
555  				IPath p = doc.getOSPath()/*.removeFileExtension()*/;
556  				p = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(p).getFullPath();
557  				if (!plugin.workspacePaths.remove(p)) {
558  					logWarning("Documents already disconnected.", null);
559  				}
560  				/* TODO: work on
561  				if (plugin.adp.canSaveDocument(doc.getSession().getInput())) {
562  					plugin.adp.
563  					try {
564  						getWorkbenchWindow().getActivePage().openEditor(doc.getSession().getInput(),"acl2s.plugin.editors.a2s");
565  						return;
566  					} catch (PartInitException e) {
567  						logWarning("Couldn't create dirty editor in response to closing other one.",e);
568  					}
569  				}*/
570  				plugin.disconnectDocument(doc);
571  				plugin.disconnectDocument(doc.getSession());
572  				doc.getSession().dispose();
573  				doc.dispose();
574  			}
575  		}
576  	
577  		public static synchronized void disconnectedFrom(ISessionDocument doc) {
578  			if (doc.getPeer() == null) return;  // standalone a2s
579  			
580  			if (plugin.getDocumentRefCount(doc) == 1 &&
581  					plugin.getDocumentRefCount(doc.getPeer()) == 1) {
582  				IPath p = doc.getOSPath().removeFileExtension();  // fs path
583  				p = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(p).getFullPath(); // to ws path
584  				if (!plugin.workspacePaths.remove(p)) {
585  					logWarning("Documents already disconnected.", null);
586  				}
587  				plugin.disconnectDocument(doc);
588  				plugin.disconnectDocument(doc.getPeer());
589  				doc.getPeer().dispose();
590  				doc.dispose();
591  			}
592  		}
593  		
594  		private int getDocumentRefCount(IDocumentWithInput doc) {
595  			Object o = doc.getInput();
596  			int count = 0;
597  			for (MyDocumentProvider dp : providers) {
598  				count += dp.getElementRefCount(o);
599  			}
600  			return count;
601  		}
602  		
603  		private void disconnectDocument(IDocumentWithInput doc) {
604  			Object o = doc.getInput();
605  			for (MyDocumentProvider dp : providers) {
606  				dp.disconnect(o);
607  			}
608  		}
609  	
610  		public static boolean hasEditor(IDocumentWithInput doc) {
611  			int refs = plugin.getDocumentRefCount(doc);
612  			if (doc instanceof ISessionDocument) {
613  				if (((ISessionDocument)doc).getPeer() != null) refs--;
614  			} else if (doc instanceof IPeerDocument) {
615  				if (((IPeerDocument)doc).getSession() != null) refs--;
616  			}
617  			return refs > 0;
618  		}
619  		
620  		
621  		public static IPeerDocument getPeer(final ISessionDocument ses,
622  				                            final A2sEditor ctx,
623  				                            final boolean open,
624  				                            final boolean focus) {
625  			final IWorkbenchPage page = open ? ctx.getEditorSite().getPage() : null;
626  			
627  			IPeerDocument peerdoc = ses.getPeer();
628  			
629  			if (peerdoc != null) {
630  				try {
631  					IWorkbenchPage targetPage = page;
632  					IEditorPart old = targetPage.getActiveEditor();
633  					if (!hasEditor(peerdoc)) {
634  						// find editor of same type
635  						found: for (IWorkbenchPage p : page.getWorkbenchWindow().getPages()) {
636  							for (IEditorReference ref : p.getEditorReferences()) {
637  								if (ref.getId().equals(LispEditor.id)) {
638  									targetPage = p;
639  									old = targetPage.getActiveEditor();
640  									// activate it so that new one will be in same group
641  									IWorkbenchPart part = ref.getPart(true);
642  									if (part != null) targetPage.activate(part);
643  									break found;
644  								}
645  							}
646  						}
647  					}
648  					targetPage.openEditor(peerdoc.getInput(),LispEditor.id,focus);
649  					if (!focus) targetPage.activate(old);
650  				} catch (PartInitException e1) {
651  					Acl2sPlugin.logWarning("Error opening peer editor.", e1);
652  				}
653  			} else {
654  				ctx.errorMessageBox("Not supported", "This session has no corresponding lisp file.");
655  			}
656  			return peerdoc;
657  		}
658  		
659  		public static ISessionDocument getSession(final IPeerDocument peer,
660  				                                  final A2sEditor ctx,
661  				                                  final boolean open,
662  				                                  final boolean focus,
663  				                                  final boolean start) {
664  			final IWorkbenchPage page = open ? ctx.getEditorSite().getPage() : null;
665  		
666  			ISessionDocument sesdoc = tryOpenSession(peer,page,focus);
667  			
668  			if (sesdoc == null) {
669  				final Shell sh = ctx.getSite().getShell();
670  				final Display display = sh.getDisplay();
671  				MessageBox mb = new MessageBox(sh, SWT.ICON_INFORMATION | SWT.YES | SWT.NO | SWT.APPLICATION_MODAL);
672  				mb.setText("No associated session");
673  				mb.setMessage("This lisp file has no associated ACL2 session file.  Would you like to create one?");
674  				int v = mb.open();
675  				if (v != SWT.YES) return null;
676  				
677  				if (peer.getInput() instanceof IFileEditorInput) {
678  					display.asyncExec(new Runnable() {
679  						public void run() {
680  							try {
681  								createSessionFileFor((IFileEditorInput) peer.getInput());
682  								display.asyncExec(new Runnable() {
683  									public void run() {
684  										ISessionDocument sesdoc = tryOpenSession(peer,page,focus);
685  										if (sesdoc != null && start) sesdoc.restartSession(ctx);
686  									}});
687  							} catch (CoreException e1) {
688  								logWarning("Error creating session file", e1);
689  								String errMsg = "Error creating session file: " + e1.getMessage();
690  								ctx.errorMessageBox("Error creating session file", errMsg);
691  							}
692  						}
693  					});
694  				} else {
695  					String errMsg = "Associated session not supported for files outside of workspace.";
696  					ctx.errorMessageBox("Error creating session file", errMsg);
697  				}
698  				return null;
699  			} else {
700  				return sesdoc;
701  			}
702  		}
703  	
704  		public static ISessionDocument tryOpenSession(IPeerDocument peer, IWorkbenchPage page, boolean focus) {
705  			ISessionDocument sesdoc = tryGetSession(peer);
706  			if (sesdoc != null && page != null) {
707  				try {
708  					if (focus || !hasEditor(sesdoc)) {
709  						IWorkbenchPage targetPage = page;
710  						IEditorPart old = targetPage.getActiveEditor();
711  						if (!hasEditor(sesdoc)) {
712  							// find editor of same type
713  							found: for (IWorkbenchPage p : page.getWorkbenchWindow().getPages()) {
714  								for (IEditorReference ref : p.getEditorReferences()) {
715  									if (ref.getId().equals(SessionEditor.id)) {
716  										targetPage = p;
717  										old = targetPage.getActiveEditor();
718  										// activate it so that new one will be in same group
719  										IWorkbenchPart part = ref.getPart(true);
720  										if (part != null) targetPage.activate(part);
721  										break found;
722  									}
723  								}
724  							}
725  						}
726  						page.openEditor(sesdoc.getInput(),SessionEditor.id, focus);
727  						if (!focus) page.activate(old);
728  					}
729  				} catch (PartInitException e1) {
730  					logWarning("Error opening peer editor.", e1);
731  					// can't really recover, so return true anyway
732  				}
733  			}
734  			return sesdoc;
735  		}
736  	
737  		public static ISessionDocument tryGetSession(IPeerDocument peer) {
738  			ISessionDocument a2s = peer.getSession();
739  			
740  			if (a2s != null) return a2s;
741  			
742  			if (!(peer.getInput() instanceof IFileEditorInput)) return null;
743  			
744  			try {
745  				disconnectedFrom(peer);
746  				newPeerEditorInput(peer.getInput());
747  				a2s = peer.getSession();
748  			} catch (CoreException ce) {
749  				logWarning(ce.getMessage(),ce);
750  			}
751  			
752  			return a2s;
753  		}
754  	
755  		
756  		// As driver for acl2s-lib
757  		
758  		public IAcl2sUiLibErrorHandler getErrorHandler() {
759  			return this;
760  		}
761  	
762  		public IAcl2sUiLibThreadManager getThreadManager() {
763  			return this;
764  		}
765  		
766  		public IAcl2sGraphicsFactory getGraphicsFactory() {
767  			return this;
768  		}
769  	
770  		public void syncExec(Runnable runnable) {
771  			display.syncExec(runnable);
772  		}
773  	
774  		public void asyncExec(Runnable runnable) {
775  			display.asyncExec(runnable);
776  		}
777  	
778  		public void delayedExec(final Runnable r, final int millis, boolean allowThisThread) {
779  			if (allowThisThread && Thread.currentThread() == display.getThread()) {
780  				display.timerExec(millis, r); // not allowed to call from non-display thread!
781  			} else {
782  				new Thread() {
783  					public void run() {
784  						try { Thread.sleep(millis);	} catch (InterruptedException e) { }
785  						r.run();
786  					}
787  				}.start();
788  			}
789  		}
790  	
791  		public boolean inEventThread() {
792  			return Thread.currentThread() == display.getThread();
793  		}
794  	
795  		public IAcl2sGraphics createGraphics(Pingable[] p, File[] searchDirs) {
796  			return new Acl2sGraphics(display, p, searchDirs);
797  		}
798  	
799  		
800  		public String getExtraInstructionOutput() {
801  			if (SWT.MOD1 == SWT.COMMAND) {
802  				return " => Hold \"Command\" to follow hyperlinks and :DOCumentation references <=\n";
803  			} else {
804  				return " => Hold \"Ctrl\" to follow hyperlinks and :DOCumentation references <=\n";
805  			}
806  		}
807  	
808  		
809  		// called from session thread
810  		public void needsImage(ISessionConfig config) {
811  			popupMessage("ACL2 image error",
812  					"I was unable to start a session, either because I could not find an ACL2 image or the image I tried is incompatible." +
813  					"\n\nIf you installed the \"ACL2 Image\" feature (see Help | About Eclipse... | Installation Details), and it's version >= " +
814  					Versions.getAcl2MinVersionString() + " and <= " + Versions.getAcl2MaxVersionString() + 
815  					", and the image matches your OS/Arch, then you should contact the ACL2s implementors about this bug." +
816  					"\n\nIf you are attempting to use an external ACL2 build, make sure you specify the correct directory in Eclipse's Preferences dialog under ACL2s | ACL2, or " +
817  					"consult the ACL2s documentation for more info." +
818  					"",
819  					SWT.ICON_ERROR | SWT.WRAP);
820  		}
821  	
822  		// called from session thread
823  		public void needsHooksCertification(ISessionConfig config) {
824  			new DisplayAsyncRunnable() {
825  				protected void asyncRun() {
826  					CertifyAcl2sSystemConsole.ensureStarted(getShell(),
827  							"It appears I need to certify the ACL2s system books before a session will load properly.  If in doubt, allow this to proceed.",
828  							"It appears I am in the process of certifying the ACL2s system books.  See the \"Console\" view for output.  When that finishes, you should be able to start a session.");
829  				}
830  			}.run();
831  		}
832  	
833  		public void error(String message, Throwable t) {
834  			logError(message,t);
835  		}
836  	
837  		public void warning(String message, Throwable t) {
838  			logWarning(message,t);
839  		}
840  	
841  		public static File getAcl2sHooksDir() {
842  			return hooksBundleRoot;
843  		}
844  		
845  		public static File getAcl2sBundleDir() {
846  			return acl2sBundleRoot;
847  		}
848  		
849  		public static File getSessionSupportDir() {
850  			return supportHome;
851  		}
852  		
853  		public static IBaseConfig getBaseConfig() {
854  			if (inDisplayThread()) {
855  				baseConfig.setAcl2Home(Acl2Prefs.getAcl2HomePref(getShell()));
856  			}
857  			return baseConfig;
858  		}
859  	
860  		public static boolean inDisplayThread() {
861  			return Thread.currentThread() == display.getThread(); 
862  		}
863  		
864  		public void postShutdown(IWorkbench workbench) {
865  			if (SWT.getPlatform().equals("carbon")) {
866  				RemoveOldStuffDialog.go(null);
867  			}
868  		}
869  	
870  		public boolean preShutdown(IWorkbench workbench, boolean forced) {
871  			for (Runnable r : shutdownHooks) {
872  				r.run();
873  			}
874  			if (!SWT.getPlatform().equals("carbon")) {
875  				RemoveOldStuffDialog.go(getShell());
876  			}
877  			return true;
878  		}
879  		
880  		public static File getAcl2sLibJar() {
881  			File jar = new File(acl2sBundleRoot, "acl2s-lib.jar");
882  			if (!jar.exists()) {
883  				File dir = new File(acl2sBundleRoot, "bin");
884  				return dir;
885  			}
886  			return jar;
887  		}
888  	
889  		static boolean noMoreWarnOutline3_3 = false;
890  		
891  		public static void maybeWarnOutline3_3() {
892  			if (noMoreWarnOutline3_3) return;
893  			try {
894  				Bundle b = ResourcesPlugin.getPlugin().getBundle();
895  				Method m = b.getClass().getMethod("getVersion");
896  				Version v = (Version) m.invoke(b);
897  				if (v.getMajor() == 3 && v.getMinor() == 3) {
898  					popupMessage("ACL2s Outline + Eclipse 3.3 problem",
899  							"Eclipse 3.3.x seems to generate errors when closing an Outline " +
900  							"view associated with an ACL2s source file or session file.  We " +
901  							"have been unable to fix it and don't plan to because (1) it " +
902  							"seems to be innocuous and (2) Eclipse 3.4 seems to have fixed " +
903  							"the problem.  Upgrade is encouraged.",
904  							SWT.ICON_INFORMATION);
905  				}
906  			} catch (Exception e) {
907  				// oh well
908  			}
909  			noMoreWarnOutline3_3 = true;
910  		}
911  		
912  		static HashSet<String> warnedUninstalledModes = new HashSet<String>();
913  		
914  		public static void maybeWarnUninstalledMode(String modeName) {
915  			if (!warnedUninstalledModes.contains(modeName)) {
916  				warnedUninstalledModes.add(modeName);
917  				Acl2sPlugin.popupMessage("Unrecognized session mode",
918  						"A file just opened uses a session mode, \"" +
919  						modeName + "\", which is not installed under this Eclipse/ACL2s.  To " +
920  						"be able to start a session in this mode, you will need to install that mode.  "+
921  						"(Ask the person you got the file from.)",
922  						SWT.ICON_WARNING);
923  			}
924  		}
925  	}