blob: 618743ee6274c392d59c204405f676ea6e367979 [file] [log] [blame]
/*
* Copyright (c) 1997, 2010, 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).
*
* <p> Most of the functionality
* of this class would be redundant except that the
* <code>ByteToChar</code> 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...
*
* <P> A subclass should override at least <code>write(char)</code>
* and <code>writeSpecial(int)</code>. For efficiency's sake it's a
* good idea to override <code>write(String)</code> 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.
* <p>This method does not close the receiver after reaching EOF on
* the input stream.
* The user must call <code>close()</code> 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.
*
* <p> <strong>PENDING:</strong> Does <code>write(byte[])</code>
* call <code>write(byte[], int, int)</code> 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 <code>write(char)</code>.
*
* @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;
}