OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
181 ilm 1
/*
2
 * Copyright 2018 Daniel Gredler
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
 * in compliance with the License. You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software distributed under the License
10
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
 * or implied. See the License for the specific language governing permissions and limitations under
12
 * the License.
13
 */
14
 
15
package uk.org.okapibarcode.util;
16
 
17
import static java.nio.charset.StandardCharsets.ISO_8859_1;
18
 
19
import java.nio.charset.StandardCharsets;
20
 
21
import uk.org.okapibarcode.backend.OkapiException;
22
 
23
/**
24
 * String utility class.
25
 *
26
 * @author Daniel Gredler
27
 */
28
public final class Strings {
29
 
30
    private Strings() {
31
        // utility class
32
    }
33
 
34
    /**
35
     * Replaces raw values with special placeholders, where applicable.
36
     *
37
     * @param s the string to add placeholders to
38
     * @return the specified string, with placeholders added
39
     * @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a>
40
     * @see #unescape(String, boolean)
41
     */
42
    public static String escape(final String s) {
43
        final StringBuilder sb = new StringBuilder(s.length() + 10);
44
        for (int i = 0; i < s.length(); i++) {
45
            final char c = s.charAt(i);
46
            switch (c) {
47
            case '\u0000':
48
                sb.append("\\0"); // null
49
                break;
50
            case '\u0004':
51
                sb.append("\\E"); // end of transmission
52
                break;
53
            case '\u0007':
54
                sb.append("\\a"); // bell
55
                break;
56
            case '\u0008':
57
                sb.append("\\b"); // backspace
58
                break;
59
            case '\u0009':
60
                sb.append("\\t"); // horizontal tab
61
                break;
62
            case '\n':
63
                sb.append("\\n"); // line feed
64
                break;
65
            case '\u000b':
66
                sb.append("\\v"); // vertical tab
67
                break;
68
            case '\u000c':
69
                sb.append("\\f"); // form feed
70
                break;
71
            case '\r':
72
                sb.append("\\r"); // carriage return
73
                break;
74
            case '\u001b':
75
                sb.append("\\e"); // escape
76
                break;
77
            case '\u001d':
78
                sb.append("\\G"); // group separator
79
                break;
80
            case '\u001e':
81
                sb.append("\\R"); // record separator
82
                break;
83
            case '\\':
84
                sb.append("\\\\"); // escape the escape character
85
                break;
86
            default:
87
                if (c >= 32 && c <= 126) {
88
                    sb.append(c); // printable ASCII
89
                } else {
90
                    final byte[] bytes = String.valueOf(c).getBytes(ISO_8859_1);
91
                    final String hex = String.format("%02X", bytes[0] & 0xFF);
92
                    sb.append("\\x").append(hex);
93
                }
94
                break;
95
            }
96
        }
97
        return sb.toString();
98
    }
99
 
100
    /**
101
     * Replaces any special placeholders with their raw values (not including FNC values).
102
     *
103
     * @param s the string to check for placeholders
104
     * @param lenient whether or not to be lenient with unrecognized escape sequences
105
     * @return the specified string, with placeholders replaced
106
     * @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a>
107
     * @see #escape(String)
108
     */
109
    public static String unescape(final String s, final boolean lenient) {
110
        final StringBuilder sb = new StringBuilder(s.length());
111
        for (int i = 0; i < s.length(); i++) {
112
            final char c = s.charAt(i);
113
            if (c != '\\') {
114
                sb.append(c);
115
            } else {
116
                if (i + 1 >= s.length()) {
117
                    final String msg = "Error processing escape sequences: expected escape character, found end of string";
118
                    throw new OkapiException(msg);
119
                } else {
120
                    final char c2 = s.charAt(i + 1);
121
                    switch (c2) {
122
                    case '0':
123
                        sb.append('\u0000'); // null
124
                        i++;
125
                        break;
126
                    case 'E':
127
                        sb.append('\u0004'); // end of transmission
128
                        i++;
129
                        break;
130
                    case 'a':
131
                        sb.append('\u0007'); // bell
132
                        i++;
133
                        break;
134
                    case 'b':
135
                        sb.append('\u0008'); // backspace
136
                        i++;
137
                        break;
138
                    case 't':
139
                        sb.append('\u0009'); // horizontal tab
140
                        i++;
141
                        break;
142
                    case 'n':
143
                        sb.append('\n'); // line feed
144
                        i++;
145
                        break;
146
                    case 'v':
147
                        sb.append('\u000b'); // vertical tab
148
                        i++;
149
                        break;
150
                    case 'f':
151
                        sb.append('\u000c'); // form feed
152
                        i++;
153
                        break;
154
                    case 'r':
155
                        sb.append('\r'); // carriage return
156
                        i++;
157
                        break;
158
                    case 'e':
159
                        sb.append('\u001b'); // escape
160
                        i++;
161
                        break;
162
                    case 'G':
163
                        sb.append('\u001d'); // group separator
164
                        i++;
165
                        break;
166
                    case 'R':
167
                        sb.append('\u001e'); // record separator
168
                        i++;
169
                        break;
170
                    case '\\':
171
                        sb.append('\\'); // escape the escape character
172
                        i++;
173
                        break;
174
                    case 'x':
175
                        if (i + 3 >= s.length()) {
176
                            final String msg = "Error processing escape sequences: expected hex sequence, found end of string";
177
                            throw new OkapiException(msg);
178
                        } else {
179
                            final char c3 = s.charAt(i + 2);
180
                            final char c4 = s.charAt(i + 3);
181
                            if (isHex(c3) && isHex(c4)) {
182
                                final byte b = (byte) Integer.parseInt("" + c3 + c4, 16);
183
                                sb.append(new String(new byte[] { b }, StandardCharsets.ISO_8859_1));
184
                                i += 3;
185
                            } else {
186
                                final String msg = "Error processing escape sequences: expected hex sequence, found '" + c3 + c4 + "'";
187
                                throw new OkapiException(msg);
188
                            }
189
                        }
190
                        break;
191
                    default:
192
                        if (lenient) {
193
                            sb.append(c);
194
                        } else {
195
                            throw new OkapiException("Error processing escape sequences: expected valid escape character, found '" + c2 + "'");
196
                        }
197
                    }
198
                }
199
            }
200
        }
201
        return sb.toString();
202
    }
203
 
204
    private static boolean isHex(final char c) {
205
        return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
206
    }
207
 
208
    /**
209
     * Appends the specific integer to the specified string, in binary format, padded to the
210
     * specified number of digits.
211
     *
212
     * @param s the string to append to
213
     * @param value the value to append, in binary format
214
     * @param digits the number of digits to pad to
215
     */
216
    public static void binaryAppend(final StringBuilder s, final int value, final int digits) {
217
        final int start = 0x01 << digits - 1;
218
        for (int i = 0; i < digits; i++) {
219
            if ((value & start >> i) == 0) {
220
                s.append('0');
221
            } else {
222
                s.append('1');
223
            }
224
        }
225
    }
226
}