| /* |
| * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation |
| // |
| package org.eclipse.persistence.jpa.jpql.tools; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.eclipse.persistence.jpa.jpql.ExpressionTools; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseChildrenVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.EntityTypeLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.Expression; |
| import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLStatementBNF; |
| import org.eclipse.persistence.jpa.jpql.parser.ResultVariable; |
| import org.eclipse.persistence.jpa.jpql.parser.SelectStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; |
| import org.eclipse.persistence.jpa.jpql.tools.resolver.Resolver; |
| import org.eclipse.persistence.jpa.jpql.tools.resolver.StateFieldResolver; |
| import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider; |
| import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping; |
| import org.eclipse.persistence.jpa.jpql.tools.spi.IQuery; |
| import org.eclipse.persistence.jpa.jpql.tools.spi.IType; |
| |
| /** |
| * The abstract implementation providing refactoring support for JPQL queries. This version does not |
| * change the {@link org.eclipse.persistence.jpa.jpql.tools.model.query.JPQLQueryStateObject query} but |
| * rather gather the changes in {@link RefactoringDelta} and it is the responsibility of the invoker |
| * to the actual change. |
| * <p> |
| * Provisional API: This interface is part of an interim API that is still under development and |
| * expected to change significantly before reaching stability. It is available at this early stage |
| * to solicit feedback from pioneering adopters on the understanding that any code that uses this |
| * API will almost certainly be broken (repeatedly) as the API evolves. |
| * |
| * @see DefaultBasicRefactoringTool |
| * @see EclipseLinkBasicRefactoringTool |
| * |
| * @version 2.6 |
| * @since 2.4 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings("nls") |
| public abstract class BasicRefactoringTool extends AbstractRefactoringTool { |
| |
| /** |
| * Keeps track of the changes made to the JPQL query. |
| */ |
| private DefaultRefactoringDelta delta; |
| |
| /** |
| * The parsed tree representation of the JPQL query. |
| */ |
| private JPQLExpression jpqlExpression; |
| |
| /** |
| * The {@link JPQLGrammar} that was used to parse the JPQL query or JPQL fragments. |
| */ |
| private JPQLGrammar jpqlGrammar; |
| |
| /** |
| * The context used to query information about the JPQL query. |
| */ |
| private JPQLQueryContext queryContext; |
| |
| /** |
| * Creates a new <code>BasicRefactoringTool</code>. |
| * |
| * @param jpqlQuery The JPQL query to manipulate |
| * @param jpqlGrammar The {@link JPQLGrammar} that was used to parse the JPQL query |
| * @param managedTypeProvider The external form of a provider that gives access to the JPA metadata |
| */ |
| protected BasicRefactoringTool(CharSequence jpqlQuery, |
| JPQLGrammar jpqlGrammar, |
| IManagedTypeProvider managedTypeProvider) { |
| |
| this(jpqlQuery, jpqlGrammar, managedTypeProvider, JPQLStatementBNF.ID); |
| } |
| |
| /** |
| * Creates a new <code>BasicRefactoringTool</code>. |
| * |
| * @param jpqlFragment The JPQL query to manipulate or a single JPQL fragment, which is parsed |
| * using the JPQL query BNF identifier by the given ID |
| * @param jpqlGrammar The {@link JPQLGrammar} that was used to parse the JPQL fragment |
| * @param managedTypeProvider The external form of a provider that gives access to the JPA metadata |
| * @param jpqlQueryBNFId The unique identifier of the {@link |
| * org.eclipse.persistence.jpa.jpql.parser.JPQLQueryBNF JPQLQueryBNF} that determines how to parse the JPQL fragment |
| */ |
| protected BasicRefactoringTool(CharSequence jpqlFragment, |
| JPQLGrammar jpqlGrammar, |
| IManagedTypeProvider managedTypeProvider, |
| String jpqlQueryBNFId) { |
| |
| super(jpqlFragment, managedTypeProvider, jpqlQueryBNFId); |
| this.jpqlGrammar = jpqlGrammar; |
| this.delta = new DefaultRefactoringDelta(jpqlFragment); |
| } |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename a type's attribute name. |
| * |
| * @param typeName The fully qualified name of the type that got one of its attributes renamed |
| * @param oldAttributeName The current name of the attribute to rename |
| * @param newAttributeName The new name of the attribute |
| * @return A new {@link AttributeNameRenamer} |
| */ |
| protected AttributeNameRenamer buildAttributeNameRenamer(String typeName, |
| String oldAttributeName, |
| String newAttributeName) { |
| |
| return new AttributeNameRenamer(getQueryContext(), typeName, oldAttributeName, newAttributeName); |
| } |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename the fully qualified class name. |
| * |
| * @param oldClassName The current name of the class to rename |
| * @param newClassName The new name of the class |
| * @return A new {@link ClassNameRenamer} |
| */ |
| protected ClassNameRenamer buildClassNameRenamer(String oldClassName, String newClassName) { |
| return new ClassNameRenamer(oldClassName, newClassName); |
| } |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename the entity name. |
| * |
| * @param oldEntityName The current name of the entity to rename |
| * @param newEntityName The new name of the entity |
| * @return A new {@link EntityNameRenamer} |
| */ |
| protected EntityNameRenamer buildEntityNameRenamer(String oldEntityName, String newEntityName) { |
| return new EntityNameRenamer(oldEntityName, newEntityName); |
| } |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename the enum constant. |
| * |
| * @param oldClassName The new name of the enum constant |
| * @param newClassName The current name of the enum constant to rename |
| * @return A new {@link EnumConstantRenamer} |
| */ |
| protected EnumConstantRenamer buildEnumConstantRenamer(String oldClassName, String newClassName) { |
| return new EnumConstantRenamer(getManagedTypeProvider(), oldClassName, newClassName); |
| } |
| |
| /** |
| * Creates a new {@link JPQLQueryContext} that can retrieve information from the declaration |
| * portion of the JPQL query. |
| * |
| * @return A new concrete instance of {@link JPQLQueryContext} |
| */ |
| protected abstract JPQLQueryContext buildJPQLQueryContext(); |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename a result variable. |
| * |
| * @param oldVariableName The current result variable name |
| * @param newVariableName The new name of the result variable |
| * @return A new {@link ResultVariableNameRenamer} |
| */ |
| protected ResultVariableNameRenamer buildResultVariableNameRenamer(String oldVariableName, |
| String newVariableName) { |
| |
| return new ResultVariableNameRenamer(oldVariableName, newVariableName); |
| } |
| |
| /** |
| * Creates the visitor that will traverse the {@link } representation of the JPQL |
| * query and will rename an identification variable. |
| * |
| * @param oldVariableName The current identification variable name |
| * @param newVariableName The new name of the identification variable |
| * @return A new {@link VariableNameRenamer} |
| */ |
| protected VariableNameRenamer buildVariableNameRenamer(String oldVariableName, |
| String newVariableName) { |
| |
| return new VariableNameRenamer(oldVariableName, newVariableName); |
| } |
| |
| /** |
| * Returns the delta of the changes made to the JPQL query. |
| * |
| * @return An object containing the refactoring events |
| */ |
| public RefactoringDelta getDelta() { |
| return delta; |
| } |
| |
| /** |
| * Returns the parsed tree representation of the JPQL query. |
| * |
| * @return The root of the parsed tree |
| */ |
| public JPQLExpression getExpression() { |
| if (jpqlExpression == null) { |
| jpqlExpression = new JPQLExpression(getJPQLFragment(), jpqlGrammar, isTolerant()); |
| } |
| return jpqlExpression; |
| } |
| |
| /** |
| * Returns the {@link JPQLGrammar} that is associated with this builder. |
| * |
| * @return The {@link JPQLGrammar} that was used to parse the JPQL query or JPQL fragments |
| */ |
| public JPQLGrammar getGrammar() { |
| return jpqlGrammar; |
| } |
| |
| /** |
| * Returns the {@link JPQLQueryContext} that is used by this visitor. |
| * |
| * @return The {@link JPQLQueryContext} holding onto the JPQL query and the cached information |
| */ |
| public JPQLQueryContext getQueryContext() { |
| if (queryContext == null) { |
| queryContext = buildJPQLQueryContext(); |
| queryContext.setJPQLExpression(getExpression()); |
| queryContext.setQuery(new JavaQuery(getManagedTypeProvider(), getJPQLFragment())); |
| } |
| return queryContext; |
| } |
| |
| /** |
| * Determines whether some refactoring operations found changes to be made in the JPQL query. |
| * |
| * @return <code>true</code> if there is at least one {@link TextEdit}; <code>false</code> otherwise |
| */ |
| public boolean hasChanges() { |
| return delta.hasTextEdits(); |
| } |
| |
| /** |
| * Renames the attribute (persistent field or persistent property) from the given type. |
| * |
| * @param type The Java class from which the change originate |
| * @param oldAttributeName The current name of the attribute to rename |
| * @param newAttributeName The new name of the attribute |
| */ |
| public void renameAttribute(Class<?> type, String oldAttributeName, String newAttributeName) { |
| renameAttribute(type.getName(), oldAttributeName, newAttributeName); |
| } |
| |
| /** |
| * Renames the attribute (persistent field or persistent property) from the given type. |
| * |
| * @param type The {@link IType} from which the change originate |
| * @param oldAttributeName The current name of the attribute to rename |
| * @param newAttributeName The new name of the attribute |
| */ |
| public void renameAttribute(IType type, String oldAttributeName, String newAttributeName) { |
| renameAttribute(type.getName(), oldAttributeName, newAttributeName); |
| } |
| |
| /** |
| * Renames the attribute (persistent field or persistent property) from the given type. |
| * |
| * @param typeName The fully qualified name of the type that got one of its attributes renamed |
| * @param oldAttributeName The current name of the attribute to rename |
| * @param newAttributeName The new name of the attribute |
| */ |
| public void renameAttribute(String typeName, String oldAttributeName, String newAttributeName) { |
| AbstractRenamer renamer = buildAttributeNameRenamer(typeName, oldAttributeName, newAttributeName); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| /** |
| * Renames a fully qualified class name. |
| * |
| * @param oldClassName The current fully qualified class name of the class to rename |
| * @param newClassName The new fully qualified class name |
| */ |
| public void renameClassName(String oldClassName, String newClassName) { |
| ClassNameRenamer renamer = buildClassNameRenamer(oldClassName, newClassName); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| /** |
| * Renames a given entity name. |
| * |
| * @param oldEntityName The current name of the entity to rename |
| * @param newEntityName The new name of the entity |
| */ |
| public void renameEntityName(String oldEntityName, String newEntityName) { |
| AbstractRenamer renamer = buildEntityNameRenamer(oldEntityName, newEntityName); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| /** |
| * Renames an enum constant, which has to be fully qualified. |
| * |
| * @param oldEnumConstant The current fully qualified name of the enum constant to rename |
| * @param newEnumConstant The new fully qualified name of the enum constant |
| */ |
| public void renameEnumConstant(String oldEnumConstant, String newEnumConstant) { |
| AbstractRenamer renamer = buildEnumConstantRenamer(oldEnumConstant, newEnumConstant); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| /** |
| * Renames a result variable name. |
| * |
| * @param oldVariableName The current identification variable name |
| * @param newVariableName The new name of the identification variable |
| */ |
| public void renameResultVariable(String oldVariableName, String newVariableName) { |
| AbstractRenamer renamer = buildResultVariableNameRenamer(oldVariableName, newVariableName); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| /** |
| * Renames a variable name. |
| * |
| * @param oldVariableName The current identification variable name |
| * @param newVariableName The new name of the identification variable |
| */ |
| public void renameVariable(String oldVariableName, String newVariableName) { |
| AbstractRenamer renamer = buildVariableNameRenamer(oldVariableName, newVariableName); |
| getExpression().accept(renamer); |
| delta.addTextEdits(renamer.textEdits); |
| } |
| |
| @Override |
| public String toActualText() { |
| return getDelta().applyChanges(); |
| } |
| |
| /** |
| * The abstract class that all refactoring classes should extend, it automatically provides the |
| * <a href="http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Ftext%2Fedits%2FMultiTextEdit.html">MultiTextEdit</a> |
| * that will hold the {@link TextEdit} objects that are related to the same refactoring event. |
| */ |
| protected abstract class AbstractRenamer extends AbstractTraverseChildrenVisitor { |
| |
| /** |
| * The list of {@link TextEdit} objects that were created for each refactoring operation. |
| */ |
| protected List<TextEdit> textEdits; |
| |
| /** |
| * Creates a new <code>AbstractRenamer</code>. |
| */ |
| protected AbstractRenamer() { |
| super(); |
| textEdits = new ArrayList<>(); |
| } |
| |
| /** |
| * Adds a new {@link TextEdit} with the given information. |
| * |
| * @param expression The {@link Expression} which should be refactored, it will be used to |
| * retrieve the offset of the change |
| * @param extraOffset Additional offset that will be added to the given {@link Expression}'s |
| * offset, which is the length of the string representation of what is before it |
| * @param oldValue The old value to change to the new one |
| * @param newValue The new value |
| */ |
| protected void addTextEdit(Expression expression, int extraOffset, String oldValue, String newValue) { |
| TextEdit textEdit = buildTextEdit( |
| reposition(expression.getOffset() + extraOffset), |
| oldValue, |
| newValue |
| ); |
| textEdits.add(textEdit); |
| } |
| |
| /** |
| * Adds a new {@link TextEdit} with the given information. |
| * |
| * @param expression The {@link Expression} which should be refactored, it will be used to |
| * retrieve the offset of the change |
| * @param oldValue The old value to change to the new one |
| * @param newValue The new value |
| */ |
| protected void addTextEdit(Expression expression, String oldValue, String newValue) { |
| addTextEdit(expression, 0, oldValue, newValue); |
| } |
| |
| /** |
| * Creates a new {@link TextEdit} for the given refactoring information. |
| * |
| * @param offset The position where the change should be made within the actual JPQL fragment |
| * @param oldValue The old value to change to the new one |
| * @param newValue The new value |
| * @return A new {@link TextEdit} |
| */ |
| protected TextEdit buildTextEdit(int offset, String oldValue, String newValue) { |
| return new DefaultTextEdit(offset, oldValue, newValue); |
| } |
| |
| /** |
| * Repositions the given position that is based on the generated JPQL query to be the position |
| * from the JPQL fragment that was parsed. |
| * |
| * @param offset The position within the string generated by {@link Expression#toActualText()} |
| * @return The position within the JPQL fragment that was passed to the refactoring tool |
| */ |
| protected int reposition(int offset) { |
| return ExpressionTools.repositionCursor( |
| getExpression().toActualText(), |
| offset, |
| getJPQLFragment() |
| ); |
| } |
| } |
| |
| /** |
| * This visitor renames any segment of a path expression. |
| */ |
| protected class AttributeNameRenamer extends AbstractRenamer { |
| |
| /** |
| * The new name of the attribute. |
| */ |
| protected final String newAttributeName; |
| |
| /** |
| * The current name of the attribute to rename. |
| */ |
| protected final String oldAttributeName; |
| |
| /** |
| * The context used to query information about the JPQL query. |
| */ |
| private final JPQLQueryContext queryContext; |
| |
| /** |
| * The fully qualified name of the type that got one of its attributes renamed. |
| */ |
| protected final String typeName; |
| |
| /** |
| * Creates a new <code>AttributeNameRenamer</code>. |
| * |
| * @param queryContext The context used to query information about the JPQL query |
| * @param typeName The fully qualified name of the type that got one of its attributes renamed |
| * @param oldAttributeName The current name of the attribute to rename |
| * @param newAttributeName The new name of the attribute |
| */ |
| public AttributeNameRenamer(JPQLQueryContext queryContext, |
| String typeName, |
| String oldAttributeName, |
| String newAttributeName) { |
| |
| super(); |
| this.typeName = typeName; |
| this.queryContext = queryContext; |
| this.oldAttributeName = oldAttributeName; |
| this.newAttributeName = newAttributeName; |
| } |
| |
| /** |
| * Performs the rename on the path expression. |
| * |
| * @param expression The {@link AbstractPathExpression} being visited, which may have to have |
| * its path renamed |
| */ |
| protected void rename(AbstractPathExpression expression) { |
| |
| if (!expression.hasIdentificationVariable() || |
| expression.startsWithDot()) { |
| |
| return; |
| } |
| |
| Resolver resolver = queryContext.getResolver(expression.getIdentificationVariable()); |
| |
| // Can't continue to resolve the path expression |
| if (resolver == null) { |
| return; |
| } |
| |
| // Now traverse the path expression after the identification variable |
| for (int index = expression.hasVirtualIdentificationVariable() ? 0 : 1, count = expression.pathSize(); index < count; index++) { |
| |
| // Retrieve the mapping for the path at the current position |
| String path = expression.getPath(index); |
| Resolver childResolver = resolver.getChild(path); |
| |
| if (childResolver == null) { |
| childResolver = new StateFieldResolver(resolver, path); |
| resolver.addChild(path, childResolver); |
| } |
| |
| IMapping mapping = childResolver.getMapping(); |
| |
| // Invalid path expression |
| if (mapping == null) { |
| break; |
| } |
| |
| // The name matches |
| if (mapping.getName().equals(oldAttributeName)) { |
| |
| // Make sure the field name is from the right type |
| String parentTypeName = mapping.getParent().getType().getName(); |
| |
| if (parentTypeName.equals(typeName)) { |
| int extraOffset = expression.toParsedText(0, index).length() + 1 /* '.' */; |
| addTextEdit(expression, extraOffset, oldAttributeName, newAttributeName); |
| break; |
| } |
| } |
| |
| resolver = childResolver; |
| } |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| rename(expression); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| rename(expression); |
| } |
| } |
| |
| /** |
| * This visitor renames a fully qualified class name. |
| */ |
| protected class ClassNameRenamer extends AbstractRenamer { |
| |
| /** |
| * The current name of the class to rename. |
| */ |
| protected final String newClassName; |
| |
| /** |
| * The new name of the class. |
| */ |
| protected final String oldClassName; |
| |
| /** |
| * Creates a new <code>ClassNameRenamer</code>. |
| * |
| * @param oldClassName The current name of the class to rename |
| * @param newClassName The new name of the class |
| */ |
| public ClassNameRenamer(String oldClassName, String newClassName) { |
| super(); |
| this.oldClassName = oldClassName; |
| this.newClassName = newClassName; |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| // Test for a fully qualified class name in a range variable declaration |
| if (!expression.startsWithDot()) { |
| visit(expression, expression.toActualText(), 0); |
| } |
| } |
| |
| @Override |
| public void visit(ConstructorExpression expression) { |
| visit(expression, expression.getClassName(), 4); // 4 = "NEW " |
| } |
| |
| /** |
| * Visits the given {@link } and if its value is the same as the old class name or |
| * if the value represents an inner class of that old class name, then the given {@link |
| * org.eclipse.persistence.jpa.jpql.tools.RefactoringTool.StateObjectUpdater StateObjectUpdater} |
| * will be notified to replace the value. |
| * |
| * @param expression The {@link } that is being visited |
| * @param extraOffset Additional offset that will be added to the given {@link Expression}'s |
| * offset, which is the length of the string representation of what is before it |
| * @param value The value to check if it's the old class name |
| */ |
| protected void visit(Expression expression, String value, int extraOffset) { |
| |
| if (oldClassName.equals(value)) { |
| addTextEdit(expression, extraOffset, oldClassName, newClassName); |
| } |
| else { |
| int index = value.lastIndexOf(AbstractExpression.DOT); |
| |
| // Traverse the value by retrieving a fragment up to the last dot (based on the index) |
| for (; index > -1; index = value.lastIndexOf(AbstractExpression.DOT, index - 1)) { |
| |
| // Retrieve the fragment from the beginning to the current dot |
| String fragment = value.substring(0, index); |
| |
| if (oldClassName.equals(fragment)) { |
| addTextEdit(expression, extraOffset, oldClassName, newClassName); |
| break; |
| } |
| // No need to continue the search |
| else if (fragment.length() < oldClassName.length()) { |
| break; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| // A fully qualified enum constant is parsed as a state field path expression |
| if (!expression.startsWithDot()) { |
| visit(expression, expression.toActualText(), 0); |
| } |
| } |
| } |
| |
| /** |
| * This visitor renames an entity name. There are three possible {@link |
| * org.eclipse.persistence.jpa.jpql.tools.model.query.StateObject StateObjects} |
| * that can represent an entity name: |
| * <ul> |
| * <li> |
| * {@link org.eclipse.persistence.jpa.jpql.tools.model.query.AbstractSchemaNameStateObject AbstractSchemaNameStateObject}: |
| * <i>Employee</i> in<br><br> |
| * <code><b>SELECT</b> e<br> |
| * <b>FROM</b> Employee e</code><br><br> |
| * </li> |
| * <li> |
| * {@link org.eclipse.persistence.jpa.jpql.tools.model.query.EntityTypeLiteralStateObject EntityTypeLiteralStateObject}: |
| * <i>Exempt</i> in<br><br> |
| * <pre><code><b> SELECT CASE TYPE</b>(e) <b>WHEN</b> Exempt <b>THEN</b> 'Exempt' |
| * <b>ELSE</b> 'NONE' |
| * <b>END</b> |
| * <b>FROM</b> Employee e</code></pre> |
| * </li> |
| * <li> |
| * {@link org.eclipse.persistence.jpa.jpql.tools.model.query.IdentificationVariableStateObject IdentificationVariableStateObject}: |
| * <i>Exempt</i> in<br> |
| * <pre><code> <b>SELECT</b> e |
| * <b>FROM</b> Employee e |
| * <b>WHERE TYPE</b>(e) {@literal <>} Exempt</code></pre> |
| * </li> |
| * </ul> |
| */ |
| protected class EntityNameRenamer extends AbstractRenamer { |
| |
| /** |
| * The current name of the entity to rename. |
| */ |
| protected final String newEntityName; |
| |
| /** |
| * The new name of the entity. |
| */ |
| protected final String oldEntityName; |
| |
| /** |
| * Creates a new <code>EntityNameRenamer</code>. |
| * |
| * @param oldEntityName The current name of the entity to rename |
| * @param newEntityName The new name of the entity |
| */ |
| public EntityNameRenamer(String oldEntityName, String newEntityName) { |
| super(); |
| this.oldEntityName = oldEntityName; |
| this.newEntityName = newEntityName; |
| } |
| |
| @Override |
| public void visit(AbstractSchemaName expression) { |
| if (oldEntityName.equals(expression.getText())) { |
| addTextEdit(expression, oldEntityName, newEntityName); |
| } |
| } |
| |
| @Override |
| public void visit(EntityTypeLiteral expression) { |
| if (oldEntityName.equals(expression.getEntityTypeName())) { |
| addTextEdit(expression, oldEntityName, newEntityName); |
| } |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| if (oldEntityName.equals(expression.getText())) { |
| addTextEdit(expression, oldEntityName, newEntityName); |
| } |
| } |
| } |
| |
| /** |
| * This visitor renames an enum constant. An enum constant is represented by a path expression. |
| */ |
| protected class EnumConstantRenamer extends AbstractRenamer { |
| |
| /** |
| * The external form of a provider that gives access to the JPA metadata. |
| */ |
| protected final IManagedTypeProvider managedTypeProvider; |
| |
| /** |
| * The current name of the enum constant to rename. |
| */ |
| protected final String newEnumConstant; |
| |
| /** |
| * The new name of the enum constant. |
| */ |
| protected final String oldEnumConstant; |
| |
| /** |
| * Creates a new <code>ClassNameRenamer</code>. |
| * |
| * @param managedTypeProvider The provider of managed types |
| * @param oldEnumConstant The new name of the enum constant |
| * @param newEnumConstant The current name of the enum constant to rename |
| */ |
| public EnumConstantRenamer(IManagedTypeProvider managedTypeProvider, |
| String oldEnumConstant, |
| String newEnumConstant) { |
| |
| super(); |
| this.oldEnumConstant = oldEnumConstant; |
| this.newEnumConstant = newEnumConstant; |
| this.managedTypeProvider = managedTypeProvider; |
| } |
| |
| protected void renameEnumConstant(AbstractPathExpression expression) { |
| |
| String path = expression.toString(); |
| |
| if (path.equals(oldEnumConstant)) { |
| |
| // Check to see if the path is actually a fully qualified enum constant |
| IType type = managedTypeProvider.getTypeRepository().getEnumType(path); |
| |
| // If it is not null, then it's a fully qualified enum constant |
| if (type != null) { |
| addTextEdit(expression, oldEnumConstant, newEnumConstant); |
| } |
| } |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| renameEnumConstant(expression); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| renameEnumConstant(expression); |
| } |
| } |
| |
| /** |
| * A simple implementation of {@link IQuery}. |
| */ |
| protected static class JavaQuery implements IQuery { |
| |
| /** |
| * The string representation of the JPQL query. |
| */ |
| private String jpqlQuery; |
| |
| /** |
| * The provider of JPA managed types. |
| */ |
| private IManagedTypeProvider provider; |
| |
| /** |
| * Creates a new <code>JavaQuery</code>. |
| * |
| * @param provider The provider of JPA managed types |
| * @param jpqlQuery The string representation of the JPQL query |
| */ |
| public JavaQuery(IManagedTypeProvider provider, CharSequence jpqlQuery) { |
| super(); |
| this.provider = provider; |
| setExpression(jpqlQuery); |
| } |
| |
| @Override |
| public String getExpression() { |
| return jpqlQuery; |
| } |
| |
| @Override |
| public IManagedTypeProvider getProvider() { |
| return provider; |
| } |
| |
| /** |
| * Sets the string representation of the JPQL query. |
| * |
| * @param jpqlQuery The string representation of the JPQL query |
| */ |
| public void setExpression(CharSequence jpqlQuery) { |
| this.jpqlQuery = (jpqlQuery != null) ? jpqlQuery.toString() : ExpressionTools.EMPTY_STRING; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("JPQL query=["); |
| sb.append(jpqlQuery); |
| sb.append("]"); |
| return sb.toString(); |
| } |
| } |
| |
| /** |
| * This visitor renames all the result variables found in the JPQL query. |
| */ |
| protected class ResultVariableNameRenamer extends AbstractRenamer { |
| |
| /** |
| * The new name of the result variable. |
| */ |
| protected final String newVariableName; |
| |
| /** |
| * The current result variable name. |
| */ |
| protected final String oldVariableName; |
| |
| /** |
| * Makes sure an identification variable is renamed only when it's used by an order by item. |
| */ |
| protected boolean renameIdentificationVariable; |
| |
| /** |
| * Creates a new <code>ResultVariableNameRenamer</code>. |
| * |
| * @param oldVariableName The current result variable name |
| * @param newVariableName The new name of the result variable |
| */ |
| public ResultVariableNameRenamer(String oldVariableName, String newVariableName) { |
| super(); |
| this.oldVariableName = oldVariableName; |
| this.newVariableName = newVariableName; |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| |
| if (renameIdentificationVariable && |
| oldVariableName.equalsIgnoreCase(expression.getText())) { |
| |
| addTextEdit(expression, oldVariableName, newVariableName); |
| } |
| } |
| |
| @Override |
| public void visit(ResultVariable expression) { |
| |
| if (expression.hasResultVariable()) { |
| renameIdentificationVariable = true; |
| try { |
| expression.getResultVariable().accept(this); |
| } |
| finally { |
| renameIdentificationVariable = false; |
| } |
| } |
| } |
| |
| @Override |
| public void visit(SelectStatement expression) { |
| |
| // Result variables defined in the SELECT clause |
| expression.getSelectClause().accept(this); |
| |
| // Result variables used in the ORDER BY clause |
| if (expression.hasOrderByClause()) { |
| renameIdentificationVariable = true; |
| try { |
| expression.getOrderByClause().accept(this); |
| } |
| finally { |
| renameIdentificationVariable = false; |
| } |
| } |
| } |
| } |
| |
| /** |
| * This visitor renames all the identification variables found in the JPQL query. |
| */ |
| protected class VariableNameRenamer extends AbstractRenamer { |
| |
| /** |
| * The new name of the identification variable. |
| */ |
| protected final String newVariableName; |
| |
| /** |
| * The current identification variable name. |
| */ |
| protected final String oldVariableName; |
| |
| /** |
| * Creates a new <code>VariableNameRenamer</code>. |
| * |
| * @param oldVariableName The current identification variable name |
| * @param newVariableName The new name of the identification variable |
| */ |
| public VariableNameRenamer(String oldVariableName, String newVariableName) { |
| super(); |
| this.oldVariableName = oldVariableName; |
| this.newVariableName = newVariableName; |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| if (oldVariableName.equalsIgnoreCase(expression.getText())) { |
| addTextEdit(expression, oldVariableName, newVariableName); |
| } |
| } |
| } |
| } |