blob: a2e75b6784fc44393ba9b91aebf542090975dbbb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 Mateusz Matela 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:
* Mateusz Matela <mateusz.matela@gmail.com> - [code manipulation] [dcr] toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=26070
* Mateusz Matela <mateusz.matela@gmail.com> - [toString] finish toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267710
*******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationMessages;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
/**
* <p>
* A workspace runnable to add implementation for <code>{@link java.lang.Object#toString()}</code>
* </p>
*
* @since 3.5
*/
public class GenerateToStringOperation implements IWorkspaceRunnable {
/** The insertion point, or <code>null</code> */
private IJavaElement fInsert;
private CompilationUnitRewrite fRewrite;
private ToStringGenerationContext fContext;
private AbstractToStringGenerator fGenerator;
private CompilationUnit fUnit;
private GenerateToStringOperation(IJavaElement insert, ToStringGenerationContext context, AbstractToStringGenerator generator, CompilationUnit unit, CompilationUnitRewrite rewrite) {
fInsert= insert;
fContext= context;
fRewrite= rewrite;
fUnit= unit;
fGenerator= generator;
}
@Override
public void run(IProgressMonitor monitor) throws CoreException {
if (monitor == null)
monitor= new NullProgressMonitor();
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(CodeGenerationMessages.GenerateToStringOperation_description);
AbstractTypeDeclaration declaration= (AbstractTypeDeclaration)ASTNodes.findDeclaration(fContext.getTypeBinding(), fRewrite.getRoot());
ListRewrite rewriter= fRewrite.getASTRewrite().getListRewrite(declaration, declaration.getBodyDeclarationsProperty());
if (fContext.getTypeBinding() != null && rewriter != null) {
MethodDeclaration toStringMethod= fGenerator.generateToStringMethod();
List<BodyDeclaration> list= declaration.bodyDeclarations();
BodyDeclaration replace= findMethodToReplace(list, toStringMethod);
if (replace == null || ((Boolean)toStringMethod.getProperty(AbstractToStringGenerator.OVERWRITE_METHOD_PROPERTY)).booleanValue())
insertMethod(toStringMethod, rewriter, replace);
List<MethodDeclaration> helperMethods= fGenerator.generateHelperMethods();
for (Iterator<MethodDeclaration> iterator= helperMethods.iterator(); iterator.hasNext();) {
MethodDeclaration method= iterator.next();
replace= findMethodToReplace(list, method);
if (replace == null || ((Boolean)method.getProperty(AbstractToStringGenerator.OVERWRITE_METHOD_PROPERTY)).booleanValue()) {
insertMethod(method, rewriter, replace);
}
}
JavaElementUtil.applyEdit((ICompilationUnit)fUnit.getJavaElement(), fRewrite.createChange(true).getEdit(), false, monitor);
}
} finally {
monitor.done();
}
}
/**
* @return RefactoringStatus with eventual errors and warnings
*/
public RefactoringStatus checkConditions() {
return fGenerator.checkConditions();
}
protected void insertMethod(MethodDeclaration method, ListRewrite rewriter, BodyDeclaration replace) throws JavaModelException {
if (replace != null) {
rewriter.replace(replace, method, null);
} else {
ASTNode insertion= StubUtility2.getNodeToInsertBefore(rewriter, fInsert);
if (insertion != null)
rewriter.insertBefore(method, insertion, null);
else
rewriter.insertLast(method, null);
}
}
/**
* Determines if given method exists in a given list
*
* @param list list of method to search through
* @param method method to find
* @return declaration of method from the list that has the same name and parameter types, or
* null if not found
*/
protected BodyDeclaration findMethodToReplace(final List<BodyDeclaration> list, MethodDeclaration method) {
for (final Iterator<BodyDeclaration> iterator= list.iterator(); iterator.hasNext();) {
final BodyDeclaration bodyDecl= iterator.next();
if (bodyDecl instanceof MethodDeclaration) {
final MethodDeclaration method2= (MethodDeclaration)bodyDecl;
if (method2.getName().getIdentifier().equals(method.getName().getIdentifier()) && method2.parameters().size() == method.parameters().size()) {
Iterator<SingleVariableDeclaration> iterator1= method.parameters().iterator();
Iterator<SingleVariableDeclaration> iterator2= method2.parameters().iterator();
boolean ok= true;
while (iterator1.hasNext()) {
if (!iterator1.next().getType().subtreeMatch(new ASTMatcher(), iterator2.next().getType())) {
ok= false;
break;
}
}
if (ok)
return method2;
}
}
}
return null;
}
public ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot();
}
public static final int STRING_CONCATENATION= 0;
public static final int STRING_BUILDER= 1;
public static final int STRING_BUILDER_CHAINED= 2;
public static final int STRING_FORMAT= 3;
public static final int CUSTOM_BUILDER= 4;
private final static String[] hardcoded_styles= {
CodeGenerationMessages.GenerateToStringOperation_stringConcatenation_style_name,
CodeGenerationMessages.GenerateToStringOperation_stringBuilder_style_name,
CodeGenerationMessages.GenerateToStringOperation_StringBuilder_chained_style_name,
CodeGenerationMessages.GenerateToStringOperation_string_format_style_name,
CodeGenerationMessages.GenerateToStringOperation_customStringBuilder_style_name
};
/**
* @return Array containing names of implemented code styles
*/
public static String[] getStyleNames() {
return hardcoded_styles;
}
/**
*
* @param toStringStyle id number of the code style (its position in the array returned by
* {@link #getStyleNames()}
* @return a toString() generator implementing given code style
*/
private static AbstractToStringGenerator createToStringGenerator(int toStringStyle) {
switch (toStringStyle) {
case STRING_CONCATENATION:
return new StringConcatenationGenerator();
case STRING_BUILDER:
return new StringBuilderGenerator();
case STRING_BUILDER_CHAINED:
return new StringBuilderChainGenerator();
case STRING_FORMAT:
return new StringFormatGenerator();
case CUSTOM_BUILDER:
return new CustomBuilderGenerator();
default:
throw new IllegalArgumentException("Undefined toString() code style: " + toStringStyle); //$NON-NLS-1$
}
}
/**
* @param toStringStyle id number of the style (its position in the array returned by
* {@link #getStyleNames()}
* @return a template parser that should be used with given code style
*/
public static ToStringTemplateParser createTemplateParser(int toStringStyle) {
return new ToStringTemplateParser();
}
/**
* Creates new <code>GenerateToStringOperation</code>, using <code>settings.toStringStyle</code>
* field to choose the right subclass.
*
* @param typeBinding binding for the type for which the toString() method will be created
* @param selectedBindings bindings for the typetype's members to be used in created method
* @param unit a compilation unit containing the type
* @param elementPosition at this position in the compilation unit created method will be added
* @param settings the settings for toString() generator
* @return a ready to use <code>GenerateToStringOperation</code> object
*/
public static GenerateToStringOperation createOperation(ITypeBinding typeBinding, Object[] selectedBindings, CompilationUnit unit, IJavaElement elementPosition,
ToStringGenerationSettings settings) {
AbstractToStringGenerator generator= createToStringGenerator(settings.toStringStyle);
ToStringTemplateParser parser= createTemplateParser(settings.toStringStyle);
parser.parseTemplate(settings.stringFormatTemplate);
CompilationUnitRewrite rewrite= new CompilationUnitRewrite((ICompilationUnit)unit.getTypeRoot(), unit);
ToStringGenerationContext context= new ToStringGenerationContext(parser, selectedBindings, settings, typeBinding, rewrite);
generator.setContext(context);
return new GenerateToStringOperation(elementPosition, context, generator, unit, rewrite);
}
}