KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > debugger > input > DBInputProcessorToken


1 /*
2
3 [The "BSD licence"]
4 Copyright (c) 2005 Jean Bovet
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products
17 derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 */

31
32 package org.antlr.works.debugger.input;
33
34 import org.antlr.xjlib.foundation.XJSystem;
35 import org.antlr.xjlib.foundation.notification.XJNotificationCenter;
36 import org.antlr.xjlib.foundation.notification.XJNotificationObserver;
37 import org.antlr.runtime.Token;
38 import org.antlr.works.debugger.Debugger;
39 import org.antlr.works.prefs.AWPrefs;
40 import org.antlr.works.prefs.AWPrefsDialog;
41 import org.antlr.works.utils.TextPane;
42 import org.antlr.works.utils.TextPaneDelegate;
43
44 import javax.swing.*;
45 import javax.swing.text.AttributeSet JavaDoc;
46 import javax.swing.text.BadLocationException JavaDoc;
47 import javax.swing.text.SimpleAttributeSet JavaDoc;
48 import javax.swing.text.StyleConstants JavaDoc;
49 import java.awt.*;
50 import java.awt.event.MouseAdapter JavaDoc;
51 import java.awt.event.MouseEvent JavaDoc;
52 import java.awt.event.MouseMotionAdapter JavaDoc;
53 import java.awt.geom.GeneralPath JavaDoc;
54 import java.util.*;
55
56 public class DBInputProcessorToken implements DBInputProcessor, TextPaneDelegate, XJNotificationObserver {
57
58     public static final Color HIGHLIGHTED_COLOR = new Color(0, 0.5f, 1, 0.4f);
59     public static final Color INPUT_BREAKPOINT_COLOR = new Color(1, 0.2f, 0, 0.5f);
60
61     protected Debugger debugger;
62     protected TextPane textPane;
63     protected int mouseIndex = -1;
64
65     protected LinkedList<Integer JavaDoc> inputTokenIndexes = new LinkedList<Integer JavaDoc>();
66     protected Map<Integer JavaDoc,DBInputTextTokenInfo> indexToTokenInfoMap = new HashMap<Integer JavaDoc, DBInputTextTokenInfo>();
67     protected Map<Integer JavaDoc,AttributeSet JavaDoc> indexToConsumeAttributeMap = new HashMap<Integer JavaDoc, AttributeSet JavaDoc>();
68     protected Set JavaDoc<Integer JavaDoc> lookaheadTokenIndexes = new HashSet<Integer JavaDoc>();
69
70     /** Current token index */
71     protected int currentTokenIndex;
72
73     /** Current token position in the input text */
74     protected int currentTokenIndexInText;
75
76     /** Last location event received by the debugger */
77     protected int locationLine;
78     protected int locationCharInLine;
79
80     /** Input breakpoints */
81     protected Set JavaDoc<Integer JavaDoc> inputBreakpointIndexes = new HashSet<Integer JavaDoc>();
82
83     protected SimpleAttributeSet JavaDoc attributeNonConsumed;
84     protected SimpleAttributeSet JavaDoc attributeConsume;
85     protected SimpleAttributeSet JavaDoc attributeConsumeHidden;
86     protected SimpleAttributeSet JavaDoc attributeConsumeDead;
87     protected SimpleAttributeSet JavaDoc attributeLookahead;
88
89     protected boolean drawTokensBox;
90
91     public DBInputProcessorToken(Debugger debugger, TextPane textPane) {
92         this.debugger = debugger;
93
94         this.textPane = textPane;
95         this.textPane.setDelegate(this);
96         this.textPane.addMouseListener(new MyMouseListener());
97         this.textPane.addMouseMotionListener(new MyMouseMotionListener());
98
99         drawTokensBox = false;
100
101         reset();
102         createTextAttributes();
103
104         XJNotificationCenter.defaultCenter().addObserver(this, AWPrefsDialog.NOTIF_PREFS_APPLIED);
105     }
106
107     public void close() {
108         XJNotificationCenter.defaultCenter().removeObserver(this);
109     }
110
111     public void setDrawTokensBox(boolean flag) {
112         drawTokensBox = flag;
113         textPane.repaint();
114     }
115
116     public boolean isTokensBoxVisible() {
117         return drawTokensBox;
118     }
119
120     public int getCurrentTokenIndex() {
121         return currentTokenIndex;
122     }
123
124     public void setLocation(int line, int charInLine) {
125         this.locationLine = line;
126         this.locationCharInLine = charInLine;
127     }
128
129     public void consumeToken(Token token, int flavor) {
130         if(ignoreToken(token))
131             return;
132
133         SimpleAttributeSet JavaDoc attr = null;
134         switch(flavor) {
135             case TOKEN_NORMAL: attr = attributeConsume; break;
136             case TOKEN_HIDDEN: attr = attributeConsumeHidden; break;
137             case TOKEN_DEAD: attr = attributeConsumeDead; break;
138         }
139         addToken(token);
140         addConsumeAttribute(token, attr);
141         removeTokenLT(token);
142     }
143
144     public void LT(Token token) {
145         addToken(token);
146         addTokenLT(token);
147     }
148
149     /**
150      * On Windows, ignore the LF token following a CR because each
151      * end of line is represented by two characters while Swing
152      * renders the text using only LF (normalized).
153      */

154     public boolean ignoreToken(Token t) {
155         if(!XJSystem.isWindows())
156             return false;
157
158         Token ct = getCurrentToken();
159         if(ct == null)
160             return false;
161
162         return ct.getText().equals("\r") && t.getText().equals("\n");
163     }
164
165     public void addConsumeAttribute(Token token, AttributeSet JavaDoc attribute) {
166         indexToConsumeAttributeMap.put(token.getTokenIndex(), attribute);
167     }
168
169     public void addTokenLT(Token token) {
170         lookaheadTokenIndexes.add(token.getTokenIndex());
171     }
172
173     public void removeTokenLT(Token token) {
174         lookaheadTokenIndexes.remove(Integer.valueOf(token.getTokenIndex()));
175     }
176
177     public void removeAllLT() {
178         lookaheadTokenIndexes.clear();
179     }
180
181     public void stop() {
182         inputBreakpointIndexes.clear();
183     }
184
185     public void reset() {
186         textPane.setText("");
187         textPane.setCharacterAttributes(SimpleAttributeSet.EMPTY, true);
188
189         currentTokenIndex = -1;
190         currentTokenIndexInText = 0;
191
192         inputTokenIndexes.clear();
193         indexToTokenInfoMap.clear();
194         indexToConsumeAttributeMap.clear();
195         lookaheadTokenIndexes.clear();
196     }
197
198     public void rewindAll() {
199         rewind(-1);
200     }
201
202     public void rewind(int start) {
203         currentTokenIndex = start;
204
205         /** Remove any consume and lookahead attribute for any token with index
206          * greater than start
207          */

208         for (Integer JavaDoc idx : inputTokenIndexes) {
209             if (idx >= start) {
210                 indexToConsumeAttributeMap.remove(idx);
211                 lookaheadTokenIndexes.remove(idx);
212             }
213         }
214     }
215
216     public void addToken(Token token) {
217         int index = token.getTokenIndex();
218         if(index == -1) {
219             // Ignore this index (it is used, for example, for EOF)
220
return;
221         }
222
223         currentTokenIndex = index;
224
225         /** Insert the index into the list of sorted indexes - used to render the token */
226
227         if(!indexToTokenInfoMap.containsKey(index)) {
228             if(inputTokenIndexes.isEmpty())
229                 inputTokenIndexes.add((Integer JavaDoc)index);
230             else {
231                 for(int i=inputTokenIndexes.size()-1; i >= 0; i--) {
232                     Integer JavaDoc n = inputTokenIndexes.get(i);
233                     if(n < index) {
234                         inputTokenIndexes.add(i+1, (Integer JavaDoc)index);
235                         break;
236                     }
237                 }
238             }
239         }
240
241         /** Add the token even if it is already in the map because its position or attribute
242          * may have changed
243          */

244
245         indexToTokenInfoMap.put((Integer JavaDoc)index, new DBInputTextTokenInfo(token, locationLine, locationCharInLine));
246     }
247
248     public Token getCurrentToken() {
249         DBInputTextTokenInfo info = indexToTokenInfoMap.get(getCurrentTokenIndex());
250         if(info == null)
251             return null;
252         else
253             return info.token;
254     }
255
256     public String JavaDoc renderTokensText() {
257         currentTokenIndexInText = 0;
258         StringBuffer JavaDoc text = new StringBuffer JavaDoc();
259         for (Integer JavaDoc idx : inputTokenIndexes) {
260             DBInputTextTokenInfo info = indexToTokenInfoMap.get(idx);
261             info.setStart(text.length());
262             text.append(info.getText());
263
264             if (idx == getCurrentTokenIndex())
265                 currentTokenIndexInText = info.start;
266         }
267         return text.toString();
268     }
269
270     public void render() {
271         /** Apply the text */
272
273         String JavaDoc text = renderTokensText();
274         textPane.setText(text);
275         textPane.getStyledDocument().setCharacterAttributes(0, text.length(), SimpleAttributeSet.EMPTY, true);
276
277         /** Apply the style for each token */
278         for (Integer JavaDoc idx : inputTokenIndexes) {
279             DBInputTextTokenInfo info = indexToTokenInfoMap.get(idx);
280             AttributeSet JavaDoc attribute = indexToConsumeAttributeMap.get(idx);
281             if (attribute == null)
282                 attribute = attributeNonConsumed;
283
284             /** LT attribute override the other */
285             if (lookaheadTokenIndexes.contains(idx))
286                 attribute = attributeLookahead;
287
288             textPane.getStyledDocument().setCharacterAttributes(info.start, info.end, attribute, true);
289         }
290     }
291
292     public void updateOnBreakEvent() {
293         render();
294
295         /** Scroll the text pane to the current token position. Invoke that later on
296          * so the pane scrolls at the correct position (otherwise the scroll will be reset).
297          */

298         SwingUtilities.invokeLater(new Runnable JavaDoc() {
299             public void run() {
300                 try {
301                     textPane.scrollRectToVisible(textPane.modelToView(currentTokenIndexInText));
302                 } catch (BadLocationException JavaDoc e) {
303                     debugger.getConsole().print(e);
304                 }
305             }
306         });
307     }
308
309     public void createTextAttributes() {
310         attributeNonConsumed = new SimpleAttributeSet JavaDoc();
311         StyleConstants.setForeground(attributeNonConsumed, AWPrefs.getNonConsumedTokenColor());
312
313         attributeConsume = new SimpleAttributeSet JavaDoc();
314         StyleConstants.setForeground(attributeConsume, AWPrefs.getConsumedTokenColor());
315
316         attributeConsumeHidden = new SimpleAttributeSet JavaDoc();
317         StyleConstants.setForeground(attributeConsumeHidden, AWPrefs.getHiddenTokenColor());
318
319         attributeConsumeDead = new SimpleAttributeSet JavaDoc();
320         StyleConstants.setForeground(attributeConsumeDead, AWPrefs.getDeadTokenColor());
321
322         attributeLookahead = new SimpleAttributeSet JavaDoc();
323         StyleConstants.setForeground(attributeLookahead, AWPrefs.getLookaheadTokenColor());
324         StyleConstants.setItalic(attributeLookahead, true);
325     }
326
327     public void textPaneDidPaint(Graphics g) {
328         for (DBInputTextTokenInfo info : indexToTokenInfoMap.values()) {
329             if (drawTokensBox)
330                 drawToken(info, (Graphics2D) g, Color.red, false);
331
332             if (inputBreakpointIndexes.contains(Integer.valueOf(info.token.getTokenIndex())))
333                 drawToken(info, (Graphics2D) g, INPUT_BREAKPOINT_COLOR, true);
334             else if (mouseIndex >= info.start && mouseIndex < info.end)
335                 drawToken(info, (Graphics2D) g, HIGHLIGHTED_COLOR, true);
336         }
337     }
338
339     public void drawToken(DBInputTextTokenInfo info, Graphics2D g, Color c, boolean fill) {
340         g.setColor(c);
341         try {
342             Rectangle r1 = textPane.modelToView(info.start);
343             Rectangle r2 = textPane.modelToView(info.end);
344
345             if(r2.y > r1.y) {
346                 // Token is displayed on more than one line
347
// We will simply create a path containing the
348
// outline of the token spanning on multiple lines
349

350                 GeneralPath JavaDoc gp = new GeneralPath JavaDoc();
351
352                 Rectangle r = null;
353                 Rectangle pr = null;
354                 int line_y = r1.y;
355
356                 gp.moveTo(r1.x, r1.y);
357                 for(int index=info.start; index<info.end; index++) {
358                     r = textPane.modelToView(index);
359                     if(r.y > line_y) {
360                         // Draw a line between the last point of the path
361
// and the last rectangle of the line which is pr
362
// because r is already in the new line.
363
line_y = r.y;
364                         if(pr != null) {
365                             gp.lineTo(pr.x+pr.width, pr.y);
366                             gp.lineTo(pr.x+pr.width, pr.y+pr.height);
367                         }
368                     }
369                     pr = r;
370                 }
371                 if(r != null) {
372                     gp.lineTo(r.x+r.width, r.y);
373                     gp.lineTo(r.x+r.width, r.y+r.height);
374
375                     gp.lineTo(r1.x, r.y+r.height);
376                     gp.lineTo(r1.x, r1.y);
377                 }
378                 if(fill)
379                     g.fill(gp);
380                 else
381                     g.draw(gp);
382             } else {
383                 if(fill)
384                     g.fillRect(r1.x, r1.y, r2.x-r1.x, r1.height);
385                 else
386                     g.drawRect(r1.x, r1.y, r2.x-r1.x, r1.height);
387             }
388         } catch (BadLocationException JavaDoc e) {
389             // Ignore exception
390
}
391     }
392
393     public DBInputTextTokenInfo getTokenInfoAtTokenIndex(int index) {
394         return indexToTokenInfoMap.get(index);
395     }
396
397     public DBInputTextTokenInfo getTokenInfoAtPositionIndex(int index) {
398         for (DBInputTextTokenInfo info : indexToTokenInfoMap.values()) {
399             if (index >= info.start && index < info.end)
400                 return info;
401         }
402         return null;
403     }
404
405     public boolean isBreakpointAtToken(Token token) {
406         return inputBreakpointIndexes.contains(Integer.valueOf(token.getTokenIndex()));
407     }
408
409     /** This method highlights the token at the specified index
410      * in the input stream.
411      */

412     public void highlightToken(int index) {
413         mouseIndex = index;
414         textPane.repaint();
415     }
416
417     /** This method selects the token t in both the grammar and the
418      * input stream
419      */

420     public void selectToken(Token t) {
421         if(t == null) {
422             highlightToken(-1);
423             return;
424         }
425
426         DBInputTextTokenInfo info = getTokenInfoForToken(t);
427         if(info != null)
428             highlightToken(info.start);
429         else
430             highlightToken(-1);
431     }
432
433     public DBInputTextTokenInfo getTokenInfoForToken(Token t) {
434         for (DBInputTextTokenInfo info : indexToTokenInfoMap.values()) {
435             // FIX AW-61 - compare also the token type to avoid selecting the wrong one (e.g. imaginary)
436
if (info.token.getTokenIndex() == t.getTokenIndex() &&
437                     info.token.getType() == t.getType())
438                 return info;
439         }
440         return null;
441     }
442
443     public void notificationFire(Object JavaDoc source, String JavaDoc name) {
444         if(name.equals(AWPrefsDialog.NOTIF_PREFS_APPLIED)) {
445             createTextAttributes();
446         }
447     }
448
449     protected class MyMouseListener extends MouseAdapter JavaDoc {
450
451         public void mousePressed(MouseEvent JavaDoc e) {
452             highlightToken(textPane.getTextIndexAtLocation(e.getPoint()));
453             if(mouseIndex == -1)
454                 return;
455
456             DBInputTextTokenInfo info = getTokenInfoAtPositionIndex(mouseIndex);
457             if(info == null)
458                 return;
459
460             boolean shiftKey = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK;
461             if(e.getButton() == MouseEvent.BUTTON1 && !shiftKey) {
462                 debugger.selectToken(info.token, info.line, info.charInLine);
463             } else {
464                 Integer JavaDoc index = info.token.getTokenIndex();
465                 if(inputBreakpointIndexes.contains(index))
466                     inputBreakpointIndexes.remove(index);
467                 else
468                     inputBreakpointIndexes.add(index);
469             }
470         }
471
472         public void mouseExited(MouseEvent JavaDoc e) {
473             highlightToken(-1);
474         }
475     }
476
477     protected class MyMouseMotionListener extends MouseMotionAdapter JavaDoc {
478
479         public void mouseMoved(MouseEvent JavaDoc e) {
480             mouseIndex = textPane.getTextIndexAtLocation(e.getPoint());
481             textPane.repaint();
482         }
483     }
484 }
485
Popular Tags