View Javadoc

1   /*******************************************************************************
2    *  Imixs Workflow 
3    *  Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,  
4    *  http://www.imixs.com
5    *  
6    *  This program is free software; you can redistribute it and/or 
7    *  modify it under the terms of the GNU General Public License 
8    *  as published by the Free Software Foundation; either version 2 
9    *  of the License, or (at your option) any later version.
10   *  
11   *  This program is distributed in the hope that it will be useful, 
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
14   *  General Public License for more details.
15   *  
16   *  You can receive a copy of the GNU General Public
17   *  License at http://www.gnu.org/licenses/gpl.html
18   *  
19   *  Project: 
20   *  	http://www.imixs.org
21   *  	http://java.net/projects/imixs-workflow
22   *  
23   *  Contributors:  
24   *  	Imixs Software Solutions GmbH - initial API and implementation
25   *  	Ralph Soika - Software Developer
26   *******************************************************************************/
27  
28  package org.imixs.workflow;
29  
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Date;
33  import java.util.HashMap;
34  import java.util.Hashtable;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.Vector;
40  import java.util.logging.Logger;
41  
42  /**
43   * This Class defines a ValueObject to be used to exchange Datastructures used
44   * by the org.imixs.workflow Framework. Every component of this framework should
45   * use this wrapper class to easy transport workflowrelevant data between the
46   * different workflow modules. ValueObjects, particular in J2EE Applications,
47   * have the advantage to improve perfomance of remote methode calls and also
48   * enables a flexibly datastructure. A ItemCollection contains various Items
49   * (attributes). Every Item exist of a Name and a Value. Internal every Value is
50   * stored inside a Vector Class. So it is also possible to store
51   * multivalueobjects. The ItemCollection wraps the
52   * <code>java.util.Hashtable</code> Class and implements the
53   * <code>java.io.Serializable</code> Interface, so the ValeOject can also be
54   * serialised inside a remote methode call.
55   * 
56   * 
57   * @author Ralph Soika
58   * @version 2.0
59   * @see org.imixs.workflow.WorkflowManager
60   */
61  
62  public class ItemCollection implements java.io.Serializable {
63  	private static Logger logger = Logger.getLogger("org.imixs.workflow");
64  
65  	private Map hash = new Hashtable();
66  
67  	/**
68  	 * Creates a empty ItemCollection
69  	 * 
70  	 */
71  	public ItemCollection() {
72  		super();
73  	}
74  
75  	/**
76  	 * Creates a new ItemCollection and transfers all Objects of map as new
77  	 * values.
78  	 * 
79  	 * @param map
80  	 */
81  	public ItemCollection(Map map) {
82  		Iterator it = map.entrySet().iterator();
83  		while (it.hasNext()) {
84  			Map.Entry entry = (Map.Entry) it.next();
85  			this.replaceItemValue(entry.getKey().toString(), entry.getValue());
86  		}
87  	}
88  
89  	public boolean equals(Object o) {
90  		if (!(o instanceof ItemCollection))
91  			return false;
92  		return hash.equals(((ItemCollection) o).getAllItems());
93  	}
94  
95  	/**
96  	 * returns the Value of a single Item inside the ItemCollection. If the item
97  	 * has no value, this method returns an empty vector. If no item with the
98  	 * specified name exists, this method returns an empty vector. It does not
99  	 * throw an exception. The ItemName is not case sensitive. Use hasItem to
100 	 * verify the existence of an item.
101 	 * 
102 	 * @param aName
103 	 *            The name of an item.
104 	 * @return The value or values contained in the item. The data type of the
105 	 *         value depends on the data type of the item.
106 	 * 
107 	 */
108 	public List getItemValue(String aName) {
109 		aName = aName.toLowerCase();
110 		Object o = hash.get(aName);
111 		if (o == null)
112 			return new Vector();
113 		else {
114 			Vector v = (Vector) o;
115 			// scan vector for null values
116 			for (int i = 0; i < v.size(); i++) {
117 				if (v.get(i) == null)
118 					v.remove(i);
119 			}
120 			return v;
121 		}
122 	}
123 
124 	/**
125 	 * Returns the value of an item with a single text value. If the item has no
126 	 * value or the value is numeric or non text, this method returns an empty
127 	 * String. If no item with the specified name exists, this method returns an
128 	 * empty String. It does not throw an exception. If the item has multiple
129 	 * values, this method returns the first value. The ItemName is not case
130 	 * sensitive. Use hasItem to verify the existence of an item.
131 	 * 
132 	 * @param aName
133 	 *            The name of an item.
134 	 * @return The value of the item
135 	 * 
136 	 */
137 	public String getItemValueString(String aName) {
138 
139 		aName = aName.toLowerCase();
140 		List v = (List) getItemValue(aName);
141 		if (v.size() == 0)
142 			return "";
143 		else {
144 			// verify if value is null
145 			Object o = v.get(0);
146 			if (o == null)
147 				return "";
148 			else
149 				return (String) o;
150 		}
151 
152 	}
153 
154 	/**
155 	 * Returns the value of an item with a single numeric value. If the item has
156 	 * no value or the value is no Integer, or empty, this method returns 0. If
157 	 * no item with the specified name exists, this method returns 0. It does
158 	 * not throw an exception. If the item has multiple values, this method
159 	 * returns the first value. The ItemName is not case sensitive. Use hasItem
160 	 * to verify the existence of an item.
161 	 * 
162 	 * @param aName
163 	 * @return integer value
164 	 * 
165 	 */
166 	public int getItemValueInteger(String aName) {
167 		try {
168 			aName = aName.toLowerCase();
169 			Vector v = (Vector) getItemValue(aName);
170 			if (v.size() == 0)
171 				return 0;
172 
173 			String sValue = v.firstElement().toString();
174 			return new Double(sValue).intValue();
175 		} catch (NumberFormatException e) {
176 			return 0;
177 		} catch (ClassCastException e) {
178 			return 0;
179 		}
180 	}
181 
182 	/**
183 	 * Returns the value of an item with a single Date value. If the item has no
184 	 * value or the value is no Date, or empty, this method returns null. If no
185 	 * item with the specified name exists, this method returns null. It does
186 	 * not throw an exception. If the item has multiple values, this method
187 	 * returns the first value. The ItemName is not case sensitive. Use hasItem
188 	 * to verify the existence of an item.
189 	 * 
190 	 * @param aName
191 	 * @return Date value
192 	 * 
193 	 */
194 	public Date getItemValueDate(String aName) {
195 		try {
196 			aName = aName.toLowerCase();
197 			Vector v = (Vector) getItemValue(aName);
198 			if (v.size() == 0)
199 				return null;
200 
201 			Object o = v.firstElement();
202 			if (!(o instanceof Date))
203 				return null;
204 
205 			return (Date) o;
206 		} catch (ClassCastException e) {
207 			return null;
208 		}
209 	}
210 
211 	/**
212 	 * Returns the value of an item with a single numeric value. If the item has
213 	 * no value or the value is no Double, or empty, this method returns 0.0. If
214 	 * no item with the specified name exists, this method returns 0.0. It does
215 	 * not throw an exception. If the item has multiple values, this method
216 	 * returns the first value. The Itemname is not case sensetive. Use hasItem
217 	 * to verify the existence of an item.
218 	 * 
219 	 * @param aName
220 	 * @return double value
221 	 * 
222 	 */
223 	public double getItemValueDouble(String aName) {
224 		try {
225 			aName = aName.toLowerCase();
226 			Vector v = (Vector) getItemValue(aName);
227 			if (v.size() == 0)
228 				return 0.0;
229 			else
230 				return ((Double) v.firstElement()).doubleValue();
231 		} catch (ClassCastException e) {
232 			return 0.0;
233 		}
234 	}
235 
236 	/**
237 	 * Returns the boolean value of an item. If the item has no value or the
238 	 * value is no boolean, or empty, this method returns false. If no item with
239 	 * the specified name exists, this method returns false. It does not throw
240 	 * an exception. If the item has multiple values, this method returns the
241 	 * first value. The Itemname is not case sensitive. Use hasItem to verify
242 	 * the existence of an item.
243 	 * 
244 	 * @param aName
245 	 * @return boolean value
246 	 * 
247 	 */
248 	public boolean getItemValueBoolean(String aName) {
249 		try {
250 			aName = aName.toLowerCase();
251 			Vector v = (Vector) getItemValue(aName);
252 			if (v.size() == 0)
253 				return false;
254 			String sValue = v.firstElement().toString();
255 			// return new Boolean(sValue).booleanValue();
256 			return Boolean.valueOf(sValue);
257 		} catch (ClassCastException e) {
258 			return false;
259 		}
260 	}
261 
262 	/**
263 	 * Indicates whether an item exists in the document.
264 	 * 
265 	 * @param aName
266 	 *            The name of an item.
267 	 * @return true if an item with name exists in the document, false if no
268 	 *         item with name exists in the document
269 	 * 
270 	 */
271 	public boolean hasItem(String aName) {
272 		aName = aName.toLowerCase();
273 		return (hash.get(aName) != null);
274 	}
275 
276 	/**
277 	 * returns all Items of the Collection as a Map
278 	 * 
279 	 * @return Map with all Items
280 	 */
281 	public Map getAllItems() {
282 		return hash;
283 
284 	}
285 
286 	/**
287 	 * replaces the current map object. In different to the method
288 	 * replaceAllItems this method overwrites the hash object and did not copy
289 	 * the values
290 	 * 
291 	 * @param aHash
292 	 */
293 	public void setAllItems(Map aHash) {
294 		hash = aHash;
295 
296 	}
297 
298 	/**
299 	 * Replaces the value of an item. If the ItemCollection does not contain an
300 	 * item with the specified name, the method creates a new item and adds it
301 	 * to the ItemCollection. The ItemName is not case sensitive. Use hasItem to
302 	 * verify the existence of an item. All item names will be lower cased.
303 	 * 
304 	 * Each item can contain a list of values (multivalue item). If a single
305 	 * value is provided the method creates a List with one single value
306 	 * (singlevalue item).
307 	 * 
308 	 * If the value is null the method will remove the item. This is equal to
309 	 * the method call removeItem()
310 	 * 
311 	 * If the ItemValue is not serializable the item will be removed.
312 	 * 
313 	 * 
314 	 * @param itemName
315 	 *            The name of the item or items you want to replace.
316 	 * @param itemValue
317 	 *            The value of the new item. The data type of the item depends
318 	 *            upon the data type of value, and does not need to match the
319 	 *            data type of the old item.
320 	 */
321 	public void replaceItemValue(String itemName, Object itemValue) {
322 		setItemValue(itemName, itemValue, false);
323 	}
324 
325 	/**
326 	 * Appends a value to an existing item. If the ItemCollection does not
327 	 * contain an item with the specified name, the method creates a new item
328 	 * and adds it to the ItemCollection. The ItemName is not case sensitive.
329 	 * Use hasItem to verify the existence of an item. All item names will be
330 	 * lower cased.
331 	 * 
332 	 * If a value list is provided the method appends each single value.
333 	 * 
334 	 * If the value is null the method will remove the item. This is equal to
335 	 * the method call removeItem()
336 	 * 
337 	 * If the ItemValue is not serializable the item will be removed.
338 	 * 
339 	 * 
340 	 * @param itemName
341 	 *            The name of the item or items you want to replace.
342 	 * @param itemValue
343 	 *            The value of the new item. The data type of the item depends
344 	 *            upon the data type of value, and does not need to match the
345 	 *            data type of the old item.
346 	 */
347 	public void appendItemValue(String itemName, Object itemValue) {
348 		setItemValue(itemName, itemValue, true);
349 	}
350 
351 	/**
352 	 * Helper method to replace an ItemValue.
353 	 * 
354 	 * @param itemName
355 	 *            - name of the value
356 	 * @param itemValue
357 	 *            - value
358 	 * @param append
359 	 *            - true if the value should be appended to an existing list
360 	 */
361 	private void setItemValue(String itemName, Object itemValue, boolean append) {
362 		if (itemName == null)
363 			return;
364 		// lower case itemname
365 		itemName = itemName.toLowerCase();
366 
367 		// test if value is null
368 		if (itemValue == null) {
369 			// remove the item
370 			this.removeItem(itemName);
371 			return;
372 		}
373 
374 		// test if value is serializable
375 		if (!(itemValue instanceof java.io.Serializable)) {
376 			logger.warning("[ItemCollection] replaceItemValue '" + itemName
377 					+ "': Object no Serializable!");
378 			this.removeItem(itemName);
379 			return;
380 		}
381 
382 		// test if value is a list and remove null values
383 		if (itemValue instanceof List) {
384 			// scan List for null values and remove them
385 			for (int i = 0; i < ((List<?>) itemValue).size(); i++) {
386 				if (((List<?>) itemValue).get(i) == null)
387 					((List<?>) itemValue).remove(i);
388 			}
389 		} else {
390 			// create an instance of Vector
391 			Vector v = new Vector();
392 			v.addElement(itemValue);
393 			itemValue = v;
394 		}
395 
396 		// now itemValue is of instance List
397 
398 		// replace item value?
399 		if (append) {
400 			// append item value
401 			List newValueList = getItemValue(itemName);
402 			for (Object o : (List) itemValue) {
403 				newValueList.add(o);
404 			}
405 			hash.put(itemName, newValueList);
406 		} else
407 			hash.put(itemName, itemValue);
408 
409 	}
410 
411 	/**
412 	 * Replaces all items specified in the map with new items, which are
413 	 * assigned to the specified values inside the map
414 	 * 
415 	 * @param map
416 	 */
417 	public void replaceAllItems(Map map) {
418 		Iterator it = map.entrySet().iterator();
419 		while (it.hasNext()) {
420 			Map.Entry entry = (Map.Entry) it.next();
421 			replaceItemValue(entry.getKey().toString(), entry.getValue());
422 		}
423 
424 	}
425 
426 	/**
427 	 * removes a attribute from the item collection
428 	 * 
429 	 * @param name
430 	 */
431 	public void removeItem(String name) {
432 		name = name.toLowerCase();
433 		this.getAllItems().remove(name);
434 	}
435 
436 	/**
437 	 * This method adds a single file to the ItemCollection. files will be
438 	 * stored into the property $file.
439 	 * 
440 	 * @param data
441 	 *            - byte array with file data
442 	 * @param fileName
443 	 *            - name of the file attachment
444 	 * @param contentType
445 	 *            - the contenttype (e.g. 'Text/HTML')
446 	 * 
447 	 */
448 	public void addFile(byte[] data, String fileName, String contentType) {
449 		if (data != null) {
450 			Vector<Object> vectorFileInfo = null;
451 
452 			// IE includes '\' characters! so remove all these characters....
453 			if (fileName.indexOf('\\') > -1)
454 				fileName = fileName.substring(fileName.lastIndexOf('\\') + 1);
455 			if (fileName.indexOf('/') > -1)
456 				fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
457 
458 			if (contentType == null || "".equals(contentType))
459 				contentType = "application/unknown";
460 
461 			// Store files using a hashmap....
462 			HashMap mapFiles = null;
463 			List vFiles = getItemValue("$file");
464 			if (vFiles != null && vFiles.size() > 0)
465 				mapFiles = (HashMap) vFiles.get(0);
466 			else
467 				mapFiles = new HashMap();
468 
469 			// existing file will be overridden!
470 			vectorFileInfo = (Vector) mapFiles.get(fileName);
471 			vectorFileInfo = new Vector<Object>();
472 			// put file in a vector containing the byte array and also the
473 			// content type
474 			vectorFileInfo.add(contentType);
475 			vectorFileInfo.add(data);
476 			mapFiles.put(fileName, vectorFileInfo);
477 			replaceItemValue("$file", mapFiles);
478 		}
479 	}
480 
481 	/**
482 	 * This method removes a single file attachment from the BlobWorkitem
483 	 * 
484 	 */
485 	public void removeFile(String aFilename) {
486 		/* delete attachment */
487 		HashMap mapFiles = null;
488 		List vFiles = getItemValue("$file");
489 		if (vFiles != null && vFiles.size() > 0) {
490 			mapFiles = (HashMap) vFiles.get(0);
491 			mapFiles.remove(aFilename);
492 			replaceItemValue("$file", mapFiles);
493 		}
494 
495 	}
496 
497 	/**
498 	 * Returns a list of file names attached to the current BlobWorkitem. File
499 	 * Attachments can be added using the method addFile().
500 	 * 
501 	 * @return
502 	 */
503 	public String[] getFiles() {
504 		// File attachments...
505 		String[] files = new String[0];
506 
507 		HashMap mapFiles = null;
508 		List vFiles = getItemValue("$file");
509 		if (vFiles != null && vFiles.size() > 0) {
510 			mapFiles = (HashMap) vFiles.get(0);
511 			files = new String[mapFiles.entrySet().size()];
512 			Iterator iter = mapFiles.entrySet().iterator();
513 			int iFileCount = 0;
514 			while (iter.hasNext()) {
515 				Map.Entry mapEntry = (Map.Entry) iter.next();
516 				String aFileName = mapEntry.getKey().toString();
517 				files[iFileCount] = aFileName;
518 				iFileCount++;
519 			}
520 		}
521 
522 		return files;
523 	}
524 
525 	public Map getItem() {
526 		return new ItemAdapter(this);
527 	}
528 
529 	public Map getItemList() {
530 		return new ItemListAdapter(this);
531 	}
532 
533 	public Map getItemListArray() {
534 		return new ItemListArrayAdapter(this);
535 	}
536 
537 	/**
538 	 * This class helps to addapt the behavior of a singel value item to be used
539 	 * in a jsf page using a expression language like this:
540 	 * 
541 	 * #{mybean.item['txtMyItem']}
542 	 * 
543 	 * 
544 	 * @author rsoika
545 	 * 
546 	 */
547 	class ItemAdapter implements Map {
548 		ItemCollection itemCollection;
549 
550 		public ItemAdapter() {
551 			itemCollection = new ItemCollection();
552 		}
553 
554 		public ItemAdapter(ItemCollection acol) {
555 			itemCollection = acol;
556 		}
557 
558 		public void setItemCollection(ItemCollection acol) {
559 			itemCollection = acol;
560 		}
561 
562 		/**
563 		 * returns a single value out of the ItemCollection if the key does not
564 		 * exist the method will create a value automatically
565 		 */
566 		@SuppressWarnings("unchecked")
567 		public Object get(Object key) {
568 			// check if a value for this key is available...
569 			// if not create a new empty value
570 			if (!itemCollection.hasItem(key.toString()))
571 				itemCollection.replaceItemValue(key.toString(), "");
572 
573 			// return first value from vector if size >0
574 			List v = itemCollection.getItemValue(key.toString());
575 			if (v.size() > 0)
576 				return v.get(0);
577 			else
578 				// otherwise return null
579 				return null;
580 		}
581 
582 		/**
583 		 * puts a single value into the ItemCollection
584 		 */
585 		public Object put(Object key, Object value) {
586 			if (key == null)
587 				return null;
588 			itemCollection.replaceItemValue(key.toString(), value);
589 			return value;
590 		}
591 
592 		/* ############### Default methods ################# */
593 
594 		public void clear() {
595 			itemCollection.getAllItems().clear();
596 		}
597 
598 		public boolean containsKey(Object key) {
599 			return itemCollection.getAllItems().containsKey(key);
600 		}
601 
602 		public boolean containsValue(Object value) {
603 			return itemCollection.getAllItems().containsValue(value);
604 		}
605 
606 		public Set entrySet() {
607 			return itemCollection.getAllItems().entrySet();
608 		}
609 
610 		public boolean isEmpty() {
611 			return itemCollection.getAllItems().isEmpty();
612 		}
613 
614 		public Set keySet() {
615 			return itemCollection.getAllItems().keySet();
616 		}
617 
618 		public void putAll(Map m) {
619 			itemCollection.getAllItems().putAll(m);
620 
621 		}
622 
623 		public Object remove(Object key) {
624 			return itemCollection.getAllItems().remove(key);
625 		}
626 
627 		public int size() {
628 			return itemCollection.getAllItems().size();
629 		}
630 
631 		public Collection values() {
632 			return itemCollection.getAllItems().values();
633 		}
634 
635 	}
636 
637 	/**
638 	 * This class helps to addapt the behavior of a multivalue item to be used
639 	 * in a jsf page using a expression language like this:
640 	 * 
641 	 * #{mybean.item['txtMyList']}
642 	 * 
643 	 * 
644 	 * @author rsoika
645 	 * 
646 	 */
647 	class ItemListAdapter extends ItemAdapter {
648 
649 		public ItemListAdapter(ItemCollection acol) {
650 			itemCollection = acol;
651 		}
652 
653 		/**
654 		 * returns a multi value out of the ItemCollection if the key dos not
655 		 * exist the method will create a value automatical
656 		 */
657 		public Object get(Object key) {
658 			// check if a value for this key is available...
659 			// if not create a new empty value
660 			if (!itemCollection.hasItem(key.toString()))
661 				itemCollection.replaceItemValue(key.toString(), "");
662 
663 			return itemCollection.getItemValue(key.toString());
664 		}
665 
666 	}
667 
668 	class ItemListArrayAdapter extends ItemAdapter {
669 
670 		public ItemListArrayAdapter(ItemCollection acol) {
671 			itemCollection = acol;
672 		}
673 
674 		/**
675 		 * returns a multi value out of the ItemCollection if the key dos not
676 		 * exist the method will create a value automatical
677 		 */
678 		public Object get(Object key) {
679 			// check if a value for this key is available...
680 			// if not create a new empty value
681 			if (!itemCollection.hasItem(key.toString()))
682 				itemCollection.replaceItemValue(key.toString(), "");
683 			// return new ArrayList Object containing values from vector
684 			ArrayList<Object> aList = new ArrayList<Object>();
685 			Collection col = itemCollection.getItemValue(key.toString());
686 			for (Object aEntryValue : col) {
687 				aList.add(aEntryValue);
688 			}
689 			return aList;
690 
691 		}
692 
693 		/**
694 		 * puts a arraylist value into the ItemCollection
695 		 */
696 		public Object put(Object key, Object value) {
697 			if (key == null)
698 				return null;
699 
700 			// skipp null values
701 			if (value == null) {
702 				itemCollection.replaceItemValue(key.toString(), new Vector());
703 				return null;
704 			}
705 			// convert List into Vector object
706 			if (value instanceof List || value instanceof Object[]) {
707 				Vector v = new Vector();
708 				// check type of list (array and list are supported but need
709 				// to be read in different ways
710 				if (value instanceof List)
711 					for (Object aEntryValue : (List) value) {
712 						v.add(aEntryValue);
713 					}
714 				else if (value instanceof Object[])
715 					for (Object aEntryValue : (Object[]) value) {
716 						v.add(aEntryValue);
717 					}
718 				itemCollection.replaceItemValue(key.toString(), v);
719 			} else
720 				// non convertable object!
721 				itemCollection.replaceItemValue(key.toString(), value);
722 
723 			return value;
724 		}
725 	}
726 }