/******************************************************************************* * Copyright (c) 2011, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation * ******************************************************************************/ package org.eclipse.persistence.jpa.jpql.tools.model.query; import java.io.IOException; import java.util.List; import org.eclipse.persistence.jpa.jpql.ExpressionTools; import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable; import org.eclipse.persistence.jpa.jpql.tools.model.Problem; import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedType; import org.eclipse.persistence.jpa.jpql.tools.spi.IMapping; import org.eclipse.persistence.jpa.jpql.tools.spi.IType; import org.eclipse.persistence.jpa.jpql.tools.spi.ITypeDeclaration; /** * This state object represents a single identification variable, which is identifying TODO. * * @see IdentificationVariable * * @version 2.4 * @since 2.4 * @author Pascal Filion */ @SuppressWarnings("nls") public class IdentificationVariableStateObject extends SimpleStateObject { /** * The {@link IManagedType} mapped to the identification variable if the identification variable * maps to an entity name. */ private IManagedType managedType; /** * */ private IMapping mapping; /** * The {@link IType} of the object being mapped to this identification variable. */ private IType type; /** * The {@link ITypeDeclaration} of the object being mapped to this identification variable. */ private ITypeDeclaration typeDeclaration; /** * Determines whether this identification variable is virtual, meaning it's not part of the query * but is required for proper navigability. */ private boolean virtual; /** * Notify a change in the defined property. */ public static final String DEFINED_PROPERTY = "defined"; /** * Creates a new <code>IdentificationVariableStateObject</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 IdentificationVariableStateObject(StateObject parent) { super(parent); } /** * Creates a new <code>IdentificationVariableStateObject</code>. * * @param parent The parent of this state object, which cannot be <code>null</code> * @param variable The name of the identification variable * @exception NullPointerException The given parent cannot be <code>null</code> */ public IdentificationVariableStateObject(StateObject parent, String variable) { super(parent, variable); } /** * {@inheritDoc} */ public void accept(StateObjectVisitor visitor) { visitor.visit(this); } /** * {@inheritDoc} */ @Override protected void addProblems(List<Problem> problems) { super.addProblems(problems); // String text = getText(); // An empty identification variable is never allowed // if (ExpressionTools.stringIsEmpty(text)) { // problems.add(buildProblem(StateObjectProblemConstants.IDENTIFICATION_VARIABLE_STATE_OBJECT_NO_TEXT)); // } // // A JPQL identifier is never allowed // else if (false) { //.isIdentifier(text)) { // problems.add(buildProblem(StateObjectProblemConstants.IDENTIFICATION_VARIABLE_STATE_OBJECT_NOT_DEFINED)); // } // // The identification variable contains some invalid characters // // TODO // // // The variable is not defined in the FROM clause // else if (!defined) { // problems.add(buildProblem(StateObjectProblemConstants.IDENTIFICATION_VARIABLE_STATE_OBJECT_NOT_DEFINED)); // } } /** * Makes sure the current identification variable and the given one are the same. If they are * not, then clears the cached values related to the type. * * @param text The new identification variable */ protected void checkIntegrity(String text) { if (ExpressionTools.valuesAreDifferent(getText(), text)) { clearResolvedObjects(); } } /** * Clears the values related to the managed type and type. */ protected void clearResolvedObjects() { type = null; managedType = null; typeDeclaration = null; } /** * {@inheritDoc} */ @Override public IdentificationVariable getExpression() { return (IdentificationVariable) super.getExpression(); } /** * Returns the {@link IManagedType} associated with the field handled by this object. If this * object does not handle a field that has a {@link IManagedType}, then <code>null</code> should * be returned. * <p> * For example: "<code><b>SELECT</b> e <b>FROM</b> Employee e</code>", the object for <i>e</i> * would be returning the {@link IManagedType} for <i>Employee</i>. * * @return Either the {@link IManagedType}, if it could be resolved; <code>null</code> otherwise */ public IManagedType getManagedType() { if ((managedType == null) && (mapping == null)) { managedType = resolveManagedType(); } return managedType; } /** * Returns * * @return */ public IMapping getMapping() { if ((managedType == null) && (mapping == null)) { mapping = resolveMapping(); } return mapping; } /** * Returns the {@link IType} of the field handled by this object. * * @return Either the {@link IType} that was resolved by this state object or the {@link IType} * for {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ public IType getType() { if (type == null) { type = resolveType(); } return type; } /** * Returns the {@link ITypeDeclaration} of the field handled by this object. * * @return Either the {@link ITypeDeclaration} that was resolved by this object or the {@link * ITypeDeclaration} for {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ public ITypeDeclaration getTypeDeclaration() { if (typeDeclaration == null) { typeDeclaration = resolveTypeDeclaration(); } return typeDeclaration; } /** * {@inheritDoc} */ @Override public boolean isEquivalent(StateObject stateObject) { if (super.isEquivalent(stateObject)) { IdentificationVariableStateObject variable = (IdentificationVariableStateObject) stateObject; return ExpressionTools.stringsAreEqualIgnoreCase(getText(), variable.getText()); } return false; } /** * Determines whether this identification variable is virtual, meaning it's not part of the query * but is required for proper navigability. * * @return <code>true</code> if this identification variable was virtually created to fully * qualify path expression; <code>false</code> if it was parsed */ public boolean isVirtual() { return virtual; } /** * Retrieves the {@link IManagedType} that is mapped to the identification variable, if and only * if the identification variable is used to declare an entity. * * @return Either the {@link IManagedType} declared by the identification variable or <code>null</code> * if it could not be resolved */ protected IManagedType resolveManagedType() { return getManagedTypeProvider().getManagedType(getType()); } /** * Resolves * * @return */ protected IMapping resolveMapping() { return null; } /** * Resolves the {@link IType} of the property handled by this object. * * @return Either the {@link IType} that was resolved by this object or the {@link IType} for * {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ protected IType resolveType() { return getTypeDeclaration().getType(); } /** * Resolves the {@link ITypeDeclaration} of the property handled by this object. * * @return Either the {@link ITypeDeclaration} that was resolved by this object or the {@link * ITypeDeclaration} for {@link IType#UNRESOLVABLE_TYPE} if it could not be resolved */ protected ITypeDeclaration resolveTypeDeclaration() { return null; } /** * Keeps a reference of the {@link IdentificationVariable 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 IdentificationVariable parsed object} representing an * identification variable */ public void setExpression(IdentificationVariable expression) { super.setExpression(expression); } /** * {@inheritDoc} */ @Override public void setText(String text) { checkIntegrity(text); super.setText(text); } /** * Sets whether this identification variable is virtual, meaning it's not part of the query but * is required for proper navigability. * * @param virtual <code>true</code> if this identification variable was virtually created to * fully qualify path expression; <code>false</code> if it was parsed */ public void setVirtual(boolean virtual) { this.virtual = virtual; } /** * {@inheritDoc} */ @Override protected void toTextInternal(Appendable writer) throws IOException { if (!virtual) { super.toTextInternal(writer); } } }