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, 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.backend;
16
 
17
import static uk.org.okapibarcode.util.Arrays.positionOf;
18
 
19
/**
20
 * <p>
21
 * Implements Code 11 bar code symbology.
22
 *
23
 * <p>
24
 * Code 11 can encode any length string consisting of the digits 0-9 and the dash character (-). One
25
 * or two modulo-11 check digits are calculated.
26
 *
27
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
28
 * @author Daniel Gredler
29
 */
30
public class Code11 extends Symbol {
31
 
32
    private static final String[] CODE_11_TABLE = { "111121", "211121", "121121", "221111", "112121", "212111", "122111", "111221", "211211", "211111", "112111" };
33
 
34
    private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' };
35
 
36
    /** Ratio of wide bar width to narrow bar width. */
37
    private double moduleWidthRatio = 2;
38
 
39
    /** The number of check digits to calculate ({@code 1} or {@code 2}). */
40
    private int checkDigitCount = 2;
41
 
42
    /** Optional start delimiter to be shown in the human-readable text. */
43
    private Character startDelimiter;
44
 
45
    /** Optional stop delimiter to be shown in the human-readable text. */
46
    private Character stopDelimiter;
47
 
48
    /**
49
     * Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
50
     * {@code 2} and {@code 3}. The default value is {@code 2}.
51
     *
52
     * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
53
     */
54
    public void setModuleWidthRatio(final double moduleWidthRatio) {
55
        this.moduleWidthRatio = moduleWidthRatio;
56
    }
57
 
58
    /**
59
     * Returns the ratio of wide bar width to narrow bar width.
60
     *
61
     * @return the ratio of wide bar width to narrow bar width
62
     */
63
    public double getModuleWidthRatio() {
64
        return this.moduleWidthRatio;
65
    }
66
 
67
    /**
68
     * Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is
69
     * {@code 2}.
70
     *
71
     * @param checkDigitCount the number of check digits to calculate
72
     */
73
    public void setCheckDigitCount(final int checkDigitCount) {
74
        if (checkDigitCount < 1 || checkDigitCount > 2) {
75
            throw new IllegalArgumentException("Check digit count must be 1 or 2.");
76
        }
77
        this.checkDigitCount = checkDigitCount;
78
    }
79
 
80
    /**
81
     * Returns the number of check digits to calculate (1 or 2).
82
     *
83
     * @return the number of check digits to calculate
84
     */
85
    public int getCheckDigitCount() {
86
        return this.checkDigitCount;
87
    }
88
 
89
    /**
90
     * Sets an optional start delimiter to be shown in the human-readable text (defaults to
91
     * <code>null</code>).
92
     *
93
     * @param startDelimiter an optional start delimiter to be shown in the human-readable text
94
     */
95
    public void setStartDelimiter(final Character startDelimiter) {
96
        this.startDelimiter = startDelimiter;
97
    }
98
 
99
    /**
100
     * Returns the optional start delimiter to be shown in the human-readable text.
101
     *
102
     * @return the optional start delimiter to be shown in the human-readable text
103
     */
104
    public Character getStartDelimiter() {
105
        return this.startDelimiter;
106
    }
107
 
108
    /**
109
     * Sets an optional stop delimiter to be shown in the human-readable text (defaults to
110
     * <code>null</code>).
111
     *
112
     * @param stopDelimiter an optional stop delimiter to be shown in the human-readable text
113
     */
114
    public void setStopDelimiter(final Character stopDelimiter) {
115
        this.stopDelimiter = stopDelimiter;
116
    }
117
 
118
    /**
119
     * Returns the optional stop delimiter to be shown in the human-readable text.
120
     *
121
     * @return the optional stop delimiter to be shown in the human-readable text
122
     */
123
    public Character getStopDelimiter() {
124
        return this.stopDelimiter;
125
    }
126
 
127
    /** {@inheritDoc} */
128
    @Override
129
    protected void encode() {
130
 
131
        if (!this.content.matches("[0-9-]+")) {
132
            throw new OkapiException("Invalid characters in input");
133
        }
134
 
135
        String horizontalSpacing = "112211";
136
        String humanReadable = this.content;
137
        final int length = this.content.length();
138
        final int[] weight = new int[length + 1];
139
 
140
        for (int i = 0; i < length; i++) {
141
            final char c = this.content.charAt(i);
142
            weight[i] = positionOf(c, CHARACTER_SET);
143
            horizontalSpacing += CODE_11_TABLE[weight[i]];
144
        }
145
 
146
        final int checkDigitC = getCheckDigitC(weight, length);
147
        horizontalSpacing += CODE_11_TABLE[checkDigitC];
148
        humanReadable += CHARACTER_SET[checkDigitC];
149
        infoLine("Check Digit C: " + checkDigitC);
150
 
151
        if (this.checkDigitCount == 2) {
152
            weight[length] = checkDigitC;
153
            final int checkDigitK = getCheckDigitK(weight, length + 1);
154
            horizontalSpacing += CODE_11_TABLE[checkDigitK];
155
            humanReadable += CHARACTER_SET[checkDigitK];
156
            infoLine("Check Digit K: " + checkDigitK);
157
        }
158
 
159
        horizontalSpacing += "112211";
160
 
161
        this.readable = humanReadable;
162
        if (this.startDelimiter != null) {
163
            this.readable = this.startDelimiter + this.readable;
164
        }
165
        if (this.stopDelimiter != null) {
166
            this.readable = this.readable + this.stopDelimiter;
167
        }
168
 
169
        this.pattern = new String[] { horizontalSpacing };
170
        this.row_count = 1;
171
        this.row_height = new int[] { -1 };
172
    }
173
 
174
    private static int getCheckDigitC(final int[] weight, final int length) {
175
        int countC = 0;
176
        int weightC = 1;
177
        for (int i = length - 1; i >= 0; i--) {
178
            countC += weightC * weight[i];
179
            weightC++;
180
            if (weightC > 10) {
181
                weightC = 1;
182
            }
183
        }
184
        return countC % 11;
185
    }
186
 
187
    private static int getCheckDigitK(final int[] weight, final int length) {
188
        int countK = 0;
189
        int weightK = 1;
190
        for (int i = length - 1; i >= 0; i--) {
191
            countK += weightK * weight[i];
192
            weightK++;
193
            if (weightK > 9) {
194
                weightK = 1;
195
            }
196
        }
197
        return countK % 11;
198
    }
199
 
200
    /** {@inheritDoc} */
201
    @Override
202
    protected double getModuleWidth(final int originalWidth) {
203
        if (originalWidth == 1) {
204
            return 1;
205
        } else {
206
            return this.moduleWidthRatio;
207
        }
208
    }
209
 
210
    /** {@inheritDoc} */
211
    @Override
212
    protected int[] getCodewords() {
213
        return getPatternAsCodewords(6);
214
    }
215
}