# TargetLobFile.java

    /*
    ** Module   : TargetLobFile.java
    ** Abstract : A target file parameter to a LobCopy operation.
    **
    ** Copyright (c) 2019-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 ECF 20190510 Created initial version.
    ** 002 ECF 20190628 Implemented COPY-LOB runtime.
    ** 003 OM  20210328 Improved COPY-LOB engine and validations.
    **     OM  20210404 Used a getter to expose the file name.
    **     EVL 20220406 Catching more exceptions to inform the target file is missing or invalid.
    **     TJD 20220607 Switch to OutputStreamWriter to allow chunked write
    ** 004 CA  20250422 The source and target in COPY-LOB can be POLY, added constructor overloads.
    */

    /*
    ** 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.util;

    import java.io.*;

    /**
     * Concrete implementation of a converted COPY-LOB target parameter, corresponding with the
     * {@code TO FILE target-filename [ APPEND ]} phrase.
     */
    public class TargetLobFile
    extends LobCopyOutput
    {
       /** Name of target output file, which is a remote resource */
       private final String filename;

       /** Whether to append to current file; if {@code false}, overwrite any existing content */
       private boolean append = false;

       /**
        * Constructor which accepts a filename.
        *
        * @param   filename
        *          Name of file from which to copy.
        */
       public TargetLobFile(BaseDataType filename)
       {
          if (!(filename instanceof character))
          {
             ErrorManager.recordOrThrowError(11346);
             this.filename = null;
             return;
          }

          this.filename = getFilename((character) filename);
       }

       /**
        * Constructor which accepts a filename.
        *
        * @param   filename
        *          Name of file from which to copy.
        */
       public TargetLobFile(String filename)
       {
          this.filename = filename;
       }

       /**
        * Constructor which accepts a filename.
        *
        * @param   filename
        *          Name of file from which to copy. Must not be unknown.
        */
       public TargetLobFile(character filename)
       {
          this(getFilename(filename));
       }

       /**
        * Obtain the type of the object wrapped by this parameter
        *
        * @return  Always {@code LobCopyParameter.TYPE_FILE}.
        */
       @Override
       public int getObjectType()
       {
          return TYPE_FILE;
       }

       /**
        * Configure the copy to append to the target file instead of overwriting it.
        *
        * @return  This object instance, to allow method chaining.
        */
       public TargetLobFile append()
       {
          this.append = true;

          return this;
       }

       /**
        * Assign the given array of bytes as the data managed by this object.
        *
        * @param   data
        *          Data to be assigned to this object.
        */
       @Override
       public void write(byte[] data)
       {
          if (data == null)
          {
             // if the source is unknown (so the [data] is null) the file will still be created but empty
             data = new byte[0];
          }
          Stream stream = openStream();
          if (stream == null)
          {
             return;
          }
          // underlying stream will be closed by wrapper at the end of write
          try (OutputStreamWrapper os = new OutputStreamWrapper(stream))
          {
             os.write(data);
          }
          catch (IOException exc)
          {
             ErrorManager.recordOrThrowError(43, filename, "13");
             // ** Cannot find or open file <file-name>, errno = <number>. (43)
          }

       }

       /**
        * Assign the given character data as the data managed by this object.
        *
        * @param   data
        *          Data to be assigned to this object.
        * @param   opcp
        *          Target OE codepage to be used in write operation.
        */
       @Override
       public void write(String data, String opcp)
       {
          String javaCS;
          if (opcp == null)
          {
             javaCS = I18nOps.getJavaCharset();

             if (javaCS == null)
             {
                javaCS = "UTF-8";
             }
          }
          else
          {
             javaCS = I18nOps.getJavaCharset(opcp);

             if (javaCS == null)
             {
                ErrorManager.recordOrThrowError(912, opcp.toUpperCase(), I18nOps.CONVERSION_MAP_FILE);
                // Code page attribute table for <code-page> was not found in <filename>. (912)
                return;
             }
          }

          Stream stream = openStream();
          if (stream == null)
          {
             return;
          }
          // underlying stream will be closed by wrapper at the end of write
          try (OutputStreamWrapper os = new OutputStreamWrapper(stream))
          {
             stream.write(data.getBytes(javaCS));
          }
          catch (IOException exc)
          {
             ErrorManager.recordOrThrowError(43, filename, "13");
             // ** Cannot find or open file <file-name>, errno = <number>. (43)
          }
       }

       /**
        * Obtain the file name of this wrapper.
        *
        * @return  the file name of this wrapper.
        */
       public String getFilename()
       {
          return filename;
       }

       /**
        * Extract the filename from the given parameter. Raises an error if the specified object is
        * unknown value.
        *
        * @param   filename
        *          File name.
        *
        * @return  String value of file name.
        */
       private static String getFilename(character filename)
       {
          if (filename.isUnknown())
          {
             ErrorManager.recordOrThrowError(11346);
             // Unknown or invalid target file specified in COPY-LOB statement. (11346)
             return null;
          }

          return filename.toStringMessage();
       }

       /**
        * Open stream using StreamFactory for writing, optionally in append mode
        * @return Stream object instance or null in case of failure
        */
       private Stream openStream()
       {
          /** File stream for output file, which is a remote resource */
          Stream stream = null;

          try
          {
             stream = StreamFactory.openFileStream(filename, true, append);
          }
          catch (DeferredLegacyErrorException dlee)
          {
             ErrorManager.recordOrThrowError(43, filename, "2");
             // ** Cannot find or open file <file-name>, errno = <number>. (43)
          }
          catch (ErrorConditionException ece)
          {
             ErrorManager.recordOrThrowError(43, filename, "3");
             // ** Cannot find or open file <file-name>, errno = <number>. (43)
          }
          return stream;
       }
    }