Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/eclipse/xml/editors/Util.java


1   package com.port80.eclipse.xml.editors;
2   
3   import java.io.IOException;
4   import java.io.StringReader;
5   
6   import javax.xml.parsers.DocumentBuilder;
7   import javax.xml.parsers.DocumentBuilderFactory;
8   
9   import org.apache.xerces.dom.DOMInputSourceImpl;
10  import org.apache.xerces.dom3.DOMError;
11  import org.apache.xerces.dom3.DOMErrorHandler;
12  import org.apache.xerces.xni.XMLLocator;
13  import org.apache.xerces.xni.parser.XMLParserConfiguration;
14  import org.eclipse.core.resources.IFile;
15  import org.eclipse.jface.text.BadLocationException;
16  import org.eclipse.jface.text.IDocument;
17  import org.eclipse.jface.text.Position;
18  import org.eclipse.jface.text.source.ISourceViewer;
19  import org.eclipse.jface.text.source.SourceViewer;
20  import org.eclipse.ui.IEditorInput;
21  import org.eclipse.ui.editors.text.TextEditor;
22  import org.eclipse.ui.texteditor.ITextEditor;
23  import org.w3c.dom.Document;
24  import org.w3c.dom.Node;
25  import org.xml.sax.SAXException;
26  import org.xml.sax.SAXParseException;
27  
28  import com.port80.eclipse.editors.EditorsPlugin;
29  import com.port80.eclipse.editors.EditorsUtil;
30  import com.port80.eclipse.xml.parser.IXRange;
31  import com.port80.eclipse.xml.parser.XComment;
32  import com.port80.eclipse.xml.parser.XDomBuilder;
33  import com.port80.eclipse.xml.parser.XElementNS;
34  import com.port80.eclipse.xml.parser.XProcessingInstruction;
35  
36  /**
37   * Static utilities routines.
38   * 
39   * @author chrisl
40   */
41  public class Util {
42  
43    ////////////////////////////////////////////////////////////////////////
44  
45    private static final String NAME = "Util";
46    private static final boolean DEBUG = false;
47  
48    ////////////////////////////////////////////////////////////////////////
49  
50    public static Document parseXML(TextEditor editor, SourceViewer viewer, boolean quiet) {
51      return customParse(editor, viewer, quiet);
52    }
53  
54    /** Parse a XML document using JAXP crimson XML parser. */
55    public static Document crimsonParse(TextEditor editor, SourceViewer viewer) {
56      IEditorInput input = editor.getEditorInput();
57      IFile file = (IFile) input.getAdapter(IFile.class);
58      if (file == null)
59        return null;
60      DocumentBuilder builder;
61      try {
62        builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
63        if (DEBUG)
64          System.err.println(NAME + ".crimsonParse(): builder=" + builder);
65      } catch (Exception e) {
66        EditorsPlugin.error("Error creating XML DOM builder", e, input);
67        return null;
68      }
69      try {
70        return builder.parse(file.getLocation().toFile());
71      } catch (SAXParseException e) {
72        reportError("Util.crimsonParser()", e, editor, viewer);
73      } catch (SAXException e) {
74        reportError("Util.crimsonParser()", e, editor, viewer);
75      } catch (IOException e) {
76        EditorsPlugin.error("Error creating XML DOM builder", e, input);
77      }
78      return null;
79    }
80  
81    /** Parse a XML document using a custom Xerces DOMBuilder. */
82    public static Document customParse(TextEditor editor, SourceViewer viewer, boolean quiet) {
83      IEditorInput input = editor.getEditorInput();
84      IFile file = (IFile) input.getAdapter(IFile.class);
85      if (file == null)
86        return null;
87      String filepath = file.getLocation().toString();
88      String dir = file.getLocation().removeLastSegments(1).toString();
89      XDomBuilder builder = new CustomDomBuilder();
90      XMLParserConfiguration cf = builder.getConfiguration();
91      //    cf.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
92      cf.setFeature("http://xml.org/sax/features/external-general-entities", false);
93      cf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
94      cf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
95      cf.setFeature("http://xml.org/sax/features/validation", false);
96      cf.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false);
97      cf.setProperty(
98        "http://apache.org/xml/properties/internal/entity-manager",
99        CustomEntityManager.createEntityManager(cf));
100     builder.setErrorHandler(new CustomErrorHandler("customParser(): ", quiet, editor, viewer, builder));
101     if (DEBUG) {
102       System.err.println(NAME + ".xercesParse(): builder=" + builder);
103       System.err.println(NAME + ".xercesParse(): filepath=" + filepath + ", dir=" + dir);
104     }
105     try {
106       return builder.parse(
107         new DOMInputSourceImpl(
108           null,
109           filepath,
110           dir,
111           new StringReader(viewer.getDocument().get()),
112           null));
113     } catch (Exception e) {
114       // Errors have been reported in error handler.
115       return null;
116     }
117   }
118 
119   /** Parse a XML document with validation. */
120   public static Document validateXML(TextEditor editor, SourceViewer viewer, boolean quiet) {
121     IEditorInput input = editor.getEditorInput();
122     IFile file = (IFile) input.getAdapter(IFile.class);
123     if (file == null)
124       return null;
125     String filepath = file.getLocation().toString();
126     String dir = file.getLocation().removeLastSegments(1).toString();
127     XDomBuilder builder = new CustomDomBuilder();
128     XMLParserConfiguration cf = builder.getConfiguration();
129     //    cf.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
130     cf.setFeature("http://xml.org/sax/features/external-general-entities", true);
131     cf.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
132     cf.setFeature("http://xml.org/sax/features/validation", true);
133     cf.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false);
134     builder.setErrorHandler(new CustomErrorHandler("validateXML(): ", quiet, editor, viewer, builder));
135     if (DEBUG) {
136       System.err.println(NAME + ".xercesParse(): builder=" + builder);
137       System.err.println(NAME + ".xercesParse(): filepath=" + filepath + ", dir=" + dir);
138     }
139     Document ret = null;
140     try {
141       ret =
142         builder.parse(
143           new DOMInputSourceImpl(
144             null,
145             filepath,
146             dir,
147             new StringReader(viewer.getDocument().get()),
148             null));
149       CustomErrorHandler handler = (CustomErrorHandler) builder.getErrorHandler();
150       if (handler.getErrors() == 0)
151         EditorsPlugin.info("Validate passed.", null, input);
152     } catch (Exception e) {
153       XMLLocator loc = builder.getLocator();
154       if (loc != null) {
155         reportError("Validate error", e, loc, editor, viewer);
156       } else {
157         EditorsPlugin.error("Validate error", e, input);
158       }
159       return null;
160     }
161     return ret;
162   }
163 
164   public static void reportError(String msg, SAXParseException e, ITextEditor editor, ISourceViewer viewer) {
165     // XMLParser line numbers starts from 1.
166     // NOTE: Apparently, e.getColumnNumber() return char. offset (starting at 1)
167     // in the line not column number! 
168     int line = e.getLineNumber();
169     int col = e.getColumnNumber();
170     int offset = EditorsUtil.findOffset(line, 1, viewer) + col - 1;
171     col = EditorsUtil.findLineColumn(offset, viewer)[1];
172     EditorsPlugin.error(
173       offset,
174       offset,
175       msg + ": @(" + line + ", " + col + ")",
176       e,
177       editor.getEditorInput(),
178       viewer);
179   }
180 
181   public static void reportError(String msg, SAXException e, ITextEditor editor, ISourceViewer viewer) {
182     Exception ex = e.getException();
183     IEditorInput input = editor.getEditorInput();
184     if (ex == null)
185       EditorsPlugin.error(msg, e, input);
186     else if (ex instanceof SAXParseException)
187       Util.reportError(msg, (SAXParseException) ex, editor, viewer);
188     else
189       EditorsPlugin.error(msg, ex, input);
190   }
191 
192   public static void reportError(
193     String msg,
194     Exception e,
195     XMLLocator locator,
196     ITextEditor editor,
197     ISourceViewer viewer) {
198     // NOTE: XDomBuilder locator column number is actually column offset.
199     int offset = EditorsUtil.findOffset(locator.getLineNumber(), 1, viewer) + locator.getColumnNumber() - 1;
200     int[] pos = EditorsUtil.findLineColumn(offset, viewer);
201     EditorsPlugin.error(
202       offset,
203       offset,
204       msg + ": @(" + pos[0] + ", " + pos[1] + ")",
205       (Exception) e,
206       editor.getEditorInput(),
207       viewer);
208   }
209 
210   ////////////////////////////////////////////////////////////////////////
211 
212   /**
213    * @return The innermost element/comment/processing instruction node that enclose the given range.
214    * @param line 1-based line number.
215    * @param column 1-based column number.
216    */
217   public static Position findElementRange(int line, int column, Node node, IDocument doc) {
218     Position ret = null;
219     for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) {
220       ret = findElementRange(line, column, n, doc);
221       if (ret != null)
222         return ret;
223     }
224     if (node instanceof XElementNS || node instanceof XComment || node instanceof XProcessingInstruction) {
225       IXRange range = (IXRange) node;
226       IXRange end = getEndRange(node, doc);
227       int endline, endcolumn;
228       if (end != null) {
229         endline = end.getStartLine();
230         endcolumn = end.getStartColumn();
231       } else {
232         endline = range.getEndLine();
233         endcolumn = range.getEndColumn();
234       }
235       if ((range.getStartLine() < line
236         || (range.getStartLine() == line && range.getStartColumn() < column))
237         && (endline > line || (endline == line && endcolumn > column)))
238         return getRange(doc, range, endline, endcolumn);
239     }
240     return null;
241   }
242 
243   /**
244    * @return Find node that starts at the given line and column.
245    * @param line 1-based line number.
246    * @param column 1-based column number.
247    */
248   public static Node findNode(int line, int column, Node node, IDocument doc) {
249     if (node instanceof IXRange) {
250       IXRange range = (IXRange) node;
251       if (range.getStartLine() == line && range.getStartColumn() == column)
252         return node;
253     }
254     if (node instanceof IXRange) {
255       IXRange range = (IXRange) node;
256       if (range.getStartLine() == line && range.getStartColumn() == column)
257         return node;
258     }
259     Node ret;
260     for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) {
261       ret = findNode(line, column, n, doc);
262       if (ret != null)
263         return ret;
264     }
265     return null;
266   }
267 
268   public static Position getRange(IDocument doc, IXRange range, int endline, int endcolumn) {
269     int startoffset, endoffset;
270     try {
271       startoffset = doc.getLineOffset(range.getStartLine() - 1) + range.getStartColumn() - 1;
272       endoffset = doc.getLineOffset(endline - 1) + endcolumn - 1;
273     } catch (BadLocationException e) {
274       return new Position(-1, -1);
275     }
276     return new Position(startoffset, endoffset - startoffset);
277   }
278 
279   public static IXRange getEndRange(Node node, IDocument doc) {
280     Node n = node.getNextSibling();
281     if (n != null && n instanceof IXRange)
282       return (IXRange) n;
283     return null;
284     //    int line = doc.getNumberOfLines();
285     //    int column;
286     //    try {
287     //      column = doc.getLineLength(line-1);
288     //    } catch (BadLocationException e) {
289     //      if(true) System.err.println(NAME+".getEndRange(): Bad location");
290     //      return null;
291     //    }
292     //    //NOTE: If the line ends with a line delimiter, there should be a text node and would not reach here.
293     //    // So here, column number do not include line delimiter and should be incremeted for 1-based
294     //    // column number.
295     //    return new XRange(line, column+1, line, column+1);
296   }
297 
298   ////////////////////////////////////////////////////////////////////////
299 
300   static class CustomDomBuilder extends XDomBuilder {
301     protected ClassLoader getClassLoader() {
302       return EditorsPlugin.getPluginClassLoader();
303     }
304   }
305 
306   ////////////////////////////////////////////////////////////////////////
307 
308   static class CustomErrorHandler implements DOMErrorHandler {
309     String fMessagePrefix;
310     boolean fQuiet;
311     TextEditor fEditor;
312     ISourceViewer fViewer;
313     XDomBuilder fBuilder;
314     DOMErrorHandler fHandler;
315     int fErrors;
316     public CustomErrorHandler(
317       String prefix,
318       boolean quiet,
319       TextEditor editor,
320       ISourceViewer viewer,
321       XDomBuilder builder) {
322       fBuilder = builder;
323       fMessagePrefix = prefix;
324       fEditor = editor;
325       fViewer = viewer;
326       fQuiet = quiet;
327       fHandler = fBuilder.getErrorHandler();
328     }
329 
330     public int getErrors() {
331       return fErrors;
332     }
333 
334     /**
335      * @see org.apache.xerces.dom3.DOMErrorHandler#handleError(org.apache.xerces.dom3.DOMError)
336      */
337     public boolean handleError(DOMError error) {
338       ++fErrors;
339       if (fQuiet)
340         return true;
341       Object e = error.getException();
342       String message = fMessagePrefix;
343       if (!(e instanceof Exception)) {
344         e = null;
345         message += error.getMessage();
346       }
347       // NOTE: DOMError do not have valid locator!
348       XMLLocator locator = fBuilder.getLocator();
349       reportError(message, (Exception) e, locator, fEditor, fViewer);
350       if (fHandler != null)
351         fHandler.handleError(error);
352       return true;
353     }
354 
355   }
356 
357   ////////////////////////////////////////////////////////////////////////
358 
359 }