/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. 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:
* Rogue Wave Software Inc. - initial implementation
*******************************************************************************/
package org.eclipse.php.internal.ui.actions;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.php.core.ast.nodes.*;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.ast.rewrite.ASTRewrite;
import org.eclipse.php.internal.core.ast.rewrite.ListRewrite;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.ui.util.CodeGenerationUtils;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
/**
* Workspace runnable to add unimplemented methods.
*
* @since 3.1
*/
public final class AddUnimplementedMethodsOperation implements IWorkspaceRunnable {
/** Should the resulting edit be applied? */
private final boolean fApply;
/** The method binding keys for which a method was generated */
private final List<String> fCreatedMethods = new ArrayList<>();
/** The insertion point, or <code>-1</code> */
private final int fInsertPos;
/** The method bindings to implement */
private final IMethodBinding[] fMethodsToImplement;
/** Specified if comments should be created */
private boolean fDoCreateComments;
/** The type declaration to add the methods to */
private final ITypeBinding fType;
/** The compilation unit AST node */
private final Program fASTRoot;
private IDocument fDocument;
private IType fElement;
/**
* Creates a new add unimplemented methods operation.
*
* @param astRoot
* the compilation unit AST node
* @param type
* the type to add the methods to
* @param methodsToImplement
* the method bindings to implement or <code>null</code> to
* implement all unimplemented methods
* @param insertPos
* the insertion point, or <code>-1</code>
* @param imports
* <code>true</code> if the import edits should be applied,
* <code>false</code> otherwise
* @param apply
* <code>true</code> if the resulting edit should be applied,
* <code>false</code> otherwise
* @param save
* <code>true</code> if the changed compilation unit should be
* saved, <code>false</code> otherwise
* @param doc
*/
public AddUnimplementedMethodsOperation(Program astRoot, IType element, ITypeBinding type,
IMethodBinding[] methodsToImplement, int insertPos, final boolean apply, IDocument doc) {
if (astRoot == null) {
throw new IllegalArgumentException("AST must not be null and has to be created from a ICompilationUnit"); //$NON-NLS-1$
}
if (type == null) {
throw new IllegalArgumentException("The type must not be null"); //$NON-NLS-1$
}
fDocument = doc;
fType = type;
fInsertPos = insertPos;
fASTRoot = astRoot;
fMethodsToImplement = methodsToImplement;
fApply = apply;
fElement = element;
}
@Override
public final void run(IProgressMonitor monitor) throws CoreException {
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask("", 2); //$NON-NLS-1$
monitor.setTaskName("AddUnimplementedMethodsOperation_description"); //$NON-NLS-1$
fCreatedMethods.clear();
Program cu = fASTRoot.getProgramRoot();
AST ast = fASTRoot.getAST();
ASTRewrite astRewrite = ASTRewrite.create(ast);
ITypeBinding currTypeBinding = fType;
ListRewrite memberRewriter = null;
try {
ASTNode node = null;
if (PHPFlags.isAnonymous(fElement.getFlags())) {
node = fASTRoot.getElementAt(fElement.getSourceRange().getOffset());
while (!(node instanceof Program)) {
node = fASTRoot.getElementAt(node.getEnd());
if (node instanceof ClassInstanceCreation) {
if (((ClassInstanceCreation) node).getAnonymousClassDeclaration() != null) {
node = ((ClassInstanceCreation) node).getAnonymousClassDeclaration();
break;
}
}
}
} else {
node = fASTRoot.getElementAt(fElement.getNameRange().getOffset()).getParent();
}
if (node instanceof ClassDeclaration) {
memberRewriter = astRewrite.getListRewrite(((ClassDeclaration) node).getBody(),
Block.STATEMENTS_PROPERTY);
} else if (node instanceof AnonymousClassDeclaration) {
memberRewriter = astRewrite.getListRewrite(((AnonymousClassDeclaration) node).getBody(),
Block.STATEMENTS_PROPERTY);
} else {
throw new IllegalArgumentException();
// not possible, we checked this in the constructor
}
} catch (ModelException e) {
throw e;
}
final CodeGenerationSettings settings = new CodeGenerationSettings();
settings.createComments = fDoCreateComments;
ASTNode insertion = null;
if (fInsertPos != -1) {
insertion = CodeGenerationUtils.getNodeToInsertBefore(memberRewriter, fInsertPos);
}
IMethodBinding[] methodsToImplement = fMethodsToImplement;
if (methodsToImplement == null) {
methodsToImplement = CodeGenerationUtils.getUnimplementedMethods(currTypeBinding);
}
for (IMethodBinding curr : methodsToImplement) {
MethodDeclaration stub = CodeGenerationUtils.createImplementationStub(cu, astRewrite, curr,
currTypeBinding.getName(), settings, currTypeBinding.isInterface());
if (stub != null) {
fCreatedMethods.add(curr.getKey());
if (insertion != null) {
memberRewriter.insertBefore(stub, insertion, null);
} else {
memberRewriter.insertLast(stub, null);
}
}
}
TextEdit fEdit = astRewrite.rewriteAST(fDocument, null);
if (fApply) {
try {
fEdit.apply(fDocument);
} catch (MalformedTreeException | BadLocationException e) {
throw new CoreException(
new Status(IStatus.ERROR, PHPUiPlugin.ID, IStatus.ERROR, "error file content", null)); //$NON-NLS-1$
}
}
} finally {
monitor.done();
}
}
public void setCreateComments(boolean createComments) {
fDoCreateComments = createComments;
}
/**
* Returns the method binding keys for which a method has been generated.
*
* @return the method binding keys
*/
public final String[] getCreatedMethods() {
final String[] keys = new String[fCreatedMethods.size()];
fCreatedMethods.toArray(keys);
return keys;
}
/**
* Returns the scheduling rule for this operation.
*
* @return the scheduling rule
*/
public final ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
}