001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.ui.text.java;
011:
012: import java.util.ArrayList;
013: import java.util.List;
014:
015: import org.eclipse.text.edits.MalformedTreeException;
016: import org.eclipse.text.edits.TextEdit;
017:
018: import org.eclipse.core.runtime.Assert;
019: import org.eclipse.core.runtime.CoreException;
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.NullProgressMonitor;
022:
023: import org.eclipse.swt.graphics.Image;
024:
025: import org.eclipse.jface.window.Window;
026:
027: import org.eclipse.jface.text.BadLocationException;
028: import org.eclipse.jface.text.Document;
029: import org.eclipse.jface.text.IDocument;
030: import org.eclipse.jface.text.IRegion;
031: import org.eclipse.jface.text.TextUtilities;
032: import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
033: import org.eclipse.jface.text.contentassist.IContextInformation;
034:
035: import org.eclipse.jdt.core.CompletionProposal;
036: import org.eclipse.jdt.core.ICompilationUnit;
037: import org.eclipse.jdt.core.IJavaProject;
038: import org.eclipse.jdt.core.ISourceRange;
039: import org.eclipse.jdt.core.IType;
040: import org.eclipse.jdt.core.JavaModelException;
041: import org.eclipse.jdt.core.Signature;
042: import org.eclipse.jdt.core.dom.AST;
043: import org.eclipse.jdt.core.dom.ASTParser;
044: import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
045: import org.eclipse.jdt.core.dom.CompilationUnit;
046: import org.eclipse.jdt.core.dom.IBinding;
047: import org.eclipse.jdt.core.dom.IMethodBinding;
048: import org.eclipse.jdt.core.dom.ITypeBinding;
049: import org.eclipse.jdt.core.dom.MethodDeclaration;
050: import org.eclipse.jdt.core.dom.Modifier;
051: import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
052: import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
053: import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
054: import org.eclipse.jdt.core.formatter.CodeFormatter;
055:
056: import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
057: import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
058: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
059: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
060: import org.eclipse.jdt.internal.corext.template.java.SignatureUtil;
061: import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
062: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
063: import org.eclipse.jdt.internal.corext.util.Strings;
064:
065: import org.eclipse.jdt.internal.ui.JavaPlugin;
066: import org.eclipse.jdt.internal.ui.JavaPluginImages;
067: import org.eclipse.jdt.internal.ui.dialogs.OverrideMethodDialog;
068: import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
069:
070: public class AnonymousTypeCompletionProposal extends
071: JavaTypeCompletionProposal implements
072: ICompletionProposalExtension4 {
073:
074: private String fDeclarationSignature;
075: private IType fSuperType;
076:
077: private boolean fIsContextInformationComputed;
078: private int fContextInformationPosition;
079:
080: public AnonymousTypeCompletionProposal(IJavaProject jproject,
081: ICompilationUnit cu, int start, int length,
082: String constructorCompletion, String displayName,
083: String declarationSignature, int relevance) {
084: super (constructorCompletion, cu, start, length, null,
085: displayName, relevance);
086: Assert.isNotNull(declarationSignature);
087: Assert.isNotNull(jproject);
088: Assert.isNotNull(cu);
089:
090: fDeclarationSignature = declarationSignature;
091: fSuperType = getDeclaringType(jproject, SignatureUtil
092: .stripSignatureToFQN(String
093: .valueOf(declarationSignature)));
094:
095: setImage(getImageForType(fSuperType));
096: setCursorPosition(constructorCompletion.indexOf('(') + 1);
097: }
098:
099: private int createDummy(String name, StringBuffer buffer)
100: throws JavaModelException {
101: String lineDelim = "\n"; // Using newline is ok since source is used in dummy compilation unit //$NON-NLS-1$
102: buffer.append("class "); //$NON-NLS-1$
103: buffer.append(name);
104: if (fSuperType.isInterface())
105: buffer.append(" implements "); //$NON-NLS-1$
106: else
107: buffer.append(" extends "); //$NON-NLS-1$
108: if (fDeclarationSignature != null)
109: buffer.append(Signature.toString(fDeclarationSignature));
110: else
111: buffer.append(fSuperType
112: .getFullyQualifiedParameterizedName());
113: int start = buffer.length();
114: buffer.append("{"); //$NON-NLS-1$
115: buffer.append(lineDelim);
116: buffer.append(lineDelim);
117: buffer.append("}"); //$NON-NLS-1$
118: return start;
119: }
120:
121: private boolean createStubs(StringBuffer buffer,
122: ImportRewrite importRewrite) throws CoreException {
123: if (importRewrite == null)
124: return false;
125: if (fSuperType == null)
126: return true;
127: ICompilationUnit copy = null;
128: try {
129: final String name = "Type" + System.currentTimeMillis(); //$NON-NLS-1$
130: copy = fCompilationUnit.getPrimary().getWorkingCopy(null);
131: final StringBuffer contents = new StringBuffer();
132: int start = 0;
133: int end = 0;
134: ISourceRange range = fSuperType.getSourceRange();
135: final boolean sameUnit = range != null
136: && fCompilationUnit.equals(fSuperType
137: .getCompilationUnit());
138: final StringBuffer dummy = new StringBuffer();
139: final int length = createDummy(name, dummy);
140: contents.append(fCompilationUnit.getBuffer().getContents());
141: if (sameUnit) {
142: final int size = range.getOffset() + range.getLength();
143: start = size + length;
144: end = contents.length() - size;
145: contents.insert(size, dummy.toString());
146: } else {
147: range = fCompilationUnit.getTypes()[0].getSourceRange();
148: start = range.getOffset() + length;
149: end = contents.length() - range.getOffset();
150: contents.insert(range.getOffset(), dummy.toString());
151: }
152: copy.getBuffer().setContents(contents.toString());
153: JavaModelUtil.reconcile(copy);
154: final ASTParser parser = ASTParser.newParser(AST.JLS3);
155: parser.setResolveBindings(true);
156: parser.setSource(copy);
157: final CompilationUnit unit = (CompilationUnit) parser
158: .createAST(new NullProgressMonitor());
159: IType type = null;
160: IType[] types = copy.getAllTypes();
161: for (int index = 0; index < types.length; index++) {
162: IType result = types[index];
163: if (result.getElementName().equals(name)) {
164: type = result;
165: break;
166: }
167: }
168: if (type != null && type.exists()) {
169: ITypeBinding binding = null;
170: final AbstractTypeDeclaration declaration = (AbstractTypeDeclaration) ASTNodes
171: .getParent(NodeFinder.perform(unit, type
172: .getNameRange()),
173: AbstractTypeDeclaration.class);
174: if (declaration != null) {
175: binding = declaration.resolveBinding();
176: if (binding != null) {
177: IMethodBinding[] bindings = StubUtility2
178: .getOverridableMethods(unit.getAST(),
179: binding, true);
180: CodeGenerationSettings settings = JavaPreferencesSettings
181: .getCodeGenerationSettings(fSuperType
182: .getJavaProject());
183: String[] keys = null;
184: if (!fSuperType.isInterface()
185: && !fSuperType.isAnnotation()) {
186: OverrideMethodDialog dialog = new OverrideMethodDialog(
187: JavaPlugin
188: .getActiveWorkbenchShell(),
189: null, type, true);
190: dialog.setGenerateComment(false);
191: dialog.setElementPositionEnabled(false);
192: if (dialog.open() == Window.OK) {
193: Object[] selection = dialog.getResult();
194: if (selection != null) {
195: ArrayList result = new ArrayList(
196: selection.length);
197: for (int index = 0; index < selection.length; index++) {
198: if (selection[index] instanceof IMethodBinding)
199: result
200: .add(((IBinding) selection[index])
201: .getKey());
202: }
203: keys = (String[]) result
204: .toArray(new String[result
205: .size()]);
206: settings.createComments = dialog
207: .getGenerateComment();
208: }
209: }
210: } else {
211: settings.createComments = false;
212: List list = new ArrayList();
213: for (int index = 0; index < bindings.length; index++) {
214: if (Modifier.isAbstract(bindings[index]
215: .getModifiers()))
216: list.add(bindings[index].getKey());
217: }
218: keys = (String[]) list
219: .toArray(new String[list.size()]);
220: }
221: if (keys == null) {
222: setReplacementString(""); //$NON-NLS-1$
223: setReplacementLength(0);
224: return false;
225: }
226: ASTRewrite rewrite = ASTRewrite.create(unit
227: .getAST());
228: ListRewrite rewriter = rewrite.getListRewrite(
229: declaration, declaration
230: .getBodyDeclarationsProperty());
231: String key = null;
232: for (int index = 0; index < keys.length; index++) {
233: key = keys[index];
234: for (int offset = 0; offset < bindings.length; offset++) {
235: if (key.equals(bindings[offset]
236: .getKey())) {
237: MethodDeclaration stub = StubUtility2
238: .createImplementationStub(
239: copy,
240: rewrite,
241: importRewrite,
242: null,
243: bindings[offset],
244: binding.getName(),
245: settings,
246: binding
247: .isInterface());
248: rewriter.insertFirst(stub, null);
249: break;
250: }
251: }
252: }
253: IDocument document = new Document(copy
254: .getBuffer().getContents());
255: try {
256: rewrite.rewriteAST(
257: document,
258: fCompilationUnit.getJavaProject()
259: .getOptions(true)).apply(
260: document, TextEdit.UPDATE_REGIONS);
261: buffer.append(document.get(start, document
262: .getLength()
263: - start - end));
264: } catch (MalformedTreeException exception) {
265: JavaPlugin.log(exception);
266: } catch (BadLocationException exception) {
267: JavaPlugin.log(exception);
268: }
269: }
270: }
271: }
272: return true;
273: } finally {
274: if (copy != null)
275: copy.discardWorkingCopy();
276: }
277: }
278:
279: private IType getDeclaringType(IJavaProject project, String typeName) {
280: try {
281: return project.findType(typeName, (IProgressMonitor) null);
282: } catch (JavaModelException e) {
283: JavaPlugin.log(e);
284: }
285: return null;
286: }
287:
288: private Image getImageForType(IType type) {
289: String imageName = JavaPluginImages.IMG_OBJS_CLASS; // default
290: if (type != null) {
291: try {
292: if (type.isAnnotation()) {
293: imageName = JavaPluginImages.IMG_OBJS_ANNOTATION;
294: } else if (type.isInterface()) {
295: imageName = JavaPluginImages.IMG_OBJS_INTERFACE;
296: }
297: } catch (JavaModelException e) {
298: JavaPlugin.log(e);
299: }
300: }
301: return JavaPluginImages.get(imageName);
302: }
303:
304: /*
305: * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension4#isAutoInsertable()
306: */
307: public boolean isAutoInsertable() {
308: return false;
309: }
310:
311: protected boolean updateReplacementString(IDocument document,
312: char trigger, int offset, ImportRewrite impRewrite)
313: throws CoreException, BadLocationException {
314: String replacementString = getReplacementString();
315:
316: // construct replacement text: an expression to be formatted
317: StringBuffer buf = new StringBuffer("new A("); //$NON-NLS-1$
318: buf.append(replacementString);
319:
320: if (!replacementString.endsWith(")")) { //$NON-NLS-1$
321: buf.append(')');
322: }
323:
324: if (!createStubs(buf, impRewrite)) {
325: return false;
326: }
327: if (document.getChar(offset) != ')')
328: buf.append(';');
329:
330: // use the code formatter
331: String lineDelim = TextUtilities
332: .getDefaultLineDelimiter(document);
333: final IJavaProject project = fCompilationUnit.getJavaProject();
334: IRegion region = document
335: .getLineInformationOfOffset(getReplacementOffset());
336: int indent = Strings.computeIndentUnits(document.get(region
337: .getOffset(), region.getLength()), project);
338:
339: String replacement = CodeFormatterUtil.format(
340: CodeFormatter.K_EXPRESSION, buf.toString(), 0,
341: lineDelim, project);
342: replacement = Strings.changeIndent(replacement, 0, project,
343: CodeFormatterUtil.createIndentString(indent, project),
344: lineDelim);
345: setReplacementString(replacement.substring(replacement
346: .indexOf('(') + 1));
347:
348: int pos = offset;
349: while (pos < document.getLength()
350: && Character.isWhitespace(document.getChar(pos))) {
351: pos++;
352: }
353:
354: if (pos < document.getLength() && document.getChar(pos) == ')') {
355: setReplacementLength(pos - offset + 1);
356: }
357: return true;
358: }
359:
360: /*
361: * @see ICompletionProposalExtension#getContextInformationPosition()
362: * @since 3.4
363: */
364: public int getContextInformationPosition() {
365: if (!fIsContextInformationComputed)
366: setContextInformation(computeContextInformation());
367: return fContextInformationPosition;
368: }
369:
370: /*
371: * @see ICompletionProposal#getContextInformation()
372: * @since 3.4
373: */
374: public final IContextInformation getContextInformation() {
375: if (!fIsContextInformationComputed)
376: setContextInformation(computeContextInformation());
377: return super .getContextInformation();
378: }
379:
380: protected IContextInformation computeContextInformation() {
381: try {
382: ProposalInfo proposalInfo = getProposalInfo();
383: fContextInformationPosition = getReplacementOffset() - 1;
384: if (!(proposalInfo instanceof MemberProposalInfo))
385: return null;
386:
387: CompletionProposal proposal = ((MemberProposalInfo) proposalInfo).fProposal;
388: // no context information for METHOD_NAME_REF proposals (e.g. for static imports)
389: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=94654
390: if (hasParameters()
391: && (getReplacementString().endsWith(")") || getReplacementString().length() == 0)) { //$NON-NLS-1$
392: ProposalContextInformation contextInformation = new ProposalContextInformation(
393: proposal);
394: fContextInformationPosition = getReplacementOffset()
395: + getCursorPosition();
396: if (fContextInformationPosition != 0
397: && proposal.getCompletion().length == 0)
398: contextInformation
399: .setContextInformationPosition(fContextInformationPosition);
400: return contextInformation;
401: }
402: return null;
403: } finally {
404: fIsContextInformationComputed = true;
405: }
406: }
407:
408: /**
409: * Returns <code>true</code> if the method being inserted has at least one parameter. Note
410: * that this does not say anything about whether the argument list should be inserted.
411: *
412: * @return <code>true</code> if the method has any parameters, <code>false</code> if it has no parameters
413: * @since 3.4
414: */
415: private boolean hasParameters() {
416: ProposalInfo proposalInfo = getProposalInfo();
417: if (!(proposalInfo instanceof MemberProposalInfo))
418: return false;
419:
420: CompletionProposal proposal = ((MemberProposalInfo) proposalInfo).fProposal;
421: return Signature.getParameterCount(proposal.getSignature()) > 0;
422: }
423:
424: }
|