001: // This file is part of KeY - Integrated Deductive Software Design
002: // Copyright (C) 2001-2007 Universitaet Karlsruhe, Germany
003: // Universitaet Koblenz-Landau, Germany
004: // Chalmers University of Technology, Sweden
005: //
006: // The KeY system is protected by the GNU General Public License.
007: // See LICENSE.TXT for details.
008: //
009: //
010: package de.uka.ilkd.key.java.recoderext;
011:
012: import java.util.HashMap;
013:
014: import recoder.ServiceConfiguration;
015: import recoder.abstraction.ClassType;
016: import recoder.abstraction.Type;
017: import recoder.abstraction.Variable;
018: import recoder.convenience.Format;
019: import recoder.convenience.Naming;
020: import recoder.java.*;
021: import recoder.java.declaration.InheritanceSpecification;
022: import recoder.java.declaration.TypeDeclaration;
023: import recoder.java.declaration.VariableDeclaration;
024: import recoder.java.declaration.VariableSpecification;
025: import recoder.list.ImportList;
026: import recoder.service.AmbiguousReferenceException;
027: import recoder.service.DefaultCrossReferenceSourceInfo;
028: import recoder.service.NameInfo;
029:
030: public class KeYCrossReferenceSourceInfo extends
031: DefaultCrossReferenceSourceInfo {
032:
033: private HashMap names2vars = null;
034:
035: /**
036: Strict checking. Does not allow "broken links" during reference
037: resolution.
038: */
039: public int STRICT = 0;
040:
041: /**
042: Sloppy checking. Allows "broken links" during reference resolution.
043: */
044: public int SLOPPY = 1;
045:
046: public KeYCrossReferenceSourceInfo(ServiceConfiguration config) {
047: super (config);
048: }
049:
050: public void setNames2Vars(HashMap names2vars) {
051: this .names2vars = names2vars;
052: }
053:
054: /**
055: Called by the service configuration indicating that all services
056: are known. Services may now start communicating or linking among
057: their configuration partners. The service configuration can be
058: memorized if it has not been passed in by a constructor already.
059: @param cfg the service configuration this services has been assigned to.
060: */
061: public void initialize(ServiceConfiguration cfg) {
062: super .initialize(cfg);
063: cfg.getChangeHistory().removeChangeHistoryListener(this );
064: cfg.getChangeHistory().addChangeHistoryListener(this );
065: }
066:
067: /**
068: Returns the class type that contains the given program element.
069: @param context a program element.
070: @return the type to which the given program element belongs
071: (may be <CODE>null</CODE>).
072: */
073: public ClassType getContainingClassType(ProgramElement context) {
074: if (context instanceof TypeDeclaration) {
075: context = context.getASTParent();
076: }
077: do {
078: if (context instanceof ClassType) {
079: return (ClassType) context;
080: } else if (context instanceof MethodCallStatement) {
081: return (ClassType) getType(((MethodCallStatement) context)
082: .getExecutionContext().getTypeReference());
083: }
084: context = context.getASTParent();
085: } while (context != null);
086: return null;
087: }
088:
089: public Variable getVariable(String name, ProgramElement context) {
090: updateModel();
091: // look for the next variable scope equals to or parent of context
092: ProgramElement pe = context;
093: while (pe != null
094: && !(pe instanceof VariableScope)
095: && !((pe instanceof MethodCallStatement)
096: && !(context instanceof ExecutionContext) && !(context
097: .equals(((MethodCallStatement) pe)
098: .getResultVariable())))) {
099: context = pe;
100: pe = pe.getASTParent();
101:
102: }
103: if (pe == null) {
104: // a null scope can happen if we try to find a variable
105: // speculatively (for URQ resolution)
106: return null;
107: }
108: if (pe instanceof MethodCallStatement
109: && !(context instanceof ExecutionContext)
110: && !(context.equals(((MethodCallStatement) pe)
111: .getResultVariable()))) {
112: pe = getTypeDeclaration((ClassType) getType(((MethodCallStatement) pe)
113: .getExecutionContext().getTypeReference()));
114: }
115: VariableScope scope = (VariableScope) pe;
116: Variable result;
117: do {
118: result = scope.getVariableInScope(name);
119: if (result != null) {
120: // must double check this result - rare cases of confusion
121: // involving field references before a local variable of the
122: // same name has been specified
123: if (scope instanceof StatementBlock) {
124: StatementContainer cont = (StatementBlock) scope;
125: // we need the topmost var-scope including context,
126: // or context itself if the found scope is the topmost one
127: VariableDeclaration def = ((VariableSpecification) result)
128: .getParent();
129: for (int i = 0; true; i += 1) {
130: Statement s = cont.getStatementAt(i);
131: if (s == def) {
132: // Debug.log(">>> Not ignored: " +
133: // Format.toString("%c \"%s\" @%p", result)
134: // + " for context " +
135: // Format.toString("@%p", context));
136:
137: // stop if definition comes first
138: break;
139: }
140: if (s == context) {
141: // tricky: reference before definition - must
142: // ignore the definition :(
143:
144: // Debug.log(">>> Ignored: " +
145: // Format.toString("%c \"%s\" @%p", result)
146: // + " for context " +
147: // Format.toString("@%p", context));
148:
149: result = null;
150: break;
151: }
152: }
153: }
154: if (result != null) {
155: // leave _now_
156: break;
157: }
158: }
159: if (scope instanceof TypeDeclaration) {
160: result = getInheritedField(name,
161: (TypeDeclaration) scope);
162: if (result != null) {
163: break;
164: }
165: // might want to check for ambiguity of outer class fields!!!
166: }
167: pe = scope.getASTParent();
168: while (pe != null
169: && !(pe instanceof VariableScope)
170: && !((pe instanceof MethodCallStatement) && !(context instanceof ExecutionContext))) {
171: context = pe; // proceed the context
172: pe = pe.getASTParent();
173: }
174: if (pe instanceof MethodCallStatement
175: && !(context instanceof ExecutionContext)
176: && !(context.equals(((MethodCallStatement) pe)
177: .getResultVariable()))) {
178: pe = getTypeDeclaration((ClassType) getType(((MethodCallStatement) pe)
179: .getExecutionContext().getTypeReference()));
180: }
181: scope = (VariableScope) pe;
182: } while (scope != null);
183: // we were at the compilation unit scope, leave for good now
184: if (result == null && names2vars != null) {
185: return (recoder.abstraction.Variable) names2vars.get(name);
186: }
187: return result;
188: }
189:
190: /**
191: * Tries to find a type with the given name using the given program element
192: * as context. Useful to check for name clashes when introducing a new
193: * identifier. Neither name nor context may be <CODE>null</CODE>.
194: *
195: * @param name
196: * the name for the type to be looked up; may or may not be
197: * qualified.
198: * @param context
199: * a program element defining the lookup context (scope).
200: * @return the corresponding type (may be <CODE>null</CODE>).
201: */
202: public Type getType(String name, ProgramElement context) {
203:
204: NameInfo ni = getNameInfo();
205:
206: // check primitive types, array types of primitive types,
207: // and void --- these happen often
208: Type t = (Type) name2primitiveType.get(name);
209: if (t != null) {
210: return t;
211: }
212: if (name.equals("void")) {
213: return null;
214: }
215: // catch array types
216: if (name.endsWith("]")) {
217: int px = name.indexOf('[');
218: // compute base type
219: Type baseType = getType(name.substring(0, px), context);
220: if (baseType == null) {
221: return null;
222: }
223: String indexExprs = name.substring(px);
224: // the basetype exists now, so fetch a corresponding array type
225: // (if there is none, the name info will create one)
226: return ni.getType(baseType.getFullName() + indexExprs);
227: }
228:
229: updateModel();
230:
231: // in the very special case that we are asking from the point of
232: // view of a supertype reference, we must move to the enclosing unit
233: // or parent type
234: if (context.getASTParent() instanceof InheritanceSpecification) {
235: context = context.getASTParent().getASTParent()
236: .getASTParent();
237: }
238:
239: ProgramElement pe = context;
240: while (pe != null && !(pe instanceof TypeScope)) {
241: context = pe;
242: pe = redirectScopeNesting(pe);
243: }
244: TypeScope scope = (TypeScope) pe;
245: ClassType result = null;
246:
247: // do the scope walk
248: TypeScope s = scope;
249: while (s != null) {
250: result = getLocalType(name, s);
251: if (result != null) {
252: // must double check this result - rare cases of confusion
253: // involving type references before a local class of the
254: // corresponding name has been specified
255: if (s instanceof StatementBlock) {
256: StatementContainer cont = (StatementBlock) s;
257: for (int i = 0; true; i += 1) {
258: Statement stmt = cont.getStatementAt(i);
259: if (stmt == result) {
260: // stop if definition comes first
261: break;
262: }
263: if (stmt == context) {
264: // tricky: reference before definition - must
265: // ignore the definition :(
266: result = null;
267: break;
268: }
269: }
270: }
271: if (result != null) {
272: // leave _now_
273: break;
274: }
275: }
276: if (s instanceof TypeDeclaration) {
277: TypeDeclaration td = (TypeDeclaration) s;
278: ClassType newResult = getInheritedType(name, td);
279:
280: if (newResult != null) {
281: if (result == null) {
282: result = newResult;
283: break;
284: } else if (result != newResult) {
285: // !!!!!!! Problematic if this is a speculative
286: // question - do we really want to bail out?
287: getErrorHandler()
288: .reportError(
289: new AmbiguousReferenceException(
290: "Type "
291: + Format
292: .toString(
293: "%N",
294: newResult)
295: + " is an inherited member type that is also defined as outer member type "
296: + Format
297: .toString(
298: "%N",
299: result),
300: null, result, newResult));
301: break;
302: }
303: }
304: }
305: scope = s;
306: pe = s.getASTParent();
307: while (pe != null && !(pe instanceof TypeScope)) {
308: context = pe;
309: pe = redirectScopeNesting(pe);
310: }
311: s = (TypeScope) pe;
312: }
313: if (result != null) {
314: return result;
315: }
316:
317: // now the outer scope is null, so we have arrived at the top
318: CompilationUnit cu = (CompilationUnit) scope;
319:
320: ImportList il = cu.getImports();
321: if (il != null) {
322: // first check type imports
323: result = getClassTypeFromTypeImports(name, il);
324: }
325: if (result == null) {
326: // then check same package
327: result = getClassTypeFromUnitPackage(name, cu);
328: if (result == null && il != null) {
329: // then check package imports
330: result = getClassTypeFromPackageImports(name, il);
331: }
332: }
333: if (result == null) {
334: // check global types: if unqualified, attempt "java.lang.<name>":
335: // any unqualified local type would have been imported already!
336: String defaultName = Naming.dot("java.lang", name);
337: result = ni.getClassType(defaultName);
338: if (result == null) {
339: result = ni.getClassType(name);
340: }
341: }
342: if (result != null) {
343: scope.addTypeToScope(result, name); // add it to the CU scope
344: }
345: return result;
346: }
347:
348: /**
349: * redirects the nesting of scopes when a method-frame occurs
350: * @param scope the current scope
351: * @return the new scope
352: */
353: private ProgramElement redirectScopeNesting(ProgramElement scope) {
354: if (scope instanceof MethodCallStatement) {
355: Type type = getType(((MethodCallStatement) scope)
356: .getExecutionContext().getTypeReference());
357: if (!(type instanceof TypeDeclaration)) {
358: throw new IllegalStateException(
359: "In the source section of"
360: + "method-frame only types for which source code is "
361: + "available are supported.");
362: }
363: return (TypeDeclaration) getType(((MethodCallStatement) scope)
364: .getExecutionContext().getTypeReference());
365: } else if (scope instanceof ExecutionContext
366: || (scope.getASTParent() instanceof MethodCallStatement && scope == ((MethodCallStatement) scope
367: .getASTParent()).getResultVariable())) {
368: scope = scope.getASTParent();
369: }
370:
371: return scope.getASTParent();
372: }
373: }
|