|
Description
|
FULL PRODUCT VERSION :
java version "1.6.0-beta2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.6.0-beta2-b72)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b72, mixed mode, sharing)
also
java version "1.5.0_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
customer Windows XP [Version 5.1.2600]
Probably all other windows versions, too.
A DESCRIPTION OF THE PROBLEM :
When using an input stream (the default class) as transfer data copying data of "text/html" mime types is not possible at all.
Java erroneously duplicates text in the native clipboard format:
Version:0.9
StartHTML:-1
EndHTML:-1
StartFragment:0000000111
EndFragment:0000000332
<!--StartFragment-->
Version:0.9
StartHTML:-1
EndHTML:-1
StartFragment:0000000111
EndFragment:0000000201
<!--StartFragment-->
The quick <font color='#78650d'>brown</font> <b>mouse</b> jumped over the lazy <b>cat</b>.<!--EndFragment-->
<!--EndFragment-->
This issue is described in more detail on
http://www.peterbuettner.de/develop/javasnippets/clipHtml/index.html
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the code below to fill the clipboard.
Try to paste in OpenOffice 2.0 Writer. Notice the error message "Requested clipboard format is not available".
Try to paste in MS Word 2003. The following text is inserted:
"Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:0000000111 EndFragment:0000000201 The quick brown mouse jumped over the lazy cat."
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
In both OpenOffice Writer and MS Word (and any other word processor capable of dealing with html type) the formatted text should be inserted.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
public class HtmlStreamCopy {
public static void main(String[] args) throws ClassNotFoundException {
String htmlText = "The quick <font color='#78650d'>brown</font> <b>mouse</b> jumped over the lazy <b>cat</b>.";
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable htmlTransferable = new HtmlStreamCopy.HtmlInputStreamTransferable(htmlText);
clipboard.setContents(htmlTransferable, null);
}
private static class HtmlInputStreamTransferable implements Transferable {
private final DataFlavor _htmlDataFlavor;
private final String _htmlText;
public HtmlInputStreamTransferable(String htmlText) throws ClassNotFoundException {
_htmlText = htmlText;
_htmlDataFlavor = new DataFlavor("text/html");
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{_htmlDataFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return "text/html".equals(flavor.getMimeType());
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
{
InputStream stringStream = new ByteArrayInputStream(_htmlText.getBytes("utf-8"));
return stringStream;
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use a string instead of an input stream as transfer customer .
Posted Date : 2006-02-28 23:38:24.0
|
|
Evaluation
|
the problem are in wrong “virtual” recursion.
correction in WDataTransfer.java provides dirty solution for the problem
--------------------------------------------
public byte[] translateTransferable(Transferable contents,
DataFlavor flavor,
long format) throws IOException
{
if (format == CF_HTML) {
return HTMLSupport.convertToHTMLFormat(
super.translateTransferable(contents, flavor, CF_TEXT)
);
}
return super.translateTransferable(contents, flavor, format);
}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To extend HTML clipboard format compatibility I would like to implement following fix
in WDataTransfer.java (additional changes in HTMLDecodingInputStream class are required!):
--------------------------------------------
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 HTML_PREFIX = "<HTML><BODY>";
public static final String HTML_SUFFIX = "</BODY></HTML>";
public static final String START_FRAGMENT_CMT = "<!--StartFragment-->";
public static final String END_FRAGMENT_CMT = "<!--EndFragment-->";
public static final String SOURCE_URL = "SourceURL:";
public static final String DEF_SOURCE_URL = "about:blank";
public static final String EOLN = "\r\n";
private static final String VERSION_NUM = "1.0";
private static final int PADDED_WIDTH = 10;
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) {
// Calculate section offsets
String stBaseUrl = DEF_SOURCE_URL;
int nStartHTML =
VERSION.length() + VERSION_NUM.length() + EOLN.length()
+ START_HTML.length() + PADDED_WIDTH + EOLN.length()
+ END_HTML.length() + PADDED_WIDTH + EOLN.length()
+ START_FRAGMENT.length() + PADDED_WIDTH + EOLN.length()
+ END_FRAGMENT.length() + PADDED_WIDTH + EOLN.length()
+ SOURCE_URL.length() + stBaseUrl.length() + EOLN.length()
;
int nStartFragment = nStartHTML + HTML_PREFIX.length();
int nEndFragment = nStartFragment
+ START_FRAGMENT_CMT.length()
+ bytes.length - 1
+ END_FRAGMENT_CMT.length()
;
int nEndHTML = nEndFragment + HTML_SUFFIX.length();
StringBuffer header = new StringBuffer(
nStartFragment
+ START_FRAGMENT_CMT.length()
);
//header
header.append(VERSION);
header.append(VERSION_NUM); header.append(EOLN);
header.append(START_HTML);
header.append(toPaddedString(nStartHTML, PADDED_WIDTH)); header.append(EOLN);
header.append(END_HTML);
header.append(toPaddedString(nEndHTML, PADDED_WIDTH)); header.append(EOLN);
header.append(START_FRAGMENT);
header.append(toPaddedString(nStartFragment, PADDED_WIDTH)); header.append(EOLN);
header.append(END_FRAGMENT);
header.append(toPaddedString(nEndFragment, PADDED_WIDTH)); header.append(EOLN);
header.append(SOURCE_URL);
header.append(stBaseUrl); header.append(EOLN);
//HTML
header.append(HTML_PREFIX);
header.append(START_FRAGMENT_CMT);
byte[] headerBytes = null, trailerBytes = null;
try {
headerBytes = new String(header).getBytes(ENCODING);
trailerBytes = (END_FRAGMENT_CMT + HTML_SUFFIX).getBytes(ENCODING);
} catch (UnsupportedEncodingException cannotHappen) {
}
byte[] retval = new byte[headerBytes.length + bytes.length +
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);
retval[retval.length-1] = 0;
return retval;
}
}
.....
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Posted Date : 2006-10-02 11:43:57.0
The main problem is in recursion call of public virtual function
public byte[] translateTransferable(Transferable contents,
DataFlavor flavor,
long format) throws IOException
in
src\share\classes\sun\awt\datatransfer\DataTransferer.java
that is reimplemeted in
src\windows\classes\sun\awt\windows\WDataTransferer.java
That leads to doubled HTML format-head concatination with clipboard data stream.
This practice does not correspond to good software design and have to be rewritten. But "two-bytes fix" is availuable for service packs (look to the "Suggested fix").
Correct fix includes translateBytesOrStream and translateTransferable functions rewriting to avoid the recursion.
A number of tests have to be implemented to test backward compartibility as well as interaction with native applications in clipboard and DnD operations.
HTML text transport have to be upgraded, but this is a subject for another task.
Posted Date : 2006-12-21 18:11:37.0
The recursive action looks like that:
WDataTransfer::translateTransferable(...) { +HTML header for CF_HTML native format }
--(super with HTMLFlavor)-->DataTransfer::translateTransferable()
--(if flavor.isRepresentationClassInputStream() call translateTransferable again with
plainTextFlavor)-->WDataTransfer::translateTransferable(...){ +second HTML header for CF_HTML native format - that is a problem!}
--(super with plainTextFlavor)-->DataTransfer::translateTransferable()
Using of recursion has no reason at that place because it needs just for particular InputStream to byte[] conversion. Now it is realized as separate translateTransferableString function.
The same problem was in the translateBytesOrStream function that is using at clipboard data extraction. It does not produce an error yet, but gets lack in performance and creates a big risk for any platform-dependent modification.
HTML format is the only format with custom platform action at translateTransferable stage:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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;
}
The clipboard data exchange in JAVA is different for in-process and out-process communications. We most of clipboard support subroutines executing entirely just in case of out-process interaction. That is why HTMLTransferTest.java need to run child process. System.err was chosen by historical reason: ImageTransferTest is a direct child of ImageTransferTest.
DnDHTMLToOutlookTest is valid for X too. Word and Outlook mentioned as optional program. Any HTML document editor are acceptable at any platform. But MS Outlook and Word for Window platform would provide more extended coverage for test in future, when HTML clipboard format ver 1.0 will accepted for review.
The tests were designed with aim to cover backward compatibility and compatibility with native applications while changing HTML clipboard format.
Posted Date : 2006-12-25 13:42:53.0
|
|
Comments
|
Submitted On 12-SEP-2006
This problem is even worse when using drag and drop than cut and paste. Please fix it!!!
Submitted On 12-SEP-2006
The problem is really much worse than stated.
DataTransfer.translateTransferable() is a dysfunctional hacked up mess.
and
WDataTransferer.translateTransferable(), after it calls that hacked up super() method, then depends on a constant mime type, CF_HTML, which is defined in that class that no one is supposed to know about. omigod, what a crock!
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|