ClipboardStream.java

/*
** Module   : ClipboardStream.java
** Abstract : Implements the output stream to write data into the client's system clipboard.
**
** Copyright (c) 2018-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------Description-----------------------------------
** 001 SBI 20180213 Created the output stream to write strings data into the system clipboard.
** 002 EVL 20220325 Base refactoring for Stream based classes getting single byte and array of bytes.
** 003 GBB 20240912 UnsupportedOperationException on launching with server-side streams.
*/
/*
** 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.*;
import java.nio.charset.*;

import com.goldencode.p2j.ui.chui.*;
import com.goldencode.p2j.util.osresource.*;

/**
 * Implements the output stream into the client's system clipboard. The actual string data are
 * written into the system clipboard on closing this stream.
 */
class ClipboardStream
extends Stream
{
   /** The default buffer size */
   private static int BUFFER_SIZE = 65536;
   
   /** The default charset */
   private final Charset charset;
   
   /**
    * The intermediate buffer for writing data into this stream.
    */
   private final ByteArrayOutputStream buffer;
   
   /** Character stream encoder */
   private final OutputStreamWriter writer;

   /**
    * Creates this instance.
    */
   public ClipboardStream()
   {
      buffer = new ByteArrayOutputStream(BUFFER_SIZE);
      charset = Charset.defaultCharset();
      writer = new OutputStreamWriter(buffer, charset);
   }

   /**
    * The number of bytes available to be immediately read without blocking.
    *
    * @return   The number of available bytes.
    *
    * @throws   IOException
    *           if an I/O error occurs.
    */
   @Override
   public long available()
   throws IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * The 0-based offset into the stream at which the next read or write will
    * occur.  This does not work for "streams" that require sequential
    * access.
    *
    * @return   The current position in the stream.
    *
    * @throws   UnsupportedOperationException
    *           If the requested operation is not supported.
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public long getPos()
   throws UnsupportedOperationException,
          IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Moves the current read/write position to the specified absolute 0-based
    * offset.  A negative offset is ignored (no action is taken).  If the
    * specified offset is larger than the length of the stream, the seek
    * position will report as the number requested, but if no subsequent
    * writes occur to the file, the file is truncated to 0 bytes.  If writes
    * do occur, all writes occur at byte 0 BUT the file actually is of a size
    * that is the requested offset + the number of bytes written!
    * <p>
    * This does not work for "streams" that require sequential access.
    *
    * @param    pos
    *           The new read/write position in the stream.
    *
    * @throws   UnsupportedOperationException
    *           If the requested operation is not supported.
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void setPos(long pos)
   throws UnsupportedOperationException,
          IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * The length of the stream in bytes.  This does not work for "streams"
    * that require sequential access.
    *
    * @return   The length of the stream.
    *
    * @throws   UnsupportedOperationException
    *           If the requested operation is not supported.
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public long getLen()
   throws UnsupportedOperationException,
          IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Truncates or extends the stream to the specified length if this stream
    * supports such an operation. If truncation is requested, all data
    * located after this point in the file is discarded.  If extending the
    * file is requested, the values of the data in the extended portion of
    * the file is undefined.
    *
    * @param    len
    *           The new length of the file.
    *
    * @throws   UnsupportedOperationException
    *           If the requested operation is not supported.
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void setLen(long len)
   throws UnsupportedOperationException,
          IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Write the given character to the output stream.
    *
    * @param    ch
    *           The character to be written.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void writeCh(char ch)
   throws IOException
   {
      writer.append(ch);
   }

   /**
    * Write the given byte to the output stream.
    *
    * @param    b
    *           The byte to be written.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void writeByte(byte b)
   throws IOException
   {
      buffer.write(b);
   }

   /**
    * Write the given string to the output stream.
    *
    * @param    data
    *           The data to be written.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void write(String data)
   throws IOException
   {
      writer.append(data);
   }

   /**
    * Write the given byte array to the output stream.
    *
    * @param    data
    *           The data to be written.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void write(byte[] data)
   throws IOException
   {
      buffer.write(data);
   }

   /**
    * Write the specified range of bytes from the given byte array to the output stream.
    *
    * @param    data
    *           The data to be written.
    * @param    off
    *           Starting offset in data from which to read bytes to be written. Must be
    *           non-negative and {@code &lt; data.length}.
    * @param    len
    *           Length of data to be written. Must be non-negative and {@code &lt;=
    *           (data.length - offset)}.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    */
   @Override
   public void write(byte[] data, int off, int len)
   throws IOException
   {
      buffer.write(data, off, len);
   }

   /**
    * Peeks at the character from the current read position in the stream (reads a character from
    * the current read position in the stream without incrementing stream read position. The next
    * {@code peekCh()} and {@code readCh()} will return the same value).
    * <p>
    * The underlying stream subclass determines the content of the result. Byte oriented streams
    * such as pipes or files will return a byte while streams that generate keystrokes or
    * characters may return a DBCS or Unicode character.
    *
    * @return  The next character read from the stream, -1 on any failure and -2 upon an
    *          {@code EOF}.
    */
   @Override
   public int peekCh()
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Read a character from the current read position in the stream and increment stream pointer.
    * <p>
    * The underlying stream subclass determines the content of the result.
    * Byte oriented streams such as pipes or files will return a byte while
    * streams that generate keystrokes or characters may return a DBCS or
    * Unicode character.
    *
    * @return   The next character read from the stream, -1 on any failure
    *           and -2 upon an <code>EOF</code>.
    */
   @Override
   public int readCh()
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Read a single byte from the underlying stream.
    * 
    * @return   A single byte from the stream, -1 on any failure and -2 upon an {@code EOF}.
    */
   @Override
   public int readByte()
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Read a chunk of bytes from the current read position in the stream to the
    * offset calculated by given length or to the <code>EOF</code>, whichever
    * comes first. The <code>EOF</code> character is not returned.
    * 
    * @param    len
    *           The number of bytes to read.
    *
    * @return   The chunk read from the stream or <code>null</code> if there
    *           is nothing to read (the <code>EOF</code> has been reached). The
    *           length of the returned array will be the smaller of the
    *           <code>len</code> parameter or the actual bytes left before the
    *           <code>EOF</code>.
    * 
    * @throws   IOException
    *           If an I/O error occurs. 
    */
   @Override
   public byte[] readBytes(int len)
   throws IOException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Read all characters from the current read position in the stream to the
    * next line separator (as determined by the <code>File.separator</code>
    * or to the <code>EOF</code>. Any line separator character(s) and the
    * <code>EOF</code> character are not returned.
    *
    * @return   The next line read from the stream.
    *
    * @throws   IOException
    *           If an I/O error occurs.
    * @throws   EOFException
    *           If this input stream reaches the end before reading all the bytes.
    * @throws   InterruptedException
    *           If any thread interrupted the current thread before or while the current thread
    *           was waiting for a notification.
    */
   @Override
   public String readLn()
   throws EOFException,
          IOException,
          InterruptedException
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Closes the input stream and releases OS resources associated with it.
    */
   @Override
   public void closeIn()
   {
      throw new UnsupportedOperationException();
   }

   /**
    * Closes the output stream and releases OS resources associated with it.
    */
   @Override
   public void closeOut()
   {
      try
      {
         writer.flush();
      }
      catch (IOException e)
      {
      }
      
      if (OSResourceManager.getInstance().isServerSide(OsResourceType.STREAMS))
      {
	 // clipboard available only with gui drivers
         throw new UnsupportedOperationException();
      }
      else
      {
         ThinClient.getInstance().setClipboardValue(new String(buffer.toByteArray(), charset));
      }
      
      try
      {
         writer.close();
      }
      catch (IOException e)
      {
      }
   }

   /**
    * Closes both the input and output streams and releases OS resources associated with it.
    */
   @Override
   public void close()
   {
      closeOut();
   }

   /**
    * State of the input side of the stream.
    *
    * @return   false
    */
   @Override
   public boolean isIn()
   {
      return false;
   }

   /**
    * State of the output side of the stream.
    *
    * @return   true
    */
   @Override
   public boolean isOut()
   {
      return true;
   }

   /**
    * Assigns the internal stream reference to the given reference.
    *
    * @param    stream
    *           The new internal stream reference to use for all operations.
    *
    * @throws   UnsupportedOperationException
    *           If the requested operation is not supported.
    */
   @Override
   public void assign(Stream stream)
   throws UnsupportedOperationException
   {
      throw new UnsupportedOperationException();
   }
}