/* * 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 Your Name: 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 Process.waitFor() 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 brown mouse jumped over the lazy cat."; 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(""); 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 Transferable which implements the capability required * to transfer an HTML. * * This Transferable properly supports * HTMLTransferTest.HTMLFlavors. * and all equivalent flavors. * No other DataFlavors are supported. * * @see java.awt.datatransfer.HTMLTransferTest.HTMLFlavors */ class HTMLSelection implements Transferable { private DataFlavor m_flavor; private Object m_data; /** * Creates a Transferable capable of transferring * the specified String. */ public HTMLSelection( DataFlavor flavor, Object data ){ m_flavor = flavor; m_data = data; } /** * Returns an array of flavors in which this Transferable * can provide the data. DataFlavor.stringFlavor * is properly supported. * Support for DataFlavor.plainTextFlavor is * deprecated. * * @return an array of length one, whose element is DataFlavor. * HTMLTransferTest.HTMLFlavors */ 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 * Transferable. * * @param flavor the requested flavor for the data * @return true if flavor is equal to * HTMLTransferTest.HTMLFlavors; * false if flavor * is not one of the above flavors * @throws NullPointerException if flavor is null */ public boolean isDataFlavorSupported(DataFlavor flavor) { System.err.println("Have:" + flavor + " Can:" + m_flavor); if(flavor.equals(m_flavor)) return true; return false; } /** * Returns the Transferable's data in the requested * DataFlavor if possible. If the desired flavor is * HTMLTransferTest.HTMLFlavors, or an equivalent flavor, * the HTML 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 HTMLTransferTest.HTMLFlavors * @throws IOException if an IOException occurs while retrieving the data. * By default, HTMLSelection never throws * this exception, but a subclass may. * @throws NullPointerException if flavor is null */ 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