KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > editor > codetemplates > AbbrevDetection


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.lib.editor.codetemplates;
21
22 import java.awt.event.ActionEvent JavaDoc;
23 import java.awt.event.ActionListener JavaDoc;
24 import java.awt.event.KeyEvent JavaDoc;
25 import java.awt.event.KeyListener JavaDoc;
26 import java.beans.PropertyChangeEvent JavaDoc;
27 import java.beans.PropertyChangeListener JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.logging.Level JavaDoc;
31 import java.util.logging.Logger JavaDoc;
32 import javax.swing.KeyStroke JavaDoc;
33 import javax.swing.Timer JavaDoc;
34 import javax.swing.event.CaretEvent JavaDoc;
35 import javax.swing.event.CaretListener JavaDoc;
36 import javax.swing.event.DocumentEvent JavaDoc;
37 import javax.swing.event.DocumentListener JavaDoc;
38 import javax.swing.text.BadLocationException JavaDoc;
39 import javax.swing.text.Document JavaDoc;
40 import javax.swing.text.JTextComponent JavaDoc;
41 import javax.swing.text.Position JavaDoc;
42 import org.netbeans.editor.Acceptor;
43 import org.netbeans.editor.AcceptorFactory;
44 import org.netbeans.editor.BaseDocument;
45 import org.netbeans.editor.Settings;
46 import org.netbeans.editor.SettingsChangeEvent;
47 import org.netbeans.editor.SettingsChangeListener;
48 import org.netbeans.editor.SettingsNames;
49 import org.netbeans.editor.SettingsUtil;
50 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
51 import org.netbeans.spi.editor.hints.ErrorDescription;
52 import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
53 import org.netbeans.spi.editor.hints.HintsController;
54 import org.netbeans.spi.editor.hints.Severity;
55 import org.openide.ErrorManager;
56 import org.openide.util.NbBundle;
57
58
59 /**
60  * Abbreviation detection detects typing of an abbreviation
61  * in the document.
62  *
63  * @author Miloslav Metelka
64  * @version 1.00
65  */

66
67 final class AbbrevDetection implements SettingsChangeListener, DocumentListener JavaDoc,
68 PropertyChangeListener JavaDoc, KeyListener JavaDoc, CaretListener JavaDoc {
69     
70     /**
71      * Document property which determines whether an ongoing document modification
72      * should be completely ignored by the abbreviation framework.
73      * <br/>
74      * This is useful e.g. for code templates parameter replication.
75      */

76     private static final String JavaDoc ABBREV_IGNORE_MODIFICATION_DOC_PROPERTY
77             = "abbrev-ignore-modification"; // NOI18N
78

79     private static final String JavaDoc SURROUND_WITH = NbBundle.getMessage(SurroundWithFix.class, "TXT_SurroundWithHint_Label"); //NOI18N
80
private static final int SURROUND_WITH_DELAY = 250;
81     
82     private static final AbbrevExpander[] abbrevExpanders = { new CodeTemplateAbbrevExpander() };
83
84     public static AbbrevDetection get(JTextComponent JavaDoc component) {
85         AbbrevDetection ad = (AbbrevDetection)component.getClientProperty(AbbrevDetection.class);
86         if (ad == null) {
87             ad = new AbbrevDetection(component);
88             component.putClientProperty(AbbrevDetection.class, ad);
89         }
90         return ad;
91     }
92     
93     private JTextComponent JavaDoc component;
94     
95     /** Document for which this abbreviation detection was constructed. */
96     private Document JavaDoc doc;
97     
98     /**
99      * Offset after the last typed character of the collected abbreviation.
100      */

101     private Position JavaDoc abbrevEndPosition;
102
103     /**
104      * Abbreviation characters captured from typing.
105      */

106     private StringBuffer JavaDoc abbrevChars = new StringBuffer JavaDoc();
107
108     /** Chars on which to expand acceptor */
109     private Acceptor expandAcceptor;
110
111     /** Which chars reset abbreviation accounting */
112     private Acceptor resetAcceptor;
113     
114     private ErrorDescription errorDescription = null;
115     private List JavaDoc surrounsWithFixes = null;
116     private Timer JavaDoc surroundsWithTimer;
117     
118     private AbbrevDetection(JTextComponent JavaDoc component) {
119         this.component = component;
120         component.addCaretListener(this);
121         doc = component.getDocument();
122         if (doc != null) {
123             doc.addDocumentListener(this);
124         }
125
126         Settings.addSettingsChangeListener(this);
127         
128         // Load the settings
129
settingsChange(null);
130         
131         component.addKeyListener(this);
132         component.addPropertyChangeListener(this);
133         
134         surroundsWithTimer = new Timer JavaDoc(0, new ActionListener JavaDoc() {
135             public void actionPerformed(ActionEvent JavaDoc e) {
136                 showSurroundWithHint();
137             }
138         });
139         surroundsWithTimer.setRepeats(false);
140     }
141
142     public void settingsChange(SettingsChangeEvent evt) {
143         Document JavaDoc d = doc;
144         Class JavaDoc kitClass = (d instanceof BaseDocument)
145             ? ((BaseDocument)d).getKitClass()
146             : org.netbeans.editor.BaseKit.class;
147
148         expandAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.ABBREV_EXPAND_ACCEPTOR, AcceptorFactory.FALSE);
149         resetAcceptor = SettingsUtil.getAcceptor(kitClass, SettingsNames.ABBREV_RESET_ACCEPTOR, AcceptorFactory.TRUE);
150     }
151     
152     public void insertUpdate(DocumentEvent JavaDoc evt) {
153         if (!isIgnoreModification()) {
154             if (DocumentUtilities.isTypingModification(evt) && !isAbbrevDisabled()) {
155                 int offset = evt.getOffset();
156                 int length = evt.getLength();
157                 appendTypedText(offset, length);
158             } else { // not typing modification -> reset abbreviation collecting
159
resetAbbrevChars();
160             }
161         }
162     }
163
164     public void removeUpdate(DocumentEvent JavaDoc evt) {
165         if (!isIgnoreModification()) {
166             if (DocumentUtilities.isTypingModification(evt) && !isAbbrevDisabled()) {
167                 int offset = evt.getOffset();
168                 int length = evt.getLength();
169                 removeAbbrevText(offset, length);
170             } else { // not typing modification -> reset abbreviation collecting
171
resetAbbrevChars();
172             }
173         }
174     }
175
176     public void changedUpdate(DocumentEvent JavaDoc evt) {
177     }
178
179     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
180         if ("document".equals(evt.getPropertyName())) {
181             if (doc != null) {
182                 doc.removeDocumentListener(this);
183             }
184             
185             doc = component.getDocument();
186             if (doc != null) {
187                 doc.addDocumentListener(this);
188             }
189             
190             settingsChange(null);
191         }
192     }
193     
194     public void keyPressed(KeyEvent JavaDoc evt) {
195         checkExpansionKeystroke(evt);
196     }
197     
198     public void keyReleased(KeyEvent JavaDoc evt) {
199         checkExpansionKeystroke(evt);
200     }
201     
202     public void keyTyped(KeyEvent JavaDoc evt) {
203         checkExpansionKeystroke(evt);
204     }
205     
206     public void caretUpdate(CaretEvent JavaDoc evt) {
207         if (evt.getDot() != evt.getMark()) {
208             surroundsWithTimer.setInitialDelay(SURROUND_WITH_DELAY);
209             surroundsWithTimer.restart();
210         } else {
211             surroundsWithTimer.stop();
212             hideSurroundWithHint();
213         }
214     }
215
216     private boolean isIgnoreModification() {
217         return Boolean.TRUE.equals(doc.getProperty(ABBREV_IGNORE_MODIFICATION_DOC_PROPERTY));
218     }
219     
220     private boolean isAbbrevDisabled() {
221         return org.netbeans.editor.Abbrev.isAbbrevDisabled(component);
222     }
223     
224     private void checkExpansionKeystroke(KeyEvent JavaDoc evt) {
225         KeyStroke JavaDoc expandKeyStroke = AbbrevSettings.getDefaultExpansionKeyStroke();
226         if (abbrevEndPosition != null && component != null
227                 && component.getCaretPosition() == abbrevEndPosition.getOffset()
228         ) {
229             Document JavaDoc doc = component.getDocument();
230             String JavaDoc mimeType = (String JavaDoc)doc.getProperty("mimeType"); // NOI18N
231
if (mimeType != null) {
232                 expandKeyStroke = AbbrevSettings.get(mimeType).getExpandKeyStroke();
233             }
234             
235             if (expandKeyStroke.equals(KeyStroke.getKeyStrokeForEvent(evt))) {
236                 if (expand()) {
237                     evt.consume();
238                 }
239             }
240         }
241     }
242
243     /**
244      * Get current abbreviation string.
245      */

246     private CharSequence JavaDoc getAbbrevText() {
247         return abbrevChars;
248     }
249
250     /**
251      * Reset abbreviation string collecting.
252      */

253     private void resetAbbrevChars() {
254         synchronized(abbrevChars) {
255             abbrevChars.setLength(0);
256             abbrevEndPosition = null;
257         }
258     }
259     
260     private void appendTypedText(int offset, int insertLength) {
261         if (abbrevEndPosition == null
262             || offset + insertLength != abbrevEndPosition.getOffset()
263         ) {
264             // Does not follow previous insert
265
resetAbbrevChars();
266         }
267
268         if (abbrevEndPosition == null) { // starting the new string
269
try {
270                 // Start new accounting if previous char would reset abbrev
271
// i.e. check that not start typing 'u' after existing 'p' which would
272
// errorneously expand to 'public'
273
if (offset == 0
274                         || resetAcceptor.accept(DocumentUtilities.getText(doc, offset - 1, 1).charAt(0))
275                 ) {
276                     abbrevEndPosition = doc.createPosition(offset + insertLength);
277                 }
278             } catch (BadLocationException JavaDoc e) {
279                 ErrorManager.getDefault().notify(e);
280             }
281         }
282
283         if (abbrevEndPosition != null) {
284             try {
285                 String JavaDoc typedText = doc.getText(offset, insertLength); // typically just one char
286
boolean textAccepted = true;
287                 for (int i = typedText.length() - 1; i >= 0; i--) {
288                     if (resetAcceptor.accept(typedText.charAt(i))) {
289                         // In theory there could be more than one character in the typed text
290
// and the resetting could occur on the very first char
291
// the next chars would not be accumulated as the insert
292
// is treated as a batch.
293
textAccepted = false;
294                         break;
295                     }
296                 }
297                 
298                 if (textAccepted) {
299                     abbrevChars.append(typedText);
300                     // abbrevEndPosition should move appropriately
301
} else {
302                     resetAbbrevChars();
303                 }
304
305             } catch (BadLocationException JavaDoc e) {
306                 ErrorManager.getDefault().notify(e);
307                 resetAbbrevChars();
308             }
309         }
310     }
311     
312     private void removeAbbrevText(int offset, int removeLength) {
313         synchronized(abbrevChars) {
314             if (abbrevEndPosition != null) {
315                 // Abbrev position should already move appropriately
316
if (offset == abbrevEndPosition.getOffset()
317                     && abbrevChars.length() >= removeLength
318                 ) { // removed at end
319
abbrevChars.setLength(abbrevChars.length() - removeLength);
320
321                 } else {
322                     resetAbbrevChars();
323                 }
324             }
325         }
326     }
327
328     public boolean expand() {
329         CharSequence JavaDoc abbrevText = getAbbrevText();
330         int abbrevEndOffset = abbrevEndPosition.getOffset();
331         for (int i = 0; i < abbrevExpanders.length; i++) {
332             if (abbrevExpanders[i].expand(component,
333                     abbrevEndOffset - abbrevText.length(), abbrevText)
334             ) {
335                 resetAbbrevChars();
336                 return true;
337             }
338         }
339         return false;
340     }
341     
342     private void showSurroundWithHint() {
343         surrounsWithFixes = SurroundWithFix.getFixes(component);
344         if (!surrounsWithFixes.isEmpty()) {
345             try {
346                 Position JavaDoc pos = doc.createPosition(component.getCaretPosition());
347                 errorDescription = ErrorDescriptionFactory.createErrorDescription(Severity.HINT, SURROUND_WITH, surrounsWithFixes, doc, pos, pos);
348                 HintsController.setErrors(doc, SURROUND_WITH, Collections.singleton(errorDescription));
349             } catch (BadLocationException JavaDoc ble) {
350                 Logger.getLogger("global").log(Level.WARNING, ble.getMessage(), ble);
351             }
352         } else {
353             hideSurroundWithHint();
354         }
355     }
356
357     private void hideSurroundWithHint() {
358         if (surrounsWithFixes != null)
359             surrounsWithFixes = null;
360         if (errorDescription != null) {
361             errorDescription = null;
362             HintsController.setErrors(doc, SURROUND_WITH, Collections.emptySet());
363         }
364     }
365 }
366
Popular Tags