jdk/test/java/awt/Clipboard/HTMLTransferTest/HTMLTransferTest.java
author ihse
Tue, 09 May 2017 12:57:30 +0200
changeset 45028 b0ea3c0bfb81
parent 32861 e87c3d1cf3cf
permissions -rw-r--r--
8179889: Fix typographic errors in copyright headers Reviewed-by: erikj, dholmes

/*
 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
  @bug 6392086 8014725
  @summary Tests basic DnD functionality in an applet
  @author Alexey Utkin, Semyon Sadetsky
  @run applet HTMLTransferTest.html
*/

/**
 * HTMLTransferTest.java
 *
 * summary: tests that HTMLs of all supported native HTML formats
 *          are transfered properly
 */

import java.applet.Applet;
import java.awt.*;
import java.awt.datatransfer.*;
import java.io.*;


public class HTMLTransferTest extends Applet {
    public static final int CODE_NOT_RETURNED = 100;
    public static final int CODE_CONSUMER_TEST_FAILED = 101;
    public static final int CODE_FAILURE = 102;
    public static DataFlavor[] HTMLFlavors = null;
    public static DataFlavor SyncFlavor = null;
    static {
        try{
            HTMLFlavors = new DataFlavor[] {
                new DataFlavor("text/html; document=selection; Class=" + InputStream.class.getName() + "; charset=UTF-8"),
                new DataFlavor("text/html; document=selection; Class=" + String.class.getName() + "; charset=UTF-8")
            };
            SyncFlavor = new DataFlavor(
                "application/x-java-serialized-object; class="
                + SyncMessage.class.getName()
                + "; charset=UTF-8"
            );
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private THTMLProducer imPr;
    private int returnCode = CODE_NOT_RETURNED;

    public void init() {
        initImpl();

        String[] instructions =
        {
            "This is an AUTOMATIC test",
            "simply wait until it is done"
        };
        Sysout.createDialog( );
        Sysout.printInstructions( instructions );

    } // init()

    private void initImpl() {
        imPr = new THTMLProducer();
        imPr.begin();
    }


    public void start() {
        try {
            String stFormats = "";

            String iniMsg = "Testing formats from the list:\n";
            for (int i = 0; i < HTMLTransferTest.HTMLFlavors.length; i++) {
                stFormats += "\"" + HTMLTransferTest.HTMLFlavors[i].getMimeType() + "\"\n";
            }
            Sysout.println(iniMsg + stFormats);
            System.err.println("===>" + iniMsg + stFormats);

            String javaPath = System.getProperty("java.home", "");
            String cmd = javaPath + File.separator + "bin" + File.separator
                + "java -cp " + System.getProperty("test.classes", ".") +
                //+ "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 "
                " THTMLConsumer"
                //+ stFormats
                ;

            Process process = Runtime.getRuntime().exec(cmd);
            ProcessResults pres = ProcessResults.doWaitFor(process);
            returnCode = pres.exitValue;

            if (pres.stderr != null && pres.stderr.length() > 0) {
                System.err.println("========= Child VM System.err ========");
                System.err.print(pres.stderr);
                System.err.println("======================================");
            }

            if (pres.stdout != null && pres.stdout.length() > 0) {
                System.err.println("========= Child VM System.out ========");
                System.err.print(pres.stdout);
                System.err.println("======================================");
            }
        } catch (Throwable e) {
            e.printStackTrace();
            //returnCode equals CODE_NOT_RETURNED
        }

        switch (returnCode) {
        case CODE_NOT_RETURNED:
            System.err.println("Child VM: failed to start");
            break;
        case CODE_FAILURE:
            System.err.println("Child VM: abnormal termination");
            break;
        case CODE_CONSUMER_TEST_FAILED:
            throw new RuntimeException("test failed: HTMLs in some " +
                "native formats are not transferred properly: " +
                "see output of child VM");
        default:
            boolean failed = false;
            String passedFormats = "";
            String failedFormats = "";

            for (int i = 0; i < imPr.passedArray.length; i++) {
               if (imPr.passedArray[i]) {
                   passedFormats += HTMLTransferTest.HTMLFlavors[i].getMimeType() + " ";
               } else {
                   failed = true;
                   failedFormats += HTMLTransferTest.HTMLFlavors[i].getMimeType() + " ";
               }
            }
            if (failed) {
                throw new RuntimeException(
                    "test failed: HTMLs in following "
                    + "native formats are not transferred properly: "
                    + failedFormats
                );
            } else {
                System.err.println(
                    "HTMLs in following native formats are "
                    + "transferred properly: "
                    + passedFormats
                );
            }
        }

    } // start()

} // class HTMLTransferTest

class SyncMessage implements Serializable {
    String msg;

    public SyncMessage(String sync) {
        this.msg = sync;
    }

    @Override
    public boolean equals(Object obj) {
        return this.msg.equals(((SyncMessage)obj).msg);
    }

    @Override
    public String toString() {
        return msg;
    }
}

class ProcessResults {
    public int exitValue;
    public String stdout;
    public String stderr;

    public ProcessResults() {
        exitValue = -1;
        stdout = "";
        stderr = "";
    }

    /**
     * Method to perform a "wait" for a process and return its exit value.
     * This is a workaround for <code>Process.waitFor()</code> never returning.
     */
    public static ProcessResults doWaitFor(Process p) {
        ProcessResults pres = new ProcessResults();

        InputStream in = null;
        InputStream err = null;

        try {
            in = p.getInputStream();
            err = p.getErrorStream();

            boolean finished = false;

            while (!finished) {
                try {
                    while (in.available() > 0) {
                        pres.stdout += (char)in.read();
                    }
                    while (err.available() > 0) {
                        pres.stderr += (char)err.read();
                    }
                    // Ask the process for its exitValue. If the process
                    // is not finished, an IllegalThreadStateException
                    // is thrown. If it is finished, we fall through and
                    // the variable finished is set to true.
                    pres.exitValue = p.exitValue();
                    finished  = true;
                }
                catch (IllegalThreadStateException e) {
                    // Process is not finished yet;
                    // Sleep a little to save on CPU cycles
                    Thread.currentThread().sleep(500);
                }
            }
            if (in != null) in.close();
            if (err != null) err.close();
        }
        catch (Throwable e) {
            System.err.println("doWaitFor(): unexpected exception");
            e.printStackTrace();
        }
        return pres;
    }
}


abstract class HTMLTransferer implements ClipboardOwner {

    static final SyncMessage S_PASSED = new SyncMessage("Y");
    static final SyncMessage S_FAILED = new SyncMessage("N");
    static final SyncMessage S_BEGIN = new SyncMessage("B");
    static final SyncMessage S_BEGIN_ANSWER = new SyncMessage("BA");
    static final SyncMessage S_END = new SyncMessage("E");



    Clipboard m_clipboard;

    HTMLTransferer() {
        m_clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    }


    abstract void notifyTransferSuccess(boolean status);


    static Object createTRInstance(int i) {
        try{
            String _htmlText =
                "The quick <font color='#78650d'>brown</font> <b>mouse</b> jumped over the lazy <b>cat</b>.";
            switch(i){
            case 0:
                return new ByteArrayInputStream(_htmlText.getBytes("utf-8"));
            case 1:
                return _htmlText;
            }
        }catch(UnsupportedEncodingException e){ e.printStackTrace(); }
        return null;
    }

    static byte[] getContent(InputStream is)
    {
        ByteArrayOutputStream tmp = new ByteArrayOutputStream();
        try{
            int read;
            while( -1 != (read = is.read()) ){
                tmp.write(read);
            };
        } catch( IOException e ) {
            e.printStackTrace();
        }
        return tmp.toByteArray();
    }

    static void Dump(byte[] b){
        System.err.println( new String(b) );
    };

    void setClipboardContents(
        Transferable contents,
        ClipboardOwner owner
    ) {
        synchronized (m_clipboard) {
            boolean set = false;
            while (!set) {
                try {
                    m_clipboard.setContents(contents, owner);
                    set = true;
                } catch (IllegalStateException ise) {
                    try {
                        Thread.sleep(100);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    Transferable getClipboardContents(Object requestor)
    {
        synchronized (m_clipboard) {
            while (true) {
                try {
                    Transferable t = m_clipboard.getContents(requestor);
                    return t;
                } catch (IllegalStateException ise) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}


class THTMLProducer extends HTMLTransferer {

    boolean[] passedArray;
    int fi = 0; // next format index
    private boolean isFirstCallOfLostOwnership = true;

    THTMLProducer() {
        passedArray = new boolean[HTMLTransferTest.HTMLFlavors.length];
    }

    void begin() {
        setClipboardContents(
            new HTMLSelection(
                HTMLTransferTest.SyncFlavor,
                S_BEGIN
            ),
            this
        );
    }

    public void lostOwnership(Clipboard cb, Transferable contents) {
        System.err.println("{PRODUCER: lost clipboard ownership");
        Transferable t = getClipboardContents(null);
        if (t.isDataFlavorSupported(HTMLTransferTest.SyncFlavor)) {
            SyncMessage msg = null;
            // for test going on if t.getTransferData() will throw an exception
            if (isFirstCallOfLostOwnership) {
                isFirstCallOfLostOwnership = false;
                msg = S_BEGIN_ANSWER;
            } else {
                msg = S_PASSED;
            }
            try {
                msg = (SyncMessage)t.getTransferData(HTMLTransferTest.SyncFlavor);
                System.err.println("++received message: " + msg);
            } catch (Exception e) {
                System.err.println("Can't getTransferData-message: " + e);
            }
            if( msg.equals(S_PASSED) ){
                notifyTransferSuccess(true);
            } else if( msg.equals(S_FAILED) ){
                notifyTransferSuccess(false);
            } else if (!msg.equals(S_BEGIN_ANSWER)) {
                throw new RuntimeException("wrong message in " +
                    "THTMLProducer.lostOwnership(): " + msg +
                    "  (possibly due to bug 4683804)");
            }
        } else {
            throw new RuntimeException(
                "DataFlavor.stringFlavor is not "
                + "suppurted by transferable in "
                + "THTMLProducer.lostOwnership()"
            );
        }

        if (fi < HTMLTransferTest.HTMLFlavors.length) {
            System.err.println(
                "testing native HTML format \""
                + HTMLTransferTest.HTMLFlavors[fi].getMimeType()
                + "\"..."
            );
            //leaveFormat( HTMLTransferTest.HTMLFlavors[fi].getMimeType() );
            setClipboardContents(
                new HTMLSelection(
                    HTMLTransferTest.HTMLFlavors[fi],
                    HTMLTransferer.createTRInstance(fi)
                ),
                this
            );
        } else {
            setClipboardContents(
                new HTMLSelection(
                    HTMLTransferTest.SyncFlavor,
                    S_END
                ),
                null
            );
        }
        System.err.println("}PRODUCER: lost clipboard ownership");
    }


    void notifyTransferSuccess(boolean status) {
        passedArray[fi] = status;
        fi++;
    }

}


class THTMLConsumer extends HTMLTransferer
{
    private static final Object LOCK = new Object();
    private static boolean failed;
    int fi = 0; // next format index

    public void lostOwnership(Clipboard cb, Transferable contents) {
        System.err.println("{CONSUMER: lost clipboard ownership");
        Transferable t = getClipboardContents(null);
        boolean bContinue = true;
        if(t.isDataFlavorSupported(HTMLTransferTest.SyncFlavor)) {
            try {
                SyncMessage msg = (SyncMessage)t.getTransferData(HTMLTransferTest.SyncFlavor);
                System.err.println("received message: " + msg);
                if(msg.equals(S_END)){
                    synchronized (LOCK) {
                        LOCK.notifyAll();
                    }
                    bContinue = false;
                }
            } catch (Exception e) {
                System.err.println("Can't getTransferData-message: " + e);
            }
        }
        if(bContinue){
            // all HTML formats have been processed
            System.err.println( "============================================================");
            System.err.println( "Put as " + HTMLTransferTest.HTMLFlavors[fi].getMimeType() );
            boolean bSuccess = false;
            for(int i = 0; i < HTMLTransferTest.HTMLFlavors.length; ++i) {
                System.err.println( "----------------------------------------------------------");
                if( t.isDataFlavorSupported(HTMLTransferTest.HTMLFlavors[i]) ){
                    Object im = null; //? HTML;
                    try {
                       im = t.getTransferData(HTMLTransferTest.HTMLFlavors[i]);
                       if (im == null) {
                           System.err.println("getTransferData returned null");
                       } else {
                            System.err.println( "Extract as " + HTMLTransferTest.HTMLFlavors[i].getMimeType() );
                            String stIn = "(unknown)", stOut = "(unknown)";
                            switch( i ){
                            case 0:
                                stIn = new String( getContent( (InputStream)HTMLTransferer.createTRInstance(i) ) );
                                stOut = new String( getContent((InputStream)im) );
                                bSuccess = stIn.equals(stOut);
                                break;
                            case 1:
                                stIn = (String)HTMLTransferer.createTRInstance(i);
                                stOut = (String)im;
                                int head = stOut.indexOf("<HTML><BODY>");
                                if (head >= 0) {
                                    stOut = stOut.substring(head + 12, stOut.length() - 14);
                                }
                                bSuccess = stIn.equals(stOut);
                                break;
                            default:
                                bSuccess = HTMLTransferer.createTRInstance(i).equals(im);
                                break;
                            };
                            System.err.println("in :" + stIn);
                            System.err.println("out:" + stOut);
                       };
                    } catch (Exception e) {
                        System.err.println("Can't getTransferData: " + e);
                    }
                    if(!bSuccess)
                        System.err.println("transferred DATA is different from initial DATA\n");
                } else {
                    System.err.println("Flavor is not supported by transferable:\n");
                    DataFlavor[] dfs = t.getTransferDataFlavors();
                    int ii;
                    for(ii = 0; ii < dfs.length; ++ii)
                        System.err.println("Supported:" + dfs[ii] + "\n");
                    dfs = HTMLTransferTest.HTMLFlavors;
                    for(ii = 0; ii < dfs.length; ++ii)
                        System.err.println("Accepted:" + dfs[ii] + "\n" );
                }
            }
            System.err.println( "----------------------------------------------------------");
            notifyTransferSuccess(bSuccess);
            System.err.println( "============================================================");
            ++fi;
        }
        System.err.println("}CONSUMER: lost clipboard ownership");
    }


    void notifyTransferSuccess(boolean status) {
        System.err.println(
            "format "
            + (status
                ? "passed"
                : "failed"
            )
            + "!!!"
        );
        setClipboardContents(
            new HTMLSelection(
                HTMLTransferTest.SyncFlavor,
                status
                    ? S_PASSED
                    : S_FAILED
            ),
            this
        );
    }


    public static void main(String[] args) {
        try {
            System.err.println("{CONSUMER: start");
            THTMLConsumer ic = new THTMLConsumer();
            ic.setClipboardContents(
                new HTMLSelection(
                    HTMLTransferTest.SyncFlavor,
                    S_BEGIN_ANSWER
                ),
                ic
            );
            synchronized (LOCK) {
                LOCK.wait();
            }
            System.err.println("}CONSUMER: start");
        } catch (Throwable e) {
            e.printStackTrace();
            System.exit(HTMLTransferTest.CODE_FAILURE);
        }
    }

}


/**
 * A <code>Transferable</code> which implements the capability required
 * to transfer an <code>HTML</code>.
 *
 * This <code>Transferable</code> properly supports
 * <code>HTMLTransferTest.HTMLFlavors</code>.
 * and all equivalent flavors.
 * No other <code>DataFlavor</code>s are supported.
 *
 * @see java.awt.datatransfer.HTMLTransferTest.HTMLFlavors
 */
class HTMLSelection implements Transferable {
    private DataFlavor m_flavor;
    private Object m_data;

    /**
     * Creates a <code>Transferable</code> capable of transferring
     * the specified <code>String</code>.
     */
    public HTMLSelection(
        DataFlavor flavor,
        Object data
    ){
        m_flavor = flavor;
        m_data = data;
    }

    /**
     * Returns an array of flavors in which this <code>Transferable</code>
     * can provide the data. <code>DataFlavor.stringFlavor</code>
     * is properly supported.
     * Support for <code>DataFlavor.plainTextFlavor</code> is
     * <b>deprecated</b>.
     *
     * @return an array of length one, whose element is <code>DataFlavor.
     *         HTMLTransferTest.HTMLFlavors</code>
     */
    public DataFlavor[] getTransferDataFlavors() {
        // returning flavors itself would allow client code to modify
        // our internal behavior
        return new DataFlavor[]{ m_flavor } ;
    }

    /**
     * Returns whether the requested flavor is supported by this
     * <code>Transferable</code>.
     *
     * @param flavor the requested flavor for the data
     * @return true if <code>flavor</code> is equal to
     *   <code>HTMLTransferTest.HTMLFlavors</code>;
     *   false if <code>flavor</code>
     *   is not one of the above flavors
     * @throws NullPointerException if flavor is <code>null</code>
     */
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        System.err.println("Have:" + flavor + " Can:" + m_flavor);
        if(flavor.equals(m_flavor))
            return true;
        return false;
    }

    /**
     * Returns the <code>Transferable</code>'s data in the requested
     * <code>DataFlavor</code> if possible. If the desired flavor is
     * <code>HTMLTransferTest.HTMLFlavors</code>, or an equivalent flavor,
     * the <code>HTML</code> representing the selection is
     * returned.
     *
     * @param flavor the requested flavor for the data
     * @return the data in the requested flavor, as outlined above
     * @throws UnsupportedFlavorException if the requested data flavor is
     *         not equivalent to <code>HTMLTransferTest.HTMLFlavors</code>
     * @throws IOException if an IOException occurs while retrieving the data.
     *         By default, <code>HTMLSelection</code> never throws
     *         this exception, but a subclass may.
     * @throws NullPointerException if flavor is <code>null</code>
     */
    public Object getTransferData(DataFlavor flavor)
        throws UnsupportedFlavorException, IOException
    {
        if (flavor.equals(m_flavor)) {
            return (Object)m_data;
        } else {
            throw new UnsupportedFlavorException(flavor);
        }
    }

} // class HTMLSelection


/****************************************************
 Standard Test Machinery
 DO NOT modify anything below -- it's a standard
  chunk of code whose purpose is to make user
  interaction uniform, and thereby make it simpler
  to read and understand someone else's test.
 ****************************************************/
class Sysout
 {
   private static TestDialog dialog;

   public static void createDialogWithInstructions( String[] instructions )
    {
      dialog = new TestDialog( new Frame(), "Instructions" );
      dialog.printInstructions( instructions );
      dialog.show();
      println( "Any messages for the tester will display here." );
    }

   public static void createDialog( )
    {
      dialog = new TestDialog( new Frame(), "Instructions" );
      String[] defInstr = { "Instructions will appear here. ", "" } ;
      dialog.printInstructions( defInstr );
      dialog.show();
      println( "Any messages for the tester will display here." );
    }


   public static void printInstructions( String[] instructions )
    {
      dialog.printInstructions( instructions );
    }


   public static void println( String messageIn )
    {
      dialog.displayMessage( messageIn );
    }

 }// Sysout  class

class TestDialog extends Dialog
 {

   TextArea instructionsText;
   TextArea messageText;
   int maxStringLength = 80;

   //DO NOT call this directly, go through Sysout
   public TestDialog( Frame frame, String name )
    {
      super( frame, name );
      int scrollBoth = TextArea.SCROLLBARS_BOTH;
      instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth );
      add( "North", instructionsText );

      messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
      add("South", messageText);

      pack();

      show();
    }// TestDialog()

   //DO NOT call this directly, go through Sysout
   public void printInstructions( String[] instructions )
    {
      //Clear out any current instructions
      instructionsText.setText( "" );

      //Go down array of instruction strings

      String printStr, remainingStr;
      for( int i=0; i < instructions.length; i++ )
       {
         //chop up each into pieces maxSringLength long
         remainingStr = instructions[ i ];
         while( remainingStr.length() > 0 )
          {
            //if longer than max then chop off first max chars to print
            if( remainingStr.length() >= maxStringLength )
             {
               //Try to chop on a word boundary
               int posOfSpace = remainingStr.
                  lastIndexOf(' ', maxStringLength - 1);

               if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;

               printStr = remainingStr.substring( 0, posOfSpace + 1 );
               remainingStr = remainingStr.substring( posOfSpace + 1 );
             }
            //else just print
            else
             {
               printStr = remainingStr;
               remainingStr = "";
             }

            instructionsText.append( printStr + "\n" );

          }// while

       }// for

    }//printInstructions()

   //DO NOT call this directly, go through Sysout
   public void displayMessage( String messageIn )
    {
      messageText.append( messageIn + "\n" );
    }

 }// TestDialog  class