001 /**
002 * This library is free software; you can redistribute it and/or modify it
003 * under the terms of the GNU Lesser General Public License (LGPL) as
004 * published by the Free Software Foundation; either version 3.0 of the
005 * License, or (at your option) any later version.
006 *
007 * This library is distributed in the hope that it will be useful, but
008 * WITHOUT ANY WARRANTY; without even the implied warranty of
009 * MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. See the GNU
010 * Lesser General Public License for more details.
011 */
012
013 /**
014 * Title: JBarcodeBean
015 * Description: Barcode JavaBeans Component
016 * Copyright: Copyright (C) 2004
017 * Company: Dafydd Walters
018 */
019 package net.sourceforge.jbarcodebean.model;
020
021 import net.sourceforge.jbarcodebean.BarcodeElement;
022 import net.sourceforge.jbarcodebean.BarcodeException;
023 import net.sourceforge.jbarcodebean.EncodedBarcode;
024
025 /**
026 * This abstract class, which implements the {@link BarcodeStrategy} interface,
027 * provides a basic implementation that is subclassed by all the concrete
028 * classes that provide Code39, Interleaved Code25, Codabar, MSI and all
029 * derivative encoding strategies.
030 */
031 public abstract class AbstractBarcodeStrategy implements BarcodeStrategy {
032
033 /**
034 * Subclasses implement this method to return an array of
035 * {@link AbstractBarcodeStrategy.CharacterCode CharacterCode}
036 * objects, representing all possible encodings of bars and spaces for
037 * every encodable character.
038 *
039 * @return An array of {@link AbstractBarcodeStrategy.CharacterCode CharacterCode}
040 * objects, one for each possible character
041 * that can be encoded using this strategy.
042 */
043 protected abstract CharacterCode[] getCodes();
044
045 /**
046 * Subclasses implement this method to calculate the checksum from the text
047 * to encode, and return a String containing the text with the checksum
048 * included.
049 *
050 * @param text The text to encode (after preprocessing - see
051 * {@link AbstractBarcodeStrategy#preprocess}).
052 *
053 * @return A String containing the text passed into the method, augmented with
054 * the checksum. For barcode types that don't support a checksum, this would
055 * simply be the text passed to the method.
056 */
057 protected abstract String augmentWithChecksum(String text) throws BarcodeException;
058
059 /**
060 * Subclasses implement this method to perform any preprocessing necessary on the
061 * original text to encode. The result of this method is the string that gets
062 * passed to the {@link AbstractBarcodeStrategy#augmentWithChecksum} method.
063 *
064 * @param text The raw text to encode.
065 *
066 * @return The string after preprocessing. If no preprocessing is required,
067 * the String passed to the method is returned.
068 *
069 * @throws BarcodeException Typically caused by passing in
070 * a String containing illegal characters (characters that cannot be encoded in
071 * this type of barcode).
072 */
073 protected abstract String preprocess(String text) throws BarcodeException;
074
075 /**
076 * Subclasses must implement this method to return <tt>true</tt> or <tt>false</tt>
077 * depending on whether the barcode type is <i>interleaved</i>.
078 *
079 * @return <tt>true</tt> if barcode type is interleaved, <tt>false</tt> if it is not.
080 */
081 protected abstract boolean isInterleaved();
082
083 /**
084 * Subclasses implement this method to return the start sentinel character.
085 *
086 * @return The character, which when encoded into bars and spaces, appears on
087 * the left edge of every barcode (immediately after the left margin).
088 */
089 protected abstract char getStartSentinel();
090
091 /**
092 * Subclasses implement this method to return the stop sentinel character.
093 *
094 * @return The character, which when encoded into bars and spaces, appears on
095 * the right edge of every barcode (just before the right margin).
096 */
097 protected abstract char getStopSentinel();
098
099 /**
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