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