1 /* RTFScanner.java --
2    Copyright (C) 2005  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.swing.text.rtf;
40 
41 import java.io.BufferedReader;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.io.Reader;
46 
47 /**
48  * Provides a scanner that scans an {@link InputStream} for tokens of the
49  * RTF syntax.
50  *
51  * This scanner is based upon the RTF specification 1.6
52  * available at:
53  *
54  * <a
55  * href="http://msdn.microsoft.com/library/en-us/dnrtfspec/html/rtfspec.asp">
56  * RTF specification at MSDN</a>
57  *
58  * @author Roman Kennke (roman@ontographics.com)
59  */
60 class RTFScanner
61 {
62 
63   /**
64    * The reader from which we read the RTF data.
65    */
66   private Reader in;
67 
68   /**
69    * This is used to constuct strings from the read in chars.
70    */
71   private StringBuffer buffer;
72 
73   /**
74    * Constructs a new RTFScanner without initializing the {@link Reader}.
75    */
RTFScanner()76   private RTFScanner()
77   {
78     buffer = new StringBuffer();
79   }
80 
81   /**
82    * Constructs a new RTFScanner for the given {@link InputStream}.
83    * The stream is wrapped into an {@link InputStreamReader} and if it's
84    * not yet buffered then the Reader is wrapped in a {@link BufferedReader}
85    *
86    * @param stream the {@link InputStream} to read RTF data from
87    */
RTFScanner(InputStream stream)88   public RTFScanner(InputStream stream)
89   {
90     this();
91     InputStreamReader reader = new InputStreamReader(stream);
92     in = new BufferedReader(reader);
93   }
94 
95   /**
96    * Constructs a new RTFScanner for the given {@link Reader}.
97    *
98    * If the reader is not an instance of {@link BufferedReader} then it
99    * is wrapped into a BufferedReader.
100    *
101    * @param reader the {@link BufferedReader} to read RTF data from
102    */
RTFScanner(Reader reader)103   public RTFScanner(Reader reader)
104   {
105     this();
106     if (reader instanceof BufferedReader)
107       {
108         in = reader;
109       }
110     else
111       {
112         in = new BufferedReader(reader);
113       }
114   }
115 
116   /**
117    * Reads in the next {@link Token} from the stream.
118    *
119    * @return the read {@link Token}
120    *
121    * @throws IOException if the underlying stream has problems
122    */
readToken()123   public Token readToken()
124     throws IOException
125   {
126     Token token = null;
127 
128     int c = in.read();
129     switch(c)
130       {
131       case -1:
132         token = new Token(Token.EOF);
133         break;
134 
135       case '{':
136         token = new Token(Token.LCURLY);
137         break;
138 
139       case '}':
140         token = new Token(Token.RCURLY);
141         break;
142 
143       case '\\':
144         buffer.delete(0, buffer.length());
145         buffer.append((char) c);
146         token = readControlWord();
147         break;
148 
149       default:
150         buffer.delete(0, buffer.length());
151         buffer.append((char) c);
152         token = readText();
153         break;
154       }
155 
156     return token;
157   }
158 
159   /**
160    * Reads in a control word and optional parameter.
161    *
162    * @return the read in control word as {@link ControlWordToken}
163    *
164    * @throws IOException if the underlying stream has problems
165    */
readControlWord()166   private Token readControlWord()
167     throws IOException
168   {
169     // this flag indicates if we are still reading the name or are already
170     // in the parameter
171     boolean readingName = true;
172     String name = null;
173     String param = null;
174 
175     while (true)
176       {
177         in.mark(1);
178         int c = in.read();
179 
180         // check for 'a'..'z'
181         if (readingName && (c >= 'a') && (c <= 'z'))
182           {
183             buffer.append((char) c);
184           }
185         else if ((c >= '0') && (c <= '9'))
186           {
187             // if the last char was in the name, then finish reading the name
188             if (readingName)
189               {
190                 name = buffer.toString();
191                 buffer.delete(0, buffer.length());
192                 readingName = false;
193               }
194             buffer.append((char) c);
195           }
196         else
197           {
198             // if we were in the name, then finish this
199             if (readingName)
200               {
201                 name = buffer.toString();
202               }
203             // otherwise finish the parameter
204             else
205               {
206                 param = buffer.toString();
207               }
208 
209             // clear up
210             buffer.delete(0, buffer.length());
211             // reset input buffer to last char
212             in.reset();
213             // break while loop
214             break;
215           }
216       }
217 
218     ControlWordToken token = null;
219 
220     if (param == null)
221       token = new ControlWordToken(name);
222     else
223       token =new ControlWordToken(name, Integer.parseInt(param));
224 
225     return token;
226 
227   }
228 
229   /**
230    * Reads in a block of text.
231    *
232    * @return the token for the text
233    */
readText()234   private Token readText()
235     throws IOException
236   {
237 
238     boolean readingText = true;
239     while (readingText)
240       {
241         in.mark(1);
242         int c = in.read();
243         switch(c)
244           {
245           case '\\':
246           case '{':
247           case '}':
248           case -1:
249             readingText = false;
250             in.reset();
251             break;
252 
253           default:
254             buffer.append((char) c);
255             break;
256           }
257 
258       }
259 
260     String text = buffer.toString();
261     Token token = new TextToken(text);
262 
263     buffer.delete(0, buffer.length());
264 
265     return token;
266 
267   }
268 }
269