View Javadoc

1   /*
2    * $Id: SWProjectInfo.java,v 1.25 2005/09/07 15:36:59 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.config;
10  
11  import java.io.ByteArrayInputStream;
12  import java.io.ByteArrayOutputStream;
13  import java.io.FileInputStream;
14  import java.io.FileNotFoundException;
15  import java.io.InputStream;
16  import java.io.OutputStream;
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  
23  import javax.xml.parsers.DocumentBuilderFactory;
24  import javax.xml.parsers.ParserConfigurationException;
25  import javax.xml.transform.Transformer;
26  import javax.xml.transform.TransformerConfigurationException;
27  import javax.xml.transform.TransformerException;
28  import javax.xml.transform.TransformerFactory;
29  import javax.xml.transform.dom.DOMSource;
30  import javax.xml.transform.stream.StreamResult;
31  
32  import org.eclipse.core.resources.IFile;
33  import org.eclipse.core.resources.IProject;
34  import org.eclipse.core.resources.IResource;
35  import org.eclipse.core.runtime.CoreException;
36  import org.eclipse.core.runtime.IPath;
37  import org.eclipse.core.runtime.Path;
38  import org.w3c.dom.DOMException;
39  import org.w3c.dom.Document;
40  import org.w3c.dom.Element;
41  import org.w3c.dom.NamedNodeMap;
42  import org.w3c.dom.Node;
43  import org.w3c.dom.NodeList;
44  import org.w3c.dom.Text;
45  
46  import com.bbn.swede.core.IOWLDocument;
47  import com.bbn.swede.core.IOWLElement;
48  import com.bbn.swede.core.IOWLExternalDocument;
49  import com.bbn.swede.core.IOWLLibrary;
50  import com.bbn.swede.core.IOWLModel;
51  import com.bbn.swede.core.OWLCore;
52  import com.bbn.swede.core.OWLElementInfo;
53  import com.bbn.swede.core.libraries.ILibraryDescriptor;
54  import com.bbn.swede.core.libraries.Libraries;
55  import com.bbn.swede.core.resources.OWLFolder;
56  import com.bbn.swede.core.resources.SWResourceManager;
57  
58  /***
59   * Contains utility methods for interacting with Semantic Web project info
60   * files.  This includes reading the file, rebuilding it based on the contents
61   * of the OWL model, and requesting individual values for specific files. Yeah.
62   * @author rblace
63   */
64  public final class SWProjectInfo
65  {
66     /***
67      * The filename used for the Semantic Web project info file.
68      */
69     public static final String S_FILE_NAME = ".swprojectinfo";
70  
71     private static final String S_TAG_DOCUMENTS = "documents";
72  
73     private static final String S_TAG_DOCUMENT = "document";
74  
75     private static final String S_TAG_URI = "uri";
76  
77     private static final String S_TAG_SOURCE = "sourceURL";
78  
79     private static final String S_TAG_PROJECT = "project";
80  
81     private static final String S_TAG_CODEGEN = "generator";
82  
83     private static final String S_ATTR_EXTERNAL = "external";
84  
85     private static final String S_ATTR_ID = "id";
86  
87     private static final String S_ATTR_PATH = "path";
88  
89     
90     
91     private static final String S_TAG_LIBRARIES = "libraries";
92     
93     private static final String S_TAG_LIBRARY = "library";
94     
95     private static final String S_ATTR_LIBRARY_NAME = "library-name";
96     
97     
98     
99     /***
100     * Creates an empty project information object.
101     */
102    private SWProjectInfo()
103    {
104    }
105 
106    /***
107     * Indicates whether a resource exists on disc.  Unlike the 
108     * IResource.exists() method, this test checks the status of the resource
109     * on disk and will function correctly at any time.
110     * @param res The resource to check.
111     * @return <code>true</code> if <code>res</code> exists on disk,
112     *         <code>false</code> if not.
113     */
114    private static boolean resourceExists(IResource res)
115    {
116       //Eclipse 3.1 creates file resources in a really weird order, so the
117       //project info file likely does not yet exist within the project by the 
118       //time we need to refer to it during an import.  Instead, we check its
119       //Java file handle, which is wild and crazy and looks at what's actually
120       //on the disk.
121       return res.getRawLocation().toFile().exists();
122    }
123 
124    /***
125     * Initializes the .swprojectinfo file. It will be created first if it
126     * doesn't already exist.
127     * @param project The project to initialize an info file for.
128     * @return <code>true</code> if the project's info file is successfully
129     *         initialized, <code>false</code> if not.
130     */
131    private static boolean initializeProjectInfo(IProject project)
132    {
133       if(project == null)
134       {
135          return false;
136       }
137       IFile file = getInfoFile(project);
138 
139       if(!resourceExists(file))
140       {
141          try
142          {
143             file.create(new ByteArrayInputStream(new String("").getBytes()),
144                true, null);
145          }
146          catch (CoreException e)
147          {
148             trace("CoreException: " + e.getMessage(), true);
149             return false;
150          }
151 
152          //create the basic xml document object w/project and documents tags
153          Document tDoc;
154          try
155          {
156             tDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
157                .getDOMImplementation().createDocument(null, S_TAG_PROJECT, null);
158          }
159          catch(DOMException e) 
160          {
161             trace("DOMException: " + e.getMessage(), true);
162             return false;
163          }
164          catch(ParserConfigurationException e)
165          {
166             trace("DOMException: " + e.getMessage(), true);
167             return false;
168          }
169 
170          //Document tDoc = DocumentDOMImplementation.createDocument(null, S_TAG_PROJECT, null);
171          Element eProject = tDoc.getDocumentElement();
172          Element eDocuments = tDoc.createElement(S_TAG_DOCUMENTS);
173 
174          //set the id to the project name and nest documents inside the project tag
175          eProject.setAttribute("id", project.getName());
176          eProject.appendChild(eDocuments);
177 
178          writeDocumentToFile(tDoc, file);
179       }
180       return true;
181    }
182 
183    /***
184     * Writes the given document to disk.  This will fail if the file
185     * doesn't exist.
186     * @param document The document to write.
187     * @param file - The file to write to.
188     * @return <code>true</code> if the config file was written successfully,
189     *         <code>false</code> if not.
190     */
191    private static boolean writeDocumentToFile(Document document, IFile file)
192    {
193       boolean retVal = true;
194 
195       try
196       {
197          //get a file output stream to the FILE_NAME file
198          OutputStream out = new ByteArrayOutputStream();
199 
200          TransformerFactory tFactory = TransformerFactory.newInstance();
201          Transformer transformer = tFactory.newTransformer();
202          DOMSource source = new DOMSource(document);
203 
204          StreamResult result = new StreamResult(out);
205          transformer.transform(source, result);
206 
207          String tOut = out.toString().replaceAll("><", ">\n<");
208 
209          InputStream in = new ByteArrayInputStream(tOut.getBytes());
210 
211          try
212          {
213             file.setContents(in, true, false, null);
214          }
215          catch (CoreException e)
216          {
217             trace("CoreException while setting contents of file", true);
218             return false;
219          }
220 
221       }
222       catch (TransformerConfigurationException e)
223       {
224          trace("TransformerConfigurationException ", true);
225          retVal = false;
226       }
227       catch (TransformerException ioe)
228       {
229          trace("TransformerException ", true);
230          retVal = false;
231       }
232 
233       return retVal;
234    }
235 
236    /***
237     * Parses a file into a DOM.
238     * @param file The file to parse.
239     * @return The document read from the file, or <code>null</code> if
240     *         the file does not exist
241     */
242    private static Document readDocumentFromFile(IFile file)
243    {
244       InputStream inputStream = null;
245       if (resourceExists(file))
246       {
247          java.io.File f = file.getRawLocation().toFile();
248          try
249          {
250             inputStream = new FileInputStream(f);
251          }
252          catch (FileNotFoundException e) 
253          {
254             //Apparently, it was a trick.
255             return null;
256          }
257       }
258       else
259       {
260          return null;
261       }
262 
263       try
264       {
265          Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder()
266             .parse(inputStream);
267          inputStream.close();
268          return d;
269       }
270       catch (Exception e)
271       {
272          //this isnt the end of the world, just means that there was some reason the file read didnt work.
273          trace("File read problem", false);
274          return null;
275       }
276    }
277 
278    /***
279     * Refreshes the config file.  This rebuilds the file based on the
280     * current state of the OWL model in memory.
281     * @param project The project to build the config file for.
282     */
283    public static void refreshInfoFile(IProject project)
284    {
285       initializeProjectInfo(project);
286 
287       //create the basic xml document object w/project and documents tags
288       Document tDoc;
289       try
290       {
291          tDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
292             .getDOMImplementation().createDocument(null, S_TAG_PROJECT, null);
293       }
294       catch(DOMException e) 
295       {
296          trace("DOMException: " + e.getMessage(), true);
297          return;
298       }
299       catch(ParserConfigurationException e)
300       {
301          trace("DOMException: " + e.getMessage(), true);
302          return;
303       }
304 
305       Element eProject = tDoc.getDocumentElement();
306       Element eDocuments = tDoc.createElement(S_TAG_DOCUMENTS);
307       Element eLibraries = tDoc.createElement(S_TAG_LIBRARIES);
308 
309       //get a list of the elements in the project
310       IOWLModel model = SWResourceManager.getModel();
311       IOWLElement tElement = model.getCorrespondingElement(project);
312 
313       if(tElement == null)
314       {
315          trace("There was a problem finding " + project.getName()
316             + " within the model.", true);
317          return;
318       }
319 
320       IOWLElement[] elements = getElements(tElement.getElementInfo()
321          .getChildren());
322 
323       //IOWLElement [] elements = tElement.getElementInfo().getChildren();
324 
325       for(int i = 0; i < elements.length; i++)
326       {
327          IOWLElement element = elements[i];
328 
329          Element eDocument = null;
330          Element eURI = null;
331          Element eSourceURL = null;
332          Text tURI = null;
333          Text tSourceURL = null;
334 
335          //Standard document stuff has to happen for both regular & external docs
336          if(element instanceof IOWLDocument)
337          {
338             IOWLDocument doc = (IOWLDocument)element;
339             trace("IOWLDOC: " + element.getElementName(), false);
340 
341             eDocument = tDoc.createElement(S_TAG_DOCUMENT);
342             eURI = tDoc.createElement(S_TAG_URI);
343 
344             eDocument.setAttribute(S_ATTR_ID, doc.getFile().getFullPath()
345                .makeAbsolute().toString());
346             eDocument.setAttribute(S_ATTR_EXTERNAL,
347                (element instanceof IOWLExternalDocument) ? "true" : "false");
348 
349             tURI = tDoc.createTextNode(((IOWLDocument)element).getURI());
350 
351             eURI.appendChild(tURI);
352             eDocument.appendChild(eURI);
353             eDocuments.appendChild(eDocument);
354 
355             Map map = doc.getGeneratedCodeMap();
356             Iterator it = map.keySet().iterator();
357             ArrayList alRemove = new ArrayList();
358             while (it.hasNext())
359             {
360                String sKey = (String)it.next();
361                IPath path = (IPath)map.get(sKey);
362                //If the file or folder referenced by path doesn't exist,
363                //do not regenerate the generator element
364                IResource res = doc.getFile().getProject().getFile(path);
365                if (!resourceExists(res))
366                {
367                   res = doc.getFile().getProject().getFolder(path);
368                   if (!resourceExists(res))
369                   {
370                      alRemove.add(sKey);
371                      continue;
372                   }
373                }
374                Element eGenerator = tDoc.createElement(S_TAG_CODEGEN);
375                eGenerator.setAttribute(S_ATTR_ID, sKey);
376                eGenerator.setAttribute(S_ATTR_PATH, path.toString());
377                eDocument.appendChild(eGenerator);
378             }
379             it = alRemove.iterator();
380             while (it.hasNext())
381             {
382                map.remove(it.next());
383             }
384          }
385          if(element instanceof IOWLExternalDocument)
386          {
387             trace("IOWLEXTERNALDOC: " + element.getElementName(), false);
388             eSourceURL = tDoc.createElement(S_TAG_SOURCE);
389 
390             tSourceURL = tDoc.createTextNode(((IOWLExternalDocument)element)
391                .getSourceURL());
392 
393             eSourceURL.appendChild(tSourceURL);
394             eDocument.appendChild(eSourceURL);
395          }
396       }
397       
398       
399       IOWLElement[] projectChildren = SWResourceManager.getModel()
400          .getCorrespondingElement(project).getElementInfo().getChildren();
401       Element libraryTag = null;
402       for(int i = 0; i < projectChildren.length; i++)
403       {
404          if(projectChildren[i] instanceof IOWLLibrary)
405          {
406             libraryTag = tDoc.createElement(S_TAG_LIBRARY);
407             libraryTag.setAttribute(S_ATTR_LIBRARY_NAME, ((IOWLLibrary)projectChildren[i]).getElementName());
408             eLibraries.appendChild(libraryTag);
409          }
410       }
411       
412       //set the id to the project name and nest documents inside the project tag
413       eProject.setAttribute(S_ATTR_ID, project.getName());
414       eProject.appendChild(eDocuments);
415       eProject.appendChild(eLibraries);
416 
417       //Write contents of file back to disk
418       writeDocumentToFile(tDoc, getInfoFile(project));
419    }
420 
421    /***
422     * Expands an array of OWL elements to include all descendant elements.
423     * This is done by first copying the input array and then recursively
424     * handling each element's children.
425     * @param elements The element array to expand
426     * @return An array of elements containing the input elements and all
427     *         elements descended from them.
428     */
429    private static IOWLElement[] getElements(IOWLElement[] elements)
430    {
431       if(elements == null)
432       {
433          return new IOWLElement[0];
434       }
435 
436       //start by copying the received elements into the array
437       IOWLElement[] returnElements = new IOWLElement[elements.length];
438       System.arraycopy(elements, 0, returnElements, 0, elements.length);
439 
440       //iterate through them and add all of their children into the return array
441       for(int i = 0; i < elements.length; i++)
442       {
443          if(elements[i] instanceof OWLFolder)
444          {
445             //get the rest of the elements
446             OWLElementInfo elementInfo = elements[i].getElementInfo();
447             if(elementInfo == null)
448             {
449                return returnElements;
450             }
451 
452             IOWLElement[] childElements = getElements(elementInfo.getChildren());
453 
454             //add them to the ones we already got
455             IOWLElement[] tElements = new IOWLElement[returnElements.length
456                + childElements.length];
457             System.arraycopy(returnElements, 0, tElements, 0,
458                returnElements.length);
459             System.arraycopy(childElements, 0, tElements,
460                returnElements.length, childElements.length);
461 
462             returnElements = new IOWLElement[tElements.length];
463             System.arraycopy(tElements, 0, returnElements, 0, tElements.length);
464          }
465       }
466 
467       //now we want to return this array
468       return returnElements;
469    }
470 
471    /***
472     * Indicates whether a file is an external OWL document.
473     * @param fileIn The file to check.
474     * @return boolean <code>true</code> if the file is an external document,
475     *                 <code>false</code> if not.
476     */
477    public static boolean isExternal(IFile fileIn)
478    {
479       Element element = getDocument(fileIn);
480       if(element == null)
481       {
482          return false;
483       }
484 
485       String retString = element.getAttribute(S_ATTR_EXTERNAL);
486       if(retString == null || !retString.equals("true"))
487       {
488          return false;
489       }
490 
491       return true;
492    }
493 
494    /***
495     * Retrieves a project's Ontology Library dependencies from the project info
496     * file.
497     * @param proj The project
498     * @return An array of library descriptors for the Ontology libraries
499     *         referenced by the project.
500     */
501    public static ILibraryDescriptor[] getLibraries(IProject proj)
502    {
503       ILibraryDescriptor[] toReturn = new ILibraryDescriptor[0];
504       Object[] tempToReturn = null;
505       List collectedLibraries = new ArrayList();
506       IFile swprojectFile = getInfoFile(proj);
507       Document doc = null;
508       String oneLibraryName = null;
509       Node librariesTag = null;
510       NodeList children = null;
511       NamedNodeMap attrs = null;
512       ILibraryDescriptor tempLibrary = null;
513       if(swprojectFile != null)
514       {
515          doc = readDocumentFromFile(swprojectFile);
516          if(doc != null)
517          {
518             children = doc.getElementsByTagName(S_TAG_LIBRARY);
519 
520             for(int i = 0; i < children.getLength(); i++)
521             {
522                attrs = children.item(i).getAttributes();
523                oneLibraryName = attrs.getNamedItem(S_ATTR_LIBRARY_NAME).getNodeValue();
524                tempLibrary = Libraries.getLibrary(oneLibraryName);
525                if(tempLibrary != null)
526                {
527                   collectedLibraries.add(tempLibrary);
528                }
529             }
530          }
531       }
532       tempToReturn = collectedLibraries.toArray();
533       toReturn = new ILibraryDescriptor[tempToReturn.length];      
534       for(int i = 0; i < tempToReturn.length; i++)
535       {
536          toReturn[i] = (ILibraryDescriptor) tempToReturn[i];
537       }
538       return toReturn;
539    }
540    
541    /***
542     * Retrieves a document's URI from the project info file.  For external
543     * documents, this is not necessarily the same URI it was imported from.
544     * That URI is stored as the source URL of the external document.
545     * @param fileIn The file whose URI to return
546     * @return String The base URI of the file, or <code>null</code> if it is
547     *         not an OWL document.
548     * @see #getDocumentSourceURL(IFile)
549     */
550    public static String getDocumentURI(IFile fileIn)
551    {
552       return getDocumentElement(fileIn, S_TAG_URI);
553    }
554 
555    /***
556     * Retrieves a document's source URL from the project info file.  This is
557     * the URL from which an external document was originally imported.  Regular
558     * documents do not have a source URL.
559     * @param fileIn The file whose source URL to return
560     * @return The source URL of the file, or <code>null</code> if it is not an
561     *         external OWL document.
562     */
563    public static String getDocumentSourceURL(IFile fileIn)
564    {
565       return getDocumentElement(fileIn, S_TAG_SOURCE);
566    }
567 
568    /***
569     * Retrieves the list of generated code locations for a document.  This
570     * indicates which code generators have been run against it and where the
571     * file(s) they created are located.
572     * @param fileIn The file whose generated code map to return
573     * @return A map of generated code locations.  The map is indexed by ID
574     *         string, as returned by AbstractCodeGenerator.getID(), and stores
575     *         the locations as instances of IPath.
576     */
577    public static Map getGeneratedCodeMap(IFile fileIn)
578    {
579       Map map = new HashMap();
580 
581       Element element = getDocument(fileIn);
582       if(element == null)
583       {
584          return map;
585       }
586 
587       NodeList nodes = element.getElementsByTagName(S_TAG_CODEGEN);
588       for(int i = 0; i < nodes.getLength(); i++)
589       {
590          Node n = nodes.item(i);
591          String sID = n.getAttributes().getNamedItem(S_ATTR_ID).getNodeValue();
592          String sPath = n.getAttributes().getNamedItem(S_ATTR_PATH)
593             .getNodeValue();
594          IPath path = fileIn.getProject().getFile(new Path(sPath))
595             .getProjectRelativePath();
596          map.put(sID, path);
597       }
598 
599       return map;
600    }
601 
602    /***
603     * Retrieves a value about a document from the info file.
604     * @param file The file whose element to return.
605     * @param elementIn The name of the element to return.
606     * @return The value of the document element named <code>elementIn</code>
607     *         for <code>file</code>
608     */
609    private static String getDocumentElement(IFile file, String elementIn)
610    {
611       Element element = getDocument(file);
612       if(element == null)
613       {
614          return getSpecialElement(file, elementIn);
615       }
616 
617       Text tText = null;
618 
619       try
620       {
621          tText = (Text)element.getElementsByTagName(elementIn).item(0)
622             .getFirstChild();
623       }
624       catch (Exception e)
625       {
626          return null;
627       }
628 
629       if(tText == null)
630       {
631          return getSpecialElement(file, elementIn);
632       }
633 
634       return (String)tText.getData();
635    }
636 
637    /***
638     * Retrieves an element from within the persistent properties
639     * of the metadata for eclipse.  This function exists solely to assist
640     * in the importing of pre-0.9 versions of SWeDE and otherwise should not
641     * be used.
642     * @param file The file to get the element for
643     * @param elementIn The element to get
644     * @return The value of of the element, or <code>null</code> if no value
645     *         could be found.
646     */
647    private static String getSpecialElement(IFile file, String elementIn)
648    {
649       try
650       {
651          if(elementIn == S_TAG_SOURCE)
652          {
653             return file.getPersistentProperty(OWLCore.PERSISTENT_SOURCE_URL);
654          }
655          else if(elementIn == S_TAG_URI)
656          {
657             return file.getPersistentProperty(OWLCore.PERSISTENT_URI);
658          }
659       }
660       catch (Exception e)
661       {
662          return null;
663       }
664       return null;
665    }
666 
667    /***
668     * Returns the XML element associated with a file in the project info file.
669     * @param fileIn The file to get the document element for
670     * @return The element corresponding to <code>fileIn</code>, or
671     *         <code>null</code> if no such element exists.
672     */
673    private static Element getDocument(IFile fileIn)
674    {
675       IFile file = getInfoFile(fileIn.getProject());
676       if(!resourceExists(file))
677       {
678          return null;
679       }
680 
681       //Read in contents of file if they exist
682       Document doc = readDocumentFromFile(file);
683       if(doc == null)
684       {
685          return null;
686       }
687 
688       //Isolate the element we're talking about
689       int index = 0;
690       NodeList list = doc.getElementsByTagName(S_TAG_DOCUMENT);
691       Element curr = (Element)list.item(index++);
692 
693       while (curr != null
694          && !(curr.getAttribute("id").equals(fileIn.getFullPath()
695             .makeAbsolute().toString())))
696       {
697          curr = (Element)list.item(index++);
698       }
699 
700       return curr;
701 
702    }
703 
704    /***
705     * Returns the first occurance of a tag in a DOM.
706     * @param tag The tag to find.
707     * @param doc The DOM to search.
708     * @return The first element in <code>doc</code> with the name
709     *         <code>tag</code>.
710     */
711    private static Element getUniqueTag(String tag, Document doc)
712    {
713       return (Element)doc.getElementsByTagName(tag).item(0);
714    }
715 
716    /***
717     * Returns the Eclipse file resource for a project's info file.
718     * @param project The project.
719     * @return A file handle for <code>project</code>'s info file, regardless
720     *         of whether the file actually exists.
721     */
722    private static IFile getInfoFile(IProject project)
723    {
724       if(project == null)
725       {
726          return null;
727       }
728 
729       return project.getFile(S_FILE_NAME);
730    }
731 
732    /***
733     * Indicates whether or not a project has a project info file.
734     * @param project The project to check for an info file.
735     * @return <code>true</code> if the project has an info file,
736     *         <code>false</code> if not.
737     */
738    public static boolean infoFileExists(IProject project)
739    {
740       IFile file = getInfoFile(project);
741       if(file == null)
742       {
743          return false;
744       }
745 
746       return resourceExists(file);
747    }
748 
749    /***
750     * <p>Sends a message to all trace writers.</p>
751     *
752     * <p>This is a convenience method, fully equivalent to:
753     * <blockquote>OWLCore.trace("SWProjectInfo.java",message,error)</blockquote></p>
754     * @param message The message to print
755     * @param error <code>true</code> if this is an error message,
756     *              <code>false</code> if not.
757     */
758    private static void trace(String message, boolean error)
759    {
760       if(false)
761       {
762          OWLCore.trace("SWProjectInfo.java", message, error);
763       }
764    }
765 
766 }