001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core;
011:
012: import org.eclipse.core.runtime.IProgressMonitor;
013: import org.eclipse.core.runtime.OperationCanceledException;
014: import org.eclipse.jdt.core.*;
015: import org.eclipse.jdt.core.compiler.CharOperation;
016: import org.eclipse.jdt.core.search.*;
017: import org.eclipse.jdt.internal.codeassist.ISearchRequestor;
018: import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
019: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
020: import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
021: import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
022: import org.eclipse.jdt.internal.compiler.env.ISourceType;
023: import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
024: import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
025: import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor;
026:
027: /**
028: * This class provides a <code>SearchableBuilderEnvironment</code> for code assist which
029: * uses the Java model as a search tool.
030: */
031: public class SearchableEnvironment implements INameEnvironment,
032: IJavaSearchConstants {
033:
034: public NameLookup nameLookup;
035: protected ICompilationUnit unitToSkip;
036: protected org.eclipse.jdt.core.ICompilationUnit[] workingCopies;
037:
038: protected JavaProject project;
039: protected IJavaSearchScope searchScope;
040:
041: protected boolean checkAccessRestrictions;
042:
043: /**
044: * Creates a SearchableEnvironment on the given project
045: */
046: public SearchableEnvironment(JavaProject project,
047: org.eclipse.jdt.core.ICompilationUnit[] workingCopies)
048: throws JavaModelException {
049: this .project = project;
050: this .checkAccessRestrictions = !JavaCore.IGNORE.equals(project
051: .getOption(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE,
052: true))
053: || !JavaCore.IGNORE.equals(project.getOption(
054: JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE,
055: true));
056: this .workingCopies = workingCopies;
057: this .nameLookup = project.newNameLookup(workingCopies);
058:
059: // Create search scope with visible entry on the project's classpath
060: if (this .checkAccessRestrictions) {
061: this .searchScope = BasicSearchEngine
062: .createJavaSearchScope(new IJavaElement[] { project });
063: } else {
064: this .searchScope = BasicSearchEngine
065: .createJavaSearchScope(this .nameLookup.packageFragmentRoots);
066: }
067: }
068:
069: /**
070: * Creates a SearchableEnvironment on the given project
071: */
072: public SearchableEnvironment(JavaProject project,
073: WorkingCopyOwner owner) throws JavaModelException {
074: this (project, owner == null ? null : JavaModelManager
075: .getJavaModelManager()
076: .getWorkingCopies(owner, true/*add primary WCs*/));
077: }
078:
079: private static int convertSearchFilterToModelFilter(int searchFilter) {
080: switch (searchFilter) {
081: case IJavaSearchConstants.CLASS:
082: return NameLookup.ACCEPT_CLASSES;
083: case IJavaSearchConstants.INTERFACE:
084: return NameLookup.ACCEPT_INTERFACES;
085: case IJavaSearchConstants.ENUM:
086: return NameLookup.ACCEPT_ENUMS;
087: case IJavaSearchConstants.ANNOTATION_TYPE:
088: return NameLookup.ACCEPT_ANNOTATIONS;
089: case IJavaSearchConstants.CLASS_AND_ENUM:
090: return NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_ENUMS;
091: case IJavaSearchConstants.CLASS_AND_INTERFACE:
092: return NameLookup.ACCEPT_CLASSES
093: | NameLookup.ACCEPT_INTERFACES;
094: default:
095: return NameLookup.ACCEPT_ALL;
096: }
097: }
098:
099: /**
100: * Returns the given type in the the given package if it exists,
101: * otherwise <code>null</code>.
102: */
103: protected NameEnvironmentAnswer find(String typeName,
104: String packageName) {
105: if (packageName == null)
106: packageName = IPackageFragment.DEFAULT_PACKAGE_NAME;
107: NameLookup.Answer answer = this .nameLookup.findType(typeName,
108: packageName, false/*exact match*/,
109: NameLookup.ACCEPT_ALL, this .checkAccessRestrictions);
110: if (answer != null) {
111: // construct name env answer
112: if (answer.type instanceof BinaryType) { // BinaryType
113: try {
114: return new NameEnvironmentAnswer(
115: (IBinaryType) ((BinaryType) answer.type)
116: .getElementInfo(),
117: answer.restriction);
118: } catch (JavaModelException npe) {
119: return null;
120: }
121: } else { //SourceType
122: try {
123: // retrieve the requested type
124: SourceTypeElementInfo sourceType = (SourceTypeElementInfo) ((SourceType) answer.type)
125: .getElementInfo();
126: ISourceType topLevelType = sourceType;
127: while (topLevelType.getEnclosingType() != null) {
128: topLevelType = topLevelType.getEnclosingType();
129: }
130: // find all siblings (other types declared in same unit, since may be used for name resolution)
131: IType[] types = sourceType.getHandle()
132: .getCompilationUnit().getTypes();
133: ISourceType[] sourceTypes = new ISourceType[types.length];
134:
135: // in the resulting collection, ensure the requested type is the first one
136: sourceTypes[0] = sourceType;
137: int length = types.length;
138: for (int i = 0, index = 1; i < length; i++) {
139: ISourceType otherType = (ISourceType) ((JavaElement) types[i])
140: .getElementInfo();
141: if (!otherType.equals(topLevelType)
142: && index < length) // check that the index is in bounds (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=62861)
143: sourceTypes[index++] = otherType;
144: }
145: return new NameEnvironmentAnswer(sourceTypes,
146: answer.restriction);
147: } catch (JavaModelException npe) {
148: return null;
149: }
150: }
151: }
152: return null;
153: }
154:
155: /**
156: * Find the packages that start with the given prefix.
157: * A valid prefix is a qualified name separated by periods
158: * (ex. java.util).
159: * The packages found are passed to:
160: * ISearchRequestor.acceptPackage(char[][] packageName)
161: */
162: public void findPackages(char[] prefix, ISearchRequestor requestor) {
163: this .nameLookup.seekPackageFragments(new String(prefix), true,
164: new SearchableEnvironmentRequestor(requestor));
165: }
166:
167: /**
168: * Find the top-level types that are defined
169: * in the current environment and whose simple name matches the given name.
170: *
171: * The types found are passed to one of the following methods (if additional
172: * information is known about the types):
173: * ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
174: * ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
175: * ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
176: *
177: * This method can not be used to find member types... member
178: * types are found relative to their enclosing type.
179: */
180: public void findExactTypes(char[] name, final boolean findMembers,
181: int searchFor, final ISearchRequestor storage) {
182:
183: try {
184: final String excludePath;
185: if (this .unitToSkip != null) {
186: if (!(this .unitToSkip instanceof IJavaElement)) {
187: // revert to model investigation
188: findExactTypes(new String(name), storage,
189: convertSearchFilterToModelFilter(searchFor));
190: return;
191: }
192: excludePath = ((IJavaElement) this .unitToSkip)
193: .getPath().toString();
194: } else {
195: excludePath = null;
196: }
197:
198: IProgressMonitor progressMonitor = new IProgressMonitor() {
199: boolean isCanceled = false;
200:
201: public void beginTask(String n, int totalWork) {
202: // implements interface method
203: }
204:
205: public void done() {
206: // implements interface method
207: }
208:
209: public void internalWorked(double work) {
210: // implements interface method
211: }
212:
213: public boolean isCanceled() {
214: return isCanceled;
215: }
216:
217: public void setCanceled(boolean value) {
218: isCanceled = value;
219: }
220:
221: public void setTaskName(String n) {
222: // implements interface method
223: }
224:
225: public void subTask(String n) {
226: // implements interface method
227: }
228:
229: public void worked(int work) {
230: // implements interface method
231: }
232: };
233: IRestrictedAccessTypeRequestor typeRequestor = new IRestrictedAccessTypeRequestor() {
234: public void acceptType(int modifiers,
235: char[] packageName, char[] simpleTypeName,
236: char[][] enclosingTypeNames, String path,
237: AccessRestriction access) {
238: if (excludePath != null && excludePath.equals(path))
239: return;
240: if (!findMembers && enclosingTypeNames != null
241: && enclosingTypeNames.length > 0)
242: return; // accept only top level types
243: storage.acceptType(packageName, simpleTypeName,
244: enclosingTypeNames, modifiers, access);
245: }
246: };
247: try {
248: new BasicSearchEngine(this .workingCopies)
249: .searchAllTypeNames(null,
250: SearchPattern.R_EXACT_MATCH, name,
251: SearchPattern.R_EXACT_MATCH, searchFor,
252: this .searchScope, typeRequestor,
253: CANCEL_IF_NOT_READY_TO_SEARCH,
254: progressMonitor);
255: } catch (OperationCanceledException e) {
256: findExactTypes(new String(name), storage,
257: convertSearchFilterToModelFilter(searchFor));
258: }
259: } catch (JavaModelException e) {
260: findExactTypes(new String(name), storage,
261: convertSearchFilterToModelFilter(searchFor));
262: }
263: }
264:
265: /**
266: * Returns all types whose simple name matches with the given <code>name</code>.
267: */
268: private void findExactTypes(String name, ISearchRequestor storage,
269: int type) {
270: SearchableEnvironmentRequestor requestor = new SearchableEnvironmentRequestor(
271: storage, this .unitToSkip, this .project, this .nameLookup);
272: this .nameLookup.seekTypes(name, null, false, type, requestor);
273: }
274:
275: /**
276: * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[][])
277: */
278: public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
279: if (compoundTypeName == null)
280: return null;
281:
282: int length = compoundTypeName.length;
283: if (length <= 1) {
284: if (length == 0)
285: return null;
286: return find(new String(compoundTypeName[0]), null);
287: }
288:
289: int lengthM1 = length - 1;
290: char[][] packageName = new char[lengthM1][];
291: System.arraycopy(compoundTypeName, 0, packageName, 0, lengthM1);
292:
293: return find(new String(compoundTypeName[lengthM1]),
294: CharOperation.toString(packageName));
295: }
296:
297: /**
298: * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#findType(char[], char[][])
299: */
300: public NameEnvironmentAnswer findType(char[] name,
301: char[][] packageName) {
302: if (name == null)
303: return null;
304:
305: return find(new String(name), packageName == null
306: || packageName.length == 0 ? null : CharOperation
307: .toString(packageName));
308: }
309:
310: /**
311: * Find the top-level types that are defined
312: * in the current environment and whose name starts with the
313: * given prefix. The prefix is a qualified name separated by periods
314: * or a simple name (ex. java.util.V or V).
315: *
316: * The types found are passed to one of the following methods (if additional
317: * information is known about the types):
318: * ISearchRequestor.acceptType(char[][] packageName, char[] typeName)
319: * ISearchRequestor.acceptClass(char[][] packageName, char[] typeName, int modifiers)
320: * ISearchRequestor.acceptInterface(char[][] packageName, char[] typeName, int modifiers)
321: *
322: * This method can not be used to find member types... member
323: * types are found relative to their enclosing type.
324: */
325: public void findTypes(char[] prefix, final boolean findMembers,
326: boolean camelCaseMatch, int searchFor,
327: final ISearchRequestor storage) {
328:
329: /*
330: if (true){
331: findTypes(new String(prefix), storage, NameLookup.ACCEPT_CLASSES | NameLookup.ACCEPT_INTERFACES);
332: return;
333: }
334: */
335: try {
336: final String excludePath;
337: if (this .unitToSkip != null) {
338: if (!(this .unitToSkip instanceof IJavaElement)) {
339: // revert to model investigation
340: findTypes(new String(prefix), storage,
341: convertSearchFilterToModelFilter(searchFor));
342: return;
343: }
344: excludePath = ((IJavaElement) this .unitToSkip)
345: .getPath().toString();
346: } else {
347: excludePath = null;
348: }
349: int lastDotIndex = CharOperation.lastIndexOf('.', prefix);
350: char[] qualification, simpleName;
351: if (lastDotIndex < 0) {
352: qualification = null;
353: if (camelCaseMatch) {
354: simpleName = prefix;
355: } else {
356: simpleName = CharOperation.toLowerCase(prefix);
357: }
358: } else {
359: qualification = CharOperation.subarray(prefix, 0,
360: lastDotIndex);
361: if (camelCaseMatch) {
362: simpleName = CharOperation.subarray(prefix,
363: lastDotIndex + 1, prefix.length);
364: } else {
365: simpleName = CharOperation
366: .toLowerCase(CharOperation.subarray(prefix,
367: lastDotIndex + 1, prefix.length));
368: }
369: }
370:
371: IProgressMonitor progressMonitor = new IProgressMonitor() {
372: boolean isCanceled = false;
373:
374: public void beginTask(String name, int totalWork) {
375: // implements interface method
376: }
377:
378: public void done() {
379: // implements interface method
380: }
381:
382: public void internalWorked(double work) {
383: // implements interface method
384: }
385:
386: public boolean isCanceled() {
387: return isCanceled;
388: }
389:
390: public void setCanceled(boolean value) {
391: isCanceled = value;
392: }
393:
394: public void setTaskName(String name) {
395: // implements interface method
396: }
397:
398: public void subTask(String name) {
399: // implements interface method
400: }
401:
402: public void worked(int work) {
403: // implements interface method
404: }
405: };
406: IRestrictedAccessTypeRequestor typeRequestor = new IRestrictedAccessTypeRequestor() {
407: public void acceptType(int modifiers,
408: char[] packageName, char[] simpleTypeName,
409: char[][] enclosingTypeNames, String path,
410: AccessRestriction access) {
411: if (excludePath != null && excludePath.equals(path))
412: return;
413: if (!findMembers && enclosingTypeNames != null
414: && enclosingTypeNames.length > 0)
415: return; // accept only top level types
416: storage.acceptType(packageName, simpleTypeName,
417: enclosingTypeNames, modifiers, access);
418: }
419: };
420: try {
421: int matchRule = SearchPattern.R_PREFIX_MATCH;
422: if (camelCaseMatch)
423: matchRule |= SearchPattern.R_CAMEL_CASE_MATCH
424: | SearchPattern.R_PREFIX_MATCH;
425: new BasicSearchEngine(this .workingCopies)
426: .searchAllTypeNames(
427: qualification,
428: SearchPattern.R_EXACT_MATCH,
429: simpleName,
430: matchRule, // not case sensitive
431: searchFor, this .searchScope,
432: typeRequestor,
433: CANCEL_IF_NOT_READY_TO_SEARCH,
434: progressMonitor);
435: } catch (OperationCanceledException e) {
436: findTypes(new String(prefix), storage,
437: convertSearchFilterToModelFilter(searchFor));
438: }
439: } catch (JavaModelException e) {
440: findTypes(new String(prefix), storage,
441: convertSearchFilterToModelFilter(searchFor));
442: }
443: }
444:
445: /**
446: * Returns all types whose name starts with the given (qualified) <code>prefix</code>.
447: *
448: * If the <code>prefix</code> is unqualified, all types whose simple name matches
449: * the <code>prefix</code> are returned.
450: */
451: private void findTypes(String prefix, ISearchRequestor storage,
452: int type) {
453: //TODO (david) should add camel case support
454: SearchableEnvironmentRequestor requestor = new SearchableEnvironmentRequestor(
455: storage, this .unitToSkip, this .project, this .nameLookup);
456: int index = prefix.lastIndexOf('.');
457: if (index == -1) {
458: this .nameLookup.seekTypes(prefix, null, true, type,
459: requestor);
460: } else {
461: String packageName = prefix.substring(0, index);
462: JavaElementRequestor elementRequestor = new JavaElementRequestor();
463: this .nameLookup.seekPackageFragments(packageName, false,
464: elementRequestor);
465: IPackageFragment[] fragments = elementRequestor
466: .getPackageFragments();
467: if (fragments != null) {
468: String className = prefix.substring(index + 1);
469: for (int i = 0, length = fragments.length; i < length; i++)
470: if (fragments[i] != null)
471: this .nameLookup.seekTypes(className,
472: fragments[i], true, type, requestor);
473: }
474: }
475: }
476:
477: /**
478: * @see org.eclipse.jdt.internal.compiler.env.INameEnvironment#isPackage(char[][], char[])
479: */
480: public boolean isPackage(char[][] parentPackageName,
481: char[] subPackageName) {
482: String[] pkgName;
483: if (parentPackageName == null)
484: pkgName = new String[] { new String(subPackageName) };
485: else {
486: int length = parentPackageName.length;
487: pkgName = new String[length + 1];
488: for (int i = 0; i < length; i++)
489: pkgName[i] = new String(parentPackageName[i]);
490: pkgName[length] = new String(subPackageName);
491: }
492: return this .nameLookup.isPackage(pkgName);
493: }
494:
495: /**
496: * Returns a printable string for the array.
497: */
498: protected String toStringChar(char[] name) {
499: return "[" //$NON-NLS-1$
500: + new String(name) + "]"; //$NON-NLS-1$
501: }
502:
503: /**
504: * Returns a printable string for the array.
505: */
506: protected String toStringCharChar(char[][] names) {
507: StringBuffer result = new StringBuffer();
508: for (int i = 0; i < names.length; i++) {
509: result.append(toStringChar(names[i]));
510: }
511: return result.toString();
512: }
513:
514: public void cleanup() {
515: // nothing to do
516: }
517: }
|