/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.ui.text.java;
import java.util.ArrayList;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.graphics.Image;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.ISourceRange;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.Signature;
import org.eclipse.wst.jsdt.core.dom.AST;
import org.eclipse.wst.jsdt.core.dom.ASTParser;
import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
import org.eclipse.wst.jsdt.core.dom.IBinding;
import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite;
import org.eclipse.wst.jsdt.core.formatter.CodeFormatter;
import org.eclipse.wst.jsdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility2;
import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
import org.eclipse.wst.jsdt.internal.corext.template.java.SignatureUtil;
import org.eclipse.wst.jsdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
import org.eclipse.wst.jsdt.internal.corext.util.Strings;
import org.eclipse.wst.jsdt.internal.ui.JavaPluginImages;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.dialogs.OverrideMethodDialog;
import org.eclipse.wst.jsdt.internal.ui.preferences.JavaPreferencesSettings;
public class AnonymousTypeCompletionProposal extends JavaTypeCompletionProposal implements ICompletionProposalExtension4 {
private String fDeclarationSignature;
private IType fSuperType;
public AnonymousTypeCompletionProposal(IJavaScriptProject jproject, IJavaScriptUnit cu, int start, int length, String constructorCompletion, String displayName, String declarationSignature, int relevance) {
super(constructorCompletion, cu, start, length, null, displayName, relevance);
Assert.isNotNull(declarationSignature);
Assert.isNotNull(jproject);
Assert.isNotNull(cu);
fDeclarationSignature= declarationSignature;
fSuperType= getDeclaringType(jproject, SignatureUtil.stripSignatureToFQN(String.valueOf(declarationSignature)));
setImage(getImageForType(fSuperType));
setCursorPosition(constructorCompletion.indexOf('(') + 1);
}
private int createDummy(String name, StringBuffer buffer) throws JavaScriptModelException {
String lineDelim= "\n"; // Using newline is ok since source is used in dummy compilation unit //$NON-NLS-1$
buffer.append("class "); //$NON-NLS-1$
buffer.append(name);
buffer.append(" extends "); //$NON-NLS-1$
if (fDeclarationSignature != null)
buffer.append(Signature.toString(fDeclarationSignature));
else
buffer.append(fSuperType.getFullyQualifiedParameterizedName());
int start= buffer.length();
buffer.append("{"); //$NON-NLS-1$
buffer.append(lineDelim);
buffer.append(lineDelim);
buffer.append("}"); //$NON-NLS-1$
return start;
}
private boolean createStubs(StringBuffer buffer, ImportRewrite importRewrite) throws CoreException {
if (importRewrite == null)
return false;
if (fSuperType == null)
return true;
IJavaScriptUnit copy= null;
try {
final String name= "Type" + System.currentTimeMillis(); //$NON-NLS-1$
copy= fCompilationUnit.getPrimary().getWorkingCopy(null);
final StringBuffer contents= new StringBuffer();
int start= 0;
int end= 0;
ISourceRange range= fSuperType.getSourceRange();
final boolean sameUnit= range != null && fCompilationUnit.equals(fSuperType.getJavaScriptUnit());
final StringBuffer dummy= new StringBuffer();
final int length= createDummy(name, dummy);
contents.append(fCompilationUnit.getBuffer().getContents());
if (sameUnit) {
final int size= range.getOffset() + range.getLength();
start= size + length;
end= contents.length() - size;
contents.insert(size, dummy.toString());
} else {
range= fCompilationUnit.getTypes()[0].getSourceRange();
start= range.getOffset() + length;
end= contents.length() - range.getOffset();
contents.insert(range.getOffset(), dummy.toString());
}
copy.getBuffer().setContents(contents.toString());
JavaModelUtil.reconcile(copy);
final ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setSource(copy);
final JavaScriptUnit unit= (JavaScriptUnit) parser.createAST(new NullProgressMonitor());
IType type= null;
IType[] types= copy.getAllTypes();
for (int index= 0; index < types.length; index++) {
IType result= types[index];
if (result.getElementName().equals(name)) {
type= result;
break;
}
}
if (type != null && type.exists()) {
ITypeBinding binding= null;
final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) ASTNodes.getParent(NodeFinder.perform(unit, type.getNameRange()), AbstractTypeDeclaration.class);
if (declaration != null) {
binding= declaration.resolveBinding();
if (binding != null) {
IFunctionBinding[] bindings= StubUtility2.getOverridableMethods(unit.getAST(), binding, true);
CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(fSuperType.getJavaScriptProject());
String[] keys= null;
OverrideMethodDialog dialog= new OverrideMethodDialog(JavaScriptPlugin.getActiveWorkbenchShell(), null, type, true);
dialog.setGenerateComment(false);
dialog.setElementPositionEnabled(false);
if (dialog.open() == Window.OK) {
Object[] selection= dialog.getResult();
if (selection != null) {
ArrayList result= new ArrayList(selection.length);
for (int index= 0; index < selection.length; index++) {
if (selection[index] instanceof IFunctionBinding)
result.add(((IBinding) selection[index]).getKey());
}
keys= (String[]) result.toArray(new String[result.size()]);
settings.createComments= dialog.getGenerateComment();
}
}
if (keys == null) {
setReplacementString(""); //$NON-NLS-1$
setReplacementLength(0);
return false;
}
ASTRewrite rewrite= ASTRewrite.create(unit.getAST());
ListRewrite rewriter= rewrite.getListRewrite(declaration, declaration.getBodyDeclarationsProperty());
String key= null;
FunctionDeclaration stub= null;
for (int index= 0; index < keys.length; index++) {
key= keys[index];
for (int offset= 0; offset < bindings.length; offset++) {
if (key.equals(bindings[offset].getKey())) {
stub= StubUtility2.createImplementationStub(copy, rewrite, importRewrite, bindings[offset], binding.getName(), false, settings);
if (stub != null)
rewriter.insertFirst(stub, null);
break;
}
}
}
IDocument document= new Document(copy.getBuffer().getContents());
try {
rewrite.rewriteAST(document, fCompilationUnit.getJavaScriptProject().getOptions(true)).apply(document, TextEdit.UPDATE_REGIONS);
buffer.append(document.get(start, document.getLength() - start - end));
} catch (MalformedTreeException exception) {
JavaScriptPlugin.log(exception);
} catch (BadLocationException exception) {
JavaScriptPlugin.log(exception);
}
}
}
}
return true;
} finally {
if (copy != null)
copy.discardWorkingCopy();
}
}
private IType getDeclaringType(IJavaScriptProject project, String typeName) {
try {
return project.findType(typeName, (IProgressMonitor) null);
} catch (JavaScriptModelException e) {
JavaScriptPlugin.log(e);
}
return null;
}
private Image getImageForType(IType type) {
String imageName= JavaPluginImages.IMG_OBJS_CLASS; // default
return JavaPluginImages.get(imageName);
}
/*
* @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension4#isAutoInsertable()
*/
public boolean isAutoInsertable() {
return false;
}
protected boolean updateReplacementString(IDocument document, char trigger, int offset, ImportRewrite impRewrite) throws CoreException, BadLocationException {
String replacementString= getReplacementString();
// construct replacement text: an expression to be formatted
StringBuffer buf= new StringBuffer("new A("); //$NON-NLS-1$
buf.append(replacementString);
if (!replacementString.endsWith(")")) { //$NON-NLS-1$
buf.append(')');
}
if (!createStubs(buf, impRewrite)) {
return false;
}
if (document.getChar(offset) != ')')
buf.append(';');
// use the code formatter
String lineDelim= TextUtilities.getDefaultLineDelimiter(document);
final IJavaScriptProject project= fCompilationUnit.getJavaScriptProject();
IRegion region= document.getLineInformationOfOffset(getReplacementOffset());
int indent= Strings.computeIndentUnits(document.get(region.getOffset(), region.getLength()), project);
String replacement= CodeFormatterUtil.format(CodeFormatter.K_EXPRESSION, buf.toString(), 0, null, lineDelim, project);
replacement= Strings.changeIndent(replacement, 0, project, CodeFormatterUtil.createIndentString(indent, project), lineDelim);
setReplacementString(replacement.substring(replacement.indexOf('(') + 1));
int pos= offset;
while (pos < document.getLength() && Character.isWhitespace(document.getChar(pos))) {
pos++;
}
if (pos < document.getLength() && document.getChar(pos) == ')') {
setReplacementLength(pos - offset + 1);
}
return true;
}
}