# FindQuery.java

    /*
    ** Module   : FindQuery.java
    ** Abstract : Backing query for converted FIND statements
    **
    ** Copyright (c) 2004-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 ECF 20060330   @25253 Created initial version. Backing query for
    **                           converted find statements.
    ** 002 ECF 20060424   @25697 Added new constructor variants. Each existing
    **                           variant now has a counterpart which does not
    **                           expect a WhereExpression parameter.
    ** 003 ECF 20060424   @25698 Roll back previous change.
    ** 004 ECF 20060529   @26646 Override parent's initializeBuffer method.
    **                           This implementation does nothing, to prevent
    **                           buffer from being reset during query
    **                           construction.
    ** 005 ECF 20061027   @30751 Replaced Object with DataModelObject for DMO
    **                           arguments to methods/constructors. Required
    **                           for compile-time type safety.
    ** 006 ECF 20061115   @31208 Override ancestor's implementation of
    **                           isResetSnapshot() method. A converted FIND
    **                           must clear the placeholder snapshot when a
    **                           record is not found.
    ** 007 ECF 20070216   @32166 Explicitly set errorIfNull to true. This is
    **                           required to override the default setting of
    **                           false made by the superclass.
    ** 008 ECF 20070712   @34859 Moved all CAN-FIND related processing from
    **                           parent class. Added support for client-side
    **                           CAN-FIND processing within a where clause
    **                           (including SHARE-LOCK support).
    ** 009 ECF 20071130   @36121 Removed isRefreshSnapshot() method. The
    **                           RecordBuffer placeholder snapshot is no
    **                           longer cleared by any query type when the
    **                           query does not find a record to place in the
    **                           buffer.
    ** 010 ECF 20071206   @36269 Modified udpateBuffer() method. Signature
    **                           changed in superclass.
    ** 011 CA  20080312   @37431 Overloaded the registerChangeListener method
    **                           to not register a change listener.
    ** 012 CA  20090731   @43466 Fixed the off-end behavior for FIND NEXT/PREV
    **                           statements; on off-end, it must throw a QOEE
    **                           exception and perform the defined END-KEY action
    **                           (which defaults to UNDO, LEAVE if no explicit
    **                           action is defined).
    ** 013 CA  20090806   @43534 Fixed regression related to H012 (@43466) - a
    **                           FIND NEXT/PREV with a NO-ERROR clause must not
    **                           end the block (a QOEE must not be thrown, so
    **                           lenientOffEnd must be true if we are in silent
    **                           error mode).
    ** 014 OM  20120612          Added bracketing for headless mode for execution of pl/java code in
    **                           CAN-FIND function.
    ** 015 ECF 20131028          Import change.
    ** 016 ECF 20150715          Added isIdOnly method.
    ** 017 OM  20160715          Added support for template records.
    ** 018 GES 20160804          Switched from WhereExpression to lambda usage.
    **         20160919          Changes to match parent class constructor/initialization changes.
    ** 019 ECF 20171220          Changes to error handling of CAN-FINDs.
    ** 020 ECF 20180201          Removed initializeBuffer.
    ** 021 ECF 20200906          New ORM implementation.
    ** 022 GES 20200731          Eliminated context-local lookups by using ErrorHelper.
    ** 023 OM  20200915          Handled query navigation on fatal error cases.
    ** 024 ECF 20210806          Corrected CAN-FIND return value in event of fatal error. Fixed nested query
    **                           error detection/reporting.
    **     ECF 20210911          Javadoc fix.
    **     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
    ** 025 DDF 20230314          Add short-circuits for CAN-FIND queries.
    ** 026 DDF 20230316          Changed CAN-FIND short-circuits conditions.
    ** 027 DDF 20230904          Removed short-circuit because the table can have a record that was not flushed.
    ** 028 AL2 20230926          Short-circuit ANY find on temp database using fastHasRecords.
    ** 029 CA  20240324          Use logical constants for TRUE, FALSE, UNKNOWN, within internal FWD runtime.
    ** 030 DDF 20240826          Added addExternalBuffers() method to return the current FindQuery instance
    **                           for silent mode methods (FIND ... WHERE CAN-FIND() NO-ERROR).
    ** 031 DDF 20240902          The query initialization should be subjected to the silent mode and the
    **                           exceptions thrown should be handled if necessary so the initialization
    **                           will not be handled in the constructor, but will be delegated to the
    **                           find operation.
    **     DDF 20240902          Override find methods, used to initialize the query.
    **     DDF 20240903          Override all remaining find methods, but only initialize the query once.
    **     DDF 20240903          Initialize the query for conversion emitted methods.
    ** 032 AL2 20241021          Allow hinting that this is a CAN-FIND query and the buffer is not going to be
    **                           loaded by the found DMO.
    ** 033 OM  20241128          Multi-tenant runtime support: selected the proper persistence context, eventually
    **                           based on [sharedDb] parameter.
    **                           Implementation of [setMultiTenantAlternative] method.
    */

    /*
    ** 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.function.*;
    import com.goldencode.p2j.persist.lock.*;
    import com.goldencode.p2j.persist.orm.*;
    import com.goldencode.p2j.util.*;

    /**
     * A convenience adapter for a <code>RandomAccessQuery</code>. Calls to
     * <code>previous</code> and <code>next</code> will throw a
     * <code>QueryOffEndException</code> when no data record is found, which is
     * consistent with the Progress FIND NEXT/PREV statement behavior.
     * <p>
     * On query off-end, the FIND NEXT/PREV statement will execute the action
     * defined for the END-KEY condition (which defaults to UNDO, LEAVE, but can
     * be overridden by an user-defined action). This behavior is only valid to
     * following blocks:
     * <ul>
     *    <li>REPEAT</li>
     *    <li>PROCEDURE</li>
     *    <li>FOR</li>
     * </ul>
     * <p>
     * This class also handles converted CAN-FIND statements.
     */
    public final class FindQuery
    extends RandomAccessQuery
    {
       /** Types of can-find lookups */
       private static enum CanFind
       {
          /** Any record matching the query criteria */
          ANY,

          /** A unique record matching the query criteria */
          ONE,
       };

       /** Mode of this query if used for CAN-FIND. */
       private CanFind canFindMode = null;

       /** Flag indicating whether buffer is updated by query. */
       private boolean readOnly = false;

       /** Data Model Object of the buffer in which the records will be retrived. */
       private DataModelObject dmo;

       /** Where clause, using HQL syntax, can be <code>null</code>. */
       private String where;

       /** Client-side where clause expression, can be <code>null</code>. */
       private Supplier<logical> whereExpr;

       /** Order by clause, using HQL syntax, can be <code>null<code>. */
       private String sort;

       /** DMO to which this query should join via a foreign relation, can be <code>null</code>. */
       private DataModelObject inverse;

       /** Substitution parameters for HQL queries. */
       private Object[] args;

       /** Lock type to apply to records retrieved. */
       private LockType lockType;

       /** If the query was already initialized. */
       private boolean initialized = false;

       /**
        * Basic constructor which defaults record lock type to
        * <code>LockType.SHARE</code>.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        */
       public FindQuery(DataModelObject dmo, String where, Supplier<logical> whereExpr, String sort)
       {
          this(dmo, where, whereExpr, sort, null, null, LockType.SHARE);
       }

       /**
        * Constructor which is used when joining to another table using a foreign
        * key.  Defaults record lock type to <code>LockType.SHARE</code>.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   inverse
        *          DMO to which this query should join via a foreign relation.
        *          May be <code>null</code>.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        DataModelObject inverse)
       {
          this(dmo, where, whereExpr, sort, inverse, null, LockType.SHARE);
       }

       /**
        * Constructor which defaults record lock type to
        * <code>LockType.SHARE</code> and accepts default substitution parameters.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   args
        *          Substitution parameters for HQL queries.  These will be used
        *          if not overridden by a record retrieval method.
        *
        * @throws  IllegalArgumentException
        *          if any substitution argument provided is not of a supported
        *          type.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        Object[] args)
       {
          this(dmo, where, whereExpr, sort, null, args, LockType.SHARE);
       }

       /**
        * Constructor which is used when joining to another table using a foreign
        * key.  Defaults record lock type to <code>LockType.SHARE</code> and
        * accepts default substitution parameters.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   inverse
        *          DMO to which this query should join via a foreign relation.
        *          May be <code>null</code>.
        * @param   args
        *          Substitution parameters for HQL queries.  These will be used
        *          if not overridden by a record retrieval method.
        *
        * @throws  IllegalArgumentException
        *          if any substitution argument provided is not of a supported
        *          type.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        DataModelObject inverse,
                        Object[] args)
       {
          this(dmo, where, whereExpr, sort, inverse, args, LockType.SHARE);
       }

       /**
        * Constructor which sets an explicit, default, record lock type.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   lockType
        *          Lock type to apply to records retrieved, if not overridden by
        *          a record retrieval method.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        LockType lockType)
       {
          this(dmo, where, whereExpr, sort, null, null, lockType);
       }

       /**
        * Constructor which is used when joining to another table using a foreign
        * key.  Sets an explicit, default, record lock type.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   inverse
        *          DMO to which this query should join via a foreign relation.
        *          May be <code>null</code>.
        * @param   lockType
        *          Lock type to apply to records retrieved, if not overridden by
        *          a record retrieval method.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        DataModelObject inverse,
                        LockType lockType)
       {
          this(dmo, where, whereExpr, sort, inverse, null, lockType);
       }

       /**
        * Constructor which sets an explicit, default, record lock type and
        * accepts default substitution parameters.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   args
        *          Substitution parameters for HQL queries.  These will be used
        *          if not overridden by a record retrieval method.
        * @param   lockType
        *          Lock type to apply to records retrieved, if not overridden by
        *          a record retrieval method.
        *
        * @throws  IllegalArgumentException
        *          if any substitution argument provided is not of a supported
        *          type.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        Object[] args,
                        LockType lockType)
       {
          this(dmo, where, whereExpr, sort, null, args, lockType);
       }

       /**
        * Constructor which is used when joining to another table using a foreign
        * key.  Sets an explicit, default, record lock type and accepts default
        * substitution parameters.
        *
        * @param   dmo
        *          Data model object which determines the record buffer into which
        *          records are retrieved.
        * @param   where
        *          Where clause, using HQL syntax.  May be <code>null</code>.
        * @param   whereExpr
        *          Client-side where clause expression.  May be <code>null</code>.
        * @param   sort
        *          Order by clause, using HQL syntax.  May be <code>null</code>
        *          only if query is to be navigated only using one of the
        *          <code>unique</code> method variants.
        * @param   inverse
        *          DMO to which this query should join via a foreign relation.
        *          May be <code>null</code>.
        * @param   args
        *          Substitution parameters for HQL queries.  These will be used
        *          if not overridden by a record retrieval method.
        * @param   lockType
        *          Lock type to apply to records retrieved, if not overridden by
        *          a record retrieval method.
        *
        * @throws  IllegalArgumentException
        *          if any substitution argument provided is not of a supported
        *          type.
        */
       public FindQuery(DataModelObject dmo,
                        String where,
                        Supplier<logical> whereExpr,
                        String sort,
                        DataModelObject inverse,
                        Object[] args,
                        LockType lockType)
       {
          this.dmo = dmo;
          this.where = where;
          this.whereExpr = whereExpr;
          this.sort = sort;
          this.inverse = inverse;
          this.args = args;
          this.lockType = lockType;
       }

       /**
        * Set this query's can-find mode to 'any', meaning can it find any record
        * which meets the query's criteria, regardless of duplicates.
        *
        * @return  This instance, so that this method can be chained to the
        *          constructor and return this instance to the caller.
        */
       public FindQuery setHasAny()
       {
          canFindMode = CanFind.ANY;

          return this;
       }

       /**
        * Set this query's can-find mode to 'one', meaning can it find one and
        * only one record which meets the query's criteria.
        *
        * @return  This instance, so that this method can be chained to the
        *          constructor and return this instance to the caller.
        */
       public FindQuery setHasOne()
       {
          canFindMode = CanFind.ONE;

          return this;
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * Default substitution arguments and a lock type of {@code NONE} are used for this test.
        *
        * @return  {@code true} if any record matching the query's criteria exists, else {@code false}.
        */
       public logical hasAny()
       {
          initializeQuery();
          return hasAny(getCurrentArgs(), getLockType());
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * The specified substitution arguments and a lock type of {@code NONE} are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        *
        * @return  {@code true} if any record matching the query's criteria exists, else {@code false}.
        */
       public logical hasAny(Object[] values)
       {
          initializeQuery();
          return hasAny(values, getLockType());
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * Default substitution arguments and the specified lock type are used for this test.
        *
        * @param   lockType
        *          Lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  {@code true} if any record matching the query's criteria exists, else {@code false}.
        */
       public logical hasAny(LockType lockType)
       {
          initializeQuery();
          return hasAny(getCurrentArgs(), lockType);
       }

       /**
        * Indicate whether a request to find the first result of this query would succeed or fail.
        * <p>
        * The specified substitution arguments and lock type are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        * @param   lockType
        *          Lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  {@code true} if any record matching the query's criteria exists, else {@code false}.
        */
       public logical hasAny(Object[] values, LockType lockType)
       {
          initializeQuery();

          if (!prepareFetch())
          {
             return (logical.of(false));
          }

          canFindMode = CanFind.ANY;

          return (logical.of(canFindImpl(values, lockType)));
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * Default substitution arguments and a lock type of {@code NONE} are used for this test.
        *
        * @return  {@code true} if one and only one record matching the query's criteria exists, else
        *          {@code false}.
        */
       public logical hasOne()
       {
          initializeQuery();
          return hasOne(getCurrentArgs(), getLockType());
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * The specified substitution arguments and a lock type of {@code NONE} are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        *
        * @return  {@code true} if one and only one record matching the query's criteria exists, else
        *          {@code false}.
        */
       public logical hasOne(Object[] values)
       {
          initializeQuery();
          return hasOne(values, getLockType());
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * Default substitution arguments and the specified lock type are used for this test.
        *
        * @param   lockType
        *          Lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  {@code true} if one and only one record matching the query's criteria exists, else
        *          {@code false}.
        */
       public logical hasOne(LockType lockType)
       {
          initializeQuery();
          return hasOne(getCurrentArgs(), lockType);
       }

       /**
        * Indicate whether this query would produce a unique result.
        * <p>
        * The specified substitution arguments and lock type are used for this test.
        *
        * @param   values
        *          Query substitution parameters.
        * @param   lockType
        *          Lock type for the query.  This lock is not actually acquired, only tested for availability.
        *
        * @return  {@code true} if one and only one record matching the query's criteria exists, else
        *          {@code false}.
        */
       public logical hasOne(Object[] values, LockType lockType)
       {
          initializeQuery();

          if (!prepareFetch())
          {
             return (logical.of(false));
          }

          canFindMode = CanFind.ONE;

          return (logical.of(canFindImpl(values, lockType)));
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters and
        * lock type.
        *
        * @return  <code>true</code> if query could retrieve the first record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean first()
       {
          initializeQuery();
          return super.first();
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default lock type, but override the
        * default substitution parameters with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean first(Object[] values)
       {
          initializeQuery();
          return super.first(values);
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters, but
        * override the default lock type with that provided.
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean first(LockType lockType)
       {
          initializeQuery();
          return super.first(lockType);
       }

       /**
        * Navigate to the first record which meets the query criteria and retrieve
        * it into its record buffer.  Override both the default substitution
        * parameters, and the default lock type with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean first(Object[] values, LockType lockType)
       {
          initializeQuery();
          return super.first(values, lockType);
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters and
        * lock type.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean last()
       {
          initializeQuery();
          return super.last();
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default lock type, but override the
        * default substitution parameters with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean last(Object[] values)
       {
          initializeQuery();
          return super.last(values);
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters, but
        * override the default lock type with that provided.
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean last(LockType lockType)
       {
          initializeQuery();
          return super.last(lockType);
       }

       /**
        * Navigate to the last record which meets the query criteria and retrieve
        * it into its record buffer.  Override both the default substitution
        * parameters, and the default lock type with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the last record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if no record
        *          could be found when operating in standalone mode.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean last(Object[] values, LockType lockType)
       {
          initializeQuery();
          return super.last(values, lockType);
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters and
        * lock type.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean next()
       {
          initializeQuery();
          return super.next();
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default lock type, but override the
        * default substitution parameters with those provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean next(Object[] values)
       {
          initializeQuery();
          return super.next(values);
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.  Use the default substitution parameters, but
        * override the default lock type with that provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean next(LockType lockType)
       {
          initializeQuery();
          return super.next(lockType);
       }

       /**
        * Navigate to the next record which meets the query criteria and retrieve
        * it into its record buffer.  Override both the default substitution
        * parameters, and the default lock type with those provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the next record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the first record
        * which meets the query criteria.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the next record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found and <code>lenientOffEnd</code> is
        *          <code>false</code>.
        */
       @Override
       public boolean next(Object[] values, LockType lockType)
       {
          initializeQuery();
          return super.next(values, lockType);
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.  Use the default substitution
        * parameters and lock type.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean previous()
       {
          initializeQuery();
          return super.previous();
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.  Use the default lock type, but
        * override the default substitution parameters with those provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean previous(Object[] values)
       {
          initializeQuery();
          return super.previous(values);
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.  Use the default substitution
        * parameters, but override the default lock type with that provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found.
        */
       @Override
       public boolean previous(LockType lockType)
       {
          initializeQuery();
          return super.previous(lockType);
       }

       /**
        * Navigate to the previous record which meets the query criteria and
        * retrieve it into its record buffer.  Override both the default
        * substitution parameters, and the default lock type with those provided.
        * <p>
        * Use the record most recently loaded into the backing buffer as the
        * reference point when determining the previous record to visit.  If no
        * record has yet been loaded into the buffer, retrieve the last record
        * which meets the query criteria.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the previous record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval.
        * @throws  QueryOffEndException
        *          if no record could be found and <code>lenientOffEnd</code> is
        *          <code>false</code>.
        */
       @Override
       public boolean previous(Object[] values, LockType lockType)
       {
          initializeQuery();
          return super.previous(values, lockType);
       }

       /**
        * Reload the record most recently loaded into the backing record buffer,
        * if any.  Use the default lock type.
        *
        * @return  <code>true</code> if query could retrieve the current record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record reload.
        */
       @Override
       public boolean current()
       {
          initializeQuery();
          return super.current();
       }

       /**
        * Reload the record most recently loaded into the backing record buffer,
        * if any.  Override the default lock type.
        *
        * @param   lockType
        *          New lock type to apply to the record.
        *
        * @return  <code>true</code> if query could retrieve the current record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record reload, or the lock could
        *          not be transitioned to the requested type.
        */
       @Override
       public boolean current(LockType lockType)
       {
          initializeQuery();
          return super.current(lockType);
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer.  No more than one record may match
        * the criteria.  Use the default substitution parameters and lock type.
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean unique()
       {
          initializeQuery();
          return super.unique();
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer.  No more than one record may match
        * the criteria.  Use the default lock type, but override the default
        * substitution parameters with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean unique(Object[] values)
       {
          initializeQuery();
          return super.unique(values);
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer.  No more than one record may match
        * the criteria.  Use the default substitution parameters, but override
        * the default lock type with that provided.
        *
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean unique(LockType lockType)
       {
          initializeQuery();
          return super.unique(lockType);
       }

       /**
        * Navigate to a particular record which meets the query criteria and
        * retrieve it into its record buffer.  No more than one record may match
        * the criteria.  Override both the default substitution parameters, and
        * the default lock type with those provided.
        *
        * @param   values
        *          Substitution values to use when issuing FQL queries.
        * @param   lockType
        *          Lock type to acquire for the retrieved record.
        *
        * @return  <code>true</code> if query could retrieve the unique record successfully;
        *          <code>false</code> if unsuccessful and <code>lenientOffEnd</code> is set on true.
        *
        * @throws  ErrorConditionException
        *          if an error occurred during record retrieval or if more than
        *          one record is found which match the criteria.
        * @throws  QueryOffEndException
        *          if no record could be found when operating in compound query
        *          mode.
        */
       @Override
       public boolean unique(Object[] values, LockType lockType)
       {
          initializeQuery();
          return super.unique(values, lockType);
       }

       /**
        * Indicate whether this query is a simple RECID/ROWID lookup FIND query. This has implications as to
        * whether a 7254 error will be raised in the event this FIND is nested within a function called within
        * another query's WHERE clause.
        *
        * @return  {@code true} if this is a RECID/ROWID lookup FIND query, else {@code false}.
        */
       public boolean isFindByRowid()
       {
          return getHelper().getFQLPreprocessor().isFindByRowid();
       }

       /**
        * Add one or more buffers to the list of those which this query references externally. These typically
        * will be buffers referenced by converted CAN-FIND expressions embedded in the query's FQL expression,
        * which are not the primary buffers associated with the query.
        *
        * @param   dmos
        *          One or more external buffers.
        *
        * @return  This query instance.
        */
       @Override
       public FindQuery addExternalBuffers(DataModelObject... dmos)
       {
          initializeQuery();
          super.addExternalBuffers(dmos);
          return this;
       }

       /**
        * Initializes the query at the latest moment possible (right before executing a find operation).
        * This method is compatible with silent mode operations, meaning that the exceptions
        * {@link LegacyErrorException} and {@link ErrorConditionException} that are thrown when the
        * query is initialized are caught and handled.
        * <p>
        * Ensures the query is only initialized once.
        */
       public void initializeQuery()
       {
          if (initialized)
          {
             return;
          }

          super.initialize(dmo, where, whereExpr, sort, null, inverse, args, lockType);
          setErrorIfNull(true);

          initialized = true;
       }

       /**
        * Indicate whether a nested query error should be suppressed (i.e., reported only as a warning, but not
        * interrupt control flow), when detected while preprocessing this query's substitution parameters.
        *
        * @return  {@code false}.
        */
       @Override
       protected boolean suppressNestedQueryError()
       {
          return false;
       }

       /**
        * This class doesn't need to register a change listener, so this method does nothing.
        */
       protected void registerChangeListener()
       {
          // do nothing
       }

       /**
        * Override the parent's method to return <code>true</code> if this is a CAN-FIND, else
        * delegate to the parent's implementation.
        *
        * @return  <code>true</code> to produce a projection query, else <code>false</code>.
        */
       @Override
       protected boolean isIdOnly()
       {
          if (canFindMode != null)
          {
             return true;
          }

          return super.isIdOnly();
       }

       /**
        * Prepare buffer associated with this query for the query's execution.
        * If this query is in read-only mode, do nothing.  Otherwise, invoke the
        * superclass' implementation.
        *
        * @throws  ValidationException
        *          if the parent's implementation is invoked and a buffer flush
        *          triggers a validation error for the record currently stored in
        *          the buffer.
        */
       protected void prepareBuffer()
       throws ValidationException
       {
          if (!readOnly)
          {
             super.prepareBuffer();
          }
       }

       /**
        * Update the record buffer with the given DMO, applying the specified
        * lock type and error-if-null setting.  This implementation causes the
        * record buffer's internal placeholder snapshot to be reset if
        * <code>dmo</code> is null.
        *
        * @param   dmo
        *          Record to be stored in buffer.
        * @param   lockType
        *          Lock type associated with the record, which will be remembered
        *          by the buffer.
        * @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.
        * @param   offEnd
        *          Enum indicating whether query is off-end, and if so, in which
        *          direction.
        */
       protected void updateBuffer(Record dmo, LockType lockType, boolean errorIfNull, OffEnd offEnd)
       {
          if (!readOnly)
          {
             super.updateBuffer(dmo, lockType, errorIfNull, offEnd);
          }
       }

       /**
        * Override the superclass' implementation to throw an exception
        * unconditionally when in read-only mode.  This ensures that any locking
        * error which occurs during a converted CAN-FIND is not reported to the
        * user, but does affect the outcome of the CAN-FIND test.
        * <p>
        * When the query is not in read-only mode, handling is delegated to the
        * superclass' implementation.
        *
        * @param   exc
        *          Cause exception.
        */
       protected void handleExecuteException(LockUnavailableException exc)
       {
          if (readOnly)
          {
             ErrorManager.throwError(445,
                                     getBuffer().getLegacyName() + " record is locked",
                                     exc,
                                     true);
          }
          else
          {
             super.handleExecuteException(exc);
          }
       }

       /**
        * Override the superclass' implementation to throw an exception
        * unconditionally when in read-only mode.  This ensures that any
        * validation error which occurs during a converted CAN-FIND is not
        * reported to the user, but does affect the outcome of the CAN-FIND test.
        * <p>
        * When the query is not in read-only mode, handling is delegated to the
        * superclass' implementation.
        *
        * @param   exc
        *          Cause exception.
        */
       protected void handleExecuteException(ValidationException exc)
       {
          if (readOnly)
          {
             ErrorManager.throwError(exc, true);
          }
          else
          {
             super.handleExecuteException(exc);
          }
       }

       /**
        * Override the superclass' implementation to throw an exception
        * unconditionally when in read-only mode.  This ensures that any
        * validation error which occurs during a converted CAN-FIND is not
        * reported to the user, but does affect the outcome of the CAN-FIND test.
        * <p>
        * When the query is not in read-only mode, handling is delegated to the
        * superclass' implementation.
        *
        * @param   exc
        *          Cause exception.
        */
       protected void handleExecuteException(PersistenceException exc)
       {
          if (readOnly)
          {
             ErrorManager.throwError(exc, true);
          }
          else
          {
             super.handleExecuteException(exc);
          }
       }

       /**
        * Test whether the {@code first} / {@code last} navigation will throw {@code QueryOffEndException} if the
        * initialization failed. The super class must throw the exception if it fails the initialization in order
        * to prematurely exit the loop on basic navigation. Now we are processing a simple {@code FIND} statement
        * which does not require such thing because there is no loop to break.
        *
        * @return  always {@code false}.
        */
       protected boolean throwExceptionOnFatalError()
       {
          return false;
       }

       /**
        * Checks if the buffer associated with this query will be loaded as the result of this query execution.
        * This should always hold, except for CAN-FIND queries that do not modify the state of the buffer.
        *
        * @return   {@code true} if the buffer is going to be loaded by the query.
        */
       @Override
       protected boolean isBufferLoadedByQuery()
       {
          return canFindMode == null;
       }

       /**
        * Indicate whether a record can be found with meets this query's criteria. The success of this
        * search is governed by the can-find mode of this query:
        * <ul>
        *   <li>ANY:  return {@code true} if <i>any</i> record can be found which meets the query's
        *             criteria, regardless of duplicates.  If no such record is found, return
        *             {@code false}.</li>
        *   <li>ONE:  return {@code true} if <i>one and only one</i> record can be found which meets
        *             the query's criteria.  If no such record is found, or if more than one such
        *             record is found, return {@code false}.</li>
        * </ul>
        *
        * @param   values
        *          Query substitution parameters
        * @param   lockType
        *          Lock type to use when performing test.  Lock is not actually acquired, only tested.
        *
        * @return  See above.
        */
       private boolean canFindImpl(Object[] values, LockType lockType)
       {
          if (lockType == null)
          {
             lockType = LockType.NONE;
          }

          RecordBuffer buffer = this.getBuffer();

          FQLHelper helper = getHelper();
          if (helper.getFQLPreprocessor().isFindByRowid() &&
              helper.getFQLPreprocessor().getFindByRowid(values) < 0)
          {
             // permanent template records ids are validated against _file meta table. If requested
             // rowid is not the proper one, then the CAN-FIND returns false
             // temporary tables don't have template records or at least they are not findable using
             // _file meta table. Returning false for negative rowid lookup.
             return !buffer.isTemporary() &&
                   helper.isFindTemplate(values, buffer.getDMOImplementationClass());
          }

          // otherwise the query is not accessing records by their rowid or the value is positive so
          // it must be checked against the database

          // slight optimization to short-circuit cases where we can answer an ANY query with a
          // direct-access. this is only available for temporary database.
          if (canFindMode == CanFind.ANY && buffer.isTemporary() && !hasConstraints())
          {
             try
             {
                // make sure we make the records visible before attempting a direct-access call
                honorRecordNursery(null);
                Boolean hasRecords = buffer.fastHasRecords();
                // this should always suceed for temporary database.
                if (hasRecords != null)
                {
                   return hasRecords;
                }
             }
             catch (PersistenceException e)
             {
                ErrorManager.recordOrThrowError(e);
                return false;
             }
          }

          if (canFindMode == CanFind.ONE && helper.getFQLPreprocessor().isUniqueFind(true))
          {
             canFindMode = CanFind.ANY;
          }

          boolean unique = false;

          switch (canFindMode)
          {
             case ANY:
                activateFirst();
                break;
             case ONE:
                activateUnique();
                unique = true;
                break;
          }

          Record dmo = null;
          readOnly = true;
          boolean result = false;

          try
          {
             // temporarily enter headless mode when executing CAN-FIND
             buffer.txHelper.errHlp.setCanFindBracket(true); // enter CAN-FIND: push a CAN-FIND level

             dmo = execute(values, lockType, unique, false);
             result = (dmo != null && testLock(dmo, lockType));
          }
          catch (ErrorConditionException exc)
          {
             result = false;
          }
          finally
          {
             // restore previous headless mode
             buffer.txHelper.errHlp.setCanFindBracket(false); // leave CAN-FIND: pop out a level

             readOnly = false;
          }

          return result;
       }

       /**
        * Test whether the specified lock type could be acquired for the given
        * DMO, but do not actually acquire the lock.
        * <p>
        * Note:  because this method does not actually acquire the lock, it is
        * not a reliable test, since another context could easily acquire the
        * target lock after this method returns <code>true</code>, but before any
        * further action is taken on that information in the current context.
        *
        * @param   dmo
        *          DMO for which the lock test should occur.
        * @param   lockType
        *          Lock type to test.
        *
        * @return  <code>true</code> if the lock could be acquired at the moment
        *          of the test, else <code>false</code>.
        */
       private boolean testLock(Record dmo, LockType lockType)
       {
          if (lockType != LockType.NONE)
          {
             RecordBuffer buffer = getBuffer();
             Persistence persistence = buffer.getPersistence();
             String table = buffer.getTable();
             Long id = dmo.primaryKey();
             try
             {
                RecordIdentifier<String> ident = new RecordIdentifier<>(table, id);
                persistence.lock(lockType, ident, false, !buffer.getDmoInfo().multiTenant);
             }
             catch (LockUnavailableException exc)
             {
                return false;
             }
          }

          return true;
       }
    }