001: package isql;
002:
003: import javax.swing.*;
004: import javax.swing.text.*;
005: import java.util.Map;
006: import java.util.HashMap;
007: import java.util.List;
008: import java.util.ArrayList;
009: import util.*;
010:
011: /* 1. should beep if no completion
012: * 2. autocomplete suggestion like in staroffice ?
013: * 3. would like trapping keys like C-n/pf as in vi
014: * as well as backward searches in same document.
015: * 4. ability to map keys (vi and emacs like bindings)
016: * */
017:
018: /** this document has the ability to complete words or or expand abbreviations
019: * from a list of possible expansions.
020: * It is intended that the abbreviations come from a file
021: * whereas the completion is 'smart'/context sensitive such as names of
022: * tables and columns, or classnames/method names.
023: * Currently this class contains the expansion logic, to my
024: * embarrassment - i have written a tabExpander which should now take
025: * over this logic!
026: * $Author: rahul_kumar $
027: * $Id: SmartDocument.java,v 1.2 2004/01/01 19:01:27 rahul_kumar Exp $
028: * I did some changes to 1.1.1.1 2001/11/10, on 2001/12/03 - if it has bombed please
029: * revert. Essentially pressing tab on emply document showed each entry
030: * in cache one by one. now it wont.
031: *
032: * RK added on 20040101 13:19:06
033: * Added ability to do some dynamic subs in expansions - however
034: * currently a weak facility - needs thought.
035: */
036:
037: public class SmartDocument extends DefaultStyledDocument {
038:
039: /** stores the current word typed by user on which we are tabbing.
040: * We would revert to this word if he doesnt accept any substitution */
041: String _lastWord = null;
042: /** stores all strings used by tab method, set by caller */
043: String[] _arrCache;
044: /** stores strings matched by last tab, for further tabs by user */
045: private String[] _arrMatched;
046: /** stores the index in _arrMatched of the last word shown to user
047: */
048: private int _matchedindex = -1;
049: private Map htParams = null;
050:
051: private MutableAttributeSet ctable;
052: private MutableAttributeSet ccolumn;
053: /** abbreviations hash table */
054: private Map htabbr;
055:
056: public SmartDocument() {
057:
058: // these are to be picked from a .isqlrc file upon loading
059: /*
060: htabbr = new Hashtable();
061: htabbr.put("REG","MU_REGISTRATION");
062: htabbr.put("SOL","MU_SOLUTIONS");
063: htabbr.put("SOLR","MU_SOLUTION_RULES");
064: htabbr.put("SOLS","SOLUTIONS");
065:
066: // this is supposed to be dynamically picked up from the db
067: // and cycled for each match, maintaining the lastword until
068: // user goes on.
069: httab = new Hashtable();
070: httab.put("REG","mu_registration");
071: httab.put("SOL","mu_solutions");
072: httab.put("SOLR","mu_solution_rules");
073: httab.put("SOLS","solutions");
074:
075: */
076: htabbr = new HashMap(); // to prevent crashes if not assigned
077: _arrCache = new String[] { "" }; // hopefully to prevent crashes
078: // temporary since databast connection is down
079: _arrCache = new String[] { "solution", "solution_rules",
080: "solutions", "registration", "registration_info",
081: "registry" };
082: //if none loaded
083: ctable = new SimpleAttributeSet();
084: ctable.removeAttribute(StyleConstants.Foreground);
085: StyleConstants.setForeground(ctable, java.awt.Color.blue);
086: StyleConstants.setBackground(ctable, java.awt.Color.orange);
087: //ctable.addAttribute(StyleConstants.Foreground,java.awt.Color.blue);
088: //ctable.removeAttribute(java.awt.font.TextAttribute.FOREGROUND);
089: //ctable.addAttribute(java.awt.font.TextAttribute.FOREGROUND,java.awt.Color.blue);
090: ccolumn = new SimpleAttributeSet();
091: //ccolumn.removeAttribute(StyleConstants.Foreground);
092: StyleConstants.setForeground(ccolumn, java.awt.Color.red);
093: StyleConstants.setBackground(ccolumn, java.awt.Color.yellow);
094: //System.out.println( " Fattr:"+ ccolumn.getAttribute(StyleConstants.Foreground));
095: //System.out.println( " Battr:"+ ccolumn.getAttribute(StyleConstants.Background));
096: //System.out.println( " Tattr:"+ ccolumn.getAttribute(java.awt.font.TextAttribute.FOREGROUND));
097: //ccolumn.addAttribute(StyleConstants.Foreground,java.awt.Color.red);
098:
099: }
100:
101: // does throw a NPE if space followed by tab
102: public void insertString(int offs, String str, AttributeSet a)
103: throws BadLocationException {
104:
105: String repls;
106: if (str == null || str.length() == 0)
107: return;
108: char lc = str.charAt(str.length() - 1);
109: // name completion
110: if (lc == '\t') {
111: if (_lastWord == null) {
112: _lastWord = getLastWord(offs, str);
113: //added this block 2001/12/03
114: if (_lastWord == null || _lastWord.length() == 0) {
115: super .insertString(offs, "\t", a);
116: return;
117: }
118:
119: if ((_lastWord.length() == 1 && Character
120: .isWhitespace(_lastWord.charAt(0)))) {
121: //added super. 2001/12/03
122: super .insertString(offs, str, a);
123: //System.err.println( "SD:returning(" + str +")");
124: return;
125: }
126: // get the matches for the last word entered
127: _arrMatched = getMatches(_lastWord);
128: if (_arrMatched != null)
129: _matchedindex = 0; // matches found, set pointer to 1st
130: else {
131: _matchedindex = -1; // no match
132: _lastWord = null;
133: insertString(offs, str, a); // let the entered word go
134: return;
135: }
136: } // there is a last word already, show next one
137: else
138: _matchedindex++;
139: // recycle TODO
140: //added if until else. 2001/12/03
141: if (_arrMatched == null)
142: _matchedindex = -1;
143: else if (_matchedindex >= _arrMatched.length)
144: _matchedindex = -1;
145:
146: // first time, update string with new word
147: if (_matchedindex == 0) {
148: remove(offs - _lastWord.length(), _lastWord.length());
149: super .insertString(offs - _lastWord.length(),
150: _arrMatched[_matchedindex], ccolumn);
151: return;
152: } else
153: // next time update previous replacement with new
154: // one
155: if (_matchedindex > 0) {
156: remove(offs - _arrMatched[_matchedindex - 1].length(),
157: _arrMatched[_matchedindex - 1].length());
158:
159: super .insertString(offs
160: - _arrMatched[_matchedindex - 1].length(),
161: _arrMatched[_matchedindex], ccolumn);
162: return;
163: } else
164: super .insertString(offs, str, a); // replace orig word
165: } // tabbed
166: else
167: // abbreviation - the user entered a whitespace char
168: if (Character.isWhitespace(lc)) {
169: String last = getLastWord(offs, str);
170: // get the expansion from abbrev hashtable
171: if ((repls = (String) htabbr.get(last)) != null) {
172: remove(offs - last.length(), last.length());
173: // RK added on 20040101 12:41:52
174: repls = runSubstitutions(repls);
175: super .insertString(offs - last.length(), repls + lc,
176: ctable);
177: } else
178: super .insertString(offs, str, a); //a
179: } else
180: // character was not WS char, let things happen
181: // normally
182: super .insertString(offs, str, a); //a
183: _lastWord = null;
184: }
185:
186: /** searches backwards are returns the last word before the cursor.
187: * Will return a space or tab if there are multiple whitespace
188: * characters.
189: * Modified to stop when encountering a period also, on 1024
190: * since we often put table.column
191: */
192: private String getLastWord(int offset, String str)
193: throws BadLocationException {
194: String lastline;
195: int spos = 0;
196: lastline = getText(0, offset);
197:
198: int i = offset - 1;
199: while (i-- > spos) {
200: if (Character.isWhitespace(lastline.charAt(i))
201: || lastline.charAt(i) == '.' // added 1024
202: )
203: break;
204: }
205: // i contains the last space
206: i++;
207: if (i < 0)
208: i = 0; // added since space in beg throws SOB ex
209: return lastline.substring(i);
210: }
211:
212: public void setTabTable(String[] tabs) {
213: _arrCache = tabs;
214: }
215:
216: public void setAbbreviationTable(Map htabbr) {
217: this .htabbr = htabbr;
218: }
219:
220: public void appendAbbreviationTable(String key, Object value) {
221: this .htabbr.put(key, value);
222: }
223:
224: public String[] getTabTable() {
225: return _arrCache;
226: }
227:
228: public Map getAbbreviationTable() {
229: return this .htabbr;
230: }
231:
232: /** finds matched strings in the cached array, does a startsWith
233: * and not a equals: ABC will return ABC, ABCD, ABCDE etc.
234: * returns null if no match
235: */
236: private String[] getMatches(String patt) {
237: if (_arrCache == null || _arrCache.length == 0)
238: return null;
239: int len = patt.length();
240: //added then removed . 2001/12/03
241: //if (len==0) return null;
242: List v = new ArrayList();
243: for (int i = 0; i < _arrCache.length; i++) {
244: // check if patt is not longer than array else subst throws
245: // up
246: // Added IgnoreCase on 1024 RK
247: if (patt.length() <= _arrCache[i].length()
248: && _arrCache[i].substring(0, len).equalsIgnoreCase(
249: patt))
250: v.add(_arrCache[i]);
251: }
252: String s[] = new String[v.size()];
253: s = (String[]) v.toArray(s);
254: return s;
255: }
256:
257: /** do some limited/fixed substitions on the string. e.g. replace
258: * current date into string.
259: * Needs refactoring.
260: */
261: public String runSubstitutions(String mstring) {
262: int pos;
263: if ((pos = mstring.indexOf("#TODAY#")) > -1) {
264: String rep = RKUtil.CurrentSQLDateString();
265: rep = htParams.get("fs_date") + rep
266: + htParams.get("fe_date");
267: mstring = Util.replace("#TODAY#", rep, mstring);
268: }
269: return mstring;
270:
271: }
272:
273: public void setParams(Map ht) {
274: htParams = ht;
275: }
276:
277: }
|