1 /**
2 * This library is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License (LGPL) as
4 * published by the Free Software Foundation; either version 3.0 of the
5 * License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 */
12
13 /**
14 * Title: JBarcodeBean
15 * Description: Barcode JavaBeans Component
16 * Copyright: Copyright (C) 2004
17 * Company: Dafydd Walters
18 */
19 package net.sourceforge.jbarcodebean.model;
20
21 import net.sourceforge.jbarcodebean.BarcodeElement;
22 import net.sourceforge.jbarcodebean.BarcodeException;
23 import net.sourceforge.jbarcodebean.EncodedBarcode;
24
25 /**
26 * This abstract class, which implements the {@link BarcodeStrategy} interface,
27 * provides a basic implementation that is subclassed by all the concrete
28 * classes that provide Code39, Interleaved Code25, Codabar, MSI and all
29 * derivative encoding strategies.
30 */
31 public abstract class AbstractBarcodeStrategy implements BarcodeStrategy {
32
33 /**
34 * Subclasses implement this method to return an array of
35 * {@link AbstractBarcodeStrategy.CharacterCode CharacterCode}
36 * objects, representing all possible encodings of bars and spaces for
37 * every encodable character.
38 *
39 * @return An array of {@link AbstractBarcodeStrategy.CharacterCode CharacterCode}
40 * objects, one for each possible character
41 * that can be encoded using this strategy.
42 */
43 protected abstract CharacterCode[] getCodes();
44
45 /**
46 * Subclasses implement this method to calculate the checksum from the text
47 * to encode, and return a String containing the text with the checksum
48 * included.
49 *
50 * @param text The text to encode (after preprocessing - see
51 * {@link AbstractBarcodeStrategy#preprocess}).
52 *
53 * @return A String containing the text passed into the method, augmented with
54 * the checksum. For barcode types that don't support a checksum, this would
55 * simply be the text passed to the method.
56 */
57 protected abstract String augmentWithChecksum(String text) throws BarcodeException;
58
59 /**
60 * Subclasses implement this method to perform any preprocessing necessary on the
61 * original text to encode. The result of this method is the string that gets
62 * passed to the {@link AbstractBarcodeStrategy#augmentWithChecksum} method.
63 *
64 * @param text The raw text to encode.
65 *
66 * @return The string after preprocessing. If no preprocessing is required,
67 * the String passed to the method is returned.
68 *
69 * @throws BarcodeException Typically caused by passing in
70 * a String containing illegal characters (characters that cannot be encoded in
71 * this type of barcode).
72 */
73 protected abstract String preprocess(String text) throws BarcodeException;
74
75 /**
76 * Subclasses must implement this method to return <tt>true</tt> or <tt>false</tt>
77 * depending on whether the barcode type is <i>interleaved</i>.
78 *
79 * @return <tt>true</tt> if barcode type is interleaved, <tt>false</tt> if it is not.
80 */
81 protected abstract boolean isInterleaved();
82
83 /**
84 * Subclasses implement this method to return the start sentinel character.
85 *
86 * @return The character, which when encoded into bars and spaces, appears on
87 * the left edge of every barcode (immediately after the left margin).
88 */
89 protected abstract char getStartSentinel();
90
91 /**
92 * Subclasses implement this method to return the stop sentinel character.
93 *
94 * @return The character, which when encoded into bars and spaces, appears on
95 * the right edge of every barcode (just before the right margin).
96 */
97 protected abstract char getStopSentinel();
98
99 /**
100 * Sublclasses implement this method to return the width of the whitespace
101 * that must appear on each side of the barcode.
102 *
103 * @return The space that must appear on the left and right sides of the barcode
104 * expressed as a multiple of the narrowest bar width.
105 */
106 protected abstract byte getMarginWidth();
107
108 /**
109 * Subclasses implement this method to return the text which appears
110 * below the barcode.
111 *
112 * @param text The raw text to encode.
113 *
114 * @return A String containing the text that will appear beneath the barcode.
115 */
116 protected abstract String getBarcodeLabelText(String text);
117
118 /**
119 * Subclasses implement this method to perform any postprocessing required
120 * to the text after including the checksum.
121 *
122 * @param text String to process (returned by
123 * {@link AbstractBarcodeStrategy#augmentWithChecksum}).
124 *
125 * @return String after postprocessing. If no postprocessing is required,
126 * the String passed to the method is returned.
127 */
128 protected abstract String postprocess(String text);
129
130 /**
131 * This implementation carries out the following steps:
132 * <ul>
133 * <li>Call {@link AbstractBarcodeStrategy#preprocess}</li>
134 * <li>Call {@link AbstractBarcodeStrategy#augmentWithChecksum} to add in the checksum</li>
135 * <li>Call {@link AbstractBarcodeStrategy#postprocess}</li>
136 * <li>Adds in the start and end sentinels</li>
137 * <li>Use the CharacterCode array returned by getCodes to encode the text into a barcode</li>
138 * <li>Insert left and right margins</li>
139 * <li>Return EncodedBarcode object</li>
140 * </ul>
141 *
142 * @param textToEncode The raw text to encode.
143 * @param checked True if a checksum is to be calculated, False if not.
144 *
145 * @return The fully encoded barcode, represented as bars and spaces, wrapped
146 * in a {@link EncodedBarcode} object.
147 *
148 * @throws BarcodeException Typically caused by passing in
149 * a String containing illegal characters (characters that cannot be encoded in
150 * this type of barcode).
151 */
152 public EncodedBarcode encode(String textToEncode, boolean checked) throws BarcodeException {
153
154 String text;
155
156 text = preprocess(textToEncode);
157 text = checked ? augmentWithChecksum(text) : text;
158 text = postprocess(text);
159
160 // Simple to check to ensure start and end characters are not present in the
161 // text to be encoded.
162 if (((getStartSentinel() != 0xffff && text.indexOf(getStartSentinel()) >= 0)) ||
163 (text.indexOf(getStopSentinel())) >= 0) {
164 throw new BarcodeException("Invalid character in barcode");
165 }
166
167 if (getStartSentinel() != 0xffff) {
168 text = getStartSentinel() + text;
169 }
170
171 text += getStopSentinel();
172
173 int size = computeSize(text);
174 BarcodeElement[] elements = new BarcodeElement[size];
175
176 // Margin
177 elements[0] = new BarcodeElement(BarcodeElement.TYPE_SPACE, getMarginWidth());
178
179 int len = text.length();
180 int j = 1;
181 for(int i = 0; i < len; i++) {
182 char ch = text.charAt(i);
183 CharacterCode cc = getCharacterCode(ch);
184 if (cc == null) {
185 throw new BarcodeException("Invalid character in barcode");
186 }
187 for (int k = 0; k < cc.widths.length; k++) {
188 int width = cc.widths[k];
189 int type = ((j % 2) == 0) ? BarcodeElement.TYPE_SPACE : BarcodeElement.TYPE_BAR;
190 elements[j] = new BarcodeElement(type, width);
191 if (isInterleaved() && ch != getStartSentinel() && ch != getStopSentinel()) {
192 j += 2;
193 } else {
194 j++;
195 }
196 }
197 if (isInterleaved() && ch != getStartSentinel() && ch != getStopSentinel()) {
198 if (i % 2 == 1) {
199 j -= (cc.widths.length * 2 - 1);
200 } else {
201 j -= 1;
202 }
203 }
204 }
205
206 elements[j] = new BarcodeElement(BarcodeElement.TYPE_SPACE, getMarginWidth());
207 j++;
208
209 if (j != size) {
210 throw new BarcodeException("Unexpected barcode size");
211 }
212
213 return new EncodedBarcode(elements, getBarcodeLabelText(textToEncode));
214 }
215
216 /**
217 * Computes the length of the barcode (in bar/space modules) based on the
218 * text to encode.
219 *
220 * @param text The text to encode including any check digit,
221 * start and end sentinels.
222 *
223 * @return The number of module segments in this barcode (including margins).
224 *
225 * @throws BarcodeException Typically
226 * occurs if attempting to encode invalid characters.
227 */
228 protected int computeSize(String text) throws BarcodeException {
229 int size = 0;
230 int l = text.length();
231 for(int i = 0; i < l; i++) {
232 char ch = text.charAt(i);
233 CharacterCode cc = getCharacterCode(ch);
234 if (cc == null) {
235 throw new BarcodeException("Invalid character in barcode");
236 }
237 size = size + cc.widths.length;
238 }
239 size += 2; // Margins
240 return size;
241 }
242
243 /**
244 * Looks for the specified character to encode in the CharacterCode array
245 * returned by the {@link AbstractBarcodeStrategy#getCodes} method.
246 *
247 * @param character The character to encode.
248 *
249 * @return CharacterCode The element in the CharacterCode array (returned by
250 * getCodes) that corresponds to the character passed to the method.
251 */
252 protected CharacterCode getCharacterCode(char character) {
253 CharacterCode[] codes = getCodes();
254 for (int i = 0; i < codes.length; i ++) {
255 if (codes[i].character == character) {
256 return codes[i];
257 }
258 }
259 return null;
260 }
261
262 /**
263 * Looks for an entry in the CharacterCode array
264 * returned by the {@link AbstractBarcodeStrategy#getCodes} method,
265 * by its <tt>check</tt> attribute.
266 *
267 * @param check The check attribute of the character being sought.
268 *
269 * @return CharacterCode The element in the CharacterCode array (returned by
270 * getCodes) that corresponds to the character whose check attribute was passed
271 * to the method.
272 */
273 protected CharacterCode getCharacterCode(int check) {
274 CharacterCode[] codes = getCodes();
275 for (int i = 0; i < codes.length; i ++) {
276 if (codes[i].check == check) {
277 return codes[i];
278 }
279 }
280 return null;
281 }
282
283 /**
284 * Inner class representing a character and its barcode encoding.
285 */
286 public static class CharacterCode {
287
288 /** The character that is encoded */
289 public char character;
290 /** The widths of the modules (bars and spaces) of this encoded character */
291 public byte[] widths;
292 /** The check digit corresponding to this character, used in checksum calculations */
293 public int check;
294
295 /** Constructor which fully initializes the properties of the object. */
296 public CharacterCode(char character, byte[] widths, int check) {
297 this.character = character;
298 this.widths = widths;
299 this.check = check;
300 }
301 }
302 }
303
304
305