]> git.uio.no Git - ifi-stolz-refaktor.git/blobdiff - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/codemanipulation/tostringgeneration/AbstractToStringGenerator.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / codemanipulation / tostringgeneration / AbstractToStringGenerator.java
diff --git a/case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/codemanipulation/tostringgeneration/AbstractToStringGenerator.java b/case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/codemanipulation/tostringgeneration/AbstractToStringGenerator.java
new file mode 100644 (file)
index 0000000..0bafd57
--- /dev/null
@@ -0,0 +1,854 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 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] Generator uses wrong suffixes and prefixes - https://bugs.eclipse.org/bugs/show_bug.cgi?id=275370
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration;
+
+import java.util.ArrayList;
+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.InstanceofExpression;
+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.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.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.dom.InfixExpression.Operator;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+
+import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
+
+/**
+ * <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 <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 */
+       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.
+        * 
+        * @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<MethodDeclaration>();
+               if (needCollectionToStringMethod)
+                       result.add(createHelperToStringMethod(false));
+
+               if (!typesThatNeedArrayToStringMethod.isEmpty())
+                       result.add(createHelperToStringMethod(true));
+
+               return result;
+       }
+
+       /**
+        * 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];
+               }
+               fContext.generated_5960924765249646172(this, objectMethod);
+       }
+
+       /**
+        * 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 <code>toString</code>
+        * method.
+        */
+       protected void initialize() {
+               needMaxLenVariable= false;
+               needCollectionToStringMethod= false;
+               typesThatNeedArrayToStringMethod= new ArrayList<ITypeBinding>();
+
+               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 <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.
+        */
+       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];
+                       isNonPrimitive= fContext.generated_4381819252754149304(this, isNonPrimitive, memberType, implementsInterfaces, isCollection);
+               }
+               if (!typesThatNeedArrayToStringMethod.isEmpty() && isNonPrimitive)
+                       typesThatNeedArrayToStringMethod.add(fAst.resolveWellKnownType("java.lang.Object")); //$NON-NLS-1$
+       }
+
+       /**
+        * 
+        * @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;
+               result= fContext.generated_6573513055157508799(this, templateElement, result);
+               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 <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 <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 <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
+        * 
+        * @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
+        * 
+        * @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
+        * 
+        * @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;
+       }
+
+       /**
+        * @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;
+       }
+
+       /**
+        * @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;
+                               }
+                       }
+               }
+               return fContext.generated_2098369527780276835(this, member);
+       }
+
+       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.
+        * 
+        * @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);
+       }
+       
+       Set<String> excluded;
+       protected String createNameSuggestion(String baseName, int variableKind) {
+               return fContext.generated_907652555048609483(this, baseName, variableKind);
+       }
+
+       /**
+        * 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;
+       }
+
+       /**
+        * 
+        * @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;
+       }
+
+       /**
+        * 
+        * @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;
+       }
+
+}