1 /*******************************************************************************
2  * Copyright (c) 2004, 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  *******************************************************************************/
14 package org.eclipse.jdt.core;
15 
16 import org.eclipse.jdt.core.compiler.IProblem;
17 
18 /**
19  * Abstract base class for a completion requestor which is passed completion
20  * proposals as they are generated in response to a code assist request.
21  * <p>
22  * This class is intended to be subclassed by clients.
23  * </p>
24  * <p>
25  * The code assist engine normally invokes methods on completion
26  * requestor in the following sequence:
27  * <pre>
28  * requestor.beginReporting();
29  * requestor.acceptContext(context);
30  * requestor.accept(proposal_1);
31  * requestor.accept(proposal_2);
32  * ...
33  * requestor.endReporting();
34  * </pre>
35  * <p>
36  * If, however, the engine is unable to offer completion proposals
37  * for whatever reason, <code>completionFailure</code> is called
38  * with a problem object describing why completions were unavailable.
39  * In this case, the sequence of calls is:
40  * <pre>
41  * requestor.beginReporting();
42  * requestor.acceptContext(context);
43  * requestor.completionFailure(problem);
44  * requestor.endReporting();
45  * </pre>
46  * <p>
47  * In either case, the bracketing <code>beginReporting</code>
48  * <code>endReporting</code> calls are always made as well as
49  * <code>acceptContext</code> call.
50  * </p>
51  * <p>
52  * The class was introduced in 3.0 as a more evolvable replacement
53  * for the <code>ICompletionRequestor</code> interface.
54  * </p>
55  *
56  * @see ICodeAssist
57  * @since 3.0
58  */
59 public abstract class CompletionRequestor {
60 
61 	/**
62 	 * The set of CompletionProposal kinds that this requestor
63 	 * ignores; <code>0</code> means the set is empty.
64 	 * 1 << completionProposalKind
65 	 */
66 	private int ignoreSet = 0;
67 
68 	private String[] favoriteReferences;
69 
70 	/**
71 	 * The set of CompletionProposal kinds that this requestor
72 	 * allows for required proposals; <code>0</code> means the set is empty.
73 	 * 1 << completionProposalKind
74 	 */
75 	private int requiredProposalAllowSet[] = null;
76 
77 	private boolean requireExtendedContext = false;
78 
79 	/**
80 	 * Creates a new completion requestor.
81 	 * The requestor is interested in all kinds of completion
82 	 * proposals; none will be ignored.
83 	 *
84 	 * Calls to this constructor are identical to calls to <code>CompletionRequestor(false)</code>
85 	 */
CompletionRequestor()86 	public CompletionRequestor() {
87 		this(false);
88 	}
89 
90 	/**
91 	 * Creates a new completion requestor.
92 	 * If <code>ignoreAll</code> is <code>true</code> the requestor is not interested in
93 	 * all kinds of completion proposals; all will be ignored. For each kind of completion proposals
94 	 * that is of interest, <code>setIgnored(kind, false)</code> must be called.
95 	 * If <code>ignoreAll</code> is <code>false</code> the requestor is interested in
96 	 * all kinds of completion proposals; none will be ignored.
97 	 *
98 	 * @param ignoreAll <code>true</code> to ignore all kinds of completion proposals,
99 	 * and <code>false</code> to propose all kinds
100 	 *
101 	 * @since 3.4
102 	 */
CompletionRequestor(boolean ignoreAll)103 	public CompletionRequestor(boolean ignoreAll) {
104 		this.ignoreSet = ignoreAll ? 0xffffffff : 0x00000000;
105 	}
106 
107 	/**
108 	 * Returns whether the given kind of completion proposal is ignored.
109 	 *
110 	 * @param completionProposalKind one of the kind constants declared
111 	 * on <code>CompletionProposal</code>
112 	 * @return <code>true</code> if the given kind of completion proposal
113 	 * is ignored by this requestor, and <code>false</code> if it is of
114 	 * interest
115 	 * @see #setIgnored(int, boolean)
116 	 * @see CompletionProposal#getKind()
117 	 */
isIgnored(int completionProposalKind)118 	public boolean isIgnored(int completionProposalKind) {
119 		if (completionProposalKind < CompletionProposal.FIRST_KIND
120 			|| completionProposalKind > CompletionProposal.LAST_KIND) {
121 				throw new IllegalArgumentException("Unknown kind of completion proposal: "+completionProposalKind); //$NON-NLS-1$
122 		}
123 		return 0 != (this.ignoreSet & (1 << completionProposalKind));
124 	}
125 
126 	/**
127 	 * Sets whether the given kind of completion proposal is ignored.
128 	 *
129 	 * @param completionProposalKind one of the kind constants declared
130 	 * on <code>CompletionProposal</code>
131 	 * @param ignore <code>true</code> if the given kind of completion proposal
132 	 * is ignored by this requestor, and <code>false</code> if it is of
133 	 * interest
134 	 * @see #isIgnored(int)
135 	 * @see CompletionProposal#getKind()
136 	 */
setIgnored(int completionProposalKind, boolean ignore)137 	public void setIgnored(int completionProposalKind, boolean ignore) {
138 		if (completionProposalKind < CompletionProposal.FIRST_KIND
139 			|| completionProposalKind > CompletionProposal.LAST_KIND) {
140 				throw new IllegalArgumentException("Unknown kind of completion proposal: "+completionProposalKind); //$NON-NLS-1$
141 		}
142 		if (ignore) {
143 			this.ignoreSet |= (1 << completionProposalKind);
144 		} else {
145 			this.ignoreSet &= ~(1 << completionProposalKind);
146 		}
147 	}
148 
149 	/**
150 	 * Returns whether a proposal of a given kind with a required proposal
151 	 * of the given kind is allowed.
152 	 *
153 	 * @param proposalKind one of the kind constants declared
154 	 * @param requiredProposalKind one of the kind constants declared
155 	 * on <code>CompletionProposal</code>
156 	 * @return <code>true</code> if a proposal of a given kind with a required proposal
157 	 * of the given kind is allowed by this requestor, and <code>false</code>
158 	 * if it isn't of interest.
159 	 * <p>
160 	 * By default, all kinds of required proposals aren't allowed.
161 	 * </p>
162 	 * @see #setAllowsRequiredProposals(int, int, boolean)
163 	 * @see CompletionProposal#getKind()
164 	 * @see CompletionProposal#getRequiredProposals()
165 	 *
166 	 * @since 3.3
167 	 */
isAllowingRequiredProposals(int proposalKind, int requiredProposalKind)168 	public boolean isAllowingRequiredProposals(int proposalKind, int requiredProposalKind) {
169 		if (proposalKind < CompletionProposal.FIRST_KIND
170 			|| proposalKind > CompletionProposal.LAST_KIND) {
171 				throw new IllegalArgumentException("Unknown kind of completion proposal: "+requiredProposalKind); //$NON-NLS-1$
172 			}
173 
174 		if (requiredProposalKind < CompletionProposal.FIRST_KIND
175 			|| requiredProposalKind > CompletionProposal.LAST_KIND) {
176 				throw new IllegalArgumentException("Unknown required kind of completion proposal: "+requiredProposalKind); //$NON-NLS-1$
177 		}
178 		if (this.requiredProposalAllowSet == null) return false;
179 
180 		return 0 != (this.requiredProposalAllowSet[proposalKind] & (1 << requiredProposalKind));
181 	}
182 
183 	/**
184 	 * Sets whether a proposal of a given kind with a required proposal
185 	 * of the given kind is allowed.
186 	 *
187 	 * A required proposal of a given kind is proposed even if {@link #isIgnored(int)}
188 	 * return <code>true</code> for that kind.
189 	 *
190 	 * Currently only a subset of kinds support required proposals. To see what combinations
191 	 * are supported you must look at {@link CompletionProposal#getRequiredProposals()}
192 	 * documentation.
193 	 *
194 	 * @param proposalKind one of the kind constants declared
195 	 * @param requiredProposalKind one of the kind constants declared
196 	 * on <code>CompletionProposal</code>
197 	 * @param allow <code>true</code> if a proposal of a given kind with a required proposal
198 	 * of the given kind is allowed by this requestor, and <code>false</code>
199 	 * if it isn't of interest
200 	 * @see #isAllowingRequiredProposals(int, int)
201 	 * @see CompletionProposal#getKind()
202 	 * @see CompletionProposal#getRequiredProposals()
203 	 *
204 	 * @since 3.3
205 	 */
setAllowsRequiredProposals(int proposalKind, int requiredProposalKind, boolean allow)206 	public void setAllowsRequiredProposals(int proposalKind, int requiredProposalKind, boolean allow) {
207 		if (proposalKind < CompletionProposal.FIRST_KIND
208 			|| proposalKind > CompletionProposal.LAST_KIND) {
209 				throw new IllegalArgumentException("Unknown kind of completion proposal: "+requiredProposalKind); //$NON-NLS-1$
210 		}
211 		if (requiredProposalKind < CompletionProposal.FIRST_KIND
212 			|| requiredProposalKind > CompletionProposal.LAST_KIND) {
213 				throw new IllegalArgumentException("Unknown required kind of completion proposal: "+requiredProposalKind); //$NON-NLS-1$
214 		}
215 
216 		if (this.requiredProposalAllowSet == null) {
217 			this.requiredProposalAllowSet = new int[CompletionProposal.LAST_KIND + 1];
218 		}
219 
220 		if (allow) {
221 			this.requiredProposalAllowSet[proposalKind] |= (1 << requiredProposalKind);
222 		} else {
223 			this.requiredProposalAllowSet[proposalKind] &= ~(1 << requiredProposalKind);
224 		}
225 	}
226 
227 	/**
228 	 * Returns the favorite references which are used to compute some completion proposals.
229 	 * <p>
230 	 * A favorite reference is a qualified reference as it can be seen in an import statement.<br>
231 	 * e.g. <code>{"java.util.Arrays"}</code><br>
232 	 * It can be an on demand reference.<br>
233 	 * e.g. <code>{"java.util.Arrays.*"}</code>
234 	 * It can be a reference to a static method or field (as in a static import)<br>
235 	 * e.g. <code>{"java.util.Arrays.equals"}</code>
236 	 * </p>
237 	 * <p>
238 	 * Currently only on demand type references (<code>"java.util.Arrays.*"</code>),
239 	 * references to a static method or a static field are used to compute completion proposals.
240 	 * Other kind of reference could be used in the future.
241 	 * </p>
242 	 * @return favorite imports
243 	 *
244 	 * @since 3.3
245 	 */
getFavoriteReferences()246 	public String[] getFavoriteReferences() {
247 		return this.favoriteReferences;
248 	}
249 
250 	/**
251 	 * Set the favorite references which will be used to compute some completion proposals.
252 	 * A favorite reference is a qualified reference as it can be seen in an import statement.<br>
253 	 *
254 	 * @param favoriteImports
255 	 *
256 	 * @see #getFavoriteReferences()
257 	 *
258 	 * @since 3.3
259 	 */
setFavoriteReferences(String[] favoriteImports)260 	public void setFavoriteReferences(String[] favoriteImports) {
261 		this.favoriteReferences = favoriteImports;
262 	}
263 
264 	/**
265 	 * Pro forma notification sent before reporting a batch of
266 	 * completion proposals.
267 	 * <p>
268 	 * The default implementation of this method does nothing.
269 	 * Clients may override.
270 	 * </p>
271 	 */
beginReporting()272 	public void beginReporting() {
273 		// do nothing
274 	}
275 
276 	/**
277 	 * Pro forma notification sent after reporting a batch of
278 	 * completion proposals.
279 	 * <p>
280 	 * The default implementation of this method does nothing.
281 	 * Clients may override.
282 	 * </p>
283 	 */
endReporting()284 	public void endReporting() {
285 		// do nothing
286 	}
287 
288 	/**
289 	 * Notification of failure to produce any completions.
290 	 * The problem object explains what prevented completing.
291 	 * <p>
292 	 * The default implementation of this method does nothing.
293 	 * Clients may override to receive this kind of notice.
294 	 * </p>
295 	 *
296 	 * @param problem the problem object
297 	 */
completionFailure(IProblem problem)298 	public void completionFailure(IProblem problem) {
299 		// default behavior is to ignore
300 	}
301 
302 	/**
303 	 * Proposes a completion. Has no effect if the kind of proposal
304 	 * is being ignored by this requestor. Callers should consider
305 	 * checking {@link #isIgnored(int)} before avoid creating proposal
306 	 * objects that would only be ignored.
307 	 * <p>
308 	 * Similarly, implementers should check
309 	 * {@link #isIgnored(int) isIgnored(proposal.getKind())}
310 	 * and ignore proposals that have been declared as uninteresting.
311 	 * The proposal object passed is only valid for the duration of
312 	 * completion operation.
313 	 *
314 	 * @param proposal the completion proposal
315 	 * @exception IllegalArgumentException if the proposal is null
316 	 */
accept(CompletionProposal proposal)317 	public abstract void accept(CompletionProposal proposal);
318 
319 	/**
320 	 * Propose the context in which the completion occurs.
321 	 * <p>
322 	 * This method is called one and only one time before any call to
323 	 * {@link #accept(CompletionProposal)}.
324 	 * The default implementation of this method does nothing.
325 	 * Clients may override.
326 	 * </p>
327 	 * @param context the completion context
328 	 *
329 	 * @since 3.1
330 	 */
acceptContext(CompletionContext context)331 	public void acceptContext(CompletionContext context) {
332 		// do nothing
333 	}
334 
335 	/**
336 	 * Returns whether this requestor requires an extended context.
337 	 *
338 	 * By default this method return <code>false</code>.
339 	 *
340 	 * @return <code>true</code> if this requestor requires an extended context.
341 	 *
342 	 * @see CompletionContext#isExtended()
343 	 *
344 	 * @since 3.4
345 	 */
isExtendedContextRequired()346 	public boolean isExtendedContextRequired() {
347 		return this.requireExtendedContext;
348 	}
349 
350 
351 	/**
352 	 * Sets whether this requestor requires an extended context.
353 	 *
354 	 * @param require <code>true</code> if this requestor requires an extended context.
355 	 *
356 	 * @see CompletionContext#isExtended()
357 	 *
358 	 * @since 3.4
359 	 */
setRequireExtendedContext(boolean require)360 	public void setRequireExtendedContext(boolean require) {
361 		this.requireExtendedContext = require;
362 	}
363 
364 	/**
365 	 * If this returns true, exclude test sources and dependencies.
366 	 *
367 	 * @return <code>true</code> if this requestor does not want to get any completions from test code.
368 	 * @see IClasspathAttribute#TEST
369 	 * @since 3.14
370 	 */
isTestCodeExcluded()371 	public boolean isTestCodeExcluded() {
372 		return false;
373 	}
374 }
375