Clover Coverage Report
Coverage timestamp: Sat Sep 18 2010 04:09:52 UTC
../../../../../../img/srcFileCovDistChart0.png 89% of files have more coverage
151   396   64   7.19
64   237   0.42   21
21     3.05  
1    
 
  LatexTextParser       Line # 28 151 64 0% 0.0
 
No Tests
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2010, Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10    * This program is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    * GNU General Public License for more details.
14    */
15   
16    package org.qedeq.kernel.bo.service.latex;
17   
18    import org.qedeq.base.io.TextInput;
19    import org.qedeq.base.trace.Trace;
20    import org.qedeq.kernel.bo.parser.MementoTextInput;
21   
22    /**
23    * Transform LaTeX into QEDEQ format.
24    *
25    * @version $Revision: 1.1 $
26    * @author Michael Meyling
27    */
 
28    public final class LatexTextParser {
29   
30    /** This class. */
31    private static final Class CLASS = LatexTextParser.class;
32   
33    /** These characters get a special treatment in LaTeX. */
34    private static final String SPECIALCHARACTERS = "(),{}\\~%$&";
35   
36    /** This is our input stream .*/
37    private MementoTextInput input;
38   
39    /** Herein goes our output. */
40    private StringBuffer output;
41   
42    /**
43    * Parse LaTeX text into QEDEQ module string.
44    *
45    * @param input Parse this input.
46    * @return QEDEQ module string.
47    */
 
48  0 toggle public static final String transform(final String input) {
49  0 final LatexTextParser parser = new LatexTextParser(input);
50  0 return parser.parse();
51    }
52   
53    /**
54    * Constructor.
55    *
56    * @param input Parse this input.
57    */
 
58  0 toggle private LatexTextParser(final String input) {
59  0 this.input = new MementoTextInput(new TextInput(input));
60  0 this.output = new StringBuffer();
61    }
62   
63    /**
64    * Do parsing.
65    *
66    * @return QEDEQ module string.
67    */
 
68  0 toggle private String parse() {
69  0 while (!eof()) {
70  0 final String token = readToken();
71  0 if ("\\begin".equals(token)) {
72  0 final String curly = readCurlyBraceContents();
73  0 if ("eqnarray".equals(curly)) {
74  0 printMathTillEnd(curly);
75  0 } else if ("eqnarray*".equals(curly)) {
76  0 printMathTillEnd(curly);
77  0 } else if ("equation".equals(curly)) {
78  0 printMathTillEnd(curly);
79  0 } else if ("equation*".equals(curly)) {
80  0 printMathTillEnd(curly);
81    } else {
82  0 print(token + "{" + curly + "}");
83    }
84  0 } else if ("$$".equals(token)) {
85  0 println();
86  0 println("<MATH>");
87  0 printMathTillToken(token);
88  0 println("\\,</MATH>");
89  0 println();
90  0 } else if ("$".equals(token)) {
91  0 print("<MATH>");
92  0 printMathTillToken(token);
93  0 print("\\,</MATH>");
94    } else {
95  0 print(token);
96    }
97    }
98  0 return output.toString();
99    }
100   
 
101  0 toggle private void printMathTillEnd(final String curly) {
102  0 final StringBuffer buffer = new StringBuffer();
103  0 do {
104  0 final String item = readToken();
105  0 if ("\\end".equals(item)) {
106  0 final String curly2 = readCurlyBraceContents();
107  0 if (curly.equals(curly2)) {
108  0 break;
109    }
110  0 buffer.append(item + "{" + curly2 + "}");
111    } else {
112  0 buffer.append(item);
113    }
114    } while (true);
115   
116    /*
117    println("\\begin{" + curly + "}");
118    println(buffer);
119    println("\\end{" + curly + "}");
120    println();
121    */
122  0 printMath(buffer);
123    }
124   
125    /**
126    * Print math content till <code>token</code> occurs.
127    *
128    * @param token Terminator token.
129    */
 
130  0 toggle private void printMathTillToken(final String token) {
131  0 final StringBuffer buffer = new StringBuffer();
132  0 do {
133  0 final String item = readToken();
134  0 if (token.equals(item)) {
135  0 break;
136    } else {
137  0 buffer.append(item);
138    }
139    } while (true);
140  0 printMath(buffer);
141    }
142   
143    /**
144    * Print math content.
145    *
146    * @param buffer This should be printed as mathematical content.
147    */
 
148  0 toggle private void printMath(final StringBuffer buffer) {
149  0 print(buffer.toString());
150    }
151   
152    /**
153    * Read next token from input stream.
154    *
155    * @return Read token.
156    */
 
157  0 toggle protected final String readToken() {
158  0 final String method = "readToken()";
159  0 Trace.begin(CLASS, this, method);
160  0 StringBuffer token = new StringBuffer();
161  0 try {
162  0 do {
163  0 if (eof()) {
164  0 if (token.length() <= 0) {
165  0 token = null;
166    }
167  0 break;
168    }
169  0 final int c = getChar();
170  0 if (Character.isDigit((char) c)) {
171  0 token.append((char) readChar());
172  0 if (Character.isDigit((char) getChar())) {
173  0 continue;
174    }
175  0 break;
176    }
177  0 if (Character.isLetter((char) c)) {
178  0 token.append((char) readChar());
179  0 if (Character.isLetter((char) getChar())) {
180  0 continue;
181    }
182  0 break;
183    }
184  0 if (SPECIALCHARACTERS.indexOf(c) >= 0) {
185  0 switch (c) {
186  0 case '&':
187  0 case '%':
188  0 case '{':
189  0 case '}':
190  0 case '~':
191  0 token.append((char) readChar());
192  0 break;
193  0 case '$':
194  0 token.append((char) readChar());
195  0 if ('$' == getChar()) {
196  0 continue;
197    }
198  0 break;
199  0 case '\\':
200  0 final String t = readBackslashToken();
201  0 token.append(t);
202  0 if ('_' == getChar() || '^' == getChar()) {
203  0 token.append((char) readChar());
204  0 continue;
205    }
206  0 break;
207  0 default:
208  0 readChar();
209  0 token.append((char) c);
210    }
211  0 break;
212    }
213  0 token.append((char) readChar());
214  0 if ('_' == getChar() || '^' == getChar()) {
215  0 token.append((char) readChar());
216  0 continue;
217    }
218  0 break;
219  0 } while (!eof());
220  0 Trace.param(CLASS, this, method, "Read token", token);
221  0 return (token != null ? token.toString() : null);
222    } finally {
223  0 Trace.end(CLASS, this, method);
224    }
225    }
226   
227    /**
228    * Get token that starts with a backlash. The backslash itself is removed from the token.
229    *
230    * @return Token (without backslash).
231    */
 
232  0 toggle private String readBackslashToken() {
233  0 final String method = "readBackslashToken()";
234  0 Trace.begin(CLASS, this, method);
235  0 if (getChar() != '\\') {
236  0 throw new IllegalArgumentException("\\ expected");
237    }
238  0 readChar(); // read \
239  0 if (eof()) {
240  0 Trace.param(CLASS, this, method, "return", null);
241  0 Trace.end(CLASS, this, method);
242  0 return null;
243    }
244  0 if (!Character.isLetter((char) getChar())) {
245  0 Trace.param(CLASS, this, method, "return", (char) getChar());
246  0 Trace.end(CLASS, this, method);
247  0 return "\\" + ((char) readChar());
248    }
249  0 final StringBuffer buffer = new StringBuffer("\\");
250  0 do {
251  0 buffer.append((char) readChar());
252  0 } while (!eof() && Character.isLetter((char) getChar()));
253  0 Trace.param(CLASS, this, method, "return", buffer.toString());
254  0 Trace.end(CLASS, this, method);
255  0 return buffer.toString();
256    }
257   
258    /**
259    * Read contents that is within { .. }.
260    *
261    * @return Contents.
262    */
 
263  0 toggle private String readCurlyBraceContents() {
264  0 final String first = readToken();
265  0 if (!"{".equals(first)) {
266  0 throw new IllegalArgumentException("\"{\" expected, but was: \"" + first + "\"");
267    }
268  0 final StringBuffer buffer = new StringBuffer();
269  0 String next;
270  0 int level = 1;
271  0 while (level > 0) {
272  0 next = readToken();
273  0 if ("{".equals(next)) {
274  0 level++;
275  0 } else if ("}".equals(next)) {
276  0 level--;
277    }
278  0 if (level <= 0) {
279  0 break;
280    }
281  0 buffer.append(next);
282    }
283  0 return buffer.toString();
284    }
285   
286    /**
287    * Print <code>line</code> to output stream.
288    *
289    * @param line Print this.
290    */
 
291  0 toggle private final void print(final String line) {
292  0 output.append(line);
293    }
294   
295    /**
296    * Print end of line.
297    */
 
298  0 toggle private final void println() {
299  0 println("");
300    }
301   
302    /**
303    * Print <code>line</code> and start new line to output stream.
304    *
305    * @param line Print this.
306    */
 
307  0 toggle private final void println(final String line) {
308  0 print(line);
309  0 print("\n");
310    }
311   
312    /**
313    * Read next token from input but don't move reading position.
314    *
315    * @return Token read, is <code>null</code> if end of data reached.
316    */
 
317  0 toggle public final String getToken() {
318  0 markPosition();
319  0 final String result = readToken();
320  0 rewindPosition();
321  0 return result;
322    }
323   
324    /**
325    * Remember current position.
326    */
 
327  0 toggle protected final void markPosition() {
328  0 input.markPosition();
329    }
330   
331    /**
332    * Rewind to previous marked position. Also clears the mark.
333    *
334    * @return Current position before pop.
335    */
 
336  0 toggle protected final long rewindPosition() {
337  0 return input.rewindPosition();
338    }
339   
340    /**
341    * Forget last remembered position.
342    */
 
343  0 toggle protected final void clearMark() {
344  0 input.clearMark();
345    }
346   
347    /**
348    * Get byte position.
349    *
350    * @return Position.
351    */
 
352  0 toggle protected long getPosition() {
353  0 return input.getPosition();
354    }
355   
356    /**
357    * Reads a single character and does not change the reading
358    * position.
359    *
360    * @return character read, if there are no more chars
361    * <code>Character.MAX_VALUE</code> is returned
362    */
 
363  0 toggle protected final int getChar() {
364  0 return input.getChar();
365    }
366   
367    /**
368    * Reads a single character and increments the reading position
369    * by one.
370    *
371    * @return character read, if there are no more chars
372    * <code>Character.MAX_VALUE</code> is returned
373    */
 
374  0 toggle protected final int readChar() {
375  0 return input.readChar();
376    }
377   
378    /**
379    * Are there still any characters to read?
380    *
381    * @return Anything left for reading further?
382    */
 
383  0 toggle public final boolean eof() {
384  0 return input.eof();
385    }
386   
387    /**
388    * Get rewind stack size.
389    *
390    * @return Rewind stack size.
391    */
 
392  0 toggle public final int getRewindStackSize() {
393  0 return input.getRewindStackSize();
394    }
395   
396    }