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 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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