View Javadoc

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