# HG changeset patch # User dpm # Date 983192981 -10800 # Mon Feb 26 16:09:41 2001 +0300 # Node ID b73874f26a410c66b24b858b6962ca9d675cf9e7 # Parent 8928e512324ddfaf4cf556d9cec0a6ae9fc6a6e5 4399204 : java.awt.datatransfer/sun.awt.datatransfer should use and support java.nio APIs 4393453 : Win32: text/html support should conform to Microsoft 0.9 standard Reviewer: das@sparc.spb.su diff -r 8928e512324d -r b73874f26a41 j2se/src/windows/classes/sun/awt/windows/WDataTransferer.java --- a/j2se/src/windows/classes/sun/awt/windows/WDataTransferer.java Mon Feb 26 16:08:39 2001 +0300 +++ b/j2se/src/windows/classes/sun/awt/windows/WDataTransferer.java Mon Feb 26 16:09:41 2001 +0300 @@ -1,5 +1,5 @@ /* - * @(#)WDataTransferer.java 1.8 01/02/06 + * @(#)WDataTransferer.java 1.9 01/02/26 * * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. * @@ -14,6 +14,7 @@ import java.awt.Graphics2D; import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; import java.awt.geom.AffineTransform; @@ -22,6 +23,15 @@ import java.awt.image.ImageObserver; import java.awt.image.Raster; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -37,7 +47,7 @@ * * @author David Mendenhall * @author Danila Sinopalnikov - * @version 1.8, 02/06/01 + * @version 1.9, 02/26/01 * * @since 1.3.1 */ @@ -64,11 +74,11 @@ private static final Map predefinedClipboardNameMap; static { - Map tempMap = new HashMap(23); + Map tempMap = new HashMap(16, 1.0f); for (int i = 1; i < predefinedClipboardNames.length; i++) { - tempMap.put(predefinedClipboardNames[i], new Long(i)); - } - predefinedClipboardNameMap = Collections.synchronizedMap(tempMap); + tempMap.put(predefinedClipboardNames[i], new Long(i)); + } + predefinedClipboardNameMap = Collections.synchronizedMap(tempMap); } /** @@ -77,19 +87,21 @@ public static final int CF_DIB = 8; public static final int CF_ENHMETAFILE = 14; public static final int CF_HDROP = 15; + + public static final long CF_HTML = registerClipboardFormat("HTML Format"); public static final Long L_CF_DIB = (Long) predefinedClipboardNameMap.get(predefinedClipboardNames[CF_DIB]); public static final Long L_CF_ENHMETAFILE = (Long) predefinedClipboardNameMap.get(predefinedClipboardNames[CF_ENHMETAFILE]); - private final Long[] imageFormats = + private static final Long[] imageFormats = new Long[] { L_CF_DIB, L_CF_ENHMETAFILE }; /** * Singleton constructor */ - private WDataTransferer () { + private WDataTransferer() { } private static WDataTransferer transferer; @@ -107,7 +119,36 @@ } public String getDefaultUnicodeEncoding() { - return "utf-16le"; + return "utf-16le"; + } + + public byte[] translateTransferable(Transferable contents, + DataFlavor flavor, + long format) throws IOException + { + byte[] bytes = super.translateTransferable(contents, flavor, format); + + if (format == CF_HTML) { + bytes = HTMLSupport.convertToHTMLFormat(bytes); + } + + return bytes; + } + + protected Object translateBytesOrStream(InputStream str, byte[] bytes, + DataFlavor flavor, long format) + throws IOException + { + if (format == CF_HTML && flavor.isFlavorTextType()) { + if (str == null) { + str = new ByteArrayInputStream(bytes); + bytes = null; + } + + str = new HTMLDecodingInputStream(str); + } + + return super.translateBytesOrStream(str, bytes, flavor, format); } public boolean isFileFormat(long format) { @@ -139,13 +180,13 @@ * Calls the Win32 RegisterClipboardFormat function to register * a non-standard format. */ - private native long registerClipboardFormat(String str); + private static native long registerClipboardFormat(String str); /** * Calls the Win32 GetClipboardFormatName function which is * the reverse operation of RegisterClipboardFormat. */ - private native String getClipboardFormatName(long format); + private static native String getClipboardFormatName(long format); protected Long[] getImageFormatsAsLongArray() { return imageFormats; @@ -209,7 +250,7 @@ public void enter() { if (!isOwned()) { - throw new IllegalMonitorStateException(); + throw new IllegalMonitorStateException(); } unlock(); startSecondaryEventLoop(); @@ -218,10 +259,251 @@ public void exit() { if (!isOwned()) { - throw new IllegalMonitorStateException(); + throw new IllegalMonitorStateException(); } WToolkit.quitSecondaryEventLoop(); } private native void startSecondaryEventLoop(); } + +final class HTMLSupport { + public static final String ENCODING = "UTF-8"; + + public static final String VERSION = "Version:"; + public static final String START_HTML = "StartHTML:"; + public static final String END_HTML = "EndHTML:"; + public static final String START_FRAGMENT = "StartFragment:"; + public static final String END_FRAGMENT = "EndFragment:"; + public static final String START_FRAGMENT_CMT = ""; + public static final String END_FRAGMENT_CMT = ""; + public static final String EOLN = "\r\n"; + + private static final String VERSION_NUM = "0.9"; + private static final String HTML_START_END = "-1"; + + private static final int PADDED_WIDTH = 10; + + private static final int HEADER_LEN = + VERSION.length() + VERSION_NUM.length() + EOLN.length() + + START_HTML.length() + HTML_START_END.length() + EOLN.length() + + END_HTML.length() + HTML_START_END.length() + EOLN.length() + + START_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() + + END_FRAGMENT.length() + PADDED_WIDTH + EOLN.length() + + START_FRAGMENT_CMT.length() + EOLN.length(); + private static final String HEADER_LEN_STR = + toPaddedString(HEADER_LEN, PADDED_WIDTH); + + private static final String TRAILER = END_FRAGMENT_CMT + EOLN + '\0'; + + private static String toPaddedString(int n, int width) { + String string = "" + n; + int len = string.length(); + if (n >= 0 && len < width) { + char[] array = new char[width - len]; + Arrays.fill(array, '0'); + StringBuffer buffer = new StringBuffer(); + buffer.append(array); + buffer.append(string); + string = buffer.toString(); + } + return string; + } + + public static byte[] convertToHTMLFormat(byte[] bytes) { + StringBuffer header = new StringBuffer(HEADER_LEN); + header.append(VERSION); + header.append(VERSION_NUM); + header.append(EOLN); + header.append(START_HTML); + header.append(HTML_START_END); + header.append(EOLN); + header.append(END_HTML); + header.append(HTML_START_END); + header.append(EOLN); + header.append(START_FRAGMENT); + header.append(HEADER_LEN_STR); + header.append(EOLN); + header.append(END_FRAGMENT); + // Strip terminating NUL byte from array + header.append(toPaddedString(HEADER_LEN + bytes.length - 1, + PADDED_WIDTH)); + header.append(EOLN); + header.append(START_FRAGMENT_CMT); + header.append(EOLN); + + byte[] headerBytes = null, trailerBytes = null; + + try { + headerBytes = new String(header).getBytes(ENCODING); + trailerBytes = TRAILER.getBytes(ENCODING); + } catch (UnsupportedEncodingException cannotHappen) { + } + + byte[] retval = new byte[headerBytes.length + bytes.length - 1 + + trailerBytes.length]; + + System.arraycopy(headerBytes, 0, retval, 0, headerBytes.length); + System.arraycopy(bytes, 0, retval, headerBytes.length, + bytes.length - 1); + System.arraycopy(trailerBytes, 0, retval, + headerBytes.length + bytes.length - 1, + trailerBytes.length); + + return retval; + } +} + +/** + * This stream takes an InputStream which provides data in CF_HTML format, + * strips off the description and context to extract the original HTML data. + */ +class HTMLDecodingInputStream extends InputStream { + + private final BufferedInputStream bufferedStream; + private boolean descriptionParsed = false; + private boolean closed = false; + private int index; + private int end; + + // InputStreamReader uses an 8K buffer. The size is not customizable. + public static final int BYTE_BUFFER_LEN = 8192; + + // CharToByteUTF8.getMaxBytesPerChar returns 3, so we should not buffer + // more chars than 3 times the number of bytes we can buffer. + public static final int CHAR_BUFFER_LEN = BYTE_BUFFER_LEN / 3; + + private static final String FAILURE_MSG = + "Unable to parse HTML description: "; + private static final String INVALID_MSG = " invalid"; + + public HTMLDecodingInputStream(InputStream bytestream) throws IOException { + bufferedStream = new BufferedInputStream(bytestream, BYTE_BUFFER_LEN); + } + + private void parseDescription() throws IOException { + bufferedStream.mark(BYTE_BUFFER_LEN); + + BufferedReader bufferedReader = new BufferedReader + (new InputStreamReader(bufferedStream, HTMLSupport.ENCODING), + CHAR_BUFFER_LEN); + String version = bufferedReader.readLine().trim(); + if (version == null || !version.startsWith(HTMLSupport.VERSION)) { + // Not MS-compliant HTML text. Return raw text from read(). + index = 0; + end = -1; + bufferedStream.reset(); + return; + } + + String input; + boolean startHTML, endHTML, startFragment, endFragment; + startHTML = endHTML = startFragment = endFragment = false; + + try { + do { + input = bufferedReader.readLine().trim(); + if (input == null) { + close(); + throw new IOException(FAILURE_MSG); + } else if (input.startsWith(HTMLSupport.START_HTML)) { + int val = Integer.parseInt + (input.substring(HTMLSupport.START_HTML.length(), + input.length()).trim()); + if (val >= 0) { + index = val; + startHTML = true; + } else if (val != -1) { + close(); + throw new IOException(FAILURE_MSG + + HTMLSupport.START_HTML + + INVALID_MSG); + } + } else if (input.startsWith(HTMLSupport.END_HTML)) { + int val = Integer.parseInt + (input.substring(HTMLSupport.END_HTML.length(), + input.length()).trim()); + if (val >= 0) { + end = val; + endHTML = true; + } else if (val != -1) { + close(); + throw new IOException(FAILURE_MSG + + HTMLSupport.END_HTML + + INVALID_MSG); + } + } else if (!startHTML && !endHTML && + input.startsWith(HTMLSupport.START_FRAGMENT)) { + index = Integer.parseInt + (input.substring(HTMLSupport.START_FRAGMENT.length(), + input.length()).trim()); + if (index < 0) { + close(); + throw new IOException(FAILURE_MSG + + HTMLSupport.START_FRAGMENT + + INVALID_MSG); + } + startFragment = true; + } else if (!startHTML && !endHTML && + input.startsWith(HTMLSupport.END_FRAGMENT)) { + end = Integer.parseInt + (input.substring(HTMLSupport.END_FRAGMENT.length(), + input.length()).trim()); + if (end < 0) { + close(); + throw new IOException(FAILURE_MSG + + HTMLSupport.END_FRAGMENT + + INVALID_MSG); + } + endFragment = true; + } + } while (!((startHTML && endHTML) || + (startFragment && endFragment))); + } catch (NumberFormatException e) { + close(); + throw new IOException(FAILURE_MSG + e); + } + + bufferedStream.reset(); + + for (int i = 0; i < index; i++) { + if (bufferedStream.read() == -1) { + close(); + throw new IOException(FAILURE_MSG + + "Byte stream ends in description."); + } + } + } + + public int read() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + + if (!descriptionParsed) { + parseDescription(); // initializes 'index' and 'end' + descriptionParsed = true; + } + + if (end != -1 && index >= end) { + return -1; + } + + int retval = bufferedStream.read(); + if (retval == -1) { + index = end = 0; // so future read() calls will fail quickly + return -1; + } else { + index++; + System.out.print((char)retval); + return retval; + } + } + + public void close() throws IOException { + if (!closed) { + closed = true; + bufferedStream.close(); + } + } +}