001: /*
002: * JavaContext.java
003: *
004: * Copyright (C) 2002 Peter Graves
005: * $Id: JavaContext.java,v 1.1.1.1 2002/09/24 16:09:21 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import gnu.regexp.RE;
025: import gnu.regexp.REMatch;
026: import gnu.regexp.UncheckedRE;
027: import java.util.ArrayList;
028: import java.util.List;
029: import java.util.Stack;
030:
031: public final class JavaContext implements Constants {
032: private static final boolean DEBUG = false;
033:
034: private static final RE parameterRE = new UncheckedRE(
035: "^(\\w+\\s+\\w+\\s*,?)");
036: private static final RE declarationRE = new UncheckedRE(
037: "^(\\w+\\s+\\w+\\s*;|\\w+\\s+\\w+\\s*=[^=])");
038:
039: // "return foo;"
040: private static final RE returnRE = new UncheckedRE("^return[ \t]");
041:
042: private final Editor editor;
043: private final Stack stack = new Stack();
044:
045: public JavaContext(Editor editor) {
046: this .editor = editor;
047: }
048:
049: public void parseContext(Position dot) {
050: final List tags = editor.getBuffer().getTags();
051: if (tags != null) {
052: Scope scope = new Scope(new Position(editor.getBuffer()
053: .getFirstLine(), 0));
054: stack.push(scope);
055: // BUG! We should only consider the current top-level class (and
056: // its inner classes if any), not all the tags in the file.
057: final int size = tags.size();
058: for (int i = 0; i < size; i++) {
059: JavaTag tag = (JavaTag) tags.get(i);
060: if (tag.getType() == TAG_FIELD)
061: scope.addField(tag.getSignature());
062: }
063: }
064: Position pos = findStartOfMethod(dot);
065: if (pos != null) {
066: while (pos.next() && pos.isBefore(dot)) {
067: char c = pos.getChar();
068: if (c == '(') {
069: Scope scope = new Scope(pos);
070: stack.push(scope);
071: scope.parseParameters();
072: continue;
073: }
074: if (c == '{') {
075: Scope scope = new Scope(pos);
076: stack.push(scope);
077: scope.parse(dot);
078: break;
079: }
080: }
081: }
082: }
083:
084: public JavaVariable findDeclaration(String name) {
085: if (DEBUG)
086: Log.debug("findDeclaration name = |" + name + "|");
087: if (name == null)
088: return null;
089: int index = name.indexOf('.');
090: if (index >= 0) {
091: // It's a qualified name.
092: String prefix = name.substring(0, index);
093: // We only handle things like "this.foo".
094: if (!prefix.equals("this"))
095: return null;
096: // It's a member of the current class.
097: name = name.substring(index + 1);
098: if (stack.size() > 0) {
099: Scope scope = (Scope) stack.get(0);
100: for (int j = 0; j < scope.list.size(); j++) {
101: JavaVariable var = scope.getVariable(j);
102: if (name.equals(var.getName()))
103: return var;
104: }
105: }
106: return null;
107: }
108: // It's a simple name. A local variable hides a class member with the
109: // same name.
110: for (int i = stack.size() - 1; i >= 0; i--) {
111: Scope scope = (Scope) stack.get(i);
112: for (int j = 0; j < scope.list.size(); j++) {
113: JavaVariable var = scope.getVariable(j);
114: if (name.equals(var.getName()))
115: return var;
116: }
117: }
118: return null;
119: }
120:
121: private Position findStartOfMethod(Position dot) {
122: if (dot != null) {
123: final List tags = editor.getBuffer().getTags();
124: if (tags != null) {
125: JavaTag tag = null;
126: // Find the last tag before dot.
127: final int target = dot.lineNumber();
128: final int limit = tags.size();
129: for (int i = 0; i < limit; i++) {
130: JavaTag nextTag = (JavaTag) tags.get(i);
131: if (nextTag.lineNumber() > target)
132: break;
133: else
134: tag = nextTag;
135: }
136: if (tag != null && tag.getType() == TAG_METHOD)
137: return tag.getPosition().copy();
138: }
139: }
140: return null;
141: }
142:
143: private final class Scope {
144: final ArrayList list = new ArrayList();
145:
146: final Position start;
147: final Position pos;
148:
149: Scope(Position pos) {
150: this .pos = pos;
151: start = pos.copy();
152: }
153:
154: void parse(Position dot) {
155: // Skip initial '{'.
156: if (pos.getChar() == '{') {
157: if (!pos.next())
158: return;
159: }
160: while (pos.isBefore(dot)) {
161: char c = pos.getChar();
162: if (c == '\'' || c == '"') {
163: pos.skipQuote();
164: continue;
165: }
166: if (c == '/' && pos.lookingAt("//")) {
167: Line next = pos.getNextLine();
168: if (next != null) {
169: pos.moveTo(next, 0);
170: continue;
171: } else
172: break;
173: }
174: if (c == '{') {
175: Scope scope = new Scope(pos);
176: stack.push(scope);
177: scope.parse(dot);
178: } else if (c == '}') {
179: stack.pop();
180: return;
181: } else {
182: final String text = pos.getLine().substring(
183: pos.getOffset());
184: final REMatch match = declarationRE.getMatch(text);
185: if (match != null) {
186: String s = match.toString();
187: if (returnRE.getMatch(s) != null) {
188: if (DEBUG)
189: Log.debug("skipping |" + s + "|");
190: } else
191: addLocalVariable(s);
192: pos.skip(s.length());
193: continue;
194: }
195: }
196: if (!pos.next())
197: return;
198: }
199: }
200:
201: void parseParameters() {
202: if (pos.getChar() == '(') {
203: if (!pos.next())
204: return;
205: }
206: FastStringBuffer sb = new FastStringBuffer();
207: while (!pos.atEnd()) {
208: char c = pos.getChar();
209: if (c == '\'' || c == '"') {
210: pos.skipQuote();
211: continue;
212: }
213: if (c == '/') {
214: if (pos.lookingAt("//")) {
215: Line next = pos.getNextLine();
216: if (next != null) {
217: pos.moveTo(next, 0);
218: continue;
219: } else
220: break;
221: }
222: if (pos.lookingAt("/*")) {
223: pos.skip(2);
224: while (!pos.lookingAt("*/")) {
225: if (!pos.next())
226: return;
227: }
228: pos.skip(2);
229: continue;
230: }
231: }
232: if (c == ')')
233: break;
234: // Default.
235: sb.append(c);
236: if (!pos.next())
237: break;
238: }
239: String parameters = sb.toString();
240: while (true) {
241: if (DEBUG)
242: Log.debug("parameters = |" + parameters + "|");
243: REMatch match = parameterRE.getMatch(parameters);
244: if (match != null) {
245: String s = match.toString();
246: addParameter(s);
247: parameters = parameters.substring(s.length())
248: .trim();
249: } else
250: break;
251: }
252: }
253:
254: void addField(String signature) {
255: int index = signature.indexOf('=');
256: if (index >= 0)
257: signature = signature.substring(0, index);
258: list.add(new JavaVariable(signature, JavaVariable.FIELD));
259: }
260:
261: void addParameter(String s) {
262: list.add(new JavaVariable(s, JavaVariable.PARAMETER));
263: }
264:
265: void addLocalVariable(String s) {
266: list.add(new JavaVariable(s, JavaVariable.LOCAL));
267: }
268:
269: JavaVariable getVariable(int index) {
270: return (JavaVariable) list.get(index);
271: }
272:
273: int getVariableCount() {
274: return list.size();
275: }
276:
277: // For debugging.
278: void dump() {
279: Log.debug("scope at " + start);
280: for (int i = 0; i < list.size(); i++)
281: Log.debug(((JavaVariable) list.get(i)).getName());
282: }
283: }
284:
285: // For debugging.
286: public static void context() {
287: final Editor editor = Editor.currentEditor();
288: JavaContext context = new JavaContext(editor);
289: context.parseContext(editor.getDot());
290: Log.debug("--- context at " + editor.getDot() + " ---");
291: context.dump();
292: }
293:
294: // For debugging.
295: private void dump() {
296: for (int i = 0; i < stack.size(); i++)
297: ((Scope) stack.get(i)).dump();
298: }
299: }
|