001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.awt.event.ActionEvent;
017: import java.beans.PropertyChangeEvent;
018: import java.beans.PropertyChangeListener;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.Map;
022:
023: import javax.swing.Action;
024: import javax.swing.text.BadLocationException;
025: import javax.swing.text.Caret;
026: import javax.swing.text.JTextComponent;
027:
028: /**
029: * Abbreviation support allowing to expand defined character sequences into the
030: * expanded strings or call the arbitrary action.
031: *
032: * @author Miloslav Metelka
033: * @version 1.00
034: */
035:
036: public class Abbrev implements SettingsChangeListener,
037: PropertyChangeListener {
038:
039: /**
040: * Abbreviation accounting string. Here the characters forming abbreviation
041: * are stored.
042: */
043: private StringBuffer abbrevSB = new StringBuffer();
044:
045: /**
046: * Check whether the document text matches the abbreviation accounting
047: * string.
048: */
049: private boolean checkDocText;
050:
051: /**
052: * Additional check whether the character right before the abbreviation
053: * string in the text is not accepted by the <tt>addTypedAcceptor</tt>.
054: * This test is only performed if <tt>checkDocText</tt> is true.
055: */
056: private boolean checkTextDelimiter;
057:
058: /** Extended UI to which this abbreviation is associated to */
059: protected EditorUI editorUI;
060:
061: /** Chars on which to expand acceptor */
062: private Acceptor doExpandAcceptor;
063:
064: /** Whether add the typed char */
065: private Acceptor addTypedAcceptor;
066:
067: /** Which chars reset abbreviation accounting */
068: private Acceptor resetAcceptor;
069:
070: /** Abbreviation map */
071: private HashMap abbrevMap;
072:
073: public Abbrev(EditorUI editorUI, boolean checkDocText,
074: boolean checkTextDelimiter) {
075: this .editorUI = editorUI;
076: this .checkDocText = checkDocText;
077: this .checkTextDelimiter = checkTextDelimiter;
078:
079: Settings.addSettingsChangeListener(this );
080:
081: synchronized (editorUI.getComponentLock()) {
082: // if component already installed in EditorUI simulate installation
083: JTextComponent component = editorUI.getComponent();
084: if (component != null) {
085: propertyChange(new PropertyChangeEvent(editorUI,
086: EditorUI.COMPONENT_PROPERTY, null, component));
087: }
088:
089: editorUI.addPropertyChangeListener(this );
090: }
091: }
092:
093: /**
094: * Called when settings were changed. The method is called by editorUI when
095: * settings were changed and from constructor.
096: */
097: public void settingsChange(SettingsChangeEvent evt) {
098: Class kitClass = Utilities.getKitClass(editorUI.getComponent());
099:
100: if (kitClass != null) {
101: String settingName = (evt != null) ? evt.getSettingName()
102: : null;
103:
104: if (settingName == null
105: || SettingsNames.ABBREV_ACTION_MAP
106: .equals(settingName)
107: || SettingsNames.ABBREV_MAP.equals(settingName)) {
108: abbrevMap = new HashMap();
109: // Inspect action abbrevs
110: Map m = (Map) Settings.getValue(kitClass,
111: SettingsNames.ABBREV_ACTION_MAP);
112: if (m != null) {
113: BaseKit kit = Utilities.getKit(editorUI
114: .getComponent());
115: Iterator iter = m.entrySet().iterator();
116: while (iter.hasNext()) {
117: Map.Entry me = (Map.Entry) iter.next();
118: Object value = me.getValue();
119: Action a = null;
120: if (value instanceof String) {
121: a = kit.getActionByName((String) value);
122: } else if (value instanceof Action) {
123: a = (Action) value;
124: }
125:
126: if (a != null) {
127: abbrevMap.put(me.getKey(), a);
128: }
129: }
130: }
131:
132: m = (Map) Settings.getValue(kitClass,
133: SettingsNames.ABBREV_MAP);
134: if (m != null) {
135: Iterator iter = m.entrySet().iterator();
136: while (iter.hasNext()) {
137: Map.Entry me = (Map.Entry) iter.next();
138: Object value = me.getValue();
139: if (value != null) {
140: abbrevMap.put(me.getKey(), value);
141: }
142: }
143: }
144: }
145:
146: if (settingName == null
147: || SettingsNames.ABBREV_EXPAND_ACCEPTOR
148: .equals(settingName)) {
149: doExpandAcceptor = SettingsUtil.getAcceptor(kitClass,
150: SettingsNames.ABBREV_EXPAND_ACCEPTOR,
151: AcceptorFactory.FALSE);
152: }
153: if (settingName == null
154: || SettingsNames.ABBREV_ADD_TYPED_CHAR_ACCEPTOR
155: .equals(settingName)) {
156: addTypedAcceptor = SettingsUtil.getAcceptor(kitClass,
157: SettingsNames.ABBREV_ADD_TYPED_CHAR_ACCEPTOR,
158: AcceptorFactory.FALSE);
159: }
160: if (settingName == null
161: || SettingsNames.ABBREV_RESET_ACCEPTOR
162: .equals(settingName)) {
163: resetAcceptor = SettingsUtil.getAcceptor(kitClass,
164: SettingsNames.ABBREV_RESET_ACCEPTOR,
165: AcceptorFactory.TRUE);
166: }
167: }
168: }
169:
170: public void propertyChange(PropertyChangeEvent evt) {
171: String propName = evt.getPropertyName();
172:
173: if (EditorUI.COMPONENT_PROPERTY.equals(propName)) {
174: JTextComponent component = (JTextComponent) evt
175: .getNewValue();
176: if (component != null) { // just installed
177:
178: settingsChange(null);
179:
180: } else { // just deinstalled
181: // component = (JTextComponent)evt.getOldValue();
182:
183: }
184:
185: }
186: }
187:
188: /** Reset abbreviation accounting. */
189: public void reset() {
190: abbrevSB.setLength(0);
191: }
192:
193: /** Add typed character to abbreviation accounting string. */
194: public void addChar(char ch) {
195: abbrevSB.append(ch);
196: }
197:
198: /** Get current abbreviation string */
199: public String getAbbrevString() {
200: return abbrevSB.toString();
201: }
202:
203: /** Get mapping table [abbrev, expanded-abbrev] */
204: public Map getAbbrevMap() {
205: return abbrevMap;
206: }
207:
208: /**
209: * Translate string using abbreviation table
210: *
211: * @param abbrev
212: * string to translate. Pass null to translate current
213: * abbreviation string
214: * @return expanded abbreviation
215: */
216: public Object translateAbbrev(String abbrev) {
217: String abbStr = (abbrev != null) ? abbrev : abbrevSB.toString();
218: return getAbbrevMap().get(abbStr);
219: }
220:
221: /**
222: * Checks whether there's valid string to expand and if so it returns it.
223: */
224: public String getExpandString(char typedChar) {
225: return (doExpandAcceptor.accept(typedChar)) ? getExpandString()
226: : null;
227: }
228:
229: public String getExpandString() {
230: BaseDocument doc = (BaseDocument) editorUI.getDocument();
231: String abbrevStr = getAbbrevString();
232: int abbrevStrLen = abbrevStr.length();
233: Object expansion = translateAbbrev(abbrevStr);
234: Caret caret = editorUI.getComponent().getCaret();
235: int dotPos = caret.getDot();
236: if (abbrevStr != null && expansion != null
237: && dotPos >= abbrevStrLen) {
238: if (checkDocText) {
239: try {
240: String prevChars = doc.getText(dotPos
241: - abbrevStrLen, abbrevStrLen);
242: if (prevChars.equals(abbrevStr)) { // abbrev chars really
243: // match text
244: if (!checkTextDelimiter
245: || dotPos == abbrevStrLen
246: || resetAcceptor
247: .accept(doc.getChars(dotPos
248: - abbrevStrLen - 1, 1)[0])) {
249: return abbrevStr;
250: }
251: }
252: } catch (BadLocationException e) {
253: }
254: }
255: }
256: return null;
257: }
258:
259: protected boolean doExpansion(int dotPos, String expandStr,
260: ActionEvent evt) throws BadLocationException {
261: Object expansion = translateAbbrev(expandStr);
262: boolean expanded = false;
263: if (expansion instanceof String) { // expand to string
264: BaseDocument doc = editorUI.getDocument();
265: String ins = (String) expansion;
266: int offset = ins.indexOf('|');
267: if (offset >= 0) {
268: if (offset > 0)
269: doc.insertString(dotPos, ins.substring(0, offset),
270: null);
271: if (offset + 1 < ins.length())
272: doc.insertString(dotPos + offset, ins
273: .substring(offset + 1), null);
274: Caret caret = editorUI.getComponent().getCaret();
275: caret.setDot(dotPos + offset);
276: } else {
277: doc.insertString(dotPos, ins, null);
278: }
279: expanded = true;
280: } else if (expansion instanceof Action) {
281: ((Action) expansion).actionPerformed(evt);
282: expanded = true;
283: }
284: return expanded;
285: }
286:
287: public boolean expandString(char typedChar, String expandStr,
288: ActionEvent evt) throws BadLocationException {
289: if (expandString(expandStr, evt)) {
290: if (addTypedAcceptor.accept(typedChar)) {
291: int dotPos = editorUI.getComponent().getCaret()
292: .getDot();
293: editorUI.getDocument().insertString(dotPos,
294: String.valueOf(typedChar), null);
295: }
296: return true;
297: }
298: return false;
299: }
300:
301: /**
302: * Expand abbreviation on current caret position. Remove characters back to
303: * the word start and insert expanded abbreviation.
304: *
305: * @return whether the typed character should be added to the abbreviation
306: * or not
307: */
308: public boolean expandString(String expandStr, ActionEvent evt)
309: throws BadLocationException {
310: boolean expanded = false;
311: BaseDocument doc = editorUI.getDocument();
312: doc.atomicLock();
313: try {
314: Caret caret = editorUI.getComponent().getCaret();
315: int pos = caret.getDot() - expandStr.length();
316: if (expandStr != null) {
317: doc.remove(pos, expandStr.length());
318: expanded = doExpansion(pos, expandStr, evt);
319: }
320: } finally {
321: if (expanded) {
322: reset();
323: } else {
324: doc.breakAtomicLock();
325: }
326: doc.atomicUnlock();
327: }
328: return expanded;
329: }
330:
331: public boolean checkReset(char typedChar) {
332: if (resetAcceptor.accept(typedChar)) {
333: reset();
334: return true;
335: }
336: return false;
337: }
338:
339: public boolean checkAndExpand(char typedChar, ActionEvent evt)
340: throws BadLocationException {
341: boolean doInsert = true;
342: String expandStr = getExpandString(typedChar);
343: if (expandStr != null) { // should expand
344: doInsert = false;
345: expandString(typedChar, expandStr, evt);
346: } else {
347: addChar(typedChar);
348: }
349: checkReset(typedChar);
350: return doInsert;
351: }
352:
353: public void checkAndExpand(ActionEvent evt)
354: throws BadLocationException {
355: String expandStr = getExpandString();
356: if (expandStr != null) {
357: expandString(expandStr, evt);
358: }
359: }
360:
361: }
|