C:\Java WorkShop\Lexx\src\lexx\parser\JavaParserListener.java

1    package lexx.parser; 
2     
3    import java.awt.Dimension; 
4    import javax.swing.JTextPane; 
5    import javax.swing.event.DocumentEvent; 
6    import javax.swing.text.BadLocationException; 
7    import javax.swing.text.StyledDocument; 
8     
9    import lexx.api.JavaPackages; 
10   import lexx.oldversion.syntax.HighLight; 
11   import lexx.parser.ast.ASTAnalyser; 
12   import lexx.parser.ast.ASTDebug; 
13   import lexx.parser.ast.IncrementAST; 
14   import lexx.parser.ast.java.JavaAST; 
15   import lexx.parser.ast.java.JavaASTAnalyser; 
16   import lexx.parser.ast.java.ParseJavaAST; 
17    
18   /** 
19    * <p>Performs parsing on a java and produces a JavaAST</p> 
20    * <p>This package takes in an DocumentEvent and creates an AST from the java 
21    * source code.</p> 
22    * <p>At the moment parsing occurs in two stages<p> 
23    * <p>1) When the user enters a keyword such as ';', '{' or '}' 
24    * <p>2) An timer is used to perfom the parsing on non keyworks</p> 
25    * <p>Copyright (c) 2002-2003</p> 
26    * @since 20/03/2003 
27    * @author Mohammed Imran 
28    * @version 1.0 
29    */ 
30   public final class JavaParserListener extends ParserListener 
31   { 
32     private static final org.apache.log4j.Logger log = org.apache.log4j.Logger. 
33         getLogger(JavaParserListener.class); 
34    
35     /** 
36      * Performs paring on a java and produces a JavaAST 
37      * @param packages required to analyse the AST can find variable declarations 
38      * @param pane required to get the text from the java code 
39      */ 
40     public JavaParserListener(JavaPackages packages, JTextPane pane) 
41     { 
42       pack = packages; 
43       textPane = pane; 
44    
45       try 
46       { 
47         code = chng.getDocument().getText(0, chng.getDocument().getLength()); 
48         root = ParseJavaAST.parseComplete(code); 
49       } 
50       catch(Exception ex) 
51       { 
52         log.warn("Unable to parse code " + ex, ex); 
53       } 
54       this.setupTimer(); 
55     } 
56    
57     private JTextPane textPane = null; 
58    
59     private ASTDebug testFrame = null; 
60    
61     public final ASTAnalyser getAnalyser() 
62     { 
63       if(root == null) 
64       { 
65         return new JavaASTAnalyser(new JavaAST(), (JavaPackages) pack); 
66       } 
67       else 
68       { 
69         return new JavaASTAnalyser(root, (JavaPackages) pack); 
70       } 
71     } 
72    
73     private DocumentEvent chng; 
74     private boolean wasInserted; 
75    
76     /** 
77      * Where parsing of the java code actually occurs 
78      * <p>At the moment parsing occurs in two stages<p> 
79      * <p>1) When the user enters a keyword such as ';', '{' or '}' 
80      * <p>2) An timer is used to perfom the parsing on non keyworks</p> 
81      * <p>also note that this runs in a thread so the user isn't waiting for the 
82      * java code to parse</p> 
83      * @param changed the documentevent telling where the change of text occured 
84      * @param wasTextInserted tells you if the text was inserted or removed 
85      */ 
86     protected final synchronized void scan(DocumentEvent changed, 
87                                            boolean wasTextInserted) 
88     { 
89       chng = changed; 
90       wasInserted = wasTextInserted; 
91    
92       if( System.getProperty("incrementalast") != null && 
93           System.getProperty("incrementalast").equals("true") ) 
94       { 
95         if(root != null && root.getMax() < root.getMin()) 
96         { //means that the tree is invalid 
97           this.scanIncrmental(changed, wasTextInserted); 
98           return; 
99         } 
100      } 
101   
102      new Thread() 
103      { 
104        public void run() 
105        { 
106          this.setPriority(Thread.MIN_PRIORITY + 2); 
107          try 
108          { 
109            code = chng.getDocument().getText(0, chng.getDocument().getLength()); 
110   
111            if(codeBeautifier(chng, wasInserted)) 
112            { 
113              return; 
114            } 
115   
116            String text = ""; 
117            String keywords = ""; 
118   
119            if(wasInserted) 
120            { 
121              text = code.substring(chng.getOffset(), 
122                                    chng.getOffset() + chng.getLength()); 
123              keywords = ";{}"; //(){}"; 
124            } 
125            else 
126            { 
127              text = code.substring(chng.getOffset() - chng.getLength(), 
128                                    chng.getOffset()); 
129              keywords = "(){}"; 
130            } 
131   
132            boolean goThrough = true; 
133   
134            if(root != null) 
135            { 
136              text = text.substring(text.length() - 1, text.length()); 
137   
138            } 
139   
140            if(lexx.utils.StringSearch.containsChar(keywords, text)) 
141            { 
142              timerOn = false; 
143              IncrementAST tmp = null; 
144              try 
145              { 
146                tmp = ParseJavaAST.parsePartially(code); 
147              } 
148              catch(Exception ex1) 
149              { 
150                log.warn("exeception in scan\n\n" + ex1, ex1); 
151              } 
152              if(tmp != null) 
153              { 
154                root = tmp; 
155                goThrough = false; 
156              } 
157            } 
158            else 
159            { 
160              timerOn = true; 
161   
162            } 
163   
164            if(lexx.utils.Config.useOldSyntaxHightlight()) 
165            { 
166              syntaxHighlight(0, chng.getDocument().getLength()); 
167            } 
168   
169            if(root == null) 
170            { 
171              return; 
172            } 
173   
174            if(goThrough) 
175            { 
176              if(wasInserted) 
177              { 
178                root.increament(chng.getLength(), chng.getOffset()); 
179              } 
180              else 
181              { 
182                root.increament( -chng.getLength(), chng.getOffset()); 
183              } 
184            } 
185   
186            if(lexx.utils.Config.isDebug()) 
187            { 
188              if(showDebugTree && root != null) 
189              { 
190                if(testFrame == null) 
191                { 
192                  testFrame = new ASTDebug( (StyledDocument) chng.getDocument()); 
193   
194                } 
195                testFrame.update(root); 
196              } 
197              else 
198              { 
199                if(testFrame != null) 
200                { 
201                  testFrame.setVisible(false); 
202                  testFrame.dispose(); 
203                } 
204                testFrame = null; 
205              } 
206            } 
207          } 
208          catch(Exception ex) 
209          { 
210            log.warn("exeception in scan\n\n" + ex, ex); 
211          } 
212        } 
213      }.start(); 
214    } 
215   
216    /** 
217     * Uses incrmental parsing note this is still under construction 
218     */ 
219    protected final void scanIncrmental(DocumentEvent chng, 
220                                                boolean wasInserted) 
221    { 
222      try 
223      { 
224        code = chng.getDocument().getText(0, chng.getDocument().getLength()); 
225   
226        if(codeBeautifier(chng, wasInserted)) 
227        { 
228          return; 
229        } 
230   
231        if(code.length() < 2000 && true) 
232        { 
233          if(log.isDebugEnabled()) 
234          { 
235            log.debug("Size of code is less than 2000, so parseing code fully"); 
236          } 
237          try 
238          { 
239            root = ParseJavaAST.parsePartially(code); 
240            return; 
241          } 
242          catch(Exception ex) 
243          { 
244            log.fatal(ex, ex); 
245          } 
246        } 
247   
248        if(log.isDebugEnabled()) 
249        { 
250          log.debug("Method: scan - scanning code\n\n\n" + code); 
251   
252        } 
253        if(root == null) //need to parse whole text 
254        { 
255          log.info("Parsing whole code"); 
256   
257          try 
258          { 
259            root = ParseJavaAST.parseComplete(code); 
260          } 
261          catch(Exception ex) 
262          { 
263            log.warn(ex); 
264          } 
265   
266          if(lexx.utils.Config.useOldSyntaxHightlight()) 
267          { 
268            this.syntaxHighlight(0, chng.getDocument().getLength()); 
269          } 
270        } 
271        else 
272        { //parse only section of coding that has changed 
273          int[] block = null; 
274          if(wasInserted) 
275          { 
276            try 
277            { 
278              block = root.updateTree(code, chng.getOffset(), chng.getLength()); 
279            } 
280            catch(Exception ex) 
281            { 
282              root = null; 
283              log.fatal("Unable to parse code", ex); 
284            } 
285          } 
286          else 
287          { 
288            try 
289            { 
290              if(chng.getLength() == 1 && code.charAt(chng.getOffset()) == '\n') 
291              { 
292                root.increament( -1, chng.getOffset()); 
293              } 
294              else 
295              { 
296                block = root.updateTree(code, chng.getOffset(), -chng.getLength()); 
297              } 
298            } 
299            catch(Exception ex) 
300            { 
301              root = null; 
302              log.fatal("Unable to parse code", ex); 
303            } 
304          } 
305   
306          if(lexx.utils.Config.useOldSyntaxHightlight() && block != null) 
307          { 
308            this.syntaxHighlight(block[0], block[1]); 
309          } 
310        } 
311   
312        if(showDebugTree && root != null) 
313        { 
314          if(testFrame == null) 
315          { 
316            testFrame = new ASTDebug( (StyledDocument) chng.getDocument()); 
317   
318          } 
319          testFrame.update(root); 
320        } 
321        else 
322        { 
323          if(testFrame != null) 
324          { 
325            testFrame.setVisible(false); 
326            testFrame.dispose(); 
327          } 
328          testFrame = null; 
329        } 
330      } 
331      catch(BadLocationException ex) 
332      { 
333        log.fatal("Method scan(" + chng + "," + wasInserted + ") ", ex); 
334      } 
335   
336      if(root == null || root.getMax() < root.getMin()) //means that the tree is invalid 
337      { 
338        try 
339        { 
340          root = ParseJavaAST.parsePartially(code); 
341        } 
342        catch(Exception ex) 
343        { 
344          log.fatal(ex, ex); 
345        } 
346      } 
347    } 
348   
349    /** 
350     * Simple code beautifier for java code 
351     */ 
352    private synchronized boolean codeBeautifier(DocumentEvent chng, 
353                                                boolean wasInserted) throws 
354        BadLocationException 
355    { 
356      if(! (chng.getLength() == 1 && code.charAt(chng.getOffset()) == '\n') || 
357         !wasInserted) 
358      { 
359        return false; 
360      } 
361   
362      Dimension dim = this.getCoord(chng.getOffset()); 
363      String text = chng.getDocument().getText(this.getPos( (int) dim. 
364          getHeight(), 0), (int) dim.getWidth()).replaceAll("\n", ""); 
365      String spaces = ""; 
366      for(int i = 0; i < text.length(); i++) 
367      { 
368        if(Character.isSpaceChar(text.charAt(i))) 
369        { 
370          spaces += ' '; 
371        } 
372        else 
373        { 
374          if(text.charAt(i) == '{' || 
375             text.charAt(text.length() - 1) == '{') 
376          { 
377            spaces += "  "; 
378          } 
379          break; 
380        } 
381      } 
382   
383      if(spaces.length() > 0) 
384      { 
385        new lexx.utils.Utilities().insertText(textPane, 
386                                              chng.getOffset() + 1, 
387                                              spaces); 
388      } 
389   
390      if(root != null) 
391      { 
392        root.increament(chng.getLength(), chng.getOffset()); 
393   
394      } 
395      return true; 
396    } 
397   
398    private boolean showDebugTree = lexx.utils.Config.isDebug(); 
399   
400    public final void setVisibleDebugAST(boolean answer) 
401    { 
402      showDebugTree = answer; 
403    } 
404   
405    public final IncrementAST getIncrementAST(int pos) 
406    { 
407      if(root != null) 
408      { 
409        return root.getIncrementAST(pos); 
410      } 
411      else 
412      { 
413        return null; 
414      } 
415    } 
416   
417    public final boolean isDebugASTVisible() 
418    { 
419      return showDebugTree; 
420    } 
421   
422    private boolean timerOn = false; 
423   
424    private void setupTimer() 
425    { 
426      new Thread() 
427      { 
428        public void run() 
429        { 
430          this.setPriority(Thread.MIN_PRIORITY); 
431          int maxSize = 1024 * 5; 
432          while(true) 
433          { 
434            try 
435            { 
436              this.sleep(1000); 
437            } 
438            catch(InterruptedException ex) 
439            { 
440              log.fatal(ex, ex); 
441            } 
442   
443            if(textPane.isCursorSet() && textPane.isShowing() && 
444               textPane.isVisible() && textPane.getDocument().getLength() < maxSize) 
445            { 
446   
447              try 
448              { 
449                if(timerOn) 
450                { 
451                  if(lexx.utils.Config.useOldSyntaxHightlight()) 
452                  { 
453                    syntaxHighlight(0, chng.getDocument().getLength()); 
454                  } 
455   
456                  String newCode = textPane.getDocument().getText(0, 
457                      textPane.getDocument().getLength()); 
458   
459                  JavaAST tmp = ParseJavaAST.parsePartially(newCode); 
460                  root = tmp; 
461                  code = newCode; 
462                } 
463              } 
464              catch(Exception ex) 
465              { 
466                log.fatal(ex, ex); 
467              } 
468              timerOn = false; 
469            } 
470          } 
471        } 
472      }.start(); 
473    } 
474   
475    /** 
476     * Used in my old version of syntaxhighlighting refer lexx.oldversion 
477     */ 
478    private void syntaxHighlight(int startPos, int endPos) 
479    { 
480      if(startPos + 1 > doc.getLength()) 
481      { 
482        log.fatal("Method syntaxHighlight: startpos is " + startPos + 
483                  " and text legnth is " + doc.getLength()); 
484        startPos = doc.getLength() - 1; 
485      } 
486   
487      if(endPos + 1 > doc.getLength()) 
488      { 
489        if(endPos + 1 != doc.getLength()) //simple mistake ignore 
490        { 
491          log.warn("Method syntaxHighlight: endpos is " + endPos + 
492                   " and text legnth is " + doc.getLength()); 
493        } 
494        else 
495        { 
496          log.fatal("Method syntaxHighlight: endpos is " + endPos + 
497                    " and text legnth is " + doc.getLength()); 
498        } 
499        endPos = doc.getLength() - 1; 
500      } 
501   
502      if( log.isInfoEnabled() ) 
503        log.info("Method: syntaxHighlight [" + startPos + "," + endPos + "]"); 
504   
505      new HighLight(startPos, endPos, (StyledDocument) doc).start(); 
506    } 
507  }