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();
   }
}