blob: 359f42b2b75650fa40998bf6c222b02e515e21e6 [file] [log] [blame]
/*
* Copyright (c) 2011, 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.model.query;
import java.io.IOException;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.persistence.jpa.jpql.parser.NewValueBNF;
import org.eclipse.persistence.jpa.jpql.parser.UpdateItem;
import org.eclipse.persistence.jpa.jpql.tools.model.INewValueStateObjectBuilder;
import org.eclipse.persistence.jpa.jpql.utility.iterable.ListIterable;
import static org.eclipse.persistence.jpa.jpql.parser.AbstractExpression.*;
/**
* The <code>new_value</code> specified for an update operation must be compatible in type with the
* field to which it is assigned.
*
* <div><p><b>BNF:</b> <code>update_item ::= [identification_variable.]{state_field | single_valued_association_field} = new_value</code></p></div>
*
* @see UpdateItem
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
@SuppressWarnings({"nls", "unused"}) // unused used for the import statement: see bug 330740
public class UpdateItemStateObject extends AbstractStateObject {
/**
* The builder is cached during the creation of the new value expression.
*/
private INewValueStateObjectBuilder builder;
/**
* The state object representing the new value.
*/
private StateObject newValue;
/**
* The state object representing the state field path expression.
*/
private StateFieldPathExpressionStateObject stateFieldPath;
/**
* Notifies the new value property has changed.
*/
public static final String NEW_VALUE_PROPERTY = "newValue";
/**
* Creates a new <code>UpdateItemStateObject</code>.
*
* @param parent The parent of this state object, which cannot be <code>null</code>
* @exception NullPointerException The given parent cannot be <code>null</code>
*/
public UpdateItemStateObject(UpdateClauseStateObject parent) {
super(parent);
}
/**
* Creates a new <code>UpdateItemStateObject</code>.
*
* @param parent The parent of this state object, which cannot be <code>null</code>
* @param path The state field path to receive the new value
* @param newValue The actual expression representing the new value
* @exception NullPointerException The given parent cannot be <code>null</code>
*/
public UpdateItemStateObject(UpdateClauseStateObject parent,
String path,
StateObject newValue) {
super(parent);
this.newValue = parent(newValue);
setPath(path);
}
/**
* Creates a new <code>UpdateItemStateObject</code>.
*
* @param parent The parent of this state object, which cannot be <code>null</code>
* @param path The state field path to receive the new value
* @param newValue The JPQL fragment representing the new value
* @exception NullPointerException The given parent cannot be <code>null</code>
*/
public UpdateItemStateObject(UpdateClauseStateObject parent,
String path,
String newValue) {
super(parent);
parseNewValue(newValue);
setPath(path);
}
@Override
public void accept(StateObjectVisitor visitor) {
visitor.visit(this);
}
@Override
protected void addChildren(List<StateObject> children) {
super.addChildren(children);
if (stateFieldPath != null) {
children.add(stateFieldPath);
}
if (newValue != null) {
children.add(newValue);
}
}
/**
* Appends the given sequence of characters to the path expression. If the sequence does not
* begin with a dot, then the first segment will be appended to the last segment and then new
* segments will be created.
*
* @param text The sequence of characters to append to the path expression
*/
public void appendToPath(String text) {
stateFieldPath.append(text);
}
/**
* Creates and returns a new {@link INewValueStateObjectBuilder} that can be used to
* programmatically create a new value expression and once the expression is complete,
* {@link INewValueStateObjectBuilder#commit()} will push the {@link StateObject}
* representation of that expression as this new value object.
*
* @return A new builder that can be used to quickly create a new value expression
*/
public INewValueStateObjectBuilder getBuilder() {
if (builder == null) {
builder = getQueryBuilder().buildStateObjectBuilder(this);
}
return builder;
}
@Override
public UpdateItem getExpression() {
return (UpdateItem) super.getExpression();
}
/**
* Returns the {@link StateObject} representing the new value.
*
* @return The new value expression or <code>null</code> if it's not yet defined
*/
public StateObject getNewValue() {
return newValue;
}
@Override
public UpdateClauseStateObject getParent() {
return (UpdateClauseStateObject) super.getParent();
}
/**
* Returns the string representation of the path expression. If the identification variable is
* virtual, then it is not part of the result.
*
* @return The path expression, which is never <code>null</code>
*/
public String getPath() {
return stateFieldPath.getPath();
}
/**
* Returns
*
*/
public StateFieldPathExpressionStateObject getStateFieldPath() {
return stateFieldPath;
}
/**
* Determines whether the {@link StateObject} representing the new value is present.
*
* @return <code>true</code> the new value exists; otherwise <code>false</code>
*/
public boolean hasNewValue() {
return newValue != null;
}
@Override
protected void initialize() {
super.initialize();
stateFieldPath = new StateFieldPathExpressionStateObject(this);
}
@Override
public boolean isEquivalent(StateObject stateObject) {
if (super.isEquivalent(stateObject)) {
UpdateItemStateObject updateItem = (UpdateItemStateObject) stateObject;
return stateFieldPath.isEquivalent(updateItem.stateFieldPath) &&
areEquivalent(newValue, updateItem.newValue);
}
return false;
}
/**
* Returns the segments in the state field path in order.
*
* @return An {@link ListIterable} over the paths of the state field path
*/
public ListIterable<String> items() {
return stateFieldPath.items();
}
/**
* Returns the number of segments in the path expression.
*
* @return The number of segments
*/
public int itemsSize() {
return stateFieldPath.itemsSize();
}
/**
* Parses the given JPQL fragment, which represents the new value.
*
* @param newValue The string representation of the new value to parse and to convert into a
* {@link StateObject}
*/
public void parseNewValue(String newValue) {
StateObject newValueStateObject = buildStateObject(newValue, NewValueBNF.ID);
setNewValue(newValueStateObject);
}
/**
* Keeps a reference of the {@link UpdateItem parsed object} object, which should only be done
* when this object is instantiated during the conversion of a parsed JPQL query into {@link
* StateObject StateObjects}.
*
* @param expression The {@link UpdateItem parsed object} representing an update item
*/
public void setExpression(UpdateItem expression) {
super.setExpression(expression);
}
/**
* Sets the new value to be the given {@link StateObject}.
*
* @param newValue The {@link StateObject} representing the new value
*/
public void setNewValue(StateObject newValue) {
builder = null;
StateObject oldNewValue = this.newValue;
this.newValue = parent(newValue);
firePropertyChanged(NEW_VALUE_PROPERTY, oldNewValue, newValue);
}
/**
* Changes the path expression with the list of segments, the identification variable will also
* be updated with the first segment.
*
* @param path The new path expression
*/
public void setPath(String path) {
stateFieldPath.setPath(path);
}
/**
* Changes the path expression with the list of segments, the identification variable will also
* be updated with the first segment.
*
* @param paths The new path expression
*/
public void setPaths(ListIterator<String> paths) {
stateFieldPath.setPaths(paths);
}
/**
* Changes the path expression with the list of segments, the identification variable will also
* be updated with the first segment.
*
* @param paths The new path expression
*/
public void setPaths(String[] paths) {
stateFieldPath.setPaths(paths);
}
/**
* The state field path expression is not qualified by the identification variable.
*
* @param identificationVariable The virtual variable that was generated based on the entity name
*/
public void setVirtualIdentificationVariable(String identificationVariable) {
IdentificationVariableStateObject variable = new IdentificationVariableStateObject(
stateFieldPath,
identificationVariable
);
variable.setVirtual(true);
stateFieldPath.setIdentificationVariableInternally(variable);
}
@Override
protected void toTextInternal(Appendable writer) throws IOException {
stateFieldPath.toString(writer);
writer.append(SPACE);
writer.append(EQUAL);
if (hasNewValue()) {
writer.append(SPACE);
newValue.toString(writer);
}
}
}