EMMA Coverage Report (generated Sun Apr 20 22:38:01 CEST 2008)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [StringTools.java]

nameclass, %method, %block, %line, %
StringTools.java100% (1/1)89%  (32/36)87%  (1113/1280)90%  (246.2/275)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class StringTools100% (1/1)89%  (32/36)87%  (1113/1280)90%  (246.2/275)
arrayToString (File []): String 0%   (0/1)0%   (0/45)0%   (0/7)
arrayToString (float []): String 0%   (0/1)0%   (0/43)0%   (0/7)
isSorted (String []): boolean 0%   (0/1)0%   (0/27)0%   (0/6)
sourced (char): String 0%   (0/1)0%   (0/5)0%   (0/1)
getMnemonics (String [], char []): char [] 100% (1/1)80%  (146/182)86%  (32/37)
isVisibleChar (char): boolean 100% (1/1)86%  (6/7)86%  (0.9/1)
integerListToArray (List): int [] 100% (1/1)87%  (34/39)95%  (11.4/12)
getNextVisibleChar (String, int): char 100% (1/1)96%  (24/25)89%  (8/9)
stringToIntArray (String): int [] 100% (1/1)98%  (140/143)97%  (32/33)
StringTools (): void 100% (1/1)99%  (99/100)100% (14/14)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
arrayToString (String []): String 100% (1/1)100% (45/45)100% (7/7)
arrayToString (double []): String 100% (1/1)100% (43/43)100% (7/7)
arrayToString (int []): String 100% (1/1)100% (43/43)100% (7/7)
asHtmlEscaped (String): String 100% (1/1)100% (86/86)100% (19/19)
camelized (String): String 100% (1/1)100% (41/41)100% (11/11)
colorString (Color): String 100% (1/1)100% (6/6)100% (1/1)
colorString (long): String 100% (1/1)100% (26/26)100% (4/4)
equalsAnyOf (String [], String): boolean 100% (1/1)100% (6/6)100% (1/1)
equalsAnyOf (String [], String, boolean): boolean 100% (1/1)100% (29/29)100% (9/9)
extractNumberBackwards (String, int): String 100% (1/1)100% (22/22)100% (5/5)
getDigitCount (int): int 100% (1/1)100% (30/30)100% (9/9)
getLeadingZeroFormat (int): DecimalFormat 100% (1/1)100% (16/16)100% (4/4)
getPercentText (double): String 100% (1/1)100% (5/5)100% (1/1)
hasChar (char [], char): boolean 100% (1/1)100% (22/22)100% (7/7)
hexString (long, int): String 100% (1/1)100% (6/6)100% (1/1)
hexString (long, int, String): String 100% (1/1)100% (44/44)100% (9/9)
instance (): StringTools 100% (1/1)100% (8/8)100% (3/3)
isEscapable (Character): boolean 100% (1/1)100% (5/5)100% (1/1)
isNullOrEmpty (String): boolean 100% (1/1)100% (15/15)100% (4/4)
isWhiteSpace (String): boolean 100% (1/1)100% (27/27)100% (7/7)
log10 (double): double 100% (1/1)100% (5/5)100% (1/1)
sourced (File): String 100% (1/1)100% (15/15)100% (4/4)
sourced (String): String 100% (1/1)100% (96/96)100% (20/20)
titled (String): String 100% (1/1)100% (5/5)100% (1/1)
trimPrefix (String, String): String 100% (1/1)100% (14/14)100% (4/4)

1// Jomic - a viewer for comic book archives.
2// Copyright (C) 2004-2008 Thomas Aglassinger
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16package net.sf.jomic.tools;
17 
18import java.awt.Color;
19import java.io.EOFException;
20import java.io.File;
21import java.io.IOException;
22import java.text.CharacterIterator;
23import java.text.DecimalFormat;
24import java.text.NumberFormat;
25import java.text.ParseException;
26import java.text.StringCharacterIterator;
27import java.util.Arrays;
28import java.util.Iterator;
29import java.util.LinkedList;
30import java.util.List;
31import java.util.Map;
32import java.util.StringTokenizer;
33import java.util.TreeMap;
34 
35import net.sf.wraplog.Logger;
36 
37/**
38 *  Utility class to simplify dealing with Strings.
39 *
40 * @author    Thomas Aglassinger
41 */
42public final class StringTools
43{
44    private static final int ASCII_CODE_OF_SPACE = 32;
45    private static final char BEFORE_COMMA_OR_CLOSE_BRAKET = ',';
46    private static final char BEFORE_END = 'x';
47    private static final char BEFORE_OPEN_BRAKET = '[';
48    private static final char BEFORE_VALUE_OR_CLOSE_BRAKET = ']';
49    private static final String CLOSE_BRAKET = "]";
50    private static final int COLOR_HEX_DIGIT_COUNT = 6;
51    /**
52     *  Size of slack in buffer escaped characters get encoded to.
53     */
54    private static final int ESCAPE_SLACK_COUNT = 16;
55    private static final int LAST_ASCII_CODE = 127;
56    private static final double LOG_OF_10 = Math.log(10);
57    private static final String OPEN_BRAKET = "[";
58    private static final int RGB_MASK = 0xffffff;
59    private static final int UNICODE_HEX_DIGIT_COUNT = 4;
60 
61    /**
62     *  Minimum RGB value. This is &lt; 0 because negative values represent 2-complements of values
63     *  with a high red component.
64     */
65    //@ private static ghost final int RGB_MIN_VALUE = -0x800000;
66    //@ private static ghost final int MAX_HEX_DIGIT_COUNT = 16;
67 
68    private static /*@ spec_public nullable @*/ StringTools instance;
69    private ErrorTools errorTools;
70    private Logger logger;
71    private NumberFormat percentFormat;
72    private Map escapeMap;
73 
74    private StringTools() {
75        logger = Logger.getLogger(StringTools.class);
76        errorTools = ErrorTools.instance();
77        percentFormat = new DecimalFormat("#.00");
78        escapeMap = new TreeMap();
79        escapeMap.put(new Character('\"'), "\\\"");
80        escapeMap.put(new Character('\''), "\\\'");
81        escapeMap.put(new Character('\\'), "\\\\");
82        escapeMap.put(new Character('\b'), "\\b");
83        escapeMap.put(new Character('\f'), "\\f");
84        escapeMap.put(new Character('\n'), "\\n");
85        escapeMap.put(new Character('\r'), "\\r");
86        escapeMap.put(new Character('\t'), "\\t");
87    }
88 
89    /**
90     *  Get number of digits required to represent <code>value</code> (including a possible '-' for
91     *  negative numbers).
92     */
93    //@ ensures \result > 0;
94    //@ ensures (value < 0) ==> (\result > 0);
95    public /*@ pure @*/ int getDigitCount(int value) {
96        int result;
97 
98        if (value == 0) {
99            result = 1;
100        } else {
101            int relevantValue;
102 
103            if (value == Integer.MIN_VALUE) {
104                // Take into account that Math.abs(Integer.MIN_VALUE) still yields Integer.MIN_VALUE.
105                relevantValue = Integer.MAX_VALUE - 1;
106            } else {
107                // Take into account that (Integer.MAX_VALUE + 1) yields 0.
108                relevantValue = Math.min(Math.abs(value), Integer.MAX_VALUE - 1);
109            }
110 
111            result = (int) Math.ceil(log10((relevantValue + 1)));
112            if (value < 0) {
113                // Add another digit got the "-" sign.
114                result += 1;
115            }
116        }
117        return result;
118    }
119 
120    /**
121     *  Get DecimalFormat with leading zeroes that can handle up to <code>maxValue</code>.
122     */
123    //@ requires maxValue >= 0;
124    public /*@ pure @*/ DecimalFormat getLeadingZeroFormat(int maxValue) {
125        int digitCount = getDigitCount(maxValue);
126        String pattern = "0000000000".substring(0, digitCount);
127        DecimalFormat result = new DecimalFormat(pattern);
128 
129        return result;
130    }
131 
132    /**
133     *  Get unique mnemonics for all <code>labels</code>.
134     *
135     * @param  labels       the long texts that need mnemonics
136     * @param  suggestions  an array with the same length as <code>labels</code> that can already
137     *      contain mnemonics for certain labels. For the rest, use <code>'\0'</code> as suggestion
138     *      so the will be computed automatically. A suggestion of <code>null</code> means that all
139     *      mnemonics should be computed.
140     */
141    //@ requires labels.length > 0;
142    //@ requires (suggestions != null) ==> (suggestions.length == labels.length);
143    //@ ensures \result.length == labels.length;
144    public  char[] getMnemonics(String[] labels, /*@ nullable @*/ char[] suggestions) {
145        int mnemonicCount = labels.length;
146        char[] result = new char[mnemonicCount];
147 
148        if (suggestions != null) {
149            int suggestionCount = suggestions.length;
150 
151            for (int i = 0; i < suggestionCount; i += 1) {
152                result[i] = suggestions[i];
153            }
154        }
155 
156        for (int i = 0; i < mnemonicCount; i += 1) {
157            String label = labels[i];
158            char mnemonic = 0;
159 
160            // Check that we have some letters.
161            int labelLength = label.length();
162            int j = 0;
163 
164            while ((j < labelLength) && !isVisibleChar(label.charAt(j))) {
165                j += 1;
166            }
167            //@ assert j < labelLength;
168 
169            // Pass 1: try first letters of words
170            StringTokenizer tokenizer = new StringTokenizer(label);
171 
172            while (tokenizer.hasMoreTokens() && (mnemonic == 0)) {
173                String token = tokenizer.nextToken();
174 
175                //@ assert token.length() > 0;
176                char candidate = Character.toLowerCase(getNextVisibleChar(token, 0));
177 
178                if (candidate != 0) {
179                    if (!hasChar(result, candidate)) {
180                        mnemonic = candidate;
181                        if (logger.isDebugEnabled()) {
182                            logger.debug("found mnemonic for " + sourced(label) + ": "
183                                    + mnemonic + " (looking for words)");
184                        }
185                    }
186                }
187            }
188 
189            // Pass 2: try any letter
190            if (mnemonic == 0) {
191                int k = 0;
192 
193                while ((k < labelLength) && (mnemonic == 0)) {
194                    char candidate = Character.toLowerCase(label.charAt(k));
195 
196                    if (isVisibleChar(candidate) && !hasChar(result, candidate)) {
197                        mnemonic = candidate;
198                        if (logger.isDebugEnabled()) {
199                            logger.debug("found mnemonic for " + sourced(label) + ": "
200                                    + mnemonic + " (looking for any letter)");
201                        }
202                    } else {
203                        k += 1;
204                    }
205                }
206            }
207 
208            if (mnemonic == 0) {
209                // Give up, the current algorithm is too dumb to find anything
210                // TODO: make auto-mnemonics work in special, nasty cases
211                throw new IllegalStateException("cannot find mnemonic for label " + i + ": "
212                        + sourced(label));
213            }
214 
215            //@ assert mnemonic == Character.toLowerCase(mnemonic);
216            result[i] = mnemonic;
217        }
218 
219        return result;
220    }
221 
222    /**
223     *  Get <code>value</code> formatted as percent.
224     */
225    public /*@ pure @*/ String getPercentText(double value) {
226        return percentFormat.format(value);
227    }
228 
229    /**
230     *  Is <code>some</code> null or only consisting of white space?
231     */
232    public /*@ pure @*/ boolean isNullOrEmpty(/*@ nullable @*/ String some) {
233        boolean result;
234 
235        if (some == null) {
236            result = true;
237        } else {
238            result = (some.trim().length() == 0);
239        }
240        return result;
241    }
242 
243    /**
244     * Return <code>true</code> if <code>some</code> is sorted.
245     *
246     * @see String#compareTo(java.lang.String)
247     */
248    public /*@ pure @*/ boolean isSorted(String[] some) {
249        boolean result = true;
250        int i = 0;
251 
252        while (result && (i < some.length - 1)) {
253            if (some[i].compareTo(some[i + 1]) > 0) {
254                result = false;
255            }
256        }
257        return result;
258    }
259 
260    /**
261     *  Are all characters in <code>some</code> white space? If <code>some</code> is empty, return
262     *  <code>false</code>.
263     */
264    public /*@ pure @*/ boolean isWhiteSpace(String some) {
265        boolean result = some.length() > 0;
266        int index = 0;
267 
268        while (result && (index < some.length())) {
269            if (Character.isWhitespace(some.charAt(index))) {
270                index += 1;
271            } else {
272                result = false;
273            }
274        }
275        return result;
276    }
277 
278    private /*@ pure @*/ char getNextVisibleChar(String text, int startIndex) {
279        char result = 0;
280        int index = startIndex;
281 
282        while (index < text.length() && (result == 0)) {
283            char nextChar = text.charAt(index);
284 
285            if (isVisibleChar(nextChar)) {
286                result = nextChar;
287            } else {
288                index += 1;
289            }
290        }
291        return result;
292    }
293 
294    /**
295     *  Returns true if <code>some</code> is visible.
296     */
297    private /*@ pure @*/ boolean isVisibleChar(char some) {
298        return some > ASCII_CODE_OF_SPACE;
299    }
300 
301    /**
302     *  Get accessor to unique instance.
303     */
304    //@ assignable instance;
305    public static synchronized StringTools instance() {
306        if (instance == null) {
307            instance = new StringTools();
308        }
309        return instance;
310    }
311 
312    public /*@ pure @*/ String arrayToString(double[] data) {
313        String result = "[";
314 
315        for (int i = 0; i < data.length; i += 1) {
316            if (i > 0) {
317                result += ", ";
318            }
319            result += data[i];
320        }
321        result += "]";
322        return result;
323    }
324 
325    public /*@ pure @*/ String arrayToString(File[] data) {
326        String result = "[";
327 
328        for (int i = 0; i < data.length; i += 1) {
329            if (i > 0) {
330                result += ", ";
331            }
332            result += sourced(data[i]);
333        }
334        result += "]";
335        return result;
336    }
337 
338    public /*@ pure @*/ String arrayToString(float[] data) {
339        String result = "[";
340 
341        for (int i = 0; i < data.length; i += 1) {
342            if (i > 0) {
343                result += ", ";
344            }
345            result += data[i];
346        }
347        result += "]";
348        return result;
349    }
350 
351    public /*@ pure @*/ String arrayToString(int[] data) {
352        String result = "[";
353 
354        for (int i = 0; i < data.length; i += 1) {
355            if (i > 0) {
356                result += ", ";
357            }
358            result += data[i];
359        }
360        result += "]";
361        return result;
362    }
363 
364    public /*@ pure @*/ String arrayToString(String[] data) {
365        String result = "[";
366 
367        for (int i = 0; i < data.length; i += 1) {
368            if (i > 0) {
369                result += ", ";
370            }
371            result += sourced(data[i]);
372        }
373        result += "]";
374        return result;
375    }
376 
377    /**
378     *  Escaped as HTML attribute value. For example, "&amp;" becomes "&amp;amp;".
379     */
380    public /*@ pure @*/ String asHtmlEscaped(String some) {
381        StringBuffer result = new StringBuffer();
382        StringCharacterIterator iterator = new StringCharacterIterator(some);
383        char character = iterator.current();
384 
385        while (character != CharacterIterator.DONE) {
386            if (character == '<') {
387                result.append("&lt;");
388            } else if (character == '>') {
389                result.append("&gt;");
390            } else if (character == '\"') {
391                result.append("&quot;");
392            } else if (character == '\'') {
393                result.append("&#39;");
394            } else if (character == '&') {
395                result.append("&amp;");
396            } else if ((character < ASCII_CODE_OF_SPACE) || (character > LAST_ASCII_CODE)) {
397                result.append("&#" + (int) character + ";");
398            } else {
399                result.append(character);
400            }
401            character = iterator.next();
402        }
403        return result.toString();
404    }
405 
406    /**
407     *  Return <code>some</code> converted to "camel case" using "-" or "_" as word seperators. For
408     *  example, "some-stuff-to-do" yields "someStuffToDo".
409     */
410    //@ ensures \result.length() <= some.length();
411    public /*@ pure @*/ String camelized(String some) {
412        StringBuffer result = new StringBuffer();
413        boolean upperNext = false;
414 
415        for (int i = 0; i < some.length(); i += 1) {
416            char c = some.charAt(i);
417 
418            if ((c == '-') || (c == '_')) {
419                upperNext = true;
420            } else {
421                if (upperNext) {
422                    c = Character.toUpperCase(c);
423                    upperNext = false;
424                }
425                result.append(c);
426            }
427        }
428 
429        return result.toString();
430    }
431 
432    /**
433     *  Hex-color representation of RGB color value. For example, and RGB tupple of red=0x12,
434     *  green=0x34, blue=0x56 would be encoded into an <code>int</code> of 0x123456, and yield
435     *  "#123456". (This is the same principle used to represent colors in HTML.)
436     *
437     * @see    Color#getRGB()
438     */
439    //@ ensures \result.length() == 7;
440    //@ ensures \result.charAt(0) == '#';
441    //@ ensures RGB_MIN_VALUE <= (RGB_MASK & rgba);
442    public /*@ pure @*/ String colorString(long rgba) {
443        long rgb = RGB_MASK & rgba;
444        String result = hexString(rgb, COLOR_HEX_DIGIT_COUNT, "");
445 
446 
447        result = "#" + result.substring(result.length() - COLOR_HEX_DIGIT_COUNT);
448        return result;
449    }
450 
451    /**
452     *  Hex-color representation of <code>color</code>.
453     *
454     * @see    java.awt.Color#getRGB()
455     */
456    //@ ensures \result.length() == 7;
457    //@ ensures \result.charAt(0) == '#';
458    //@ ensures RGB_MIN_VALUE <= (RGB_MASK & rgba);
459    public /*@ pure @*/ String colorString(Color color) {
460        return colorString(color.getRGB());
461    }
462 
463    //@ requires \nonnullelements(haystack);
464    //@ requires isSorted(haystack);
465    public  boolean equalsAnyOf(String[] haystack, String needle) {
466        return equalsAnyOf(haystack, needle, false);
467    }
468 
469    //@ requires \nonnullelements(haystack);
470    // TODO: assert !sort implies "sorted"
471    // TODO: Change "sort" to "sorted"; if true, do a binary search, otherwise a sequential search.
472    public  boolean equalsAnyOf(String[] haystack, String needle, boolean sort) {
473        boolean result;
474        String[] sortedHaystack;
475 
476        if (sort) {
477            int haystackLength = haystack.length;
478 
479            sortedHaystack = new String[haystackLength];
480            System.arraycopy(haystack, 0, sortedHaystack, 0, haystackLength);
481            Arrays.sort(sortedHaystack);
482        } else {
483            sortedHaystack = haystack;
484        }
485        result = (Arrays.binarySearch(sortedHaystack, needle) >= 0);
486        return result;
487    }
488 
489    /**
490     *  Extract number by moving backwards starting at <code>endIndex</code>. If the character at
491     *  <code>endIndex</code> is no digit, return an empty string ("").
492     */
493    //@ requires endIndex >= 0;
494    //@ requires endIndex < possiblyPagedName.length();
495    public  String extractNumberBackwards(String possiblyPagedName, int endIndex) {
496        String result;
497        int index = endIndex;
498 
499        while ((index >= 0) && Character.isDigit(possiblyPagedName.charAt(index))) {
500            index -= 1;
501        }
502        result = possiblyPagedName.substring(index + 1, endIndex + 1);
503 
504        return result;
505    }
506 
507    /**
508     *  Hex representation of <code>value</code>.
509     *
510     * @param  digits  the number of digits the result should have at least; if necessary, leading
511     *      "0"s are prepended
512     * @param  prefix  the text to be used as the fist few characters of the result; "0x" if null.
513     */
514    //@ requires digits > 0;
515    //@ requires digits <= MAX_HEX_DIGIT_COUNT;
516    public /*@ pure @*/ String hexString(long value, int digits, /*@ nullable @*/ String prefix) {
517        String result = Long.toHexString(value);
518        String actualPrefix;
519 
520        if (prefix == null) {
521            actualPrefix = "0x";
522        } else {
523            actualPrefix = prefix;
524        }
525 
526        if (result.length() < digits) {
527            String zeros = "000000000000000";
528 
529            //@ assert zeros.length() == (MAX_HEX_DIGIT_COUNT - 1);
530            result = zeros.substring(result.length() - 1, digits - 1) + result;
531        }
532        result = actualPrefix + result;
533        return result;
534    }
535 
536 
537    /**
538     *  Hex representation of <code>value</code>, prefixed with "0x".
539     *
540     * @param  digits  the number of digits the result should have at least; if necessary, leading
541     *      "0"s are prepended
542     */
543    //@ requires digits > 0;
544    //@ ensures \result.length() >= (2 + digits);
545    public /*@ pure @*/ String hexString(long value, int digits) {
546        return hexString(value, digits, null);
547    }
548 
549    /**
550     *  Get 10-log.
551     */
552    //@ requires some > 0;
553    public /*@ pure @*/ double log10(double some) {
554        return Math.log(some) / LOG_OF_10;
555    }
556 
557    private boolean isEscapable(Character some) {
558        return escapeMap.containsKey(some);
559    }
560 
561    /**
562     *  Source code version of <code>some</code> that can be pasted into a Java source. The result
563     *  is embedded in two quotes, escape characters are rendered where possible. Invisible
564     *  characters are rendered as unicode escape. The value <code>null</code> results in the the
565     *  text "null" (without quotes).
566     */
567    public /*@ pure @*/ String sourced(/*@ nullable @*/ String some) {
568        String result;
569 
570        if (some == null) {
571            result = "null";
572        } else {
573            StringBuffer buffer = new StringBuffer(some.length() + ESCAPE_SLACK_COUNT);
574 
575            buffer.append('\"');
576            for (int i = 0; i < some.length(); i += 1) {
577                char c = some.charAt(i);
578                Character cAsCharacter = new Character(c);
579                String escaped = null;
580 
581                if (isEscapable(cAsCharacter)) {
582                    escaped = (String) escapeMap.get(cAsCharacter);
583                } else if (c < ' ') {
584                    escaped = hexString(c, UNICODE_HEX_DIGIT_COUNT, "\\u");
585                }
586                if (escaped == null) {
587                    buffer.append(c);
588                } else {
589                    if (logger.isDebugEnabled()) {
590                        logger.debug("escaping: " + c + " -> " + escaped);
591                    }
592                    buffer.append(escaped);
593                }
594            }
595            buffer.append('\"');
596            result = buffer.toString();
597        }
598        return result;
599    }
600 
601    /**
602     *  Source code version of character <code>some</code> that can be pasted into a Java source.
603     *  The result is embedded in two quotes, escape characters are rendered where possible.
604     *  Invisible characters are rendered as unicode escape.
605     */
606    public /*@ pure @*/ String sourced(char some) {
607        return sourced(Character.toString(some));
608    }
609 
610    /**
611     *  Same as <code>sourced(String)</code>, but uses absolute file path.
612     *
613     * @see    #sourced(String)
614     */
615    public /*@ pure @*/ String sourced(/*@ nullable @*/ File some) {
616        String result;
617 
618        if (some == null) {
619            result = sourced((String) null);
620        } else {
621            result = sourced(some.getAbsolutePath());
622        }
623        return result;
624    }
625 
626    public int[] stringToIntArray(String data)
627        throws IOException, ParseException {
628        int[] result;
629        char parserState = BEFORE_OPEN_BRAKET;
630        List values = new LinkedList();
631        StringTokenizer parser = new StringTokenizer(data, "," + OPEN_BRAKET + CLOSE_BRAKET, true);
632        LocaleTools localeTools = LocaleTools.instance();
633 
634        while (parserState != BEFORE_END) {
635            if (parser.hasMoreTokens()) {
636                // Get next token without white space
637                String token = parser.nextToken().trim();
638 
639                if (logger.isDebugEnabled()) {
640                    logger.debug("parse: state=" + parserState + ", token=" + sourced(token));
641                }
642                if (token.length() != 0) {
643                    if (parserState == BEFORE_OPEN_BRAKET) {
644                        if (token.equals(OPEN_BRAKET)) {
645                            parserState = BEFORE_VALUE_OR_CLOSE_BRAKET;
646                        } else {
647                            String message = localeTools.getMessage("errors.arrayMustStartWithOpenBraket",
648                                    new Object[]{sourced(OPEN_BRAKET), sourced(token)});
649 
650                            throw new ParseException(message, 0);
651                        }
652                    } else if (parserState == BEFORE_VALUE_OR_CLOSE_BRAKET) {
653                        if (token.equals(CLOSE_BRAKET)) {
654                            parserState = BEFORE_END;
655                        } else {
656                            Integer integerValue = new Integer(token);
657 
658                            values.add(integerValue);
659                            parserState = BEFORE_COMMA_OR_CLOSE_BRAKET;
660                        }
661                    } else if (parserState == BEFORE_COMMA_OR_CLOSE_BRAKET) {
662                        if (token.equals(CLOSE_BRAKET)) {
663                            parserState = BEFORE_END;
664                        } else if (token.equals(",")) {
665                            parserState = BEFORE_VALUE_OR_CLOSE_BRAKET;
666                        } else {
667                            String message = localeTools.getMessage("errors.afterValueCommaOrBraketMustFollow");
668 
669                            throw new ParseException(message, 0);
670                        }
671                    } else {
672                        //@ assert false;
673                    }
674                }
675            } else {
676                throw new EOFException();
677            }
678        }
679 
680        result = integerListToArray(values);
681        return result;
682    }
683 
684    /**
685     *  Some as <code>some</code>, but with the first character converted to title case.
686     *
687     * @see    Character#toTitleCase(char)
688     */
689    //@ ensures (some.length() > 1) ==> some.substring(1).equals(\result.substring(1));
690    public /*@ pure @*/ String titled(String some) {
691        return errorTools.titled(some);
692    }
693 
694    /**
695     *  If <code>text</code> starts with <code>prefix</code>, trim it from the left, otherwise just
696     *  return <code>text</code>.
697     */
698    public /*@ pure @*/ String trimPrefix(String text, String prefix) {
699        String result;
700 
701        if (text.startsWith(prefix)) {
702            result = text.substring(prefix.length());
703        } else {
704            result = text;
705        }
706        return result;
707    }
708 
709    /**
710     *  Return <code>true</code> if <code>haystack</code> contains <code>needle</code>.
711     */
712    private /*@ pure @*/ boolean hasChar(char[] haystack, char needle) {
713        boolean result = false;
714        int i = 0;
715 
716        while (!result && i < haystack.length) {
717            if (haystack[i] == needle) {
718                result = true;
719            } else {
720                i += 1;
721            }
722        }
723        return result;
724    }
725 
726    //@ requires \nonnullelements(list.toArray());
727    //@ requires (\forall int i; (i >= 0) && (i < list.size()); list.get(i) instanceof Integer);
728    //@ ensures \result.length == list.size();
729    //@ ensures (\forall int i; (i >= 0) && (i < list.size()); \result[i] == ((Integer) list.get(i)).intValue());
730    private  /*@ pure @*/ int[] integerListToArray(List list) {
731        int[] result;
732 
733        synchronized (list) {
734            int itemCount = list.size();
735 
736            result = new int[itemCount];
737 
738            int index = 0;
739            Iterator rider = list.iterator();
740 
741            while (rider.hasNext()) {
742                Integer value = (Integer) rider.next();
743 
744                result[index] = value.intValue();
745                index += 1;
746            }
747            //@ assert itemCount == index;
748        }
749        return result;
750    }
751}

[all classes][net.sf.jomic.tools]
EMMA 2.0.4217 (C) Vladimir Roubtsov