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-2018 Robin Stuart, 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
package uk.org.okapibarcode.backend;
15
 
16
import static java.nio.charset.StandardCharsets.ISO_8859_1;
17
 
18
/**
19
 * <p>
20
 * Implements Code 128 bar code symbology according to ISO/IEC 15417:2007.
21
 *
22
 * <p>
23
 * Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters.
24
 *
25
 * <p>
26
 * Setting GS1 mode allows encoding in GS1-128 (also known as UCC/EAN-128).
27
 *
28
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
29
 * @author Daniel Gredler
30
 */
31
public class Code128 extends Symbol {
32
 
33
    private enum Mode {
34
        NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC
35
    }
36
 
37
    private enum FMode {
38
        SHIFTN, LATCHN, SHIFTF, LATCHF
39
    }
40
 
41
    private enum Composite {
42
        OFF, CCA, CCB, CCC
43
    }
44
 
45
    protected static final String[] CODE128_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132",
46
            "122231", "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321",
47
            "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331",
48
            "231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122",
49
            "141221", "112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211",
50
            "411212", "421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412",
51
            "211214", "211232", "2331112" };
52
 
53
    private boolean suppressModeC = false;
54
    private Composite compositeMode = Composite.OFF;
55
 
56
    /**
57
     * Optionally prevents this symbol from using subset mode C for numeric data compression.
58
     *
59
     * @param suppressModeC whether or not to prevent this symbol from using subset mode C
60
     */
61
    public void setSuppressModeC(final boolean suppressModeC) {
62
        this.suppressModeC = suppressModeC;
63
    }
64
 
65
    /**
66
     * Returns whether or not this symbol is prevented from using subset mode C for numeric data
67
     * compression.
68
     *
69
     * @return whether or not this symbol is prevented from using subset mode C for numeric data
70
     *         compression
71
     */
72
    public boolean getSuppressModeC() {
73
        return this.suppressModeC;
74
    }
75
 
76
    protected void setCca() {
77
        this.compositeMode = Composite.CCA;
78
    }
79
 
80
    protected void setCcb() {
81
        this.compositeMode = Composite.CCB;
82
    }
83
 
84
    protected void setCcc() {
85
        this.compositeMode = Composite.CCC;
86
    }
87
 
88
    public void unsetCc() {
89
        this.compositeMode = Composite.OFF;
90
    }
91
 
92
    @Override
93
    protected boolean gs1Supported() {
94
        return true;
95
    }
96
 
97
    @Override
98
    protected void encode() {
99
        int i, j, k;
100
        final int input_point = 0;
101
        Mode mode, last_mode;
102
        Mode last_set, current_set;
103
        double glyph_count;
104
        int bar_characters = 0, total_sum = 0;
105
        FMode f_state = FMode.LATCHN;
106
        final Mode[] mode_type = new Mode[200];
107
        final int[] mode_length = new int[200];
108
        final int[] values = new int[200];
109
        int c;
110
        int linkage_flag = 0;
111
        int index_point = 0;
112
        int read = 0;
113
 
114
        this.inputData = toBytes(this.content, ISO_8859_1);
115
        if (this.inputData == null) {
116
            throw new OkapiException("Invalid characters in input data");
117
        }
118
 
119
        final int sourcelen = this.inputData.length;
120
 
121
        final FMode[] fset = new FMode[200];
122
        final Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */
123
 
124
        if (sourcelen > 170) {
125
            throw new OkapiException("Input data too long");
126
        }
127
 
128
        /* Detect extended ASCII characters */
129
        for (i = 0; i < sourcelen; i++) {
130
            final int ch = this.inputData[i];
131
            if (ch >= 128 && ch != FNC1 && ch != FNC2 && ch != FNC3 && ch != FNC4) {
132
                fset[i] = FMode.SHIFTF;
133
            } else {
134
                fset[i] = FMode.LATCHN;
135
            }
136
        }
137
 
138
        /* Decide when to latch to extended mode - Annex E note 3 */
139
        j = 0;
140
        for (i = 0; i < sourcelen; i++) {
141
            if (fset[i] == FMode.SHIFTF) {
142
                j++;
143
            } else {
144
                j = 0;
145
            }
146
            if (j >= 5) {
147
                for (k = i; k > i - 5; k--) {
148
                    fset[k] = FMode.LATCHF;
149
                }
150
            }
151
            if (j >= 3 && i == sourcelen - 1) {
152
                for (k = i; k > i - 3; k--) {
153
                    fset[k] = FMode.LATCHF;
154
                }
155
            }
156
        }
157
 
158
        /*
159
         * Decide if it is worth reverting to 646 encodation for a few characters as described in
160
         * 4.3.4.2 (d)
161
         */
162
        for (i = 1; i < sourcelen; i++) {
163
            if (fset[i - 1] == FMode.LATCHF && fset[i] == FMode.LATCHN) {
164
                /* Detected a change from 8859-1 to 646 - count how long for */
165
                for (j = 0; fset[i + j] == FMode.LATCHN && i + j < sourcelen; j++) {
166
                    ;
167
                }
168
                if (j < 5 || j < 3 && i + j == sourcelen - 1) {
169
                    /* Uses the same figures recommended by Annex E note 3 */
170
                    /* Change to shifting back rather than latching back */
171
                    for (k = 0; k < j; k++) {
172
                        fset[i + k] = FMode.SHIFTN;
173
                    }
174
                }
175
            }
176
        }
177
 
178
        /* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */
179
        int letter = this.inputData[input_point];
180
        int numbers = letter >= '0' && letter <= '9' ? 1 : 0;
181
        mode = findSubset(letter, numbers);
182
        mode_type[0] = mode;
183
        mode_length[0] += length(letter, mode);
184
        for (i = 1; i < sourcelen; i++) {
185
            letter = this.inputData[i];
186
            last_mode = mode;
187
            mode = findSubset(letter, numbers);
188
            if (mode == last_mode) {
189
                mode_length[index_point] += length(letter, mode);
190
            } else {
191
                index_point++;
192
                mode_type[index_point] = mode;
193
                mode_length[index_point] = length(letter, mode);
194
            }
195
            if (letter >= '0' && letter <= '9') {
196
                numbers++;
197
            } else {
198
                numbers = 0;
199
            }
200
        }
201
        index_point++;
202
        index_point = reduceSubsetChanges(mode_type, mode_length, index_point);
203
 
204
        /* Put set data into set[] */
205
        read = 0;
206
        for (i = 0; i < index_point; i++) {
207
            for (j = 0; j < mode_length[i]; j++) {
208
                set[read] = mode_type[i];
209
                read++;
210
            }
211
        }
212
 
213
        /* Resolve odd length LATCHC blocks */
214
        int cs = 0, nums = 0, fncs = 0;
215
        for (i = 0; i < read; i++) {
216
            if (set[i] == Mode.LATCHC) {
217
                cs++;
218
                if (this.inputData[i] >= '0' && this.inputData[i] <= '9') {
219
                    nums++;
220
                } else if (this.inputData[i] == FNC1) {
221
                    fncs++;
222
                }
223
            } else {
224
                resolveOddCs(set, i, cs, nums, fncs);
225
                cs = 0;
226
                nums = 0;
227
                fncs = 0;
228
            }
229
        }
230
        resolveOddCs(set, i, cs, nums, fncs);
231
 
232
        /* Adjust for strings which start with shift characters - make them latch instead */
233
        if (set[0] == Mode.SHIFTA) {
234
            i = 0;
235
            do {
236
                set[i] = Mode.LATCHA;
237
                i++;
238
            } while (set[i] == Mode.SHIFTA);
239
        }
240
        if (set[0] == Mode.SHIFTB) {
241
            i = 0;
242
            do {
243
                set[i] = Mode.LATCHB;
244
                i++;
245
            } while (set[i] == Mode.SHIFTB);
246
        }
247
 
248
        /*
249
         * Now we can calculate how long the barcode is going to be - and stop it from being too
250
         * long
251
         */
252
        last_set = Mode.NULL;
253
        glyph_count = 0.0;
254
        for (i = 0; i < sourcelen; i++) {
255
            if (set[i] == Mode.SHIFTA || set[i] == Mode.SHIFTB) {
256
                glyph_count += 1.0;
257
            }
258
            if (fset[i] == FMode.SHIFTF || fset[i] == FMode.SHIFTN) {
259
                glyph_count += 1.0;
260
            }
261
            if (set[i] == Mode.LATCHA || set[i] == Mode.LATCHB || set[i] == Mode.LATCHC) {
262
                if (set[i] != last_set) {
263
                    last_set = set[i];
264
                    glyph_count += 1.0;
265
                }
266
            }
267
            if (i == 0) {
268
                if (fset[i] == FMode.LATCHF) {
269
                    glyph_count += 2.0;
270
                }
271
            } else {
272
                if (fset[i] == FMode.LATCHF && fset[i - 1] != FMode.LATCHF) {
273
                    glyph_count += 2.0;
274
                }
275
                if (fset[i] != FMode.LATCHF && fset[i - 1] == FMode.LATCHF) {
276
                    glyph_count += 2.0;
277
                }
278
            }
279
            if (set[i] == Mode.LATCHC) {
280
                if (this.inputData[i] == FNC1) {
281
                    glyph_count += 1.0;
282
                } else {
283
                    glyph_count += 0.5;
284
                }
285
            } else {
286
                glyph_count += 1.0;
287
            }
288
        }
289
        if (glyph_count > 80.0) {
290
            throw new OkapiException("Input data too long");
291
        }
292
 
293
        info("Encoding: ");
294
 
295
        /* So now we know what start character to use - we can get on with it! */
296
        if (this.readerInit) {
297
            /* Reader Initialisation mode */
298
            switch (set[0]) {
299
            case LATCHA:
300
                values[0] = 103;
301
                current_set = Mode.LATCHA;
302
                values[1] = 96;
303
                bar_characters++;
304
                info("STARTA FNC3 ");
305
                break;
306
            case LATCHB:
307
                values[0] = 104;
308
                current_set = Mode.LATCHB;
309
                values[1] = 96;
310
                bar_characters++;
311
                info("STARTB FNC3 ");
312
                break;
313
            default: /* Start C */
314
                values[0] = 104;
315
                values[1] = 96;
316
                values[2] = 99;
317
                bar_characters += 2;
318
                current_set = Mode.LATCHC;
319
                info("STARTB FNC3 CODEC ");
320
                break;
321
            }
322
        } else {
323
            /* Normal mode */
324
            switch (set[0]) {
325
            case LATCHA:
326
                values[0] = 103;
327
                current_set = Mode.LATCHA;
328
                info("STARTA ");
329
                break;
330
            case LATCHB:
331
                values[0] = 104;
332
                current_set = Mode.LATCHB;
333
                info("STARTB ");
334
                break;
335
            default:
336
                values[0] = 105;
337
                current_set = Mode.LATCHC;
338
                info("STARTC ");
339
                break;
340
            }
341
        }
342
        bar_characters++;
343
 
344
        if (this.inputDataType == DataType.GS1) {
345
            values[1] = 102;
346
            bar_characters++;
347
            info("FNC1 ");
348
        }
349
 
350
        if (fset[0] == FMode.LATCHF) {
351
            switch (current_set) {
352
            case LATCHA:
353
                values[bar_characters] = 101;
354
                values[bar_characters + 1] = 101;
355
                info("FNC4 FNC4 ");
356
                break;
357
            case LATCHB:
358
                values[bar_characters] = 100;
359
                values[bar_characters + 1] = 100;
360
                info("FNC4 FNC4 ");
361
                break;
362
            }
363
            bar_characters += 2;
364
            f_state = FMode.LATCHF;
365
        }
366
 
367
        /* Encode the data */
368
        read = 0;
369
        do {
370
 
371
            if (read != 0 && set[read] != current_set) { /* Latch different code set */
372
                switch (set[read]) {
373
                case LATCHA:
374
                    values[bar_characters] = 101;
375
                    bar_characters++;
376
                    current_set = Mode.LATCHA;
377
                    info("CODEA ");
378
                    break;
379
                case LATCHB:
380
                    values[bar_characters] = 100;
381
                    bar_characters++;
382
                    current_set = Mode.LATCHB;
383
                    info("CODEB ");
384
                    break;
385
                case LATCHC:
386
                    values[bar_characters] = 99;
387
                    bar_characters++;
388
                    current_set = Mode.LATCHC;
389
                    info("CODEC ");
390
                    break;
391
                }
392
            }
393
 
394
            if (read != 0) {
395
                if (fset[read] == FMode.LATCHF && f_state == FMode.LATCHN) {
396
                    /* Latch beginning of extended mode */
397
                    switch (current_set) {
398
                    case LATCHA:
399
                        values[bar_characters] = 101;
400
                        values[bar_characters + 1] = 101;
401
                        info("FNC4 FNC4 ");
402
                        break;
403
                    case LATCHB:
404
                        values[bar_characters] = 100;
405
                        values[bar_characters + 1] = 100;
406
                        info("FNC4 FNC4 ");
407
                        break;
408
                    }
409
                    bar_characters += 2;
410
                    f_state = FMode.LATCHF;
411
                }
412
                if (fset[read] == FMode.LATCHN && f_state == FMode.LATCHF) {
413
                    /* Latch end of extended mode */
414
                    switch (current_set) {
415
                    case LATCHA:
416
                        values[bar_characters] = 101;
417
                        values[bar_characters + 1] = 101;
418
                        info("FNC4 FNC4 ");
419
                        break;
420
                    case LATCHB:
421
                        values[bar_characters] = 100;
422
                        values[bar_characters + 1] = 100;
423
                        info("FNC4 FNC4 ");
424
                        break;
425
                    }
426
                    bar_characters += 2;
427
                    f_state = FMode.LATCHN;
428
                }
429
            }
430
 
431
            if (fset[read] == FMode.SHIFTF || fset[read] == FMode.SHIFTN) {
432
                /* Shift to or from extended mode */
433
                switch (current_set) {
434
                case LATCHA:
435
                    values[bar_characters] = 101;
436
                    info("FNC4 ");
437
                    break;
438
                case LATCHB:
439
                    values[bar_characters] = 100;
440
                    info("FNC4 ");
441
                    break;
442
                }
443
                bar_characters++;
444
            }
445
 
446
            if (set[read] == Mode.SHIFTA || set[read] == Mode.SHIFTB) {
447
                /* Insert shift character */
448
                values[bar_characters] = 98;
449
                info("SHFT ");
450
                bar_characters++;
451
            }
452
 
453
            /* Encode data characters */
454
            c = this.inputData[read];
455
            switch (set[read]) {
456
            case SHIFTA:
457
            case LATCHA:
458
                if (c == FNC1) {
459
                    values[bar_characters] = 102;
460
                    info("FNC1 ");
461
                } else if (c == FNC2) {
462
                    values[bar_characters] = 97;
463
                    info("FNC2 ");
464
                } else if (c == FNC3) {
465
                    values[bar_characters] = 96;
466
                    info("FNC3 ");
467
                } else if (c == FNC4) {
468
                    values[bar_characters] = 101;
469
                    info("FNC4 ");
470
                } else if (c > 127) {
471
                    if (c < 160) {
472
                        values[bar_characters] = c - 128 + 64;
473
                    } else {
474
                        values[bar_characters] = c - 128 - 32;
475
                    }
476
                    infoSpace(values[bar_characters]);
477
                } else {
478
                    if (c < 32) {
479
                        values[bar_characters] = c + 64;
480
                    } else {
481
                        values[bar_characters] = c - 32;
482
                    }
483
                    infoSpace(values[bar_characters]);
484
                }
485
                bar_characters++;
486
                read++;
487
                break;
488
            case SHIFTB:
489
            case LATCHB:
490
                if (c == FNC1) {
491
                    values[bar_characters] = 102;
492
                    info("FNC1 ");
493
                } else if (c == FNC2) {
494
                    values[bar_characters] = 97;
495
                    info("FNC2 ");
496
                } else if (c == FNC3) {
497
                    values[bar_characters] = 96;
498
                    info("FNC3 ");
499
                } else if (c == FNC4) {
500
                    values[bar_characters] = 100;
501
                    info("FNC4 ");
502
                } else if (c > 127) {
503
                    values[bar_characters] = c - 32 - 128;
504
                    infoSpace(values[bar_characters]);
505
                } else {
506
                    values[bar_characters] = c - 32;
507
                    infoSpace(values[bar_characters]);
508
                }
509
                bar_characters++;
510
                read++;
511
                break;
512
            case LATCHC:
513
                if (c == FNC1) {
514
                    values[bar_characters] = 102;
515
                    info("FNC1 ");
516
                    bar_characters++;
517
                    read++;
518
                } else {
519
                    final int d = this.inputData[read + 1];
520
                    final int weight = 10 * (c - '0') + d - '0';
521
                    values[bar_characters] = weight;
522
                    infoSpace(values[bar_characters]);
523
                    bar_characters++;
524
                    read += 2;
525
                }
526
                break;
527
            }
528
 
529
        } while (read < sourcelen);
530
 
531
        infoLine();
532
 
533
        /*
534
         * "...note that the linkage flag is an extra code set character between the last data
535
         * character and the Symbol Check Character" (GS1 Specification)
536
         */
537
 
538
        /* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */
539
 
540
        switch (this.compositeMode) {
541
        case CCA:
542
        case CCB:
543
            /* CC-A or CC-B 2D component */
544
            switch (set[sourcelen - 1]) {
545
            case LATCHA:
546
                linkage_flag = 100;
547
                break;
548
            case LATCHB:
549
                linkage_flag = 99;
550
                break;
551
            case LATCHC:
552
                linkage_flag = 101;
553
                break;
554
            }
555
            infoLine("Linkage Flag: " + linkage_flag);
556
            break;
557
        case CCC:
558
            /* CC-C 2D component */
559
            switch (set[sourcelen - 1]) {
560
            case LATCHA:
561
                linkage_flag = 99;
562
                break;
563
            case LATCHB:
564
                linkage_flag = 101;
565
                break;
566
            case LATCHC:
567
                linkage_flag = 100;
568
                break;
569
            }
570
            infoLine("Linkage Flag: " + linkage_flag);
571
            break;
572
        default:
573
            break;
574
        }
575
 
576
        if (linkage_flag != 0) {
577
            values[bar_characters] = linkage_flag;
578
            bar_characters++;
579
        }
580
 
581
        infoLine("Data Codewords: " + bar_characters);
582
 
583
        /* Check digit calculation */
584
        for (i = 0; i < bar_characters; i++) {
585
            total_sum += i == 0 ? values[i] : values[i] * i;
586
        }
587
        final int checkDigit = total_sum % 103;
588
        infoLine("Check Digit: " + checkDigit);
589
 
590
        /* Build pattern string */
591
        final StringBuilder dest = new StringBuilder(6 * bar_characters + 6 + 7);
592
        for (i = 0; i < bar_characters; i++) {
593
            dest.append(CODE128_TABLE[values[i]]);
594
        }
595
        dest.append(CODE128_TABLE[checkDigit]);
596
        dest.append(CODE128_TABLE[106]); // stop character
597
 
598
        /* Readable text */
599
        if (this.inputDataType != DataType.GS1) {
600
            this.readable = removeFncEscapeSequences(this.content);
601
            if (this.inputDataType == DataType.HIBC) {
602
                this.readable = "*" + this.readable + "*";
603
            }
604
        }
605
 
606
        if (this.compositeMode == Composite.OFF) {
607
            this.pattern = new String[] { dest.toString() };
608
            this.row_height = new int[] { -1 };
609
            this.row_count = 1;
610
        } else {
611
            /* Add the separator pattern for composite symbols */
612
            this.pattern = new String[] { "0" + dest, dest.toString() };
613
            this.row_height = new int[] { 1, -1 };
614
            this.row_count = 2;
615
        }
616
    }
617
 
618
    private static String removeFncEscapeSequences(final String s) {
619
        return s.replace(FNC1_STRING, "").replace(FNC2_STRING, "").replace(FNC3_STRING, "").replace(FNC4_STRING, "");
620
    }
621
 
622
    private void resolveOddCs(final Mode[] set, final int i, final int cs, final int nums, final int fncs) {
623
        if ((nums & 1) != 0) {
624
            int index;
625
            Mode m;
626
            if (i - cs == 0 || fncs > 0) {
627
                // Rule 2: first block -> swap last digit to A or B
628
                index = i - 1;
629
                if (index + 1 < set.length && set[index + 1] != null && set[index + 1] != Mode.LATCHC) {
630
                    // next block is either A or B -- match it
631
                    m = set[index + 1];
632
                } else {
633
                    // next block is C, or there is no next block -- just latch to B
634
                    m = Mode.LATCHB;
635
                }
636
            } else {
637
                // Rule 3b: subsequent block -> swap first digit to A or B
638
                // Note that we make an exception for C blocks which contain one (or more) FNC1
639
                // characters,
640
                // since swapping the first digit would place the FNC1 in an invalid position in the
641
                // block
642
                index = i - nums;
643
                if (index - 1 >= 0 && set[index - 1] != null && set[index - 1] != Mode.LATCHC) {
644
                    // previous block is either A or B -- match it
645
                    m = set[index - 1];
646
                } else {
647
                    // previous block is C, or there is no previous block -- just latch to B
648
                    m = Mode.LATCHB;
649
                }
650
            }
651
            set[index] = m;
652
        }
653
    }
654
 
655
    private Mode findSubset(final int letter, final int numbers) {
656
 
657
        Mode mode;
658
 
659
        if (letter == FNC1) {
660
            if (numbers % 2 == 0) {
661
                /* ISO 15417 Annex E Note 2 */
662
                /*
663
                 * FNC1 may use subset C, so long as it doesn't break data into an odd number of
664
                 * digits
665
                 */
666
                mode = Mode.ABORC;
667
            } else {
668
                mode = Mode.AORB;
669
            }
670
        } else if (letter == FNC2 || letter == FNC3 || letter == FNC4) {
671
            mode = Mode.AORB;
672
        } else if (letter <= 31) {
673
            mode = Mode.SHIFTA;
674
        } else if (letter >= 48 && letter <= 57) {
675
            mode = Mode.ABORC;
676
        } else if (letter <= 95) {
677
            mode = Mode.AORB;
678
        } else if (letter <= 127) {
679
            mode = Mode.SHIFTB;
680
        } else if (letter <= 159) {
681
            mode = Mode.SHIFTA;
682
        } else if (letter <= 223) {
683
            mode = Mode.AORB;
684
        } else {
685
            mode = Mode.SHIFTB;
686
        }
687
 
688
        if (this.suppressModeC && mode == Mode.ABORC) {
689
            mode = Mode.AORB;
690
        }
691
 
692
        return mode;
693
    }
694
 
695
    private int length(final int letter, final Mode mode) {
696
        if (letter == FNC1 && mode == Mode.ABORC) {
697
            /* ISO 15417 Annex E Note 2 */
698
            /* Logical length used for making subset switching decisions, not actual length */
699
            return 2;
700
        } else {
701
            return 1;
702
        }
703
    }
704
 
705
    /** Implements rules from ISO 15417 Annex E. Returns the updated index point. */
706
    private int reduceSubsetChanges(final Mode[] mode_type, final int[] mode_length, final int index_point) {
707
 
708
        int totalLength = 0;
709
        int i, length;
710
        Mode current, last, next;
711
 
712
        for (i = 0; i < index_point; i++) {
713
            current = mode_type[i];
714
            length = mode_length[i];
715
            if (i != 0) {
716
                last = mode_type[i - 1];
717
            } else {
718
                last = Mode.NULL;
719
            }
720
            if (i != index_point - 1) {
721
                next = mode_type[i + 1];
722
            } else {
723
                next = Mode.NULL;
724
            }
725
 
726
            /* ISO 15417 Annex E Note 2 */
727
            /* Calculate difference between logical length and actual length in this block */
728
            int extraLength = 0;
729
            for (int j = 0; j < length - extraLength; j++) {
730
                if (length(this.inputData[totalLength + j], current) == 2) {
731
                    extraLength++;
732
                }
733
            }
734
 
735
            if (i == 0) { /* first block */
736
                if (index_point == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */
737
                    mode_type[i] = Mode.LATCHC;
738
                    current = Mode.LATCHC;
739
                }
740
                if (current == Mode.ABORC) {
741
                    if (length >= 4) { /* Rule 1b */
742
                        mode_type[i] = Mode.LATCHC;
743
                        current = Mode.LATCHC;
744
                    } else {
745
                        mode_type[i] = Mode.AORB;
746
                        current = Mode.AORB;
747
                    }
748
                }
749
                if (current == Mode.SHIFTA) { /* Rule 1c */
750
                    mode_type[i] = Mode.LATCHA;
751
                    current = Mode.LATCHA;
752
                }
753
                if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */
754
                    mode_type[i] = Mode.LATCHA;
755
                    current = Mode.LATCHA;
756
                }
757
                if (current == Mode.AORB) { /* Rule 1d */
758
                    mode_type[i] = Mode.LATCHB;
759
                    current = Mode.LATCHB;
760
                }
761
            } else {
762
                if (current == Mode.ABORC && length >= 4) { /* Rule 3 */
763
                    mode_type[i] = Mode.LATCHC;
764
                    current = Mode.LATCHC;
765
                }
766
                if (current == Mode.ABORC) {
767
                    mode_type[i] = Mode.AORB;
768
                    current = Mode.AORB;
769
                }
770
                if (current == Mode.AORB && last == Mode.LATCHA) {
771
                    mode_type[i] = Mode.LATCHA;
772
                    current = Mode.LATCHA;
773
                }
774
                if (current == Mode.AORB && last == Mode.LATCHB) {
775
                    mode_type[i] = Mode.LATCHB;
776
                    current = Mode.LATCHB;
777
                }
778
                if (current == Mode.AORB && next == Mode.SHIFTA) {
779
                    mode_type[i] = Mode.LATCHA;
780
                    current = Mode.LATCHA;
781
                }
782
                if (current == Mode.AORB && next == Mode.SHIFTB) {
783
                    mode_type[i] = Mode.LATCHB;
784
                    current = Mode.LATCHB;
785
                }
786
                if (current == Mode.AORB) {
787
                    mode_type[i] = Mode.LATCHB;
788
                    current = Mode.LATCHB;
789
                }
790
                if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */
791
                    mode_type[i] = Mode.LATCHA;
792
                    current = Mode.LATCHA;
793
                }
794
                if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */
795
                    mode_type[i] = Mode.LATCHB;
796
                    current = Mode.LATCHB;
797
                }
798
                if (current == Mode.SHIFTA && last == Mode.LATCHA) {
799
                    mode_type[i] = Mode.LATCHA;
800
                    current = Mode.LATCHA;
801
                }
802
                if (current == Mode.SHIFTB && last == Mode.LATCHB) {
803
                    mode_type[i] = Mode.LATCHB;
804
                    current = Mode.LATCHB;
805
                }
806
                if (current == Mode.SHIFTA && next == Mode.AORB) {
807
                    mode_type[i] = Mode.LATCHA;
808
                    current = Mode.LATCHA;
809
                }
810
                if (current == Mode.SHIFTB && next == Mode.AORB) {
811
                    mode_type[i] = Mode.LATCHB;
812
                    current = Mode.LATCHB;
813
                }
814
                if (current == Mode.SHIFTA && last == Mode.LATCHC) {
815
                    mode_type[i] = Mode.LATCHA;
816
                    current = Mode.LATCHA;
817
                }
818
                if (current == Mode.SHIFTB && last == Mode.LATCHC) {
819
                    mode_type[i] = Mode.LATCHB;
820
                    current = Mode.LATCHB;
821
                }
822
            } /* Rule 2 is implemented elsewhere, Rule 6 is implied */
823
 
824
            /* ISO 15417 Annex E Note 2 */
825
            /*
826
             * Convert logical length back to actual length for this block, now that we've decided
827
             * on a subset
828
             */
829
            mode_length[i] -= extraLength;
830
            totalLength += mode_length[i];
831
        }
832
 
833
        return combineSubsetBlocks(mode_type, mode_length, index_point);
834
    }
835
 
836
    /**
837
     * Modifies the specified mode and length arrays to combine adjacent modes of the same type,
838
     * returning the updated index point.
839
     */
840
    private int combineSubsetBlocks(final Mode[] mode_type, final int[] mode_length, int index_point) {
841
        /* bring together same type blocks */
842
        if (index_point > 1) {
843
            for (int i = 1; i < index_point; i++) {
844
                if (mode_type[i - 1] == mode_type[i]) {
845
                    /* bring together */
846
                    mode_length[i - 1] = mode_length[i - 1] + mode_length[i];
847
                    /* decrease the list */
848
                    for (int j = i + 1; j < index_point; j++) {
849
                        mode_length[j - 1] = mode_length[j];
850
                        mode_type[j - 1] = mode_type[j];
851
                    }
852
                    index_point--;
853
                    i--;
854
                }
855
            }
856
        }
857
        return index_point;
858
    }
859
 
860
    /** {@inheritDoc} */
861
    @Override
862
    protected int[] getCodewords() {
863
        return getPatternAsCodewords(6);
864
    }
865
}