View Javadoc

1   /*
2    * $Id: OWLCore.java,v 1.50 2005/07/08 18:05:44 jlerner Exp $
3    *
4    * Copyright (c) 1999-2004, BBN Technologies, LLC.
5    * All rights reserved.
6    * http://www.daml.org/legal/opensource/bbn_license.html
7    */
8   
9   package com.bbn.swede.core;
10  import org.eclipse.core.resources.IFile;
11  import org.eclipse.core.resources.IFolder;
12  import org.eclipse.core.resources.IProject;
13  import org.eclipse.core.resources.IProjectDescription;
14  import org.eclipse.core.runtime.CoreException;
15  import org.eclipse.core.runtime.IConfigurationElement;
16  import org.eclipse.core.runtime.IExtension;
17  import org.eclipse.core.runtime.IExtensionPoint;
18  import org.eclipse.core.runtime.IExtensionRegistry;
19  import org.eclipse.core.runtime.IPath;
20  import org.eclipse.core.runtime.IProgressMonitor;
21  import org.eclipse.core.runtime.IStatus;
22  import org.eclipse.core.runtime.Path;
23  import org.eclipse.core.runtime.Platform;
24  import org.eclipse.core.runtime.Plugin;
25  import org.eclipse.core.runtime.QualifiedName;
26  import org.eclipse.core.runtime.Status;
27  import org.eclipse.jdt.core.IClasspathEntry;
28  import org.eclipse.jdt.core.IJavaProject;
29  import org.eclipse.jdt.core.JavaCore;
30  import org.eclipse.jface.dialogs.ErrorDialog;
31  import org.eclipse.swt.widgets.Shell;
32  import org.eclipse.ui.IEditorPart;
33  import org.eclipse.ui.IWorkbenchPage;
34  import org.eclipse.ui.IWorkbenchWindow;
35  import org.eclipse.ui.PartInitException;
36  import org.eclipse.ui.PlatformUI;
37  import org.eclipse.ui.ide.IDE;
38  import org.eclipse.ui.progress.UIJob;
39  
40  import com.hp.hpl.jena.rdf.arp.MalformedURIException;
41  import com.hp.hpl.jena.rdf.arp.URI;
42  
43  /***
44   * Plugin class for the core package.  Contains static convenience methods for
45   * working with OWL core classes.
46   * @author jlerner
47   * @author tself
48   */
49  public class OWLCore extends Plugin
50  {
51     /***
52      * Unique identifier of the SWeDE core plug-in.
53      */
54     public static final String PLUGIN_ID = "com.bbn.swede.core";
55     
56  
57     /***
58      * Preference controlling whether Ontology Libraries are updated
59      * automatically or manually.  This is a boolean preference.
60      */
61     public static final String PREFERENCE_LIBRARY_AUTOMATIC_UPDATE = "preference-library-automatic-update-schedule";
62     /***
63      * Preference controlling the frequency of automatic Ontology Library 
64      * updates.  This is a long preference, indicating the number of milliseconds
65      * between updates.
66      */
67     public static final String PREFERENCE_LIBRARY_UPDATE_SCHEDULE_FREQUENCY_STR = "preference-library-update-frequency";
68     /***
69      * Preference that stores the time of the most recent update to the Ontology
70      * Libraries.  This is a long preference, indicating the date of the last
71      * update in milliseconds.
72      */
73     public static final String PREFERENCE_LIBRARY_UPDATE_SCHEDULE_LAST_TIME_STR = "preference-library-update-last-time";
74     
75     /***
76      * Unique identifier for OWL syntax builder.  See com.bbn.swede.core.builders
77      * package for implementation.
78      */
79     public static final String SYNTAX_BUILDER = PLUGIN_ID + ".syntaxbuilder";
80     /***
81      * Unique identifier for OWL config builder.  See com.bbn.swede.core.builders
82      * and com.bbn.swede.core.config for implementation.
83      */
84     public static final String CONFIG_BUILDER = PLUGIN_ID + ".configbuilder";
85     /***
86      * Persistent property identifier for document URI.  This is included
87      * solely to support migration of pre-0.9 SWeDE projects, as this information
88      * is now stored in the .swprojectinfo file.
89      */
90     public static final QualifiedName PERSISTENT_URI =
91        new QualifiedName(PLUGIN_ID, "uri");
92     /***
93      * Persistent property identifier for external document source URL.  This is 
94      * included solely to support migration of pre-0.9 SWeDE projects, as this 
95      * information is now stored in the .swprojectinfo file.
96      */
97     public static final QualifiedName PERSISTENT_SOURCE_URL =
98        new QualifiedName(PLUGIN_ID, "sourceURL");
99     /***
100     * Unique identifier of the Semantic Web nature.
101     */
102    public static final String OWL_NATURE = "com.bbn.swede.core.owlnature";
103    /***
104     * Unique identifier for validation error markers.
105     */
106    public static final String MARKER_OWL_VALIDATOR = "com.bbn.swede.ui.validationmarker";
107    /***
108     * Unique identifier for Invalid XML markers.
109     */
110    public static final String MARKER_UNPARSEABLE = "com.bbn.swede.ui.unparseablemarker";
111 
112    private static Plugin _plugin;
113    private static ITraceWriter[] _traceWriters;
114 
115    /***
116     * Default constructor.
117     */
118    public OWLCore()
119    {
120       super();
121       _plugin = this;
122       setExtensions();
123    }
124    
125    
126    
127    private void setExtensions()
128    {
129       if (_traceWriters == null)
130       {
131          _traceWriters = new ITraceWriter[0];
132       }
133       IExtension[] extensions;
134       IExtensionRegistry registry = Platform.getExtensionRegistry();
135       IExtensionPoint point =
136          registry.getExtensionPoint(PLUGIN_ID + ".traceWriter");
137       if (point != null)
138       {
139          extensions = point.getExtensions();
140       }
141       else
142       {
143          extensions = new IExtension[0];
144          logWarning(getID(), "traceWriter extension point not found.", new Exception());
145       }
146       if (extensions.length == 0)
147       {
148          return;
149       }
150       OWLCore.trace("Trace", extensions.length + " total trace extensions.", false);
151       for (int i = 0; i < extensions.length; i++)
152       {
153          IConfigurationElement[] configElems =
154             extensions[i].getConfigurationElements();
155          for (int j = 0; j < configElems.length; j++)
156          {
157             if (!configElems[j].getName().equals("trace-writer"))
158             {
159                continue;
160             }
161             try
162             {
163                ITraceWriter writer =
164                   (ITraceWriter) configElems[j].createExecutableExtension(
165                      "class");
166                ITraceWriter[] newWriters = new ITraceWriter[_traceWriters.length + 1];
167                System.arraycopy(_traceWriters, 0, newWriters, 0, _traceWriters.length);
168                newWriters[newWriters.length - 1] = writer;
169                _traceWriters = newWriters;
170             }
171             catch (CoreException e)
172             {
173                OWLCore.logWarning(
174                   getID(),
175                   "Error creating trace-writer extension.",
176                   e);
177             }
178          }
179       }
180    }
181 
182    /***
183     * Provides access to the one and only instance of the plugin class.
184     * @return The singleton instance.
185     */
186    public static Plugin getPlugin()
187    {
188       return _plugin;
189    }
190 
191    /***
192     * Provides access to the one and only instance of OWLCore.
193     * @return The singleton instance.
194     */
195    public static OWLCore getOWLCore()
196    {
197       return (OWLCore) _plugin;
198    }
199 
200    /***
201     * Configures a project as an OWL project by adding the OWL nature and its
202     * associated builders.
203     * @param project The project
204     */
205    public static void addOWLNature(IProject project)
206    {
207       try
208       {
209          IProjectDescription description = project.getDescription();
210          if (!description.hasNature(OWLCore.OWL_NATURE))
211          {
212             String[] natures = description.getNatureIds();
213             String[] newNatures = new String[natures.length + 1];
214             System.arraycopy(natures, 0, newNatures, 0, natures.length);
215             newNatures[natures.length] = OWLCore.OWL_NATURE;
216             description.setNatureIds(newNatures);
217             project.setDescription(description, null);
218          }
219       }
220       catch (CoreException e)
221       {
222          logError(
223             getID(),
224             "The SWeDE nature could not be added to the project.",
225             e);
226       }
227    }
228 
229    private static final String S_JAVA_NATURE = "org.eclipse.jdt.core.javanature";
230    private static final String S_JRE_CONTAINER_PATH = "org.eclipse.jdt.launching.JRE_CONTAINER";
231 
232    /***
233     * Configures a project as Java project.  This includes adding the Java
234     * nature (and associated builders) as well as creating a classpath entry
235     * for the JRE container and creating a source folder.  The source folder
236     * is configured to ignore OWL documents so as not to create superfulous
237     * copies of *.owl files in the output directory when the compiler is run.
238     * @param project The project
239     */
240    public static void addJavaNature(IProject project)
241    {
242       try
243       {
244          IProjectDescription description = project.getDescription();
245          if (!description.hasNature(S_JAVA_NATURE))
246          {
247             String[] natures = description.getNatureIds();
248             String[] newNatures = new String[natures.length + 1];
249             System.arraycopy(natures, 0, newNatures, 0, natures.length);
250             newNatures[natures.length] = S_JAVA_NATURE;
251             description.setNatureIds(newNatures);
252             project.setDescription(description, null);
253          }
254          IJavaProject proj = JavaCore.create(project);
255          IClasspathEntry[] entries = proj.getRawClasspath();
256          IClasspathEntry jreContainer =
257             JavaCore.newContainerEntry(
258                new Path(S_JRE_CONTAINER_PATH));
259          IFolder srcFolder = project.getFolder("/src");
260          IClasspathEntry srcEntry = JavaCore.newSourceEntry(
261             srcFolder.getFullPath(),
262             new IPath[]{new Path("**/*.owl")}
263          );
264          int iSourceRoot = -1;
265          boolean bJRE = false;
266          boolean bSource = false;
267          for (int i = 0; i < entries.length; i++)
268          {
269             if (entries[i].equals(jreContainer))
270             {
271                bJRE = true;
272             }
273             if (entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE)
274             {
275                bSource = true;
276                if (entries[i].getPath().equals(project.getFullPath()))
277                {
278                   iSourceRoot = i;
279                }
280                else
281                {
282                   srcFolder = project.getFolder(entries[i].getPath().removeFirstSegments(1));
283                }
284             }
285          }
286          if (!srcFolder.exists())
287          {
288             srcFolder.create(false, true, null);
289          }
290          int iNewLength = entries.length;
291          if (!bJRE)
292          {
293             iNewLength++;
294          }
295          if (!bSource)
296          {
297             iSourceRoot = iNewLength;
298             iNewLength++;
299          }
300          IClasspathEntry[] newEntries;
301          if (iNewLength > entries.length)
302          {
303             newEntries = new IClasspathEntry[iNewLength];
304             System.arraycopy(entries, 0, newEntries, 0, entries.length);
305          }
306          else //JRE already there, need to fix source entry
307          {
308             newEntries = entries;
309          }
310          if (!bJRE)
311          {
312             newEntries[entries.length] = jreContainer;
313          }
314          if (iSourceRoot >= 0)
315          {
316             newEntries[iSourceRoot] = srcEntry;
317          }
318          proj.setRawClasspath(newEntries, null);
319       }
320       catch (CoreException e)
321       {
322          logError(
323             getID(),
324             "The Java nature could not be added to the project.",
325             e);
326       }
327    }
328 
329    /***
330     * Removes the OWL nature and the associated builders from a project.  If
331     * the project does not have the OWL nature, nothing happens.
332     * @param project The project
333     * @throws CoreException if there is a problem accessing the project
334     *                       descriptor to remove the nature.
335     */
336    public static void removeOWLNature(IProject project) throws CoreException
337    {
338       IProjectDescription description = project.getDescription();
339       String[] natureIDs = description.getNatureIds();
340       for (int i = 0; i < natureIDs.length; i++)
341       {
342          if (natureIDs[i].equals(OWL_NATURE))
343          {
344             String[] newNatureIDs = new String[natureIDs.length - 1];
345             System.arraycopy(natureIDs, 0, newNatureIDs, 0, i);
346             System.arraycopy(
347                natureIDs,
348                i + 1,
349                newNatureIDs,
350                i,
351                natureIDs.length - i - 1);
352             description.setNatureIds(newNatureIDs);
353             project.setDescription(description, null);
354             return;
355          }
356       }
357    }
358 
359    /***
360     * A job for opening an editor in a UI thread.
361     */
362    private class EditorJob extends UIJob
363    {
364       private IFile _file;
365       private IEditorPart _editor;
366       
367       /***
368        * Creates a job to open an OWL editor for a specific document.
369        * @param doc The document.
370        */
371       public EditorJob(IOWLDocument doc)
372       {
373          super("Opening editor for " + doc.getElementName());
374          _file = doc.getFile();
375       }
376       public IStatus runInUIThread(IProgressMonitor monitor)
377       {
378          IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
379          //window should be non-null this time since this is a UI thread
380          IWorkbenchPage page = window.getActivePage();
381          try
382          {
383             IDE.openEditor(page, _file, "com.bbn.swede.editor.OWLEditor", true);
384          }
385          catch (PartInitException e)
386          {
387             return new Status(IStatus.ERROR, OWLCore.getID(), 0, "Error opening editor", e);
388          }
389          return Status.OK_STATUS;
390       }
391       
392       /***
393        * Returns the editor opened or activated by the job.
394        * @return The editor, or <code>null</code> if the job has not yet
395        *         completed or an error occurred.
396        */
397       public IEditorPart getEditor()
398       {
399          return _editor;
400       }
401    }
402    /***
403     * Opens an OWL document for editing in the OWL editor.  If an editor is 
404     * already open for the specified document, it will be activated.  This
405     * method will automatically take steps to ensure that the editor is opened
406     * from a UI thread without deadlocking the current thread.
407     * @param doc The document to open in the editor
408     * @return The editor that was opened or activated.  This will always
409     *         be an instance of {@link com.bbn.swede.editor.OWLEditor}, but
410     *         is returned as an IEditorPart to avoid a circular dependency
411     *         between the SWeDE Core and SWeDE Editor plug-ins.
412     * @throws PartInitException if an error occurs opening the editor
413     */
414    public static IEditorPart openEditor(IOWLDocument doc) throws PartInitException
415    {
416       final IFile file = doc.getFile();
417       IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
418       if (window == null)
419       {
420          //The current thread is a non-UI thread, so open the editor in a UIJob
421          EditorJob job = getOWLCore().new EditorJob(doc);
422          job.setSystem(true);
423          job.schedule();
424          try
425          {
426             job.join();
427             IStatus status = job.getResult();
428             if (status != null && status.getSeverity() != IStatus.OK)
429             {
430                throw (PartInitException)status.getException();
431             }
432          }
433          catch (InterruptedException e)
434          {
435             OWLCore.logError(OWLCore.getID(), "Error opening editor for " + doc.getElementName() + " from UI job", e);
436          }
437          return job.getEditor();
438       }
439       else
440       {
441          //This is already a UI thread, so just open the editor directly to
442          //avoid deadlock
443          IWorkbenchPage page = window.getActivePage();
444          return IDE.openEditor(page, file, "com.bbn.swede.editor.OWLEditor", true);
445       }
446    }
447    
448    /***
449     * Removes the Java nature and the associated builders from a project.  If
450     * the project does not have the Java nature, nothing happens.
451     * @param project The project
452     * @throws CoreException if there is a problem accessing the project
453     *                       descriptor to remove the nature.
454     */
455    public static void removeJavaNature(IProject project) throws CoreException
456    {
457       IProjectDescription description = project.getDescription();
458       String[] natureIDs = description.getNatureIds();
459       for (int i = 0; i < natureIDs.length; i++)
460       {
461          if (natureIDs[i].equals(S_JAVA_NATURE))
462          {
463             String[] newNatureIDs = new String[natureIDs.length - 1];
464             System.arraycopy(natureIDs, 0, newNatureIDs, 0, i);
465             System.arraycopy(
466                natureIDs,
467                i + 1,
468                newNatureIDs,
469                i,
470                natureIDs.length - i - 1);
471             description.setNatureIds(newNatureIDs);
472             project.setDescription(description, null);
473             return;
474          }
475       }
476    }
477 
478    /***
479     * Retrieves the plugin's ID from the resource bundle.
480     * @return The plugin ID.
481     */
482    public static String getID()
483    {
484       return OWLCore.getOWLCore().getBundle().getSymbolicName();
485    }
486 
487    /***
488     * Checks if a string is a valid URI.
489     * @param uri The URI to check
490     * @return <code>true</code> if <code>uri</code> is valid, <code>false</code>
491     *         if it is malformed.
492     */
493    public static boolean isValidURI(String uri)
494    {
495       URI u = null;
496       try
497       {
498          u = new URI(uri);
499       }
500       catch (MalformedURIException me)
501       {
502          return false;
503       }
504       return true;
505    }
506    private static void trace(String s)
507    {
508       trace(s, false);
509    }
510    private static void trace(String s, boolean error)
511    {
512       trace("OWLCore", s, error);
513    }
514 
515    /***
516     * Sends output to all extenders of com.bbn.swede.core.traceWriter.
517     * @param id A string to help identify the source of this trace message
518     * @param msg The trace message
519     * @param error <code>true</code> if <code>msg</code> is an error message,
520     *              <code>false</code> if it is not.
521     */
522    public static void trace(String id, String msg, boolean error)
523    {
524       for (int i = 0; i < _traceWriters.length; i++)
525       {
526          _traceWriters[i].write(id, msg, error);
527       }
528    }
529 
530    /***
531     * Records an error in the Eclipse error log without alerting the user.  This
532     * method should be used only for lesser problems that are unlikely to have a
533     * noticeable impact on the behavior of SWeDE.  Major problems that may
534     * render some aspect(s) of SWeDe unusable should be logged as errors so that
535     * the user knows there is a potential problem.
536     * @param sPluginID The ID of the pluging where the error occured.
537     * @param sMessage The message to display as the cause of the error.
538     * @param e The exception that caused the error.
539     * @see #logError(String, String, Exception)
540     */
541    public static void logWarning(
542       String sPluginID,
543       String sMessage,
544       Exception e)
545    {
546       getPlugin().getLog().log(
547          new Status(IStatus.WARNING, sPluginID, IStatus.WARNING, sMessage, e));
548    }
549 
550    private static final String S_STANDARD_ERROR_TITLE = "SWeDE Error";
551    private static final String S_STANDARD_ERROR_MESSAGE =
552       "An error has occurred that may affect the behavior of the SWeDE.\n"
553          + "\n"
554          + "Please help us improve the SWeDE by reporting this problem.  "
555          + "Bug reporting guidelines are located in the SWeDE documentation.  "
556          + "On the help menu, select Help Contents, then navigate to "
557          + "SWeDE User Guide->Feedback->Bug Reports.";
558 
559    /***
560     * Alerts the user to an error and records it in the Eclipse error log.  Use
561     * of this method should be limited to only severe errors that are likely
562     * to cause noticeable problems in SWeDE.  Lesser errors that should be safe
563     * if they go unnoticed should be logged as warnings.
564     * @param sPluginID The ID of the pluging where the error occured.
565     * @param sMessage The message to display as the cause of the error.
566     * @param e The exception that caused the error.
567     * @see #logWarning(String, String, Exception)
568     */
569    public static void logError(String sPluginID, String sMessage, Exception e)
570    {
571       final IStatus status =
572          new Status(IStatus.ERROR, sPluginID, IStatus.ERROR, sMessage, e);
573       getPlugin().getLog().log(status);
574       try
575       {
576          ErrorDialog.openError(
577             (Shell)null,
578             S_STANDARD_ERROR_TITLE,
579             S_STANDARD_ERROR_MESSAGE,
580             status);
581       }
582       catch (RuntimeException e1)
583       {
584          OWLCore.trace("OWLCore", "Error dialog caused SWT exception (error logged)", false);
585       }
586    }
587 }