/* * Copyright (c) 1997, 2018, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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. */ package javax.swing.text.rtf; import java.io.*; import java.lang.*; /** * A generic superclass for streams which read and parse text * consisting of runs of characters interspersed with occasional * ``specials'' (formatting characters). * *
Most of the functionality
* of this class would be redundant except that the
* ByteToChar
converters
* are suddenly private API. Presumably this class will disappear
* when the API is made public again. (sigh) That will also let us handle
* multibyte character sets...
*
*
A subclass should override at least write(char)
* and writeSpecial(int)
. For efficiency's sake it's a
* good idea to override write(String)
as well. The subclass'
* initializer may also install appropriate translation and specials tables.
*
* @see OutputStream
*/
abstract class AbstractFilter extends OutputStream
{
/** A table mapping bytes to characters */
protected char[] translationTable;
/** A table indicating which byte values should be interpreted as
* characters and which should be treated as formatting codes */
protected boolean[] specialsTable;
/** A translation table which does ISO Latin-1 (trivial) */
static final char[] latin1TranslationTable;
/** A specials table which indicates that no characters are special */
static final boolean[] noSpecialsTable;
/** A specials table which indicates that all characters are special */
static final boolean[] allSpecialsTable;
static {
int i;
noSpecialsTable = new boolean[256];
for (i = 0; i < 256; i++)
noSpecialsTable[i] = false;
allSpecialsTable = new boolean[256];
for (i = 0; i < 256; i++)
allSpecialsTable[i] = true;
latin1TranslationTable = new char[256];
for (i = 0; i < 256; i++)
latin1TranslationTable[i] = (char)i;
}
/**
* A convenience method that reads text from a FileInputStream
* and writes it to the receiver.
* The format in which the file
* is read is determined by the concrete subclass of
* AbstractFilter to which this method is sent.
*
This method does not close the receiver after reaching EOF on
* the input stream.
* The user must call close()
to ensure that all
* data are processed.
*
* @param in An InputStream providing text.
*/
public void readFromStream(InputStream in)
throws IOException
{
byte[] buf;
int count;
buf = new byte[16384];
while(true) {
count = in.read(buf);
if (count < 0)
break;
this.write(buf, 0, count);
}
}
public void readFromReader(Reader in)
throws IOException
{
char[] buf;
int count;
buf = new char[2048];
while(true) {
count = in.read(buf);
if (count < 0)
break;
for (int i = 0; i < count; i++) {
this.write(buf[i]);
}
}
}
public AbstractFilter()
{
translationTable = latin1TranslationTable;
specialsTable = noSpecialsTable;
}
/**
* Implements the abstract method of OutputStream, of which this class
* is a subclass.
*/
public void write(int b)
throws IOException
{
if (b < 0)
b += 256;
if (specialsTable[b])
writeSpecial(b);
else {
char ch = translationTable[b];
if (ch != (char)0)
write(ch);
}
}
/**
* Implements the buffer-at-a-time write method for greater
* efficiency.
*
*
PENDING: Does write(byte[])
* call write(byte[], int, int)
or is it the other way
* around?
*/
public void write(byte[] buf, int off, int len)
throws IOException
{
StringBuilder accumulator = null;
while (len > 0) {
short b = (short)buf[off];
// stupid signed bytes
if (b < 0)
b += 256;
if (specialsTable[b]) {
if (accumulator != null) {
write(accumulator.toString());
accumulator = null;
}
writeSpecial(b);
} else {
char ch = translationTable[b];
if (ch != (char)0) {
if (accumulator == null)
accumulator = new StringBuilder();
accumulator.append(ch);
}
}
len --;
off ++;
}
if (accumulator != null)
write(accumulator.toString());
}
/**
* Hopefully, all subclasses will override this method to accept strings
* of text, but if they don't, AbstractFilter's implementation
* will spoon-feed them via write(char)
.
*
* @param s The string of non-special characters written to the
* OutputStream.
*/
public void write(String s)
throws IOException
{
int index, length;
length = s.length();
for(index = 0; index < length; index ++) {
write(s.charAt(index));
}
}
/**
* Subclasses must provide an implementation of this method which
* accepts a single (non-special) character.
*
* @param ch The character written to the OutputStream.
*/
protected abstract void write(char ch) throws IOException;
/**
* Subclasses must provide an implementation of this method which
* accepts a single special byte. No translation is performed
* on specials.
*
* @param b The byte written to the OutputStream.
*/
protected abstract void writeSpecial(int b) throws IOException;
}