/*
 * Copyright (c) 2011, 2018 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 org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.tools.model.IJPQLQueryBuilder;
import org.eclipse.persistence.jpa.jpql.tools.model.IPropertyChangeListener;
import org.eclipse.persistence.jpa.jpql.tools.spi.IManagedTypeProvider;

/**
 * A <code>StateObject</code> is an editable representation of a JPQL query.
 * <p>
 * {@link org.eclipse.persistence.jpa.jpql.tools.model.IJPQLQueryBuilder IJPQLQueryBuilder} can be used to
 * create the state model from an existing JPQL query.
 *
 * @version 2.5
 * @since 2.4
 * @author Pascal Filion
 */
public interface StateObject {

    /**
     * Visits this {@link StateObject} by the given {@link StateObjectVisitor visitor}.
     *
     * @param visitor The {@link StateObjectVisitor visitor} to visit this object
     */
    void accept(StateObjectVisitor visitor);

    /**
     * Registers the given {@link IPropertyChangeListener} for the specified property. The listener
     * will be notified only for changes to the specified property.
     *
     * @param propertyName The name of the property for which the listener was registered
     * @param listener The listener to be notified upon changes
     * @exception NullPointerException {@link IPropertyChangeListener} cannot be <code>null</code>
     * @exception IllegalArgumentException The listener is already registered with the property name
     */
    void addPropertyChangeListener(String propertyName, IPropertyChangeListener<?> listener);

    /**
     * Returns the ordered children of this {@link StateObject}.
     *
     * @return The children of this {@link StateObject} or an empty iterable this state object does
     * not have children
     */
    Iterable<StateObject> children();

    /**
     * Decorates this {@link StateObject} with the given decorator. It means the behavior of this
     * {@link StateObject} is modified by the given one. By default, this {@link StateObject}
     * becomes the parent of the given one.
     *
     * @param decorator The {@link StateObject} decorating this one
     */
    void decorate(StateObject decorator);

    /**
     * Returns the {@link IdentificationVariableStateObject} representing the given identification
     * variable.
     *
     * @param identificationVariable The name of the identification variable to retrieve its state object
     * @return The {@link IdentificationVariableStateObject} defining the given identification variable
     */
    IdentificationVariableStateObject findIdentificationVariable(String identificationVariable);

    /**
     * Returns the declaration clause which defines the domain of the query by declaring
     * identification variables.
     *
     * @return The declaration clause of which this {@link StateObject} is a child; i.e. either the
     * top-level declaration if this is part of the top query or the sub-level declaration if this is
     * part of a subquery
     */
    DeclarationStateObject getDeclaration();

    /**
     * Returns the {@link StateObject} decorating this one if one has been set, which means the
     * behavior of this {@link StateObject} is modified by the decorator.
     *
     * @return The {@link StateObject} decorating this one
     */
    StateObject getDecorator();

    /**
     * Returns the actual parsed object if this {@link StateObject} representation of the JPQL query
     * was created by parsing an existing JPQL query.
     *
     * @return The parsed object when a JPQL query is parsed and converted into a {@link StateObject}
     * or <code>null</code> when the JPQL query is manually created (i.e. not from a string)
     */
    Expression getExpression();

    /**
     * Returns the grammar that defines how to parse a JPQL query.
     *
     * @return The grammar that was used to parse the JPQL query
     */
    JPQLGrammar getGrammar();

    /**
     * Returns the provider of managed types.
     *
     * @return The provider that gives access to the managed types
     */
    IManagedTypeProvider getManagedTypeProvider();

    /**
     * Returns the parent of this {@link StateObject}.
     *
     * @return Returns the parent of this {@link StateObject}, which is <code>null</code> only when
     * this is the root of the hierarchy
     */
    StateObject getParent();

    /**
     * Returns the {@link IJPQLQueryBuilder} that is responsible to create various part of the {@link
     * StateObject} hierarchy.
     *
     * @return The builder that created this {@link StateObject} from a JPQL query or that gives
     * access to various sub-builders
     */
    IJPQLQueryBuilder getQueryBuilder();

    /**
     * Returns the root of the {@link StateObject} hierarchy.
     *
     * @return The root of the state model representing the JPQL query
     */
    JPQLQueryStateObject getRoot();

    /**
     * Determines whether this {@link StateObject} is being decorated by another {@link StateObject},
     * which means the behavior is modified by the given one.
     *
     * return <code>true</code> if this {@link StateObject} is being decorated; <code>false</code>
     * otherwise
     */
    boolean isDecorated();

    /**
     * Determines whether the given {@link StateObject} is equivalent to this one, i.e. the
     * information of both {@link StateObject} is the same.
     *
     * @param stateObject The {@link StateObject} to compare its content to this one
     * @return <code>true</code> if both object are equivalent; <code>false</code> otherwise
     */
    boolean isEquivalent(StateObject stateObject);

    /**
     * Unregisters the given {@link IPropertyChangeListener} that was registered for the specified
     * property. The listener will no longer be notified when the property changes.
     *
     * @param propertyName The name of the property for which the listener was registered
     * @param listener The listener to unregister
     * @exception NullPointerException {@link IPropertyChangeListener} cannot be <code>null</code>
     * @exception IllegalArgumentException The listener was never registered with the property name
     */
    void removePropertyChangeListener(String propertyName, IPropertyChangeListener<?> listener);

    /**
     * Sets the given {@link StateObject} to become the parent of this one.
     *
     * @param parent The new parent {@link StateObject} of this one, which cannot be <code>null</code>
     */
    void setParent(StateObject parent);

    /**
     * Prints out a string representation of this {@link StateObject}, which should not be used to
     * define a <code>true</code> string representation of a JPQL query but should be used for
     * debugging purposes.
     * <p>
     * <b>Important:</b> If this {@link StateObject} is decorated by another one, then {@link
     * #toString(Appendable)} from that decorator is invoked, otherwise the information contained in
     * this one will be printed out.
     *
     * @param writer The writer used to print out the string representation
     * @see #toText(Appendable)
     */
    void toString(Appendable writer);

    /**
     * Prints out a string representation of this {@link StateObject}, which should not be used to
     * define a <code>true</code> string representation of a JPQL query but should be used for
     * debugging purposes.
     * <p>
     * <b>Important:</b> Even if this {@link StateObject} is decorated by another one, the decorator
     * will not be used to print out a string representation of this one.
     *
     * @param writer The writer used to print out the string representation
     * @see #toString(Appendable)
     */
    void toText(Appendable writer);
}
