001: /*
002: * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.windows;
027:
028: import java.awt.Image;
029: import java.awt.Graphics2D;
030: import java.awt.Transparency;
031:
032: import java.awt.color.ColorSpace;
033:
034: import java.awt.datatransfer.DataFlavor;
035: import java.awt.datatransfer.FlavorTable;
036: import java.awt.datatransfer.Transferable;
037: import java.awt.datatransfer.UnsupportedFlavorException;
038:
039: import java.awt.geom.AffineTransform;
040:
041: import java.awt.image.BufferedImage;
042: import java.awt.image.ColorModel;
043: import java.awt.image.ComponentColorModel;
044: import java.awt.image.DataBuffer;
045: import java.awt.image.DataBufferByte;
046: import java.awt.image.DataBufferInt;
047: import java.awt.image.DirectColorModel;
048: import java.awt.image.ImageObserver;
049: import java.awt.image.Raster;
050: import java.awt.image.WritableRaster;
051:
052: import java.io.BufferedInputStream;
053: import java.io.BufferedReader;
054: import java.io.ByteArrayInputStream;
055: import java.io.InputStream;
056: import java.io.InputStreamReader;
057: import java.io.IOException;
058: import java.io.UnsupportedEncodingException;
059:
060: import java.net.URL;
061:
062: import java.util.Arrays;
063: import java.util.Collections;
064: import java.util.HashMap;
065: import java.util.Map;
066: import java.util.SortedMap;
067:
068: import sun.awt.Mutex;
069: import sun.awt.datatransfer.DataTransferer;
070: import sun.awt.datatransfer.ToolkitThreadBlockedHandler;
071:
072: import sun.awt.image.ImageRepresentation;
073: import sun.awt.image.ToolkitImage;
074:
075: /**
076: * Platform-specific support for the data transfer subsystem.
077: *
078: * @author David Mendenhall
079: * @author Danila Sinopalnikov
080: * @version 1.32, 05/05/07
081: *
082: * @since 1.3.1
083: */
084: public class WDataTransferer extends DataTransferer {
085: private static final String[] predefinedClipboardNames = { "",
086: "TEXT", "BITMAP", "METAFILEPICT", "SYLK", "DIF", "TIFF",
087: "OEM TEXT", "DIB", "PALETTE", "PENDATA", "RIFF", "WAVE",
088: "UNICODE TEXT", "ENHMETAFILE", "HDROP", "LOCALE", "DIBV5" };
089:
090: private static final Map predefinedClipboardNameMap;
091: static {
092: Map tempMap = new HashMap(predefinedClipboardNames.length, 1.0f);
093: for (int i = 1; i < predefinedClipboardNames.length; i++) {
094: tempMap.put(predefinedClipboardNames[i], Long.valueOf(i));
095: }
096: predefinedClipboardNameMap = Collections
097: .synchronizedMap(tempMap);
098: }
099:
100: /**
101: * from winuser.h
102: */
103: public static final int CF_TEXT = 1;
104: public static final int CF_METAFILEPICT = 3;
105: public static final int CF_DIB = 8;
106: public static final int CF_ENHMETAFILE = 14;
107: public static final int CF_HDROP = 15;
108: public static final int CF_LOCALE = 16;
109:
110: public static final long CF_HTML = registerClipboardFormat("HTML Format");
111: public static final long CFSTR_INETURL = registerClipboardFormat("UniformResourceLocator");
112: public static final long CF_PNG = registerClipboardFormat("PNG");
113: public static final long CF_JFIF = registerClipboardFormat("JFIF");
114:
115: private static final Long L_CF_LOCALE = (Long) predefinedClipboardNameMap
116: .get(predefinedClipboardNames[CF_LOCALE]);
117:
118: private static final DirectColorModel directColorModel = new DirectColorModel(
119: 24, 0x00FF0000, /* red mask */
120: 0x0000FF00, /* green mask */
121: 0x000000FF); /* blue mask */
122:
123: private static final int[] bandmasks = new int[] {
124: directColorModel.getRedMask(),
125: directColorModel.getGreenMask(),
126: directColorModel.getBlueMask() };
127:
128: /**
129: * Singleton constructor
130: */
131: private WDataTransferer() {
132: }
133:
134: private static WDataTransferer transferer;
135:
136: public static WDataTransferer getInstanceImpl() {
137: if (transferer == null) {
138: synchronized (WDataTransferer.class) {
139: if (transferer == null) {
140: transferer = new WDataTransferer();
141: }
142: }
143: }
144: return transferer;
145: }
146:
147: public SortedMap getFormatsForFlavors(DataFlavor[] flavors,
148: FlavorTable map) {
149: SortedMap retval = super .getFormatsForFlavors(flavors, map);
150:
151: // The Win32 native code does not support exporting LOCALE data, nor
152: // should it.
153: retval.remove(L_CF_LOCALE);
154:
155: return retval;
156: }
157:
158: public String getDefaultUnicodeEncoding() {
159: return "utf-16le";
160: }
161:
162: public byte[] translateTransferable(Transferable contents,
163: DataFlavor flavor, long format) throws IOException {
164: byte[] bytes = super .translateTransferable(contents, flavor,
165: format);
166:
167: if (format == CF_HTML) {
168: bytes = HTMLCodec.convertToHTMLFormat(bytes);
169: }
170: return bytes;
171: }
172:
173: protected Object translateBytesOrStream(InputStream str,
174: byte[] bytes, DataFlavor flavor, long format,
175: Transferable localeTransferable) throws IOException {
176: if (format == CF_HTML && flavor.isFlavorTextType()) {
177: if (str == null) {
178: str = new ByteArrayInputStream(bytes);
179: bytes = null;
180: }
181:
182: str = new HTMLCodec(str, EHTMLReadMode.HTML_READ_SELECTION);
183: }
184:
185: if (format == CFSTR_INETURL
186: && URL.class.equals(flavor.getRepresentationClass())) {
187: if (bytes == null) {
188: bytes = inputStreamToByteArray(str);
189: str = null;
190: }
191: String charset = getDefaultTextCharset();
192: if (localeTransferable != null
193: && localeTransferable
194: .isDataFlavorSupported(javaTextEncodingFlavor)) {
195: try {
196: charset = new String((byte[]) localeTransferable
197: .getTransferData(javaTextEncodingFlavor),
198: "UTF-8");
199: } catch (UnsupportedFlavorException cannotHappen) {
200: }
201: }
202: return new URL(new String(bytes, charset));
203: }
204:
205: return super .translateBytesOrStream(str, bytes, flavor, format,
206: localeTransferable);
207: }
208:
209: public boolean isLocaleDependentTextFormat(long format) {
210: return format == CF_TEXT || format == CFSTR_INETURL;
211: }
212:
213: public boolean isFileFormat(long format) {
214: return format == CF_HDROP;
215: }
216:
217: protected Long getFormatForNativeAsLong(String str) {
218: Long format = (Long) predefinedClipboardNameMap.get(str);
219: if (format == null) {
220: format = Long.valueOf(registerClipboardFormat(str));
221: }
222: return format;
223: }
224:
225: protected String getNativeForFormat(long format) {
226: return (format < predefinedClipboardNames.length) ? predefinedClipboardNames[(int) format]
227: : getClipboardFormatName(format);
228: }
229:
230: private final ToolkitThreadBlockedHandler handler = new WToolkitThreadBlockedHandler();
231:
232: public ToolkitThreadBlockedHandler getToolkitThreadBlockedHandler() {
233: return handler;
234: }
235:
236: /**
237: * Calls the Win32 RegisterClipboardFormat function to register
238: * a non-standard format.
239: */
240: private static native long registerClipboardFormat(String str);
241:
242: /**
243: * Calls the Win32 GetClipboardFormatName function which is
244: * the reverse operation of RegisterClipboardFormat.
245: */
246: private static native String getClipboardFormatName(long format);
247:
248: public boolean isImageFormat(long format) {
249: return format == CF_DIB || format == CF_ENHMETAFILE
250: || format == CF_METAFILEPICT || format == CF_PNG
251: || format == CF_JFIF;
252: }
253:
254: protected byte[] imageToPlatformBytes(Image image, long format)
255: throws IOException {
256: String mimeType = null;
257: if (format == CF_PNG) {
258: mimeType = "image/png";
259: } else if (format == CF_JFIF) {
260: mimeType = "image/jpeg";
261: }
262: if (mimeType != null) {
263: return imageToStandardBytes(image, mimeType);
264: }
265:
266: int width = 0;
267: int height = 0;
268:
269: if (image instanceof ToolkitImage) {
270: ImageRepresentation ir = ((ToolkitImage) image)
271: .getImageRep();
272: ir.reconstruct(ImageObserver.ALLBITS);
273: width = ir.getWidth();
274: height = ir.getHeight();
275: } else {
276: width = image.getWidth(null);
277: height = image.getHeight(null);
278: }
279:
280: // Fix for 4919639.
281: // Some Windows native applications (e.g. clipbrd.exe) do not handle
282: // 32-bpp DIBs correctly.
283: // As a workaround we switched to 24-bpp DIBs.
284: // MSDN prescribes that the bitmap array for a 24-bpp should consist of
285: // 3-byte triplets representing blue, green and red components of a
286: // pixel respectively. Additionally each scan line must be padded with
287: // zeroes to end on a LONG data-type boundary. LONG is always 32-bit.
288: // We render the given Image to a BufferedImage of type TYPE_3BYTE_BGR
289: // with non-default scanline stride and pass the resulting data buffer
290: // to the native code to fill the BITMAPINFO structure.
291: int mod = (width * 3) % 4;
292: int pad = mod > 0 ? 4 - mod : 0;
293:
294: ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
295: int[] nBits = { 8, 8, 8 };
296: int[] bOffs = { 2, 1, 0 };
297: ColorModel colorModel = new ComponentColorModel(cs, nBits,
298: false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
299: WritableRaster raster = Raster.createInterleavedRaster(
300: DataBuffer.TYPE_BYTE, width, height, width * 3 + pad,
301: 3, bOffs, null);
302:
303: BufferedImage bimage = new BufferedImage(colorModel, raster,
304: false, null);
305:
306: // Some Windows native applications (e.g. clipbrd.exe) do not understand
307: // top-down DIBs.
308: // So we flip the image vertically and create a bottom-up DIB.
309: AffineTransform imageFlipTransform = new AffineTransform(1, 0,
310: 0, -1, 0, height);
311:
312: Graphics2D g2d = bimage.createGraphics();
313:
314: try {
315: g2d.drawImage(image, imageFlipTransform, null);
316: } finally {
317: g2d.dispose();
318: }
319:
320: DataBufferByte buffer = (DataBufferByte) raster.getDataBuffer();
321:
322: byte[] imageData = buffer.getData();
323: return imageDataToPlatformImageBytes(imageData, width, height,
324: format);
325: }
326:
327: /**
328: * Returns a byte array which contains data special for the given format
329: * and for the given image data.
330: */
331: private native byte[] imageDataToPlatformImageBytes(
332: byte[] imageData, int width, int height, long format);
333:
334: /**
335: * Translates either a byte array or an input stream which contain
336: * platform-specific image data in the given format into an Image.
337: */
338: protected Image platformImageBytesOrStreamToImage(InputStream str,
339: byte[] bytes, long format) throws IOException {
340: String mimeType = null;
341: if (format == CF_PNG) {
342: mimeType = "image/png";
343: } else if (format == CF_JFIF) {
344: mimeType = "image/jpeg";
345: }
346: if (mimeType != null) {
347: return standardImageBytesOrStreamToImage(str, bytes,
348: mimeType);
349: }
350:
351: if (bytes == null) {
352: bytes = inputStreamToByteArray(str);
353: }
354:
355: int[] imageData = platformImageBytesToImageData(bytes, format);
356: if (imageData == null) {
357: throw new IOException("data translation failed");
358: }
359:
360: int len = imageData.length - 2;
361: int width = imageData[len];
362: int height = imageData[len + 1];
363:
364: DataBufferInt buffer = new DataBufferInt(imageData, len);
365: WritableRaster raster = Raster.createPackedRaster(buffer,
366: width, height, width, bandmasks, null);
367:
368: return new BufferedImage(directColorModel, raster, false, null);
369: }
370:
371: /**
372: * Translates a byte array which contains platform-specific image data in
373: * the given format into an integer array which contains pixel values in
374: * ARGB format. The two last elements in the array specify width and
375: * height of the image respectively.
376: */
377: private native int[] platformImageBytesToImageData(byte[] bytes,
378: long format) throws IOException;
379:
380: protected native String[] dragQueryFile(byte[] bytes);
381: }
382:
383: final class WToolkitThreadBlockedHandler extends Mutex implements
384: ToolkitThreadBlockedHandler {
385:
386: public void enter() {
387: if (!isOwned()) {
388: throw new IllegalMonitorStateException();
389: }
390: unlock();
391: startSecondaryEventLoop();
392: lock();
393: }
394:
395: public void exit() {
396: if (!isOwned()) {
397: throw new IllegalMonitorStateException();
398: }
399: WToolkit.quitSecondaryEventLoop();
400: }
401:
402: private native void startSecondaryEventLoop();
403: }
404:
405: enum EHTMLReadMode {
406: HTML_READ_ALL, HTML_READ_FRAGMENT, HTML_READ_SELECTION
407: }
408:
409: /**
410: * on decode: This stream takes an InputStream which provides data in CF_HTML format,
411: * strips off the description and context to extract the original HTML data.
412: *
413: * on encode: static convertToHTMLFormat is responsible for HTML clipboard header creation
414: */
415: class HTMLCodec extends InputStream {
416: //static section
417: public static final String ENCODING = "UTF-8";
418:
419: public static final String VERSION = "Version:";
420: public static final String START_HTML = "StartHTML:";
421: public static final String END_HTML = "EndHTML:";
422: public static final String START_FRAGMENT = "StartFragment:";
423: public static final String END_FRAGMENT = "EndFragment:";
424: public static final String START_SELECTION = "StartSelection:"; //optional
425: public static final String END_SELECTION = "EndSelection:"; //optional
426:
427: public static final String START_FRAGMENT_CMT = "<!--StartFragment-->";
428: public static final String END_FRAGMENT_CMT = "<!--EndFragment-->";
429: public static final String SOURCE_URL = "SourceURL:";
430: public static final String DEF_SOURCE_URL = "about:blank";
431:
432: public static final String EOLN = "\r\n";
433:
434: private static final String VERSION_NUM = "1.0";
435: private static final int PADDED_WIDTH = 10;
436:
437: private static String toPaddedString(int n, int width) {
438: String string = "" + n;
439: int len = string.length();
440: if (n >= 0 && len < width) {
441: char[] array = new char[width - len];
442: Arrays.fill(array, '0');
443: StringBuffer buffer = new StringBuffer(width);
444: buffer.append(array);
445: buffer.append(string);
446: string = buffer.toString();
447: }
448: return string;
449: }
450:
451: /**
452: * convertToHTMLFormat adds the MS HTML clipboard header to byte array that
453: * contains the parameters pairs.
454: *
455: * The consequence of parameters is fixed, but some or all of them could be
456: * omitted. One parameter per one text line.
457: * It looks like that:
458: *
459: * Version:1.0\r\n -- current supported version
460: * StartHTML:000000192\r\n -- shift in array to the first byte after the header
461: * EndHTML:000000757\r\n -- shift in array of last byte for HTML syntax analysis
462: * StartFragment:000000396\r\n -- shift in array jast after <!--StartFragment-->
463: * EndFragment:000000694\r\n -- shift in array before start <!--EndFragment-->
464: * StartSelection:000000398\r\n -- shift in array of the first char in copied selection
465: * EndSelection:000000692\r\n -- shift in array of the last char in copied selection
466: * SourceURL:http://sun.com/\r\n -- base URL for related referenses
467: * <HTML>...<BODY>...<!--StartFragment-->.....................<!--EndFragment-->...</BODY><HTML>
468: * ^ ^ ^ ^^ ^
469: * \ StartHTML | \-StartSelection | \EndFragment EndHTML/
470: * \-StartFragment \EndSelection
471: *
472: *Combinations with tags sequence
473: *<!--StartFragment--><HTML>...<BODY>...</BODY><HTML><!--EndFragment-->
474: * or
475: *<HTML>...<!--StartFragment-->...<BODY>...</BODY><!--EndFragment--><HTML>
476: * are vailid too.
477: */
478: public static byte[] convertToHTMLFormat(byte[] bytes) {
479: // Calculate section offsets
480: String htmlPrefix = "";
481: String htmlSuffix = "";
482: {
483: //we have extend the fragment to full HTML document correctly
484: //to avoid HTML and BODY tags doubling
485: String stContext = new String(bytes);
486: String stUpContext = stContext.toUpperCase();
487: if (-1 == stUpContext.indexOf("<HTML")) {
488: htmlPrefix = "<HTML>";
489: htmlSuffix = "</HTML>";
490: if (-1 == stUpContext.indexOf("<BODY")) {
491: htmlPrefix = htmlPrefix + "<BODY>";
492: htmlSuffix = "</BODY>" + htmlSuffix;
493: }
494: ;
495: }
496: ;
497: htmlPrefix = htmlPrefix + START_FRAGMENT_CMT;
498: htmlSuffix = END_FRAGMENT_CMT + htmlSuffix;
499: }
500:
501: String stBaseUrl = DEF_SOURCE_URL;
502: int nStartHTML = VERSION.length() + VERSION_NUM.length()
503: + EOLN.length() + START_HTML.length() + PADDED_WIDTH
504: + EOLN.length() + END_HTML.length() + PADDED_WIDTH
505: + EOLN.length() + START_FRAGMENT.length()
506: + PADDED_WIDTH + EOLN.length() + END_FRAGMENT.length()
507: + PADDED_WIDTH + EOLN.length() + SOURCE_URL.length()
508: + stBaseUrl.length() + EOLN.length();
509: int nStartFragment = nStartHTML + htmlPrefix.length();
510: int nEndFragment = nStartFragment + bytes.length - 1;
511: int nEndHTML = nEndFragment + htmlSuffix.length();
512:
513: StringBuilder header = new StringBuilder(nStartFragment
514: + START_FRAGMENT_CMT.length());
515: //header
516: header.append(VERSION);
517: header.append(VERSION_NUM);
518: header.append(EOLN);
519:
520: header.append(START_HTML);
521: header.append(toPaddedString(nStartHTML, PADDED_WIDTH));
522: header.append(EOLN);
523:
524: header.append(END_HTML);
525: header.append(toPaddedString(nEndHTML, PADDED_WIDTH));
526: header.append(EOLN);
527:
528: header.append(START_FRAGMENT);
529: header.append(toPaddedString(nStartFragment, PADDED_WIDTH));
530: header.append(EOLN);
531:
532: header.append(END_FRAGMENT);
533: header.append(toPaddedString(nEndFragment, PADDED_WIDTH));
534: header.append(EOLN);
535:
536: header.append(SOURCE_URL);
537: header.append(stBaseUrl);
538: header.append(EOLN);
539:
540: //HTML
541: header.append(htmlPrefix);
542:
543: byte[] headerBytes = null, trailerBytes = null;
544:
545: try {
546: headerBytes = new String(header).getBytes(ENCODING);
547: trailerBytes = htmlSuffix.getBytes(ENCODING);
548: } catch (UnsupportedEncodingException cannotHappen) {
549: }
550:
551: byte[] retval = new byte[headerBytes.length + bytes.length
552: + trailerBytes.length];
553:
554: System.arraycopy(headerBytes, 0, retval, 0, headerBytes.length);
555: System.arraycopy(bytes, 0, retval, headerBytes.length,
556: bytes.length - 1);
557: System.arraycopy(trailerBytes, 0, retval, headerBytes.length
558: + bytes.length - 1, trailerBytes.length);
559: retval[retval.length - 1] = 0;
560:
561: return retval;
562: }
563:
564: ////////////////////////////////////
565: //decoder instance data and methods:
566:
567: private final BufferedInputStream bufferedStream;
568: private boolean descriptionParsed = false;
569: private boolean closed = false;
570:
571: // InputStreamReader uses an 8K buffer. The size is not customizable.
572: public static final int BYTE_BUFFER_LEN = 8192;
573:
574: // CharToByteUTF8.getMaxBytesPerChar returns 3, so we should not buffer
575: // more chars than 3 times the number of bytes we can buffer.
576: public static final int CHAR_BUFFER_LEN = BYTE_BUFFER_LEN / 3;
577:
578: private static final String FAILURE_MSG = "Unable to parse HTML description: ";
579: private static final String INVALID_MSG = " invalid";
580:
581: //HTML header mapping:
582: private long iHTMLStart,// StartHTML -- shift in array to the first byte after the header
583: iHTMLEnd, // EndHTML -- shift in array of last byte for HTML syntax analysis
584: iFragStart,// StartFragment -- shift in array jast after <!--StartFragment-->
585: iFragEnd, // EndFragment -- shift in array before start <!--EndFragment-->
586: iSelStart, // StartSelection -- shift in array of the first char in copied selection
587: iSelEnd; // EndSelection -- shift in array of the last char in copied selection
588: private String stBaseURL; // SourceURL -- base URL for related referenses
589: private String stVersion; // Version -- current supported version
590:
591: //Stream reader markers:
592: private long iStartOffset, iEndOffset, iReadCount;
593:
594: private EHTMLReadMode readMode;
595:
596: public HTMLCodec(InputStream _bytestream, EHTMLReadMode _readMode)
597: throws IOException {
598: bufferedStream = new BufferedInputStream(_bytestream,
599: BYTE_BUFFER_LEN);
600: readMode = _readMode;
601: }
602:
603: public synchronized String getBaseURL() throws IOException {
604: if (!descriptionParsed) {
605: parseDescription();
606: }
607: return stBaseURL;
608: }
609:
610: public synchronized String getVersion() throws IOException {
611: if (!descriptionParsed) {
612: parseDescription();
613: }
614: return stVersion;
615: }
616:
617: /**
618: * parseDescription parsing HTML clipboard header as it described in
619: * comment to convertToHTMLFormat
620: */
621: private void parseDescription() throws IOException {
622: stBaseURL = null;
623: stVersion = null;
624:
625: // initialization of array offset pointers
626: // to the same "uninitialized" state.
627: iHTMLEnd = iHTMLStart = iFragEnd = iFragStart = iSelEnd = iSelStart = -1;
628:
629: bufferedStream.mark(BYTE_BUFFER_LEN);
630: String astEntries[] = new String[] {
631: //common
632: VERSION, START_HTML, END_HTML, START_FRAGMENT,
633: END_FRAGMENT,
634: //ver 1.0
635: START_SELECTION, END_SELECTION, SOURCE_URL };
636: BufferedReader bufferedReader = new BufferedReader(
637: new InputStreamReader(bufferedStream, ENCODING),
638: CHAR_BUFFER_LEN);
639: long iHeadSize = 0;
640: long iCRSize = EOLN.length();
641: int iEntCount = astEntries.length;
642: boolean bContinue = true;
643:
644: for (int iEntry = 0; iEntry < iEntCount; ++iEntry) {
645: String stLine = bufferedReader.readLine();
646: if (null == stLine) {
647: break;
648: }
649: //some header entries are optional, but the order is fixed.
650: for (; iEntry < iEntCount; ++iEntry) {
651: if (!stLine.startsWith(astEntries[iEntry])) {
652: continue;
653: }
654: iHeadSize += stLine.length() + iCRSize;
655: String stValue = stLine.substring(
656: astEntries[iEntry].length()).trim();
657: if (null != stValue) {
658: try {
659: switch (iEntry) {
660: case 0:
661: stVersion = stValue;
662: break;
663: case 1:
664: iHTMLStart = Integer.parseInt(stValue);
665: break;
666: case 2:
667: iHTMLEnd = Integer.parseInt(stValue);
668: break;
669: case 3:
670: iFragStart = Integer.parseInt(stValue);
671: break;
672: case 4:
673: iFragEnd = Integer.parseInt(stValue);
674: break;
675: case 5:
676: iSelStart = Integer.parseInt(stValue);
677: break;
678: case 6:
679: iSelEnd = Integer.parseInt(stValue);
680: break;
681: case 7:
682: stBaseURL = stValue;
683: break;
684: }
685: ;
686: } catch (NumberFormatException e) {
687: throw new IOException(FAILURE_MSG
688: + astEntries[iEntry] + " value " + e
689: + INVALID_MSG);
690: }
691: }
692: break;
693: }
694: }
695: //some entries could absent in HTML header,
696: //so we have find they by another way.
697: if (-1 == iHTMLStart)
698: iHTMLStart = iHeadSize;
699: if (-1 == iFragStart)
700: iFragStart = iHTMLStart;
701: if (-1 == iFragEnd)
702: iFragEnd = iHTMLEnd;
703: if (-1 == iSelStart)
704: iSelStart = iFragStart;
705: if (-1 == iSelEnd)
706: iSelEnd = iFragEnd;
707:
708: //one of possible modes
709: switch (readMode) {
710: case HTML_READ_ALL:
711: iStartOffset = iHTMLStart;
712: iEndOffset = iHTMLEnd;
713: break;
714: case HTML_READ_FRAGMENT:
715: iStartOffset = iFragStart;
716: iEndOffset = iFragEnd;
717: break;
718: case HTML_READ_SELECTION:
719: default:
720: iStartOffset = iSelStart;
721: iEndOffset = iSelEnd;
722: break;
723: }
724:
725: bufferedStream.reset();
726: if (-1 == iStartOffset) {
727: throw new IOException(FAILURE_MSG + "invalid HTML format.");
728: }
729: iReadCount = bufferedStream.skip(iStartOffset);
730: if (iStartOffset != iReadCount) {
731: throw new IOException(FAILURE_MSG
732: + "Byte stream ends in description.");
733: }
734: descriptionParsed = true;
735: }
736:
737: public synchronized int read() throws IOException {
738: if (closed) {
739: throw new IOException("Stream closed");
740: }
741:
742: if (!descriptionParsed) {
743: parseDescription();
744: }
745: if (-1 != iEndOffset && iReadCount >= iEndOffset) {
746: return -1;
747: }
748:
749: int retval = bufferedStream.read();
750: if (retval == -1) {
751: return -1;
752: }
753: ++iReadCount;
754: return retval;
755: }
756:
757: public synchronized void close() throws IOException {
758: if (!closed) {
759: closed = true;
760: bufferedStream.close();
761: }
762: }
763: }
|