1 /******************************************************************************* 2 * Copyright (c) 2008, 2019 Mateusz Matela and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Mateusz Matela <mateusz.matela@gmail.com> - [code manipulation] [dcr] toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=26070 13 * Red Hat Inc. - moved to jdt.core.manipulation 14 *******************************************************************************/ 15 package org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration; 16 17 import org.eclipse.core.runtime.CoreException; 18 19 import org.eclipse.jdt.core.NamingConventions; 20 import org.eclipse.jdt.core.dom.ASTNode; 21 import org.eclipse.jdt.core.dom.Block; 22 import org.eclipse.jdt.core.dom.ClassInstanceCreation; 23 import org.eclipse.jdt.core.dom.Expression; 24 import org.eclipse.jdt.core.dom.IfStatement; 25 import org.eclipse.jdt.core.dom.Name; 26 import org.eclipse.jdt.core.dom.ReturnStatement; 27 import org.eclipse.jdt.core.dom.Statement; 28 import org.eclipse.jdt.core.dom.StringLiteral; 29 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 30 import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 31 import org.eclipse.jdt.core.dom.InfixExpression.Operator; 32 33 34 /** 35 * <p> 36 * Implementation of <code>AbstractToStringGenerator</code> that creates <code>toString()</code> 37 * method using <code>StringBuilder</code> (or <code>StringBuffer</code> for old versions of JDK) 38 * </p> 39 * <p> 40 * Generated methods look like this: 41 * 42 * <pre> 43 * public String toString() { 44 * StringBuilder builder= new StringBuilder(); 45 * builder.append("FooClass( field1="); 46 * builder.append(field1); 47 * builder.append(", field2="); 48 * builder.append(field2); 49 * builder.append(" )"); 50 * return builder.toString(); 51 * } 52 * </pre> 53 * 54 * </p> 55 * 56 * @since 3.5 57 */ 58 public class StringBuilderGenerator extends AbstractToStringGenerator { 59 protected StringBuffer fBuffer; 60 61 protected String fBuilderVariableName; 62 63 protected final String APPEND_METHOD_NAME= "append"; //$NON-NLS-1$ 64 flushBuffer(Block target)65 protected void flushBuffer(Block target) { 66 if (fBuffer.length() > 0) { 67 StringLiteral literal= fAst.newStringLiteral(); 68 literal.setLiteralValue(fBuffer.toString()); 69 if (target == null) 70 target= toStringMethod.getBody(); 71 target.statements().add(fAst.newExpressionStatement(createMethodInvocation(fBuilderVariableName, APPEND_METHOD_NAME, literal))); 72 fBuffer.setLength(0); 73 } 74 } 75 76 @Override initialize()77 protected void initialize() { 78 super.initialize(); 79 fBuilderVariableName= createNameSuggestion(getContext().is50orHigher() ? "builder" : "buffer", NamingConventions.VK_LOCAL); //$NON-NLS-1$ //$NON-NLS-2$ 80 fBuffer= new StringBuffer(); 81 VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); 82 fragment.setName(fAst.newSimpleName(fBuilderVariableName)); 83 ClassInstanceCreation classInstance= fAst.newClassInstanceCreation(); 84 Name typeName= addImport(getContext().is50orHigher() ? "java.lang.StringBuilder" : "java.lang.StringBuffer"); //$NON-NLS-1$ //$NON-NLS-2$ 85 classInstance.setType(fAst.newSimpleType(typeName)); 86 fragment.setInitializer(classInstance); 87 VariableDeclarationStatement vStatement= fAst.newVariableDeclarationStatement(fragment); 88 vStatement.setType(fAst.newSimpleType((Name)ASTNode.copySubtree(fAst, typeName))); 89 toStringMethod.getBody().statements().add(vStatement); 90 } 91 92 @Override complete()93 protected void complete() throws CoreException { 94 flushBuffer(null); 95 super.complete(); 96 ReturnStatement rStatement= fAst.newReturnStatement(); 97 rStatement.setExpression(createMethodInvocation(fBuilderVariableName, "toString", null)); //$NON-NLS-1$ 98 toStringMethod.getBody().statements().add(rStatement); 99 } 100 addElement(Object element, Block block)101 protected void addElement(Object element, Block block) { 102 if (element instanceof String) 103 fBuffer.append((String)element); 104 if (element instanceof Expression) { 105 flushBuffer(block); 106 block.statements().add(fAst.newExpressionStatement(createMethodInvocation(fBuilderVariableName, APPEND_METHOD_NAME, (Expression)element))); 107 } 108 } 109 110 @Override addMemberCheckNull(Object member, boolean addSeparator)111 protected void addMemberCheckNull(Object member, boolean addSeparator) { 112 IfStatement ifStatement= fAst.newIfStatement(); 113 ifStatement.setExpression(createInfixExpression(createMemberAccessExpression(member, true, true), Operator.NOT_EQUALS, fAst.newNullLiteral())); 114 Block thenBlock= fAst.newBlock(); 115 flushBuffer(null); 116 for (String s : getContext().getTemplateParser().getBody()) { 117 addElement(processElement(s, member), thenBlock); 118 } 119 if (addSeparator) 120 addElement(getContext().getTemplateParser().getSeparator(), thenBlock); 121 flushBuffer(thenBlock); 122 123 if (thenBlock.statements().size() == 1 && !getContext().isForceBlocks()) { 124 ifStatement.setThenStatement((Statement)ASTNode.copySubtree(fAst, (ASTNode)thenBlock.statements().get(0))); 125 } else { 126 ifStatement.setThenStatement(thenBlock); 127 } 128 toStringMethod.getBody().statements().add(ifStatement); 129 } 130 131 @Override addElement(Object element)132 protected void addElement(Object element) { 133 addElement(element, toStringMethod.getBody()); 134 } 135 } 136