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