KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > krysalis > barcode > impl > UPCELogicImpl


1 /*
2  * $Id: UPCALogicImpl.java,v 1.3 2003/04/02 06:40:40 jmaerki Exp $
3  * ============================================================================
4  * The Krysalis Patchy Software License, Version 1.1_01
5  * Copyright (c) 2002-2003 Nicola Ken Barozzi. All rights reserved.
6  *
7  * This Licence is compatible with the BSD licence as described and
8  * approved by http://www.opensource.org/, and is based on the
9  * Apache Software Licence Version 1.1.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in
20  * the documentation and/or other materials provided with the
21  * distribution.
22  *
23  * 3. The end-user documentation included with the redistribution,
24  * if any, must include the following acknowledgment:
25  * "This product includes software developed for project
26  * Krysalis (http://www.krysalis.org/)."
27  * Alternately, this acknowledgment may appear in the software itself,
28  * if and wherever such third-party acknowledgments normally appear.
29  *
30  * 4. The names "Krysalis" and "Nicola Ken Barozzi" and
31  * "Krysalis Barcode" must not be used to endorse or promote products
32  * derived from this software without prior written permission. For
33  * written permission, please contact nicolaken@krysalis.org.
34  *
35  * 5. Products derived from this software may not be called "Krysalis",
36  * "Krysalis Barcode", nor may "Krysalis" appear in their name,
37  * without prior written permission of Nicola Ken Barozzi.
38  *
39  * 6. This software may contain voluntary contributions made by many
40  * individuals, who decided to donate the code to this project in
41  * respect of this licence, and was originally created by
42  * Jeremias Maerki <jeremias@maerki.org>.
43  *
44  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
45  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
46  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
47  * DISCLAIMED. IN NO EVENT SHALL THE KRYSALIS PROJECT OR
48  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
50  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
51  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
52  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
53  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
54  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  * ====================================================================
57  */

58 package org.krysalis.barcode.impl;
59
60 import org.krysalis.barcode.BarGroup;
61 import org.krysalis.barcode.ChecksumMode;
62 import org.krysalis.barcode.ClassicBarcodeLogicHandler;
63
64 /**
65  * This class is an implementation of the UPC-E barcode.
66  *
67  * @author Jeremias Maerki
68  */

69 public class UPCELogicImpl extends UPCEANLogicImpl {
70
71     private static final byte O = ODD_PARITY;
72     private static final byte E = EVEN_PARITY;
73
74     private static final byte[][] NUMBER_SYSTEM_0 =
75                 {{E, E, E, O, O, O},
76                  {E, E, O, E, O, O},
77                  {E, E, O, O, E, O},
78                  {E, E, O, O, O, E},
79                  {E, O, E, E, O, O},
80                  {E, O, O, E, E, O},
81                  {E, O, O, O, E, E},
82                  {E, O, E, O, E, O},
83                  {E, O, E, O, O, E},
84                  {E, O, O, E, O, E}};
85
86     /**
87      * Main constructor
88      * @param mode the checksum mode
89      */

90     public UPCELogicImpl(ChecksumMode mode) {
91         super(mode);
92     }
93     
94     private static final String JavaDoc substring(String JavaDoc s, int idx, int len) {
95         return s.substring(idx, idx + len);
96     }
97     
98     /**
99      * Compacts an UPC-A message to an UPC-E message if possible.
100      * @param msg an UPC-A message
101      * @return String the derived UPC-E message (with checksum),
102      * null if the message is a valid UPC-A message but no UPC-E representation
103      * is possible
104      */

105     public static String JavaDoc compactMessage(String JavaDoc msg) {
106         UPCALogicImpl.validateMessage(msg);
107         String JavaDoc upca = UPCALogicImpl.handleChecksum(msg, ChecksumMode.CP_AUTO);
108         final byte numberSystem = extractNumberSystem(upca);
109         if ((numberSystem != 0) && (numberSystem != 1)) {
110             return null;
111         }
112         final byte check = Byte.parseByte(upca.substring(11, 12));
113         StringBuffer JavaDoc upce = new StringBuffer JavaDoc();
114         upce.append(Byte.toString(numberSystem));
115         try {
116             String JavaDoc manufacturer = substring(upca, 1, 5);
117             String JavaDoc product = substring(upca, 6, 5);
118             String JavaDoc mtemp, ptemp;
119             //Rule 1
120
mtemp = substring(manufacturer, 2, 3);
121             ptemp = substring(product, 0, 2);
122             if (("000|100|200".indexOf(mtemp) >= 0)
123                     && ("00".equals(ptemp))) {
124                 upce.append(substring(manufacturer, 0, 2));
125                 upce.append(substring(product, 2, 3));
126                 upce.append(mtemp.charAt(0));
127             } else {
128                 //Rule 2
129
ptemp = substring(product, 0, 3);
130                 if (("300|400|500|600|700|800|900".indexOf(mtemp) >= 0)
131                         && ("000".equals(ptemp))) {
132                     upce.append(substring(manufacturer, 0, 3));
133                     upce.append(substring(product, 3, 2));
134                     upce.append("3");
135                 } else {
136                     //Rule 3
137
mtemp = substring(manufacturer, 3, 2);
138                     ptemp = substring(product, 0, 4);
139                     if (("10|20|30|40|50|60|70|80|90".indexOf(mtemp) >= 0)
140                             && ("0000".equals(ptemp))) {
141                         upce.append(substring(manufacturer, 0, 4));
142                         upce.append(substring(product, 4, 1));
143                         upce.append("4");
144                     } else {
145                         //Rule 4
146
mtemp = substring(manufacturer, 4, 1);
147                         ptemp = substring(product, 4, 1);
148                         if (!"0".equals(mtemp)
149                             && ("5|6|7|8|9".indexOf(ptemp) >= 0)) {
150                             upce.append(manufacturer);
151                             upce.append(ptemp);
152                         } else {
153                             return null;
154                         }
155                     }
156                 }
157             }
158         } catch (NumberFormatException JavaDoc nfe) {
159             return null;
160         }
161         upce.append(Byte.toString(check));
162         return upce.toString();
163     }
164
165     /**
166      * Expands an UPC-E message to an UPC-A message.
167      * @param msg an UPC-E message (7 or 8 characters)
168      * @return String the expanded UPC-A message (with checksum, 12 characters)
169      */

170     public static String JavaDoc expandMessage(String JavaDoc msg) {
171         char check = '\u0000';
172         if (msg.length() == 8) {
173             check = msg.charAt(7);
174         }
175         String JavaDoc upce = substring(msg, 0, 7);
176         final byte numberSystem = extractNumberSystem(upce);
177         if ((numberSystem != 0) && (numberSystem != 1)) {
178             throw new IllegalArgumentException JavaDoc("Invalid UPC-E message: " + msg);
179         }
180         StringBuffer JavaDoc upca = new StringBuffer JavaDoc();
181         upca.append(Byte.toString(numberSystem));
182         byte mode = Byte.parseByte(substring(upce, 6, 1));
183         if ((mode >= 0) && (mode <= 2)) {
184             upca.append(substring(upce, 1, 2));
185             upca.append(Byte.toString(mode));
186             upca.append("0000");
187             upca.append(substring(upce, 3, 3));
188         } else if (mode == 3) {
189             upca.append(substring(upce, 1, 3));
190             upca.append("00000");
191             upca.append(substring(upce, 4, 2));
192         } else if (mode == 4) {
193             upca.append(substring(upce, 1, 4));
194             upca.append("00000");
195             upca.append(substring(upce, 5, 1));
196         } else if ((mode >= 5) && (mode <= 9)) {
197             upca.append(substring(upce, 1, 5));
198             upca.append("0000");
199             upca.append(Byte.toString(mode));
200         } else {
201             //Shouldn't happen
202
throw new RuntimeException JavaDoc("Internal error");
203         }
204         String JavaDoc upcaFinished = upca.toString();
205         char expectedCheck = calcChecksum(upcaFinished);
206         if ((check != '\u0000') && (check != expectedCheck)) {
207             throw new IllegalArgumentException JavaDoc("Invalid checksum. Expected "
208                 + expectedCheck + " but was " + check);
209         }
210         return upcaFinished + expectedCheck;
211     }
212     
213     private static byte extractNumberSystem(String JavaDoc msg) {
214         return Byte.parseByte(msg.substring(0, 1));
215     }
216     
217     private String JavaDoc convertUPCAtoUPCE(String JavaDoc msg) {
218         if ((msg.length() == 11) || (msg.length() == 12)) {
219             final String JavaDoc s = compactMessage(msg);
220             if (s == null) {
221                 throw new IllegalArgumentException JavaDoc(
222                     "UPC-A message cannot be compacted to UPC-E. Message: " + msg);
223             }
224             return s;
225         }
226         return msg;
227     }
228
229     /**
230      * Validates an UPC-E message. The message can also be UPC-A in which case
231      * the message is compacted to a UPC-E message if possible. If it's not
232      * possible an IllegalArgumentException is thrown
233      * @param msg the message to validate
234      */

235     public static void validateMessage(String JavaDoc msg) {
236         UPCEANLogicImpl.validateMessage(msg);
237         if ((msg.length() < 7) || (msg.length() > 8)) {
238             throw new IllegalArgumentException JavaDoc(
239                 "Message must be 7 or 8 characters long. Message: " + msg);
240         }
241         byte numberSystem = extractNumberSystem(msg);
242         if ((numberSystem < 0) || (numberSystem > 1)) {
243             throw new IllegalArgumentException JavaDoc(
244                 "Valid number systems for UPC-E are 0 or 1. Found: "
245                     + numberSystem);
246         }
247     }
248     
249     private String JavaDoc handleChecksum(String JavaDoc msg) {
250         ChecksumMode mode = getChecksumMode();
251         if (mode == ChecksumMode.CP_AUTO) {
252             if (msg.length() == 7) {
253                 mode = ChecksumMode.CP_ADD;
254             } else if (msg.length() == 8) {
255                 mode = ChecksumMode.CP_CHECK;
256             } else {
257                 //Shouldn't happen because of validateMessage
258
throw new RuntimeException JavaDoc("Internal error");
259             }
260         }
261         if (mode == ChecksumMode.CP_ADD) {
262             if (msg.length() != 7) {
263                 throw new IllegalArgumentException JavaDoc(
264                     "Message must be 7 characters long");
265             }
266             return msg + expandMessage(msg).charAt(11);
267         } else if (mode == ChecksumMode.CP_CHECK) {
268             if (msg.length() != 8) {
269                 throw new IllegalArgumentException JavaDoc(
270                     "Message must be 8 characters long");
271             }
272             char check = msg.charAt(7);
273             char expected = expandMessage(msg).charAt(11);
274             if (check != expected) {
275                 throw new IllegalArgumentException JavaDoc(
276                     "Checksum is bad (" + check + "). Expected: " + expected);
277             }
278             return msg;
279         } else if (mode == ChecksumMode.CP_IGNORE) {
280             if (msg.length() != 8) {
281                 throw new IllegalArgumentException JavaDoc(
282                     "Message must be 8 characters long");
283             }
284             return msg;
285         } else {
286             throw new UnsupportedOperationException JavaDoc(
287                 "Unknown checksum mode: " + mode);
288         }
289     }
290     
291     private byte selectCharset(byte check, byte numberSystem, int position) {
292         byte charset = NUMBER_SYSTEM_0[check][position];
293         if (numberSystem == 1) {
294             //Number System 1 is inversion of Number System 0
295
if (charset == ODD_PARITY) {
296                 charset = EVEN_PARITY;
297             } else {
298                 charset = ODD_PARITY;
299             }
300         }
301         return charset;
302     }
303     
304     /**
305      * Generates a UPC-E right guard.
306      * @param logic the logic handler to receive generated events
307      */

308     protected void drawUPCERightGuard(ClassicBarcodeLogicHandler logic) {
309         //draw guard bars 010101
310
logic.startBarGroup(BarGroup.UPC_EAN_GUARD, null);
311         logic.addBar(false, 1);
312         logic.addBar(true, 1);
313         logic.addBar(false, 1);
314         logic.addBar(true, 1);
315         logic.addBar(false, 1);
316         logic.addBar(true, 1);
317         logic.endBarGroup();
318     }
319
320     /** @see org.krysalis.barcode.impl.UPCEANLogicImpl */
321     public void generateBarcodeLogic(ClassicBarcodeLogicHandler logic, String JavaDoc msg) {
322         String JavaDoc supp = retrieveSupplemental(msg);
323         String JavaDoc s = removeSupplemental(msg);
324         s = convertUPCAtoUPCE(s);
325         validateMessage(s);
326         s = handleChecksum(s);
327
328         String JavaDoc canonicalMessage = s;
329         if (supp != null) {
330             canonicalMessage = canonicalMessage + "+" + supp;
331         }
332         logic.startBarcode(canonicalMessage);
333         
334         //Left guard
335
drawSideGuard(logic);
336
337         //Number system character
338
final byte numberSystem = extractNumberSystem(msg);
339         logic.startBarGroup(BarGroup.UPC_EAN_LEAD, Byte.toString(numberSystem));
340         //No bars, number system is implicitly encoded
341
logic.endBarGroup();
342
343         //Checksum
344
final byte check = Byte.parseByte(s.substring(7, 8));
345         
346         logic.startBarGroup(BarGroup.UPC_EAN_GROUP, s.substring(1, 7));
347         
348         //First five data characters
349
for (int i = 1; i < 7; i++) {
350             final byte charset = selectCharset(check, numberSystem, i - 1);
351             encodeChar(logic, s.charAt(i), charset);
352         }
353         
354         logic.endBarGroup();
355
356         //Checksum
357
logic.startBarGroup(BarGroup.UPC_EAN_CHECK, Byte.toString(check));
358         //No bars, checksum is implicitly encoded
359
logic.endBarGroup();
360
361         //Right guard
362
drawUPCERightGuard(logic);
363         
364         //Optional Supplemental
365
if (supp != null) {
366             drawSupplemental(logic, supp);
367         }
368         logic.endBarcode();
369     }
370
371 }
372
Popular Tags