OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 25 | Rev 61 | 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
 
19 ilm 19
import java.nio.charset.Charset;
17 ilm 20
import java.util.ArrayList;
21
import java.util.Collections;
22
import java.util.HashMap;
23
import java.util.HashSet;
24
import java.util.LinkedHashMap;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.Set;
28 ilm 28
import java.util.regex.Pattern;
17 ilm 29
 
30
/**
31
 * @author Sylvain CUAZ
32
 */
33
public class StringUtils {
34
 
28 ilm 35
    // required encoding see
36
    // http://docs.oracle.com/javase/7/docs/technotes/guides/intl/encoding.doc.html
37
    public static final Charset UTF8 = Charset.forName("UTF8");
38
    public static final Charset UTF16 = Charset.forName("UTF-16");
39
    public static final Charset ASCII = Charset.forName("ASCII");
40
    public static final Charset ISO8859_1 = Charset.forName("ISO8859_1");
41
    public static final Charset Cp1252 = Charset.forName("Cp1252");
42
    public static final Charset Cp850 = Charset.forName("Cp850");
19 ilm 43
 
17 ilm 44
    /**
45
     * Retourne la chaine avec la première lettre en majuscule et le reste en minuscule.
46
     *
47
     * @param s la chaîne à transformer.
48
     * @return la chaine avec la première lettre en majuscule et le reste en minuscule.
49
     */
50
    public static String firstUpThenLow(String s) {
51
        if (s.length() == 0) {
52
            return s;
53
        }
54
        if (s.length() == 1) {
55
            return s.toUpperCase();
56
        }
57
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
58
    }
59
 
60
    public static String firstUp(String s) {
61
        if (s.length() == 0) {
62
            return s;
63
        }
64
        if (s.length() == 1) {
65
            return s.toUpperCase();
66
        }
67
        return s.substring(0, 1).toUpperCase() + s.substring(1);
68
    }
69
 
19 ilm 70
    static public abstract class Shortener {
71
 
72
        private final int hashSize;
73
        private final int hashPartSize;
74
        private final String prefix;
75
        private final String suffix;
76
        private final int minStringLength;
77
 
78
        protected Shortener(int hashSize, String prefix, String suffix, int minCharsBeforeAndAfter) {
79
            super();
80
            this.hashSize = hashSize;
81
            this.prefix = prefix;
82
            this.suffix = suffix;
83
            this.hashPartSize = this.hashSize + this.prefix.length() + this.suffix.length();
84
            if (minCharsBeforeAndAfter < 1)
85
                throw new IllegalArgumentException("minCharsBeforeAndAfter must be at least 1: " + minCharsBeforeAndAfter);
86
            this.minStringLength = this.hashPartSize + minCharsBeforeAndAfter * 2;
87
        }
88
 
89
        public final int getMinStringLength() {
90
            return this.minStringLength;
91
        }
92
 
93
        public final String getBoundedLengthString(final String s, final int maxLength) {
94
            // don't test first for s.length, it's more predictable
95
            // (otherwise boundedString("a", 2) would succeed)
96
            if (maxLength < this.getMinStringLength())
97
                throw new IllegalArgumentException("Maximum too low : " + maxLength + "<" + getMinStringLength());
98
            if (s.length() <= maxLength)
99
                return s;
100
            else
101
                return this.shorten(s, maxLength);
102
        }
103
 
104
        final String shorten(final String s, final int maxLength) {
105
            assert s.length() >= this.getMinStringLength();
106
            final int toRemoveLength = s.length() - maxLength + this.hashPartSize;
107
            // remove the middle part of encoded
108
            final int toRemoveStartIndex = s.length() / 2 - toRemoveLength / 2;
109
            final String toHash = s.substring(toRemoveStartIndex, toRemoveStartIndex + toRemoveLength);
110
 
111
            final String hash = shorten(toHash);
112
            assert this.hashSize == hash.length();
113
 
114
            final String res = s.substring(0, toRemoveStartIndex) + this.prefix + hash + this.suffix + s.substring(toRemoveStartIndex + toRemoveLength);
115
            assert res.length() == maxLength;
116
            return res;
117
        }
118
 
119
        protected abstract String shorten(String s);
120
 
121
        static public final Shortener Ellipsis = new Shortener(1, "", "", 1) {
122
            @Override
123
            protected String shorten(String s) {
124
                return "…";
125
            }
126
        };
127
 
128
        // String.hashCode() is an int written in hex
129
        static public final Shortener JavaHashCode = new Shortener(Integer.SIZE / 8 * 2, "#", "#", 3) {
130
            @Override
131
            protected String shorten(String s) {
132
                return MessageDigestUtils.asHex(MessageDigestUtils.int2bytes(s.hashCode()));
133
            }
134
        };
135
 
136
        // 128 bits written in hex
137
        static public final Shortener MD5 = new Shortener(128 / 8 * 2, "#", "#", 11) {
138
            @Override
139
            protected String shorten(String s) {
140
                return MessageDigestUtils.getHashString(MessageDigestUtils.getMD5(), s.getBytes(UTF8));
141
            }
142
        };
143
 
144
        // order descendant by getMinStringLength()
145
        static final Shortener[] ORDERED = new Shortener[] { MD5, JavaHashCode, Ellipsis };
146
    }
147
 
148
    /**
149
     * The minimum value for {@link #getBoundedLengthString(String, int)}.
150
     *
151
     * @return the minimum value for <code>maxLength</code>.
152
     */
153
    public static final int getLeastMaximum() {
154
        return Shortener.ORDERED[Shortener.ORDERED.length - 1].getMinStringLength();
155
    }
156
 
157
    private static final Shortener getShortener(final int l) {
158
        for (final Shortener sh : Shortener.ORDERED) {
159
            if (l >= sh.getMinStringLength())
160
                return sh;
161
        }
162
        return null;
163
    }
164
 
165
    /**
166
     * Return a string built from <code>s</code> that is at most <code>maxLength</code> long.
167
     *
168
     * @param s the string to bound.
169
     * @param maxLength the maximum length the result must have.
170
     * @return a string built from <code>s</code>.
171
     * @throws IllegalArgumentException if <code>maxLength</code> is too small.
172
     * @see #getLeastMaximum()
173
     * @see Shortener#getBoundedLengthString(String, int)
174
     */
175
    public static final String getBoundedLengthString(final String s, final int maxLength) throws IllegalArgumentException {
176
        // don't test first for s.length, it's more predictable
177
        // (otherwise boundedString("a", 2) would succeed)
178
        if (maxLength < getLeastMaximum())
179
            throw new IllegalArgumentException("Maximum too low : " + maxLength + "<" + getLeastMaximum());
180
 
181
        final String res;
182
        if (s.length() <= maxLength) {
183
            res = s;
184
        } else {
185
            // use maxLength to choose the shortener since it's generally a constant
186
            // and thus the strings returned by this method have the same pattern
187
            res = getShortener(maxLength).shorten(s, maxLength);
188
        }
189
        return res;
190
    }
191
 
17 ilm 192
    public static final List<String> fastSplit(final String string, final char sep) {
193
        final List<String> l = new ArrayList<String>();
194
        final int length = string.length();
195
        final char[] cars = string.toCharArray();
196
        int rfirst = 0;
197
 
198
        for (int i = 0; i < length; i++) {
199
            if (cars[i] == sep) {
200
                l.add(new String(cars, rfirst, i - rfirst));
201
                rfirst = i + 1;
202
            }
203
        }
204
 
205
        if (rfirst < length) {
206
            l.add(new String(cars, rfirst, length - rfirst));
207
        }
208
        return l;
209
    }
210
 
19 ilm 211
    /**
212
     * Split une string s tous les nbCharMaxLine
213
     *
214
     * @param s
215
     * @param nbCharMaxLine
216
     * @return
217
     */
218
    public static String splitString(String s, int nbCharMaxLine) {
219
 
220
        if (s == null) {
221
            return s;
222
        }
223
 
224
        if (s.trim().length() < nbCharMaxLine) {
225
            return s;
226
        }
227
        StringBuffer lastString = new StringBuffer();
228
        StringBuffer result = new StringBuffer();
229
        for (int i = 0; i < s.length(); i++) {
230
 
231
            if (lastString.length() == nbCharMaxLine) {
232
                int esp = lastString.lastIndexOf(" ");
25 ilm 233
                if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
19 ilm 234
                    result.append("\n");
235
                }
236
                if (esp > 0) {
237
                    result.append(lastString.substring(0, esp).toString().trim());
238
                    lastString = new StringBuffer(lastString.substring(esp, lastString.length()));
239
                } else {
240
                    result.append(lastString.toString().trim());
241
                    lastString = new StringBuffer();
242
                }
25 ilm 243
                result.append("\n");
19 ilm 244
            }
245
 
246
            char charAt = s.charAt(i);
247
            if (charAt == '\n') {
248
                lastString.append(charAt);
21 ilm 249
                result.append(lastString);
19 ilm 250
                lastString = new StringBuffer();
251
            } else {
252
                lastString.append(charAt);
253
            }
254
        }
255
 
25 ilm 256
        if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
19 ilm 257
            result.append("\n");
258
        }
259
 
260
        result.append(lastString.toString().trim());
261
 
262
        return result.toString();
263
    }
264
 
28 ilm 265
    static private final Pattern quotePatrn = Pattern.compile("([\\\\]*)\"");
266
    static private final Pattern endSlashPatrn = Pattern.compile("([\\\\]*)\\z");
267
 
268
    static public String doubleQuote(String s) {
269
        if (s.length() > 0) {
270
            // replace '(\*)"' by '$1$1\"', e.g. '\quote " \"' by '\quote \" \\\"'
271
            // $1 needed so that the backslash we add isn't escaped itself by a preceding backslash
272
            s = quotePatrn.matcher(s).replaceAll("$1$1\\\\\"");
273
            // replace '(\*)\z' by '$1$1', e.g. 'foo\' by 'foo\\'
274
            // needed to not escape closing quote
275
            s = endSlashPatrn.matcher(s).replaceAll("$1$1");
276
        }
277
        return '"' + s + '"';
278
    }
279
 
17 ilm 280
    public static final class Escaper {
281
 
282
        // eg '
283
        private final char esc;
284
 
285
        // eg { '=> S, " => D}
286
        private final Map<Character, Character> substitution;
287
        private final Map<Character, Character> inv;
288
 
289
        /**
290
         * A new escaper that will have <code>esc</code> as escape character.
291
         *
292
         * @param esc the escape character, eg '
293
         * @param name the character that will be appended to <code>esc</code>, eg with S all
294
         *        occurrences of ' will be replaced by 'S
295
         */
296
        public Escaper(char esc, char name) {
297
            super();
298
            this.esc = esc;
299
            this.substitution = new LinkedHashMap<Character, Character>();
300
            this.inv = new HashMap<Character, Character>();
301
            this.add(esc, name);
302
        }
303
 
304
        public Escaper add(char toRemove, char escapedName) {
305
            if (this.inv.containsKey(escapedName))
306
                throw new IllegalArgumentException(escapedName + " already replaces " + this.inv.get(escapedName));
307
            this.substitution.put(toRemove, escapedName);
308
            this.inv.put(escapedName, toRemove);
309
            return this;
310
        }
311
 
312
        public final Set<Character> getEscapedChars() {
313
            final Set<Character> res = new HashSet<Character>(this.substitution.keySet());
314
            res.remove(this.esc);
315
            return res;
316
        }
317
 
318
        /**
319
         * Escape <code>s</code>, so that the resulting string has none of
320
         * {@link #getEscapedChars()}.
321
         *
322
         * @param s a string to escape.
323
         * @return the escaped form.
324
         */
325
        public final String escape(String s) {
326
            String res = s;
327
            // this.esc en premier
328
            for (final Character toEsc : this.substitution.keySet()) {
329
                // use Pattern.LITERAL to avoid interpretion
330
                res = res.replace(toEsc + "", getEscaped(toEsc));
331
            }
332
            return res;
333
        }
334
 
335
        private String getEscaped(final Character toEsc) {
336
            return this.esc + "" + this.substitution.get(toEsc);
337
        }
338
 
339
        public final String unescape(String escaped) {
340
            String res = escaped;
341
            final List<Character> toEscs = new ArrayList<Character>(this.substitution.keySet());
342
            Collections.reverse(toEscs);
343
            for (final Character toEsc : toEscs) {
344
                res = res.replaceAll(getEscaped(toEsc), toEsc + "");
345
            }
346
            return res;
347
        }
348
 
349
        @Override
350
        public boolean equals(Object obj) {
351
            if (obj instanceof Escaper) {
352
                final Escaper o = (Escaper) obj;
353
                return this.esc == o.esc && this.substitution.equals(o.substitution);
354
            } else
355
                return false;
356
        }
357
 
358
        @Override
359
        public int hashCode() {
360
            return this.esc + this.substitution.hashCode();
361
        }
362
    }
363
 
25 ilm 364
    public static String rightAlign(String s, int width) {
365
        String r = s;
366
        int n = width - s.length();
367
        for (int i = 0; i < n; i++) {
368
            r = ' ' + r;
369
        }
370
        return r;
371
    }
372
 
373
    public static String leftAlign(String s, int width) {
374
        String r = s;
375
        int n = width - s.length();
376
        for (int i = 0; i < n; i++) {
377
            r += ' ';
378
        }
379
        return r;
380
    }
17 ilm 381
}