001: package isql;
002:
003: import java.util.List;
004: import java.util.ArrayList;
005: import java.util.Map;
006: import java.lang.reflect.*;
007: import java.util.Arrays;
008: import java.util.Enumeration;
009: import java.util.HashMap;
010: import java.util.Iterator;
011: import util.*;
012:
013: /** This expander class expands a given word, based on a given data model.
014: * This would typically be used by a Document, but could be used
015: * from anywhere where Tab expansion is required.
016: * This implementation uses a TabModel object which can be customized by
017: * caller.
018: * For convenience, a constructir with a String[] argument has been
019: * created.
020: * Following are examples of usage:
021: * <pre>
022: * // MyTabModel implements TabModel with your custom data structure.
023: * ETabExpander et = ETabExpander(new MyTabModel());
024: * String exp = et.getNextExpansion(pattern);
025: * // or
026: * // myArray would be populated from a file or database
027: * String myArray[] = new String[] {"abc","def", ...};
028: * ETabExpander et = ETabExpander(myArray);
029: * String exp = et.getNextExpansion(pattern);
030: *
031: * // or
032: * ETabExpander et = ETabExpander();
033: * et.setModel(ETabExpander.createTabModel(myArray));
034: * String exp = et.getNextExpansion(pattern);
035: *
036: * </pre>
037: *
038: * @author rahul kumar, 2001 $Author: rahul_kumar $
039: * $Id: ClassMethodTabExpander.java,v 1.2 2004/01/01 07:01:03 rahul_kumar Exp $
040: */
041: public class ClassMethodTabExpander implements ITabExpander {
042:
043: /** stores the current word typed by user on which we are tabbing.
044: * We would revert to this word if he doesnt accept any substitution */
045: protected String _pattern = null;
046: /** stores the index in _arrMatched of the last word shown to user
047: */
048: protected int _matchedindex = -1;
049: /** stores that data model created by caller */
050: TabModel tm;
051:
052: /** convenience method for creating a TabModel based on a
053: * Map. May be used to initialize the Expander if constructed
054: * without parameters, or just for updating model.
055: */
056: public static TabModel createTabModel(final Map htClassMethods,
057: final Map htPackageClasses) {
058: return new TabModel() {
059: String[] _arrMatched = null;
060:
061: public int doMatch(String patt) {
062: _arrMatched = getMatches(patt);
063: return _arrMatched.length;
064: }
065:
066: public int matchedCount() {
067: if (_arrMatched == null)
068: return 0;
069: return _arrMatched.length;
070: }
071:
072: public String getMatched(int index) {
073: if (index >= 0 && index <= _arrMatched.length)
074: return _arrMatched[index];
075: return null;
076: }
077:
078: public String[] getMatches() {
079: return _arrMatched;
080: }
081:
082: /** pattern here contains classname followed by : abd then
083: * the method pattern. classname is fully qualified.
084: */
085: public String[] getMatches(String patt) {
086: if (htClassMethods == null
087: || htClassMethods.size() == 0)
088: return null;
089: List v = new ArrayList();
090: // we have sent in the patter in the format
091: // 'class:pattern' so we split it.
092: String t[] = ArrayUtil.split(patt, ':');
093: String classname = t[0];
094: String newpatt = null;
095: // in case we are passing no classpattern..
096: if (t.length == 1)
097: newpatt = "";
098: else
099: newpatt = t[1];
100: int len = newpatt.length();
101: // a caller may pass the fullname if explicitly entered
102: // by user or the part name. There is no other way to
103: // determine since classes in the current directory
104: // wouls have the same full and part classname
105: if (classname.indexOf('.') == -1)
106: if (!htClassMethods.containsKey(classname))
107: classname = getFullClass(classname);
108: String sarr[] = getClassMethods(classname);
109: if (sarr == null)
110: System.err.println(" none found:" + classname);
111:
112: for (int i = 0; i < sarr.length; i++) {
113: // check if patt is not longer than array else subst throws
114: // up
115: if (newpatt.length() <= sarr[i].length()
116: && sarr[i].substring(0, len)
117: .equals(newpatt))
118: v.add(sarr[i]);
119: }
120: String s[] = new String[v.size()];
121: s = (String[]) v.toArray(s);
122: return s;
123:
124: }
125:
126: /** Reflects on the fully qualified classname, returning its
127: * methods.
128: */
129: private String[] retrieveMethods(String classname) {
130:
131: try {
132: Class cl = Class.forName(classname);
133: Method[] methods = cl.getMethods();
134: List v = new ArrayList(methods.length);
135: for (int j = 0; j < methods.length; j++) {
136: String params = join(methods[j]
137: .getParameterTypes(), ',');
138: v
139: .add(methods[j].getName() + '('
140: + params + ')');
141: }
142: Constructor[] conss = cl.getConstructors();
143: for (int j = 0; j < conss.length; j++) {
144: String params = join(methods[j]
145: .getParameterTypes(), ',');
146: v.add(conss[j].getName() + '(' + params + ')');
147: }
148: String[] sarr = new String[v.size()];
149: v.toArray(sarr);
150: Arrays.sort(sarr);
151: return sarr;
152: } catch (Exception exc) {
153: System.err.println("EXC 132:" + exc.toString());
154: return null;
155: }
156: }
157:
158: /** appends each index of string using getName and given separator.
159: * This would work only for methods having a get Name such as class
160: * and Method
161: */
162: private String join(Class[] sarr, char csep) {
163: int len = sarr.length;
164: StringBuffer sbuf = new StringBuffer(len * 10);
165: for (int i = 0; i < len; i++) {
166: if (i == len - 1)
167: sbuf.append(sarr[i].getName());
168: else
169: sbuf.append(sarr[i].getName()).append(csep);
170: }
171: return sbuf.toString();
172: }
173:
174: /** given a classname without package name, returns the class name
175: * prefixed by package: given "FileReader" will return
176: * "java.io.FileReader". However, java.io must be in the import
177: * list, and the jar file containing the class should be in
178: * classpath.
179: */
180: private String getFullClass(String partclassname) {
181: Iterator e = htPackageClasses.keySet().iterator();
182: while (e.hasNext()) {
183: // can be java.io.* or java.io.IOException
184: String packpattern = (String) e.next(); // get package name
185: System.out.println(partclassname + " searching in "
186: + packpattern);
187: if (packpattern.indexOf('*') == -1
188: && !packpattern.equals(".")) {
189: if (packpattern.endsWith('.' + partclassname))
190: return packpattern;
191: continue;
192: }
193: List al = (ArrayList) htPackageClasses
194: .get(packpattern);
195:
196: for (int i = 0; i < al.size(); i++) {
197: if (partclassname.equals((String) al.get(i)))
198: return packpattern.substring(0, packpattern
199: .length() - 1)
200: + partclassname;
201: }
202: }
203: System.out.println(partclassname + " not found!!");
204: return null;
205: }
206:
207: /** given a fully qualified classname, returns the names of methods
208: * and constructors from cache, if not found it reflects and adds to
209: * cache.
210: */
211: private String[] getClassMethods(String classname) {
212: String[] sarr = (String[]) (htClassMethods
213: .get(classname));
214: if (sarr != null)
215: return sarr;
216: else {
217: sarr = retrieveMethods(classname);
218: htClassMethods.put(classname, sarr);
219: }
220: return sarr;
221: }
222:
223: };
224: }
225:
226: public ClassMethodTabExpander(final Map cache,
227: final Map htPackageClasses) {
228: this (createTabModel(cache, htPackageClasses));
229: }
230:
231: public ClassMethodTabExpander(TabModel tm) {
232: this .tm = tm;
233: }
234:
235: public ClassMethodTabExpander() {
236: //this.tm = tm;
237: }
238:
239: public void setModel(TabModel newModel) {
240: TabModel oldModel = tm;
241:
242: if (newModel == null)
243: throw new IllegalArgumentException(
244: "Cannot set a null TabModel");
245:
246: if (newModel != oldModel) {
247: tm = newModel;
248: }
249: }
250:
251: /** returns the installed model, which may be updated by caller.
252: */
253: public TabModel getModel() {
254: return tm;
255: }
256:
257: /** finds matched strings in the cached array, does a startsWith
258: * and not a equals: ABC will return ABC, ABCD, ABCDE etc.
259: * returns null if no match or if cache not set.
260: */
261: /** expands the given pattern, returning the match at count
262: * position. USer would start with 0, and continue till null is
263: * returned.
264: */
265: public String expand(String patt, int index) {
266: if (patt != _pattern) {
267: _pattern = null;
268: int mcount = tm.doMatch(patt);
269: if (mcount == 0)
270: return null;
271: _pattern = patt;
272: }
273: if (index >= 0 && index <= tm.matchedCount())
274: return (tm.getMatched(index));
275: else
276: return null;
277: }
278:
279: /** returns expansions for a string. Caller would just send in a
280: * string and keep calling this until a null is returned.
281: * Caller would either call getNextExpansion or expand, whatever
282: * suits his needs better.
283: */
284: public String getNextExpansion(String patt) {
285: if (patt != _pattern)
286: _matchedindex = 0;
287: else
288: _matchedindex++;
289:
290: return expand(patt, _matchedindex);
291: }
292:
293: /** returns all expansions for the previously searched pattern
294: */
295: public String[] getExpansions() {
296: return tm.getMatches();
297: }
298:
299: /** returns expansions for the given string. Has no impact on
300: * existing searches.
301: */
302: public String[] getExpansions(String patt) {
303: return tm.getMatches(patt);
304: }
305:
306: }
|