OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 /*
15
 * Créé le 3 mars 2005
16
 */
17
package org.openconcerto.utils;
18
 
83 ilm 19
import java.awt.FontMetrics;
20
import java.math.BigDecimal;
19 ilm 21
import java.nio.charset.Charset;
17 ilm 22
import java.util.ArrayList;
23
import java.util.Collections;
24
import java.util.HashMap;
25
import java.util.HashSet;
83 ilm 26
import java.util.Iterator;
17 ilm 27
import java.util.LinkedHashMap;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.Set;
28 ilm 31
import java.util.regex.Pattern;
17 ilm 32
 
33
/**
34
 * @author Sylvain CUAZ
35
 */
36
public class StringUtils {
37
 
80 ilm 38
    // required encoding see Charset
39
    public static final Charset UTF8 = Charset.forName("UTF-8");
40
    public static final Charset UTF16 = Charset.forName("UTF-16");
41
    public static final Charset ASCII = Charset.forName("US-ASCII");
42
    public static final Charset ISO8859_1 = Charset.forName("ISO-8859-1");
43
    // included in rt.jar see
28 ilm 44
    // http://docs.oracle.com/javase/7/docs/technotes/guides/intl/encoding.doc.html
80 ilm 45
    public static final Charset ISO8859_15 = Charset.forName("ISO-8859-15");
28 ilm 46
    public static final Charset Cp1252 = Charset.forName("Cp1252");
47
    public static final Charset Cp850 = Charset.forName("Cp850");
19 ilm 48
 
144 ilm 49
    public static final char BOM = '\ufeff';
50
 
93 ilm 51
    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
52
 
17 ilm 53
    /**
54
     * Retourne la chaine avec la première lettre en majuscule et le reste en minuscule.
55
     *
56
     * @param s la chaîne à transformer.
57
     * @return la chaine avec la première lettre en majuscule et le reste en minuscule.
58
     */
59
    public static String firstUpThenLow(String s) {
60
        if (s.length() == 0) {
61
            return s;
62
        }
63
        if (s.length() == 1) {
64
            return s.toUpperCase();
65
        }
66
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
67
    }
68
 
69
    public static String firstUp(String s) {
70
        if (s.length() == 0) {
71
            return s;
72
        }
73
        if (s.length() == 1) {
74
            return s.toUpperCase();
75
        }
76
        return s.substring(0, 1).toUpperCase() + s.substring(1);
77
    }
78
 
19 ilm 79
    static public abstract class Shortener {
80
 
81
        private final int hashSize;
82
        private final int hashPartSize;
83
        private final String prefix;
84
        private final String suffix;
85
        private final int minStringLength;
86
 
87
        protected Shortener(int hashSize, String prefix, String suffix, int minCharsBeforeAndAfter) {
88
            super();
89
            this.hashSize = hashSize;
90
            this.prefix = prefix;
91
            this.suffix = suffix;
92
            this.hashPartSize = this.hashSize + this.prefix.length() + this.suffix.length();
93
            if (minCharsBeforeAndAfter < 1)
94
                throw new IllegalArgumentException("minCharsBeforeAndAfter must be at least 1: " + minCharsBeforeAndAfter);
95
            this.minStringLength = this.hashPartSize + minCharsBeforeAndAfter * 2;
96
        }
97
 
98
        public final int getMinStringLength() {
99
            return this.minStringLength;
100
        }
101
 
102
        public final String getBoundedLengthString(final String s, final int maxLength) {
103
            // don't test first for s.length, it's more predictable
104
            // (otherwise boundedString("a", 2) would succeed)
105
            if (maxLength < this.getMinStringLength())
106
                throw new IllegalArgumentException("Maximum too low : " + maxLength + "<" + getMinStringLength());
107
            if (s.length() <= maxLength)
108
                return s;
109
            else
110
                return this.shorten(s, maxLength);
111
        }
112
 
113
        final String shorten(final String s, final int maxLength) {
114
            assert s.length() >= this.getMinStringLength();
115
            final int toRemoveLength = s.length() - maxLength + this.hashPartSize;
116
            // remove the middle part of encoded
117
            final int toRemoveStartIndex = s.length() / 2 - toRemoveLength / 2;
118
            final String toHash = s.substring(toRemoveStartIndex, toRemoveStartIndex + toRemoveLength);
119
 
120
            final String hash = shorten(toHash);
121
            assert this.hashSize == hash.length();
122
 
123
            final String res = s.substring(0, toRemoveStartIndex) + this.prefix + hash + this.suffix + s.substring(toRemoveStartIndex + toRemoveLength);
124
            assert res.length() == maxLength;
125
            return res;
126
        }
127
 
128
        protected abstract String shorten(String s);
129
 
130
        static public final Shortener Ellipsis = new Shortener(1, "", "", 1) {
131
            @Override
132
            protected String shorten(String s) {
133
                return "…";
134
            }
135
        };
136
 
137
        // String.hashCode() is an int written in hex
138
        static public final Shortener JavaHashCode = new Shortener(Integer.SIZE / 8 * 2, "#", "#", 3) {
139
            @Override
140
            protected String shorten(String s) {
141
                return MessageDigestUtils.asHex(MessageDigestUtils.int2bytes(s.hashCode()));
142
            }
143
        };
144
 
145
        // 128 bits written in hex
146
        static public final Shortener MD5 = new Shortener(128 / 8 * 2, "#", "#", 11) {
147
            @Override
148
            protected String shorten(String s) {
149
                return MessageDigestUtils.getHashString(MessageDigestUtils.getMD5(), s.getBytes(UTF8));
150
            }
151
        };
152
 
153
        // order descendant by getMinStringLength()
154
        static final Shortener[] ORDERED = new Shortener[] { MD5, JavaHashCode, Ellipsis };
155
    }
156
 
157
    /**
158
     * The minimum value for {@link #getBoundedLengthString(String, int)}.
159
     *
160
     * @return the minimum value for <code>maxLength</code>.
161
     */
162
    public static final int getLeastMaximum() {
163
        return Shortener.ORDERED[Shortener.ORDERED.length - 1].getMinStringLength();
164
    }
165
 
166
    private static final Shortener getShortener(final int l) {
167
        for (final Shortener sh : Shortener.ORDERED) {
168
            if (l >= sh.getMinStringLength())
169
                return sh;
170
        }
171
        return null;
172
    }
173
 
174
    /**
175
     * Return a string built from <code>s</code> that is at most <code>maxLength</code> long.
176
     *
177
     * @param s the string to bound.
178
     * @param maxLength the maximum length the result must have.
179
     * @return a string built from <code>s</code>.
180
     * @throws IllegalArgumentException if <code>maxLength</code> is too small.
181
     * @see #getLeastMaximum()
182
     * @see Shortener#getBoundedLengthString(String, int)
183
     */
184
    public static final String getBoundedLengthString(final String s, final int maxLength) throws IllegalArgumentException {
185
        // don't test first for s.length, it's more predictable
186
        // (otherwise boundedString("a", 2) would succeed)
187
        if (maxLength < getLeastMaximum())
188
            throw new IllegalArgumentException("Maximum too low : " + maxLength + "<" + getLeastMaximum());
189
 
190
        final String res;
191
        if (s.length() <= maxLength) {
192
            res = s;
193
        } else {
194
            // use maxLength to choose the shortener since it's generally a constant
195
            // and thus the strings returned by this method have the same pattern
196
            res = getShortener(maxLength).shorten(s, maxLength);
197
        }
198
        return res;
199
    }
200
 
80 ilm 201
    static public enum Side {
202
        LEFT, RIGHT
203
    }
204
 
205
    public static String getFixedWidthString(final String s, final int width, final Side align) {
206
        return getFixedWidthString(s, width, align, false);
207
    }
208
 
132 ilm 209
    public static String getFixedWidthString(final String s, final int width, final Side align, final boolean allowShorten) {
80 ilm 210
        final int length = s.length();
211
        final String res;
212
        if (length == width) {
213
            res = s;
214
        } else if (length < width) {
132 ilm 215
            // we already tested length, so no need to allow shorten
216
            res = appendFixedWidthString(new StringBuilder(width), s, width, align, ' ', false).toString();
217
        } else {
218
            res = getTooWideString(s, width, allowShorten);
219
        }
220
        assert res.length() == width;
221
        return res;
222
    }
223
 
224
    private static String getTooWideString(final String s, final int width, final boolean allowShorten) {
225
        assert s.length() > width;
226
        if (!allowShorten)
227
            throw new IllegalArgumentException("Too wide : " + s.length() + " > " + width);
228
        return getBoundedLengthString(s, width);
229
    }
230
 
231
    public static StringBuilder appendFixedWidthString(final StringBuilder sb, final String s, final int width, final Side align, final char filler, final boolean allowShorten) {
232
        final int origBuilderLen = sb.length();
233
        final int length = s.length();
234
        if (length <= width) {
235
            sb.ensureCapacity(origBuilderLen + width);
80 ilm 236
            if (align == Side.LEFT)
237
                sb.append(s);
132 ilm 238
            for (int i = length; i < width; i++) {
239
                sb.append(filler);
80 ilm 240
            }
241
            if (align == Side.RIGHT)
242
                sb.append(s);
243
        } else {
132 ilm 244
            sb.append(getTooWideString(s, width, allowShorten));
80 ilm 245
        }
132 ilm 246
        assert sb.length() == origBuilderLen + width;
247
        return sb;
80 ilm 248
    }
249
 
17 ilm 250
    public static final List<String> fastSplit(final String string, final char sep) {
251
        final List<String> l = new ArrayList<String>();
252
        final int length = string.length();
253
        final char[] cars = string.toCharArray();
254
        int rfirst = 0;
255
 
256
        for (int i = 0; i < length; i++) {
257
            if (cars[i] == sep) {
258
                l.add(new String(cars, rfirst, i - rfirst));
259
                rfirst = i + 1;
260
            }
261
        }
262
 
263
        if (rfirst < length) {
264
            l.add(new String(cars, rfirst, length - rfirst));
265
        }
266
        return l;
267
    }
268
 
83 ilm 269
    public static final List<String> fastSplitTrimmed(final String string, final char sep) {
270
        final List<String> l = new ArrayList<String>();
271
        final int length = string.length();
272
        final char[] cars = string.toCharArray();
273
        int rfirst = 0;
274
 
275
        for (int i = 0; i < length; i++) {
276
            if (cars[i] == sep) {
277
                l.add(new String(cars, rfirst, i - rfirst).trim());
278
                rfirst = i + 1;
279
            }
280
        }
281
 
282
        if (rfirst < length) {
283
            l.add(new String(cars, rfirst, length - rfirst).trim());
284
        }
285
        return l;
286
    }
287
 
19 ilm 288
    /**
289
     * Split une string s tous les nbCharMaxLine
290
     *
291
     * @param s
292
     * @param nbCharMaxLine
293
     * @return
294
     */
295
    public static String splitString(String s, int nbCharMaxLine) {
296
 
297
        if (s == null) {
298
            return s;
299
        }
300
 
301
        if (s.trim().length() < nbCharMaxLine) {
302
            return s;
303
        }
304
        StringBuffer lastString = new StringBuffer();
305
        StringBuffer result = new StringBuffer();
306
        for (int i = 0; i < s.length(); i++) {
307
 
308
            if (lastString.length() == nbCharMaxLine) {
309
                int esp = lastString.lastIndexOf(" ");
25 ilm 310
                if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
19 ilm 311
                    result.append("\n");
312
                }
313
                if (esp > 0) {
314
                    result.append(lastString.substring(0, esp).toString().trim());
315
                    lastString = new StringBuffer(lastString.substring(esp, lastString.length()));
316
                } else {
317
                    result.append(lastString.toString().trim());
318
                    lastString = new StringBuffer();
319
                }
25 ilm 320
                result.append("\n");
19 ilm 321
            }
322
 
323
            char charAt = s.charAt(i);
324
            if (charAt == '\n') {
325
                lastString.append(charAt);
21 ilm 326
                result.append(lastString);
19 ilm 327
                lastString = new StringBuffer();
328
            } else {
329
                lastString.append(charAt);
330
            }
331
        }
332
 
25 ilm 333
        if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
19 ilm 334
            result.append("\n");
335
        }
336
 
337
        result.append(lastString.toString().trim());
338
 
339
        return result.toString();
340
    }
341
 
83 ilm 342
    static public int firstIndexOf(final String s, final char[] chars) {
343
        return firstIndexOf(s, 0, chars);
344
    }
345
 
346
    static public int firstIndexOf(final String s, final int offset, final char[] chars) {
347
        int res = -1;
348
        for (final char c : chars) {
349
            final int index = s.indexOf(c, offset);
350
            if (index >= 0 && (res == -1 || index < res))
351
                res = index;
352
        }
353
        return res;
354
    }
355
 
61 ilm 356
    static private final Pattern quotePatrn = Pattern.compile("\"", Pattern.LITERAL);
357
    static private final Pattern slashPatrn = Pattern.compile("(\\\\+)");
28 ilm 358
 
359
    static public String doubleQuote(String s) {
142 ilm 360
        return doubleQuote(s, true);
361
    }
362
 
363
    static public String doubleQuote(String s, final boolean escapeEscapeChar) {
61 ilm 364
        // http://developer.apple.com/library/mac/#documentation/applescript/conceptual/applescriptlangguide/reference/ASLR_classes.html#//apple_ref/doc/uid/TP40000983-CH1g-SW6
365
        // http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5
366
        // https://developer.mozilla.org/en/JavaScript/Guide/Values%2C_Variables%2C_and_Literals#Escaping_characters
28 ilm 367
        if (s.length() > 0) {
142 ilm 368
            if (escapeEscapeChar)
369
                s = slashPatrn.matcher(s).replaceAll("$1$1");
61 ilm 370
            s = quotePatrn.matcher(s).replaceAll("\\\\\"");
28 ilm 371
        }
372
        return '"' + s + '"';
373
    }
374
 
83 ilm 375
    /**
376
     * Unquote a double quoted string.
377
     *
378
     * @param s the string to unquote, e.g. "foo\\bar".
379
     * @return the unquoted form, e.g. foo\bar.
380
     * @throws IllegalArgumentException if the string is not quoted, or if there's some extra
381
     *         content at the end.
382
     */
383
    static public String unDoubleQuote(String s) {
384
        final Tuple2<String, Integer> res = unDoubleQuote(s, 0);
385
        if (res.get1().intValue() != s.length())
386
            throw new IllegalArgumentException("Extra content at the end : " + s.substring(res.get1()));
387
        return res.get0();
388
    }
389
 
390
    /**
391
     * Unquote part of a double quoted string.
392
     *
393
     * @param s the string to unquote, e.g. pre"foo\\bar"post.
394
     * @param offset the start index of the quotes, e.g. 3.
395
     * @return the unquoted form and the index after the end quote, e.g. foo\bar and 13.
396
     * @throws IllegalArgumentException if the string is not quoted.
397
     */
398
    static public Tuple2<String, Integer> unDoubleQuote(String s, int offset) {
399
        if (s.charAt(offset) != '"')
400
            throw new IllegalArgumentException("Expected quote but got : " + s.charAt(offset));
401
        final int l = s.length();
402
        if (offset + 1 < l && s.charAt(offset + 1) == '"')
403
            return Tuple2.create("", offset + 2);
404
 
405
        offset++;
406
        final char[] chars = new char[] { '"', '\\' };
407
        final StringBuilder sb = new StringBuilder(512);
408
        boolean foundEnd = false;
409
        while (offset < l && !foundEnd) {
410
            final int index = firstIndexOf(s, offset, chars);
411
            if (index < 0)
412
                throw new IllegalArgumentException("End quote not found after " + offset);
413
            sb.append(s.substring(offset, index));
414
            if (s.charAt(index) == '"') {
415
                offset = index + 1;
416
                foundEnd = true;
417
            } else {
418
                assert s.charAt(index) == '\\';
419
                sb.append(s.charAt(index + 1));
420
                offset = index + 2;
421
            }
422
        }
423
        if (!foundEnd)
424
            throw new IllegalArgumentException("End quote not found after " + offset);
425
        return Tuple2.create(sb.toString(), offset);
426
    }
427
 
17 ilm 428
    public static final class Escaper {
429
 
430
        // eg '
431
        private final char esc;
432
 
433
        // eg { '=> S, " => D}
434
        private final Map<Character, Character> substitution;
435
        private final Map<Character, Character> inv;
436
 
437
        /**
438
         * A new escaper that will have <code>esc</code> as escape character.
439
         *
440
         * @param esc the escape character, eg '
441
         * @param name the character that will be appended to <code>esc</code>, eg with S all
442
         *        occurrences of ' will be replaced by 'S
443
         */
444
        public Escaper(char esc, char name) {
445
            super();
446
            this.esc = esc;
447
            this.substitution = new LinkedHashMap<Character, Character>();
448
            this.inv = new HashMap<Character, Character>();
449
            this.add(esc, name);
450
        }
451
 
452
        public Escaper add(char toRemove, char escapedName) {
453
            if (this.inv.containsKey(escapedName))
454
                throw new IllegalArgumentException(escapedName + " already replaces " + this.inv.get(escapedName));
455
            this.substitution.put(toRemove, escapedName);
456
            this.inv.put(escapedName, toRemove);
457
            return this;
458
        }
459
 
460
        public final Set<Character> getEscapedChars() {
461
            final Set<Character> res = new HashSet<Character>(this.substitution.keySet());
462
            res.remove(this.esc);
463
            return res;
464
        }
465
 
466
        /**
467
         * Escape <code>s</code>, so that the resulting string has none of
468
         * {@link #getEscapedChars()}.
469
         *
470
         * @param s a string to escape.
471
         * @return the escaped form.
472
         */
473
        public final String escape(String s) {
474
            String res = s;
475
            // this.esc en premier
476
            for (final Character toEsc : this.substitution.keySet()) {
477
                // use Pattern.LITERAL to avoid interpretion
478
                res = res.replace(toEsc + "", getEscaped(toEsc));
479
            }
480
            return res;
481
        }
482
 
483
        private String getEscaped(final Character toEsc) {
484
            return this.esc + "" + this.substitution.get(toEsc);
485
        }
486
 
487
        public final String unescape(String escaped) {
488
            String res = escaped;
489
            final List<Character> toEscs = new ArrayList<Character>(this.substitution.keySet());
490
            Collections.reverse(toEscs);
491
            for (final Character toEsc : toEscs) {
492
                res = res.replaceAll(getEscaped(toEsc), toEsc + "");
493
            }
494
            return res;
495
        }
496
 
497
        @Override
498
        public boolean equals(Object obj) {
499
            if (obj instanceof Escaper) {
500
                final Escaper o = (Escaper) obj;
501
                return this.esc == o.esc && this.substitution.equals(o.substitution);
502
            } else
503
                return false;
504
        }
505
 
506
        @Override
507
        public int hashCode() {
508
            return this.esc + this.substitution.hashCode();
509
        }
510
    }
511
 
25 ilm 512
    public static String rightAlign(String s, int width) {
513
        String r = s;
514
        int n = width - s.length();
515
        for (int i = 0; i < n; i++) {
516
            r = ' ' + r;
517
        }
518
        return r;
519
    }
520
 
521
    public static String leftAlign(String s, int width) {
522
        String r = s;
523
        int n = width - s.length();
524
        for (int i = 0; i < n; i++) {
525
            r += ' ';
526
        }
527
        return r;
528
    }
73 ilm 529
 
530
    public static String trim(final String s, final boolean leading) {
531
        // from String.trim()
532
        int end = s.length();
533
        int st = 0;
534
 
535
        if (leading) {
536
            while ((st < end) && (s.charAt(st) <= ' ')) {
537
                st++;
538
            }
539
        } else {
540
            while ((st < end) && (s.charAt(end - 1) <= ' ')) {
541
                end--;
542
            }
543
        }
544
        return ((st > 0) || (end < s.length())) ? s.substring(st, end) : s;
545
    }
80 ilm 546
 
547
    public static String limitLength(String s, int maxLength) {
548
        if (s.length() <= maxLength) {
549
            return s;
550
        }
551
        return s.substring(0, maxLength);
552
    }
553
 
554
    public static String removeAllSpaces(String text) {
555
        final int length = text.length();
556
        final StringBuilder builder = new StringBuilder(length);
557
        for (int i = 0; i < length; i++) {
558
            char c = text.charAt(i);
559
            if (c <= ' ' && c != 160) {
560
                // remove non printable chars
561
                // spaces
562
                // non breakable space (160)
563
                builder.append(c);
564
            }
565
        }
566
        return builder.toString();
567
    }
568
 
569
    public static String removeNonDecimalChars(String text) {
570
        final int length = text.length();
571
        final StringBuilder builder = new StringBuilder(length);
572
        for (int i = 0; i < length; i++) {
573
            char c = text.charAt(i);
574
            if (Character.isDigit(c) || c == '.' || c == '+' || c == '-') {
575
                builder.append(c);
576
            }
577
        }
578
        return builder.toString();
579
    }
83 ilm 580
 
581
    public static BigDecimal getBigDecimalFromUserText(String text) {
582
        text = text.trim();
583
        if (text.isEmpty() || text.equals("-")) {
584
            return BigDecimal.ZERO;
585
        }
586
        text = removeNonDecimalChars(text);
587
        BigDecimal result = null;
588
        try {
589
            result = new BigDecimal(text);
590
        } catch (Exception e) {
591
            Log.get().info(text + " is not a valid decimal");
592
        }
593
        return result;
594
    }
595
 
596
    /**
597
     * Returns an array of strings, one for each line in the string after it has been wrapped to fit
598
     * lines of <var>maxWidth</var>. Lines end with any of cr, lf, or cr lf. A line ending at the
599
     * end of the string will not output a further, empty string.
600
     * <p>
601
     * This code assumes <var>str</var> is not <code>null</code>.
602
     *
603
     * @param str the string to split
604
     * @param fm needed for string width calculations
605
     * @param maxWidth the max line width, in points
606
     * @return a non-empty list of strings
607
     */
608
    public static List<String> wrap(String str, FontMetrics fm, int maxWidth) {
609
        List<String> lines = splitIntoLines(str);
610
        if (lines.size() == 0)
611
            return lines;
612
 
613
        List<String> strings = new ArrayList<String>();
614
        for (Iterator<String> iter = lines.iterator(); iter.hasNext();) {
615
            wrapLineInto(iter.next(), strings, fm, maxWidth);
616
        }
617
        return strings;
618
    }
619
 
620
    /**
621
     * Given a line of text and font metrics information, wrap the line and add the new line(s) to
622
     * <var>list</var>.
623
     *
624
     * @param line a line of text
625
     * @param list an output list of strings
626
     * @param fm font metrics
627
     * @param maxWidth maximum width of the line(s)
628
     */
629
    public static void wrapLineInto(String line, List<String> list, FontMetrics fm, int maxWidth) {
630
        int len = line.length();
631
        int width;
632
        while (len > 0 && (width = fm.stringWidth(line)) > maxWidth) {
633
            // Guess where to split the line. Look for the next space before
634
            // or after the guess.
635
            int guess = len * maxWidth / width;
636
            String before = line.substring(0, guess).trim();
637
 
638
            width = fm.stringWidth(before);
639
            int pos;
640
            if (width > maxWidth) // Too long
641
                pos = findBreakBefore(line, guess);
642
            else { // Too short or possibly just right
643
                pos = findBreakAfter(line, guess);
644
                if (pos != -1) { // Make sure this doesn't make us too long
645
                    before = line.substring(0, pos).trim();
646
                    if (fm.stringWidth(before) > maxWidth)
647
                        pos = findBreakBefore(line, guess);
648
                }
649
            }
650
            if (pos == -1)
651
                pos = guess; // Split in the middle of the word
652
 
653
            list.add(line.substring(0, pos).trim());
654
            line = line.substring(pos).trim();
655
            len = line.length();
656
        }
657
        if (len > 0) {
658
            list.add(line);
659
        }
660
    }
661
 
662
    /**
663
     * Returns the index of the first whitespace character or '-' in <var>line</var> that is at or
664
     * before <var>start</var>. Returns -1 if no such character is found.
665
     *
666
     * @param line a string
667
     * @param start where to star looking
668
     */
669
    public static int findBreakBefore(String line, int start) {
670
        for (int i = start; i >= 0; --i) {
671
            char c = line.charAt(i);
672
            if (Character.isWhitespace(c) || c == '-')
673
                return i;
674
        }
675
        return -1;
676
    }
677
 
678
    /**
679
     * Returns the index of the first whitespace character or '-' in <var>line</var> that is at or
680
     * after <var>start</var>. Returns -1 if no such character is found.
681
     *
682
     * @param line a string
683
     * @param start where to star looking
684
     */
685
    public static int findBreakAfter(String line, int start) {
686
        int len = line.length();
687
        for (int i = start; i < len; ++i) {
688
            char c = line.charAt(i);
689
            if (Character.isWhitespace(c) || c == '-')
690
                return i;
691
        }
692
        return -1;
693
    }
694
 
695
    /**
696
     * Returns an array of strings, one for each line in the string. Lines end with any of cr, lf,
697
     * or cr lf. A line ending at the end of the string will not output a further, empty string.
698
     * <p>
699
     * This code assumes <var>str</var> is not <code>null</code>.
700
     *
701
     * @param str the string to split
702
     * @return a non-empty list of strings
703
     */
704
    public static List<String> splitIntoLines(String str) {
705
        List<String> strings = new ArrayList<String>();
706
 
707
        int len = str.length();
708
        if (len == 0) {
709
            strings.add("");
710
            return strings;
711
        }
712
 
713
        int lineStart = 0;
714
 
715
        for (int i = 0; i < len; ++i) {
716
            char c = str.charAt(i);
717
            if (c == '\r') {
718
                int newlineLength = 1;
719
                if ((i + 1) < len && str.charAt(i + 1) == '\n')
720
                    newlineLength = 2;
721
                strings.add(str.substring(lineStart, i));
722
                lineStart = i + newlineLength;
723
                if (newlineLength == 2) // skip \n next time through loop
724
                    ++i;
725
            } else if (c == '\n') {
726
                strings.add(str.substring(lineStart, i));
727
                lineStart = i + 1;
728
            }
729
        }
730
        if (lineStart < len)
731
            strings.add(str.substring(lineStart));
732
 
733
        return strings;
734
    }
735
 
93 ilm 736
    /**
737
     * convert a byte array to its hexa representation
132 ilm 738
     */
93 ilm 739
    public static String bytesToHexString(byte[] bytes) {
740
        final int length = bytes.length;
741
        char[] hexChars = new char[length * 2];
742
        for (int j = 0; j < length; j++) {
743
            int v = bytes[j] & 0xFF;
744
            hexChars[j * 2] = hexArray[v >>> 4];
745
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
746
        }
747
        return new String(hexChars);
748
    }
142 ilm 749
 
750
    /**
751
     * Whether the parameter is empty.
752
     *
753
     * @param s the string to test.
754
     * @return <code>true</code> if <code>null</code> or {@link String#isEmpty() empty}.
755
     */
756
    public static boolean isEmpty(final String s) {
757
        return isEmpty(s, false);
758
    }
759
 
760
    public static boolean isEmpty(final String s, final boolean trim) {
761
        return s == null || (trim ? s.trim() : s).isEmpty();
762
    }
763
 
764
    /**
765
     * Return the first parameter that is non-empty.
766
     *
767
     * @param s1 the string to test.
768
     * @param s2 the alternate value.
769
     * @return <code>s1</code> if not <code>null</code> and not {@link String#isEmpty() empty},
770
     *         <code>s2</code> otherwise.
771
     */
772
    public static String coalesce(String s1, String s2) {
773
        return isEmpty(s1) ? s2 : s1;
774
    }
775
 
776
    /**
777
     * Return the first value that is non-empty.
778
     *
779
     * @param values values to test for emptiness.
780
     * @return the first value that is neither <code>null</code> nor {@link String#isEmpty() empty}.
781
     */
782
    public static String coalesce(String... values) {
783
        return coalesce(false, values);
784
    }
785
 
786
    public static String coalesce(final boolean trim, String... values) {
787
        for (final String s : values)
788
            if (!isEmpty(s, trim))
789
                return s;
790
        return null;
791
    }
17 ilm 792
}