# Joinable.java

    /*
    ** Module   : Joinable.java
    ** Abstract : Interface for query types which can be added as components to CompoundQuery
    **
    ** Copyright (c) 2004-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
    ** 001 ECF 20051113   @23385 Created initial version. Query types which
    **                           can be added as components to CompoundQuery
    **                           must implement this interface.
    ** 002 ECF 20051214   @23749 Added support for scrolling queries. Added
    **                           several methods and extend the new Scrollable
    **                           base interface.
    ** 003 ECF 20060106   @23797 Removed record retrieval methods. Methods
    **                           first, last, next, previous, unique, and
    **                           current are now defined higher up in this
    **                           interface hierarchy.
    ** 004 ECF 20060216   @24719 Added client-side where clause support. Added
    **                           setResults method to allow CompoundQuery to
    **                           set a final results list into a component.
    **                           Added resolveArgs parameter to reset method
    **                           to allow conditional substitution parameter
    **                           resolution.
    ** 005 ECF 20070504   @33408 Changed load() signature to return boolean.
    ** 006 ECF 20070518   @33681 Added getRecordBuffers(). Must return an
    **                           array of RecordBuffer instances used by this
    **                           query.
    ** 007 ECF 20080112   @37000 Removed restriction of dealing only with IDs.
    **                           This interface now handles both DMO instances
    **                           and primary key IDs.
    ** 008 ECF 20081105   @40310 Added registerRecordChangeListeners(). Used
    **                           by CompoundQuery to ensure change listeners
    **                           are registered with the change broker at the
    **                           appropriate scope.
    ** 009 ECF 20090312   @42983 Removed getTableCount(). This method has moved to
    **                           the P2JQuery interface.
    ** 010 ECF 20090703   @43052 API change. Modified load() signature.
    ** 011 ECF 20131028          Import change.
    ** 012 ECF 20150802          Added methods required for compound query optimization.
    ** 013 SVL 20160608          Added silentIfNullId parameter to load() function.
    ** 014 ECF 20160620          Fixed javadoc and copyright.
    ** 015 ECF 20160727          Added getReferencedBuffers.
    ** 016 ECF 20180206          Cleaned up inheritance hierarchy.
    ** 017 OM  20180220          Added outer-join attribute.
    ** 018 OM  20181023          Added external buffers when making the adaptive server join
    **                           components.
    ** 019 CA  20221130          Refactored the WHERE clause translation (when the bound and definition buffers
    **                           are not the same), to be aware of the external buffers (i.e. added for CAN-FIND
    **                           sub-select clauses).  The translate will be performed before the query is being
    **                           executed.
    ** 020 CA  20240422          Allow the query to be reset to its initialization state, so the same instance can
    **                           be used to execute child buffer FILL operations.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.persist;

    import java.util.*;
    import com.goldencode.p2j.persist.lock.*;

    /**
     * Query types which can be added as components to <code>CompoundQuery</code>
     * must implement this interface.  It defines methods which will be called by
     * the enclosing, compound query object, which retrieve records and update
     * the state of the implementing query and its buffer(s).
     */
    interface Joinable
    extends Scrollable
    {
       /**
        * Clear the current results from the query, so the same query instance can be executed again, without
        * having to {@link #open() re-open} it.
        */
       @Override
       default void clearResults()
       {
          reset(true, true);
       }

       /**
        * Sets the left-outer-join property for this query.
        *
        * @param   outer
        *          {@code true} iif this is a left outer join component of the query.
        */
       public void setOuter(boolean outer);

       /**
        * Set a mode on the underlying buffer(s) such that setting a null record
        * into a buffer may or may not result in an error condition.  This applies
        * only to <code>FIRST</code>, <code>LAST</code>, and <code>UNIQUE</code>
        * iteration types.
        *
        * @param   errorIfNull
        *          If <code>true</code>, setting a <code>null</code> value as the
        *          current record in an underlying buffer will raise an error
        *          condition (if not in silent error mode); if <code>false</code>,
        *          this action will raise an end condition instead.
        */
       public void setErrorIfNull(boolean errorIfNull);

       /**
        * Force the query to retrieve full records, rather than primary keys only.
        */
       public void setFullRecords();

       /**
        * Set each buffer backing the query to unknown mode.
        *
        * @see  RecordBuffer#setUnknownMode()
        */
       public void setUnknownRecord();

       /**
        * Reset the query by clearing its state to a known default.  This is
        * used to ensure that subsequent requests for FIRST and LAST record
        * retrievals operate on a known query/buffer state.
        *
        * @param   resolveArgs
        *          <code>true</code> if query substitution arguments should be
        *          resolved as part of the reset;  <code>false</code> if existing
        *          argument values should be used for the next query execution.
        * @param   forceOrigArgs
        *          Flag indicating that the original arguments used at the construction of the query must be
        *          re-calculated.
        */
       public void reset(boolean resolveArgs, boolean forceOrigArgs);

       /**
        * Get an array of all record buffers referenced by this query, including those directly
        * involved in the query, as well as those (if any) indirectly referenced by inlined,
        * converted, CAN-FIND expressions.
        *
        * @return  All record buffers referenced by this query.
        */
       public RecordBuffer[] getReferencedBuffers();

       /**
        * Load one or more records into their associated buffers, given an array
        * of primary key ID values, or the actual DMOs themselves.  The IDs/DMOs
        * are arranged to coincide with the tables underlying the query, from
        * left-most to right-most (in the sense of how they are joined by the
        * query).
        * <p>
        * The contract of this method is that if an implementor cannot load the
        * expected value (e.g., the record has been deleted or is otherwise no
        * longer available), it must throw {@link MissingRecordException}, after
        * setting the associated buffer into {@link RecordBuffer#setUnknownMode()
        * unknown mode}. Note that a record being locked by another session does
        * not constitute a "missing" record. Note also that this exception must
        * not be thrown until all the elements of <code>data</code> have been
        * processed to the extent possible.
        *
        * @param   data
        *          Array of primary key IDs or DMOs, one per table involved in the
        *          query.
        * @param   lockType
        *          Lock type which should override that of the query.  Set to
        *          <code>null</code> to allow query to use its current lock type.
        * @param   silentIfNullId
        *          If <code>true</code>, do not raise {@link MissingRecordException}
        *          if some of the provided IDs are <code>null</code>. <code>null</code>
        *          IDs are valid for queries with OUTER join.
        *
        * @throws  MissingRecordException
        *          if any record cannot be loaded, because it is no longer
        *          available (e.g., deleted, etc.).
        * @throws  PersistenceException
        *          if there is an error loading data.
        *
        * @see     #getRow()
        */
       public void load(Object[] data, LockType lockType, boolean silentIfNullId)
       throws MissingRecordException,
              PersistenceException;

       /**
        * Set the given results object into this query.
        *
        * @param   results
        *          New result set for this query.
        */
       public void setResults(Results results);

       /**
        * Assemble an array of primary key IDs or DMOs for the current record(s)
        * in the buffers underlying this query, from left-most to right-most (in
        * the sense of how the query joins the associated tables).  This method
        * essentially takes a lightweight snapshot of the currently loaded
        * record(s), so that this may be restored later.
        *
        * @return  Array of primary key IDs, one per table involved in the query.
        *
        * @see     #load(Object[], LockType, boolean)
        */
       public Object[] getRow();

       /**
        * Fetch the array of primary key IDs associated with the first result in
        * this query's result list.
        *
        * @return  Array of record IDs reflecting the first query result, or
        *          <code>null</code> if there is no such result.
        */
       public Object[] peekFirst();

       /**
        * Fetch the array of primary key IDs associated with the last result in
        * this query's result list.
        *
        * @return  Array of record IDs reflecting the last query result, or
        *          <code>null</code> if there is no such result.
        */
       public Object[] peekLast();

       /**
        * Check if this type of query should register record change listeners. Use this
        * to avoid computing any scope for registration and end up with a no registration
        * behavior.
        *
        * @return   {@code true} if we should invest time into computing the scope in order
        *           to call {@link #registerRecordChangeListeners} or {@code false} if we
        *           can skip doing any work to honor {@link #registerRecordChangeListeners}.
        */
       public default boolean shouldRegisterRecordChangeListeners()
       {
          return true;
       }

       /**
        * Register all <code>RecordChangeListener</code>s associated with this
        * query (if any), with the context-local <code>ChangeBroker</code> at the
        * indicated scope.
        *
        * @param   scope
        *          Scope at which listeners should be registered with the change
        *          broker.
        */
       public void registerRecordChangeListeners(Integer scope);

       /**
        * Create an adaptive query component based on the information in this query (which is
        * presumed to be a single-table query), and given a list of adaptive query components which
        * represent outer, nested query loops, which will perform a server-side join to the
        * innermost, nested query component.
        *
        * @param   joinList
        *          List of adaptive components representing nested query loops which will contain
        *          the adaptive query component returned by this method. The last component in the
        *          list represents the nested query loop immediately containing the returned query
        *          component, with which the join will be made.
        * @param   query
        *          Adaptive query which will manage the server-side join of <code>joinList</code> and
        *          the returned query component.
        * @param   iteration
        *          Iteration type: FIRST, LAST, or NEXT.
        * @param   outer
        *          <code>true</code> if the join should be a left outer join. This type of join is
        *          not supported at the time of this writing, so this parameter should always be
        *          <code>false</code>. It is here to support a planned, future enhancement.
        * @param   fallback
        *          A compound query component to use in the event the multi-table adaptive query
        *          switches from preselect to dynamic retrieval mode.
        *
        * @return  An adaptive query component suitable for a server-side join, based on the
        *          information in this query.
        */
       public AdaptiveComponent makeAdaptiveServerJoinComponent(List<AdaptiveComponent> joinList,
                                                                AdaptiveQuery query,
                                                                int iteration,
                                                                boolean outer,
                                                                CompoundComponent fallback);

       /**
        * Create a preselect query component based on the information in this query (which is
        * presumed to be a single-table query), and given a list of preselect query components which
        * represent outer, nested query loops, which will perform a server-side join to the
        * innermost, nested query component.
        *
        * @param   joinList
        *          List of preselect components representing nested query loops which will contain
        *          the preselect query component returned by this method. The last component in the
        *          list represents the nested query loop immediately containing the returned query
        *          component, with which the join will be made.
        * @param   iteration
        *          Iteration type: FIRST, LAST, or NEXT.
        * @param   outer
        *          <code>true</code> if the join should be a left outer join. This type of join is
        *          not supported at the time of this writing, so this parameter should always be
        *          <code>false</code>. It is here to support a planned, future enhancement.
        *
        * @return  A preselect query component suitable for a server-side join, based on the
        *          information in this query.
        */
       public QueryComponent makePreselectServerJoinComponent(List<QueryComponent> joinList,
                                                              int iteration,
                                                              boolean outer);

       /**
        * Indicate whether this query has a client-side where clause expression associated with it.
        *
        * @return  <code>true</code> if there is a where expression; else <code>false</code>.
        */
       public boolean hasWhereExpression();

       /**
        * Get the query substitution arguments associated with this query, if any.
        *
        * @return  Substitution arguments, in order, or <code>null</code> if there are none.
        */
       public Object[] getArgs();

       /**
        * Get the original, HQL where clause associated with this query.
        *
        * @return  Original where clause, or <code>null</code> if there is none.
        */
       public String getOriginalWhere();

       /**
        * Force this query to operate in dynamic retrieval mode, if it supports this mode.
        */
       public void forceDynamicOperation();

       /**
        * Get the sort phrase associated with this query.
        *
        * @return  Sort phrase.
        */
       public String getSortPhrase();

       /**
        * Get the external buffers, if any, associated with this query.
        *
        * @return  Array of external buffers or {@code null}.
        */
       public RecordBuffer[] getExternalBuffers();

       /**
        * Translate the WHERE clause.
        */
       public void translateWhere();
    }