View Javadoc

1   /*
2    * $Id: InterfaceGenerator.java,v 1.22 2005/06/01 17:38:40 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   package com.bbn.swede.library.kazuki;
9   
10  import java.util.ArrayList;
11  import java.util.Iterator;
12  import java.util.List;
13  
14  import org.daml.kazuki.ClassCatalog;
15  import org.daml.kazuki.Datatypes;
16  import org.daml.kazuki.GenerateInterface;
17  import org.daml.kazuki.VocabularyCatalog;
18  import org.eclipse.core.resources.IFile;
19  import org.eclipse.core.resources.IFolder;
20  import org.eclipse.core.resources.IProject;
21  import org.eclipse.core.resources.IResource;
22  import org.eclipse.core.resources.IncrementalProjectBuilder;
23  import org.eclipse.core.runtime.CoreException;
24  import org.eclipse.core.runtime.IPath;
25  import org.eclipse.core.runtime.IProgressMonitor;
26  import org.eclipse.core.runtime.SubProgressMonitor;
27  import org.eclipse.jdt.core.IClasspathEntry;
28  import org.eclipse.jdt.core.ICompilationUnit;
29  import org.eclipse.jdt.core.IJavaProject;
30  import org.eclipse.jdt.core.JavaCore;
31  import org.eclipse.jdt.core.JavaModelException;
32  import org.eclipse.jface.dialogs.MessageDialog;
33  import org.eclipse.swt.widgets.Shell;
34  import org.eclipse.ui.PlatformUI;
35  
36  import com.bbn.jena.JenaPlugin;
37  import com.bbn.swede.core.IOWLDocument;
38  import com.bbn.swede.core.IOWLProject;
39  import com.bbn.swede.core.OWLCore;
40  import com.bbn.swede.core.OWLDocumentInfo;
41  import com.bbn.swede.core.dom.IOWLAbstractSyntaxTree;
42  import com.bbn.swede.core.dom.OASTNode;
43  import com.bbn.swede.core.resources.SWResourceManager;
44  import com.bbn.swede.tools.ToolsPlugin;
45  import com.bbn.swede.tools.codegenerator.AbstractCodeGenerator;
46  import com.bbn.swede.tools.codegenerator.VocabularyGenerator;
47  import com.hp.hpl.jena.ontology.OntModel;
48  import com.hp.hpl.jena.rdf.model.Model;
49  import com.hp.hpl.jena.rdf.model.ModelFactory;
50  import com.hp.hpl.jena.rdf.model.NsIterator;
51  
52  /***
53   * <p>Generates Kazuki interfaces for OWL ontologies.  The Kazuki wrapper 
54   * recursively calculates dependencies when it is run to ensure that all 
55   * referenced ontologies are available as SchemaGen vocabularies before APIs 
56   * are created.</p>
57   *  
58   * <p>Generated classes are placed in the
59   * <code>kazuki.<i>ontology_name</i>.base</code> and 
60   * <code>kazuki.<i>ontology_name</i>.custom</code> packages,
61   * where <i>ontology_name</i> is the filename of the source ontology, converted
62   * to a legal Java name (i.e. my-ont.owl becomes my_ont).</p>
63   * @author jlerner
64   * @author tself
65   */
66  public class InterfaceGenerator extends AbstractCodeGenerator
67  {
68     /*
69      *  (non-Javadoc)
70      * @see com.bbn.swede.ui.AbstractCodeGenerator#configureProject(org.eclipse.core.resources.IProject)
71      */
72     public void configureProject(IProject project)
73     {
74        OWLCore.addJavaNature(project);
75        IJavaProject proj = JavaCore.create(project);
76        try
77        {
78           JenaPlugin.addJenaToClasspath(proj);
79           KazukiPlugin.addKazukiToClasspath(proj);
80        }
81        catch (JavaModelException e)
82        {
83           OWLCore.logError(KazukiPlugin.getID(), "Classpath update failed", e);
84        }
85     }
86  
87     /*
88      *  (non-Javadoc)
89      * @see com.bbn.swede.ui.AbstractCodeGenerator#performGeneration(
90      *       com.bbn.swede.core.IOWLDocument, org.eclipse.core.resources.IProject, 
91      *       org.eclipse.core.runtime.IProgressMonitor)
92      */
93     public IPath performGeneration(IOWLDocument document, IProject project,
94        IProgressMonitor monitor) throws CoreException, InterruptedException
95     {
96        monitor.beginTask("Generating Java", 100);
97        GenerateInterface gi = new GenerateInterface();
98        OWLDocumentInfo info = document.getDocumentInfo();
99        Model model = info.getModel();
100       String sBase = document.getURI();
101       monitor.worked(10); // 10/100
102       if(monitor.isCanceled())
103       {
104          throw new InterruptedException();
105       }
106       //TODO: allow user to select package name
107       String sName = document.getLocalURI();
108       int iPos = sName.lastIndexOf('/');
109       if(iPos >= 0)
110       {
111          sName = sName.substring(iPos + 1);
112       }
113       iPos = sName.lastIndexOf(".owl");
114       if(iPos >= 0)
115       {
116          sName = sName.substring(0, iPos);
117       }
118       sName = sName.replace('-', '_');
119       String sUri = document.getLocalURI();
120       monitor.worked(10); // 20/100
121       if(monitor.isCanceled())
122       {
123          throw new InterruptedException();
124       }
125 
126       //TODO: allow user to select desired source folder
127       //      and possibly create a new one
128       IJavaProject proj = JavaCore.create(project);
129       IClasspathEntry[] entries = proj.getRawClasspath();
130       IClasspathEntry srcEntry = null;
131       for(int i = 0; i < entries.length; i++)
132       {
133          if(entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE)
134          {
135             srcEntry = entries[i];
136             break;
137          }
138       }
139       IFolder srcFolder = project.getFolder(srcEntry.getPath()
140          .removeFirstSegments(1));
141       monitor.worked(10); // 30/100
142       if(monitor.isCanceled())
143       {
144          throw new InterruptedException();
145       }
146 
147       //TODO: allow user to select kazuki package name
148       //      probably a Kazuki pref. page option, not a per-generation option
149       IFolder folder = srcFolder.getFolder("/kazuki");
150       if(!folder.exists())
151       {
152          folder.create(false, true, null);
153       }
154       folder = folder.getFolder(sName);
155 
156       IOWLAbstractSyntaxTree tree = document.getDocumentInfo().getOAST();
157       OASTNode root = tree.getRoot();
158       //TODO: somehow, miraculously, also find manually-referenced URIs.
159       //      It might be easier to rejigger my class loader fix so it gets
160       //      passed directly to GenerateInterfaces so it can make its own
161       //      vocab catalog and try to find everything on its own like it's
162       //      supposed to.
163 
164       List l = computeDependencies(document);
165       monitor.worked(10); // 40/100
166       if(monitor.isCanceled())
167       {
168          throw new InterruptedException();
169       }
170 
171       IFolder binFolder = project.getFolder(proj.getOutputLocation()
172          .removeFirstSegments(1));
173       if(!binFolder.exists())
174       {
175          binFolder.create(false, true, null);
176       }
177 
178       gi.OPT_ROOT_DIR = folder.getRawLocation().toString();
179       gi.OPT_ROOT_PACKAGE = "kazuki." + sName;
180       gi.OPT_CUSTOM_P = true;
181       gi.OPT_GENERATE_VOCABULARY = false;
182       gi.OPT_LOAD_IMPORTS = false;
183       gi.OPT_DATE_P = true;
184 
185       try
186       {
187          IOWLProject owlProj = (IOWLProject)SWResourceManager.getModel()
188             .getCorrespondingElement(project);
189          OntModel ontModel = buildModel(l, document, owlProj);
190          VocabularyCatalog vc = buildVocabs(l, owlProj, binFolder,
191             new SubProgressMonitor(monitor, 20));
192          gi
193             .generateInterface(ontModel, vc, new ClassCatalog(),
194                new Datatypes());
195       }
196       catch (Exception e)
197       {
198          OWLCore.logError(KazukiPlugin.getID(),
199             "Kazuki inteface generation failed", e);
200       }
201       monitor.worked(20); // 80/100
202       if(monitor.isCanceled())
203       {
204          throw new InterruptedException();
205       }
206 
207       project.refreshLocal(IResource.DEPTH_INFINITE, null);
208       project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null);
209       project.refreshLocal(IResource.DEPTH_INFINITE, null);
210       monitor.worked(20); // 100/100
211       if(monitor.isCanceled())
212       {
213          throw new InterruptedException();
214       }
215       monitor.done();
216 
217       return folder.getProjectRelativePath();
218    }
219 
220    /***
221     * Computes a list of URIs representing all direct and indirect dependencies
222     * of a document.
223     * 
224     * @param doc The document
225     * @return a List of Strings containing all URIs referenced by the document,
226     *         including the document's own URI.
227     */
228    private List computeDependencies(IOWLDocument doc)
229    {
230       List l = new ArrayList();
231       l.add(doc.getURI());
232       doComputeDependencies(doc, l);
233       return l;
234    }
235 
236    /***
237     * Strips off the #fragment of a URI.
238     * @param sURI The URI whose base is desired
239     * @return The XML base of the URI
240     */
241    private String getBase(String sURI)
242    {
243       int iPos = sURI.indexOf('#');
244       if(iPos < 0)
245       {
246          return sURI;
247       }
248       return sURI.substring(0, iPos);
249    }
250 
251    /***
252     * Recursive helper method for dependency computation.
253     * @see #computeDependencies(IOWLDocument doc)
254     * @param doc The document
255     * @param l A List of dependencies that have already been found
256     */
257    private void doComputeDependencies(IOWLDocument doc, List l)
258    {
259       IOWLAbstractSyntaxTree tree = doc.getDocumentInfo().getOAST();
260       Model model = doc.getDocumentInfo().getModel();
261       NsIterator nsi = model.listNameSpaces();
262       while (nsi.hasNext())
263       {
264          String s = getBase(nsi.nextNs());
265          //OWLCore.trace(getClass().getName(), s, false);
266          //ignore language namespaces and already-found URIs
267          if(l.contains(s) || s.equals(OASTNode.S_OWL_URI)
268             || s.equals(OASTNode.S_RDF_URI) || s.equals(OASTNode.S_RDFS_URI))
269          {
270             continue;
271          }
272          //TODO: do something about unavailable dependencies?
273          IOWLDocument docNS = tree.getNamespace(s);
274          if(docNS != null)
275          {
276             //OWLCore.trace(getClass().getName(), "Adding", false);
277             l.add(s);
278             doComputeDependencies(docNS, l);
279          }
280 
281       }
282       nsi.close();
283    }
284 
285    private static final String S_TITLE = "Kazuki Error";
286 
287    private static final String S_MESSAGE_BEGIN = "Schemagen vocabulary generation failed for ";
288 
289    private static final String S_MESSAGE_END = ".  This may prevent Kazuki from "
290       + " working correctly.";
291 
292    private static final String[] AS_BUTTONS = new String[] {"OK"};
293 
294    /***
295     * Builds a vocabulary catalogue for a given set of namespaces. Schemagen
296     * vocabularies that have already been generated will be used if possible.
297     * Others will be generated automatically.
298     * 
299     * @param lNamespaces A list of strings specifying the URIs of the namespaces
300     *        that need to be added to the vocabular catalogue
301     * @return The generated vocabulary catalog
302     */
303    private VocabularyCatalog buildVocabs(List lNamespaces, IOWLProject project,
304       IFolder binFolder, IProgressMonitor monitor) throws CoreException,
305       InterruptedException
306    {
307       int workAmt = 10;
308       monitor.beginTask("Generating vocabulary", lNamespaces.size() * 10);
309       VocabularyCatalog vc = null;
310       try
311       {
312          Class klass = Class.forName("com.hp.hpl.jena.rdf.model.Resource");
313          ClassLoader loader = klass.getClassLoader();
314          VocabularyClassLoader vcl = new VocabularyClassLoader(loader,
315             binFolder.getRawLocation().toString());
316          vc = new VocabularyCatalog(vcl);
317       }
318       catch (ClassNotFoundException e)
319       {
320          OWLCore.logError(KazukiPlugin.getID(),
321             "Error instantiating custom class loader", e);
322          return null;
323       }
324 
325       VocabularyGenerator vg = new VocabularyGenerator();
326       vg.setID(ToolsPlugin.S_SCHEMAGEN_ID);
327       Iterator it = lNamespaces.iterator();
328       while (it.hasNext())
329       {
330          String sURI = (String)it.next();
331          IOWLDocument doc = project.getDocument(sURI);
332          IPath path = doc.getGeneratedCodeLocation(vg.getID());
333          if(path == null)
334          {
335             vg.runGeneration(doc, new SubProgressMonitor(monitor, workAmt));
336             path = doc.getGeneratedCodeLocation(vg.getID());
337             if(path == null)
338             {
339                error(sURI);
340                continue;
341             }
342          }
343 
344          IFile vocab = project.getProject().getFile(path);
345          ICompilationUnit cu = (ICompilationUnit)JavaCore.create(vocab);
346          String sQualifiedName = cu.findPrimaryType().getFullyQualifiedName();
347          try
348          {
349             vc.parseVocabulary(sQualifiedName);
350          }
351          catch (IllegalArgumentException e1)
352          {
353             OWLCore.trace(getClass().getName(), e1.getMessage(), true);
354          }
355          catch (ClassNotFoundException e1)
356          {
357             OWLCore.trace(getClass().getName(), e1.getMessage(), true);
358          }
359          catch (IllegalAccessException e1)
360          {
361             OWLCore.trace(getClass().getName(), e1.getMessage(), true);
362          }
363          monitor.done();
364       }
365 
366       return vc;
367    }
368 
369    /***
370     * Builds a Jena model to hand off to Kazuki for API generation.  The model
371     * will include the current document and all dependant ontologies to ensure
372     * meaningful return types for object properties.
373     * @param lNamespaces java.util.List of directly and indirectly referenced
374     *                    namespaces
375     * @param document The document whose APIs are being generated
376     * @param project The project containing <code>document</code>
377     * @return A com.hp.hpl.jena.ontology.OntModel that contains 
378     *         <code>document</code> and all its referenced namespaces
379     */
380    private OntModel buildModel(List lNamespaces, IOWLDocument document,
381       IOWLProject project)
382    {
383       OntModel ontModel = ModelFactory.createOntologyModel();
384 
385       //TODO: make use of a class catalog to avoid always generating the entire
386       //contents of ontologies the current one depends on
387       Iterator it = lNamespaces.iterator();
388       while (it.hasNext())
389       {
390          String sURI = (String)it.next();
391          IOWLDocument doc = project.getDocument(sURI);
392          ontModel.add(doc.getDocumentInfo().getModel());
393       }
394 
395       return ontModel;
396    }
397 
398    /***
399     * Displays a message dialog informing the user that vocabulary generation
400     * failed for one of the document's referenced ontologies.
401     * @param sURI The URI that could not be turned into a vocabulary
402     */
403    private void error(String sURI)
404    {
405       //TODO: dump this to the error log instead of using a dialog?
406       Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
407          .getShell();
408       String sMessage = S_MESSAGE_BEGIN + sURI + S_MESSAGE_END;
409       MessageDialog md = new MessageDialog(shell, S_TITLE, null, sMessage,
410          MessageDialog.WARNING, AS_BUTTONS, 0);
411    }
412 }