Fixture_.java

/*
** Module   : Fixture_.java
** Abstract : Implementation of the OEUnit.Data.Fixture class.
**
** Copyright (c) 2023-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- -------------------------------Description--------------------------------
** 001 VVT 20230318 Created initial version
** 002 CA  20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
**                  dropped if is a no-op.
** 003 CA  20250317 Added 'fixtures' variable.
*/

/*
** 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.oo.oeunit.data;

import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.BlockManager.*;

import java.util.*;
import java.util.function.*;

import com.goldencode.p2j.oo.lang.*;
import com.goldencode.p2j.persist.*;
import com.goldencode.p2j.persist.lock.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.InternalEntry.*;

/**
 * Implementation for OEUnit.Data.Fixture.
 */
@LegacyResource(resource = "OEUnit.Data.Fixture")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public class Fixture_
extends BaseObject
{
   @LegacySignature(type = Type.VARIABLE, name = "Fixtures")
   protected object<? extends com.goldencode.p2j.oo.oeunit.util.List> fixtures = TypeFactory.object(com.goldencode.p2j.oo.oeunit.util.List.class);

   /**
    * The list of datasets
    */
   protected final List<DataSet> datasets = new ArrayList<>();

   /**
    * The static constructor
    */
   @LegacySignature(type = Type.CONSTRUCTOR)
   public static void __oeunit_data_Fixture__constructor__static__()
   {
      externalProcedure(Fixture_.class, new Block((Body) () -> {
         onRoutineLevel(Condition.ERROR, Action.THROW);
      }));
   }

   /**
    * The constructor
    */
   @LegacySignature(type = Type.CONSTRUCTOR)
   public void __oeunit_data_Fixture__constructor__()
   {
      internalProcedure(Fixture_.class, this, "__oeunit_data_fixture__constructor__",
               new Block((Body) () -> {
                  __lang_BaseObject_constructor__();
               }));
   }

   /**
    * The destructor
    */
   @LegacySignature(type = Type.DESTRUCTOR)
   public void __oeunit_data_Fixture__destructor__()
   {
      internalProcedure(Fixture_.class, this, "__oeunit_data_Fixture__destructor__",
               new Block((Body) () -> {
                  for(final DataSet ds : datasets)
                  {
                     ds.delete();
                  }}));
   }

   /**
    * The 'execute' method
    */
   @LegacySignature(type = Type.EXECUTE)
   public void __oeunit_data_Fixture__execute__()
   {
      onRoutineLevel(Condition.ERROR, Action.THROW);
   }

   /**
    * Create data in the attached databases, based on data in dataset tables.
    * 
    * @return {@code true} if the operation succeeded, {@code false} if there is no
    * datasets in this fixture or any error occurred while creating data from the datasets.
    */
   public final boolean createData()
   {
      if (datasets.isEmpty())
      {
         return false;
      }

      for (final DataSet dsData : datasets)
      {
         final int numTopBuffers = dsData._getNumTopBuffers();
         for (int i = 0; i < numTopBuffers; i++)
         {
            // Note: counters are 1-based in OE
            final handle hBuffer = dsData.getTopBuffer(i + 1);
            if (!hBuffer._isValid())
            {
               return false;
            }

            // HandleOps.delete(hQbuffer);
            final handle hQuerybuffer = TypeFactory.handle();
            RecordBuffer.create(hQuerybuffer, hBuffer);

            final character name = hBuffer.unwrap().name();
            final handle hDestbuffer = TypeFactory.handle();
            RecordBuffer.create(hDestbuffer, name);

            final handle hQuery = TypeFactory.handle();
            QueryWrapper.createQuery(hQuery);
            final P2JQuery query = hQuery.unwrapQuery();
            try
            {
               if (!query.setBuffers(hQuerybuffer).booleanValue()
                        || !query.prepare("FOR EACH " + name.toStringMessage() + " NO-LOCK:").booleanValue()
                        || !query.queryOpen().booleanValue())
               {
                  return false;
               }

               if (query.getFirst(LockType.LT_NONE).booleanValue())
               {
                  final Buffer destBuffer = hDestbuffer.unwrapBuffer();
                  while (!query.isOffEnd().booleanValue())
                  {
                     destBuffer.bufferCreate();
                     destBuffer.bufferCopy(hQuerybuffer);
                     destBuffer.bufferRelease();

                     query.getNext();
                  }
               }

               query.queryClose();
            }
            finally
            {
               HandleOps.delete(hDestbuffer);
               HandleOps.delete(hQuerybuffer);
               HandleOps.delete(hQuery);
            }
         }

      }

      return true;
   }

   /**
    * Initialize this fixture from a dataset.
    * 
    * @param  _dsSrc
    *         the source dataset handle
    * 
    * @return {@code true} if the operation succeeded
    */
   @LegacySignature(type = Type.METHOD, name = "FromDataSet", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "dsSrc", type = "HANDLE", mode = "INPUT") })
   public logical fromDataSet(final handle _dsSrc)
   {
      final handle dsSrc = TypeFactory.initInput(_dsSrc);
      return addDataset("FromDataSet", ds -> {
         return ds.copyDataset(dsSrc).booleanValue() ? ds : null;
      });
   }

   /**
    * Initialize from a JSON code.
    * 
    * @param  json
    *         the JSON code
    * 
    * @return {@code true} if the operation succeeded
    */
   @SuppressWarnings("resource")
   @LegacySignature(type = Type.METHOD, name = "FromJSON", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "json", type = "LONGCHAR", mode = "INPUT") })
   public logical fromJson(final Text json)
   {
      return addDataset("FromJSON", ds -> {
         return ds.readJson(new SourceData("LONGCHAR", json),
                  new character("EMPTY")).booleanValue() ? ds : null;
      });
   }

   /**
    * Initialize from a JSON file.
    * 
    * @param  path
    *         the JSON file
    * 
    * @return {@code true} if the operation succeeded
    */
   @SuppressWarnings("resource")
   @LegacySignature(type = Type.METHOD, name = "FromJSONFile", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "path", type = "CHARACTER", mode = "INPUT") })
   public logical fromJsonfile(final Text path)
   {
      return addDataset("FromJSONFile", ds -> {
         return ds.readJson(new SourceData("FILE", path),
                  new character("EMPTY")).booleanValue() ? ds : null;
      });
   }

   /**
    * Initialize from a TEMP-TABLE.
    * 
    * @param  _ttSrc
    *         the source TEMP-TABLE handle
    * 
    * @return {@code true} if the operation succeeded
    */
   @LegacySignature(type = Type.METHOD, name = "FromTempTable", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "ttSrc", type = "HANDLE", mode = "INPUT") })
   public logical fromTempTable(final handle _ttSrc)
   {
      final handle ttSrc = TypeFactory.initInput(_ttSrc);
      return addDataset("FromTempTable",
               ds -> {
                  final handle ttData = TypeFactory.handle();
                  TempTableBuilder.create(ttData);
                  final logical res = ttData.unwrapTempTableDuplicator().copyTempTable(ttSrc,
                           new logical(false), new logical(false), new logical(false),
                           new character(""));
                  if (!res.booleanValue())
                  {
                     HandleOps.delete(ttData);
                     return null;
                  }

                  final handle ttDataBuf = ttData.unwrapTempTable().defaultBufferHandle();
                  ds.addBuffer(ttDataBuf);
                  return ds;
               });
   }

   /**
    * Initialize from XML code.
    * 
    * @param  xml
    *         source XML text
    * 
    * @return {@code true} if the operation succeeded
    */
   @LegacySignature(type = Type.METHOD, name = "FromXML", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "xml", type = "LONGCHAR", mode = "INPUT") })
   public logical fromXml(final longchar xml)
   {
      return addDataset("FromXML", ds -> {
         return ds.readXml(new SourceData("LONGCHAR", xml),
                  new character("EMPTY"), new character(), new logical()).booleanValue() ? ds : null;
      });
   }

   /**
    * Initialize from an XML file.
    * 
    * @param  path
    *         the XML file
    * 
    * @return {@code true} if the operation succeeded
    */
   @LegacySignature(type = Type.METHOD, name = "FromXMLFile", returns = "LOGICAL", parameters = {
            @LegacyParameter(name = "path", type = "CHARACTER", mode = "INPUT") })
   public logical fromXmlfile(final character path)
   {
      return addDataset("FromXMLFile", ds -> {
         return ds.readXml(new SourceData("FILE", path),
                  new character("EMPTY"), new character(), new logical()).booleanValue() ? ds : null;
      });
   }

   /**
    * Get number of datasets in this fixture
    * 
    * @return see above
    */
   @LegacySignature(type = Type.GETTER, name = "Size", returns = "INTEGER")
   public integer getSize()
   {
      return function(Fixture_.class, this, "Size", integer.class, new Block((Body) () -> {
         storeReturnValue(datasets.size());
      }));
   }

   /**
    * Get total number of tables in all datasets
    * 
    * @return see above
    */
   @LegacySignature(type = Type.GETTER, name = "TableCount", returns = "INTEGER")
   public integer getTableCount()
   {
      return function(Fixture_.class, this, "TableCount", integer.class, new Block((Body) () -> {
         int result = 0;
         for (final DataSet ds : datasets)
         {
            result += ds != null ? ds.getNumTopBuffers().intValue() : 0;
         }

         storeReturnValue(result);
      }));
   }

   /**
    * Create, initialize and add a fixture dataset.
    * 
    * @param  method
    *         the legacy method name
    * @param  initialize
    *         function to initialize dataset, on success returns the dataset, or error
    *         returns {@code null}
    * 
    * @return {@code true} on success
    */
   private logical addDataset(final String method, final Function<DataSet, DataSet> initialize)
   {
      return function(Fixture_.class, this, method, logical.class, new Block((Body) () -> {
         final handle h = new handle();
         DataSet.create(h);
         final DataSet ds = (DataSet) h.getResource();
         assert ds != null;

         if (initialize.apply(ds) != null)
         {
            datasets.add(ds);
            storeReturnValue(true);
         }
         else
         {
            ds.delete();
            storeReturnValue(false);
         }
      }));
   }

}