Copyright (c) 2008, 2019 Mateusz Matela and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: Mateusz Matela - [code manipulation] [dcr] toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=26070 Mateusz Matela - [toString] Generator uses wrong suffixes and prefixes - https://bugs.eclipse.org/bugs/show_bug.cgi?id=275370 Red Hat Inc. - moved to jdt.core.manipulation
/******************************************************************************* * Copyright (c) 2008, 2019 Mateusz Matela and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * 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] Generator uses wrong suffixes and prefixes - https://bugs.eclipse.org/bugs/show_bug.cgi?id=275370 * Red Hat Inc. - moved to jdt.core.manipulation *******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.jdt.core.NamingConventions; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.ConditionalExpression; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.InstanceofExpression; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.PostfixExpression; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.manipulation.CodeGeneration; import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2Core; import org.eclipse.jdt.internal.core.manipulation.StubUtility; import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;

This generator creates an implementation of Object.toString() which lists all selected fields and methods. What exactly is listed and how members are separated is determined by a format template.

To print out items of arrays and/or limit number items printed for arrays, collections and so on, various methods are used according to actual jdk compatibility:

java.util.List java.util.Collection java.util.Map Array of primitive types Array of non-primitive types
jdk 1.4 - - - helper method arrayToString(Object array, int length) Arrays.asList(member)
jdk 1.4/1.5, limit elements member.subList() helper method toSting(Collection) helper method toString(Collection) with member.entrySet() helper method arrayToString(Object array, int length) Arrays.asList(member).subList()
jdk 1.5 - - - Arrays.toString() Arrays.asList(member)
jdk 1.6 - - - Arrays.toString() Arrays.toString()
jdk 1.6, limit elements member.subList() helper method toSting(Collection) helper method toString(Collection) with member.entrySet() Arrays.toString(Arrays.copyOf(member, ...)) Arrays.asList(member).subList()
Additionally, if helper method is generated it is also used for other members members (even if other solution could be used), as it makes the code cleaner.

Since:3.5
/** * <p> * This generator creates an implementation of <code>{@link java.lang.Object#toString()}</code> * which lists all selected fields and methods. What exactly is listed and how members are separated * is determined by a format template. * <p> * * <p> * To print out items of arrays and/or limit number items printed for arrays, collections and so on, * various methods are used according to actual jdk compatibility: * <table border="10"> * <tr> * <td></td> * <th><code>java.util.List</code></th> * <th><code>java.util.Collection</code></th> * <th><code>java.util.Map</code></th> * <th>Array of primitive types</th> * <th>Array of non-primitive types</th> * </tr> * <tr> * <th>jdk 1.4</th> * <td>-</td> * <td>-</td> * <td>-</td> * <td>helper method <code>arrayToString(Object array, int length)</code></td> * <td><code>Arrays.asList(member)</code></td> * </tr> * <tr> * <th>jdk 1.4/1.5, limit elements</th> * <td><code>member.subList()</code></td> * <td>helper method <code>toSting(Collection)</code></td> * <td>helper method <code>toString(Collection)</code> with <code>member.entrySet()</code></td> * <td>helper method <code>arrayToString(Object array, int length)</code></td> * <td><code>Arrays.asList(member).subList()</code></td> * </tr> * <tr> * <th>jdk 1.5</th> * <td>-</td> * <td>-</td> * <td>-</td> * <td><code>Arrays.toString()<code></td> * <td><code>Arrays.asList(member)</code></td> * </tr> * <tr> * <th>jdk 1.6</th> * <td>-</td> * <td>-</td> * <td>-</td> * <td><code>Arrays.toString()</code></td> * <td><code>Arrays.toString()</code></td> * </tr> * <tr> * <th>jdk 1.6, limit elements</th> * <td><code>member.subList()</code></td> * <td>helper method <code>toSting(Collection)</code></td> * <td>helper method <code>toString(Collection)</code> with <code>member.entrySet()</code></td> * <td><code>Arrays.toString(Arrays.copyOf(member, ...))</code></td> * <td><code>Arrays.asList(member).subList()</code></td> * </tr> * </table> * Additionally, if helper method is generated it is also used for other members members (even if * other solution could be used), as it makes the code cleaner. * </p> * * @since 3.5 */
public abstract class AbstractToStringGenerator { protected static final String METHODNAME_TO_STRING= "toString"; //$NON-NLS-1$ protected static final String TYPENAME_STRING= "String"; //$NON-NLS-1$ final protected String HELPER_TOSTRING_METHOD_NAME= "toString"; //$NON-NLS-1$ final private String HELPER_ARRAYTOSTRING_METHOD_NAME= "arrayToString"; //$NON-NLS-1$ final private String MAX_LEN_VARIABLE_NAME= "maxLen"; //$NON-NLS-1$ protected String fMaxLenVariableName= MAX_LEN_VARIABLE_NAME;
The name of the property that every MethodDeclaration generated should have. This property determines whether the method should be overwritten if already exists. The data of this property is a Boolean.
/** * The name of the property that every <code>MethodDeclaration</code> generated should have. * This property determines whether the method should be overwritten if already exists. The data * of this property is a Boolean. */
protected final static String OVERWRITE_METHOD_PROPERTY= "override_method"; //$NON-NLS-1$ public ToStringTemplateParser getTemplateParser() { return new ToStringTemplateParser(); } protected ToStringGenerationContext fContext;
The ast to be used. Convenience accessor field
/** The ast to be used. Convenience accessor field */
protected AST fAst; protected MethodDeclaration toStringMethod; protected boolean needMaxLenVariable; protected boolean needCollectionToStringMethod; protected List<ITypeBinding> typesThatNeedArrayToStringMethod; public RefactoringStatus checkConditions() { return new RefactoringStatus(); }
This method is an implementation of Director in Builder pattern. It goes through all elements of the format template and calls methods responsible for processing them.
Throws:
Returns:declaration of the generated toString() method
/** * This method is an implementation of Director in Builder pattern. It goes through all elements * of the format template and calls methods responsible for processing them. * * @return declaration of the generated <code>toString()</code> method * @throws CoreException if creation failed */
public MethodDeclaration generateToStringMethod() throws CoreException { initialize(); String[] stringArray= fContext.getTemplateParser().getBeginning(); for (int i= 0; i < stringArray.length; i++) { addElement(processElement(stringArray[i], null)); } stringArray= fContext.getTemplateParser().getBody(); Object[] members= fContext.getSelectedMembers(); for (int i= 0; i < members.length; i++) { if (!fContext.isSkipNulls() || getMemberType(members[i]).isPrimitive()) addMember(members[i], i != members.length - 1); else addMemberCheckNull(members[i], i != members.length - 1); } stringArray= fContext.getTemplateParser().getEnding(); for (int i= 0; i < stringArray.length; i++) { addElement(processElement(stringArray[i], null)); } complete(); return toStringMethod; } public List<MethodDeclaration> generateHelperMethods() { List<MethodDeclaration> result= new ArrayList<>(); if (needCollectionToStringMethod) result.add(createHelperToStringMethod(false)); if (!typesThatNeedArrayToStringMethod.isEmpty()) result.add(createHelperToStringMethod(true)); return result; }
adds a comment (if necessary) and an @Override annotation to the generated method
Throws:
/** * adds a comment (if necessary) and an <code>@Override</code> annotation to the generated * method * * @throws CoreException if creation failed */
protected void createMethodComment() throws CoreException { ITypeBinding object= fAst.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ IMethodBinding[] objms= object.getDeclaredMethods(); IMethodBinding objectMethod= null; for (int i= 0; i < objms.length; i++) { if (objms[i].getName().equals(METHODNAME_TO_STRING) && objms[i].getParameterTypes().length == 0) objectMethod= objms[i]; } if (fContext.isCreateComments()) { String docString= CodeGeneration.getMethodComment(fContext.getCompilationUnit(), fContext.getTypeBinding().getQualifiedName(), toStringMethod, objectMethod, StubUtility .getLineDelimiterUsed(fContext.getCompilationUnit())); if (docString != null) { Javadoc javadoc= (Javadoc)fContext.getASTRewrite().createStringPlaceholder(docString, ASTNode.JAVADOC); toStringMethod.setJavadoc(javadoc); } } StubUtility2Core.addOverrideAnnotation(fContext.getCodeGenerationSettings(), fContext.getTypeBinding().getJavaElement().getJavaProject(), fContext.getASTRewrite(), fContext.getImportRewrite(), toStringMethod, objectMethod.getDeclaringClass().isInterface(), null); }
Creates a method that takes a Collection or an Array and returns a String containing it's first fSettings.limitValue elements
Params:
  • array – if true, generated method will convert array to string, otherwise - Collection
Returns:arrayToString(Object[] array) or collectionToString(Collection collection) method
/** * Creates a method that takes a <code>Collection</code> or an Array and returns a * <code>String</code> containing it's first <code>fSettings.limitValue</code> elements * * @param array if true, generated method will convert array to string, otherwise - * <code>Collection</code> * @return <code>arrayToString(Object[] array)</code> or </code>collectionToString(Collection * collection)</code> method */
protected MethodDeclaration createHelperToStringMethod(boolean array) { final String iteratorName= createNameSuggestion("iterator", NamingConventions.VK_LOCAL); //$NON-NLS-1$ final String appendMethodName= "append"; //$NON-NLS-1$ final String indexName= createNameSuggestion("i", NamingConventions.VK_LOCAL); //$NON-NLS-1$ final String lengthParamName= createNameSuggestion("len", NamingConventions.VK_PARAMETER); //$NON-NLS-1$ final String maxLenParamName= createNameSuggestion(MAX_LEN_VARIABLE_NAME, NamingConventions.VK_PARAMETER); String paramName; String stringBuilderName; String stringBuilderTypeName; if (fContext.is50orHigher()) { stringBuilderTypeName= "java.lang.StringBuilder"; //$NON-NLS-1$ stringBuilderName= createNameSuggestion("builder", NamingConventions.VK_LOCAL); //$NON-NLS-1$ } else { stringBuilderTypeName= "java.lang.StringBuffer"; //$NON-NLS-1$ stringBuilderName= createNameSuggestion("buffer", NamingConventions.VK_LOCAL); //$NON-NLS-1$ } //private arrayToString() { MethodDeclaration arrayToStringMethod= fAst.newMethodDeclaration(); arrayToStringMethod.setName(fAst.newSimpleName(array ? HELPER_ARRAYTOSTRING_METHOD_NAME : HELPER_TOSTRING_METHOD_NAME)); arrayToStringMethod.modifiers().add(fAst.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD)); arrayToStringMethod.setReturnType2(fAst.newSimpleType(fAst.newName(TYPENAME_STRING))); if (array) { //(Objec array, int length, ...) paramName= createNameSuggestion("array", NamingConventions.VK_PARAMETER); //$NON-NLS-1$ SingleVariableDeclaration param= fAst.newSingleVariableDeclaration(); param.setType(fAst.newSimpleType(addImport("java.lang.Object"))); //$NON-NLS-1$ param.setName(fAst.newSimpleName(paramName)); arrayToStringMethod.parameters().add(param); param= fAst.newSingleVariableDeclaration(); param.setType(fAst.newPrimitiveType(PrimitiveType.INT)); param.setName(fAst.newSimpleName(lengthParamName)); arrayToStringMethod.parameters().add(param); } else { //(Collection<?> collection, ...) paramName= createNameSuggestion("collection", NamingConventions.VK_PARAMETER); //$NON-NLS-1$ SingleVariableDeclaration param= fAst.newSingleVariableDeclaration(); Type collectionType= fAst.newSimpleType(addImport("java.util.Collection")); //$NON-NLS-1$ if (fContext.is50orHigher()) { ParameterizedType genericType= fAst.newParameterizedType(collectionType); genericType.typeArguments().add(fAst.newWildcardType()); param.setType(genericType); } else { param.setType(collectionType); } param.setName(fAst.newSimpleName(paramName)); arrayToStringMethod.parameters().add(param); } if (fContext.isLimitItems()) { SingleVariableDeclaration param= fAst.newSingleVariableDeclaration(); param.setType(fAst.newPrimitiveType(PrimitiveType.INT)); param.setName(fAst.newSimpleName(maxLenParamName)); arrayToStringMethod.parameters().add(param); } Block body= fAst.newBlock(); arrayToStringMethod.setBody(body); //StringBuilder stringBuilder= new StringBuilder(); VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); fragment.setName(fAst.newSimpleName(stringBuilderName)); ClassInstanceCreation classInstance= fAst.newClassInstanceCreation(); classInstance.setType(fAst.newSimpleType(addImport(stringBuilderTypeName))); fragment.setInitializer(classInstance); VariableDeclarationStatement vStatement= fAst.newVariableDeclarationStatement(fragment); vStatement.setType(fAst.newSimpleType(addImport(stringBuilderTypeName))); body.statements().add(vStatement); if (array && fContext.isLimitItems()) { //length = Math.min(length, maxLen); MethodInvocation minInvocation= createMethodInvocation(addImport("java.lang.Math"), "min", fAst.newSimpleName(lengthParamName)); //$NON-NLS-1$ //$NON-NLS-2$ minInvocation.arguments().add(fAst.newSimpleName(maxLenParamName)); Assignment lengthAssignment= fAst.newAssignment(); lengthAssignment.setLeftHandSide(fAst.newSimpleName(lengthParamName)); lengthAssignment.setRightHandSide(minInvocation); body.statements().add(fAst.newExpressionStatement(lengthAssignment)); } //stringBuilder.add("["); StringLiteral literal= fAst.newStringLiteral(); literal.setLiteralValue("["); //$NON-NLS-1$ body.statements().add(fAst.newExpressionStatement(createMethodInvocation(stringBuilderName, appendMethodName, literal))); //for(... ForStatement forStatement= fAst.newForStatement(); Block forBlock= fAst.newBlock(); forStatement.setBody(forBlock); //int i = 0; VariableDeclarationFragment indexDeclFragment= fAst.newVariableDeclarationFragment(); indexDeclFragment.setName(fAst.newSimpleName(indexName)); indexDeclFragment.setInitializer(fAst.newNumberLiteral("0")); //$NON-NLS-1$ VariableDeclarationExpression indexDeclExpression= fAst.newVariableDeclarationExpression(indexDeclFragment); indexDeclExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT)); // i++ PostfixExpression postfixExpr= fAst.newPostfixExpression(); postfixExpr.setOperand(fAst.newSimpleName(indexName)); postfixExpr.setOperator(org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT); forStatement.updaters().add(postfixExpr); //if (i > 0) builder.append(", "); IfStatement ifStatement= fAst.newIfStatement(); ifStatement.setExpression(createInfixExpression(fAst.newSimpleName(indexName), Operator.GREATER, fAst.newNumberLiteral(String.valueOf(0)))); literal= fAst.newStringLiteral(); literal.setLiteralValue(", "); //$NON-NLS-1$ ifStatement.setThenStatement(createOneStatementBlock(createMethodInvocation(stringBuilderName, appendMethodName, literal))); forBlock.statements().add(ifStatement); if (array) { forStatement.initializers().add(indexDeclExpression); // i < length; forStatement.setExpression(createInfixExpression(fAst.newSimpleName(indexName), Operator.LESS, fAst.newSimpleName(lengthParamName))); for (Iterator<ITypeBinding> iterator= typesThatNeedArrayToStringMethod.iterator(); iterator.hasNext();) { ITypeBinding typeBinding= iterator.next(); //if (array instanceof int[]) { String typeName= typeBinding.getName(); PrimitiveType.Code code= null; if (typeName.equals("byte")) code= PrimitiveType.BYTE; //$NON-NLS-1$ if (typeName.equals("short")) code= PrimitiveType.SHORT; //$NON-NLS-1$ if (typeName.equals("char")) code= PrimitiveType.CHAR; //$NON-NLS-1$ if (typeName.equals("int")) code= PrimitiveType.INT; //$NON-NLS-1$ if (typeName.equals("long")) code= PrimitiveType.LONG; //$NON-NLS-1$ if (typeName.equals("float")) code= PrimitiveType.FLOAT; //$NON-NLS-1$ if (typeName.equals("double")) code= PrimitiveType.DOUBLE; //$NON-NLS-1$ if (typeName.equals("boolean")) code= PrimitiveType.BOOLEAN; //$NON-NLS-1$ if (code == null && !typeName.equals("Object"))continue; //$NON-NLS-1$ InstanceofExpression instanceOf= fAst.newInstanceofExpression(); instanceOf.setLeftOperand(fAst.newSimpleName(paramName)); instanceOf.setRightOperand(fAst.newArrayType(code != null ? (Type)fAst.newPrimitiveType(code) : fAst.newSimpleType(addImport("java.lang.Object")))); //$NON-NLS-1$ ifStatement= fAst.newIfStatement(); ifStatement.setExpression(instanceOf); //builder.append(((int[]) array)[i]); CastExpression arrayCast= fAst.newCastExpression(); arrayCast.setExpression(fAst.newSimpleName(paramName)); arrayCast.setType(fAst.newArrayType(code != null ? (Type)fAst.newPrimitiveType(code) : fAst.newSimpleType(addImport("java.lang.Object")))); //$NON-NLS-1$ ParenthesizedExpression parenthesizedCast= fAst.newParenthesizedExpression(); parenthesizedCast.setExpression(arrayCast); ArrayAccess arrayAccess= fAst.newArrayAccess(); arrayAccess.setArray(parenthesizedCast); arrayAccess.setIndex(fAst.newSimpleName(indexName)); ifStatement.setThenStatement(createOneStatementBlock(createMethodInvocation(stringBuilderName, appendMethodName, arrayAccess))); forBlock.statements().add(ifStatement); } } else { body.statements().add(fAst.newExpressionStatement(indexDeclExpression)); //... Iterator iterator= collection.iterator() ... fragment= fAst.newVariableDeclarationFragment(); fragment.setName(fAst.newSimpleName(iteratorName)); fragment.setInitializer(createMethodInvocation(paramName, "iterator", null)); //$NON-NLS-1$ VariableDeclarationExpression vExpression= fAst.newVariableDeclarationExpression(fragment); SimpleType iteratorType= fAst.newSimpleType(addImport("java.util.Iterator")); //$NON-NLS-1$ if (fContext.is50orHigher()) { ParameterizedType pType= fAst.newParameterizedType(iteratorType); pType.typeArguments().add(fAst.newWildcardType()); vExpression.setType(pType); } else { vExpression.setType(iteratorType); } forStatement.initializers().add(vExpression); //iterator.hasNext() && i < maxLen; Expression indexExpression= createInfixExpression(fAst.newSimpleName(indexName), Operator.LESS, fAst.newSimpleName(maxLenParamName)); forStatement.setExpression(createInfixExpression(createMethodInvocation(iteratorName, "hasNext", null), Operator.CONDITIONAL_AND, indexExpression)); //$NON-NLS-1$ //if (i > 0) //stringBuilder.append(iterator.next()); MethodInvocation nextInvocation= createMethodInvocation(iteratorName, "next", null); //$NON-NLS-1$ forBlock.statements().add(fAst.newExpressionStatement(createMethodInvocation(stringBuilderName, appendMethodName, nextInvocation))); } body.statements().add(forStatement); //stringBuilder.add("]"); literal= fAst.newStringLiteral(); literal.setLiteralValue("]"); //$NON-NLS-1$ body.statements().add(fAst.newExpressionStatement(createMethodInvocation(stringBuilderName, appendMethodName, literal))); //return stringBuilder.toString(); ReturnStatement returnStatement= fAst.newReturnStatement(); returnStatement.setExpression(createMethodInvocation(stringBuilderName, "toString", null)); //$NON-NLS-1$ body.statements().add(returnStatement); arrayToStringMethod.setProperty(OVERWRITE_METHOD_PROPERTY, Boolean.valueOf(array)); return arrayToStringMethod; }
This method initializes all variables used in the process of generating toString method.
/** * This method initializes all variables used in the process of generating <code>toString</code> * method. */
protected void initialize() { needMaxLenVariable= false; needCollectionToStringMethod= false; typesThatNeedArrayToStringMethod= new ArrayList<>(); checkNeedForHelperMethods(); toStringMethod= fAst.newMethodDeclaration(); toStringMethod.modifiers().addAll(ASTNodeFactory.newModifiers(fAst, Modifier.PUBLIC)); toStringMethod.setName(fAst.newSimpleName(METHODNAME_TO_STRING)); toStringMethod.setConstructor(false); toStringMethod.setReturnType2(fAst.newSimpleType(fAst.newName(TYPENAME_STRING))); Block body= fAst.newBlock(); toStringMethod.setBody(body); fMaxLenVariableName= createNameSuggestion(MAX_LEN_VARIABLE_NAME, NamingConventions.VK_LOCAL); }
This method is called at the end of the process of generating toString method. It should make sure the processed properly and clean the environment.
Throws:
  • CoreException – if creation failed
/** * This method is called at the end of the process of generating <code>toString</code> method. * It should make sure the processed properly and clean the environment. * * @throws CoreException if creation failed */
protected void complete() throws CoreException { if (needMaxLenVariable) { toStringMethod.getBody().statements().add(0, createMaxLenDeclaration()); } createMethodComment(); toStringMethod.setProperty(OVERWRITE_METHOD_PROPERTY, Boolean.valueOf(true)); }
Iterates over selected members to determine whether helper methods will be needed.
/** * Iterates over selected members to determine whether helper methods will be needed. */
protected void checkNeedForHelperMethods() { if ((!fContext.isLimitItems() && !fContext.isCustomArray()) || (fContext.isLimitItems() && fContext.getLimitItemsValue() == 0)) return; boolean isNonPrimitive= false; for (int i= 0; i < fContext.getSelectedMembers().length; i++) { ITypeBinding memberType= getMemberType(fContext.getSelectedMembers()[i]); boolean[] implementsInterfaces= implementsInterfaces(memberType.getErasure(), new String[] { "java.util.Collection", "java.util.List", "java.util.Map" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean isCollection= implementsInterfaces[0]; boolean isList= implementsInterfaces[1]; boolean isMap= implementsInterfaces[2]; if (fContext.isLimitItems() && (isCollection || isMap) && !isList) { needCollectionToStringMethod= true; } if (fContext.isCustomArray() && memberType.isArray()) { ITypeBinding componentType= memberType.getComponentType(); if (componentType.isPrimitive() && (!fContext.is50orHigher() || (!fContext.is60orHigher() && fContext.isLimitItems()))) { if (!typesThatNeedArrayToStringMethod.contains(componentType)) typesThatNeedArrayToStringMethod.add(componentType); } else if (!componentType.isPrimitive()) isNonPrimitive= true; } } if (!typesThatNeedArrayToStringMethod.isEmpty() && isNonPrimitive) typesThatNeedArrayToStringMethod.add(fAst.resolveWellKnownType("java.lang.Object")); //$NON-NLS-1$ }
Params:
Returns:String or Expression switching
/** * * @param templateElement the template element, see constants in {@link ToStringTemplateParser} * @param member the member * @return <code>String</code> or <code>Expression</code> switching */
protected Object processElement(String templateElement, Object member) { Object result= templateElement; if (templateElement == ToStringTemplateParser.OBJECT_NAME_VARIABLE) { result= fContext.getTypeBinding().getName(); } if (templateElement == ToStringTemplateParser.OBJECT_GET_NAME_VARIABLE) { //this.getClass().getName() MethodInvocation getClassInvocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) getClassInvocation.setExpression(fAst.newThisExpression()); getClassInvocation.setName(fAst.newSimpleName("getClass")); //$NON-NLS-1$ MethodInvocation getNameInvocation= fAst.newMethodInvocation(); getNameInvocation.setExpression(getClassInvocation); getNameInvocation.setName(fAst.newSimpleName("getName")); //$NON-NLS-1$ result= getNameInvocation; } if (templateElement == ToStringTemplateParser.OBJECT_SUPER_TOSTRING_VARIABLE) { //super.toString() SuperMethodInvocation superToStringInvocation= fAst.newSuperMethodInvocation(); superToStringInvocation.setName(fAst.newSimpleName(METHODNAME_TO_STRING)); result= superToStringInvocation; } if (templateElement == ToStringTemplateParser.OBJECT_HASHCODE_VARIABLE) { //this.hashCode() MethodInvocation hashCodeInvocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) hashCodeInvocation.setExpression(fAst.newThisExpression()); hashCodeInvocation.setName(fAst.newSimpleName("hashCode")); //$NON-NLS-1$ result= hashCodeInvocation; } if (templateElement == ToStringTemplateParser.OBJECT_SYSTEM_HASHCODE_VARIABLE) { //system.identityHashCode(this) result= createMethodInvocation(addImport("java.lang.System"), "identityHashCode", fAst.newThisExpression()); //$NON-NLS-1$ //$NON-NLS-2$ } if (templateElement == ToStringTemplateParser.MEMBER_NAME_VARIABLE || templateElement == ToStringTemplateParser.MEMBER_NAME_PARENTHESIS_VARIABLE) { result= getMemberName(member, templateElement); } if (templateElement == ToStringTemplateParser.MEMBER_VALUE_VARIABLE) { result= createMemberAccessExpression(member, false, fContext.isSkipNulls()); } if (result instanceof StringLiteral) return ((StringLiteral)result).getLiteralValue(); else return result; }
Adds an element to the generated toString method. This method is called for every element of the format template.
Params:
  • element – String or expression to be added (IVariableBinding or IMethodBinding
/** * Adds an element to the generated <code>toString</code> method. This method is called for * every element of the format template. * * @param element String or expression to be added (<code>IVariableBinding</code> or * <code>IMethodBinding</code> */
protected abstract void addElement(Object element);
Adds a member to the toString method. This method is called for every member if "Skip null values" options is unchecked.
Params:
  • member – a member to be added
  • addSeparator – true, if separator should be added after the member (i.e. this is not the last member)
/** * Adds a member to the <code>toString</code> method. This method is called for every member if * "Skip null values" options is unchecked. * * @param member a member to be added * @param addSeparator true, if separator should be added after the member (i.e. this is not the * last member) */
protected void addMember(Object member, boolean addSeparator) { String[] stringArray= fContext.getTemplateParser().getBody(); for (int i= 0; i < stringArray.length; i++) { addElement(processElement(stringArray[i], member)); } if (addSeparator) addElement(fContext.getTemplateParser().getSeparator()); }
Adds a code checking if member's value is not null and adding it to the generated string. This method is called for every non-primitive type member if "Skip null values" options is checked, or for every Collection and Map member if there's a limit for number of elements.
Params:
  • member – a member to be added
  • addSeparator – true, if separator should be added after the member (i.e. this is not the last member)
/** * Adds a code checking if member's value is not null and adding it to the generated string. * This method is called for every non-primitive type member if "Skip null values" options is * checked, or for every <code>Collection</code> and <code>Map</code> member if there's a limit * for number of elements. * * @param member a member to be added * @param addSeparator true, if separator should be added after the member (i.e. this is not the * last member) */
protected void addMemberCheckNull(Object member, boolean addSeparator) { addMember(member, addSeparator); }
Creates an invocation of a method that takes zero or one argument
Params:
  • expression – the receiver expression
  • methodName – the method name
  • argument – the argument, can be null if the method does not take any arguments
Returns:MethodInvocation in following form: expression.methodName(argument)
/** * Creates an invocation of a method that takes zero or one argument * * @param expression the receiver expression * @param methodName the method name * @param argument the argument, can be <code>null</code> if the method does not take any arguments * @return MethodInvocation in following form: <code>expression.methodName(argument)</code> */
protected MethodInvocation createMethodInvocation(Expression expression, String methodName, Expression argument) { MethodInvocation invocation= fAst.newMethodInvocation(); invocation.setExpression(expression); invocation.setName(fAst.newSimpleName(methodName)); if (argument != null) invocation.arguments().add(argument); return invocation; }
Creates an invocation of a method that takes zero or one argument
Params:
  • receiver – the receiver name
  • methodName – the method name
  • argument – the argument, can be null if the method does not take any arguments
Returns:MethodInvocation in following form: expression.methodName(argument)
/** * Creates an invocation of a method that takes zero or one argument * * @param receiver the receiver name * @param methodName the method name * @param argument the argument, can be <code>null</code> if the method does not take any arguments * @return MethodInvocation in following form: <code>expression.methodName(argument)</code> */
protected MethodInvocation createMethodInvocation(String receiver, String methodName, Expression argument) { return createMethodInvocation(fAst.newName(receiver), methodName, argument); }
Creates a statement that can be used as for/while/if-then-else block
Params:
  • expression – an expression
Returns:a single-line statement, or a block, depending on settings
/** * Creates a statement that can be used as for/while/if-then-else block * * @param expression an expression * @return a single-line statement, or a block, depending on settings */
protected Statement createOneStatementBlock(Expression expression) { if (fContext.isForceBlocks()) { Block forBlock= fAst.newBlock(); forBlock.statements().add(fAst.newExpressionStatement(expression)); return forBlock; } else { return fAst.newExpressionStatement(expression); } } protected InfixExpression createInfixExpression(Expression leftOperand, Operator operator, Expression rightOperand) { InfixExpression expression= fAst.newInfixExpression(); expression.setLeftOperand(leftOperand); expression.setOperator(operator); expression.setRightOperand(rightOperand); return expression; }
Returns:a statement in form of final int maxLen = 10;
/** * @return a statement in form of <code>final int maxLen = 10;</code> */
protected VariableDeclarationStatement createMaxLenDeclaration() { VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment(); fragment.setName(fAst.newSimpleName(fMaxLenVariableName)); fragment.setInitializer(fAst.newNumberLiteral(String.valueOf(fContext.getLimitItemsValue()))); VariableDeclarationStatement declExpression= fAst.newVariableDeclarationStatement(fragment); declExpression.setType(fAst.newPrimitiveType(PrimitiveType.INT)); declExpression.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD)); return declExpression; }
Params:
  • member – IVariableBinding or IMethodBinding representing a member
  • ignoreArraysCollections – if false and limit number of items is set, this method will use custom methods to print out Arrays, Collections and Sets
  • ignoreNulls – if false, method will add checking for nulls when using custom methods
Returns:an expression that accesses given member
/** * @param member <code>IVariableBinding</code> or <code>IMethodBinding</code> representing a * member * @param ignoreArraysCollections if false and <i>limit number of items</i> is set, this method * will use custom methods to print out Arrays, Collections and Sets * @param ignoreNulls if false, method will add checking for nulls when using custom methods * @return an expression that accesses given member */
protected Expression createMemberAccessExpression(Object member, boolean ignoreArraysCollections, boolean ignoreNulls) { if (!ignoreArraysCollections) { ITypeBinding memberType= getMemberType(member); boolean isArray= memberType.isArray(); boolean[] implementsInterfaces= implementsInterfaces(memberType.getErasure(), new String[] { "java.util.Collection", "java.util.List", "java.util.Map" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean isCollection= implementsInterfaces[0]; boolean isList= implementsInterfaces[1]; boolean isMap= implementsInterfaces[2]; if (isCollection || isMap || (isArray && fContext.isCustomArray())) { Expression accessExpression= null; if (fContext.isLimitItems()) { if (fContext.getLimitItemsValue() == 0) { accessExpression= fAst.newStringLiteral(); ((StringLiteral)accessExpression).setLiteralValue("[]"); //$NON-NLS-1$ } else { if (isList && !needCollectionToStringMethod) { //member.subList(0, Math.min(maxLen, member.size()) MethodInvocation memberSizeInvocation= fAst.newMethodInvocation(); memberSizeInvocation.setExpression(createMemberAccessExpression(member, true, true)); memberSizeInvocation.setName(fAst.newSimpleName("size")); //$NON-NLS-1$ accessExpression= createSubListInvocation(createMemberAccessExpression(member, true, true), memberSizeInvocation); needMaxLenVariable= true; } else if (isCollection || isMap) { //toString(member, maxLen) Expression memberAccess= createMemberAccessExpression(member, true, true); if (isMap) { //member.entrySet(); MethodInvocation entrySetInvocation= fAst.newMethodInvocation(); entrySetInvocation.setExpression(memberAccess); entrySetInvocation.setName(fAst.newSimpleName("entrySet")); //$NON-NLS-1$ memberAccess= entrySetInvocation; } MethodInvocation toStringInvocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) toStringInvocation.setExpression(fAst.newThisExpression()); toStringInvocation.setName(fAst.newSimpleName(HELPER_TOSTRING_METHOD_NAME)); toStringInvocation.arguments().add(memberAccess); toStringInvocation.arguments().add(fAst.newSimpleName(fMaxLenVariableName)); needMaxLenVariable= true; accessExpression= toStringInvocation; } else if (isArray) { FieldAccess lengthAccess= fAst.newFieldAccess(); lengthAccess.setExpression(createMemberAccessExpression(member, true, true)); lengthAccess.setName(fAst.newSimpleName("length")); //$NON-NLS-1$ ITypeBinding arrayComponentType= memberType.getComponentType(); if (!arrayComponentType.isPrimitive() && typesThatNeedArrayToStringMethod.isEmpty()) { //Arrays.asList(member).subList(0, Math.min(maxLen, member.length)) MethodInvocation asListInvocation= createMethodInvocation(addImport("java.util.Arrays"), "asList", createMemberAccessExpression(member, true, true)); //$NON-NLS-1$ //$NON-NLS-2$ accessExpression= createSubListInvocation(asListInvocation, lengthAccess); } else { if (fContext.is60orHigher()) { // Arrays.toString( Arrays.copyOf ( member, Math.min (maxLen, member.length) ) Name arraysImport= addImport("java.util.Arrays"); //$NON-NLS-1$ MethodInvocation minInvocation= createMethodInvocation(addImport("java.lang.Math"), "min", lengthAccess); //$NON-NLS-1$ //$NON-NLS-2$ minInvocation.arguments().add(fAst.newSimpleName(fMaxLenVariableName)); needMaxLenVariable= true; MethodInvocation copyOfInvocation= createMethodInvocation(arraysImport, "copyOf", createMemberAccessExpression(member, true, true)); //$NON-NLS-1$ copyOfInvocation.arguments().add(minInvocation); Name arraysImportCopy= (Name)ASTNode.copySubtree(fAst, arraysImport); accessExpression= createMethodInvocation(arraysImportCopy, "toString", copyOfInvocation); //$NON-NLS-1$ } else { // arrayToString(member, member.length, maxLen) MethodInvocation arrayToStringInvocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) arrayToStringInvocation.setExpression(fAst.newThisExpression()); arrayToStringInvocation.setName(fAst.newSimpleName(HELPER_ARRAYTOSTRING_METHOD_NAME)); arrayToStringInvocation.arguments().add(createMemberAccessExpression(member, true, true)); arrayToStringInvocation.arguments().add(lengthAccess); arrayToStringInvocation.arguments().add(fAst.newSimpleName(fMaxLenVariableName)); needMaxLenVariable= true; accessExpression= arrayToStringInvocation; } } } } } else { if (isArray && fContext.isCustomArray()) { if (fContext.is50orHigher()) { // Arrays.toString(member) return createMethodInvocation(addImport("java.util.Arrays"), "toString", createMemberAccessExpression(member, true, true)); //$NON-NLS-1$ //$NON-NLS-2$ } else { ITypeBinding arrayComponentType= memberType.getComponentType(); if (!arrayComponentType.isPrimitive() && typesThatNeedArrayToStringMethod.isEmpty()) { // Arrays.asList(member) accessExpression= createMethodInvocation(addImport("java.util.Arrays"), "asList", createMemberAccessExpression(member, true, true)); //$NON-NLS-1$ //$NON-NLS-2$ } else { // arrayToString(member, member.length, maxLen) FieldAccess lengthAccess= fAst.newFieldAccess(); lengthAccess.setExpression(createMemberAccessExpression(member, true, true)); lengthAccess.setName(fAst.newSimpleName("length")); //$NON-NLS-1$ MethodInvocation arrayToStringInvocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) arrayToStringInvocation.setExpression(fAst.newThisExpression()); arrayToStringInvocation.setName(fAst.newSimpleName(HELPER_ARRAYTOSTRING_METHOD_NAME)); arrayToStringInvocation.arguments().add(createMemberAccessExpression(member, true, true)); arrayToStringInvocation.arguments().add(lengthAccess); accessExpression= arrayToStringInvocation; } } } } if (accessExpression != null) { if (!ignoreNulls) { ConditionalExpression conditional= fAst.newConditionalExpression(); conditional.setExpression(createInfixExpression(createMemberAccessExpression(member, true, true), Operator.NOT_EQUALS, fAst.newNullLiteral())); conditional.setThenExpression(accessExpression); conditional.setElseExpression(fAst.newNullLiteral()); return conditional; } return accessExpression; } } } if (member instanceof IVariableBinding) { if (fContext.isKeywordThis()) { FieldAccess fa= fAst.newFieldAccess(); fa.setExpression(fAst.newThisExpression()); fa.setName(fAst.newSimpleName(((IVariableBinding)member).getName())); return fa; } return fAst.newSimpleName(((IVariableBinding)member).getName()); } if (member instanceof IMethodBinding) { if (((IMethodBinding)member).getName().equals(METHODNAME_TO_STRING)) { SuperMethodInvocation invocation= fAst.newSuperMethodInvocation(); invocation.setName(fAst.newSimpleName(((IMethodBinding)member).getName())); return invocation; } MethodInvocation invocation= fAst.newMethodInvocation(); if (fContext.isKeywordThis()) { invocation.setExpression(fAst.newThisExpression()); } invocation.setName(fAst.newSimpleName(((IMethodBinding)member).getName())); return invocation; } return null; } protected Expression createSubListInvocation(Expression memberAccess, Expression sizeAccess) { MethodInvocation subListInvocation= fAst.newMethodInvocation(); subListInvocation.setExpression(memberAccess); subListInvocation.setName(fAst.newSimpleName("subList")); //$NON-NLS-1$ subListInvocation.arguments().add(fAst.newNumberLiteral(String.valueOf(0))); MethodInvocation minInvocation= createMethodInvocation(addImport("java.lang.Math"), "min", sizeAccess); //$NON-NLS-1$ //$NON-NLS-2$ minInvocation.arguments().add(fAst.newSimpleName(fMaxLenVariableName)); subListInvocation.arguments().add(minInvocation); needMaxLenVariable= true; return subListInvocation; }
Adds an import to the class. This method should be used for every class reference added to the generated code.
Params:
  • typeName – a fully qualified name of a type
Returns:simple name of a class if the import was added and fully qualified name if there was a conflict
/** * Adds an import to the class. This method should be used for every class reference added to * the generated code. * * @param typeName a fully qualified name of a type * @return simple name of a class if the import was added and fully qualified name if there was * a conflict */
protected Name addImport(String typeName) { String importedName= fContext.getImportRewrite().addImport(typeName); return fAst.newName(importedName); } private Set<String> excluded; protected String createNameSuggestion(String baseName, int variableKind) { if (excluded == null) { excluded= new HashSet<>(); IVariableBinding[] fields= fContext.getTypeBinding().getDeclaredFields(); for (int i= 0; i < fields.length; i++) { excluded.add(fields[i].getName()); } ITypeBinding superType= fContext.getTypeBinding().getSuperclass(); while (superType != null) { fields= superType.getDeclaredFields(); for (int i= 0; i < fields.length; i++) { if (!Modifier.isPrivate(fields[i].getModifiers())) { excluded.add(fields[i].getName()); } } superType= superType.getSuperclass(); } ITypeBinding[] types= fContext.getTypeBinding().getDeclaredTypes(); for (int i= 0; i < types.length; i++) { excluded.add(types[i].getName()); } superType= fContext.getTypeBinding().getSuperclass(); while (superType != null) { types= superType.getDeclaredTypes(); for (int i= 0; i < types.length; i++) { if (!Modifier.isPrivate(types[i].getModifiers())) { excluded.add(types[i].getName()); } } superType= superType.getSuperclass(); } } return StubUtility.getVariableNameSuggestions(variableKind, fContext.getCompilationUnit().getJavaProject(), baseName, 0, excluded, true)[0]; }
Checks whether given type implements given interface
Params:
  • memberType – binding of the type to check
  • interfaceNames – fully qualified names of the interfaces to seek for
Returns:array of booleans, every element is set to true if interface at the same position in interfaceNames is implemented by memberType
/** * Checks whether given type implements given interface * * @param memberType binding of the type to check * @param interfaceNames fully qualified names of the interfaces to seek for * @return array of booleans, every element is set to true if interface at the same position in * <code>interfaceNames</code> is implemented by <code>memberType</code> */
protected boolean[] implementsInterfaces(ITypeBinding memberType, String[] interfaceNames) { boolean[] result= new boolean[interfaceNames.length]; for (int i= 0; i < interfaceNames.length; i++) { if (memberType.getQualifiedName().equals(interfaceNames[i])) result[i]= true; } ITypeBinding[] interfaces= memberType.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { boolean[] deeper= implementsInterfaces(interfaces[i].getErasure(), interfaceNames); for (int j= 0; j < interfaceNames.length; j++) { result[j]= result[j] || deeper[j]; } } return result; }
Params:
  • member – IVariableBinding or IMethodBinding representing a member
  • templateElement – the template element
Returns:the name of the member, with parenthesis at the end if the member is a method
/** * * @param member <code>IVariableBinding</code> or <code>IMethodBinding</code> representing a * member * @param templateElement the template element * @return the name of the member, with parenthesis at the end if the member is a method */
protected String getMemberName(Object member, String templateElement) { if (member instanceof IVariableBinding) { return ((IVariableBinding)member).getName(); } if (member instanceof IMethodBinding) { String result= ((IMethodBinding)member).getName(); if (templateElement == ToStringTemplateParser.MEMBER_NAME_PARENTHESIS_VARIABLE) result+= "()"; //$NON-NLS-1$ return result; } return null; }
Params:
  • member – member to check
Returns:type of field or method's return type
/** * * @param member member to check * @return type of field or method's return type */
protected ITypeBinding getMemberType(Object member) { if (member instanceof IVariableBinding) { return ((IVariableBinding)member).getType(); } if (member instanceof IMethodBinding) { return ((IMethodBinding)member).getReturnType(); } return null; } public void setContext(ToStringGenerationContext context) { fContext= context; fAst= fContext.getAST(); excluded= null; } public ToStringGenerationContext getContext() { return fContext; } }