content: /*******************************************************************************
   * 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;
  	}
  }
  