~ubuntu-branches/ubuntu/utopic/libjmathtex-java/utopic

« back to all changes in this revision

Viewing changes to be/ugent/caagt/jmathtex/TeXFormula.java

  • Committer: Bazaar Package Importer
  • Author(s): Adriaan Peeters
  • Date: 2007-09-25 14:07:55 UTC
  • Revision ID: james.westby@ubuntu.com-20070925140755-mro37pqu4wkhqa9t
Tags: upstream-0.7~pre
ImportĀ upstreamĀ versionĀ 0.7~pre

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* TeXFormula.java
 
2
 * =========================================================================
 
3
 * This file is part of the JMathTeX Library - http://jmathtex.sourceforge.net
 
4
 *
 
5
 * Copyright (C) 2004-2007 Universiteit Gent
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or modify
 
8
 * it under the terms of the GNU General Public License as published by
 
9
 * the Free Software Foundation; either version 2 of the License, or (at
 
10
 * your option) any later version.
 
11
 *
 
12
 * This program is distributed in the hope that it will be useful, but
 
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * General Public License for more details.
 
16
 *
 
17
 * A copy of the GNU General Public License can be found in the file
 
18
 * LICENSE.txt provided with the source distribution of this program (see
 
19
 * the META-INF directory in the source jar). This license can also be
 
20
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 
21
 *
 
22
 * If you did not receive a copy of the GNU General Public License along
 
23
 * with this program, contact the lead developer, or write to the Free
 
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
25
 * 02110-1301, USA.
 
26
 *
 
27
 */
 
28
 
 
29
package be.ugent.caagt.jmathtex;
 
30
 
 
31
import java.awt.Color;
 
32
import java.util.HashMap;
 
33
import java.util.HashSet;
 
34
import java.util.Map;
 
35
import java.util.List;
 
36
import java.util.Set;
 
37
 
 
38
/**
 
39
 * Represents a logical mathematical formula that will be displayed (by creating a
 
40
 * {@link TeXIcon} from it and painting it) using algorithms that are based on the
 
41
 * TeX algorithms.
 
42
 * <p>
 
43
 * These formula's can be built using the built-in primitive TeX parser
 
44
 * (methods with String arguments) or using other TeXFormula objects. Most methods
 
45
 * have (an) equivalent(s) where one or more TeXFormula arguments are replaced with
 
46
 * String arguments. These are just shorter notations, because all they do is parse
 
47
 * the string(s) to TeXFormula's and call an equivalent method with (a) TeXFormula argument(s).
 
48
 * Most methods also come in 2 variants. One kind will use this TeXFormula to build
 
49
 * another mathematical construction and then change this object to represent the newly
 
50
 * build construction. The other kind will only use other
 
51
 * TeXFormula's (or parse strings), build a mathematical construction with them and
 
52
 * insert this newly build construction at the end of this TeXFormula.
 
53
 * Because all the provided methods return a pointer to this (modified) TeXFormula
 
54
 * (except for the createTeXIcon method that returns a TeXIcon pointer),
 
55
 * method chaining is also possible.
 
56
 * <p>
 
57
 * <b> Important: All the provided methods modify this TeXFormula object, but all the
 
58
 * TeXFormula arguments of these methods will remain unchanged and independent of
 
59
 * this TeXFormula object!</b>
 
60
 */
 
61
public class TeXFormula {
 
62
    
 
63
    // TeX commands and text styles (for parsing)
 
64
    private static Set<String> commands = new HashSet<String>();
 
65
    
 
66
    private static Set<String> textStyles;
 
67
    
 
68
    // table for putting delimiters over and under formula's,
 
69
    // indexed by constants from "TeXConstants"
 
70
    private static final String[][] delimiterNames = {
 
71
        { "lbrace", "rbrace" },
 
72
        { "lsqbrack", "rsqbrack" },
 
73
        { "lbrack", "rbrack" },
 
74
        { "downarrow", "downarrow" },
 
75
        { "uparrow", "uparrow" },
 
76
        { "updownarrow", "updownarrow" },
 
77
        { "Downarrow", "Downarrow" },
 
78
        { "Uparrow", "Uparrow" },
 
79
        { "Updownarrow", "Updownarrow" },
 
80
        { "vert", "vert" },
 
81
        { "Vert", "Vert" }
 
82
    };
 
83
    
 
84
    // the escape character
 
85
    private static final char ESCAPE = '\\';
 
86
    
 
87
    // grouping characters (for parsing)
 
88
    private static final char L_GROUP = '{';
 
89
    private static final char R_GROUP = '}';
 
90
    private static final char L_BRACK = '[';
 
91
    private static final char R_BRACK = ']';
 
92
    
 
93
    // used as second index in "delimiterNames" table (over or under)
 
94
    private static final int OVER_DEL = 0;
 
95
    private static final int UNDER_DEL = 1;
 
96
    
 
97
    // for comparing floats with 0
 
98
    protected static final float PREC = 0.0000001f;
 
99
    
 
100
    // predefined TeXFormula's
 
101
    private static Map<String,TeXFormula> predefinedTeXFormulas = new HashMap<String,TeXFormula>();
 
102
    
 
103
    // script characters (for parsing)
 
104
    private static final char SUB_SCRIPT = '_';
 
105
    private static final char SUPER_SCRIPT = '^';
 
106
    private static final char PRIME = '\'';
 
107
    
 
108
    // character-to-symbol and character-to-delimiter mappings
 
109
    private static String[] symbolMappings;
 
110
    private static String[] delimiterMappings;
 
111
    
 
112
    static {
 
113
        // character-to-symbol and character-to-delimiter mappings
 
114
        TeXFormulaSettingsParser parser = new TeXFormulaSettingsParser();
 
115
        symbolMappings = parser.parseSymbolMappings();
 
116
        delimiterMappings = parser.parseDelimiterMappings();
 
117
        
 
118
        // textstyle commands
 
119
        textStyles = parser.parseTextStyles();
 
120
        
 
121
        // commands
 
122
        commands.add("frac");
 
123
        commands.add("sqrt");
 
124
        
 
125
        // predefined TeXFormula's
 
126
        new PredefinedTeXFormulaParser().parse(predefinedTeXFormulas);
 
127
    }
 
128
    
 
129
    // the string to be parsed
 
130
    private String parseString;
 
131
    
 
132
    // current position in the parse string
 
133
    private int pos = 0;
 
134
    
 
135
    // the root atom of the "atom tree" that represents the formula
 
136
    protected Atom root = null;
 
137
    
 
138
    // the current text style
 
139
    private String textStyle = null;
 
140
    
 
141
    /**
 
142
     * Creates an empty TeXFormula.
 
143
     *
 
144
     */
 
145
    public TeXFormula() {
 
146
        // do nothing
 
147
    }
 
148
    
 
149
    /**
 
150
     * Creates a new TeXFormula from a list of TeXFormula objects.
 
151
     * <p>
 
152
     * If the list is empty (or null), then an empty TeXFormula will be created.
 
153
     * Otherwise, the newly created TeXFormula is the same as if
 
154
     * all the TeXFormula's in the list were added one after another (starting with the
 
155
     * first one) to an empty TeXFormula using the {@link #add(TeXFormula)} method.
 
156
     * <p>
 
157
     * <b> The new TeXFormula is independent of all the TeXFormula's from the list!</b>
 
158
     *
 
159
     * @param l a list of TeXFormula objects
 
160
     */
 
161
    public TeXFormula(List<TeXFormula> l) {
 
162
        if (l != null) {
 
163
            if (l.size() == 1)
 
164
                addImpl(l.get(0));
 
165
            else {
 
166
                try {
 
167
                    root = new RowAtom(l);
 
168
                } catch (EmptyFormulaException e) {
 
169
                    root = null;
 
170
                }
 
171
            }
 
172
        }
 
173
    }
 
174
    
 
175
    /**
 
176
     * Creates a new TeXFormula by parsing the given string (using a primitive TeX parser).
 
177
     *
 
178
     * @param s the string to be parsed
 
179
     * @throws ParseException if the string could not be parsed correctly
 
180
     */
 
181
    public TeXFormula(String s) throws ParseException {
 
182
        this(s, null);
 
183
    }
 
184
    
 
185
   /*
 
186
    * Creates a TeXFormula by parsing the given string in the given text style.
 
187
    * Used when a text style command was found in the parse string.
 
188
    */
 
189
    private TeXFormula(String s, String textStyle) throws ParseException {
 
190
        this.textStyle = textStyle;
 
191
        if (s != null && s.length() != 0)
 
192
            parse(s);
 
193
    }
 
194
    
 
195
    /**
 
196
     * Creates a new TeXFormula that is a copy of the given TeXFormula.
 
197
     * <p>
 
198
     * <b>Both TeXFormula's are independent of one another!</b>
 
199
     *
 
200
     * @param f the formula to be copied
 
201
     */
 
202
    public TeXFormula(TeXFormula f) {
 
203
        if (f != null)
 
204
            addImpl(f);
 
205
    }
 
206
    
 
207
   /*
 
208
    * Inserts an atom at the end of the current formula
 
209
    */
 
210
    private TeXFormula add(Atom el) {
 
211
        if (el != null) {
 
212
            if (root == null)
 
213
                root = el;
 
214
            else {
 
215
                if (!(root instanceof RowAtom))
 
216
                    root = new RowAtom(root);
 
217
                ((RowAtom) root).add(el);
 
218
            }
 
219
        }
 
220
        return this;
 
221
    }
 
222
    
 
223
    /**
 
224
     * Parses the given string and inserts the resulting formula
 
225
     * at the end of the current TeXFormula.
 
226
     *
 
227
     * @param s the string to be parsed and inserted
 
228
     * @throws ParseException if the string could not be parsed correctly
 
229
     * @return the modified TeXFormula
 
230
     */
 
231
    public TeXFormula add(String s) throws ParseException {
 
232
        if (s != null && s.length() != 0) {
 
233
            // reset parsing variables
 
234
            textStyle = null;
 
235
            pos = 0;
 
236
            // parse and add the string
 
237
            parse(s);
 
238
        }
 
239
        return this;
 
240
    }
 
241
    
 
242
    /**
 
243
     * Inserts the given TeXFormula at the end of the current TeXFormula.
 
244
     *
 
245
     * @param f the TeXFormula to be inserted
 
246
     * @return the modified TeXFormula
 
247
     */
 
248
    public TeXFormula add(TeXFormula f) {
 
249
        addImpl (f);
 
250
        return this;
 
251
    }
 
252
    
 
253
    private void addImpl (TeXFormula f) {
 
254
        if (f.root != null) {
 
255
            // special copy-treatment for Mrow as a root!!
 
256
            if (f.root instanceof RowAtom)
 
257
                add(new RowAtom(f.root));
 
258
            else
 
259
                add(f.root);
 
260
        }
 
261
    }
 
262
    
 
263
    /**
 
264
     * Centers the current TeXformula vertically on the axis (defined by the parameter
 
265
     * "axisheight" in the resource "DefaultTeXFont.xml".
 
266
     *
 
267
     * @return the modified TeXFormula
 
268
     */
 
269
    public TeXFormula centerOnAxis() {
 
270
        root = new VCenteredAtom(root);
 
271
        return this;
 
272
    }
 
273
    
 
274
    /**
 
275
     * Parses the given string(s) into a TeXFormula, puts the given accent above it and
 
276
     * inserts the result at the end of the current TeXFormula.
 
277
     *
 
278
     * @param s the string to be parsed into a TeXFormula above which te given accent
 
279
     *                  will be placed
 
280
     * @param accentName the name of the accent symbol
 
281
     * @return the modified TeXFormula
 
282
     * @throws InvalidSymbolTypeException if the symbol is not defined as an accent
 
283
     * @throws SymbolNotFoundException if there's no symbol defined with the given name
 
284
     * @throws ParseException if the string(s) could not be parsed correctly
 
285
     */
 
286
    public TeXFormula addAcc(String s, String accentName)
 
287
    throws InvalidSymbolTypeException, SymbolNotFoundException,
 
288
            ParseException {
 
289
        return addAcc(new TeXFormula(s), accentName);
 
290
    }
 
291
    
 
292
    /**
 
293
     * Puts the given accent above the given TeXFormula and inserts the result
 
294
     * at the end of the current TeXFormula.
 
295
     *
 
296
     * @param base the TeXFormula above which the given accent will be placed
 
297
     * @param accentName the name of the accent symbol
 
298
     * @return the modified TeXFormula
 
299
     * @throws InvalidSymbolTypeException if the symbol is not defined as an accent
 
300
     * @throws SymbolNotFoundException if there's no symbol defined with the given name
 
301
     */
 
302
    public TeXFormula addAcc(TeXFormula base, String accentName)
 
303
    throws InvalidSymbolTypeException, SymbolNotFoundException {
 
304
        return add(new AccentedAtom((base == null ? null : base.root), accentName));
 
305
    }
 
306
    
 
307
    /**
 
308
     * Puts the given accent TeXFormula (that must represent a single accent symbol!) above
 
309
     * the given base TeXFormula and inserts the result at the end of the current TeXFormula.
 
310
     * <p>
 
311
     * <b>It's recommended to use one of the other more simple "addAcc"-mehods that require
 
312
     * the symbolname of the accent as a string! This method was added only because it was the
 
313
     * best way for parsing the MathML "mover" element into a TeXFormula.</b>
 
314
     *
 
315
     * @param base the TeXFormula above which the given accent will be placed
 
316
     * @param accent the TeXFormula that must represent a single accent symbol
 
317
     * @return the modified TeXFormula
 
318
     * @throws InvalidSymbolTypeException if the symbol that the given accent TeXFormula
 
319
     *                  represents, is not defined as an accent
 
320
     * @throws InvalidTeXFormulaException if the given accent TeXFormula does not represent
 
321
     *                  a single symbol
 
322
     */
 
323
    public TeXFormula addAcc(TeXFormula base, TeXFormula accent)
 
324
    throws InvalidSymbolTypeException, InvalidTeXFormulaException {
 
325
        return add(new AccentedAtom((base == null ? null : base.root), accent));
 
326
    }
 
327
    
 
328
    /**
 
329
     * Parses the given string into a TeXFormula, surrounds it with the given
 
330
     * delimiters and inserts the result at the end of the current TeXformula.
 
331
     *
 
332
     * @param s the string to be parsed into a TeXFormula that will be surrounded
 
333
     *                  by the given delimiters
 
334
     * @param l the left delimiter character
 
335
     * @param r the right delimiter character
 
336
     * @return the modified TeXFormula
 
337
     * @throws SymbolNotFoundException if one of the delimiter characters is mapped
 
338
     *                  to an unknown symbol
 
339
     * @throws InvalidDelimiterException if one of the delimiter characters is mapped
 
340
     *                  to a symbol that is not defined as a delimiter symbol
 
341
     * @throws ParseException if the string could not be parsed correctly
 
342
     * @throws DelimiterMappingNotFoundException if no character-to-symbol mapping is
 
343
     *                  found for one of the delimiter characters
 
344
     */
 
345
    public TeXFormula addEmbraced(String s, char l, char r)
 
346
    throws SymbolNotFoundException, InvalidDelimiterException,
 
347
            ParseException, DelimiterMappingNotFoundException {
 
348
        return addEmbraced(new TeXFormula(s), l, r);
 
349
    }
 
350
    
 
351
    /**
 
352
     * Parses the given string(s) into a TeXFormula, surrounds it with the given
 
353
     * delimiters (if not null) and inserts the result at the end of the current
 
354
     * TeXFormula.
 
355
     *
 
356
     * @param s the string to be parsed into a TeXFormula that will be
 
357
     *                  surrounded by the given delimiters
 
358
     * @param left the symbol name of the left delimiter (or null: no delimiter)
 
359
     * @param right the symbol name of the right delimiter (or null: no delimiter)
 
360
     * @return the modified TeXFormula
 
361
     * @throws SymbolNotFoundException if no symbol is defined for one of the
 
362
     *                  given names
 
363
     * @throws ParseException if the string(s) could not be parsed correctly
 
364
     * @throws InvalidDelimiterException if one of the symbols is not defined as a
 
365
     *                  delimiter symbol
 
366
     */
 
367
    public TeXFormula addEmbraced(String s, String left, String right)
 
368
    throws SymbolNotFoundException, ParseException,
 
369
            InvalidDelimiterException {
 
370
        return addEmbraced(new TeXFormula(s), left, right);
 
371
    }
 
372
    
 
373
    /**
 
374
     * Surrounds the given TeXFormula with the given delimiters and
 
375
     * inserts the result at the end of the current TeXformula.
 
376
     *
 
377
     * @param f the TeXFormula that will be surrounded by the given delimiters
 
378
     * @param l the left delimiter character
 
379
     * @param r the right delimiter character
 
380
     * @return the modified TeXFormula
 
381
     * @throws SymbolNotFoundException if one of the delimiter characters is mapped
 
382
     *                  to an unknown symbol
 
383
     * @throws InvalidDelimiterException if one of the delimiter characters is mapped
 
384
     *                  to a symbol that is not defined as a delimiter symbol
 
385
     * @throws DelimiterMappingNotFoundException if no character-to-symbol mapping is
 
386
     *                  found for one of the delimiter characters
 
387
     */
 
388
    public TeXFormula addEmbraced(TeXFormula f, char l, char r)
 
389
    throws SymbolNotFoundException, InvalidDelimiterException,
 
390
            DelimiterMappingNotFoundException {
 
391
        return addEmbraced(f, getCharacterToDelimiterMapping(l),
 
392
                getCharacterToDelimiterMapping(r));
 
393
    }
 
394
    
 
395
    /**
 
396
     * Surrounds the given TeXFormula with the given delimiters (if not null) and inserts the
 
397
     * result at the end of the current TeXFormula.
 
398
     *
 
399
     * @param f the TeXFormula that will be surrounded by the given delimiters
 
400
     * @param left the symbol name of the left delimiter (or null: no delimiter)
 
401
     * @param right the symbol name of the right delimiter (or null: no delimiter)
 
402
     * @return the modified TeXFormula
 
403
     * @throws SymbolNotFoundException if no symbol is defined for one of the
 
404
     *                  given names
 
405
     * @throws InvalidDelimiterException if one of the symbols is not defined as a
 
406
     *                  delimiter symbol
 
407
     */
 
408
    public TeXFormula addEmbraced(TeXFormula f, String left, String right)
 
409
    throws SymbolNotFoundException, InvalidDelimiterException {
 
410
        return add(new FencedAtom((f == null ? null : f.root),
 
411
                getDelimiterSymbol(left), getDelimiterSymbol(right)));
 
412
    }
 
413
    
 
414
    /**
 
415
     * Parses the given strings into TeXFormula's that will represent the numerator (num)
 
416
     * and the denominator (denom) of a fraction, draws a line between them
 
417
     * depending on "rule" and inserts the result at the end of the current
 
418
     * TeXFormula.
 
419
     *
 
420
     * @param num the string to be parsed into a TeXFormula that will represent the
 
421
     *                  numerator of the fraction
 
422
     * @param denom the string to be parsed into a TeXFormula that will represent the
 
423
     *                  denominator of the fraction
 
424
     * @param rule whether a line should be drawn between numerator and denominator
 
425
     * @return the modified TeXFormula
 
426
     * @throws ParseException if one of the strings could not be parsed correctly
 
427
     */
 
428
    public TeXFormula addFraction(String num, String denom, boolean rule)
 
429
    throws ParseException {
 
430
        return addFraction(new TeXFormula(num), new TeXFormula(denom), rule);
 
431
    }
 
432
    
 
433
    /**
 
434
     * Parses the given strings into TeXFormula's that will represent the numerator (num)
 
435
     * and denominator (denom) of a fraction, draws a line between them
 
436
     * depending on "rule", aligns the numerator and denominator in comparison with
 
437
     * each other (indicated by numAlign and denomAlign) and inserts the result at
 
438
     * the end of the current TeXFormula.
 
439
     *
 
440
     * @param num the string to be parsed into a TeXFormula that will represent the
 
441
     *                  numerator of the fraction
 
442
     * @param denom the string to be parsed into a TeXFormula that will represent the
 
443
     *                  denominator of the fraction
 
444
     * @param rule whether a line should be drawn between numerator and denominator
 
445
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
446
     *                  how the numerator should be aligned in comparison with the (larger)
 
447
     *                  denominator
 
448
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
449
     *                  how the denominator should be aligned in comparison with the (larger)
 
450
     *                  numerator
 
451
     * @return the modified TeXFormula
 
452
     * @throws ParseException if one of the strings could not be parsed correctly
 
453
     */
 
454
    public TeXFormula addFraction(String num, String denom, boolean rule,
 
455
            int numAlign, int denomAlign) throws ParseException {
 
456
        return addFraction(new TeXFormula(num), new TeXFormula(denom), rule,
 
457
                numAlign, denomAlign);
 
458
    }
 
459
    
 
460
    /**
 
461
     * Parses the given string into a TeXFormula that will represent the numerator of
 
462
     * a fraction, uses the given TeXFormula as the denominator of this fraction,
 
463
     * draws a line between them depending on "rule" and inserts the
 
464
     * result at the end of the current TeXFormula.
 
465
     *
 
466
     * @param num the string to be parsed into a TeXFormula that will represent the
 
467
     *                  numerator of the fraction
 
468
     * @param denom the TeXFormula that will represent the
 
469
     *                  denominator of the fraction
 
470
     * @param rule whether a line should be drawn between numerator and denominator
 
471
     * @return the modified TeXFormula
 
472
     * @throws ParseException if the string could not be parsed correctly
 
473
     */
 
474
    public TeXFormula addFraction(String num, TeXFormula denom, boolean rule)
 
475
    throws ParseException {
 
476
        return addFraction(new TeXFormula(num), denom, rule);
 
477
    }
 
478
    
 
479
    /**
 
480
     * Parses the given string into a TeXFormula that will represent the denominator of
 
481
     * a fraction, uses the given TeXFormula as the numerator of this fraction,
 
482
     * draws a line between them depending on "rule" and inserts the
 
483
     * result at the end of the current TeXFormula.
 
484
     *
 
485
     * @param num the TeXFormula that will represent the
 
486
     *                  numerator of the fraction
 
487
     * @param denom the string to be parsed into a TeXFormula that will represent the
 
488
     *                  denominator of the fraction
 
489
     * @param rule whether a line should be drawn between numerator and denominator
 
490
     * @return the modified TeXFormula
 
491
     * @throws ParseException if the string could not be parsed correctly
 
492
     */
 
493
    public TeXFormula addFraction(TeXFormula num, String denom, boolean rule)
 
494
    throws ParseException {
 
495
        return addFraction(num, new TeXFormula(denom), rule);
 
496
    }
 
497
    
 
498
    /**
 
499
     * Uses the given TeXFormula's as the numerator (num) and denominator (denom) of
 
500
     * a fraction, draws a line between them depending on "rule"
 
501
     * and inserts the result at the end of the current TeXFormula.
 
502
     *
 
503
     * @param num the TeXFormula that will represent the
 
504
     *                  numerator of the fraction
 
505
     * @param denom the TeXFormula that will represent the
 
506
     *                  denominator of the fraction
 
507
     * @param rule whether a line should be drawn between numerator and denominator
 
508
     * @return the modified TeXFormula
 
509
     */
 
510
    public TeXFormula addFraction(TeXFormula num, TeXFormula denom, boolean rule) {
 
511
        return add(new FractionAtom((num == null ? null : num.root),
 
512
                (denom == null ? null : denom.root), rule));
 
513
    }
 
514
    
 
515
    /**
 
516
     * Uses the given TeXFormula's as the numerator (num) and denominator (denom) of
 
517
     * a fraction, draws a line between them depending on "rule",
 
518
     * aligns the numerator and denominator in comparison with
 
519
     * each other (indicated by numAlign and denomAlign) and inserts the result at
 
520
     * the end of the current TeXFormula.
 
521
     *
 
522
     * @param num the TeXFormula that will represent the
 
523
     *                  numerator of the fraction
 
524
     * @param denom the TeXFormula that will represent the
 
525
     *                  denominator of the fraction
 
526
     * @param rule whether a line should be drawn between numerator and denominator
 
527
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
528
     *                  how the numerator should be aligned in comparison with the (larger)
 
529
     *                  denominator
 
530
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
531
     *                  how the denominator should be aligned in comparison with the (larger)
 
532
     *                  numerator
 
533
     * @return the modified TeXFormula
 
534
     */
 
535
    public TeXFormula addFraction(TeXFormula num, TeXFormula denom,
 
536
            boolean rule, int numAlign, int denomAlign) {
 
537
        return add(new FractionAtom((num == null ? null : num.root),
 
538
                (denom == null ? null : denom.root), rule, numAlign, denomAlign));
 
539
    }
 
540
    
 
541
    /**
 
542
     * Parses the given strings into TeXFormula's, puts them under a root
 
543
     * sign (base) and in the upper left corner over this root sign (nthRoot)
 
544
     * and inserts the result at the end of the current TeXFormula.
 
545
     *
 
546
     * @param base the string to be parsed into a TeXFormula that will be put
 
547
     *                  under the root sign.
 
548
     * @param nthRoot the string to be parsed into a TeXFormula that will be put
 
549
     *                  in the upper left corner over the root sign
 
550
     * @return the modified TeXFormula
 
551
     * @throws ParseException if one of the strings could not be parsed correctly
 
552
     */
 
553
    public TeXFormula addNthRoot(String base, String nthRoot)
 
554
    throws ParseException {
 
555
        return addNthRoot(new TeXFormula(base), new TeXFormula(nthRoot));
 
556
    }
 
557
    
 
558
    /**
 
559
     * Parses the given string into a TeXFormula, puts it under a root
 
560
     * sign, puts the given TeXFormula in the upper left corner over this root sign
 
561
     * and inserts the result at the end of the current TeXFormula.
 
562
     *
 
563
     * @param base the string to be parsed into a TeXFormula that will be put
 
564
     *                  under the root sign.
 
565
     * @param nthRoot the TeXFormula that will be put
 
566
     *                  in the upper left corner over the root sign
 
567
     * @return the modified TeXFormula
 
568
     * @throws ParseException if the string could not be parsed correctly
 
569
     */
 
570
    public TeXFormula addNthRoot(String base, TeXFormula nthRoot)
 
571
    throws ParseException {
 
572
        return addNthRoot(new TeXFormula(base), nthRoot);
 
573
    }
 
574
    
 
575
    /**
 
576
     * Parses the given string into a TeXFormula, puts it in the upper
 
577
     * left corner over the root sign, puts the given TeXFormula under this root sign
 
578
     * and inserts the result at the end of the current TeXFormula.
 
579
     *
 
580
     * @param base the TeXFormula that will be put under the root sign.
 
581
     * @param nthRoot the string to be parsed into a TeXFormula that will be put
 
582
     *                  in the upper left corner over the root sign
 
583
     * @return the modified TeXFormula
 
584
     * @throws ParseException if the strings could not be parsed correctly
 
585
     */
 
586
    public TeXFormula addNthRoot(TeXFormula base, String nthRoot)
 
587
    throws ParseException {
 
588
        return addNthRoot(base, new TeXFormula(nthRoot));
 
589
    }
 
590
    
 
591
    /**
 
592
     * Puts the given TeXFormula's under a root sign (base) and in the upper left
 
593
     * corner over this root sign (nthRoot) and inserts the result at the end of the
 
594
     * current TeXFormula.
 
595
     *
 
596
     * @param base the TeXFormula that will be put under the root sign.
 
597
     * @param nthRoot the TeXFormula that will be put
 
598
     *                  in the upper left corner over the root sign
 
599
     * @return the modified TeXFormula
 
600
     */
 
601
    public TeXFormula addNthRoot(TeXFormula base, TeXFormula nthRoot) {
 
602
        return add(new NthRoot((base == null ? null : base.root),
 
603
                (nthRoot == null ? null : nthRoot.root)));
 
604
    }
 
605
    
 
606
    /**
 
607
     * Parses the given strings into TeXFormula's that will represent a "big operator"
 
608
     * (op), it's lower (low) and upper (up) bound, and inserts the result at the end
 
609
     * of the current TeXFormula. The positioning of the upper and lower bound
 
610
     * (as limits: over and under the "big operator", or as scripts: superscript and
 
611
     * subscript) will be determined automatically according to the TeX algorithms.
 
612
     * If low is null, the lower bound will be omitted. If up is null, the upper
 
613
     * bound will be omitted.
 
614
     *
 
615
     * @param op the string to be parsed into a TeXFormula that will represent the
 
616
     *                  "big operator"
 
617
     * @param low the string to be parsed into a TeXFormula that will represent
 
618
     *                  the lower bound of the "big operator" (or null: no lower bound)
 
619
     * @param up the string to be parsed into a TeXFormula that will represent
 
620
     *                  the upper bound of the "big operator" (or null: no upper bound)
 
621
     * @return the modified TeXFormula
 
622
     * @throws ParseException if one of the strings could not be parsed correctly
 
623
     */
 
624
    public TeXFormula addOp(String op, String low, String up)
 
625
    throws ParseException {
 
626
        return addOp(new TeXFormula(op), new TeXFormula(low), new TeXFormula(up));
 
627
    }
 
628
    
 
629
    /**
 
630
     * Parses the given strings into TeXFormula's that will represent a "big operator"
 
631
     * (op), it's lower (low) and upper (up) bound, and inserts the result at the end
 
632
     * of the current TeXFormula. The positioning of the upper and lower bound
 
633
     * (as limits: over and under the "big operator", or as scripts: superscript and
 
634
     * subscript) is determined by lim.
 
635
     *
 
636
     * @param op the string to be parsed into a TeXFormula that will represent the
 
637
     *                  "big operator"
 
638
     * @param low the string to be parsed into a TeXFormula that will represent
 
639
     *                  the lower bound of the "big operator" (or null: no lower bound)
 
640
     * @param up the string to be parsed into a TeXFormula that will represent
 
641
     *                  the upper bound of the "big operator" (or null: no upper bound)
 
642
     * @param lim whether the upper and lower bound should be displayed as limits
 
643
     *                  (<-> scripts)
 
644
     * @return the modified TeXFormula
 
645
     * @throws ParseException if one of the strings could not be parsed correctly
 
646
     */
 
647
    public TeXFormula addOp(String op, String low, String up, boolean lim)
 
648
    throws ParseException {
 
649
        return addOp(new TeXFormula(op), new TeXFormula(low), new TeXFormula(up),
 
650
                lim);
 
651
    }
 
652
    
 
653
    /**
 
654
     * Uses the given TeXFormula's as a "big operator"
 
655
     * (op), it's lower (low) and upper (up) bound, and inserts the result at the end
 
656
     * of the current TeXFormula. The positioning of the upper and lower bound
 
657
     * (as limits: over and under the "big operator", or as scripts: superscript and
 
658
     * subscript) will be determined automatically according to the TeX algorithms.
 
659
     * If low is null, the lower bound will be omitted. If up is null, the upper
 
660
     * bound will be omitted.
 
661
     *
 
662
     * @param op the TeXFormula that will represent the
 
663
     *                  "big operator"
 
664
     * @param low the TeXFormula that will represent
 
665
     *                  the lower bound of the "big operator" (or null: no lower bound)
 
666
     * @param up the TeXFormula that will represent
 
667
     *                  the upper bound of the "big operator" (or null: no upper bound)
 
668
     * @return the modified TeXFormula
 
669
     */
 
670
    public TeXFormula addOp(TeXFormula op, TeXFormula low, TeXFormula up) {
 
671
        return add(new BigOperatorAtom((op == null ? null : op.root),
 
672
                (low == null ? null : low.root), (up == null ? null : up.root)));
 
673
    }
 
674
    
 
675
    /**
 
676
     * Uses the given TeXFormula's as a "big operator"
 
677
     * (op), it's lower (low) and upper (up) bound, and inserts the result at the end
 
678
     * of the current TeXFormula. The positioning of the upper and lower bound
 
679
     * (as limits: over and under the "big operator", or as scripts: superscript and
 
680
     * subscript) is determined by lim.
 
681
     *
 
682
     * @param op the TeXFormula that will represent the
 
683
     *                  "big operator"
 
684
     * @param low the TeXFormula that will represent
 
685
     *                  the lower bound of the "big operator" (or null: no lower bound)
 
686
     * @param up the TeXFormula that will represent
 
687
     *                  the upper bound of the "big operator" (or null: no upper bound)
 
688
     * @param lim whether the upper and lower bound should be displayed as limits
 
689
     *                  (<-> scripts)
 
690
     * @return the modified TeXFormula
 
691
     */
 
692
    public TeXFormula addOp(TeXFormula op, TeXFormula low, TeXFormula up,
 
693
            boolean lim) {
 
694
        return add(new BigOperatorAtom((op == null ? null : op.root),
 
695
                (low == null ? null : low.root), (up == null ? null : up.root), lim));
 
696
    }
 
697
    
 
698
    /**
 
699
     * Parses the given string into a phantom TeXFormula and inserts the result at the
 
700
     * end of the current TeXFormula. A phantom TeXFormula will be rendered invisibly.
 
701
     * Although the inserted formula is invisible, it's
 
702
     * still treated as a normal visible formula when it comes to inserting glue.
 
703
     *
 
704
     * @param phantom the string to be parsed as a phantom TeXFormula
 
705
     * @return the modified TeXFormula
 
706
     * @throws ParseException if the string could not be parsed correctly
 
707
     */
 
708
    public TeXFormula addPhantom(String phantom) throws ParseException {
 
709
        return addPhantom(new TeXFormula(phantom));
 
710
    }
 
711
    
 
712
    /**
 
713
     * Parses the given string into a phantom TeXFormula and inserts the result at the
 
714
     * end of the current TeXFormula. Only the dimensions set to true will be taken into
 
715
     * account for drawing the whitespace. Although the inserted formula is invisible, it's
 
716
     * still treated as a normal visible formula when it comes to inserting glue.
 
717
     *
 
718
     * @param phantom the string to be parsed as a phantom TeXFormula
 
719
     * @param width whether the width of the TeXFormula's box should be used (<-> width 0)
 
720
     * @param height whether the height of the TeXFormula's box should be used (<-> height 0)
 
721
     * @param depth whether the depth of the TeXFormula's box should be used (<-> depth 0)
 
722
     * @return the modified TeXFormula
 
723
     * @throws ParseException if the string could not be parsed correctly
 
724
     */
 
725
    public TeXFormula addPhantom(String phantom, boolean width, boolean height,
 
726
            boolean depth) throws ParseException {
 
727
        return addPhantom(new TeXFormula(phantom), width, height, depth);
 
728
    }
 
729
    
 
730
    /**
 
731
     * Inserts the given TeXFormula as a phantom TeXFormula at the
 
732
     * end of the current TeXFormula. A phantom TeXFormula will be rendered invisibly.
 
733
     * Although the inserted formula is invisible, it's
 
734
     * still treated as a normal visible formula when it comes to inserting glue.
 
735
     *
 
736
     * @param phantom the TeXFormula to be inserted as a phantom TeXFormula
 
737
     * @return the modified TeXFormula
 
738
     */
 
739
    public TeXFormula addPhantom(TeXFormula phantom) {
 
740
        return add(new PhantomAtom((phantom == null ? null : phantom.root)));
 
741
    }
 
742
    
 
743
    /**
 
744
     * Inserts the given TeXFormula as a phantom TeXFormula at the
 
745
     * end of the current TeXFormula. Only the dimensions set to true will be taken into
 
746
     * account for drawing the whitespace. Although the inserted formula is invisible, it's
 
747
     * still treated as a normal visible formula when it comes to inserting glue.
 
748
     *
 
749
     * @param phantom the TeXFormula to be inserted as a phantom TeXFormula
 
750
     * @param width whether the width of the TeXFormula's box should be used (<-> width 0)
 
751
     * @param height whether the height of the TeXFormula's box should be used (<-> height 0)
 
752
     * @param depth whether the depth of the TeXFormula's box should be used (<-> depth 0)
 
753
     * @return the modified TeXFormula
 
754
     */
 
755
    public TeXFormula addPhantom(TeXFormula phantom, boolean width,
 
756
            boolean height, boolean depth) {
 
757
        return add(new PhantomAtom((phantom == null ? null : phantom.root),
 
758
                width, height, depth));
 
759
    }
 
760
    
 
761
    /**
 
762
     * Parses the given string into a TeXFormula that will be displayed under a root
 
763
     * sign and inserts the result at the end of the current TeXFormula.
 
764
     *
 
765
     * @param base the string to be parsed into a TeXFormula that will be displayed
 
766
     *                  under a root sign
 
767
     * @return the modified TeXFormula
 
768
     * @throws ParseException if the string could not be parsed correctly
 
769
     */
 
770
    public TeXFormula addSqrt(String base) throws ParseException {
 
771
        return addSqrt(new TeXFormula(base));
 
772
    }
 
773
    
 
774
    /**
 
775
     * Displays the given TeXFormula under a root
 
776
     * sign and inserts the result at the end of the current TeXFormula.
 
777
     *
 
778
     * @param base the TeXFormula that will be displayed
 
779
     *                  under a root sign
 
780
     * @return the modified TeXFormula
 
781
     */
 
782
    public TeXFormula addSqrt(TeXFormula base) {
 
783
        return addNthRoot(base, (TeXFormula) null);
 
784
    }
 
785
    
 
786
    /**
 
787
     * Inserts a strut box (whitespace) with the given width, height and depth (in
 
788
     * the given unit) at the end of the current TeXFormula.
 
789
     *
 
790
     * @param unit a unit constant (from {@link TeXConstants})
 
791
     * @param width the width of the strut box
 
792
     * @param height the height of the strut box
 
793
     * @param depth the depth of the strut box
 
794
     * @return the modified TeXFormula
 
795
     * @throws InvalidUnitException if the given integer value does not represent
 
796
     *                  a valid unit
 
797
     */
 
798
    public TeXFormula addStrut(int unit, float width, float height, float depth)
 
799
    throws InvalidUnitException {
 
800
        return add(new SpaceAtom(unit, width, height, depth));
 
801
    }
 
802
    
 
803
    /**
 
804
     * Inserts a strut box (whitespace) with the given width (in widthUnits), height
 
805
     * (in heightUnits) and depth (in depthUnits) at the end of the current TeXFormula.
 
806
     *
 
807
     * @param widthUnit a unit constant used for the width (from {@link TeXConstants})
 
808
     * @param width the width of the strut box
 
809
     * @param heightUnit a unit constant used for the height (from TeXConstants)
 
810
     * @param height the height of the strut box
 
811
     * @param depthUnit a unit constant used for the depth (from TeXConstants)
 
812
     * @param depth the depth of the strut box
 
813
     * @return the modified TeXFormula
 
814
     * @throws InvalidUnitException if the given integer value does not represent
 
815
     *                  a valid unit
 
816
     */
 
817
    public TeXFormula addStrut(int widthUnit, float width, int heightUnit,
 
818
            float height, int depthUnit, float depth) throws InvalidUnitException {
 
819
        return add(new SpaceAtom(widthUnit, width, heightUnit, height, depthUnit,
 
820
                depth));
 
821
    }
 
822
    
 
823
    /**
 
824
     * Inserts the symbol with the given name at the end of the current TeXFormula.
 
825
     *
 
826
     * @param name the name of the symbol
 
827
     * @return the modified TeXFormula
 
828
     * @throws SymbolNotFoundException if there's no symbol defined with the given name
 
829
     */
 
830
    public TeXFormula addSymbol(String name) throws SymbolNotFoundException {
 
831
        return add(SymbolAtom.get(name));
 
832
    }
 
833
    
 
834
    /**
 
835
     * Inserts the symbol with the given name at the end of the current TeXFormula
 
836
     * as a symbol of the given symbol type. This type can be (and is meant to be)
 
837
     * different from the symbol's defined type.
 
838
     *
 
839
     * @param name the name of the symbol
 
840
     * @param type a symbol type constant (from {@link TeXConstants})
 
841
     * @return the modified TeXFormula
 
842
     * @throws SymbolNotFoundException if there's no symbol defined with the given name
 
843
     * @throws InvalidSymbolTypeException if the given integer value does not represent
 
844
     *                  a valid symbol type
 
845
     */
 
846
    public TeXFormula addSymbol(String name, int type)
 
847
    throws SymbolNotFoundException, InvalidSymbolTypeException {
 
848
        return add(new SymbolAtom(SymbolAtom.get(name), type));
 
849
    }
 
850
    
 
851
   /*
 
852
    * Look for scripts at the current position in the parse string
 
853
    * and attach them to the given atom (if found)
 
854
    */
 
855
    private Atom attachScripts(Atom atom) throws ParseException {
 
856
        skipWhiteSpace();
 
857
        Atom f = atom;
 
858
        
 
859
        if (pos < parseString.length()) {
 
860
            // attach script(s) if present
 
861
            char ch = parseString.charAt(pos);
 
862
            
 
863
            // ' = ^{\prime... so first replace this, then attach this script
 
864
            if (ch == PRIME) {
 
865
                replaceAccents();
 
866
                ch = parseString.charAt(pos);
 
867
            }
 
868
            
 
869
            // look for scripts and attach them
 
870
            if (ch == SUPER_SCRIPT || ch == SUB_SCRIPT) {
 
871
                pos++;
 
872
                if (ch == SUPER_SCRIPT) { // superscript
 
873
                    TeXFormula sup = getScript(), sub = new TeXFormula();
 
874
                    skipWhiteSpace();
 
875
                    if (pos < parseString.length()
 
876
                    && parseString.charAt(pos) == SUB_SCRIPT) { // both
 
877
                        pos++;
 
878
                        sub = getScript();
 
879
                    }
 
880
                    if(f.getRightType() == TeXConstants.TYPE_BIG_OPERATOR)
 
881
                        f = new BigOperatorAtom(f, sub.root, sup.root);
 
882
                    else
 
883
                        f = new ScriptsAtom(f, sub.root, sup.root);
 
884
                } else { // subscript
 
885
                    TeXFormula sub = getScript(), sup = new TeXFormula();
 
886
                    skipWhiteSpace();
 
887
                    if (pos < parseString.length()
 
888
                    && parseString.charAt(pos) == SUPER_SCRIPT) { // both
 
889
                        pos++;
 
890
                        sup = getScript();
 
891
                    }
 
892
                    if(f.getRightType() == TeXConstants.TYPE_BIG_OPERATOR)
 
893
                        f = new BigOperatorAtom(f, sub.root, sup.root);
 
894
                    else
 
895
                        f = new ScriptsAtom(f, sub.root, sup.root);
 
896
                }
 
897
            }
 
898
        }
 
899
        return f;
 
900
    }
 
901
    
 
902
   /*
 
903
    * Converts a character (from the parse string) to an atom (CharAtom or Symbol)
 
904
    */
 
905
    private Atom convertCharacter(char c) throws ParseException {
 
906
        pos++;
 
907
        if (isSymbol(c)) {
 
908
            String symbolName = symbolMappings[c];
 
909
            if (symbolName == null)
 
910
                throw new ParseException("Unknown character : '"
 
911
                        + Character.toString(c) + "'");
 
912
            else
 
913
                try {
 
914
                    return SymbolAtom.get(symbolName);
 
915
                } catch (SymbolNotFoundException e) {
 
916
                    throw new ParseException("The character '"
 
917
                            + Character.toString(c)
 
918
                            + "' was mapped to an unknown symbol with the name '"
 
919
                            + (String) symbolName + "'!", e);
 
920
                }
 
921
        } else
 
922
            // alphanumeric character
 
923
            return new CharAtom(c, textStyle);
 
924
    }
 
925
    
 
926
   /*
 
927
    * Convert this TeXFormula into a box, starting form the given style
 
928
    */
 
929
    private Box createBox(TeXEnvironment style) {
 
930
        if (root == null)
 
931
            return new StrutBox(0, 0, 0, 0);
 
932
        else
 
933
            return root.createBox(style);
 
934
    }
 
935
    
 
936
    /**
 
937
     * Creates a TeXIcon from this TeXFormula using the default TeXFont in the given
 
938
     * point size and starting from the given TeX style. If the given integer value
 
939
     * does not represent a valid TeX style, the default style
 
940
     * TeXConstants.STYLE_DISPLAY will be used.
 
941
     *
 
942
     * @param style a TeX style constant (from {@link TeXConstants}) to start from
 
943
     * @param size the default TeXFont's point size
 
944
     * @return the created TeXIcon
 
945
     */
 
946
    public TeXIcon createTeXIcon(int style, float size) {
 
947
        return new TeXIcon(createBox(new TeXEnvironment(style,
 
948
                new DefaultTeXFont(size))), size);
 
949
    }
 
950
    
 
951
    /**
 
952
     * Surrounds this TeXFormula with the given delimiters.
 
953
     *
 
954
     * @param left the left delimiter character
 
955
     * @param right the right delimiter character
 
956
     * @return the modified TeXFormula
 
957
     * @throws SymbolNotFoundException if one of the delimiter characters is mapped
 
958
     *                  to an unknown symbol
 
959
     * @throws InvalidDelimiterException if one of the delimiter characters is mapped
 
960
     *                  to a symbol that is not defined as a delimiter symbol
 
961
     * @throws DelimiterMappingNotFoundException if no character-to-symbol mapping is
 
962
     *                  found for one of the delimiter characters
 
963
     */
 
964
    public TeXFormula embrace(char left, char right)
 
965
    throws SymbolNotFoundException, InvalidDelimiterException,
 
966
            DelimiterMappingNotFoundException {
 
967
        return embrace(getCharacterToDelimiterMapping(left),
 
968
                getCharacterToDelimiterMapping(right));
 
969
    }
 
970
    
 
971
    /**
 
972
     * Surrounds this TeXFormula with the given delimiters (if not null).
 
973
     *
 
974
     * @param left the symbol name of the left delimiter (or null: no delimiter)
 
975
     * @param right the symbol name of the right delimiter (or null: no delimiter)
 
976
     * @return the modified TeXFormula
 
977
     * @throws SymbolNotFoundException if no symbol is defined for one of the
 
978
     *                  given names
 
979
     * @throws InvalidDelimiterException if one of the symbols is not defined as a
 
980
     *                  delimiter symbol
 
981
     */
 
982
    public TeXFormula embrace(String left, String right)
 
983
    throws SymbolNotFoundException, InvalidDelimiterException {
 
984
        root = new FencedAtom(root, getDelimiterSymbol(left),
 
985
                getDelimiterSymbol(right));
 
986
        return this;
 
987
    }
 
988
    
 
989
    /**
 
990
     * Uses the current TeXFormula as the numerator of a fraction, parses the given string
 
991
     * into a TeXFormula that will represent the denominator of the fraction, draws a line
 
992
     * between them depending on "rule" and changes the current TeXFormula
 
993
     * into this resulting fraction.
 
994
     *
 
995
     * @param s the string to be parsed into a TeXFormula that will represent the
 
996
     *                  denominator of the fraction
 
997
     * @param rule whether a line should be drawn between numerator and denominator
 
998
     * @return the modified TeXFormula
 
999
     * @throws ParseException if the string could not be parsed correctly
 
1000
     */
 
1001
    public TeXFormula fraction(String s, boolean rule) throws ParseException {
 
1002
        return fraction(new TeXFormula(s), rule);
 
1003
    }
 
1004
    
 
1005
    /**
 
1006
     * Uses the current TeXFormula as the numerator of a fraction, parses the given string
 
1007
     * into a TeXFormula that will represent the denominator of the fraction, possibly
 
1008
     * draws a line between them depending on "rule", aligns the numerator and
 
1009
     * denominator in comparison with each other (indicated by numAlign and denomAlign)
 
1010
     * and changes the current TeXFormula into this resulting fraction.
 
1011
     *
 
1012
     * @param s the string to be parsed into a TeXFormula that will represent the
 
1013
     *                  denominator of the fraction
 
1014
     * @param rule whether a line should be drawn between numerator and denominator
 
1015
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
1016
     *                  how the numerator should be aligned in comparison with the (larger)
 
1017
     *                  denominator
 
1018
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1019
     *                  how the denominator should be aligned in comparison with the (larger)
 
1020
     *                  numerator
 
1021
     * @return the modified TeXFormula
 
1022
     * @throws ParseException if the string could not be parsed correctly
 
1023
     */
 
1024
    public TeXFormula fraction(String s, boolean rule, int numAlign,
 
1025
            int denomAlign) throws ParseException {
 
1026
        return fraction(new TeXFormula(s), rule, numAlign, denomAlign);
 
1027
    }
 
1028
    
 
1029
    /**
 
1030
     * Uses the current TeXFormula as the numerator of a fraction, the given TeXFormula
 
1031
     * as the denominator of the fraction, draws a line between them depending on "rule"
 
1032
     * and changes the current TeXFormula into this resulting fraction.
 
1033
     *
 
1034
     * @param f the TeXFormula that will represent the
 
1035
     *                  denominator of the fraction
 
1036
     * @param rule whether a line should be drawn between numerator and denominator
 
1037
     * @return the modified TeXFormula
 
1038
     */
 
1039
    public TeXFormula fraction(TeXFormula f, boolean rule) {
 
1040
        root = new FractionAtom(root, (f == null ? null : f.root), rule);
 
1041
        return this;
 
1042
    }
 
1043
    
 
1044
    /**
 
1045
     * Uses the current TeXFormula as the numerator of a fraction, the given TeXFormula
 
1046
     * as the denominator of the fraction, draws a line between them with the given
 
1047
     * thickness (in the given unit)
 
1048
     * and changes the current TeXFormula into this resulting fraction.
 
1049
     *
 
1050
     * @param f the TeXFormula that will represent the
 
1051
     *                  denominator of the fraction
 
1052
     * @param unit a unit constant (from {@link TeXConstants})
 
1053
     * @param thickness the thickness (in the given unit) of the line to be put between
 
1054
     *                  the numerator and denominator
 
1055
     * @return the modified TeXFormula
 
1056
     * @throws InvalidUnitException if the given integer value does not
 
1057
     *                  represent a valid unit
 
1058
     */
 
1059
    public TeXFormula fraction(TeXFormula f, int unit, float thickness)
 
1060
    throws InvalidUnitException {
 
1061
        root = new FractionAtom(root, (f == null ? null : f.root), unit,
 
1062
                thickness);
 
1063
        return this;
 
1064
    }
 
1065
    
 
1066
    /**
 
1067
     * Uses the current TeXFormula as the numerator of a fraction, the given TeXFormula
 
1068
     * as the denominator of the fraction, draws a line between them depending on "rule",
 
1069
     * aligns the numerator and denominator in comparison with
 
1070
     * each other (indicated by numAlign and denomAlign) and changes the current
 
1071
     * TeXFormula into this resulting fraction.
 
1072
     *
 
1073
     * @param f the TeXFormula that will represent the
 
1074
     *                  denominator of the fraction
 
1075
     * @param unit a unit constant (from {@link TeXConstants})
 
1076
     * @param thickness the thickness (in the given unit) of the line to be put between
 
1077
     *                  the numerator and denominator
 
1078
     * @param numAlign an alignment constant (from TeXConstants) indicating
 
1079
     *                  how the numerator should be aligned in comparison with the (larger)
 
1080
     *                  denominator
 
1081
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1082
     *                  how the denominator should be aligned in comparison with the (larger)
 
1083
     *                  numerator
 
1084
     * @return the modified TeXFormula
 
1085
     * @throws InvalidUnitException if the given integer value does not
 
1086
     *                  represent a valid unit
 
1087
     */
 
1088
    public TeXFormula fraction(TeXFormula f, int unit, float thickness,
 
1089
            int numAlign, int denomAlign) throws InvalidUnitException {
 
1090
        root = new FractionAtom(root, (f == null ? null : f.root), unit,
 
1091
                thickness, numAlign, denomAlign);
 
1092
        return this;
 
1093
    }
 
1094
    
 
1095
    /**
 
1096
     * Uses the current TeXFormula as the numerator of a fraction, the given TeXFormula
 
1097
     * as the denominator of the fraction, draws a line between them with a thickness
 
1098
     * of "defaultFactor" times the default rule thickness,
 
1099
     * aligns the numerator and denominator in comparison with
 
1100
     * each other (indicated by numAlign and denomAlign) and changes the current
 
1101
     * TeXFormula into this resulting fraction.
 
1102
     *
 
1103
     * @param f the TeXFormula that will represent the
 
1104
     *                  denominator of the fraction
 
1105
     * @param defaultFactor the thickness factor (to be multiplied by the default
 
1106
     *                  rule thickness)
 
1107
     * @param numAlign an alignment constant (from TeXConstants) indicating
 
1108
     *                  how the numerator should be aligned in comparison with the (larger)
 
1109
     *                  denominator
 
1110
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1111
     *                  how the denominator should be aligned in comparison with the (larger)
 
1112
     *                  numerator
 
1113
     * @return the modified TeXFormula
 
1114
     */
 
1115
    public TeXFormula fraction(TeXFormula f, float defaultFactor, int numAlign,
 
1116
            int denomAlign) {
 
1117
        root = new FractionAtom(root, (f == null ? null : f.root), defaultFactor,
 
1118
                numAlign, denomAlign);
 
1119
        return this;
 
1120
    }
 
1121
    
 
1122
    /**
 
1123
     * Uses the current TeXFormula as the numerator of a fraction, the given TeXFormula
 
1124
     * as the denominator of the fraction, draws a line between them depending on "rule",
 
1125
     * aligns the numerator and denominator in comparison with
 
1126
     * each other (indicated by numAlign and denomAlign) and changes the current
 
1127
     * TeXFormula into this resulting fraction.
 
1128
     *
 
1129
     * @param f the TeXFormula that will represent the
 
1130
     *                  denominator of the fraction
 
1131
     * @param rule whether a line should be drawn between numerator and denominator
 
1132
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
1133
     *                  how the numerator should be aligned in comparison with the (larger)
 
1134
     *                  denominator
 
1135
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1136
     *                  how the denominator should be aligned in comparison with the (larger)
 
1137
     *                  numerator
 
1138
     * @return the modified TeXFormula
 
1139
     */
 
1140
    public TeXFormula fraction(TeXFormula f, boolean rule, int numAlign,
 
1141
            int denomAlign) {
 
1142
        root = new FractionAtom(root, (f == null ? null : f.root), rule,
 
1143
                numAlign, denomAlign);
 
1144
        return this;
 
1145
    }
 
1146
    
 
1147
    /**
 
1148
     * Uses the current TeXFormula as the denominator of a fraction, parses the given string
 
1149
     * into a TeXFormula that will represent the numerator of the fraction, draws a line
 
1150
     * between them depending on "rule" and changes the current TeXFormula
 
1151
     * into this resulting fraction.
 
1152
     *
 
1153
     * @param s the string to be parsed into a TeXFormula that will represent the
 
1154
     *                  numerator of the fraction
 
1155
     * @param rule whether a line should be drawn between numerator and denominator
 
1156
     * @return the modified TeXFormula
 
1157
     * @throws ParseException if the string could not be parsed correctly
 
1158
     */
 
1159
    public TeXFormula fractionInvert(String s, boolean rule)
 
1160
    throws ParseException {
 
1161
        return fractionInvert(new TeXFormula(s), rule);
 
1162
    }
 
1163
    
 
1164
    /**
 
1165
     * Uses the current TeXFormula as the denominator of a fraction, parses the given string
 
1166
     * into a TeXFormula that will represent the numerator of the fraction, draws a line
 
1167
     * between them depending on "rule", aligns the numerator and
 
1168
     * denominator in comparison with each other (indicated by numAlign and denomAlign)
 
1169
     * and changes the current TeXFormula into this resulting fraction.
 
1170
     *
 
1171
     * @param s the string to be parsed into a TeXFormula that will represent the
 
1172
     *                  numerator of the fraction
 
1173
     * @param rule whether a line should be drawn between numerator and denominator
 
1174
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
1175
     *                  how the numerator should be aligned in comparison with the (larger)
 
1176
     *                  denominator
 
1177
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1178
     *                  how the denominator should be aligned in comparison with the (larger)
 
1179
     *                  numerator
 
1180
     * @return the modified TeXFormula
 
1181
     * @throws ParseException if the string could not be parsed correctly
 
1182
     */
 
1183
    public TeXFormula fractionInvert(String s, boolean rule, int numAlign,
 
1184
            int denomAlign) throws ParseException {
 
1185
        return fractionInvert(new TeXFormula(s), rule, numAlign, denomAlign);
 
1186
    }
 
1187
    
 
1188
    /**
 
1189
     * Uses the current TeXFormula as the denominator of a fraction, the given TeXFormula
 
1190
     * as the numerator of the fraction, draws a line between them depending on "rule"
 
1191
     * and changes the current TeXFormula into this resulting fraction.
 
1192
     *
 
1193
     * @param f the TeXFormula that will represent the
 
1194
     *                  numerator of the fraction
 
1195
     * @param rule whether a line should be drawn between numerator and denominator
 
1196
     * @return the modified TeXFormula
 
1197
     */
 
1198
    public TeXFormula fractionInvert(TeXFormula f, boolean rule) {
 
1199
        root = new FractionAtom((f == null ? null : f.root), root, rule);
 
1200
        return this;
 
1201
    }
 
1202
    
 
1203
    /**
 
1204
     * Uses the current TeXFormula as the denominator of a fraction, the given TeXFormula
 
1205
     * as the numerator of the fraction, draws a line between them depending on "rule",
 
1206
     * aligns the numerator and denominator in comparison with
 
1207
     * each other (indicated by numAlign and denomAlign) and changes the current
 
1208
     * TeXFormula into this resulting fraction.
 
1209
     *
 
1210
     * @param f the TeXFormula that will represent the
 
1211
     *                  numerator of the fraction
 
1212
     * @param rule whether a line should be drawn between numerator and denominator
 
1213
     * @param numAlign an alignment constant (from {@link TeXConstants}) indicating
 
1214
     *                  how the numerator should be aligned in comparison with the (larger)
 
1215
     *                  denominator
 
1216
     * @param denomAlign an alignment constant (from TeXConstants) indicating
 
1217
     *                  how the denominator should be aligned in comparison with the (larger)
 
1218
     *                  numerator
 
1219
     * @return the modified TeXFormula
 
1220
     */
 
1221
    public TeXFormula fractionInvert(TeXFormula f, boolean rule, int numAlign,
 
1222
            int denomAlign) {
 
1223
        root = new FractionAtom((f == null ? null : f.root), root, rule,
 
1224
                numAlign, denomAlign);
 
1225
        return this;
 
1226
    }
 
1227
    
 
1228
   /*
 
1229
    * Get the next group (between the given opening and closing characters)
 
1230
    * at the current position in the parse string, return it as a string and
 
1231
    * adjust the current position (after the group).
 
1232
    */
 
1233
    private String getGroup(char open, char close) throws ParseException {
 
1234
        int group = 0;
 
1235
        if (pos < parseString.length()) {
 
1236
            char ch = parseString.charAt(pos);
 
1237
            if (ch == open) {
 
1238
                pos++;
 
1239
                StringBuffer buf = new StringBuffer();
 
1240
                while (pos < parseString.length()
 
1241
                && !(parseString.charAt(pos) == close && group == 0)) {
 
1242
                    if (parseString.charAt(pos) == open)
 
1243
                        group++;
 
1244
                    else if (parseString.charAt(pos) == close)
 
1245
                        group--;
 
1246
                    buf.append(parseString.charAt(pos));
 
1247
                    pos++;
 
1248
                }
 
1249
                if (pos == parseString.length())
 
1250
                    // end of string reached, but not processed properly
 
1251
                    throw new ParseException("Illegal end,  missing '" + close
 
1252
                            + "'!");
 
1253
                else { // end of group
 
1254
                    pos++;
 
1255
                    return buf.toString();
 
1256
                }
 
1257
            } else
 
1258
                throw new ParseException("missing '" + open + "'!");
 
1259
        }
 
1260
        // end of string reached, but not processed properly
 
1261
        throw new ParseException("Illegal end, missing '" + close + "'!");
 
1262
    }
 
1263
    
 
1264
   /*
 
1265
    * Get the next script at the current position in the parse string.
 
1266
    * If a group opening character is found, this is the next group, otherwise
 
1267
    * the next character. Parse it, return it as a TeXFormula and adjust
 
1268
    * the current position.
 
1269
    */
 
1270
    private TeXFormula getScript() throws ParseException {
 
1271
        skipWhiteSpace();
 
1272
        char ch;
 
1273
        if (pos < parseString.length()) {
 
1274
            ch = parseString.charAt(pos);
 
1275
            if (ch == L_GROUP) {
 
1276
                return new TeXFormula(getGroup(L_GROUP, R_GROUP));
 
1277
            } else {
 
1278
                pos++;
 
1279
                return new TeXFormula(Character.toString(ch));
 
1280
            }
 
1281
        }
 
1282
        // end of string reached, but not processed properly
 
1283
        throw new ParseException("illegal end, missing script!");
 
1284
    }
 
1285
    
 
1286
    /**
 
1287
     * Changes this TeXFormula into a phantom TeXFormula. It will be rendered invisibly.
 
1288
     * This means that a strut box (whitespace) will be displayed instead of the current
 
1289
     * TeXFormula, with the same width, height and depth as the current TeXFormula's
 
1290
     * box would have. Although this formula is now made invisible, it's
 
1291
     * still treated as a normal visible formula when it comes to inserting glue.
 
1292
     *
 
1293
     * @return the modified TeXFormula
 
1294
     */
 
1295
    public TeXFormula makePhantom() {
 
1296
        root = new PhantomAtom(root);
 
1297
        return this;
 
1298
    }
 
1299
    
 
1300
    /**
 
1301
     * Changes this TeXFormula into a phantom TeXFormula. Only the dimensions set to true will be taken into
 
1302
     * account for drawing the whitespace. Although this formula is now made invisible, it's
 
1303
     * still treated as a normal visible formula when it comes to inserting glue.
 
1304
     *
 
1305
     * @param width whether the width of the TeXFormula's box should be used (<-> width 0)
 
1306
     * @param height whether the height of the TeXFormula's box should be used (<-> height 0)
 
1307
     * @param depth whether the depth of the TeXFormula's box should be used (<-> depth 0)
 
1308
     * @return the modified TeXFormula
 
1309
     */
 
1310
    public TeXFormula makePhantom(boolean width, boolean height, boolean depth) {
 
1311
        root = new PhantomAtom(root, width, height, depth);
 
1312
        return this;
 
1313
    }
 
1314
    
 
1315
    /**
 
1316
     * Parses the given string into a TeXFormula, puts it in the upper
 
1317
     * left corner over a root sign (nthRoot), puts the current TeXFormula under this
 
1318
     * root sign and changes the current TeXFormula into this resulting root construction.
 
1319
     *
 
1320
     * @param nthRoot the string to be parsed into a TeXFormula that will be put
 
1321
     *                  in the upper left corner over the root sign
 
1322
     * @return the modified TeXFormula
 
1323
     * @throws ParseException if the string could not be parsed correctly
 
1324
     */
 
1325
    public TeXFormula nthRoot(String nthRoot) throws ParseException {
 
1326
        return nthRoot(new TeXFormula(nthRoot));
 
1327
    }
 
1328
    
 
1329
    /**
 
1330
     * Puts the given TeXFormula in the upper left corner over a root sign, puts the
 
1331
     * current TeXFormula under this root sign and changes the current TeXFormula
 
1332
     * into this resulting root construction.
 
1333
     *
 
1334
     * @param nthRoot the TeXFormula that will be put
 
1335
     *                  in the upper left corner over the root sign
 
1336
     * @return the modified TeXFormula
 
1337
     */
 
1338
    public TeXFormula nthRoot(TeXFormula nthRoot) {
 
1339
        root = new NthRoot(root, (nthRoot == null ? null : nthRoot.root));
 
1340
        return this;
 
1341
    }
 
1342
    
 
1343
    /**
 
1344
     * Puts a line over the current TeXFormula and changes the current TeXFormula into
 
1345
     * the resulting construction.
 
1346
     *
 
1347
     * @return the modified TeXFormula
 
1348
     */
 
1349
    public TeXFormula overline() {
 
1350
        root = new OverlinedAtom(root);
 
1351
        return this;
 
1352
    }
 
1353
    
 
1354
   /*
 
1355
    * Starts parsing the given string (at position 0).
 
1356
    */
 
1357
    private void parse(String s) throws ParseException {
 
1358
        parseString = s;
 
1359
        if (parseString.length() != 0) {
 
1360
            while (pos < parseString.length()) {
 
1361
                char ch = parseString.charAt(pos);
 
1362
                if (isWhiteSpace(ch))
 
1363
                    pos++; // ignore white space
 
1364
                else if (ch == ESCAPE)
 
1365
                    processEscape();
 
1366
                else if (ch == L_GROUP)
 
1367
                    add(attachScripts(new TeXFormula(getGroup(L_GROUP, R_GROUP)).root));
 
1368
                else if (ch == R_GROUP)
 
1369
                    throw new ParseException("Found a closing '" + R_GROUP
 
1370
                            + "' without an opening '" + L_GROUP + "'!");
 
1371
                else if (ch == SUPER_SCRIPT || ch == SUB_SCRIPT || ch == PRIME) // ' = ^{\prime...
 
1372
                    if (pos == 0) // first character
 
1373
                        throw new ParseException("Every script needs a base: \""
 
1374
                                + SUPER_SCRIPT + "\", \"" + SUB_SCRIPT + "\" and \""
 
1375
                                + PRIME + "\" can't be the first character!");
 
1376
                    else
 
1377
                        throw new ParseException(
 
1378
                                "Double scripts found! Try using more braces.");
 
1379
                else
 
1380
                    add(attachScripts(convertCharacter(ch)));
 
1381
            }
 
1382
        }
 
1383
    }
 
1384
    
 
1385
   /*
 
1386
    * Processes the given TeX command (by parsing following command arguments
 
1387
    * in the parse string).
 
1388
    */
 
1389
    private Atom processCommands(String command) throws ParseException {
 
1390
        skipWhiteSpace();
 
1391
        if ("frac".equals(command)) {
 
1392
            TeXFormula num = new TeXFormula(getGroup(L_GROUP, R_GROUP));
 
1393
            skipWhiteSpace();
 
1394
            TeXFormula denom = new TeXFormula(getGroup(L_GROUP, R_GROUP));
 
1395
            if (num.root == null || denom.root == null)
 
1396
                throw new ParseException(
 
1397
                        "Both numerator and denominator of a fraction can't be empty!");
 
1398
            return new FractionAtom(num.root, denom.root, true);
 
1399
        } else { // sqrt
 
1400
            skipWhiteSpace();
 
1401
            if (pos == parseString.length())
 
1402
                // end of string reached, but not processed properly
 
1403
                throw new ParseException("illegal end!");
 
1404
            
 
1405
            TeXFormula nRoot = new TeXFormula();
 
1406
            if (parseString.charAt(pos) == L_BRACK) { // n-th root
 
1407
                nRoot = new TeXFormula(getGroup(L_BRACK, R_BRACK));
 
1408
                skipWhiteSpace();
 
1409
            }
 
1410
            return new NthRoot(new TeXFormula(getGroup(L_GROUP, R_GROUP)).root,
 
1411
                    nRoot.root);
 
1412
            
 
1413
        }
 
1414
    }
 
1415
    
 
1416
   /*
 
1417
    * Tries to find a TeX command or TeX symbol name at the current position
 
1418
    * in the parse string (just after an escape character was found).
 
1419
    */
 
1420
    private void processEscape() throws ParseException {
 
1421
        pos++;
 
1422
        StringBuffer buf = new StringBuffer();
 
1423
        // no longer symbol name or predefined TeXFormula name possible
 
1424
        boolean endOfEscape = false;
 
1425
        // temporarily save symbol
 
1426
        SymbolAtom symbolFound = null;
 
1427
        int symbolPos = -1;
 
1428
        // temporarily save predefined TeXFormula
 
1429
        TeXFormula predefFound = null;
 
1430
        int predefPos = -1;
 
1431
        // what was the longest match: symbol or predefined TeXFormula?
 
1432
        boolean symbolLongest = true;
 
1433
        
 
1434
        while (pos < parseString.length()) {
 
1435
            char ch = parseString.charAt(pos);
 
1436
            boolean isEnd = (pos == parseString.length() - 1);
 
1437
            
 
1438
            // the following characters can't be part of a command or symbol, so
 
1439
            // if there's no command or symbol found, then an exception is
 
1440
            // thrown
 
1441
            if (isWhiteSpace(ch) || ch == ESCAPE || ch == SUB_SCRIPT
 
1442
                    || ch == SUPER_SCRIPT || isEnd) {
 
1443
                endOfEscape = true;
 
1444
                if (isEnd) {
 
1445
                    buf.append(ch);
 
1446
                    pos++;
 
1447
                }
 
1448
            } else {
 
1449
                buf.append(ch);
 
1450
                pos++;
 
1451
            }
 
1452
            
 
1453
            String command = buf.toString();
 
1454
            
 
1455
            SymbolAtom s = null;
 
1456
            TeXFormula predef = null;
 
1457
            try { // check if 'command' is a valid symbolname
 
1458
                s = SymbolAtom.get(command);
 
1459
            } catch (SymbolNotFoundException e) { // symbol not found
 
1460
                // check if a predefined TeXFormula exists with that name
 
1461
                try {
 
1462
                    predef = TeXFormula.get(command);
 
1463
                } catch (FormulaNotFoundException e1) {
 
1464
                    predef = null; // none found
 
1465
                }
 
1466
            }
 
1467
            
 
1468
            if (s != null) { // symbol found!   // NOPMD
 
1469
                if (endOfEscape) {
 
1470
                    // no longer symbol name or predefined TeXFormula name possible
 
1471
                    add(attachScripts(s));
 
1472
                    return;
 
1473
                } else { // could be part of another valid symbolname, like "in" and "infty"
 
1474
                    symbolFound = s;
 
1475
                    symbolPos = pos;
 
1476
                    symbolLongest = true;
 
1477
                }
 
1478
            } else if (predef != null) { // predefined TeXFormula found!   // NOPMD
 
1479
                if (endOfEscape) {
 
1480
                    // no longer symbol name or predefined TeXFormula name possible
 
1481
                    add(attachScripts(predef.root));
 
1482
                    return;
 
1483
                } else { // could be part of another valid symbolname, like "in" and "infty"
 
1484
                    predefFound = predef;
 
1485
                    predefPos = pos;
 
1486
                    symbolLongest = false;
 
1487
                }
 
1488
            } else if ("nbsp".equals(command)) { // space found (for MathML-purposes!)
 
1489
                add(attachScripts(new SpaceAtom()));
 
1490
                return;
 
1491
            } else if (textStyles.contains(command)) { // textstyle found
 
1492
                skipWhiteSpace();
 
1493
                add(attachScripts(new TeXFormula(getGroup(L_GROUP, R_GROUP),
 
1494
                        command).root));
 
1495
                return;
 
1496
            } else if (commands.contains(command)) { // command found
 
1497
                add(attachScripts(processCommands(command)));
 
1498
                return;
 
1499
            } else if (endOfEscape) { // searching is over
 
1500
                if (symbolLongest && symbolFound != null) {
 
1501
                    // go back to that position and add that symbol
 
1502
                    pos = symbolPos;
 
1503
                    add(attachScripts(symbolFound));
 
1504
                    return;
 
1505
                } else if (!symbolLongest && predefFound != null) { // NOPMD
 
1506
                    // go back to that position and add that predefined TeXFormula
 
1507
                    pos = predefPos;
 
1508
                    add(attachScripts(predefFound.root));
 
1509
                    return;
 
1510
                } else
 
1511
                    // not a valid command or symbol or predefined TeXFormula found
 
1512
                    throw new ParseException(
 
1513
                            "Unknown symbol or command or predefined TeXFormula: '"
 
1514
                            + command + "'");
 
1515
            }
 
1516
        }
 
1517
        
 
1518
        // escape-char found at the end of the string
 
1519
        throw new ParseException("The escape-character '" + ESCAPE
 
1520
                + "' can't be the last one!");
 
1521
    }
 
1522
    
 
1523
    /**
 
1524
     * Puts the given accent above the current TeXFormula and changes the current
 
1525
     * TeXFormula into the resulting accent construction.
 
1526
     *
 
1527
     * @param accentName the name of the accent symbol
 
1528
     * @return the modified TeXFormula
 
1529
     * @throws InvalidSymbolTypeException if the symbol is not defined as an accent
 
1530
     * @throws SymbolNotFoundException if there's no symbol defined with the given name
 
1531
     */
 
1532
    public TeXFormula putAccentOver(String accentName)
 
1533
    throws InvalidSymbolTypeException, SymbolNotFoundException {
 
1534
        root = new AccentedAtom(root, accentName);
 
1535
        return this;
 
1536
    }
 
1537
    
 
1538
    /**
 
1539
     * Puts the delimiter symbol represented by the given delimiter type constant above the
 
1540
     * current TeXFormula and changes the current TeXFormula into the resulting construction.
 
1541
     *
 
1542
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1543
     *                  a delimiter symbol that will be put above the current TeXFormula
 
1544
     * @return the modified TeXFormula
 
1545
     * @throws InvalidDelimiterTypeException if the given integer value does not represent
 
1546
     *                  a valid delimiter type
 
1547
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1548
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1549
     */
 
1550
    public TeXFormula putDelimiterOver(int delimiter)
 
1551
    throws InvalidDelimiterTypeException, SymbolNotFoundException {
 
1552
        if (delimiter < 0 || delimiter >= delimiterNames.length)
 
1553
            throw new InvalidDelimiterTypeException();
 
1554
        
 
1555
        String name = delimiterNames[delimiter][OVER_DEL];
 
1556
        root = new OverUnderDelimiter(root, null, SymbolAtom.get(name),
 
1557
                TeXConstants.UNIT_EX, 0, true);
 
1558
        return this;
 
1559
    }
 
1560
    
 
1561
    /**
 
1562
     * Puts the delimiter symbol represented by the given delimiter type constant above the
 
1563
     * current TeXFormula, parses the given string into a TeXFormula and
 
1564
     * puts it above the delimiter symbol (seperated by an amount of vertical space defined
 
1565
     * by the given float value and unit) in a smaller size (unless the current TeXFormula
 
1566
     * will be displayed in the smallest possible size: the script_script style's size)
 
1567
     * and finally changes the current TeXFormula into the resulting construction.
 
1568
     *
 
1569
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1570
     *                  a delimiter symbol that will be put above the current TeXFormula
 
1571
     * @param sup the string to be parsed into a TeXFormula that will be put above the
 
1572
     *                  delimiter symbol, in a smaller size if possible
 
1573
     * @param kernUnit a unit constant (from {@link TeXConstants})
 
1574
     * @param kern amount of vertical space (in kernUnit) to be put between the delimiter
 
1575
     *                  and the given TeXFormula sub
 
1576
     * @return the modified TeXFormula
 
1577
     * @throws InvalidDelimiterTypeException if the given integer value does not represent
 
1578
     *                  a valid delimiter type
 
1579
     * @throws InvalidUnitException if the given integer value (kernUnit) does not
 
1580
     *                  represent a valid unit
 
1581
     * @throws ParseException if the given string could not be parsed correctly
 
1582
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1583
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1584
     */
 
1585
    public TeXFormula putDelimiterOver(int delimiter, String sup, int kernUnit,
 
1586
            float kern) throws InvalidDelimiterTypeException,
 
1587
            InvalidUnitException, ParseException, SymbolNotFoundException {
 
1588
        return putDelimiterOver(delimiter, new TeXFormula(sup), kernUnit, kern);
 
1589
    }
 
1590
    
 
1591
    /**
 
1592
     * Puts the delimiter symbol represented by the given delimiter type constant above the
 
1593
     * current TeXFormula, puts the given TeXFormula above the delimiter symbol (seperated
 
1594
     * by an amount of vertical space defined by the given float value and unit) in a
 
1595
     * smaller size (unless the current TeXFormula will be displayed in the smallest
 
1596
     * possible size: the "script_script" style's size) and finally changes the current
 
1597
     * TeXFormula into the resulting construction.
 
1598
     *
 
1599
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1600
     *                  a delimiter symbol that will be put above the current TeXFormula
 
1601
     * @param sup the TeXFormula that will be put above the
 
1602
     *                  delimiter symbol, in a smaller size if possible
 
1603
     * @param kernUnit a unit constant (from {@link TeXConstants})
 
1604
     * @param kern amount of vertical space (in kernUnit) to be put between the delimiter
 
1605
     *                  and the given TeXFormula sub
 
1606
     * @return the modified TeXFormula
 
1607
     * @throws InvalidDelimiterTypeException if the given integer value does not represent
 
1608
     *                  a valid delimiter type
 
1609
     * @throws InvalidUnitException if the given integer value (kernUnit) does not
 
1610
     *                  represent a valid unit
 
1611
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1612
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1613
     */
 
1614
    public TeXFormula putDelimiterOver(int delimiter, TeXFormula sup,
 
1615
            int kernUnit, float kern) throws InvalidDelimiterTypeException,
 
1616
            InvalidUnitException, SymbolNotFoundException {
 
1617
        if (delimiter < 0 || delimiter >= delimiterNames.length)
 
1618
            throw new InvalidDelimiterTypeException();
 
1619
        
 
1620
        String name = delimiterNames[delimiter][OVER_DEL];
 
1621
        root = new OverUnderDelimiter(root, (sup == null ? null : sup.root),
 
1622
                SymbolAtom.get(name), kernUnit, kern, true);
 
1623
        return this;
 
1624
    }
 
1625
    
 
1626
    /**
 
1627
     * Puts the delimiter symbol represented by the given delimiter type constant under the
 
1628
     * current TeXFormula and changes the current
 
1629
     * TeXFormula into the resulting construction.
 
1630
     *
 
1631
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1632
     *                  a delimiter symbol that will be put under the current TeXFormula
 
1633
     * @return the modified TeXFormula
 
1634
     * @throws InvalidDelimiterTypeException if the given integer value does not represent
 
1635
     *                  a valid delimiter type
 
1636
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1637
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1638
     */
 
1639
    public TeXFormula putDelimiterUnder(int delimiter)
 
1640
    throws InvalidDelimiterTypeException, SymbolNotFoundException {
 
1641
        if (delimiter < 0 || delimiter >= delimiterNames.length)
 
1642
            throw new InvalidDelimiterTypeException();
 
1643
        
 
1644
        String name = delimiterNames[delimiter][UNDER_DEL];
 
1645
        root = new OverUnderDelimiter(root, null, SymbolAtom.get(name),
 
1646
                TeXConstants.UNIT_EX, 0, false);
 
1647
        return this;
 
1648
    }
 
1649
    
 
1650
    /**
 
1651
     * Puts the delimiter symbol represented by the given delimiter type constant under the
 
1652
     * current TeXFormula, parses the given string into a TeXFormula and puts
 
1653
     * it under the delimiter symbol (seperated by an amount of vertical space defined
 
1654
     * by the given float value and unit) in a smaller size (unless the current TeXFormula
 
1655
     * will be displayed in the smallest possible size: the script_script style's size)
 
1656
     * and finally changes the current TeXFormula into the resulting construction.
 
1657
     *
 
1658
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1659
     *                  a delimiter symbol that will be put under the current TeXFormula
 
1660
     * @param sub the string to be parsed into a TeXFormula that will be put under the
 
1661
     *                  delimiter symbol, in a smaller size if possible
 
1662
     * @param kernUnit a unit constant (from {@link TeXConstants})
 
1663
     * @param kern amount of vertical space (in kernUnit) to be put between the delimiter
 
1664
     *                  and the given TeXFormula sub
 
1665
     * @return the modified TeXFormula
 
1666
     * @throws InvalidDelimiterTypeException if the given integer value does not represent
 
1667
     *                  a valid delimiter type
 
1668
     * @throws InvalidUnitException if the given integer value (kernUnit) does not
 
1669
     *                  represent a valid unit
 
1670
     * @throws ParseException if the given string could not be parsed correctly
 
1671
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1672
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1673
     */
 
1674
    public TeXFormula putDelimiterUnder(int delimiter, String sub, int kernUnit,
 
1675
            float kern) throws InvalidDelimiterTypeException,
 
1676
            InvalidUnitException, ParseException, SymbolNotFoundException {
 
1677
        return putDelimiterUnder(delimiter, new TeXFormula(sub), kernUnit, kern);
 
1678
    }
 
1679
    
 
1680
    /**
 
1681
     * Puts the delimiter symbol represented by the given delimiter type constant under the
 
1682
     * current TeXFormula, puts the given TeXFormula under the delimiter symbol (seperated
 
1683
     * by an amount of vertical space defined by the given float value and unit) in a
 
1684
     * smaller size (unless the current TeXFormula will be displayed in the smallest
 
1685
     * possible size: the "script_script" style's size) and finally changes the current
 
1686
     * TeXFormula into the resulting construction.
 
1687
     *
 
1688
     * @param delimiter a delimiter type constant (from {@link TeXConstants}) that represents
 
1689
     *                  a delimiter symbol that will be put under the current TeXFormula
 
1690
     * @param sub the TeXFormula that will be put under the
 
1691
     *                  delimiter symbol, in a smaller size if possible
 
1692
     * @param kernUnit a unit constant (from {@link TeXConstants})
 
1693
     * @param kern amount of vertical space (in kernUnit) to be put between the delimiter
 
1694
     *                  and the given TeXFormula sub
 
1695
     * @return the modified TeXFormula
 
1696
     * @throws InvalidDelimiterTypeException if the given integer value (delimiter)
 
1697
     *                  does not represent a valid delimiter type
 
1698
     * @throws InvalidUnitException if the given integer value (kernUnit) does not
 
1699
     *                  represent a valid unit
 
1700
     * @throws SymbolNotFoundException if the definition of the symbol represented
 
1701
     *                  by the delimiter constant was not found (due to user-made changes!)
 
1702
     */
 
1703
    public TeXFormula putDelimiterUnder(int delimiter, TeXFormula sub,
 
1704
            int kernUnit, float kern) throws InvalidDelimiterTypeException,
 
1705
            InvalidUnitException, SymbolNotFoundException {
 
1706
        if (delimiter < 0 || delimiter >= delimiterNames.length)
 
1707
            throw new InvalidDelimiterTypeException();
 
1708
        
 
1709
        String name = delimiterNames[delimiter][UNDER_DEL];
 
1710
        root = new OverUnderDelimiter(root, (sub == null ? null : sub.root),
 
1711
                SymbolAtom.get(name), kernUnit, kern, false);
 
1712
        return this;
 
1713
    }
 
1714
    
 
1715
    /**
 
1716
     * Puts the given TeXFormula
 
1717
     * above the current TeXFormula, in a smaller size
 
1718
     * depending on "overScriptSize" and seperated by a vertical space of size
 
1719
     * "overSpace" in "overUnit" and changes the current TeXFormula into the
 
1720
     * resulting construction.
 
1721
     *
 
1722
     * @param over the TeXFormula to be put over the current TeXFormula
 
1723
     * @param overUnit a unit constant (from TeXConstants)
 
1724
     * @param overSpace the size (in overUnit) of the vertical space between the
 
1725
     *                  current TeXFormula and the TeXFormula (over) that will be put over it
 
1726
     * @param overScriptSize whether the TeXFormula (over) that will be put over the
 
1727
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1728
     * @return the modified TeXFormula
 
1729
     * @throws InvalidUnitException if the given unit integer values doesn't
 
1730
     *                  represent a valid unit
 
1731
     */
 
1732
    public TeXFormula putOver(TeXFormula over, int overUnit, float overSpace,
 
1733
            boolean overScriptSize) throws InvalidUnitException {
 
1734
        root = new UnderOverAtom(root, (over == null ? null : over.root),
 
1735
                overUnit, overSpace, overScriptSize, true);
 
1736
        return this;
 
1737
    }
 
1738
    
 
1739
    /**
 
1740
     * Parses the given string into a TeXFormula that will be put
 
1741
     * above the current
 
1742
     * TeXFormula, in a smaller size
 
1743
     * depending on "overScriptSize" and seperated by a vertical space of size
 
1744
     * "overSpace" in "overUnit" and changes the current TeXFormula into the
 
1745
     * resulting construction.
 
1746
     *
 
1747
     * @param over the string to be parsed into a TeXFormula that will be put over the
 
1748
     *                  current TeXFormula
 
1749
     * @param overUnit a unit constant (from TeXConstants)
 
1750
     * @param overSpace the size (in overUnit) of the vertical space between the
 
1751
     *                  current TeXFormula and the TeXFormula (over) that will be put over it
 
1752
     * @param overScriptSize whether the TeXFormula (over) that will be put over the
 
1753
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1754
     * @return the modified TeXFormula
 
1755
     * @throws InvalidUnitException if one of the given unit integer values doesn't
 
1756
     *                  represent a valid unit
 
1757
     * @throws parseException if the given string could not be parsed correctly
 
1758
     */
 
1759
    public TeXFormula putOver(String over, int overUnit, float overSpace,
 
1760
            boolean overScriptSize) throws InvalidUnitException {
 
1761
        return putOver((over == null ? null : new TeXFormula(over)), overUnit,
 
1762
                overSpace, overScriptSize);
 
1763
    }
 
1764
    
 
1765
    /**
 
1766
     * Parses the given string into a TeXFormula that will be put
 
1767
     * under the current TeXFormula,
 
1768
     * in a smaller size
 
1769
     * depending on "underScriptSize" and seperated by a
 
1770
     * vertical space of size "underSpace" in "underUnit" and changes the current
 
1771
     * TeXFormula into the resulting construction.
 
1772
     *
 
1773
     * @param under the string to be parsed into a TeXFormula that will be put under
 
1774
     *                  the current TeXFormula, or null to put nothing under it
 
1775
     * @param underUnit a unit constant (from {@link TeXConstants})
 
1776
     * @param underSpace the size (in underUnit) of the vertical space between the
 
1777
     *                  current TeXFormula and the TeXFormula (under) that will be put under it
 
1778
     * @param underScriptSize whether the TeXFormula (under) that will be put under the
 
1779
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1780
     * @return the modified TeXFormula
 
1781
     * @throws InvalidUnitException if one of the given unit integer values doesn't
 
1782
     *                  represent a valid unit
 
1783
     * @throws parseException if the given string could not be parsed correctly
 
1784
     */
 
1785
    public TeXFormula putUnder(String under, int underUnit, float underSpace,
 
1786
            boolean underScriptSize) throws InvalidUnitException {
 
1787
        return putUnder((under == null ? null : new TeXFormula(under)),
 
1788
                underUnit, underSpace, underScriptSize);
 
1789
    }
 
1790
    
 
1791
    /**
 
1792
     * Puts the given TeXFormula under the current TeXFormula,
 
1793
     * in a smaller size
 
1794
     * depending on "underScriptSize" and seperated by a
 
1795
     * vertical space of size "underSpace" in "underUnit" and changes the current
 
1796
     * TeXFormula into the resulting construction.
 
1797
     *
 
1798
     * @param under the TeXFormula to be put under the current TeXFormula
 
1799
     * @param underUnit a unit constant (from {@link TeXConstants})
 
1800
     * @param underSpace the size (in underUnit) of the vertical space between the
 
1801
     *                  current TeXFormula and the TeXFormula (under) that will be put under it
 
1802
     *                  (if not null)
 
1803
     * @param underScriptSize whether the TeXFormula (under) that will be put under the
 
1804
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1805
     * @return the modified TeXFormula
 
1806
     * @throws InvalidUnitException if the given unit integer values doesn't
 
1807
     *                  represent a valid unit
 
1808
     */
 
1809
    public TeXFormula putUnder(TeXFormula under, int underUnit,
 
1810
            float underSpace, boolean underScriptSize) throws InvalidUnitException {
 
1811
        root = new UnderOverAtom(root, (under == null ? null : under.root),
 
1812
                underUnit, underSpace, underScriptSize, false);
 
1813
        return this;
 
1814
    }
 
1815
    
 
1816
    /**
 
1817
     * Parses the given string "under" into a TeXFormula that will be put
 
1818
     * under the current TeXFormula,
 
1819
     * in a smaller size
 
1820
     * depending on "underScriptSize" and seperated by a
 
1821
     * vertical space of size "underSpace" in "underUnit", parses the given string
 
1822
     * "over" into a TeXFormula that will be put above the current
 
1823
     * TeXFormula, in a smaller size
 
1824
     * depending on "overScriptSize" and seperated by a vertical space of size
 
1825
     * "overSpace" in "overUnit" and finally changes the current TeXFormula into the
 
1826
     * resulting construction.
 
1827
     *
 
1828
     * @param under the string to be parsed into a TeXFormula that will be put under
 
1829
     *                  the current TeXFormula, or null to put nothing under it
 
1830
     * @param underUnit a unit constant (from {@link TeXConstants})
 
1831
     * @param underSpace the size (in underUnit) of the vertical space between the
 
1832
     *                  current TeXFormula and the TeXFormula (under) that will be put under it
 
1833
     * @param underScriptSize whether the TeXFormula (under) that will be put under the
 
1834
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1835
     * @param over the string to be parsed into a TeXFormula that will be put over the
 
1836
     *                  current TeXFormula, or null to put nothing over it
 
1837
     * @param overUnit a unit constant (from TeXConstants)
 
1838
     * @param overSpace the size (in overUnit) of the vertical space between the
 
1839
     *                  current TeXFormula and the TeXFormula (over) that will be put over it
 
1840
     * @param overScriptSize whether the TeXFormula (over) that will be put over the
 
1841
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1842
     * @return the modified TeXFormula
 
1843
     * @throws InvalidUnitException if one of the given unit integer values doesn't
 
1844
     *                  represent a valid unit
 
1845
     * @throws parseException if one of the given strings could not be parsed correctly
 
1846
     */
 
1847
    public TeXFormula putUnderAndOver(String under, int underUnit,
 
1848
            float underSpace, boolean underScriptSize, String over, int overUnit,
 
1849
            float overSpace, boolean overScriptSize) throws InvalidUnitException,
 
1850
            ParseException {
 
1851
        return putUnderAndOver((under == null ? null : new TeXFormula(under)),
 
1852
                underUnit, underSpace, underScriptSize, (over == null ? null
 
1853
                : new TeXFormula(over)), overUnit, overSpace, overScriptSize);
 
1854
    }
 
1855
    
 
1856
    /**
 
1857
     * Puts the given TeXFormula "under" under the current TeXFormula,
 
1858
     * in a smaller size
 
1859
     * depending on "underScriptSize" and seperated by a
 
1860
     * vertical space of size "underSpace" in "underUnit", puts the given TeXFormula
 
1861
     * "over" above the current TeXFormula, in a smaller size
 
1862
     * depending on "overScriptSize" and seperated by a vertical space of size
 
1863
     * "overSpace" in "overUnit" and finally changes the current TeXFormula into the
 
1864
     * resulting construction.
 
1865
     *
 
1866
     * @param under the TeXFormula to be put under the current TeXFormula
 
1867
     * @param underUnit a unit constant (from {@link TeXConstants})
 
1868
     * @param underSpace the size (in underUnit) of the vertical space between the
 
1869
     *                  current TeXFormula and the TeXFormula (under) that will be put under it
 
1870
     * @param underScriptSize whether the TeXFormula (under) that will be put under the
 
1871
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1872
     * @param over the TeXFormula to be put over the current TeXFormula
 
1873
     * @param overUnit a unit constant (from TeXConstants)
 
1874
     * @param overSpace the size (in overUnit) of the vertical space between the
 
1875
     *                  current TeXFormula and the TeXFormula (over) that will be put over it
 
1876
     * @param overScriptSize whether the TeXFormula (over) that will be put over the
 
1877
     *                  current TeXFormula should be displayed in a smaller size (if possible)
 
1878
     * @return the modified TeXFormula
 
1879
     * @throws InvalidUnitException if one of the given unit integer values doesn't
 
1880
     *                  represent a valid unit
 
1881
     */
 
1882
    public TeXFormula putUnderAndOver(TeXFormula under, int underUnit,
 
1883
            float underSpace, boolean underScriptSize, TeXFormula over,
 
1884
            int overUnit, float overSpace, boolean overScriptSize)
 
1885
            throws InvalidUnitException {
 
1886
        root = new UnderOverAtom(root, (under == null ? null : under.root),
 
1887
                underUnit, underSpace, underScriptSize, (over == null ? null
 
1888
                : over.root), overUnit, overSpace, overScriptSize);
 
1889
        return this;
 
1890
    }
 
1891
    
 
1892
   /*
 
1893
    * Replaces "'" with "^{\prime}", "''" with "^{\prime\prime}", etc. at the
 
1894
    * current position in the parse string.
 
1895
    */
 
1896
    private void replaceAccents() {
 
1897
        StringBuffer buf = new StringBuffer(SUPER_SCRIPT);
 
1898
        buf.append(L_GROUP);
 
1899
        buf.append("\\prime");
 
1900
        int i = pos + 1;
 
1901
        while (i < parseString.length()) {
 
1902
            if (parseString.charAt(i) == PRIME)
 
1903
                buf.append("\\prime");
 
1904
            else if (!isWhiteSpace(parseString.charAt(i)))
 
1905
                break;
 
1906
            i++;
 
1907
        }
 
1908
        buf.append(R_GROUP);
 
1909
        // construct the new parsing string
 
1910
        parseString = parseString.substring(0, pos) + buf.toString()
 
1911
        + parseString.substring(i);
 
1912
    }
 
1913
    
 
1914
    /**
 
1915
     * Changes the background color of the <i>current</i> TeXFormula into the given color.
 
1916
     * By default, a TeXFormula has no background color, it's transparent.
 
1917
     * The backgrounds of subformula's will be painted on top of the background of
 
1918
     * the whole formula! Any changes that will be made to this TeXFormula after this
 
1919
     * background color was set, will have the default background color (unless it will
 
1920
     * also be changed into another color afterwards)!
 
1921
     *
 
1922
     * @param c the desired background color for the <i>current</i> TeXFormula
 
1923
     * @return the modified TeXFormula
 
1924
     */
 
1925
    public TeXFormula setBackground(Color c) {
 
1926
        if (c != null) {
 
1927
            if (root instanceof ColorAtom)
 
1928
                root = new ColorAtom(c, null, (ColorAtom) root);
 
1929
            else
 
1930
                root = new ColorAtom(root, c, null);
 
1931
        }
 
1932
        return this;
 
1933
    }
 
1934
    
 
1935
    /**
 
1936
     * Changes the (foreground) color of the <i>current</i> TeXFormula into the given color.
 
1937
     * By default, the foreground color of a TeXFormula is the foreground color of the
 
1938
     * component on which the TeXIcon (created from this TeXFormula) will be painted. The
 
1939
     * color of subformula's overrides the color of the whole formula.
 
1940
     * Any changes that will be made to this TeXFormula after this color was set, will be
 
1941
     * painted in the default color (unless the color will also be changed afterwards into
 
1942
     * another color)!
 
1943
     *
 
1944
     * @param c the desired foreground color for the <i>current</i> TeXFormula
 
1945
     * @return the modified TeXFormula
 
1946
     */
 
1947
    public TeXFormula setColor(Color c) {
 
1948
        if (c != null) {
 
1949
            if (root instanceof ColorAtom)
 
1950
                root = new ColorAtom(null, c, (ColorAtom) root);
 
1951
            else
 
1952
                root = new ColorAtom(root, null, c);
 
1953
        }
 
1954
        return this;
 
1955
    }
 
1956
    
 
1957
    /**
 
1958
     * Sets a fixed left and right type of the current TeXFormula. This has an influence
 
1959
     * on the glue that will be inserted before and after this TeXFormula.
 
1960
     *
 
1961
     * @param leftType atom type constant (from {@link TeXConstants})
 
1962
     * @param rightType atom type constant (from TeXConstants)
 
1963
     * @return the modified TeXFormula
 
1964
     * @throws InvalidAtomTypeException if the given integer value does not represent
 
1965
     *                  a valid atom type
 
1966
     */
 
1967
    public TeXFormula setFixedTypes(int leftType, int rightType)
 
1968
    throws InvalidAtomTypeException {
 
1969
        root = new TypedAtom(leftType, rightType, root);
 
1970
        return this;
 
1971
    }
 
1972
    
 
1973
    /**
 
1974
     * Parses the given strings into TeXFormula's and attaches them <i>together</i> to
 
1975
     * the <i>current</i> TeXFormula as a subscript (sub) and a superscript (sup).
 
1976
     * This is not the
 
1977
     * same as attaching both scripts seperately one after another (in either order) using
 
1978
     * the setSubscript(String) and the setSuperscript(String) methods!
 
1979
     *
 
1980
     * @param sub the string to be parsed into a TeXFormula that will be attached to
 
1981
     *                  the current TeXFormula as a subscript
 
1982
     * @param sup the string to be parsed into a TeXFormula that will be attached to
 
1983
     *                  the current TeXFormula as a superscript
 
1984
     * @return the modified TeXFormula
 
1985
     * @throws ParseException if one of the given strings could not be parsed correctly
 
1986
     */
 
1987
    public TeXFormula setScripts(String sub, String sup) throws ParseException {
 
1988
        return setScripts(new TeXFormula(sub), new TeXFormula(sup));
 
1989
    }
 
1990
    
 
1991
    /**
 
1992
     * Parses the given string into a TeXFormula's and attaches it to
 
1993
     * the <i>current</i> TeXFormula as a subscript <i>together</i> with the given
 
1994
     * TeXFormula
 
1995
     * (as a superscript). This is not the same as attaching both scripts seperately
 
1996
     * one after another (in either order) using the setSubscript(String)
 
1997
     * and the setSuperscript(TeXFormula) methods!
 
1998
     *
 
1999
     * @param sub the string to be parsed into a TeXFormula that will be attached to
 
2000
     *                  the current TeXFormula as a subscript
 
2001
     * @param sup the TeXFormula that will be attached to
 
2002
     *                  the current TeXFormula as a superscript
 
2003
     * @return the modified TeXFormula
 
2004
     * @throws ParseException if the given string could not be parsed correctly
 
2005
     */
 
2006
    public TeXFormula setScripts(String sub, TeXFormula sup)
 
2007
    throws ParseException {
 
2008
        return setScripts(new TeXFormula(sub), sup);
 
2009
    }
 
2010
    
 
2011
    /**
 
2012
     * Parses the given string into a TeXFormula's and attaches it to
 
2013
     * the <i>current</i> TeXFormula as a superscript <i>together</i> with the given TeXFormula
 
2014
     * (as a subscript). This is not the same as attaching both scripts seperately
 
2015
     * one after another (in either order) using the setSubscript(TeXFormula)
 
2016
     * and the setSuperscript(String) methods!
 
2017
     *
 
2018
     * @param sub the TeXFormula that will be attached to
 
2019
     *                  the current TeXFormula as a subscript
 
2020
     * @param sup the string to be parsed into a TeXFormula that will be attached to
 
2021
     *                  the current TeXFormula as a superscript
 
2022
     * @return the modified TeXFormula
 
2023
     * @throws ParseException if the given string could not be parsed correctly
 
2024
     */
 
2025
    public TeXFormula setScripts(TeXFormula sub, String sup)
 
2026
    throws ParseException {
 
2027
        return setScripts(sub, new TeXFormula(sup));
 
2028
    }
 
2029
    
 
2030
    /**
 
2031
     * Attaches the given TeXFormula's <i>together</i> to the <i>current</i> TeXFormula as a subscript
 
2032
     * (sub) and a superscript (sup). This is not the same as attaching both scripts seperately
 
2033
     * one after another (in either order) using the setSubscript(TeXFormula)
 
2034
     * and the setSuperscript(TeXFormula) methods!
 
2035
     *
 
2036
     * @param sub the TeXFormula that will be attached to
 
2037
     *                  the current TeXFormula as a subscript
 
2038
     * @param sup the TeXFormula that will be attached to
 
2039
     *                  the current TeXFormula as a superscript
 
2040
     * @return the modified TeXFormula
 
2041
     */
 
2042
    public TeXFormula setScripts(TeXFormula sub, TeXFormula sup) {
 
2043
        root = new ScriptsAtom(root, (sub == null ? null : sub.root),
 
2044
                (sup == null ? null : sup.root));
 
2045
        return this;
 
2046
    }
 
2047
    
 
2048
    /**
 
2049
     * Parses the given string into a TeXFormula and attaches it to the <i>current</i>
 
2050
     * TeXFormula as a subscript.
 
2051
     *
 
2052
     * @param sub the string to be parsed into a TeXFormula that will be attached
 
2053
     *                  to the current TeXFormula as a subscript
 
2054
     * @return the modified TeXFormula
 
2055
     * @throws ParseException if the given string could not be parsed correctly
 
2056
     */
 
2057
    public TeXFormula setSubscript(String sub) throws ParseException {
 
2058
        return setSubscript(new TeXFormula(sub));
 
2059
    }
 
2060
    
 
2061
    /**
 
2062
     * Attaches the given TeXFormula to the <i>current</i> TeXFormula as a subscript.
 
2063
     *
 
2064
     * @param sub the TeXFormula that will be attached
 
2065
     *                  to the current TeXFormula as a subscript
 
2066
     * @return the modified TeXFormula
 
2067
     */
 
2068
    public TeXFormula setSubscript(TeXFormula sub) {
 
2069
        root = new ScriptsAtom(root, (sub == null ? null : sub.root), null);
 
2070
        return this;
 
2071
    }
 
2072
    
 
2073
    /**
 
2074
     * Parses the given string into a TeXFormula and attaches it to the <i>current</i>
 
2075
     * TeXFormula as a superscript.
 
2076
     *
 
2077
     * @param sup the string to be parsed into a TeXFormula that will be attached
 
2078
     *                  to the current TeXFormula as a superscript
 
2079
     * @return the modified TeXFormula
 
2080
     * @throws ParseException if the given string could not be parsed correctly
 
2081
     */
 
2082
    public TeXFormula setSuperscript(String sup) throws ParseException {
 
2083
        return setSuperscript(new TeXFormula(sup));
 
2084
    }
 
2085
    
 
2086
    /**
 
2087
     * Attaches the given TeXFormula to the <i>current</i> TeXFormula as a superscript.
 
2088
     *
 
2089
     * @param sup the TeXFormula that will be attached
 
2090
     *                  to the current TeXFormula as a superscript
 
2091
     * @return the modified TeXFormula
 
2092
     */
 
2093
    public TeXFormula setSuperscript(TeXFormula sup) {
 
2094
        root = new ScriptsAtom(root, null, (sup == null ? null : sup.root));
 
2095
        return this;
 
2096
    }
 
2097
    
 
2098
   /*
 
2099
    * change the current position (in the parse string) to the first following
 
2100
    * non-whitespace character
 
2101
    */
 
2102
    private void skipWhiteSpace() {
 
2103
        while (pos < parseString.length()
 
2104
        && isWhiteSpace(parseString.charAt(pos)))
 
2105
            pos++;
 
2106
    }
 
2107
    
 
2108
    /**
 
2109
     * Puts the current TeXFormula under a root sign and changes the current TeXFormula
 
2110
     * into the resulting square root construction.
 
2111
     *
 
2112
     * @return the modified TeXFormula
 
2113
     */
 
2114
    public TeXFormula sqrt() {
 
2115
        return nthRoot((TeXFormula) null);
 
2116
    }
 
2117
    
 
2118
    /**
 
2119
     * Puts a line under the current TeXFormula and changes the current TeXFormula into
 
2120
     * the resulting construction.
 
2121
     *
 
2122
     * @return the modified TeXFormula
 
2123
     */
 
2124
    public TeXFormula underline() {
 
2125
        root = new UnderlinedAtom(root);
 
2126
        return this;
 
2127
    }
 
2128
    
 
2129
    /**
 
2130
     * Get a predefined TeXFormula.
 
2131
     *
 
2132
     * @param name the name of the predefined TeXFormula
 
2133
     * @return a copy of the predefined TeXFormula
 
2134
     * @throws FormulaNotFoundException if no predefined TeXFormula is found with the
 
2135
     *                  given name
 
2136
     */
 
2137
    public static TeXFormula get(String name) throws FormulaNotFoundException {
 
2138
        Object formula = predefinedTeXFormulas.get(name);
 
2139
        if (formula == null)
 
2140
            throw new FormulaNotFoundException(name);
 
2141
        else
 
2142
            return new TeXFormula((TeXFormula) formula);
 
2143
    }
 
2144
    
 
2145
   /*
 
2146
    * Retrieves the delimiter mapping (a symbol name) of the given character
 
2147
    * from a hash table.
 
2148
    */
 
2149
    private static String getCharacterToDelimiterMapping(char ch)
 
2150
    throws DelimiterMappingNotFoundException {
 
2151
        String str = delimiterMappings[ch];
 
2152
        if (str == null)
 
2153
            throw new DelimiterMappingNotFoundException(ch);
 
2154
        else
 
2155
            return str;
 
2156
    }
 
2157
    
 
2158
   /*
 
2159
    * Retrieves the delimiter symbol with the given name from a hash table
 
2160
    * and checks if it's a valid delimiter.
 
2161
    */
 
2162
    private static SymbolAtom getDelimiterSymbol(String delName)
 
2163
    throws SymbolNotFoundException, InvalidDelimiterException {
 
2164
        SymbolAtom res = null;
 
2165
        // null means no delimiter
 
2166
        if (delName != null) {
 
2167
            res = SymbolAtom.get(delName);
 
2168
            // check if the symbol is a delimiter
 
2169
            if (!res.isDelimiter())
 
2170
                throw new InvalidDelimiterException(delName);
 
2171
        }
 
2172
        return res;
 
2173
    }
 
2174
    
 
2175
   /*
 
2176
    * Tests if the given character is a symbol character. A character is a
 
2177
    * symbol character if it is not alphanumeric.
 
2178
    */
 
2179
    private static boolean isSymbol(char c) {
 
2180
        return !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
 
2181
    }
 
2182
    
 
2183
   /*
 
2184
    * Tests if the given character is a whitespace character.
 
2185
    */
 
2186
    private static boolean isWhiteSpace(char ch) {
 
2187
        return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
 
2188
    }
 
2189
}