View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common 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
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.util;
21  
22  import java.io.Serializable;
23  import java.lang.reflect.Array;
24  
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.ListIterator;
29  import java.util.NoSuchElementException;
30  
31  
32  /**
33   * A simple resizable threadsafe array. Offers minimal capability and overhead
34   * for all operations except <code>toArray(Class)</code>. The main cost of the
35   * array maintainance is in the addition and removal of the elements. The
36   * potential traversals are very fast,  because they can occur outside the
37   * synchronized blocks of code through normal arrays.  The arrays returned by
38   * the <code>toArray</code> methods are of the correct runtime type. If, for
39   * example, the type of the list was specified to be <code>X</code>, the
40   * following cast is permitted:
41   * 
42   * <p>
43   * <code>X[] array = (X[])list.toArray()</code>
44   * </p>
45   * Only one type cast is thus needed to obtain the final array for traversals.
46   *
47   * @author <a href="mailto:gasper.tkacik@cosylab.com">Gasper Tkacik</a>
48   * @author <a href="mailto:gasper.pajor@cosylab.com">Gasper Pajor</a>
49   * @version $id$
50   */
51  public class ObjectList implements List, Serializable
52  {
53  	private static final long serialVersionUID = 2766482196760164880L;
54  	private Class type = null;
55  	private Object[] array = null;
56  
57  	private class ArrayIterator implements ListIterator
58  	{
59  		private Object[] array;
60  		private int index = 0;
61  
62  		/**
63  		 * Creates a new ArrayIterator object.
64  		 *
65  		 * @param o the array for which the iterator is created
66  		 * @param index DOCUMENT ME!
67  		 *
68  		 * @throws NullPointerException when object is null
69  		 */
70  		public ArrayIterator(Object[] o, int index)
71  		{
72  			if (o == null) {
73  				throw new NullPointerException();
74  			}
75  
76  			array = o;
77  			this.index = index;
78  		}
79  
80  		/* (non-Javadoc)
81  		 * @see java.util.Iterator#hasNext()
82  		 */
83  		public boolean hasNext()
84  		{
85  			return index < array.length;
86  		}
87  
88  		/* (non-Javadoc)
89  		 * @see java.util.Iterator#next()
90  		 */
91  		public Object next()
92  		{
93  			if (index == array.length) {
94  				throw new NoSuchElementException();
95  			}
96  
97  			return array[index++];
98  		}
99  
100 		/**
101 		 * DOCUMENT ME!
102 		 *
103 		 * @throws UnsupportedOperationException when invoked
104 		 *
105 		 * @see java.util.Iterator#remove()
106 		 * @deprecated uniplemented
107 		 */
108 		public void remove()
109 		{
110 			throw new UnsupportedOperationException();
111 		}
112 
113 		/* (non-Javadoc)
114 		 * @see java.util.ListIterator#nextIndex()
115 		 */
116 		public int nextIndex()
117 		{
118 			if (index == array.length) {
119 				throw new NoSuchElementException();
120 			}
121 
122 			return index;
123 		}
124 
125 		/* (non-Javadoc)
126 		 * @see java.util.ListIterator#previousIndex()
127 		 */
128 		public int previousIndex()
129 		{
130 			if (index == 0) {
131 				throw new NoSuchElementException();
132 			}
133 
134 			return index - 1;
135 		}
136 
137 		/* (non-Javadoc)
138 		 * @see java.util.ListIterator#hasPrevious()
139 		 */
140 		public boolean hasPrevious()
141 		{
142 			return index > 0;
143 		}
144 
145 		/* (non-Javadoc)
146 		 * @see java.util.ListIterator#previous()
147 		 */
148 		public Object previous()
149 		{
150 			if (index == 0) {
151 				throw new NoSuchElementException();
152 			}
153 
154 			index--;
155 
156 			return array[index];
157 		}
158 
159 		/* (non-Javadoc)
160 		 * @see java.util.ListIterator#add(java.lang.Object)
161 		 */
162 		public void add(Object o)
163 		{
164 			throw new UnsupportedOperationException();
165 		}
166 
167 		/**
168 		 * DOCUMENT ME!
169 		 *
170 		 * @param o DOCUMENT ME!
171 		 *
172 		 * @throws UnsupportedOperationException when invoked
173 		 *
174 		 * @see java.util.ListIterator#set(java.lang.Object)
175 		 * @deprecated uniplemented
176 		 */
177 		public void set(Object o)
178 		{
179 			throw new UnsupportedOperationException();
180 		}
181 	}
182 
183 	/**
184 	 * Creates a new ObjectList object.
185 	 *
186 	 * @param type the type of objects this list will hold
187 	 *
188 	 * @throws NullPointerException if the type is <code>null</code>
189 	 */
190 	public ObjectList(Class type)
191 	{
192 		super();
193 
194 		if (type == null) {
195 			throw new NullPointerException("type");
196 		}
197 
198 		this.type = type;
199 		array = (Object[])Array.newInstance(type, 0);
200 	}
201 
202 	/**
203 	 * Adds an object to this list. This list can contain multiple entries of
204 	 * the same object.
205 	 *
206 	 * @param o object to add to the collection
207 	 * @param index where the object should be inserted
208 	 *
209 	 * @deprecated replaced by {@link #add(int, Object)}
210 	 */
211 	public void add(Object o, int index)
212 	{
213 		add(index, o);
214 	}
215 
216 	/**
217 	 * Adds an object to this list. This list can contain multiple entries of
218 	 * the same object.
219 	 *
220 	 * @param o object to add to the collection
221 	 *
222 	 * @return true
223 	 *
224 	 * @see java.util.List#add(Object)
225 	 */
226 	public boolean add(Object o)
227 	{
228 		add(o, array.length);
229 
230 		return true;
231 	}
232 
233 	/**
234 	 * Removes an element from the list. If there is more than one reference to
235 	 * the same object in the array, this method will remove only one
236 	 * reference.
237 	 *
238 	 * @param o element to be removed from this list, if present.
239 	 *
240 	 * @return <tt>true</tt> if this list contained the specified element.
241 	 *
242 	 * @see java.util.List#remove(java.lang.Object)
243 	 */
244 	public boolean remove(Object o)
245 	{
246 		if (o == null) {
247 			return false;
248 		}
249 
250 		if (!type.isAssignableFrom(o.getClass())) {
251 			return false;
252 		}
253 
254 		synchronized (this) {
255 			int index = -1;
256 
257 			for (int i = 0; i < array.length; i++) {
258 				if (array[i] == o) {
259 					index = i;
260 
261 					break;
262 				}
263 			}
264 
265 			if (index == -1) {
266 				return false;
267 			}
268 
269 			int length = array.length;
270 			Object els = Array.newInstance(type, length - 1);
271 			System.arraycopy(array, 0, els, 0, index);
272 			System.arraycopy(array, index + 1, els, index, length - index - 1);
273 			array = (Object[])els;
274 		}
275 
276 		return true;
277 	}
278 
279 	/**
280 	 * Returns the size of the list.
281 	 *
282 	 * @return int list size
283 	 */
284 	public int size()
285 	{
286 		return array.length;
287 	}
288 
289 	/**
290 	 * Returns all objects in the list which are subtypes of the
291 	 * <code>class</code> parameter. This necessitates the construction of a
292 	 * new array which must occur in a synchronized block of code. This method
293 	 * therefore takes some time to  execute.
294 	 *
295 	 * @param type the type of the objects to look for
296 	 *
297 	 * @return Object[] an array containing only those elements of the list,
298 	 *         which are  a subtype of <code>type</code>
299 	 *
300 	 * @throws NullPointerException when type is <code>null</code>
301 	 * @exception IllegalArgumentException if <code>type</code> is not a
302 	 *            subtype of the type specified as the constructor parameter
303 	 */
304 	public Object[] toArray(Class type)
305 	{
306 		if (type == null) {
307 			throw new NullPointerException("type");
308 		}
309 
310 		if (!this.type.isAssignableFrom(type)) {
311 			throw new IllegalArgumentException("Type '" + type
312 			    + "' is not a subtype of '" + this.type + "'. Cannot cast.");
313 		}
314 
315 		synchronized (this) {
316 			int count = 0;
317 
318 			for (int i = 0; i < array.length; i++) {
319 				if (type.isAssignableFrom(array[i].getClass())) {
320 					count++;
321 				}
322 			}
323 
324 			Object retVal = Array.newInstance(type, count);
325 			count = 0;
326 
327 			for (int i = 0; i < array.length; i++) {
328 				if (type.isAssignableFrom(array[i].getClass())) {
329 					Array.set(retVal, count, array[i]);
330 					count++;
331 				}
332 			}
333 
334 			return (Object[])retVal;
335 		}
336 	}
337 
338 	/**
339 	 * Returns the elements of this list. <b>Do not modify this array in any
340 	 * way.</b>. List membership must only be affected through
341 	 * <code>add</code> and <code>remove()</code> methods of this class.
342 	 *
343 	 * @return an array of stored objects, of the run time type specified by
344 	 *         the constructor argument
345 	 */
346 	public Object[] toArray()
347 	{
348 		return array;
349 	}
350 
351 	/**
352 	 * Checks if the list contains a given element. Comparison with <code>equals</code>.
353 	 * Uses linear search.
354 	 *
355 	 * @param o the object to look for
356 	 *
357 	 * @return <code>true</code> iff the list contains object <code>o</code>.
358 	 */
359 	public boolean contains(Object o)
360 	{
361 		if (array == null || array.length == 0) {
362 			return false;
363 		}
364 
365 		for (int i = 0; i < array.length; i++) {
366 			if (array[i]==null ? o==null : array[i].equals(o)) {
367 				return true;
368 			}
369 		}
370 
371 		return false;
372 	}
373 
374 	/**
375 	 * Returns the object at the specified index
376 	 *
377 	 * @param index the index of the requested object
378 	 *
379 	 * @return the object at the specified index.
380 	 */
381 	public Object get(int index)
382 	{
383 		return array[index];
384 	}
385 
386 	/**
387 	 * Searches for the first occurence of the given argument, testing  for
388 	 * equality using the <tt>equals</tt> method.
389 	 *
390 	 * @param o an object.
391 	 *
392 	 * @return the index of the first occurrence of the argument in this list;
393 	 *         returns <tt>-1</tt> if the object is not found.
394 	 *
395 	 * @see Object#equals(Object)
396 	 */
397 	public int indexOf(Object o)
398 	{
399 		for (int i = 0; i < array.length; i++) {
400 			if (o.equals(array[i])) {
401 				return i;
402 			}
403 		}
404 
405 		return -1;
406 	}
407 
408 	/**
409 	 * Produces a string rendering of this instance by enumerating all members.
410 	 * Contract of <code>Object.toString</code>.
411 	 *
412 	 * @return String all members
413 	 */
414 	public String toString()
415 	{
416 		StringBuffer sb = new StringBuffer(500);
417 		sb.append("ObjectList (size ");
418 		sb.append(toArray().length);
419 		sb.append(") = { ");
420 
421 		Object[] array = toArray();
422 
423 		for (int i = 0; i < array.length; i++) {
424 			if (array[i] == null) {
425 				continue;
426 			}
427 
428 			sb.append(array[i].toString());
429 			sb.append(' ');
430 		}
431 
432 		sb.append('}');
433 
434 		return new String(sb);
435 	}
436 
437 	/**
438 	 * Empties this list. References to contained elementsw are released.
439 	 */
440 	public synchronized void clear()
441 	{
442 		array = (Object[])Array.newInstance(type, 0);
443 	}
444 
445 	/**
446 	 * Returns iterator which iterates over elements, which was contained in
447 	 * list at the moment iterator was called. Iteration does not fail, if
448 	 * elements are added or removed during the iteration.
449 	 *
450 	 * @return non-failing iterator with copy of elements
451 	 */
452 	public Iterator iterator()
453 	{
454 		return new ArrayIterator(array, 0);
455 	}
456 
457 	/**
458 	 * Tests if this list has no elements.
459 	 *
460 	 * @return <tt>true</tt> if this list has no elements; <tt>false</tt>
461 	 *         otherwise.
462 	 */
463 	public boolean isEmpty()
464 	{
465 		return array.length == 0;
466 	}
467 
468 	/**
469 	 * Removes the element at the specified position in this list (optional
470 	 * operation).  Shifts any subsequent elements to the left (subtracts one
471 	 * from their indices).  Returns the element that was removed from the
472 	 * list.
473 	 *
474 	 * @param index the index of the element to removed.
475 	 *
476 	 * @return the element previously at the specified position.
477 	 *
478 	 * @throws IndexOutOfBoundsException DOCUMENT ME!
479 	 */
480 	public Object remove(int index)
481 	{
482 		if (index < 0 || index > array.length) {
483 			throw new IndexOutOfBoundsException("Index: " + index);
484 		}
485 
486 		Object retObj = array[index];
487 
488 		synchronized (this) {
489 			int length = array.length;
490 			Object els = Array.newInstance(type, length - 1);
491 			System.arraycopy(array, 0, els, 0, index);
492 			System.arraycopy(array, index + 1, els, index, length - index - 1);
493 			array = (Object[])els;
494 		}
495 
496 		return retObj;
497 	}
498 
499 	/**
500 	 * Adds an object to this list. This list can contain multiple entries of
501 	 * the same object.
502 	 *
503 	 * @param index where the object should be inserted
504 	 * @param o object to add to the collection
505 	 *
506 	 * @throws NullPointerException
507 	 * @exception ClassCastException if the RTT of o is not assignable to the
508 	 *            type specified in the constructor
509 	 *
510 	 * @see java.util.List#add(int, Object)
511 	 */
512 	public void add(int index, Object o)
513 	{
514 		if (o == null) {
515 			throw new NullPointerException("o");
516 		}
517 
518 		if (type.isAssignableFrom(o.getClass()) == false) {
519 			throw new ClassCastException("Object '" + o + "' !instanceof '"
520 			    + type + "'.");
521 		}
522 
523 		synchronized (this) {
524 			int length = array.length;
525 			Object els = Array.newInstance(type, length + 1);
526 			index = index > length ? length : index;
527 			
528 			if (index == length) {
529 				System.arraycopy(array, 0, els, 0, length);
530 				Array.set(els, length, o);
531 			} else {
532 				System.arraycopy(array, 0, els, 0, index);
533 				Array.set(els, index, o);
534 				System.arraycopy(array, index, els, index + 1, length - index);
535 			}
536 
537 			array = (Object[])els;
538 		}
539 	}
540 
541 	/**
542 	 * Returns the index of the last occurrence of the specified object in this
543 	 * list.
544 	 *
545 	 * @param elem the desired element.
546 	 *
547 	 * @return the index of the last occurrence of the specified object in this
548 	 *         list; returns -1 if the object is not found.
549 	 *
550 	 * @see java.util.List#lastIndexOf(java.lang.Object)
551 	 */
552 	public int lastIndexOf(Object elem)
553 	{
554 		if (elem == null) {
555 			for (int i = array.length - 1; i >= 0; i--) {
556 				if (array[i] == null) {
557 					return i;
558 				}
559 			}
560 		} else {
561 			for (int i = array.length - 1; i >= 0; i--) {
562 				if (elem.equals(array[i])) {
563 					return i;
564 				}
565 			}
566 		}
567 
568 		return -1;
569 	}
570 
571 	/**
572 	 * Inserts all of the elements in the specified Collection into this list,
573 	 * starting at the specified position.  Shifts the element currently at
574 	 * that position (if any) and any subsequent elements to the right
575 	 * (increases their indices).  The new elements will appear in the list in
576 	 * the order that they are returned by the specified Collection's
577 	 * iterator.
578 	 *
579 	 * @param index index at which to insert first element from the specified
580 	 *        collection.
581 	 * @param c elements to be inserted into this list.
582 	 *
583 	 * @return <tt>true</tt> if this list changed as a result of the call.
584 	 *
585 	 * @see java.util.List#addAll(int, java.util.Collection)
586 	 */
587 	public boolean addAll(int index, Collection c)
588 	{
589 		Object[] a = c.toArray();
590 		int numNew = a.length;
591 
592 		synchronized (this) {
593 			int length = array.length;
594 			Object nArray = Array.newInstance(type, length + a.length);
595 
596 			//if (index == length) {
597 			//	System.arraycopy(array, 0, nArray, 0, length);
598 			//	Array.set(nArray, length, o);
599 			//} else {
600 			System.arraycopy(array, 0, nArray, 0, index);
601 			System.arraycopy(a, 0, nArray, index, a.length);
602 
603 			//Array.set(els, index, o);
604 			System.arraycopy(array, index, nArray, index + a.length,
605 			    length - index);
606 
607 			//}
608 			array = (Object[])nArray;
609 		}
610 
611 		return numNew != 0;
612 	}
613 
614 	/**
615 	 * Appends all of the elements in the specified Collection to the end of
616 	 * this list, in the order that they are returned by the specified
617 	 * Collection's Iterator.
618 	 *
619 	 * @param c the elements to be inserted into this list.
620 	 *
621 	 * @return <tt>true</tt> if this list changed as a result of the call.
622 	 *
623 	 * @see java.util.List#addAll(java.util.Collection)
624 	 */
625 	public boolean addAll(Collection c)
626 	{
627 		Object[] a = c.toArray();
628 		int numNew = a.length;
629 
630 		for (int i = 0; i < a.length; i++) {
631 			add(a[i]);
632 		}
633 
634 		return numNew != 0;
635 	}
636 
637 	/**
638 	 * Returns <tt>true</tt> if this collection contains all of the elements in
639 	 * the specified collection.
640 	 * 
641 	 * <p>
642 	 * This implementation iterates over the specified collection, checking
643 	 * each element returned by the iterator in turn to see if it's contained
644 	 * in this collection.  If all elements are so contained <tt>true</tt> is
645 	 * returned, otherwise <tt>false</tt>.
646 	 * </p>
647 	 *
648 	 * @param c collection to be checked for containment in this collection.
649 	 *
650 	 * @return <tt>true</tt> if this collection contains all of the elements in
651 	 *         the specified collection.
652 	 *
653 	 * @see #contains(Object)
654 	 * @see java.util.List#containsAll(java.util.Collection)
655 	 */
656 	public boolean containsAll(Collection c)
657 	{
658 		Iterator e = c.iterator();
659 
660 		while (e.hasNext()) {
661 			if (!contains(e.next())) {
662 				return false;
663 			}
664 		}
665 
666 		return true;
667 	}
668 
669 	/**
670 	 * Removes the elements that are contained in the collection.
671 	 *
672 	 * @param c the collection of elements to be removed
673 	 *
674 	 * @return <code>true</code> if at least one element is removed
675 	 *
676 	 * @see java.util.List#removeAll(java.util.Collection)
677 	 */
678 	public boolean removeAll(Collection c)
679 	{
680 		boolean modified = false;
681 
682 		for (int i = 0; i < array.length; i++) {
683 			if (c.contains(array[i])) {
684 				remove(i);
685 				i = i - 1;
686 				modified = true;
687 			}
688 		}
689 
690 		return modified;
691 	}
692 
693 	/**
694 	 * Removes all elements but those contained in the collection.
695 	 *
696 	 * @param c the collection of elements to keep
697 	 *
698 	 * @return <code>true</code> if at lest one element is removed
699 	 *
700 	 * @see java.util.List#retainAll(java.util.Collection)
701 	 */
702 	public boolean retainAll(Collection c)
703 	{
704 		boolean modified = false;
705 
706 		for (int i = 0; i < array.length; i++) {
707 			if (!c.contains(array[i])) {
708 				remove(i);
709 				i = i - 1;
710 				modified = true;
711 			}
712 		}
713 
714 		return modified;
715 	}
716 
717 	/**
718 	 * DOCUMENT ME!
719 	 *
720 	 * @param fromIndex DOCUMENT ME!
721 	 * @param toIndex DOCUMENT ME!
722 	 *
723 	 * @return DOCUMENT ME!
724 	 *
725 	 * @throws UnsupportedOperationException when invoked
726 	 *
727 	 * @see java.util.List#subList(int, int)
728 	 * @deprecated This method is not implemented.
729 	 */
730 	public List subList(int fromIndex, int toIndex)
731 	{
732 		/*
733 		 * to implement this method, a new extended class should be made from object list,
734 		 * where all methods that change the elements would need to reflect the change onto
735 		 * the referenced (this) object list.
736 		 */
737 		/*if (fromIndex < 0 || toIndex > array.length || fromIndex>toIndex) throw new IllegalArgumentException();
738 
739 		ObjectList ol = new ObjectList(type);
740 		for (int i = fromIndex; i < toIndex; i++) {
741 		    ol.add(i, array[i]);
742 
743 		}
744 		return ol;
745 		*/
746 		throw new UnsupportedOperationException();
747 	}
748 
749 	/**
750 	 * Returns iterator which iterates over elements, which was contained in
751 	 * list at the moment iterator was called. Iteration does not fail, if
752 	 * elements are added or removed during the iteration.
753 	 *
754 	 * @param index index of the first element to be returned from the list
755 	 *        iterator (by a call to the <tt>next</tt> method).
756 	 *
757 	 * @return a list iterator of the elements in this list, starting at the
758 	 *         specified position in the list.
759 	 *
760 	 * @throws IndexOutOfBoundsException if the specified index is out of range
761 	 *         (<tt>index &lt; 0 || index &gt; size()</tt>).
762 	 *
763 	 * @see java.util.List#listIterator(int)
764 	 */
765 	public ListIterator listIterator(final int index)
766 	{
767 		if (index < 0 || index > array.length) {
768 			throw new IndexOutOfBoundsException("Index: " + index);
769 		}
770 
771 		return new ArrayIterator(array, index);
772 	}
773 
774 	/**
775 	 * Sets the element at specified index to specified object.
776 	 *
777 	 * @param index the index of object to set
778 	 * @param element the object to be set to the index
779 	 *
780 	 * @return the element previously at the specified position
781 	 *
782 	 * @throws NullPointerException DOCUMENT ME!
783 	 * @throws ClassCastException DOCUMENT ME!
784 	 *
785 	 * @see java.util.List#set(int, Object)
786 	 */
787 	public Object set(int index, Object element)
788 	{
789 		if (element == null) {
790 			throw new NullPointerException("element");
791 		}
792 
793 		if (type.isAssignableFrom(element.getClass()) == false) {
794 			throw new ClassCastException("Object '" + element
795 			    + "' !instanceof '" + type + "'.");
796 		}
797 
798 		Object retObj = null;
799 
800 		synchronized (this) {
801 			retObj = array[index];
802 			array[index] = element;
803 		}
804 
805 		return retObj;
806 	}
807 
808 	/**
809 	 * Returns an array containing all of the elements in this list.
810 	 *
811 	 * @param a the array into which the elements of this list are to be
812 	 *        stored, if it is big enough; otherwise, a new array  is
813 	 *        allocated for this purpose.
814 	 *
815 	 * @return an array containing the elements of this list.
816 	 *
817 	 * @throws NullPointerException if the specified array is <tt>null</tt>.
818 	 * @throws ArrayStoreException if the runtime type of the specified array
819 	 *         is not a supertype of the runtime type of every element in this
820 	 *         list.
821 	 *
822 	 * @see java.util.List#toArray(java.lang.Object[])
823 	 */
824 	public Object[] toArray(Object[] a)
825 	{
826 		if (a == null) {
827 			throw new NullPointerException("o");
828 		}
829 
830 		if (type.isAssignableFrom(a.getClass().getComponentType()) == false) {
831 			throw new ArrayStoreException("Object '" + a + "' !instanceof '"
832 			    + type + "'.");
833 		}
834 
835 		return (Object[])toArray();
836 	}
837 
838 	/**
839 	 * Returns a list iterator of the elements in this list, iterator will
840 	 * iterate over the elements, which were in list at the moment iterator
841 	 * was created thus iteration will not fail if list is modified during
842 	 * iteration.
843 	 *
844 	 * @return a list iterator of the elements in this list
845 	 *
846 	 * @see java.util.List#listIterator(int)
847 	 */
848 	public ListIterator listIterator()
849 	{
850 		return new ArrayIterator(array, 0);
851 	}
852 }
853 
854 /* __oOo__ */