| /******************************************************************************* |
| * 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 |
| * Red Hat Inc. - moved to jdt.core.manipulation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.codemanipulation.tostringgeneration; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationMessages; |
| |
| /** |
| * <p> |
| * A class used for parsing the toString() template and storing the results. |
| * </p> |
| * <p> |
| * The template is divided into four parts: |
| * <ul> |
| * <li><b>Beginning</b> - all the characters from the beginning of the template (inclusively) to the |
| * first occurrence of any <code>{$member.*}</code> variable (exclusively). This part can contain |
| * only <code>${object.*}</code> variables and is used to generate the beginning of the generated |
| * <code>String</code>.</li> |
| * <li><b>Body</b> - all the characters from the first occurrence of any <code>${member.*}</code> |
| * variable to the variable directly preceding the <code>${otherMembers}</code>, inclusively. The |
| * content of this part is used once for every processed member. All template variables are |
| * available (except for <code>${otherMembers}</code>). |
| * <li><b>Separator</b></li> - all the characters from the template variable directly preceding the |
| * <code>${otherMembers}</code> to the <code>${otherMembers}</code>, exclusively. This part does not |
| * contain template variables and is used to separate members in the generated <code>String</code>. |
| * <li><b>Ending</b> - all the characters from the occurrence of the <code>${otherMembers}</code> |
| * variable (exclusively) to the end of the template (inclusively). This part can also contain only |
| * <code>${object.*}</code> variables.</li> |
| * </ul> |
| * </p> |
| * <p> |
| * Note: for the template to work properly, it must contain at least one <code>${member.*}</code> |
| * variable and exactly one <code>${otherMembers}</code> variable. Additionally, no |
| * <code>${member.*}</code> variables can follow the <code>${otherMembers}</code> variable. |
| * </p> |
| * |
| * @since 3.5 |
| */ |
| public class ToStringTemplateParser { |
| protected String[] beginning; |
| |
| protected String[] body; |
| |
| protected String separator; |
| |
| protected String[] ending; |
| |
| protected Map<String, String> descriptions; |
| |
| /** The variable that inserts the name of the object's class **/ |
| public final static String OBJECT_NAME_VARIABLE= "${object.className}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts a call to <code>this.getClass.getName()</code> **/ |
| public final static String OBJECT_GET_NAME_VARIABLE= "${object.getClassName}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts a call to <code>super.toString()</code> **/ |
| public final static String OBJECT_SUPER_TOSTRING_VARIABLE= "${object.superToString}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts a call to <code>this.hashCode()</code> **/ |
| public final static String OBJECT_HASHCODE_VARIABLE= "${object.hashCode}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts a call to <code>System.identityHashCode(this)</code> **/ |
| public final static String OBJECT_SYSTEM_HASHCODE_VARIABLE= "${object.identityHashCode}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts the member name **/ |
| public final static String MEMBER_NAME_VARIABLE= "${member.name}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts the member name followed by parenthesis in case of methods **/ |
| public final static String MEMBER_NAME_PARENTHESIS_VARIABLE= "${member.name()}"; //$NON-NLS-1$ |
| |
| /** The variable that inserts the values of a member **/ |
| public final static String MEMBER_VALUE_VARIABLE= "${member.value}"; //$NON-NLS-1$ |
| |
| /** The variable used for determining separator between members **/ |
| private final static String OTHER_MEMBERS_VARIABLE= "${otherMembers}"; //$NON-NLS-1$ |
| |
| private final static String[] OBJECT_RELAED_VARIABLE= { OBJECT_NAME_VARIABLE, OBJECT_GET_NAME_VARIABLE, OBJECT_SUPER_TOSTRING_VARIABLE, OBJECT_HASHCODE_VARIABLE, OBJECT_SYSTEM_HASHCODE_VARIABLE }; |
| |
| private final static String[] MEMBER_RELATED_VARIABLE= { MEMBER_NAME_VARIABLE, MEMBER_NAME_PARENTHESIS_VARIABLE, MEMBER_VALUE_VARIABLE }; |
| |
| private final static String[] OBJECT_AND_MEMBER_RELATED_VARIABLES= { OBJECT_NAME_VARIABLE, OBJECT_GET_NAME_VARIABLE, OBJECT_SUPER_TOSTRING_VARIABLE, OBJECT_HASHCODE_VARIABLE, |
| OBJECT_SYSTEM_HASHCODE_VARIABLE, MEMBER_NAME_VARIABLE, MEMBER_NAME_PARENTHESIS_VARIABLE, MEMBER_VALUE_VARIABLE }; |
| |
| /** A default template for toString() format **/ |
| public final static String DEFAULT_TEMPLATE= "${object.className} [${member.name()}=${member.value}, ${otherMembers}]"; //$NON-NLS-1$ |
| |
| /** Variables that can be used in toSting() format template **/ |
| private final static String[] VARIABLES= { |
| OBJECT_NAME_VARIABLE, |
| OBJECT_GET_NAME_VARIABLE, |
| OBJECT_SUPER_TOSTRING_VARIABLE, |
| OBJECT_HASHCODE_VARIABLE, |
| OBJECT_SYSTEM_HASHCODE_VARIABLE, |
| MEMBER_NAME_VARIABLE, |
| MEMBER_NAME_PARENTHESIS_VARIABLE, |
| MEMBER_VALUE_VARIABLE, |
| OTHER_MEMBERS_VARIABLE }; |
| |
| /** Descriptions of variables in toString format template **/ |
| private final static String[] VARIABLE_DESCRIPTIONS= { |
| CodeGenerationMessages.GenerateToStringOperation_objectClassNameVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_objectClassGetNameVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_objectSuperToStringVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_objectHashCodeVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_objectIdentityHashCodeVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_memberNameVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_memberNameParenthesesVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_memberValueVariableDescription, |
| CodeGenerationMessages.GenerateToStringOperation_otherFieldsVariableDescription }; |
| |
| /** |
| * This method is used in {@link #parseTemplate(String)} to determine what member specific |
| * variables are expected in a template. |
| * |
| * @return member related variables recognized by this parser |
| */ |
| protected String[] getMemberRelatedVariables() { |
| return ToStringTemplateParser.MEMBER_RELATED_VARIABLE; |
| } |
| |
| /** |
| * This method is used in {@link #parseTemplate(String)} to determine what object related |
| * variables are expected in a template. |
| * |
| * @return object related variables recognized by this parser |
| */ |
| protected String[] getObjectRelatedVariables() { |
| return ToStringTemplateParser.OBJECT_RELAED_VARIABLE; |
| } |
| |
| /** |
| * This method is used in {@link #parseTemplate(String)}. It returns all variables returned by |
| * {@link #getMemberRelatedVariables()} and {@link #getObjectRelatedVariables()} (sum of sets). |
| * |
| * @return member and object related variables recognized by this parser (all variables but |
| * {$otherMembers}) |
| */ |
| protected String[] getObjectAndMemberRelatedVariables() { |
| return ToStringTemplateParser.OBJECT_AND_MEMBER_RELATED_VARIABLES; |
| } |
| |
| public void parseTemplate(String template) { |
| String[] emptyArray= new String[0]; |
| int beginningEnd= firstOccuranceOf(template, getMemberRelatedVariables()); |
| if (beginningEnd >= 0) { |
| beginning= extractElements(template.substring(0, beginningEnd), getObjectRelatedVariables()).toArray(emptyArray); |
| } else { |
| beginningEnd= 0; |
| beginning= emptyArray; |
| } |
| int endingStart= template.indexOf(ToStringTemplateParser.OTHER_MEMBERS_VARIABLE); |
| if (endingStart == -1) |
| endingStart= template.length(); |
| |
| ArrayList<String> bodyList= extractElements(template.substring(beginningEnd, endingStart), getObjectAndMemberRelatedVariables()); |
| |
| try { |
| separator= bodyList.get(bodyList.size() - 1); |
| bodyList.remove(bodyList.size() - 1); |
| } catch (Exception e) { |
| separator= ""; //$NON-NLS-1$ |
| } |
| |
| body= bodyList.toArray(emptyArray); |
| ending= extractElements(template.substring(endingStart + ToStringTemplateParser.OTHER_MEMBERS_VARIABLE.length()), getObjectRelatedVariables()).toArray(emptyArray); |
| |
| } |
| |
| protected int firstOccuranceOf(String template, String[] wantedVariables) { |
| int result= -1; |
| for (int i= 0; i < wantedVariables.length; i++) { |
| int indexOf= template.indexOf(wantedVariables[i]); |
| if (result == -1 || (indexOf > 0 && indexOf < result)) |
| result= indexOf; |
| } |
| return result; |
| } |
| |
| protected ArrayList<String> extractElements(String template, String[] wantedVariables) { |
| ArrayList<String> result= new ArrayList<>(); |
| while (true) { |
| if (template.length() == 0) |
| break; |
| String foundVariable= null; |
| int variablePosition= template.length(); |
| for (int i= 0; i < wantedVariables.length; i++) { |
| int position= template.indexOf(wantedVariables[i]); |
| if (position >= 0 && position < variablePosition) { |
| variablePosition= position; |
| foundVariable= wantedVariables[i]; |
| } |
| } |
| if (variablePosition == template.length()) { |
| result.add(template); |
| break; |
| } else { |
| if (variablePosition != 0) |
| result.add(template.substring(0, variablePosition)); |
| result.add(foundVariable); |
| template= template.substring(variablePosition + foundVariable.length()); |
| } |
| } |
| return result; |
| } |
| |
| public String[] getBeginning() { |
| return beginning; |
| } |
| |
| public String[] getBody() { |
| return body; |
| } |
| |
| public String[] getEnding() { |
| return ending; |
| } |
| |
| public String getSeparator() { |
| return separator; |
| } |
| |
| public Map<String, String> getVariableDescriptions() { |
| if (descriptions == null) { |
| descriptions= new HashMap<>(); |
| for (int i= 0; i < ToStringTemplateParser.VARIABLES.length; i++) |
| descriptions.put(ToStringTemplateParser.VARIABLES[i], ToStringTemplateParser.VARIABLE_DESCRIPTIONS[i]); |
| } |
| return descriptions; |
| } |
| |
| public String[] getVariables() { |
| return ToStringTemplateParser.VARIABLES; |
| } |
| } |