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 2014 Robin Stuart
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
package uk.org.okapibarcode.backend;
15
 
16
import static uk.org.okapibarcode.backend.DataBarLimited.getWidths;
17
import static uk.org.okapibarcode.util.Strings.binaryAppend;
18
 
19
import java.nio.charset.StandardCharsets;
20
import java.util.Arrays;
21
 
22
/**
23
 * <p>
24
 * Implements GS1 DataBar Expanded Omnidirectional and GS1 DataBar Expanded Stacked Omnidirectional
25
 * according to ISO/IEC 24724:2011.
26
 *
27
 * <p>
28
 * DataBar expanded encodes GS1 data in either a linear or stacked format.
29
 *
30
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
31
 */
32
public class DataBarExpanded extends Symbol {
33
 
34
    private static final int[] G_SUM_EXP = { 0, 348, 1388, 2948, 3988 };
35
 
36
    private static final int[] T_EVEN_EXP = { 4, 20, 52, 104, 204 };
37
 
38
    private static final int[] MODULES_ODD_EXP = { 12, 10, 8, 6, 4 };
39
 
40
    private static final int[] MODULES_EVEN_EXP = { 5, 7, 9, 11, 13 };
41
 
42
    private static final int[] WIDEST_ODD_EXP = { 7, 5, 4, 3, 1 };
43
 
44
    private static final int[] WIDEST_EVEN_EXP = { 2, 4, 5, 6, 8 };
45
 
46
    /** Table 14 */
47
    private static final int[] CHECKSUM_WEIGHT_EXP = { 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189, 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91, 62, 186,
48
            136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36, 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52, 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51,
49
            153, 37, 111, 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30, 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202, 184, 130, 179, 115, 134, 191,
50
            151, 31, 93, 68, 204, 190, 148, 22, 66, 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25, 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83, 38, 114,
51
            131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165, 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89 };
52
 
53
    /** Table 15 */
54
    private static final int[] FINDER_PATTERN_EXP = { 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1, 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5, 6,
55
            2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2 };
56
 
57
    /** Table 16 */
58
    private static final int[] FINDER_SEQUENCE = { 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 7,
59
            12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9,
60
            12, 11 };
61
 
62
    private static final int[] WEIGHT_ROWS = { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4, 13, 14, 0,
63
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18,
64
            3, 4, 13, 14, 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 15, 16,
65
            0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20 };
66
 
67
    private enum EncodeMode {
68
        NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
69
    }
70
 
71
    private boolean linkageFlag;
72
    private int preferredColumns = 2;
73
    private boolean stacked = true;
74
 
75
    public DataBarExpanded() {
76
        this.inputDataType = DataType.GS1;
77
    }
78
 
79
    @Override
80
    public void setDataType(final DataType dataType) {
81
        if (dataType != Symbol.DataType.GS1) {
82
            throw new IllegalArgumentException("Only GS1 data type is supported for DataBar Expanded symbology.");
83
        }
84
    }
85
 
86
    @Override
87
    protected boolean gs1Supported() {
88
        return true;
89
    }
90
 
91
    /**
92
     * Sets the preferred width of a stacked symbol by selecting the number of "columns" or symbol
93
     * segments in each row of data.
94
     *
95
     * @param columns the number of segments in each row
96
     */
97
    public void setPreferredColumns(final int columns) {
98
        if (columns < 1 || columns > 10) {
99
            throw new IllegalArgumentException("Invalid column count: " + columns);
100
        }
101
        this.preferredColumns = columns;
102
    }
103
 
104
    /**
105
     * Returns the preferred width of a stacked symbol by selecting the number of "columns" or
106
     * symbol segments in each row of data.
107
     *
108
     * @return the number of segments in each row
109
     */
110
    public int getPreferredColumns() {
111
        return this.preferredColumns;
112
    }
113
 
114
    /**
115
     * Sets whether or not this symbology is stacked.
116
     *
117
     * @param stacked <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt>
118
     *        for GS1 DataBar Expanded Omnidirectional
119
     */
120
    public void setStacked(final boolean stacked) {
121
        this.stacked = stacked;
122
    }
123
 
124
    /**
125
     * Returns whether or not this symbology is stacked.
126
     *
127
     * @return <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt> for
128
     *         GS1 DataBar Expanded Omnidirectional
129
     */
130
    public boolean isStacked() {
131
        return this.stacked;
132
    }
133
 
134
    protected void setLinkageFlag(final boolean linkageFlag) {
135
        this.linkageFlag = linkageFlag;
136
    }
137
 
138
    @Override
139
    protected void encode() {
140
        int i;
141
        int j;
142
        int k;
143
        int data_chars;
144
        final int[] vs = new int[21];
145
        final int[] group = new int[21];
146
        final int[] v_odd = new int[21];
147
        final int[] v_even = new int[21];
148
        final int[][] char_widths = new int[21][8];
149
        int checksum;
150
        int row;
151
        int check_char;
152
        int c_group;
153
        int c_odd;
154
        int c_even;
155
        final int[] check_widths = new int[8];
156
        int pattern_width;
157
        final int[] elements = new int[235];
158
        int codeblocks;
159
        int stack_rows;
160
        int blocksPerRow;
161
        int current_block;
162
        int current_row;
163
        boolean special_case_row;
164
        int elements_in_sub;
165
        int reader;
166
        int writer;
167
        final int[] sub_elements = new int[235];
168
        int l;
169
        int symbol_row;
170
        String separator_pattern;
171
        boolean black;
172
        boolean left_to_right;
173
        int compositeOffset;
174
 
175
        this.inputData = toBytes(this.content, StandardCharsets.US_ASCII);
176
 
177
        final StringBuilder binaryString = new StringBuilder(this.inputData.length * 8);
178
 
179
        if (this.linkageFlag) {
180
            binaryString.append('1');
181
            compositeOffset = 1;
182
        } else {
183
            binaryString.append('0');
184
            compositeOffset = 0;
185
        }
186
 
187
        final int encodingMethod = calculateBinaryString(this.inputData, binaryString); // updates
188
        // binaryString
189
        infoLine("Encoding Method: " + encodingMethod);
190
        logBinaryStringInfo(binaryString);
191
 
192
        data_chars = binaryString.length() / 12;
193
 
194
        info("Data Characters: ");
195
        for (i = 0; i < data_chars; i++) {
196
            vs[i] = 0;
197
            for (j = 0; j < 12; j++) {
198
                if (binaryString.charAt(i * 12 + j) == '1') {
199
                    vs[i] += 2048 >> j;
200
                }
201
            }
202
            infoSpace(vs[i]);
203
        }
204
        infoLine();
205
 
206
        for (i = 0; i < data_chars; i++) {
207
            if (vs[i] <= 347) {
208
                group[i] = 1;
209
            }
210
            if (vs[i] >= 348 && vs[i] <= 1387) {
211
                group[i] = 2;
212
            }
213
            if (vs[i] >= 1388 && vs[i] <= 2947) {
214
                group[i] = 3;
215
            }
216
            if (vs[i] >= 2948 && vs[i] <= 3987) {
217
                group[i] = 4;
218
            }
219
            if (vs[i] >= 3988) {
220
                group[i] = 5;
221
            }
222
            v_odd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1];
223
            v_even[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1];
224
 
225
            int[] widths = getWidths(v_odd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0);
226
            char_widths[i][0] = widths[0];
227
            char_widths[i][2] = widths[1];
228
            char_widths[i][4] = widths[2];
229
            char_widths[i][6] = widths[3];
230
 
231
            widths = getWidths(v_even[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1);
232
            char_widths[i][1] = widths[0];
233
            char_widths[i][3] = widths[1];
234
            char_widths[i][5] = widths[2];
235
            char_widths[i][7] = widths[3];
236
        }
237
 
238
        /* 7.2.6 Check character */
239
        /*
240
         * The checksum value is equal to the mod 211 residue of the weighted sum of the widths of
241
         * the elements in the data characters.
242
         */
243
        checksum = 0;
244
        for (i = 0; i < data_chars; i++) {
245
            row = WEIGHT_ROWS[(data_chars - 2) / 2 * 21 + i];
246
            for (j = 0; j < 8; j++) {
247
                checksum += char_widths[i][j] * CHECKSUM_WEIGHT_EXP[row * 8 + j];
248
 
249
            }
250
        }
251
 
252
        check_char = 211 * (data_chars + 1 - 4) + checksum % 211;
253
 
254
        infoLine("Check Character: " + check_char);
255
 
256
        c_group = 1;
257
        if (check_char >= 348 && check_char <= 1387) {
258
            c_group = 2;
259
        }
260
        if (check_char >= 1388 && check_char <= 2947) {
261
            c_group = 3;
262
        }
263
        if (check_char >= 2948 && check_char <= 3987) {
264
            c_group = 4;
265
        }
266
        if (check_char >= 3988) {
267
            c_group = 5;
268
        }
269
 
270
        c_odd = (check_char - G_SUM_EXP[c_group - 1]) / T_EVEN_EXP[c_group - 1];
271
        c_even = (check_char - G_SUM_EXP[c_group - 1]) % T_EVEN_EXP[c_group - 1];
272
 
273
        int[] widths = getWidths(c_odd, MODULES_ODD_EXP[c_group - 1], 4, WIDEST_ODD_EXP[c_group - 1], 0);
274
        check_widths[0] = widths[0];
275
        check_widths[2] = widths[1];
276
        check_widths[4] = widths[2];
277
        check_widths[6] = widths[3];
278
 
279
        widths = getWidths(c_even, MODULES_EVEN_EXP[c_group - 1], 4, WIDEST_EVEN_EXP[c_group - 1], 1);
280
        check_widths[1] = widths[0];
281
        check_widths[3] = widths[1];
282
        check_widths[5] = widths[2];
283
        check_widths[7] = widths[3];
284
 
285
        /* Initialise element array */
286
        pattern_width = ((data_chars + 1) / 2 + (data_chars + 1 & 1)) * 5 + (data_chars + 1) * 8 + 4;
287
        for (i = 0; i < pattern_width; i++) {
288
            elements[i] = 0;
289
        }
290
 
291
        elements[0] = 1;
292
        elements[1] = 1;
293
        elements[pattern_width - 2] = 1;
294
        elements[pattern_width - 1] = 1;
295
 
296
        /* Put finder patterns in element array */
297
        for (i = 0; i < (data_chars + 1) / 2 + (data_chars + 1 & 1); i++) {
298
            k = ((data_chars + 1 - 2) / 2 + (data_chars + 1 & 1) - 1) * 11 + i;
299
            for (j = 0; j < 5; j++) {
300
                elements[21 * i + j + 10] = FINDER_PATTERN_EXP[(FINDER_SEQUENCE[k] - 1) * 5 + j];
301
            }
302
        }
303
 
304
        /* Put check character in element array */
305
        for (i = 0; i < 8; i++) {
306
            elements[i + 2] = check_widths[i];
307
        }
308
 
309
        /* Put forward reading data characters in element array */
310
        for (i = 1; i < data_chars; i += 2) {
311
            for (j = 0; j < 8; j++) {
312
                elements[(i - 1) / 2 * 21 + 23 + j] = char_widths[i][j];
313
            }
314
        }
315
 
316
        /* Put reversed data characters in element array */
317
        for (i = 0; i < data_chars; i += 2) {
318
            for (j = 0; j < 8; j++) {
319
                elements[i / 2 * 21 + 15 + j] = char_widths[i][7 - j];
320
            }
321
        }
322
 
323
        if (!this.stacked) {
324
            /* Copy elements into symbol */
325
            this.row_count = 1 + compositeOffset;
326
            this.row_height = new int[1 + compositeOffset];
327
            this.row_height[0 + compositeOffset] = -1;
328
            this.pattern = new String[1 + compositeOffset];
329
 
330
            writer = 0;
331
            black = false;
332
            final StringBuilder pat = new StringBuilder("0");
333
            final StringBuilder separator_binary = new StringBuilder();
334
            for (i = 0; i < pattern_width; i++) {
335
                pat.append((char) (elements[i] + '0'));
336
                for (j = 0; j < elements[i]; j++) {
337
                    if (black) {
338
                        separator_binary.append('0');
339
                    } else {
340
                        separator_binary.append('1');
341
                    }
342
                }
343
                black = !black;
344
                writer += elements[i];
345
            }
346
            this.pattern[0 + compositeOffset] = pat.toString();
347
 
348
            separator_binary.setCharAt(0, '0');
349
            separator_binary.setCharAt(1, '0');
350
            separator_binary.setCharAt(2, '0');
351
            separator_binary.setCharAt(3, '0');
352
            separator_binary.delete(writer - 4, separator_binary.length());
353
            for (j = 0; j < writer / 49; j++) {
354
                k = 49 * j + 18;
355
                for (i = 0; i < 15; i++) {
356
                    if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
357
                        separator_binary.setCharAt(i + k, '0');
358
                    }
359
                }
360
            }
361
            if (this.linkageFlag) {
362
                // Add composite code separator
363
                this.pattern[0] = bin2pat(separator_binary);
364
                this.row_height[0] = 1;
365
            }
366
 
367
        } else {
368
            /* RSS Expanded Stacked */
369
            codeblocks = (data_chars + 1) / 2 + (data_chars + 1) % 2;
370
 
371
            blocksPerRow = this.preferredColumns;
372
 
373
            if (this.linkageFlag && blocksPerRow == 1) {
374
                /*
375
                 * "There shall be a minimum of four symbol characters in the first row of an RSS
376
                 * Expanded Stacked symbol when it is the linear component of an EAN.UCC Composite
377
                 * symbol."
378
                 */
379
                blocksPerRow = 2;
380
            }
381
 
382
            stack_rows = codeblocks / blocksPerRow;
383
            if (codeblocks % blocksPerRow > 0) {
384
                stack_rows++;
385
            }
386
 
387
            this.row_count = stack_rows * 4 - 3;
388
            this.row_height = new int[this.row_count + compositeOffset];
389
            this.pattern = new String[this.row_count + compositeOffset];
390
            symbol_row = 0;
391
 
392
            current_block = 0;
393
            for (current_row = 1; current_row <= stack_rows; current_row++) {
394
                for (i = 0; i < 235; i++) {
395
                    sub_elements[i] = 0;
396
                }
397
                special_case_row = false;
398
 
399
                /* Row Start */
400
                sub_elements[0] = 1;
401
                sub_elements[1] = 1;
402
                elements_in_sub = 2;
403
 
404
                /* Row Data */
405
                reader = 0;
406
                do {
407
                    if ((blocksPerRow & 1) != 0 || (current_row & 1) != 0
408
                            || current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
409
                        /* left to right */
410
                        left_to_right = true;
411
                        i = 2 + current_block * 21;
412
                        for (j = 0; j < 21; j++) {
413
                            if (i + j < pattern_width) {
414
                                sub_elements[j + reader * 21 + 2] = elements[i + j];
415
                                elements_in_sub++;
416
                            }
417
                        }
418
                    } else {
419
                        /* right to left */
420
                        left_to_right = false;
421
                        if (current_row * blocksPerRow < codeblocks) {
422
                            /* a full row */
423
                            i = 2 + (current_row * blocksPerRow - reader - 1) * 21;
424
                            for (j = 0; j < 21; j++) {
425
                                if (i + j < pattern_width) {
426
                                    sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
427
                                    elements_in_sub++;
428
                                }
429
                            }
430
                        } else {
431
                            /* a partial row */
432
                            k = current_row * blocksPerRow - codeblocks;
433
                            l = current_row * blocksPerRow - reader - 1;
434
                            i = 2 + (l - k) * 21;
435
                            for (j = 0; j < 21; j++) {
436
                                if (i + j < pattern_width) {
437
                                    sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
438
                                    elements_in_sub++;
439
                                }
440
                            }
441
                        }
442
                    }
443
                    reader++;
444
                    current_block++;
445
                } while (reader < blocksPerRow && current_block < codeblocks);
446
 
447
                /* Row Stop */
448
                sub_elements[elements_in_sub] = 1;
449
                sub_elements[elements_in_sub + 1] = 1;
450
                elements_in_sub += 2;
451
 
452
                black = true;
453
                StringBuilder pat = new StringBuilder();
454
                this.row_height[symbol_row + compositeOffset] = -1;
455
 
456
                if ((current_row & 1) != 0) {
457
                    pat.append('0');
458
                    black = false;
459
                } else {
460
                    if (current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
461
                        /* Special case bottom row */
462
                        special_case_row = true;
463
                        sub_elements[0] = 2;
464
                        pat.append('0');
465
                        black = false;
466
                    }
467
                }
468
 
469
                writer = 0;
470
 
471
                final StringBuilder separator_binary = new StringBuilder();
472
                for (i = 0; i < elements_in_sub; i++) {
473
                    pat.append((char) (sub_elements[i] + '0'));
474
                    for (j = 0; j < sub_elements[i]; j++) {
475
                        separator_binary.append(black ? '0' : '1');
476
                    }
477
                    black = !black;
478
                    writer += sub_elements[i];
479
                }
480
                this.pattern[symbol_row + compositeOffset] = pat.toString();
481
                separator_binary.setCharAt(0, '0');
482
                separator_binary.setCharAt(1, '0');
483
                separator_binary.setCharAt(2, '0');
484
                separator_binary.setCharAt(3, '0');
485
                separator_binary.delete(writer - 4, separator_binary.length());
486
                for (j = 0; j < reader; j++) {
487
                    k = 49 * j + (special_case_row ? 19 : 18);
488
                    if (left_to_right) {
489
                        for (i = 0; i < 15; i++) {
490
                            if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
491
                                separator_binary.setCharAt(i + k, '0');
492
                            }
493
                        }
494
                    } else {
495
                        for (i = 14; i >= 0; i--) {
496
                            if (separator_binary.charAt(i + k + 1) == '1' && separator_binary.charAt(i + k) == '1') {
497
                                separator_binary.setCharAt(i + k, '0');
498
                            }
499
                        }
500
                    }
501
                }
502
                separator_pattern = bin2pat(separator_binary);
503
 
504
                if (current_row == 1 && this.linkageFlag) {
505
                    // Add composite code separator
506
                    this.row_height[0] = 1;
507
                    this.pattern[0] = separator_pattern;
508
                }
509
 
510
                if (current_row != 1) {
511
                    /* middle separator pattern (above current row) */
512
                    pat = new StringBuilder("05");
513
                    for (j = 5; j < 49 * blocksPerRow; j += 2) {
514
                        pat.append("11");
515
                    }
516
                    this.pattern[symbol_row - 2 + compositeOffset] = pat.toString();
517
                    this.row_height[symbol_row - 2 + compositeOffset] = 1;
518
                    /* bottom separator pattern (above current row) */
519
                    this.row_height[symbol_row - 1 + compositeOffset] = 1;
520
                    this.pattern[symbol_row - 1 + compositeOffset] = separator_pattern;
521
                }
522
 
523
                if (current_row != stack_rows) {
524
                    this.row_height[symbol_row + 1 + compositeOffset] = 1;
525
                    this.pattern[symbol_row + 1 + compositeOffset] = separator_pattern;
526
                }
527
 
528
                symbol_row += 4;
529
            }
530
            this.readable = "";
531
            this.row_count += compositeOffset;
532
        }
533
    }
534
 
535
    /** Handles all data encodation from section 7.2.5 of ISO/IEC 24724. */
536
    private static int calculateBinaryString(final int[] inputData, final StringBuilder binaryString) {
537
 
538
        EncodeMode last_mode = EncodeMode.NUMERIC;
539
        int i;
540
        boolean latch;
541
        int remainder, d1, d2, value;
542
        String padstring;
543
        int current_length;
544
 
545
        /*
546
         * Decide whether a compressed data field is required and if so what method to use: method 2
547
         * = no compressed data field
548
         */
549
 
550
        int encodingMethod;
551
        if (inputData.length >= 16 && inputData[0] == '0' && inputData[1] == '1') {
552
            /* (01) and other AIs */
553
            encodingMethod = 1;
554
        } else {
555
            /* any AIs */
556
            encodingMethod = 2;
557
        }
558
 
559
        if (inputData.length >= 20 && encodingMethod == 1 && inputData[2] == '9' && inputData[16] == '3') {
560
 
561
            /* Possibly encoding method > 2 */
562
 
563
            if (inputData.length >= 26 && inputData[17] == '1') {
564
                /* Methods 3, 7, 9, 11 and 13 */
565
                if (inputData[18] == '0') {
566
                    /* (01) and (310x), weight in kilos */
567
                    double weight = 0;
568
                    for (i = 0; i < 6; i++) {
569
                        weight *= 10;
570
                        weight += inputData[20 + i] - '0';
571
                    }
572
                    if (weight < 99_999) { /* Maximum weight = 99999 */
573
                        if (inputData[19] == '3' && inputData.length == 26) {
574
                            /* (01) and (3103) */
575
                            weight /= 1000.0;
576
                            if (weight <= 32.767) {
577
                                encodingMethod = 3;
578
                            }
579
                        }
580
                        if (inputData.length == 34) {
581
                            if (inputData[26] == '1' && inputData[27] == '1') {
582
                                /* (01), (310x) and (11) - metric weight and production date */
583
                                encodingMethod = 7;
584
                            }
585
                            if (inputData[26] == '1' && inputData[27] == '3') {
586
                                /* (01), (310x) and (13) - metric weight and packaging date */
587
                                encodingMethod = 9;
588
                            }
589
                            if (inputData[26] == '1' && inputData[27] == '5') {
590
                                /* (01), (310x) and (15) - metric weight and "best before" date */
591
                                encodingMethod = 11;
592
                            }
593
                            if (inputData[26] == '1' && inputData[27] == '7') {
594
                                /* (01), (310x) and (17) - metric weight and expiration date */
595
                                encodingMethod = 13;
596
                            }
597
                        }
598
                    }
599
                }
600
            }
601
 
602
            if (inputData.length >= 26 && inputData[17] == '2') {
603
                /* Methods 4, 8, 10, 12 and 14 */
604
                if (inputData[18] == '0') {
605
                    /* (01) and (320x), weight in pounds */
606
                    double weight = 0;
607
                    for (i = 0; i < 6; i++) {
608
                        weight *= 10;
609
                        weight += inputData[20 + i] - '0';
610
                    }
611
                    if (weight < 99_999) { /* Maximum weight = 99999 */
612
                        if ((inputData[19] == '2' || inputData[19] == '3') && inputData.length == 26) {
613
                            /* (01) and (3202)/(3203) */
614
                            if (inputData[19] == '3') {
615
                                weight /= 1000.0;
616
                                if (weight <= 22.767) {
617
                                    encodingMethod = 4;
618
                                }
619
                            } else {
620
                                weight /= 100.0;
621
                                if (weight <= 99.99) {
622
                                    encodingMethod = 4;
623
                                }
624
                            }
625
                        }
626
                        if (inputData.length == 34) {
627
                            if (inputData[26] == '1' && inputData[27] == '1') {
628
                                /* (01), (320x) and (11) - English weight and production date */
629
                                encodingMethod = 8;
630
                            }
631
                            if (inputData[26] == '1' && inputData[27] == '3') {
632
                                /* (01), (320x) and (13) - English weight and packaging date */
633
                                encodingMethod = 10;
634
                            }
635
                            if (inputData[26] == '1' && inputData[27] == '5') {
636
                                /* (01), (320x) and (15) - English weight and "best before" date */
637
                                encodingMethod = 12;
638
                            }
639
                            if (inputData[26] == '1' && inputData[27] == '7') {
640
                                /* (01), (320x) and (17) - English weight and expiration date */
641
                                encodingMethod = 14;
642
                            }
643
                        }
644
                    }
645
                }
646
            }
647
 
648
            if (inputData[17] == '9') {
649
                /* Methods 5 and 6 */
650
                if (inputData[18] == '2' && inputData[19] >= '0' && inputData[19] <= '3') {
651
                    /* (01) and (392x) */
652
                    encodingMethod = 5;
653
                }
654
                if (inputData[18] == '3' && inputData[19] >= '0' && inputData[19] <= '3') {
655
                    /* (01) and (393x) */
656
                    encodingMethod = 6;
657
                }
658
            }
659
        }
660
 
661
        /* Encoding method - Table 10 */
662
        /* Variable length symbol bit field is just given a place holder (XX) for the time being */
663
        int read_posn;
664
        switch (encodingMethod) {
665
        case 1:
666
            binaryString.append("1XX");
667
            read_posn = 16;
668
            break;
669
        case 2:
670
            binaryString.append("00XX");
671
            read_posn = 0;
672
            break;
673
        case 3:
674
            binaryString.append("0100");
675
            read_posn = inputData.length;
676
            break;
677
        case 4:
678
            binaryString.append("0101");
679
            read_posn = inputData.length;
680
            break;
681
        case 5:
682
            binaryString.append("01100XX");
683
            read_posn = 20;
684
            break;
685
        case 6:
686
            binaryString.append("01101XX");
687
            read_posn = 23;
688
            break;
689
        default: /* modes 7 (0111000) to 14 (0111111) */
690
            binaryString.append("0" + Integer.toBinaryString(56 + encodingMethod - 7));
691
            read_posn = inputData.length;
692
            break;
693
        }
694
 
695
        /*
696
         * Verify that the data to be placed in the compressed data field is all numeric data before
697
         * carrying out compression
698
         */
699
        for (i = 0; i < read_posn; i++) {
700
            if (inputData[i] < '0' || inputData[i] > '9') {
701
                /* Something is wrong */
702
                throw new OkapiException("Invalid characters in input data");
703
            }
704
        }
705
 
706
        /* Now encode the compressed data field */
707
 
708
        if (encodingMethod == 1) {
709
            /* Encoding method field "1" - general item identification data */
710
            binaryAppend(binaryString, inputData[2] - '0', 4);
711
            for (i = 1; i < 5; i++) {
712
                final int group = parseInt(inputData, i * 3, 3);
713
                binaryAppend(binaryString, group, 10);
714
            }
715
        }
716
 
717
        if (encodingMethod == 3 || encodingMethod == 4) {
718
            /* Encoding method field "0100" - variable weight item (0,001 kilogram increments) */
719
            /*
720
             * Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment)
721
             */
722
            for (i = 1; i < 5; i++) {
723
                final int group = parseInt(inputData, i * 3, 3);
724
                binaryAppend(binaryString, group, 10);
725
            }
726
            int group = parseInt(inputData, 20, 6);
727
            if (encodingMethod == 4 && inputData[19] == '3') {
728
                group += 10_000;
729
            }
730
            binaryAppend(binaryString, group, 15);
731
        }
732
 
733
        if (encodingMethod == 5 || encodingMethod == 6) {
734
            /* Encoding method field "01100" - variable measure item and price */
735
            /*
736
             * Encoding method "01101" - variable measure item and price with ISO 4217 currency code
737
             */
738
            for (i = 1; i < 5; i++) {
739
                final int group = parseInt(inputData, i * 3, 3);
740
                binaryAppend(binaryString, group, 10);
741
            }
742
            binaryAppend(binaryString, inputData[19] - '0', 2);
743
            if (encodingMethod == 6) {
744
                final int currency = parseInt(inputData, 20, 3);
745
                binaryAppend(binaryString, currency, 10);
746
            }
747
        }
748
 
749
        if (encodingMethod >= 7 && encodingMethod <= 14) {
750
            /*
751
             * Encoding method fields "0111000" through "0111111" - variable weight item plus date
752
             */
753
            for (i = 1; i < 5; i++) {
754
                final int group = parseInt(inputData, i * 3, 3);
755
                binaryAppend(binaryString, group, 10);
756
            }
757
            int weight = inputData[19] - '0';
758
            for (i = 0; i < 5; i++) {
759
                weight *= 10;
760
                weight += inputData[21 + i] - '0';
761
            }
762
            binaryAppend(binaryString, weight, 20);
763
            int date;
764
            if (inputData.length == 34) {
765
                /* Date information is included */
766
                date = parseInt(inputData, 28, 2) * 384;
767
                date += (parseInt(inputData, 30, 2) - 1) * 32;
768
                date += parseInt(inputData, 32, 2);
769
            } else {
770
                date = 38_400;
771
            }
772
            binaryAppend(binaryString, date, 16);
773
        }
774
 
775
        /*
776
         * The compressed data field has been processed if appropriate - the rest of the data (if
777
         * any) goes into a general-purpose data compaction field
778
         */
779
 
780
        final int[] generalField = Arrays.copyOfRange(inputData, read_posn, inputData.length);
781
 
782
        if (generalField.length != 0) {
783
 
784
            latch = false;
785
            final EncodeMode[] generalFieldType = new EncodeMode[generalField.length];
786
 
787
            for (i = 0; i < generalField.length; i++) {
788
                /* Tables 11, 12, 13 - ISO/IEC 646 encodation */
789
                final int c = generalField[i];
790
                EncodeMode mode;
791
                if (c == FNC1) {
792
                    // FNC1 can be encoded in any system
793
                    mode = EncodeMode.ANY_ENC;
794
                } else if (c >= '0' && c <= '9') {
795
                    // numbers can be encoded in any system, but will usually narrow down to numeric
796
                    // encodation
797
                    mode = EncodeMode.ANY_ENC;
798
                } else if (c >= 'A' && c <= 'Z' || c == '*' || c == ',' || c == '-' || c == '.' || c == '/') {
799
                    // alphanumeric encodation or ISO/IEC encodation
800
                    mode = EncodeMode.ALPHA_OR_ISO;
801
                } else if (c >= 'a' && c <= 'z' || c == '!' || c == '"' || c == '%' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '+' || c == ':' || c == ';' || c == '<' || c == '='
802
                        || c == '>' || c == '?' || c == '_' || c == ' ') {
803
                    // ISO/IEC encodation
804
                    mode = EncodeMode.ISOIEC;
805
                } else {
806
                    // unable to encode this character
807
                    mode = EncodeMode.INVALID_CHAR;
808
                    latch = true;
809
                }
810
                generalFieldType[i] = mode;
811
            }
812
 
813
            if (latch) {
814
                throw new OkapiException("Invalid characters in input data");
815
            }
816
 
817
            for (i = 0; i < generalField.length - 1; i++) {
818
                if (generalFieldType[i] == EncodeMode.ISOIEC && generalField[i + 1] == FNC1) {
819
                    generalFieldType[i + 1] = EncodeMode.ISOIEC;
820
                }
821
            }
822
 
823
            for (i = 0; i < generalField.length - 1; i++) {
824
                if (generalFieldType[i] == EncodeMode.ALPHA_OR_ISO && generalField[i + 1] == FNC1) {
825
                    generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO;
826
                }
827
            }
828
 
829
            latch = applyGeneralFieldRules(generalFieldType); // modifies generalFieldType
830
 
831
            /* Set initial mode if not NUMERIC */
832
            if (generalFieldType[0] == EncodeMode.ALPHA) {
833
                binaryString.append("0000"); /* Alphanumeric latch */
834
                last_mode = EncodeMode.ALPHA;
835
            }
836
            if (generalFieldType[0] == EncodeMode.ISOIEC) {
837
                binaryString.append("0000"); /* Alphanumeric latch */
838
                binaryString.append("00100"); /* ISO/IEC 646 latch */
839
                last_mode = EncodeMode.ISOIEC;
840
            }
841
 
842
            i = 0;
843
            do {
844
                switch (generalFieldType[i]) {
845
                case NUMERIC:
846
                    if (last_mode != EncodeMode.NUMERIC) {
847
                        binaryString.append("000"); /* Numeric latch */
848
                    }
849
                    if (generalField[i] != FNC1) {
850
                        d1 = generalField[i] - '0';
851
                    } else {
852
                        d1 = 10;
853
                    }
854
                    if (generalField[i + 1] != FNC1) {
855
                        d2 = generalField[i + 1] - '0';
856
                    } else {
857
                        d2 = 10;
858
                    }
859
                    value = 11 * d1 + d2 + 8;
860
                    binaryAppend(binaryString, value, 7);
861
                    i += 2;
862
                    last_mode = EncodeMode.NUMERIC;
863
                    break;
864
 
865
                case ALPHA:
866
                    if (i != 0) {
867
                        if (last_mode == EncodeMode.NUMERIC) {
868
                            binaryString.append("0000"); /* Alphanumeric latch */
869
                        }
870
                        if (last_mode == EncodeMode.ISOIEC) {
871
                            binaryString.append("00100"); /* Alphanumeric latch */
872
                        }
873
                    }
874
                    if (generalField[i] >= '0' && generalField[i] <= '9') {
875
                        value = generalField[i] - 43;
876
                        binaryAppend(binaryString, value, 5);
877
                    }
878
                    if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
879
                        value = generalField[i] - 33;
880
                        binaryAppend(binaryString, value, 6);
881
                    }
882
                    last_mode = EncodeMode.ALPHA;
883
                    if (generalField[i] == FNC1) {
884
                        binaryString.append("01111");
885
                        // TODO: FNC1 should act as an implicit numeric latch, so the commented out
886
                        // line below should be correct, but ZXing cannot
887
                        // read barcodes which use FNC1 as an implicit numeric latch... so for now,
888
                        // and in order to achieve widest compatibility,
889
                        // we waste 3 bits and don't perform the implicit mode change (see
890
                        // https://sourceforge.net/p/zint/tickets/145/)
891
                        // last_mode = EncodeMode.NUMERIC;
892
                    } /* FNC1 / Numeric latch */
893
                    if (generalField[i] == '*') {
894
                        binaryString.append("111010"); /* asterisk */
895
                    }
896
                    if (generalField[i] == ',') {
897
                        binaryString.append("111011"); /* comma */
898
                    }
899
                    if (generalField[i] == '-') {
900
                        binaryString.append("111100"); /* minus or hyphen */
901
                    }
902
                    if (generalField[i] == '.') {
903
                        binaryString.append("111101"); /* period or full stop */
904
                    }
905
                    if (generalField[i] == '/') {
906
                        binaryString.append("111110"); /* slash or solidus */
907
                    }
908
                    i++;
909
                    break;
910
 
911
                case ISOIEC:
912
                    if (i != 0) {
913
                        if (last_mode == EncodeMode.NUMERIC) {
914
                            binaryString.append("0000"); /* Alphanumeric latch */
915
                            binaryString.append("00100"); /* ISO/IEC 646 latch */
916
                        }
917
                        if (last_mode == EncodeMode.ALPHA) {
918
                            binaryString.append("00100"); /* ISO/IEC 646 latch */
919
                        }
920
                    }
921
                    if (generalField[i] >= '0' && generalField[i] <= '9') {
922
                        value = generalField[i] - 43;
923
                        binaryAppend(binaryString, value, 5);
924
                    }
925
                    if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
926
                        value = generalField[i] - 1;
927
                        binaryAppend(binaryString, value, 7);
928
                    }
929
                    if (generalField[i] >= 'a' && generalField[i] <= 'z') {
930
                        value = generalField[i] - 7;
931
                        binaryAppend(binaryString, value, 7);
932
                    }
933
                    last_mode = EncodeMode.ISOIEC;
934
                    if (generalField[i] == FNC1) {
935
                        binaryString.append("01111");
936
                        // TODO: FNC1 should act as an implicit numeric latch, so the commented out
937
                        // line below should be correct, but ZXing cannot
938
                        // read barcodes which use FNC1 as an implicit numeric latch... so for now,
939
                        // and in order to achieve widest compatibility,
940
                        // we waste 3 bits and don't perform the implicit mode change (see
941
                        // https://sourceforge.net/p/zint/tickets/145/)
942
                        // last_mode = EncodeMode.NUMERIC;
943
                    } /* FNC1 / Numeric latch */
944
                    if (generalField[i] == '!') {
945
                        binaryString.append("11101000"); /* exclamation mark */
946
                    }
947
                    if (generalField[i] == 34) {
948
                        binaryString.append("11101001"); /* quotation mark */
949
                    }
950
                    if (generalField[i] == 37) {
951
                        binaryString.append("11101010"); /* percent sign */
952
                    }
953
                    if (generalField[i] == '&') {
954
                        binaryString.append("11101011"); /* ampersand */
955
                    }
956
                    if (generalField[i] == 39) {
957
                        binaryString.append("11101100"); /* apostrophe */
958
                    }
959
                    if (generalField[i] == '(') {
960
                        binaryString.append("11101101"); /* left parenthesis */
961
                    }
962
                    if (generalField[i] == ')') {
963
                        binaryString.append("11101110"); /* right parenthesis */
964
                    }
965
                    if (generalField[i] == '*') {
966
                        binaryString.append("11101111"); /* asterisk */
967
                    }
968
                    if (generalField[i] == '+') {
969
                        binaryString.append("11110000"); /* plus sign */
970
                    }
971
                    if (generalField[i] == ',') {
972
                        binaryString.append("11110001"); /* comma */
973
                    }
974
                    if (generalField[i] == '-') {
975
                        binaryString.append("11110010"); /* minus or hyphen */
976
                    }
977
                    if (generalField[i] == '.') {
978
                        binaryString.append("11110011"); /* period or full stop */
979
                    }
980
                    if (generalField[i] == '/') {
981
                        binaryString.append("11110100"); /* slash or solidus */
982
                    }
983
                    if (generalField[i] == ':') {
984
                        binaryString.append("11110101"); /* colon */
985
                    }
986
                    if (generalField[i] == ';') {
987
                        binaryString.append("11110110"); /* semicolon */
988
                    }
989
                    if (generalField[i] == '<') {
990
                        binaryString.append("11110111"); /* less-than sign */
991
                    }
992
                    if (generalField[i] == '=') {
993
                        binaryString.append("11111000"); /* equals sign */
994
                    }
995
                    if (generalField[i] == '>') {
996
                        binaryString.append("11111001"); /* greater-than sign */
997
                    }
998
                    if (generalField[i] == '?') {
999
                        binaryString.append("11111010"); /* question mark */
1000
                    }
1001
                    if (generalField[i] == '_') {
1002
                        binaryString.append("11111011"); /* underline or low line */
1003
                    }
1004
                    if (generalField[i] == ' ') {
1005
                        binaryString.append("11111100"); /* space */
1006
                    }
1007
                    i++;
1008
                    break;
1009
                }
1010
 
1011
                current_length = i;
1012
                if (latch) {
1013
                    current_length++;
1014
                }
1015
 
1016
            } while (current_length < generalField.length);
1017
 
1018
            remainder = calculateRemainder(binaryString.length());
1019
 
1020
            if (latch) {
1021
                /* There is still one more numeric digit to encode */
1022
                if (last_mode == EncodeMode.NUMERIC) {
1023
                    if (remainder >= 4 && remainder <= 6) {
1024
                        value = generalField[i] - '0';
1025
                        value++;
1026
                        binaryAppend(binaryString, value, 4);
1027
                    } else {
1028
                        d1 = generalField[i] - '0';
1029
                        d2 = 10;
1030
                        value = 11 * d1 + d2 + 8;
1031
                        binaryAppend(binaryString, value, 7);
1032
                    }
1033
                } else {
1034
                    value = generalField[i] - 43;
1035
                    binaryAppend(binaryString, value, 5);
1036
                }
1037
            }
1038
        }
1039
 
1040
        if (binaryString.length() > 252) {
1041
            throw new OkapiException("Input too long");
1042
        }
1043
 
1044
        remainder = calculateRemainder(binaryString.length());
1045
 
1046
        /* Now add padding to binary string (7.2.5.5.4) */
1047
        i = remainder;
1048
        if (generalField.length != 0 && last_mode == EncodeMode.NUMERIC) {
1049
            padstring = "0000";
1050
            i -= 4;
1051
        } else {
1052
            padstring = "";
1053
        }
1054
        for (; i > 0; i -= 5) {
1055
            padstring += "00100";
1056
        }
1057
 
1058
        binaryString.append(padstring.substring(0, remainder));
1059
 
1060
        /* Patch variable length symbol bit field */
1061
        char patchEvenOdd, patchSize;
1062
        if ((binaryString.length() / 12 + 1 & 1) == 0) {
1063
            patchEvenOdd = '0';
1064
        } else {
1065
            patchEvenOdd = '1';
1066
        }
1067
        if (binaryString.length() <= 156) {
1068
            patchSize = '0';
1069
        } else {
1070
            patchSize = '1';
1071
        }
1072
 
1073
        if (encodingMethod == 1) {
1074
            binaryString.setCharAt(2, patchEvenOdd);
1075
            binaryString.setCharAt(3, patchSize);
1076
        }
1077
        if (encodingMethod == 2) {
1078
            binaryString.setCharAt(3, patchEvenOdd);
1079
            binaryString.setCharAt(4, patchSize);
1080
        }
1081
        if (encodingMethod == 5 || encodingMethod == 6) {
1082
            binaryString.setCharAt(6, patchEvenOdd);
1083
            binaryString.setCharAt(7, patchSize);
1084
        }
1085
 
1086
        return encodingMethod;
1087
    }
1088
 
1089
    private static int calculateRemainder(final int binaryStringLength) {
1090
        int remainder = 12 - binaryStringLength % 12;
1091
        if (remainder == 12) {
1092
            remainder = 0;
1093
        }
1094
        if (binaryStringLength < 36) {
1095
            remainder = 36 - binaryStringLength;
1096
        }
1097
        return remainder;
1098
    }
1099
 
1100
    /** Logs binary string as hexadecimal */
1101
    private void logBinaryStringInfo(final StringBuilder binaryString) {
1102
 
1103
        infoLine("Binary Length: " + binaryString.length());
1104
        info("Binary String: ");
1105
 
1106
        int nibble = 0;
1107
        for (int i = 0; i < binaryString.length(); i++) {
1108
            switch (i % 4) {
1109
            case 0:
1110
                if (binaryString.charAt(i) == '1') {
1111
                    nibble += 8;
1112
                }
1113
                break;
1114
            case 1:
1115
                if (binaryString.charAt(i) == '1') {
1116
                    nibble += 4;
1117
                }
1118
                break;
1119
            case 2:
1120
                if (binaryString.charAt(i) == '1') {
1121
                    nibble += 2;
1122
                }
1123
                break;
1124
            case 3:
1125
                if (binaryString.charAt(i) == '1') {
1126
                    nibble += 1;
1127
                }
1128
                info(Integer.toHexString(nibble));
1129
                nibble = 0;
1130
                break;
1131
            }
1132
        }
1133
 
1134
        if (binaryString.length() % 4 != 0) {
1135
            info(Integer.toHexString(nibble));
1136
        }
1137
 
1138
        infoLine();
1139
    }
1140
 
1141
    /**
1142
     * Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC 24724:2006
1143
     */
1144
    private static boolean applyGeneralFieldRules(final EncodeMode[] generalFieldType) {
1145
 
1146
        int block_count, i, j, k;
1147
        EncodeMode current, next, last;
1148
        final int[] blockLength = new int[200];
1149
        final EncodeMode[] blockType = new EncodeMode[200];
1150
 
1151
        block_count = 0;
1152
 
1153
        blockLength[block_count] = 1;
1154
        blockType[block_count] = generalFieldType[0];
1155
 
1156
        for (i = 1; i < generalFieldType.length; i++) {
1157
            current = generalFieldType[i];
1158
            last = generalFieldType[i - 1];
1159
 
1160
            if (current == last) {
1161
                blockLength[block_count] = blockLength[block_count] + 1;
1162
            } else {
1163
                block_count++;
1164
                blockLength[block_count] = 1;
1165
                blockType[block_count] = generalFieldType[i];
1166
            }
1167
        }
1168
 
1169
        block_count++;
1170
 
1171
        for (i = 0; i < block_count; i++) {
1172
            current = blockType[i];
1173
            next = blockType[i + 1];
1174
 
1175
            if (current == EncodeMode.ISOIEC && i != block_count - 1) {
1176
                if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 4) {
1177
                    blockType[i + 1] = EncodeMode.NUMERIC;
1178
                }
1179
                if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 4) {
1180
                    blockType[i + 1] = EncodeMode.ISOIEC;
1181
                }
1182
                if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) {
1183
                    blockType[i + 1] = EncodeMode.ALPHA;
1184
                }
1185
                if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) {
1186
                    blockType[i + 1] = EncodeMode.ISOIEC;
1187
                }
1188
            }
1189
 
1190
            if (current == EncodeMode.ALPHA_OR_ISO) {
1191
                blockType[i] = EncodeMode.ALPHA;
1192
                current = EncodeMode.ALPHA;
1193
            }
1194
 
1195
            if (current == EncodeMode.ALPHA && i != block_count - 1) {
1196
                if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 6) {
1197
                    blockType[i + 1] = EncodeMode.NUMERIC;
1198
                }
1199
                if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 6) {
1200
                    if (i == block_count - 2 && blockLength[i + 1] >= 4) {
1201
                        blockType[i + 1] = EncodeMode.NUMERIC;
1202
                    } else {
1203
                        blockType[i + 1] = EncodeMode.ALPHA;
1204
                    }
1205
                }
1206
            }
1207
 
1208
            if (current == EncodeMode.ANY_ENC) {
1209
                blockType[i] = EncodeMode.NUMERIC;
1210
            }
1211
        }
1212
 
1213
        if (block_count > 1) {
1214
            i = 1;
1215
            while (i < block_count) {
1216
                if (blockType[i - 1] == blockType[i]) {
1217
                    /* bring together */
1218
                    blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
1219
                    j = i + 1;
1220
 
1221
                    /* decrease the list */
1222
                    while (j < block_count) {
1223
                        blockLength[j - 1] = blockLength[j];
1224
                        blockType[j - 1] = blockType[j];
1225
                        j++;
1226
                    }
1227
                    block_count--;
1228
                    i--;
1229
                }
1230
                i++;
1231
            }
1232
        }
1233
 
1234
        for (i = 0; i < block_count - 1; i++) {
1235
            if (blockType[i] == EncodeMode.NUMERIC && (blockLength[i] & 1) != 0) {
1236
                /* Odd size numeric block */
1237
                blockLength[i] = blockLength[i] - 1;
1238
                blockLength[i + 1] = blockLength[i + 1] + 1;
1239
            }
1240
        }
1241
 
1242
        j = 0;
1243
        for (i = 0; i < block_count; i++) {
1244
            for (k = 0; k < blockLength[i]; k++) {
1245
                generalFieldType[j] = blockType[i];
1246
                j++;
1247
            }
1248
        }
1249
 
1250
        if (blockType[block_count - 1] == EncodeMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) {
1251
            /*
1252
             * If the last block is numeric and an odd size, further processing needs to be done
1253
             * outside this procedure
1254
             */
1255
            return true;
1256
        } else {
1257
            return false;
1258
        }
1259
    }
1260
 
1261
    private static int parseInt(final int[] chars, final int index, final int length) {
1262
        int val = 0;
1263
        int pow = (int) Math.pow(10, length - 1);
1264
        for (int i = 0; i < length; i++) {
1265
            final int c = chars[index + i];
1266
            val += (c - '0') * pow;
1267
            pow /= 10;
1268
        }
1269
        return val;
1270
    }
1271
}