EMMA Coverage Report (generated Sat Oct 08 11:41:37 CEST 2011)
[all classes][net.sf.jomic.tools]

COVERAGE SUMMARY FOR SOURCE FILE [StringTools.java]

nameclass, %method, %block, %line, %
StringTools.java100% (1/1)89%  (32/36)81%  (1042/1280)88%  (242.2/275)

COVERAGE BREAKDOWN BY CLASS AND METHOD

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

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