# DataSetContainer.java

    /*
    ** Module   : DataSetContainer.java
    ** Abstract : A container used to serialize a dataset and send it to a remote side.
    **
    ** Copyright (c) 2019-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 OM  20190708 Created initial version.
    **     OM  20190716 Javadoc updates. Added c'tor used for passing an OUTPUT DATASET as parameter.
    **     CA  20190812 Added toString().
    ** 002 SVL 20210614 Added copyRows parameter.
    **     SVL 20210701 Added clearRows.
    ** 003 IAS 20221111 Added 'xmlNodeName', 'nsURI', and 'nsPrefix' fields.
    ** 004 HC  20230116 Replaced some handle usages with the actual wrapped resources for
    **                  performance.
    ** 005 CA  20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
    ** 006 OM  20231027 Added support for ERROR attribute.
    ** 007 CA  20240305 Added namespace URI, namespace prefix and XML node-name for table/graph metadata in
    **                  OpenClient.
    ** 008 CA  20240426 Track if this container holds a valid dataset.
    ** 009 AS  20250320 Set the name of the container to 'ProDataSet', if the dataset's name is empty.
    */

    /*
    ** 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 com.goldencode.p2j.util.*;
    import java.util.*;

    /**
     * Provides the main mechanism of passing a dataset with its tables metadata and data.
     * The remote side will use the sent table properties (and their other settings, as type and
     * extent) to dynamically build the DMO interface, implementation class and .hbm.xml configuration
     * file. The inter-table relations will also be available.
     * <p>
     * As transport, any instance of this class will be wrapped in a {@link DatasetWrapper} instance
     * internally by P2J.
     */
    public class DataSetContainer
    extends MassiveContainer
    {
       /** The handle which will store the DATASET after the called method returns. */
       private handle oDSHandle;

       /** The definitions for all tables in the dataset. */
       private List<DsTableDefinition> tableDefinitions = null;

       /** The definitions for all relations between the tables in the dataset. */
       private List<DsRelationDefinition> relationDefinitions = null;

       /** The XML namespace for this dataset. */
       private String xmlns = null;

       /** The XML prefix for this dataset. */
       private String xmlPrefix = null;

       /** The XML-NODE-NAME attribute for this dataset. */
       private String xmlNodeName = null;

       /** The {@code ERROR} attribute of the dataset. */
       private boolean hasError = false;

       /** Flag indicating if this container holds a valid dataset. */
       private boolean valid = true;

       /**
        * Default c'tor, explicitly added to allow instances of this class to be created on
        * deserialization. Not for public use.
        */
       public DataSetContainer()
       {
       }

       /**
        * Create a new instance of this {@code DataSet} container.
        *
        * @param    input
        *           Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
        * @param    output
        *           Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
        * @param    append
        *           Flag indicating this table is sent in APPEND mode.
        */
       public DataSetContainer(boolean input, boolean output, boolean append)
       {
          super(input, output, append);
       }

       /**
        * Create a new instance of this {@code DataSet} container.
        *
        * @param    ds
        *           The {@code DataSet} to be serialized.
        * @param    input
        *           Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
        * @param    output
        *           Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
        * @param    append
        *           Flag indicating this table is sent in APPEND mode.
        */
       public DataSetContainer(DataSet ds, boolean input, boolean output, boolean append)
       {
          this(ds, input, output, append, true);
       }

       /**
        * Create a new instance of this {@code DataSet} container.
        *
        * @param    ds
        *           The {@code DataSet} to be serialized.
        * @param    input
        *           Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
        * @param    output
        *           Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
        * @param    append
        *           Flag indicating this table is sent in APPEND mode.
        * @param    copyRows
        *           <code>true</code>> to transfer rows in INPUT direction. <code>false</code>> to transfer only
        *           table definitions.
        */
       public DataSetContainer(DataSet ds, boolean input, boolean output, boolean append, boolean copyRows)
       {
          super(input, output, append);
          init(ds, copyRows);
       }

       /**
        * Constructor used for passing a DATASET-HANDLE parameter.
        *
        * @param   dsHandle
        *          The handle which will store the DATASET after the called method returns.
        * @param   input
        *          Flag indicating this table is sent in INPUT or INPUT-OUTPUT mode.
        * @param   output
        *          Flag indicating this table is sent in OUTPUT or INPUT-OUTPUT mode.
        * @param   append
        *          Flag indicating this table is sent in APPEND mode.
        */
       public DataSetContainer(handle dsHandle, boolean input, boolean output, boolean append)
       {
          super(input, output, append);
          this.oDSHandle = dsHandle;
          if (oDSHandle._isValid())
          {
             init((DataSet) oDSHandle.getResource(), true);
          }
       }

       /**
        * Set the {@link #valid} flag.
        *
        * @param    valid
        *           {@code true} if this container is for a valid dataset.
        */
       public void setValid(boolean valid)
       {
          this.valid = valid;
       }

       /**
        * Get the {@link #valid} flag.
        *
        * @return   {@code true} if this container is for a valid dataset.
        */
       public boolean isValid()
       {
          return valid;
       }

       /**
        * Obtain the number of tables in the {@code DataSet}.
        *
        * @return  the number of tables in the {@code DataSet}.
        */
       public int getTableCount()
       {
          return tableDefinitions == null ? 0 : tableDefinitions.size();
       }

       /**
        * Gets the list of table definitions of this {@code DataSet} definition.
        *
        * @return  the list of table definitions of this {@code DataSet} definition.
        */
       public List<DsTableDefinition> getTableDefinitions()
       {
          return tableDefinitions == null ? Collections.emptyList() : tableDefinitions;
       }

       /**
        * Obtain the number of inter-table relations in the {@code DataSet}. The count of all
        * relations is returned, whether thery are {@code ACTIVE} or not.
        *
        * @return  the number of all relations in the {@code DataSet}.
        */
       public int getRelationCount()
       {
          return relationDefinitions == null ? 0 : relationDefinitions.size();
       }

       /**
        * Gets the list of relations definitions of this {@code DataSet} definition. All relations are
        * returned, regardless they are active or not.
        *
        * @return  the list of relations definitions of this {@code DataSet} definition.
        */
       public List<DsRelationDefinition> getRelationDefinitions()
       {
          return relationDefinitions == null ? Collections.emptyList() : relationDefinitions;
       }

       /**
        * Sets a new list of table definitions of this {@code DataSet} definition.
        *
        * @param   tableDefinitions
        *          the (new) list of table definitions of this {@code DataSet} definition.
        */
       public void setTableDefinitions(List<DsTableDefinition> tableDefinitions)
       {
          this.tableDefinitions = tableDefinitions;
       }

       /**
        * Sets a new list of relations definitions of this {@code DataSet} definition.
        *
        * @param   relationDefinitions
        *          the (new) list of relations definitions of this {@code DataSet} definition.
        */
       public void setRelationDefinitions(List<DsRelationDefinition> relationDefinitions)
       {
          this.relationDefinitions = relationDefinitions;
       }

       /**
        * Initialize the internal structure of this object using the {@code DataSet} provided.
        *
        * @param   ds
        *          The {@code DataSet} used for initialization of internal data.
        * @param   copyRows
        *          <code>true</code>> to transfer rows in INPUT direction. <code>false</code>> to transfer only
        *          table definitions.
        */
       private void init(DataSet ds, boolean copyRows)
       {
          if (ds == null)
          {
             return;
          }

          String dsName = ds.name().toStringMessage();
          setStructureName(dsName.isEmpty() ? "ProDataSet" : dsName);
          character tmp = ds.namespaceURI();
          this.xmlns = tmp.isUnknown() ? "" : tmp.toJavaType();
          tmp = ds.namespacePrefix();
          this.xmlPrefix = ds.namespacePrefix().toJavaType();
          tmp = ds.getXmlNodeName();
          this.xmlNodeName = ds.getXmlNodeName().toJavaType();
          this.hasError = ds.error().booleanValue();

          // prepare relation data
          int relCnt = ds.getNumRelations().intValue();
          relationDefinitions = new ArrayList<>(relCnt);
          for (int k = 0; k < relCnt; k++)
          {
             relationDefinitions.add(new DsRelationDefinition(ds.getRelationNative(k + 1)));
          }

          int bufCnt = ds.numBuffers().intValue();
          tableDefinitions = new ArrayList<>();
          for (int k = 0; k < bufCnt; k++)
          {
             tableDefinitions.add(new DsTableDefinition(ds.getBufferByIndex(k + 1), copyRows));
          }
       }

       /**
        * Get the XML namespace of the DATASET.
        * @return XML namespace of the DATASET
        */
       public String namespaceURI()
       {
          return xmlns;
       }

       /**
        * Set the XML namespace of the DATASET.
        *
        * @param    xmlns
        *           The XML namespace.
        */
       public void namespaceUri(String xmlns)
       {
          this.xmlns = xmlns;
       }

       /**
        * Get the XML prefix of the DATASET.
        * @return XML prefix of the DATASET
        */
       public String namespacePrefix()
       {
          return xmlPrefix;
       }

       /**
        * Set the XML prefix of the DATASET.
        *
        * @param    xmlPrefix
        *           The XML prefix.
        */
       public void namespacePrefix(String xmlPrefix)
       {
          this.xmlPrefix = xmlPrefix;
       }

       /**
        * Get the XML node name of the DATASET.
        * @return XML node name of the DATASET
        */
       public String getXmlNodeName()
       {
          return xmlNodeName;
       }

       /**
        * Set the XML node name of the DATASET.
        *
        * @param    xmlNodeName
        *           The XML node name.
        */
       public void setXmlNodeName(String xmlNodeName)
       {
          this.xmlNodeName = xmlNodeName;
       }

       /**
        * Get the ERROR attribute of the DATASET.
        *
        * @return the ERROR attribute.
        */
       public boolean hasError()
       {
          return hasError;
       }

       /**
        * Delete row data.
        */
       public void clearRows()
       {
          if (tableDefinitions != null)
          {
             for (DsTableDefinition def : tableDefinitions)
             {
                def.clearRows();
             }
          }
       }

       /**
        * Get a string representation of this dataset.
        *
        * @return   See above.
        */
       @Override
       public String toString()
       {
          StringBuilder sb = new StringBuilder();
          sb.append("DATASET[").append(getStructureName()).append("]");
          sb.append("\n");

          if (tableDefinitions != null)
          {
             for (DsTableDefinition dst : tableDefinitions)
             {
                sb.append(dst.toString());
                sb.append("\n");
             }
          }

          if (relationDefinitions != null)
          {
             for (DsRelationDefinition dsr : relationDefinitions)
             {
                sb.append(dsr.toString());
                sb.append("\n");
             }
          }

          return sb.toString();
       }
    }