AbstractFilter.java revision 10444:f08705540498
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing.text.rtf;
26
27import java.io.*;
28import java.lang.*;
29
30/**
31 * A generic superclass for streams which read and parse text
32 * consisting of runs of characters interspersed with occasional
33 * ``specials'' (formatting characters).
34 *
35 * <p> Most of the functionality
36 * of this class would be redundant except that the
37 * <code>ByteToChar</code> converters
38 * are suddenly private API. Presumably this class will disappear
39 * when the API is made public again. (sigh) That will also let us handle
40 * multibyte character sets...
41 *
42 * <P> A subclass should override at least <code>write(char)</code>
43 * and <code>writeSpecial(int)</code>. For efficiency's sake it's a
44 * good idea to override <code>write(String)</code> as well. The subclass'
45 * initializer may also install appropriate translation and specials tables.
46 *
47 * @see OutputStream
48 */
49abstract class AbstractFilter extends OutputStream
50{
51    /** A table mapping bytes to characters */
52    protected char translationTable[];
53    /** A table indicating which byte values should be interpreted as
54     *  characters and which should be treated as formatting codes */
55    protected boolean specialsTable[];
56
57    /** A translation table which does ISO Latin-1 (trivial) */
58    static final char latin1TranslationTable[];
59    /** A specials table which indicates that no characters are special */
60    static final boolean noSpecialsTable[];
61    /** A specials table which indicates that all characters are special */
62    static final boolean allSpecialsTable[];
63
64    static {
65      int i;
66
67      noSpecialsTable = new boolean[256];
68      for (i = 0; i < 256; i++)
69        noSpecialsTable[i] = false;
70
71      allSpecialsTable = new boolean[256];
72      for (i = 0; i < 256; i++)
73        allSpecialsTable[i] = true;
74
75      latin1TranslationTable = new char[256];
76      for (i = 0; i < 256; i++)
77        latin1TranslationTable[i] = (char)i;
78    }
79
80    /**
81     * A convenience method that reads text from a FileInputStream
82     * and writes it to the receiver.
83     * The format in which the file
84     * is read is determined by the concrete subclass of
85     * AbstractFilter to which this method is sent.
86     * <p>This method does not close the receiver after reaching EOF on
87     * the input stream.
88     * The user must call <code>close()</code> to ensure that all
89     * data are processed.
90     *
91     * @param in      An InputStream providing text.
92     */
93    public void readFromStream(InputStream in)
94      throws IOException
95    {
96        byte buf[];
97        int count;
98
99        buf = new byte[16384];
100
101        while(true) {
102            count = in.read(buf);
103            if (count < 0)
104                break;
105
106            this.write(buf, 0, count);
107        }
108    }
109
110    public void readFromReader(Reader in)
111      throws IOException
112    {
113        char buf[];
114        int count;
115
116        buf = new char[2048];
117
118        while(true) {
119            count = in.read(buf);
120            if (count < 0)
121                break;
122            for (int i = 0; i < count; i++) {
123              this.write(buf[i]);
124            }
125        }
126    }
127
128    public AbstractFilter()
129    {
130        translationTable = latin1TranslationTable;
131        specialsTable = noSpecialsTable;
132    }
133
134    /**
135     * Implements the abstract method of OutputStream, of which this class
136     * is a subclass.
137     */
138    public void write(int b)
139      throws IOException
140    {
141      if (b < 0)
142        b += 256;
143      if (specialsTable[b])
144        writeSpecial(b);
145      else {
146        char ch = translationTable[b];
147        if (ch != (char)0)
148          write(ch);
149      }
150    }
151
152    /**
153     * Implements the buffer-at-a-time write method for greater
154     * efficiency.
155     *
156     * <p> <strong>PENDING:</strong> Does <code>write(byte[])</code>
157     * call <code>write(byte[], int, int)</code> or is it the other way
158     * around?
159     */
160    public void write(byte[] buf, int off, int len)
161      throws IOException
162    {
163      StringBuilder accumulator = null;
164      while (len > 0) {
165        short b = (short)buf[off];
166
167        // stupid signed bytes
168        if (b < 0)
169            b += 256;
170
171        if (specialsTable[b]) {
172          if (accumulator != null) {
173            write(accumulator.toString());
174            accumulator = null;
175          }
176          writeSpecial(b);
177        } else {
178          char ch = translationTable[b];
179          if (ch != (char)0) {
180            if (accumulator == null)
181              accumulator = new StringBuilder();
182            accumulator.append(ch);
183          }
184        }
185
186        len --;
187        off ++;
188      }
189
190      if (accumulator != null)
191        write(accumulator.toString());
192    }
193
194    /**
195     * Hopefully, all subclasses will override this method to accept strings
196     * of text, but if they don't, AbstractFilter's implementation
197     * will spoon-feed them via <code>write(char)</code>.
198     *
199     * @param s The string of non-special characters written to the
200     *          OutputStream.
201     */
202    public void write(String s)
203      throws IOException
204    {
205      int index, length;
206
207      length = s.length();
208      for(index = 0; index < length; index ++) {
209        write(s.charAt(index));
210      }
211    }
212
213    /**
214     * Subclasses must provide an implementation of this method which
215     * accepts a single (non-special) character.
216     *
217     * @param ch The character written to the OutputStream.
218     */
219    protected abstract void write(char ch) throws IOException;
220
221    /**
222     * Subclasses must provide an implementation of this method which
223     * accepts a single special byte. No translation is performed
224     * on specials.
225     *
226     * @param b The byte written to the OutputStream.
227     */
228    protected abstract void writeSpecial(int b) throws IOException;
229}
230