1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Lukas Hanke <hanke@yatta.de> - [templates][content assist] Content assist for 'for' loop should suggest member variables - https://bugs.eclipse.org/117215
14  *     Microsoft Corporation - moved template related code to jdt.core.manipulation - https://bugs.eclipse.org/549989
15  *******************************************************************************/
16 package org.eclipse.jdt.internal.corext.template.java;
17 
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.ListIterator;
23 import java.util.Map;
24 
25 import org.eclipse.core.runtime.Assert;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Status;
29 
30 import org.eclipse.jdt.core.CompletionProposal;
31 import org.eclipse.jdt.core.CompletionRequestor;
32 import org.eclipse.jdt.core.ICompilationUnit;
33 import org.eclipse.jdt.core.IJavaProject;
34 import org.eclipse.jdt.core.IType;
35 import org.eclipse.jdt.core.ITypeHierarchy;
36 import org.eclipse.jdt.core.ITypeParameter;
37 import org.eclipse.jdt.core.JavaModelException;
38 import org.eclipse.jdt.core.Signature;
39 import org.eclipse.jdt.core.compiler.IProblem;
40 
41 import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
42 
43 /**
44  * A completion requester to collect informations on local variables.
45  * This class is used for guessing variable names like arrays, collections, etc.
46  */
47 final class CompilationUnitCompletion extends CompletionRequestor {
48 
49 	/**
50 	 * Describes a local variable (including parameters) inside the method where
51 	 * code completion was invoked. Special predicates exist to query whether
52 	 * a variable can be iterated over.
53 	 */
54 	public final class Variable {
55 		private static final int UNKNOWN= 0, NONE= 0;
56 		private static final int ARRAY= 1;
57 		private static final int COLLECTION= 2;
58 		private static final int ITERABLE= 4;
59 
60 		/**
61 		 * The name of the local variable.
62 		 */
63 		private final String name;
64 
65 		/**
66 		 * The signature of the local variable's type.
67 		 */
68 		private final String signature;
69 
70 		/* lazily computed properties */
71 		private int fType= UNKNOWN;
72 		private int fChecked= NONE;
73 		private String[] fMemberTypes;
74 
Variable(String name, String signature)75 		private Variable(String name, String signature) {
76 			this.name= name;
77 			this.signature= signature;
78 		}
79 
80 		/**
81 		 * Returns the name of the variable.
82 		 *
83 		 * @return the name of the variable
84 		 */
getName()85 		public String getName() {
86 			return name;
87 		}
88 
89 		/**
90 		 * Returns <code>true</code> if the type of the local variable is an
91 		 * array type.
92 		 *
93 		 * @return <code>true</code> if the receiver's type is an array,
94 		 *         <code>false</code> if not
95 		 */
isArray()96 		public boolean isArray() {
97 			if (fType == UNKNOWN && (fChecked & ARRAY) == 0 && Signature.getTypeSignatureKind(signature) == Signature.ARRAY_TYPE_SIGNATURE)
98 				fType= ARRAY;
99 			fChecked |= ARRAY;
100 			return fType == ARRAY;
101 		}
102 
103 		/**
104 		 * Returns <code>true</code> if the receiver's type is a subclass of
105 		 * <code>java.util.Collection</code>, <code>false</code> otherwise.
106 		 *
107 		 * @return <code>true</code> if the receiver's type is a subclass of
108 		 *         <code>java.util.Collection</code>, <code>false</code>
109 		 *         otherwise
110 		 */
isCollection()111 		public boolean isCollection() {
112 			// Collection extends Iterable
113 			if ((fType == UNKNOWN || fType == ITERABLE) && (fChecked & COLLECTION) == 0 && isSubtypeOf("java.util.Collection")) //$NON-NLS-1$
114 				fType= COLLECTION;
115 			fChecked |= COLLECTION;
116 			return fType == COLLECTION;
117 		}
118 
119 		/**
120 		 * Returns <code>true</code> if the receiver's type is a subclass of
121 		 * <code>java.lang.Iterable</code>, <code>false</code> otherwise.
122 		 *
123 		 * @return <code>true</code> if the receiver's type is a subclass of
124 		 *         <code>java.lang.Iterable</code>, <code>false</code>
125 		 *         otherwise
126 		 */
isIterable()127 		public boolean isIterable() {
128 			if (fType == UNKNOWN && (fChecked & ITERABLE) == 0 && isSubtypeOf("java.lang.Iterable")) //$NON-NLS-1$
129 				fType= ITERABLE;
130 			fChecked |= ITERABLE;
131 			return fType == ITERABLE || fType == COLLECTION; // Collection extends Iterable
132 		}
133 
134 		/**
135 		 * Returns <code>true</code> if the receiver's type is an implementor
136 		 * of <code>interfaceName</code>.
137 		 *
138 		 * @param supertype the fully qualified name of the interface
139 		 * @return <code>true</code> if the receiver's type implements the
140 		 *         type named <code>interfaceName</code>
141 		 */
isSubtypeOf(String supertype)142 		private boolean isSubtypeOf(String supertype) {
143 			String implementorName= SignatureUtil.stripSignatureToFQN(signature);
144 			if (implementorName.length() == 0)
145 				return false;
146 
147 			int implementorDims= Signature.getArrayCount(signature);
148 			int superDimsIndex= supertype.indexOf("[]"); //$NON-NLS-1$
149 			int superDims;
150 			if (superDimsIndex != -1) {
151 				superDims= (supertype.length() - superDimsIndex) / 2;
152 				supertype= supertype.substring(0, superDimsIndex);
153 			} else {
154 				superDims= 0;
155 			}
156 			if (implementorDims > superDims) {
157 				return "java.lang.Object".equals(supertype); //$NON-NLS-1$
158 			} else if (superDims != implementorDims) {
159 				return false;
160 			}
161 
162 			boolean qualified= supertype.indexOf('.') != -1;
163 
164 			// try cheap test first
165 			if (implementorName.equals(supertype) || !qualified && Signature.getSimpleName(implementorName).equals(supertype))
166 				return true;
167 
168 			if (fUnit == null)
169 				return false;
170 
171 			IJavaProject project= fUnit.getJavaProject();
172 
173 			try {
174 				IType sub= project.findType(implementorName);
175 				if (sub == null)
176 					return false;
177 
178 				if (qualified) {
179 					IType sup= project.findType(supertype);
180 					if (sup == null)
181 						return false;
182 					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
183 					return hierarchy.contains(sup);
184 				} else {
185 					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
186 					IType[] allTypes= hierarchy.getAllTypes();
187 					for (IType type : allTypes) {
188 						if (type.getElementName().equals(supertype))
189 							return true;
190 					}
191 				}
192 
193 			} catch (JavaModelException e) {
194 				// ignore and return false
195 			}
196 
197 			return false;
198 		}
199 
getSupertypes(String supertype)200 		private IType[] getSupertypes(String supertype) {
201 			IType[] empty= new IType[0];
202 			String implementorName= SignatureUtil.stripSignatureToFQN(signature);
203 			if (implementorName.length() == 0)
204 				return empty;
205 
206 			boolean qualified= supertype.indexOf('.') != -1;
207 
208 			if (fUnit == null)
209 				return empty;
210 
211 			IJavaProject project= fUnit.getJavaProject();
212 
213 			try {
214 				IType sub= project.findType(implementorName);
215 				if (sub == null)
216 					return empty;
217 
218 				if (qualified) {
219 					IType sup= project.findType(supertype);
220 					if (sup == null)
221 						return empty;
222 					return new IType[] {sup};
223 				} else {
224 					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
225 					IType[] allTypes= hierarchy.getAllTypes();
226 					List<IType> matches= new ArrayList<>();
227 					for (IType type : allTypes) {
228 						if (type.getElementName().equals(supertype))
229 							matches.add(type);
230 					}
231 					return matches.toArray(new IType[matches.size()]);
232 				}
233 
234 			} catch (JavaModelException e) {
235 				// ignore and return false
236 			}
237 
238 			return empty;
239 		}
240 
241 		/**
242 		 * Returns the signature of the member type.
243 		 *
244 		 * @return the signature of the member type
245 		 */
getMemberTypeSignature()246 		public String getMemberTypeSignature() {
247 			return getMemberTypeSignatures()[0];
248 		}
249 
250 		/**
251 		 * Returns the signatures of all member type bounds.
252 		 *
253 		 * @return the signatures of all member type bounds
254 		 */
getMemberTypeSignatures()255 		public String[] getMemberTypeSignatures() {
256 			if (isArray()) {
257 				return new String[] {Signature.createArraySignature(Signature.getElementType(signature), Signature.getArrayCount(signature) - 1)};
258 			} else if (fUnit != null && (isIterable() || isCollection())) {
259 				if (fMemberTypes == null) {
260 					try {
261 						try {
262 							TypeParameterResolver util= new TypeParameterResolver(this);
263 							fMemberTypes= util.computeBinding("java.lang.Iterable", 0); //$NON-NLS-1$
264 						} catch (JavaModelException e) {
265 							try {
266 								TypeParameterResolver util= new TypeParameterResolver(this);
267 								fMemberTypes= util.computeBinding("java.util.Collection", 0); //$NON-NLS-1$
268 							} catch (JavaModelException x) {
269 								fMemberTypes= new String[0];
270 							}
271 						}
272 					} catch (IndexOutOfBoundsException e) {
273 						fMemberTypes= new String[0];
274 					}
275 				}
276 				if (fMemberTypes.length > 0)
277 					return fMemberTypes;
278 			}
279 			return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
280 		}
281 
282 		/**
283 		 * Returns the type names of all member type bounds, as they would be
284 		 * appear when referenced in the current compilation unit.
285 		 *
286 		 * @return type names of all member type bounds
287 		 */
getMemberTypeNames()288 		public String[] getMemberTypeNames() {
289 			String[] signatures= getMemberTypeSignatures();
290 			String[] names= new String[signatures.length];
291 
292 			for (int i= 0; i < signatures.length; i++) {
293 				String sig= signatures[i];
294 				String local= fLocalTypes.get(Signature.getElementType(sig));
295 				int dim= Signature.getArrayCount(sig);
296 				if (local != null && dim > 0) {
297 					StringBuilder array= new StringBuilder(local);
298 					for (int j= 0; j < dim; j++)
299 						array.append("[]"); //$NON-NLS-1$
300 					local= array.toString();
301 				}
302 				if (local != null)
303 					names[i]= local;
304 				else
305 					names[i]= Signature.getSimpleName(Signature.getSignatureSimpleName(sig));
306 			}
307 			return names;
308 		}
309 
310 		/**
311 		 * Returns the type arguments of the declared type of the variable. Returns
312 		 * an empty array if it is not a parameterized type.
313 		 *
314 		 * @param type the fully qualified type name of which to match a type argument
315 		 * @param index the index of the type parameter in the type
316 		 * @return the type bounds for the specified type argument in this local variable
317 		 * @since 3.3
318 		 */
getTypeArgumentBoundSignatures(String type, int index)319 		public String[] getTypeArgumentBoundSignatures(String type, int index) {
320 			List<String> all= new ArrayList<>();
321 			IType[] supertypes= getSupertypes(type);
322 			if (fUnit != null) {
323 				for (IType supertype : supertypes) {
324 					try {
325 						TypeParameterResolver util= new TypeParameterResolver(this);
326 						String[] result= util.computeBinding(supertype.getFullyQualifiedName(), index);
327 						all.addAll(Arrays.asList(result));
328 					} catch (JavaModelException e) {
329 					} catch (IndexOutOfBoundsException e) {
330 					}
331 				}
332 			}
333 			if (all.isEmpty())
334 				return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
335 			return all.toArray(new String[all.size()]);
336 		}
337 
338 		/*
339 		 * @see java.lang.Object#toString()
340 		 */
341 		@Override
toString()342 		public String toString() {
343 			String type;
344 			switch (fType) {
345 				case ITERABLE:
346 					type= "ITERABLE"; //$NON-NLS-1$
347 					break;
348 				case COLLECTION:
349 					type= "COLLECTION"; //$NON-NLS-1$
350 					break;
351 				case ARRAY:
352 					type= "ARRAY"; //$NON-NLS-1$
353 					break;
354 				default:
355 					type= "UNKNOWN"; //$NON-NLS-1$
356 					break;
357 			}
358 			return "LocalVariable [name=\"" + name + "\" signature=\"" + signature + "\" type=\"" + type + "\" member=\"" + getMemberTypeSignature() + "\"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
359 		}
360 	}
361 
362 	/**
363 	 * Given a java type, a resolver computes the bounds of type variables
364 	 * declared in a super type, considering any type constraints along the
365 	 * inheritance path.
366 	 */
367 	private final class TypeParameterResolver {
368 		private static final String OBJECT_SIGNATURE= "Ljava.lang.Object;"; //$NON-NLS-1$
369 
370 		private final ITypeHierarchy fHierarchy;
371 		private final Variable fVariable;
372 		private final IType fType;
373 		private final List<String> fBounds= new ArrayList<>();
374 
375 		/**
376 		 * Creates a new type parameter resolver to compute the bindings of type
377 		 * parameters for the declared type of <code>variable</code>. For any
378 		 * super type of the type of <code>variable</code>, calling
379 		 * {@link #computeBinding(IType, int) computeBinding} will find the type
380 		 * bounds of type variables in the super type, considering any type
381 		 * constraints along the inheritance path.
382 		 *
383 		 * @param variable the local variable under investigation
384 		 * @throws JavaModelException if the type of <code>variable</code>
385 		 *         cannot be found
386 		 */
TypeParameterResolver(Variable variable)387 		public TypeParameterResolver(Variable variable) throws JavaModelException {
388 			String typeName= SignatureUtil.stripSignatureToFQN(variable.signature);
389 			IJavaProject project= fUnit.getJavaProject();
390 			fType= project.findType(typeName);
391 			fHierarchy= fType.newSupertypeHierarchy(null);
392 			fVariable= variable;
393 		}
394 
395 		/**
396 		 * Given a type parameter of <code>superType</code> at position
397 		 * <code>index</code>, this method computes and returns the (lower)
398 		 * type bound(s) of that parameter for an instance of <code>fType</code>.
399 		 * <p>
400 		 * <code>superType</code> must be a super type of <code>fType</code>,
401 		 * and <code>superType</code> must have at least
402 		 * <code>index + 1</code> type parameters.
403 		 * </p>
404 		 *
405 		 * @param superType the qualified type name of the super type to compute
406 		 *        the type parameter binding for
407 		 * @param index the index into the list of type parameters of
408 		 *        <code>superType</code>
409 		 * @return the binding
410 		 * @throws JavaModelException if any java model operation fails
411 		 * @throws IndexOutOfBoundsException if the index is not valid
412 		 */
computeBinding(String superType, int index)413 		public String[] computeBinding(String superType, int index) throws JavaModelException, IndexOutOfBoundsException {
414 			IJavaProject project= fUnit.getJavaProject();
415 			IType type= project.findType(superType);
416 			if (type == null)
417 				throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaManipulationPlugin.getPluginId(), IStatus.OK, "No such type", null))); //$NON-NLS-1$
418 			return computeBinding(type, index);
419 		}
420 
421 		/**
422 		 * Given a type parameter of <code>superType</code> at position
423 		 * <code>index</code>, this method computes and returns the (lower)
424 		 * type bound(s) of that parameter for an instance of <code>fType</code>.
425 		 * <p>
426 		 * <code>superType</code> must be a super type of <code>fType</code>,
427 		 * and <code>superType</code> must have at least
428 		 * <code>index + 1</code> type parameters.
429 		 * </p>
430 		 *
431 		 * @param superType the super type to compute the type parameter binding
432 		 *        for
433 		 * @param index the index into the list of type parameters of
434 		 *        <code>superType</code>
435 		 * @return the binding
436 		 * @throws JavaModelException if any java model operation fails
437 		 * @throws IndexOutOfBoundsException if the index is not valid
438 		 */
computeBinding(IType superType, int index)439 		public String[] computeBinding(IType superType, int index) throws JavaModelException, IndexOutOfBoundsException {
440 			initBounds();
441 			computeTypeParameterBinding(superType, index);
442 			return fBounds.toArray(new String[fBounds.size()]);
443 		}
444 
445 		/**
446 		 * Given a type parameter of <code>superType</code> at position
447 		 * <code>index</code>, this method recursively computes the (lower)
448 		 * type bound(s) of that parameter for an instance of <code>fType</code>.
449 		 * <p>
450 		 * <code>superType</code> must be a super type of <code>fType</code>,
451 		 * and <code>superType</code> must have at least
452 		 * <code>index + 1</code> type parameters.
453 		 * </p>
454 		 * <p>
455 		 * The type bounds are stored in <code>fBounds</code>.
456 		 * </p>
457 		 *
458 		 * @param superType the super type to compute the type parameter binding
459 		 *        for
460 		 * @param index the index into the list of type parameters of
461 		 *        <code>superType</code>
462 		 * @throws JavaModelException if any java model operation fails
463 		 * @throws IndexOutOfBoundsException if the index is not valid
464 		 */
computeTypeParameterBinding(final IType superType, final int index)465 		private void computeTypeParameterBinding(final IType superType, final int index) throws JavaModelException, IndexOutOfBoundsException {
466 			int nParameters= superType.getTypeParameters().length;
467 			if (nParameters <= index)
468 				throw new IndexOutOfBoundsException();
469 
470 			IType[] subTypes= fHierarchy.getSubtypes(superType);
471 
472 			if (subTypes.length == 0) {
473 				// we have reached down to the base type
474 				Assert.isTrue(superType.equals(fType));
475 
476 				String match= findMatchingTypeArgument(fVariable.signature, index, fUnit.findPrimaryType());
477 				String bound= SignatureUtil.getUpperBound(match);
478 
479 				// use the match whether it is a concrete type or not - if not,
480 				// the generic type will at least be in visible in our context
481 				// and can be referenced
482 				addBound(bound);
483 				return;
484 			}
485 
486 			IType subType= subTypes[0]; // take the first, as they all lead to fType
487 
488 			String signature= findMatchingSuperTypeSignature(subType, superType);
489 			String match= findMatchingTypeArgument(signature, index, subType);
490 
491 			if (isConcreteType(match, subType)) {
492 				addBound(match);
493 				return;
494 			}
495 
496 			ITypeParameter[] typeParameters= subType.getTypeParameters();
497 
498 			for (int k= 0; k < typeParameters.length; k++) {
499 				ITypeParameter formalParameter= typeParameters[k];
500 				if (formalParameter.getElementName().equals(SignatureUtil.stripSignatureToFQN(match))) {
501 					String[] bounds= formalParameter.getBounds();
502 					for (String bound : bounds) {
503 						String boundSignature= Signature.createTypeSignature(bound, true);
504 						addBound(SignatureUtil.qualifySignature(boundSignature, subType));
505 					}
506 					computeTypeParameterBinding(subType, k);
507 					return;
508 				}
509 			}
510 
511 			// We have a non-concrete type argument T, but no matching type
512 			// parameter in the sub type. This can happen if T is declared in
513 			// the enclosing type. Since it the declaration is probably visible
514 			// then, its fine to simply copy the match to the bounds and return.
515 			addBound(match);
516 			return;
517 		}
518 
519 		/**
520 		 * Finds and returns the type argument with index <code>index</code>
521 		 * in the given type super type signature. If <code>signature</code>
522 		 * is a generic signature, the type parameter at <code>index</code> is
523 		 * extracted. If the type parameter is an upper bound (<code>? super SomeType</code>),
524 		 * the type signature of <code>java.lang.Object</code> is returned.
525 		 * <p>
526 		 * Also, if <code>signature</code> has no type parameters (i.e. is a
527 		 * reference to the raw type), the type signature of
528 		 * <code>java.lang.Object</code> is returned.
529 		 * </p>
530 		 *
531 		 * @param signature the super type signature from a type's
532 		 *        <code>extends</code> or <code>implements</code> clause
533 		 * @param index the index of the type parameter to extract from
534 		 *        <code>signature</code>
535 		 * @param context the type context inside which unqualified types should
536 		 *        be resolved
537 		 * @return the type argument signature of the type parameter at
538 		 *         <code>index</code> in <code>signature</code>
539 		 * @throws IndexOutOfBoundsException if the index is not valid
540 		 */
findMatchingTypeArgument(String signature, int index, IType context)541 		private String findMatchingTypeArgument(String signature, int index, IType context) throws IndexOutOfBoundsException {
542 			String[] typeArguments= Signature.getTypeArguments(signature);
543 			if (typeArguments.length > 0 && typeArguments.length <= index)
544 				throw new IndexOutOfBoundsException();
545 			if (typeArguments.length == 0) {
546 				// raw binding - bound to Object
547 				return OBJECT_SIGNATURE;
548 			} else {
549 				String bound= SignatureUtil.getUpperBound(typeArguments[index]);
550 				return SignatureUtil.qualifySignature(bound, context);
551 			}
552 		}
553 
554 		/**
555 		 * Finds and returns the super type signature in the
556 		 * <code>extends</code> or <code>implements</code> clause of
557 		 * <code>subType</code> that corresponds to <code>superType</code>.
558 		 *
559 		 * @param subType a direct and true sub type of <code>superType</code>
560 		 * @param superType a direct super type (super class or interface) of
561 		 *        <code>subType</code>
562 		 * @return the super type signature of <code>subType</code> referring
563 		 *         to <code>superType</code>
564 		 * @throws JavaModelException if extracting the super type signatures
565 		 *         fails, or if <code>subType</code> contains no super type
566 		 *         signature to <code>superType</code>
567 		 */
findMatchingSuperTypeSignature(IType subType, IType superType)568 		private String findMatchingSuperTypeSignature(IType subType, IType superType) throws JavaModelException {
569 			String[] signatures= getSuperTypeSignatures(subType, superType);
570 			for (String signature : signatures) {
571 				String qualified= SignatureUtil.qualifySignature(signature, subType);
572 				String subFQN= SignatureUtil.stripSignatureToFQN(qualified);
573 
574 				String superFQN= superType.getFullyQualifiedName();
575 				if (subFQN.equals(superFQN)) {
576 					return signature;
577 				}
578 
579 				// handle local types
580 				if (fLocalTypes.containsValue(subFQN)) {
581 					return signature;
582 				}
583 			}
584 
585 			throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaManipulationPlugin.getPluginId(), IStatus.OK, "Illegal hierarchy", null))); //$NON-NLS-1$
586 		}
587 
588 		/**
589 		 * Returns the super interface signatures of <code>subType</code> if
590 		 * <code>superType</code> is an interface, otherwise returns the super
591 		 * type signature.
592 		 *
593 		 * @param subType the sub type signature
594 		 * @param superType the super type signature
595 		 * @return the super type signatures of <code>subType</code>
596 		 * @throws JavaModelException if any java model operation fails
597 		 */
getSuperTypeSignatures(IType subType, IType superType)598 		private String[] getSuperTypeSignatures(IType subType, IType superType) throws JavaModelException {
599 			if (superType.isInterface())
600 				return subType.getSuperInterfaceTypeSignatures();
601 			else
602 				return new String[] {subType.getSuperclassTypeSignature()};
603 		}
604 
605 		/**
606 		 * Clears the collected type bounds and initializes it with
607 		 * <code>java.lang.Object</code>.
608 		 */
initBounds()609 		private void initBounds() {
610 			fBounds.clear();
611 			fBounds.add(OBJECT_SIGNATURE);
612 		}
613 
614 		/**
615 		 * Filters the current list of type bounds through the additional type
616 		 * bound described by <code>boundSignature</code>.
617 		 *
618 		 * @param boundSignature the additional bound to add to the list of
619 		 *        collected bounds
620 		 */
addBound(String boundSignature)621 		private void addBound(String boundSignature) {
622 			if (SignatureUtil.isJavaLangObject(boundSignature))
623 				return;
624 
625 			boolean found= false;
626 			for (ListIterator<String> it= fBounds.listIterator(); it.hasNext();) {
627 				String old= it.next();
628 				if (isTrueSubtypeOf(boundSignature, old)) {
629 					if (!found) {
630 						it.set(boundSignature);
631 						found= true;
632 					} else {
633 						it.remove();
634 					}
635 				}
636 			}
637 			if (!found)
638 				fBounds.add(boundSignature);
639 		}
640 
641 		/**
642 		 * Returns <code>true</code> if <code>subTypeSignature</code>
643 		 * describes a type which is a true sub type of the type described by
644 		 * <code>superTypeSignature</code>.
645 		 *
646 		 * @param subTypeSignature the potential subtype's signature
647 		 * @param superTypeSignature the potential supertype's signature
648 		 * @return <code>true</code> if the inheritance relationship holds
649 		 */
isTrueSubtypeOf(String subTypeSignature, String superTypeSignature)650 		private boolean isTrueSubtypeOf(String subTypeSignature, String superTypeSignature) {
651 			// try cheap test first
652 			if (subTypeSignature.equals(superTypeSignature))
653 				return true;
654 
655 			if (SignatureUtil.isJavaLangObject(subTypeSignature))
656 				return false; // Object has no super types
657 
658 			if (Signature.getTypeSignatureKind(subTypeSignature) != Signature.BASE_TYPE_SIGNATURE && SignatureUtil.isJavaLangObject(superTypeSignature))
659 				return true;
660 
661 			IJavaProject project= fUnit.getJavaProject();
662 
663 			try {
664 
665 				if ((Signature.getTypeSignatureKind(subTypeSignature) & (Signature.TYPE_VARIABLE_SIGNATURE | Signature.CLASS_TYPE_SIGNATURE)) == 0)
666 					return false;
667 				IType subType= project.findType(SignatureUtil.stripSignatureToFQN(subTypeSignature));
668 				if (subType == null)
669 					return false;
670 
671 				if ((Signature.getTypeSignatureKind(superTypeSignature) & (Signature.TYPE_VARIABLE_SIGNATURE | Signature.CLASS_TYPE_SIGNATURE)) == 0)
672 					return false;
673 				IType superType= project.findType(SignatureUtil.stripSignatureToFQN(superTypeSignature));
674 				if (superType == null)
675 					return false;
676 
677 				ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(null);
678 				IType[] types= hierarchy.getAllSupertypes(subType);
679 
680 				for (IType type : types)
681 					if (type.equals(superType))
682 						return true;
683 			} catch (JavaModelException e) {
684 				// ignore and return false
685 			}
686 
687 			return false;
688 		}
689 
690 		/**
691 		 * Returns <code>true</code> if <code>signature</code> is a concrete type signature,
692 		 * <code>false</code> if it is a type variable.
693 		 *
694 		 * @param signature the signature to check
695 		 * @param context the context inside which to resolve the type
696 		 * @return <code>true</code> if the given signature is a concrete type signature
697 		 * @throws JavaModelException if finding the type fails
698 		 */
isConcreteType(String signature, IType context)699 		private boolean isConcreteType(String signature, IType context) throws JavaModelException {
700 			// Inexpensive check for the variable type first
701 			if (Signature.TYPE_VARIABLE_SIGNATURE == Signature.getTypeSignatureKind(signature))
702 				return false;
703 
704 			// try and resolve otherwise
705 			if (context.isBinary()) {
706 				return fUnit.getJavaProject().findType(SignatureUtil.stripSignatureToFQN(signature)) != null;
707 			} else {
708 				return context.resolveType(SignatureUtil.stripSignatureToFQN(signature)) != null;
709 			}
710 		}
711 	}
712 
713 	private ICompilationUnit fUnit;
714 
715 	private List<Variable> fLocalVariables= new ArrayList<>();
716 	private List<Variable> fFields= new ArrayList<>();
717 	private Map<String, String> fLocalTypes= new HashMap<>();
718 
719 	private boolean fError;
720 
721 	/**
722 	 * Creates a compilation unit completion.
723 	 *
724 	 * @param unit the compilation unit, may be <code>null</code>.
725 	 */
CompilationUnitCompletion(ICompilationUnit unit)726 	CompilationUnitCompletion(ICompilationUnit unit) {
727 		reset(unit);
728 		setIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, true);
729 		setIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, true);
730 		setIgnored(CompletionProposal.KEYWORD, true);
731 		setIgnored(CompletionProposal.LABEL_REF, true);
732 		setIgnored(CompletionProposal.METHOD_DECLARATION, true);
733 		setIgnored(CompletionProposal.METHOD_NAME_REFERENCE, true);
734 		setIgnored(CompletionProposal.METHOD_REF, true);
735 		setIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, true);
736 		setIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, true);
737 		setIgnored(CompletionProposal.PACKAGE_REF, true);
738 		setIgnored(CompletionProposal.MODULE_REF, true);
739 		setIgnored(CompletionProposal.MODULE_DECLARATION, true);
740 		setIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION, true);
741 		setIgnored(CompletionProposal.VARIABLE_DECLARATION, true);
742 		setIgnored(CompletionProposal.TYPE_REF, true);
743 	}
744 
745 	/**
746 	 * Resets the completion requester.
747 	 *
748 	 * @param unit the compilation unit, may be <code>null</code>.
749 	 */
reset(ICompilationUnit unit)750 	private void reset(ICompilationUnit unit) {
751 		fUnit= unit;
752 		fLocalVariables.clear();
753 		fFields.clear();
754 		fLocalTypes.clear();
755 
756 		if (fUnit != null) {
757 			try {
758 				IType[] cuTypes= fUnit.getAllTypes();
759 				for (IType cuType : cuTypes) {
760 					String fqn= cuType.getFullyQualifiedName();
761 					String sig= Signature.createTypeSignature(fqn, true);
762 					fLocalTypes.put(sig, cuType.getElementName());
763 				}
764 			} catch (JavaModelException e) {
765 				// ignore
766 			}
767 		}
768 		fError= false;
769 	}
770 
771 	/*
772 	 * @see org.eclipse.jdt.core.CompletionRequestor#accept(org.eclipse.jdt.core.CompletionProposal)
773 	 */
774 	@Override
accept(CompletionProposal proposal)775 	public void accept(CompletionProposal proposal) {
776 
777 		String name= String.valueOf(proposal.getCompletion());
778 		String signature= String.valueOf(proposal.getSignature());
779 
780 		switch (proposal.getKind()) {
781 
782 			case CompletionProposal.LOCAL_VARIABLE_REF:
783 				// collect local variables
784 				fLocalVariables.add(new Variable(name, signature));
785 				break;
786 			case CompletionProposal.FIELD_REF:
787 				// collect local variables
788 				fFields.add(new Variable(name, signature));
789 				break;
790 
791 			default:
792 				break;
793 		}
794 	}
795 
796 	/*
797 	 * @see org.eclipse.jdt.core.CompletionRequestor#completionFailure(org.eclipse.jdt.core.compiler.IProblem)
798 	 */
799 	@Override
completionFailure(IProblem problem)800 	public void completionFailure(IProblem problem) {
801 		fError= true;
802 	}
803 
804 	/**
805 	 * Tests if the code completion process produced errors.
806 	 *
807 	 * @return <code>true</code> if there are errors, <code>false</code>
808 	 *         otherwise
809 	 */
hasErrors()810 	public boolean hasErrors() {
811 		return fError;
812 	}
813 
814 	/**
815 	 * Returns all local variable names.
816 	 *
817 	 * @return all local variable names
818 	 */
getLocalVariableNames()819 	public String[] getLocalVariableNames() {
820 		String[] names= new String[fLocalVariables.size()];
821 		int i= 0;
822 		for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
823 			Variable localVariable= iterator.previous();
824 			names[i++]= localVariable.getName();
825 		}
826 		return names;
827 	}
828 
829 	/**
830 	 * Returns all field names.
831 	 *
832 	 * @return all field names
833 	 * @since 3.3
834 	 */
getFieldNames()835 	public String[] getFieldNames() {
836 		String[] names= new String[fFields.size()];
837 		int i= 0;
838 		for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
839 			Variable field= iterator.previous();
840 			names[i++]= field.getName();
841 		}
842 		return names;
843 	}
844 
845 	/**
846 	 * Returns all arrays, visible in the current context's scope, in the order that they appear.
847 	 *
848 	 * @return all visible arrays
849 	 */
findArraysInCurrentScope()850 	public Variable[] findArraysInCurrentScope() {
851 		List<Variable> arrays= new ArrayList<>();
852 
853 		// local variables
854 		for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
855 			Variable localVariable= iterator.previous();
856 
857 			if (localVariable.isArray())
858 				arrays.add(localVariable);
859 		}
860 
861 		// fields
862 		for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
863 			Variable field= iterator.previous();
864 
865 			if (field.isArray())
866 				arrays.add(field);
867 		}
868 
869 		return arrays.toArray(new Variable[arrays.size()]);
870 	}
871 
872 	/**
873 	 * Returns all local variables implementing or extending
874 	 * <code>clazz</code> in the order that they appear.
875 	 *
876 	 * @param clazz the fully qualified type name of the class to match
877 	 * @return all local variables matching <code>clazz</code>
878 	 */
findLocalVariables(String clazz)879 	public Variable[] findLocalVariables(String clazz) {
880 		List<Variable> matches= new ArrayList<>();
881 
882 		for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
883 			Variable localVariable= iterator.previous();
884 
885 			if (localVariable.isSubtypeOf(clazz))
886 				matches.add(localVariable);
887 		}
888 
889 		return matches.toArray(new Variable[matches.size()]);
890 	}
891 
892 	/**
893 	 * Returns all local variables implementing or extending
894 	 * <code>clazz</code> in the order that they appear.
895 	 *
896 	 * @param clazz the fully qualified type name of the class to match
897 	 * @return all local variables matching <code>clazz</code>
898 	 */
findFieldVariables(String clazz)899 	public Variable[] findFieldVariables(String clazz) {
900 		List<Variable> matches= new ArrayList<>();
901 
902 		for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
903 			Variable localVariable= iterator.previous();
904 
905 			if (localVariable.isSubtypeOf(clazz))
906 				matches.add(localVariable);
907 		}
908 
909 		return matches.toArray(new Variable[matches.size()]);
910 	}
911 
912 	/**
913 	 * Returns all variables, visible in the current context's scope, implementing
914 	 * <code>java.lang.Iterable</code> <em>and</em> all arrays, in the order that they appear. That
915 	 * is, the returned variables can be used within the <code>foreach</code> language construct.
916 	 *
917 	 * @return all visible <code>Iterable</code>s and arrays
918 	 */
findIterablesInCurrentScope()919 	public Variable[] findIterablesInCurrentScope() {
920 		List<Variable> iterables= new ArrayList<>();
921 
922 		// local variables
923 		for (ListIterator<Variable> iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
924 			Variable localVariable= iterator.previous();
925 
926 			if (localVariable.isArray() || localVariable.isIterable())
927 				iterables.add(localVariable);
928 		}
929 
930 		// fields
931 		for (ListIterator<Variable> iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
932 			Variable field= iterator.previous();
933 
934 			if (field.isArray() || field.isIterable())
935 				iterables.add(field);
936 		}
937 
938 		return iterables.toArray(new Variable[iterables.size()]);
939 	}
940 
941 }
942 
943