View Javadoc

1   /*
2    * Copyright (C) 2007 ETH Zurich
3    *
4    * This file is part of Fosstrak (www.fosstrak.org).
5    *
6    * Fosstrak is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License version 2.1, as published by the Free Software Foundation.
9    *
10   * Fosstrak is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with Fosstrak; if not, write to the Free
17   * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   * Boston, MA  02110-1301  USA
19   */
20  
21  package org.fosstrak.epcis.captureclient;
22  
23  import java.net.URL;
24  import java.text.DecimalFormat;
25  import java.util.ArrayList;
26  import java.util.Calendar;
27  import java.util.GregorianCalendar;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.StringTokenizer;
31  import java.util.TimeZone;
32  import java.util.Map.Entry;
33  
34  import javax.swing.ImageIcon;
35  
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  
39  /**
40   * This is a helper class which encapsulates common functionality used within
41   * the capture client classes.
42   * 
43   * @author Marco Steybe
44   */
45  public class CaptureClientHelper {
46  
47      /**
48       * Miscellaneous numeric formats used in formatting.
49       */
50      public static final DecimalFormat XX_FORMAT = new DecimalFormat("00");
51      public static final DecimalFormat XXX_FORMAT = new DecimalFormat("000");
52      public static final DecimalFormat XXXX_FORMAT = new DecimalFormat("0000");
53  
54      /**
55       * The various tooltips.
56       */
57      public static final String toolTipDate = "Format is ISO 8601, i.e. YYYY-MM-DDThh:mm:ss.SSSZ";
58      public static final String toolTipUri = "URI";
59      public static final String toolTipUris = "One or multiple URIs, separated by spaces";
60      public static final String toolTipInteger = "Integer number";
61      public static final String toolTipOptional = ". This field is optional";
62      public static final String toolTipBizTransType = "Business Transaction Type";
63      public static final String toolTipBizTransID = "Business Transactio ID";
64  
65      /**
66       * The possible values for the "actions" parameter.
67       */
68      public static final String[] ACTIONS = { "ADD", "OBSERVE", "DELETE" };
69  
70      /**
71       * The four possible event types, in human readable form.
72       */
73      public static final String[] EPCIS_EVENT_NAMES = {
74              "Object event", "Aggregation event", "Quantity event", "Transaction event" };
75  
76      public enum EpcisEventType {
77  
78          ObjectEvent(0, "Object event"), AggregationEvent(1, "Aggregation event"), QuantityEvent(2, "Quantity event"),
79          TransactionEvent(3, "Transaction event");
80  
81          private int guiIndex;
82          private String guiName;
83  
84          private EpcisEventType(int guiIndex, String guiName) {
85              this.guiIndex = guiIndex;
86              this.guiName = guiName;
87          }
88  
89          public static EpcisEventType fromGuiIndex(int index) {
90              for (EpcisEventType eventType : values()) {
91                  if (eventType.guiIndex == index) {
92                      return eventType;
93                  }
94              }
95              return null;
96          }
97  
98          public static String[] guiNames() {
99              String[] guiNames = new String[values().length];
100             for (int i = 0; i < values().length; i++) {
101                 guiNames[i] = values()[i].getGuiName();
102             }
103             return guiNames;
104         }
105 
106         public String getGuiName() {
107             return guiName;
108         }
109     }
110 
111     /**
112      * Returns the time zone designator in a ISO6601-compliant format from the
113      * given <code>Calendar</code> value.
114      * 
115      * @param cal
116      *            The Calendar to be formatted.
117      * @return The time zone designator from the given Calendar.
118      */
119     public static String getTimeZone(final Calendar cal) {
120         StringBuilder buf = new StringBuilder();
121         TimeZone tz = cal.getTimeZone();
122         // determine offset of timezone from UTC (incl. daylight saving)
123         int offset = tz.getOffset(cal.getTimeInMillis());
124         int hours = Math.abs((offset / (60 * 1000)) / 60);
125         int minutes = Math.abs((offset / (60 * 1000)) % 60);
126         buf.append(offset < 0 ? '-' : '+');
127         buf.append(XX_FORMAT.format(hours));
128         buf.append(':');
129         buf.append(XX_FORMAT.format(minutes));
130         return buf.toString();
131     }
132 
133     /**
134      * Formats a <code>Calendar</code> value into an ISO8601-compliant date/time
135      * string.
136      * 
137      * @param cal
138      *            The time value to be formatted into a date/time string.
139      * @return The formatted date/time string.
140      */
141     public static String format(final Calendar cal) {
142         if (cal == null) {
143             throw new IllegalArgumentException("argument can not be null");
144         }
145 
146         // determine era and adjust year if necessary
147         int year = cal.get(Calendar.YEAR);
148         if (cal.isSet(Calendar.ERA) && cal.get(Calendar.ERA) == GregorianCalendar.BC) {
149             /**
150              * calculate year using astronomical system: year n BCE =>
151              * astronomical year -n + 1
152              */
153             year = 0 - year + 1;
154         }
155 
156         /**
157          * the format of the date/time string is: YYYY-MM-DDThh:mm:ss.SSSTZD
158          * note that we cannot use java.text.SimpleDateFormat for formatting
159          * because it can't handle years <= 0 and TZD's
160          */
161         StringBuilder buf = new StringBuilder();
162         // year ([-]YYYY)
163         buf.append(XXXX_FORMAT.format(year));
164         buf.append('-');
165         // month (MM)
166         buf.append(XX_FORMAT.format(cal.get(Calendar.MONTH) + 1));
167         buf.append('-');
168         // day (DD)
169         buf.append(XX_FORMAT.format(cal.get(Calendar.DAY_OF_MONTH)));
170         buf.append('T');
171         // hour (hh)
172         buf.append(XX_FORMAT.format(cal.get(Calendar.HOUR_OF_DAY)));
173         buf.append(':');
174         // minute (mm)
175         buf.append(XX_FORMAT.format(cal.get(Calendar.MINUTE)));
176         buf.append(':');
177         // second (ss)
178         buf.append(XX_FORMAT.format(cal.get(Calendar.SECOND)));
179         buf.append('.');
180         // millisecond (SSS)
181         buf.append(XXX_FORMAT.format(cal.get(Calendar.MILLISECOND)));
182         // time zone designator (+/-hh:mm)
183         buf.append(getTimeZone(cal));
184         return buf.toString();
185     }
186 
187     /**
188      * Implements a class that holds examples for the EPCIS Capture Interface
189      * Client.
190      * 
191      * @author David Gubler
192      */
193     public static final class ExampleEvents {
194  
195         private static List<CaptureEvent> examples = null;
196 
197         /**
198          * Sets up the examples. Add examples here if you wish.
199          */
200         private static void initEvents() {
201             examples = new ArrayList<CaptureEvent>();
202             CaptureEvent ex = new CaptureEvent();
203             ex.setDescription("DEMO 1: Item is assigned a new EPC");
204             ex.setType(0);
205             ex.setEventTime("2006-09-20T06:36:17Z");
206             ex.setEventTimeZoneOffset("+00:00");
207             ex.setAction(0);
208             ex.setBizStep("urn:epcglobal:cbv:bizstep:commissioning");
209             ex.setDisposition("urn:epcglobal:cbv:bizstep:active");
210             ex.setBizLocation("urn:epc:id:sgln:0614141.00729.loc5");
211             ex.setReadPoint("urn:epc:id:sgln:0614141.00729.rp97");
212             ex.setEpcList("urn:epc:id:sgtin:0057000.123780.7788");
213             examples.add(ex);
214 
215             ex = new CaptureEvent();
216             ex.setDescription("DEMO 2: Item is inspected for QA");
217             ex.setType(0);
218             ex.setEventTime("2006-09-20T07:33:31.116Z");
219             ex.setEventTimeZoneOffset("+00:00");
220             ex.setAction(1);
221             ex.setBizStep("urn:epcglobal:cbv:bizstep:inspecting");
222             ex.setDisposition("urn:epcglobal:cbv:bizstep:active");
223             ex.setBizLocation("urn:epc:id:sgln:0614141.00729.loc5");
224             ex.setReadPoint("urn:epc:id:sgln:0614141.00729.handheld8");
225             ex.setEpcList("urn:epc:id:sgtin:0057000.123780.7788");
226             examples.add(ex);
227 
228             ex = new CaptureEvent();
229             ex.setDescription("DEMO 3: Item is aggregated onto a pallet");
230             ex.setType(1);
231             ex.setAction(0);
232             ex.setEventTime("2006-09-20T08:55:04Z");
233             ex.setEventTimeZoneOffset("+00:00");
234             ex.setBizStep("urn:epcglobal:cbv:bizstep:packing");
235             ex.setDisposition("urn:epcglobal:cbv:disp:in_progress");
236             ex.setBizLocation("urn:epc:id:sgln:0614141.00729.loc450");
237             ex.setReadPoint("urn:epc:id:sgln:0614141.00729.rp104");
238             ex.setBizTransaction("urn:epcglobal:cbv:btt:po", "http://transaction.fosstrak.org/po/12345678");
239             ex.setParentID("urn:epc:id:sscc:0614141.1234567890");
240             ex.setChildEPCs("urn:epc:id:sgtin:0057000.123780.7788 urn:epc:id:sgtin:0057000.123430.2027 "
241                     + "urn:epc:id:sgtin:0057000.123430.2028 urn:epc:id:sgtin:0057000.123430.2029");
242             examples.add(ex);
243 
244             ex = new CaptureEvent();
245             ex.setDescription("DEMO 4: Pallet arrives at port of Kaohsiung");
246             ex.setType(0);
247             ex.setEventTime("2006-09-20T10:33:31.116Z");
248             ex.setEventTimeZoneOffset("+00:00");
249             ex.setAction(1);
250             ex.setBizStep("urn:epcglobal:cbv:bizstep:arriving");
251             ex.setDisposition("urn:epcglobal:cbv:disp:in_progress");
252             ex.setBizLocation("http://epcis.fosstrak.org/demo/loc/china/kaohsiung");
253             ex.setReadPoint("http://epcis.fosstrak.org/demo/loc/china/kaohsiung/e62");
254             ex.setEpcList("urn:epc:id:sscc:0614141.1234567890");
255             examples.add(ex);
256 
257             ex = new CaptureEvent();
258             ex.setDescription("DEMO 5: Pallet departs from port of Kaohsiung");
259             ex.setType(0);
260             ex.setEventTime("2006-09-21T13:27:08.155Z");
261             ex.setEventTimeZoneOffset("+00:00");
262             ex.setAction(1);
263             ex.setBizStep("urn:epcglobal:cbv:bizstep:departing");
264             ex.setDisposition("urn:epcglobal:cbv:disp:in_transit");
265             ex.setBizLocation("http://epcis.fosstrak.org/demo/loc/china/kaohsiung");
266             ex.setReadPoint("http://epcis.fosstrak.org/demo/loc/china/kaohsiung/b18");
267             ex.setEpcList("urn:epc:id:sscc:0614141.1234567890");
268             examples.add(ex);
269 
270             ex = new CaptureEvent();
271             ex.setDescription("Item passes a reader during manufacturing process");
272             ex.setType(0);
273             ex.setEventTime("2006-04-03T20:33:31.116Z");
274             ex.setEventTimeZoneOffset("+00:00");
275             ex.setAction(1);
276             ex.setBizStep("http://epcis.fosstrak.org/bizstep/production");
277             ex.setBizLocation("urn:epc:id:sgln:0614141.00101.loc210");
278             ex.setReadPoint("urn:epc:id:sgln:0614141.00101.rp210");
279             ex.setEpcList("urn:epc:id:sgtin:0057000.123780.3167");
280             examples.add(ex);
281 
282             ex = new CaptureEvent();
283             ex.setDescription("Two pallets are loaded onto a truck");
284             ex.setType(0);
285             ex.setEventTime("2006-05-09T21:01:44Z");
286             ex.setEventTimeZoneOffset("+00:00");
287             ex.setAction(1);
288             ex.setBizStep("urn:epcglobal:cbv:bizstep:loading");
289             ex.setDisposition("urn:epcglobal:cbv:disp:in_transit");
290             ex.setBizLocation("urn:epc:id:sgln:0614141.00101.loc209");
291             ex.setReadPoint("urn:epc:id:sgln:0614141.00101.rp53");
292             ex.setEpcList("urn:epc:id:sscc:0614141.2644895423 urn:epc:id:sscc:0614141.2644895424");
293             ex.setBizTransaction("urn:epcglobal:cbv:fmcg:btt:po", "http://transaction.example.com/po/12345678");
294             examples.add(ex);
295 
296             ex = new CaptureEvent();
297             ex.setDescription("Item arrives for repair");
298             ex.setType(0);
299             ex.setEventTime("2006-05-10T04:50:35Z");
300             ex.setEventTimeZoneOffset("+00:00");
301             ex.setAction(1);
302             ex.setBizStep("urn:epcglobal:cbv:bizstep:receiving");
303             ex.setDisposition("http://epcis.fosstrak.org/disp/in_repair");
304             ex.setBizLocation("urn:epc:id:sgln:0614141.00101.repair-center");
305             ex.setReadPoint("urn:epc:id:sgln:0614141.00101.rp77");
306             ex.setEpcList("urn:epc:id:sgtin:0034000.987650.2686");
307             examples.add(ex);
308 
309             ex = new CaptureEvent();
310             ex.setDescription("Three items are aggregated onto a barcode-labeled pallet");
311             ex.setType(1);
312             ex.setAction(0);
313             ex.setEventTime("2006-06-01T15:55:04Z");
314             ex.setEventTimeZoneOffset("+00:00");
315             ex.setBizStep("urn:epcglobal:cbv:bizstep:packing");
316             ex.setDisposition("urn:epcglobal:cbv:disp:in_progress");
317             ex.setBizLocation("urn:epc:id:sgln:0614141.00101.loc208");
318             ex.setReadPoint("urn:epc:id:sgln:0614141.00101.rp98");
319             ex.setBizTransaction("urn:epcglobal:cbv:fmcg:btt:po", "http://transaction.example.com/po/12345678");
320             ex.setBizTransaction("urn:epcglobal:cbv:btt:asn", "http://transaction.example.com/asn/1152");
321             ex.setParentID("urn:epc:id:sscc:0614141.2644895423");
322             ex.setChildEPCs("urn:epc:id:sgtin:0057000.123430.2025 urn:epc:id:sgtin:0057000.123430.2027 "
323                     + "urn:epc:id:sgtin:0057000.123430.2028");
324             examples.add(ex);
325 
326             ex = new CaptureEvent();
327             ex.setDescription("Aggregation ends at customer site");
328             ex.setType(1);
329             ex.setAction(2);
330             ex.setEventTime("2006-06-05T09:26:06Z");
331             ex.setEventTimeZoneOffset("+00:00");
332             ex.setBizLocation("urn:epc:id:sgln:0614141.82863.loc3");
333             ex.setReadPoint("urn:epc:id:sgln:0614141.82863.reader10");
334             ex.setBizTransaction("urn:epcglobal:cbv:fmcg:btt:po", "http://po.example.org/E58J3Q");
335             ex.setBizTransaction("urn:epcglobal:cbv:btt:asn", "http://transaction.example.com/asn/1152");
336             ex.setParentID("urn:epc:id:sscc:0614141.2644895423");
337             examples.add(ex);
338 
339             ex = new CaptureEvent();
340             ex.setDescription("Physical inventory count: 67 items");
341             ex.setType(2);
342             ex.setEventTime("2006-01-15T16:15:31Z");
343             ex.setEventTimeZoneOffset("+00:00");
344             ex.setBizStep("urn:epcglobal:cbv:bizstep:storing");
345             ex.setDisposition("urn:epcglobal:cbv:disp:active");
346             ex.setBizLocation("urn:epc:id:sgln:0614141.82863.loc6");
347             ex.setReadPoint("urn:epc:id:sgln:0614141.82863.loc6-virt");
348             ex.setEpcClass("urn:epc:id:sgtin:0069000.957110");
349             ex.setQuantity(67);
350             examples.add(ex);
351 
352             ex = new CaptureEvent();
353             ex.setDescription("Order changed by customer - two more objects added to transaction");
354             ex.setType(3);
355             ex.setEventTime("2006-08-18T11:53:01Z");
356             ex.setEventTimeZoneOffset("+00:00");
357             ex.setAction(0);
358             ex.setBizTransaction("urn:epcglobal:cbv:fmcg:btt:po", "http://transaction.example.com/po/6677150");
359             ex.setEpcList("urn:epc:id:sgtin:0057000.678930.5003 urn:epc:id:sgtin:0057000.678930.5004");
360             examples.add(ex);
361 
362             ex = new CaptureEvent();
363             ex.setDescription("Transaction is finished");
364             ex.setType(3);
365             ex.setEventTime("2006-08-20T07:03:51Z");
366             ex.setEventTimeZoneOffset("+00:00");
367             ex.setAction(2);
368             ex.setBizTransaction("urn:epcglobal:cbv:fmcg:btt:po", "http://transaction.example.com/po/6677150");
369             ex.setEpcList("urn:epc:id:sgtin:0057000.678930.5003 urn:epc:id:sgtin:0057000.678930.5004");
370             examples.add(ex);
371         }
372         
373         public static List<CaptureEvent> getExamples() {
374             if (examples == null) {
375                 initEvents();
376             }
377             return examples;
378         }
379     }
380 
381     /**
382      * Loads ImageIcon from either JAR or filesystem.
383      * 
384      * @param filename
385      *            The name of the file holding the image icon.
386      * @return The ImageIcon.
387      */
388     public static ImageIcon getImageIcon(final String filename) {
389         // try loading image from JAR (Web Start environment)
390         ClassLoader classLoader = CaptureClientHelper.class.getClassLoader();
391         URL url = classLoader.getResource("gui/" + filename);
392         if (url != null) {
393             return new ImageIcon(url);
394         } else {
395             // try loading image from filesystem - hack as we
396             // can be called in either Eclipse or shell environment
397             ImageIcon ii;
398             ii = new ImageIcon("./tools/capturingGUI/media/" + filename);
399             if (ii.getImageLoadStatus() != java.awt.MediaTracker.COMPLETE) {
400                 ii = new ImageIcon("./gui/" + filename);
401             }
402             return ii;
403         }
404     }
405 
406     /**
407      * Adds the given quantity value as {@code <quantity>} element to the XML
408      * document.
409      * 
410      * @param document
411      *            The Document to generate the {@code <quantity>} element from.
412      * @param root
413      *            The root Element under which the {@code <quantity>} element
414      *            should be generated.
415      * @param quantity
416      *            The value for the {@code <quantity>} element.
417      * @return <true> if the {@code <quantity>} element was successfully
418      *         created, <code>false</code> otherwise.
419      */
420     public static boolean addQuantity(final Document document, final Element root, final String quantity) {
421         Integer n = null;
422         try {
423             n = new Integer(quantity);
424         } catch (NumberFormatException e) {
425             return false;
426         }
427         return addElement(document, root, n.toString(), "quantity");
428     }
429 
430     /**
431      * Adds the given epcClass value as {@code <epcClass>} element to the XML
432      * document.
433      * 
434      * @param document
435      *            The Document to generate the {@code <epcClass>} element from.
436      * @param root
437      *            The root Element under which the {@code <epcClass>} element
438      *            should be generated.
439      * @param epcClass
440      *            The value for the {@code <epcClass>} element.
441      * @return <true> if the {@code <epcClass>} element was successfully
442      *         created, <code>false</code> otherwise.
443      */
444     public static boolean addEpcClass(final Document document, final Element root, final String epcClass) {
445         return addElement(document, root, epcClass, "epcClass");
446     }
447 
448     /**
449      * Adds the EPCs from the given list of childEPCs as {@code <epc>} elements
450      * inside an {@code <childEPCs>} element to the XML document.
451      * 
452      * @param document
453      *            The Document to generate the {@code <epc>} and {@code
454      *            <childEPCs>} element from.
455      * @param root
456      *            The root Element under which the {@code <childEPCs>} element
457      *            should be generated.
458      * @param childEPCs
459      *            A space-separated list of EPCs.
460      * @return <true> if the {@code <childEPCs>} element was successfully
461      *         created, <code>false</code> otherwise.
462      */
463     public static boolean addChildEpcList(final Document document, final Element root, final String childEPCs) {
464         if (isEmpty(childEPCs)) {
465             return false;
466         }
467         Element element = document.createElement("childEPCs");
468         StringTokenizer st = new StringTokenizer(childEPCs);
469         while (st.hasMoreTokens()) {
470             addElement(document, element, st.nextToken(), "epc");
471         }
472         root.appendChild(element);
473         return true;
474     }
475 
476     /**
477      * Adds the given mapping of business transactions ([business transaction
478      * IDs] -> [business transaction types]) as part of a {@code
479      * <bizTransactionList>} element to the XML document.
480      * 
481      * @param document
482      *            The Document to generate the {@code <bizTransactionList>}
483      *            element from.
484      * @param root
485      *            The root Element under which the {@code <bizTransactionList>}
486      *            element should be generated.
487      * @param bizTransactions
488      *            A mapping of business transaction IDs to business transaction
489      *            types.
490      * @return <true> if the {@code <bizTransactionList>} element was
491      *         successfully created, <code>false</code> otherwise.
492      */
493     public static boolean addBizTransactions(final Document document, final Element root,
494             final Map<String, String> bizTransactions) {
495         if (bizTransactions == null || bizTransactions.isEmpty() || bizTransactions.keySet().size() < 1) {
496             return false;
497         }
498         Element element = document.createElement("bizTransactionList");
499         for (Entry<String, String> bizTransEntry : bizTransactions.entrySet()) {
500             if (!isEmpty(bizTransEntry.getKey()) && !isEmpty(bizTransEntry.getValue())) {
501                 Element bizNode = document.createElement("bizTransaction");
502                 bizNode.appendChild(document.createTextNode(bizTransEntry.getKey()));
503                 bizNode.setAttribute("type", bizTransEntry.getValue());
504                 element.appendChild(bizNode);
505             }
506         }
507         root.appendChild(element);
508         return true;
509     }
510 
511     /**
512      * Adds the given bizLocation as {@code <id>} element inside a {@code
513      * <bizLocation>} element to the XML document.
514      * 
515      * @param document
516      *            The Document to generate the {@code <id>} and {@code
517      *            <bizLocation>} elements from.
518      * @param root
519      *            The root Element under which the {@code <bizLocation>} element
520      *            should be generated.
521      * @param bizLocation
522      *            The value for the {@code <id>} element.
523      * @return <true> if the {@code <bizLocation>} element was successfully
524      *         created, <code>false</code> otherwise.
525      */
526     public static boolean addBizLocation(final Document document, final Element root, final String bizLocation) {
527         if (isEmpty(bizLocation)) {
528             return false;
529         }
530         Element element = document.createElement("bizLocation");
531         addElement(document, element, bizLocation, "id");
532         root.appendChild(element);
533         return true;
534     }
535 
536     /**
537      * Adds the given readPoint as {@code <id>} element inside a {@code
538      * <readPoint>} element to the XML document.
539      * 
540      * @param document
541      *            The Document to generate the {@code <id>} and {@code
542      *            <readPoint>} elements from.
543      * @param root
544      *            The root Element under which the {@code <readPoint>} element
545      *            should be generated.
546      * @param readPoint
547      *            The value for the {@code <id>} element.
548      * @return <true> if the {@code <readPoint>} element was successfully
549      *         created, <code>false</code> otherwise.
550      */
551     public static boolean addReadPoint(final Document document, final Element root, final String readPoint) {
552         if (isEmpty(readPoint)) {
553             return false;
554         }
555         Element element = document.createElement("readPoint");
556         addElement(document, element, readPoint, "id");
557         root.appendChild(element);
558         return true;
559     }
560 
561     /**
562      * Adds the given disposition value as {@code <disposition>} element to the
563      * XML document.
564      * 
565      * @param document
566      *            The Document to generate the {@code <disposition>} element
567      *            from.
568      * @param root
569      *            The root Element under which the {@code <disposition>} element
570      *            should be generated.
571      * @param disposition
572      *            The value for the {@code <disposition>} element.
573      * @return <true> if the {@code <disposition>} element was successfully
574      *         created, <code>false</code> otherwise.
575      */
576     public static boolean addDisposition(final Document document, final Element root, String disposition) {
577         return addElement(document, root, disposition, "disposition");
578     }
579 
580     /**
581      * Adds the given bizStep value as {@code <bizStep>} element to the XML
582      * document.
583      * 
584      * @param document
585      *            The Document to generate the {@code <bizStep>} element from.
586      * @param root
587      *            The root Element under which the {@code <bizStep>} element
588      *            should be generated.
589      * @param bizStep
590      *            The value for the {@code <bizStep>} element.
591      * @return <true> if the {@code <bizStep>} element was successfully created,
592      *         <code>false</code> otherwise.
593      */
594     public static boolean addBizStep(final Document document, final Element root, final String bizStep) {
595         return addElement(document, root, bizStep, "bizStep");
596     }
597 
598     /**
599      * Adds the given action value as {@code <action>} element to the XML
600      * document.
601      * 
602      * @param document
603      *            The Document to generate the {@code <action>} element from.
604      * @param root
605      *            The root Element under which the {@code <action>} element
606      *            should be generated.
607      * @param action
608      *            The value for the {@code <action>} element.
609      * @return <true> if the {@code <action>} element was successfully created,
610      *         <code>false</code> otherwise.
611      */
612     public static boolean addAction(final Document document, final Element root, String action) {
613         return addElement(document, root, action, "action");
614     }
615 
616     /**
617      * Adds the EPCs from the given epcList as {@code <epc>} elements inside an
618      * {@code <epcList>} element to the XML document.
619      * 
620      * @param document
621      *            The Document to generate the {@code <epc>} and {@code
622      *            <epcList>} element from.
623      * @param root
624      *            The root Element under which the {@code <childEPCs>} element
625      *            should be generated.
626      * @param epcList
627      *            A space-separated list of EPCs.
628      * @return <true> if the {@code <epcList>} element was successfully created,
629      *         <code>false</code> otherwise.
630      */
631     public static boolean addEpcList(final Document document, final Element root, final String epcList) {
632         if (isEmpty(epcList)) {
633             return false;
634         }
635         Element element = document.createElement("epcList");
636         StringTokenizer st = new StringTokenizer(epcList);
637         while (st.hasMoreTokens()) {
638             addElement(document, element, st.nextToken(), "epc");
639         }
640         root.appendChild(element);
641         return true;
642     }
643 
644     /**
645      * Adds the given parentID value as {@code <parentID>} element to the XML
646      * document.
647      * 
648      * @param document
649      *            The Document to generate the {@code <parentID>} element from.
650      * @param root
651      *            The root Element under which the {@code <parentID>} element
652      *            should be generated.
653      * @param parentID
654      *            The value for the {@code <parentID>} element.
655      * @return <true> if the {@code <parentID>} element was successfully
656      *         created, <code>false</code> otherwise.
657      */
658     public static boolean addParentId(final Document document, final Element root, String parentID) {
659         return addElement(document, root, parentID, "parentID");
660     }
661 
662     /**
663      * Adds the given eventTime value as {@code <eventTime>} element to the XML
664      * document.
665      * 
666      * @param document
667      *            The Document to generate the {@code <eventTime>} element from.
668      * @param root
669      *            The root Element under which the {@code <eventTime>} element
670      *            should be generated.
671      * @param eventTime
672      *            The value for the {@code <eventTime>} element.
673      * @return <true> if the {@code <eventTime>} element was successfully
674      *         created, <code>false</code> otherwise.
675      */
676     public static boolean addEventTime(final Document document, final Element root, final String eventTime) {
677         return addElement(document, root, eventTime, "eventTime");
678     }
679 
680     /**
681      * Adds the given eventTimeZoneOffset value as {@code <eventTimeZoneOffset>}
682      * element to the XML document.
683      * 
684      * @param document
685      *            The Document to generate the {@code <eventTimeZoneOffset>}
686      *            element from.
687      * @param root
688      *            The root Element under which the {@code <eventTimeZoneOffset>}
689      *            element should be generated.
690      * @param eventTimeZoneOffset
691      *            The value for the {@code <eventTimeZoneOffset>} element.
692      * @return <true> if the {@code <eventTimeZoneOffset>} element was
693      *         successfully created, <code>false</code> otherwise.
694      */
695     public static boolean addEventTimeZoneOffset(final Document document, final Element root,
696             final String eventTimeZoneOffset) {
697         return addElement(document, root, eventTimeZoneOffset, "eventTimeZoneOffset");
698     }
699 
700     private static boolean addElement(final Document document, final Element root, final String elementValue,
701             final String elementName) {
702         if (isEmpty(elementValue)) {
703             return false;
704         }
705         Element element = document.createElement(elementName);
706         element.appendChild(document.createTextNode(elementValue));
707         root.appendChild(element);
708         return true;
709     }
710 
711     private static boolean isEmpty(String s) {
712         return s == null || s.trim().equals("");
713     }
714 }