View Javadoc

1   /*
2    * $Id: OWLSyntaxBuilder.java,v 1.20 2005/06/01 14:36:03 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.builders;
10  
11  import java.io.File;
12  import java.io.FileNotFoundException;
13  import java.io.IOException;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.Iterator;
17  import java.util.List;
18  import java.util.Map;
19  
20  import org.eclipse.core.resources.IProject;
21  import org.eclipse.core.resources.IResource;
22  import org.eclipse.core.resources.IResourceDelta;
23  import org.eclipse.core.resources.IResourceDeltaVisitor;
24  import org.eclipse.core.resources.IncrementalProjectBuilder;
25  import org.eclipse.core.runtime.CoreException;
26  import org.eclipse.core.runtime.IProgressMonitor;
27  import org.eclipse.jface.text.IRegion;
28  import org.eclipse.jface.text.Region;
29  
30  import com.bbn.swede.core.IOWLDocument;
31  import com.bbn.swede.core.IOWLElement;
32  import com.bbn.swede.core.OWLCore;
33  import com.bbn.swede.core.dom.OAST;
34  import com.bbn.swede.core.dom.OASTNode;
35  import com.bbn.swede.core.dom.UnparseableFilterRandomAccessFile;
36  import com.bbn.swede.core.dom.UnparseableNode;
37  import com.bbn.swede.core.libraries.LibraryOWLDocument;
38  import com.bbn.swede.core.resources.SWResourceManager;
39  
40  /***
41   * The builder responsible for running basic syntax checks against OWL
42   * documents.
43   * @author tself
44   * @author jlerner
45   */
46  public class OWLSyntaxBuilder extends IncrementalProjectBuilder
47  {
48     private IResourceDeltaVisitor _visitor;
49  
50     /***
51      * Default constructor for OWLSyntaxBuilder.
52      */
53     public OWLSyntaxBuilder()
54     {
55        super();
56        _visitor = new SyntaxDeltaVisitor();
57     }
58  
59     /*
60      *  (non-Javadoc)
61      * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
62      *      java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
63      */
64     protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
65        throws CoreException
66     {
67        switch(kind)
68        {
69           case IncrementalProjectBuilder.FULL_BUILD:
70              performFullBuild();
71              break;
72           case IncrementalProjectBuilder.INCREMENTAL_BUILD:
73           case IncrementalProjectBuilder.AUTO_BUILD:
74              performIncrementalBuild();
75           default:
76        }
77        return null;
78     }
79  
80     /***
81      * Checks the syntax of the latest modifications in the project.
82      */
83     private void performIncrementalBuild() throws CoreException
84     {
85        runSyntaxBuilder(getProject());
86     }
87  
88     private void runSyntaxBuilder(IProject proj) throws CoreException
89     {
90        IResourceDelta delta = getDelta(proj);
91        if (delta != null)
92        {
93           delta.accept(_visitor);
94        }
95     }
96  
97     /***
98      * Checks the syntax of all OWL files in the project.
99      */
100    private void performFullBuild() throws CoreException
101    {
102       runSyntaxBuilder(getProject());
103    }
104 
105    /***
106     * Watches for OWL documents to change so the syntax checks can be re-run
107     * on them.
108     */
109    class SyntaxDeltaVisitor implements IResourceDeltaVisitor
110    {
111       /*
112        *  (non-Javadoc)
113        * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
114        */
115       public boolean visit(IResourceDelta delta) throws CoreException
116       {
117          IResource resource = delta.getResource();
118          if (resource == null)
119          {
120             return false;
121          }
122          if (delta.getKind() == IResourceDelta.REMOVED)
123          {
124             return false;
125          }
126          IOWLElement element = SWResourceManager.getModel()
127             .getCorrespondingElement(resource);
128          if (element == null)
129          {
130             return false;
131          }
132          if (element instanceof IOWLDocument)
133          {
134             if(!(element instanceof LibraryOWLDocument))
135             {
136                checkSyntax((IOWLDocument)element, delta.findMember(element
137                   .getPath()));
138             }
139          }
140          return true;
141       }
142 
143       /***
144        * Checks the syntax of the given OWL document.
145        * @param document IOWLDocument object to be checked
146        * @param delta Resource delta of the given document
147        */
148       private void checkSyntax(IOWLDocument document, IResourceDelta delta)
149       {
150          //         OWLCore.trace("checkSyntax()", "Checking syntax for "
151          //            + document.getElementName(), false);
152          removeUnparseableMarkers(document);
153          List badNodes = getUnparseableNodes(document);
154 //         if(badNodes == null || badNodes.size() < 1)
155 //         {
156 //            return;
157 //         }
158          Iterator it = badNodes.iterator();
159          while (it.hasNext())
160          {
161             UnparseableNode node = (UnparseableNode)it.next();
162             try
163             {
164                node.createMarker(document.getFile());
165             }
166             catch (CoreException e)
167             {
168                OWLCore.logWarning(OWLCore.getID(),
169                   "Unable to add Invalid XML marker", e);
170             }
171          }
172          OAST oast = (OAST)document.getDocumentInfo().getOAST();
173          oast.persistUnparseability();
174       }
175 
176       /***
177        * Defragments unparseable nodes within a document and returns the minimal
178        * list.
179        * @param d Document possibly containing unparseable nodes
180        * @return Minimal list of UnparseableNodes
181        */
182       private List getUnparseableNodes(IOWLDocument d)
183       {
184          OAST oast = (OAST)d.getDocumentInfo().getOAST();
185          List badNodes = new ArrayList(
186             Arrays.asList(oast.getRoot().getNodesOfType(OASTNode.UNPARSEABLE)));
187          //Filter out nested unparseables
188          for (int i = badNodes.size(); i > 0; i--)
189          {
190             UnparseableNode un = (UnparseableNode)badNodes.get(i - 1);
191             if (un.getParent() instanceof UnparseableNode)
192             {
193                badNodes.remove(i - 1);
194             }
195          }
196          if (badNodes == null || badNodes.size() < 2)
197          {
198             return badNodes;
199          }
200          // some unparseables exist. Defragment.
201          boolean changed = false;
202          UnparseableNode node1 = null, node2 = null;
203       loop:
204          do
205          {
206             changed = false;
207             // get latest copy of unparseable nodes
208             badNodes.clear();
209             badNodes.addAll(Arrays.asList(
210                oast.getRoot().getNodesOfType(OASTNode.UNPARSEABLE)));
211             for (int i = 0; i < badNodes.size() - 1; i++)
212             {
213                for (int j = i + 1; j < badNodes.size(); j++)
214                {
215                   node1 = (UnparseableNode)badNodes.get(i);
216                   node2 = (UnparseableNode)badNodes.get(j);
217                   if (node1 != node2
218                       && node1.getParent().equals(node2.getParent())) // siblings
219                   {
220                      // build inclusive range of nodes as list
221                      ArrayList nodeRange = new ArrayList(j - i + 1);
222                      for (int k = i; k <= j; k++)
223                      {
224                         nodeRange.add(badNodes.get(k));
225                      }
226                      if (changed = resolve(d, nodeRange))
227                      {
228                         continue loop;
229                      }
230                   }
231                }
232             }
233          } while (changed);
234          return badNodes;
235       }
236 
237       /***
238        * Resolves XML errors by reparsing a region delimted by two unparseable
239        * nodes.  Additional unparseable nodes within the reparse area are
240        * blanked out to avoid interfering with resolution of surrounding, valid
241        * XML.
242        * @param document The document containing the unparseable XML
243        * @param nodeRange List of <code>UnparseableNode</code> objects.  The
244        *                  first and last nodes in the list determine the range
245        *                  of text to reparse.  Any additional nodes will be
246        *                  blanked out for the reparse.
247        * @return <code>true</code> if the reparse resulted in valid XML,
248        *         <code>false</code> if nothing was resolved.
249        */
250       private boolean resolve(IOWLDocument document, List nodeRange)
251       {
252          UnparseableNode[] unparseables = new UnparseableNode[nodeRange.size()];
253          nodeRange.toArray(unparseables);
254          UnparseableNode unFirst = unparseables[0];
255          UnparseableNode unLast = unparseables[unparseables.length - 1];
256          int iOffset = unFirst.getOffset();
257          int iEndOffset = unLast.getOffset() + unLast.getLength();
258          int iLength = iEndOffset - iOffset;
259          File file = document.getFile().getLocation().toFile();
260          IRegion[] regions = new IRegion[unparseables.length - 2];
261          for (int i = 1; i < unparseables.length - 1; i++)
262          {
263             regions[i - 1] = new Region(unparseables[i].getOffset(),
264                                         unparseables[i].getLength());
265          }
266          String sText = null;
267          try
268          {
269             UnparseableFilterRandomAccessFile ufraf =
270                new UnparseableFilterRandomAccessFile(file, "r", regions);
271             ufraf.seek(iOffset);
272             byte[] bytes = new byte[iLength];
273             ufraf.read(bytes);
274             ufraf.close();
275             sText = new String(bytes);
276          }
277          catch (FileNotFoundException e)
278          {
279             OWLCore.logWarning(
280                OWLCore.getID(),
281                "Unable to open file for unparseable resolution",
282                e);
283             return false;
284          }
285          catch (IOException e)
286          {
287             OWLCore.logWarning(
288                OWLCore.getID(),
289                "Unable to retrieve text for unparseable resolution",
290                e);
291             return false;
292          }
293 
294          try
295          {
296             OAST tree = (OAST)document.getDocumentInfo().getOAST();
297             OASTNode dummy = tree.partialParse(unFirst.getParent(),
298                                                       sText, iOffset);
299             OASTNode[] children = dummy.getChildren();
300             if (children.length > 0
301                 && children[0] instanceof UnparseableNode)
302             {
303                return false;
304             }
305             else if (children.length > 0)
306             {
307                //Detach the unaffected unparseables before replacing things
308                //so they don't get displaced
309                for (int i = 1; i < unparseables.length - 1; i++)
310                {
311                   unparseables[i].detach(false);
312                }
313                tree.replace(iOffset, iLength, dummy);
314                for (int i = 1; i < unparseables.length - 1; i++)
315                {
316                   //No node displacement necessary, the OAST currently just
317                   //thinks there's whitespace where the remaining unparseables go
318                   unparseables[i].attach(tree, false);
319                }
320                return true;
321             }
322          }
323          catch (IOException e)
324          {
325             OWLCore.logWarning(
326                OWLCore.getID(),
327                "Unable to reparse text for unparseable resolution",
328                e);
329             return false;
330          }
331          return false;
332       }
333 
334       /***
335        * Removes all Unparseable XML Error markers from a document.
336        * @param doc IOWLDocument to be cleaned of markers.
337        */
338       private void removeUnparseableMarkers(IOWLDocument doc)
339       {
340          IResource res = doc.getResource();
341          try
342          {
343             res.deleteMarkers(OWLCore.MARKER_UNPARSEABLE, true,
344                IResource.DEPTH_INFINITE);
345          }
346          catch (CoreException ce)
347          {
348             OWLCore.logWarning(OWLCore.getID(),
349                "Unable to delete Invalid XML marker", ce);
350          }
351       }
352    }
353 }