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