1 /*
   2  * Copyright (c) 1997, 2018, 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  */
  25 package javax.swing.text.rtf;
  26 
  27 import java.io.*;
  28 import 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  */
  49 abstract 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 }