View Javadoc

1   /*
2    * Copyright 2000-2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.marevol.utils.hibernate.faces.model;
17  
18  import java.util.AbstractCollection;
19  import java.util.AbstractSet;
20  import java.util.Collection;
21  import java.util.Comparator;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.TreeMap;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import javax.faces.FacesException;
31  import javax.faces.model.DataModel;
32  import javax.faces.model.DataModelEvent;
33  import javax.faces.model.DataModelListener;
34  
35  import org.hibernate.HibernateException;
36  import org.hibernate.Query;
37  import org.hibernate.ScrollableResults;
38  import org.hibernate.type.Type;
39  
40  import com.marevol.utils.hibernate.HibernateUtil;
41  
42  /***
43   *  HibernateDataModel is DataModel class for Hibernate ScrollableResults.
44   * 
45   * 
46   * @author <a href="mailto:shinsuke@yahoo.co.jp">Shinsuke Sugaya</a>
47   */
48  public class HibernateDataModel extends DataModel
49  {
50      /***
51       * Logger for this class
52       */
53      private static final Log log = LogFactory.getLog(HibernateDataModel.class);
54  
55      // FIELDS
56  
57      private int _currentIndex = -1;
58  
59      private int _rowCount = -1;
60  
61      /***
62       * The ResultSet being wrapped by this DataModel.
63       */
64      private ScrollableResults _scrollableResults = null;
65  
66      // CONSTRUCTORS
67      public HibernateDataModel()
68      {
69          super();
70      }
71  
72      public HibernateDataModel(String hSql, Object[] params)
73      {
74          super();
75  
76          if (hSql == null)
77          {
78              throw new IllegalArgumentException("HSQL statement is null.");
79          }
80          Query query = HibernateUtil.createQuery(hSql);
81  
82          if (params != null)
83          {
84              for (int i = 0; i < params.length; i++)
85              {
86                  query.setParameter(i, params[i]);
87              }
88          }
89  
90          _scrollableResults = query.scroll();
91          setWrappedData(_scrollableResults);
92  
93          guessRowCount(hSql, params);
94      }
95  
96      private void guessRowCount(String hSql, Object[] params)
97      {
98          int index = hSql.toLowerCase().indexOf("from");
99          if (index >= 0)
100         {
101             Query q = HibernateUtil.createQuery(createHsqlForCount(hSql));
102             for (int i = 0; i < params.length; i++)
103             {
104                 q.setParameter(i, params[i]);
105             }
106             Integer count = (Integer) q.uniqueResult();
107             if (count != null)
108             {
109                 setRowCount(count.intValue());
110             }
111             else
112             {
113                 setRowCount(0);
114             }
115         }
116         else
117         {
118             setRowCount(0);
119         }
120     }
121 
122     public HibernateDataModel(String hSql, Object[] params, Type[] types)
123     {
124         super();
125 
126         if (hSql == null)
127         {
128             throw new IllegalArgumentException("HSQL statement is null.");
129         }
130         Query query = HibernateUtil.createQuery(hSql);
131 
132         if ((params == null && types != null) || (params != null && types == null))
133         {
134             throw new IllegalArgumentException("Wrong parameters or types.");
135         }
136         if (params != null && types != null)
137         {
138             if (params.length != types.length)
139             {
140                 throw new IllegalArgumentException("The number of parameter is not equal to the number of type.");
141             }
142             for (int i = 0; i < params.length; i++)
143             {
144                 query.setParameter(i, params[i], types[i]);
145             }
146         }
147 
148         _scrollableResults = query.scroll();
149         setWrappedData(_scrollableResults);
150 
151         guessRowCount(hSql, params, types);
152     }
153 
154     private void guessRowCount(String hSql, Object[] params, Type[] types)
155     {
156         int index = hSql.toLowerCase().indexOf("from");
157         if (index >= 0)
158         {
159             Query q = HibernateUtil.createQuery(createHsqlForCount(hSql));
160             for (int i = 0; i < params.length; i++)
161             {
162                 q.setParameter(i, params[i], types[i]);
163             }
164             Integer count = (Integer) q.uniqueResult();
165             if (count != null)
166             {
167                 setRowCount(count.intValue());
168             }
169             else
170             {
171                 setRowCount(0);
172             }
173         }
174         else
175         {
176             setRowCount(0);
177         }
178     }
179 
180     private String createHsqlForCount(String hSql)
181     {
182         String sql = "select count(*) " + hSql.substring(hSql.toLowerCase().indexOf("from"));
183         return sql.replaceAll("order by [^ ]* desc", "").replaceAll("order by [^ ]* asc", "").replaceAll(
184                 "order by [^ ]*", "");
185     }
186 
187     public void setRowCount(int count)
188     {
189         _rowCount = count;
190     }
191 
192     public int getRowCount()
193     {
194         return _rowCount;
195     }
196 
197     /*** Get the actual data of this row
198      *  wrapped into a map.
199      *  The specification is very strict about what has to be
200      *  returned from here, so check the spec before
201      *  modifying anything here.
202      */
203     public Object getRowData()
204     {
205         if (_scrollableResults == null)
206         {
207             if (log.isDebugEnabled())
208             {
209                 log.debug("getRowData() - _query is null.");
210             }
211             return null;
212         }
213         else if (!isRowAvailable())
214         {
215             throw new IllegalArgumentException(
216                     "the requested row is not available in the ScrollableResults - you have scrolled beyond the end.");
217         }
218 
219         try
220         {
221             return new WrapScrollableResultsMap(String.CASE_INSENSITIVE_ORDER);
222         }
223         catch (HibernateException e)
224         {
225             log.error("getRowData()", e);
226 
227             throw new FacesException(e);
228         }
229 
230     }
231 
232     public int getRowIndex()
233     {
234         return _currentIndex;
235     }
236 
237     public Object getWrappedData()
238     {
239         return _scrollableResults;
240     }
241 
242     public boolean isRowAvailable()
243     {
244         if (_scrollableResults == null)
245         {
246             if (log.isDebugEnabled())
247             {
248                 log.debug("isRowAvailable() - _query is null.");
249             }
250             return false;
251         }
252         else if (_currentIndex < 0)
253         {
254             if (log.isDebugEnabled())
255             {
256                 log.debug("isRowAvailable() - _currentIndex < 0");
257             }
258             return false;
259         }
260 
261         try
262         {
263             return _scrollableResults.setRowNumber(_currentIndex);
264         }
265         catch (HibernateException e)
266         {
267             log.error("isRowAvailable()", e);
268 
269             throw new FacesException(e);
270         }
271     }
272 
273     public void setRowIndex(int rowIndex)
274     {
275         if (rowIndex < -1)
276         {
277             throw new IllegalArgumentException("you cannot set the rowIndex to anything less than 0");
278         }
279 
280         int old = _currentIndex;
281         _currentIndex = rowIndex;
282 
283         //if no underlying data has been set, the listeners
284         //need not be notified
285         if (_scrollableResults == null)
286         {
287             if (log.isDebugEnabled())
288             {
289                 log.debug("setRowIndex(int) - _query is null.");
290             }
291             return;
292         }
293 
294         //Notify all listeners of the upated row
295         DataModelListener[] listeners = getDataModelListeners();
296 
297         if ((old != _currentIndex) && (listeners != null))
298         {
299             Object rowData = null;
300 
301             if (isRowAvailable())
302             {
303                 rowData = getRowData();
304             }
305 
306             DataModelEvent event = new DataModelEvent(this, _currentIndex, rowData);
307 
308             int n = listeners.length;
309 
310             for (int i = 0; i < n; i++)
311             {
312                 if (listeners[i] != null)
313                 {
314                     listeners[i].rowSelected(event);
315                 }
316             }
317         }
318 
319     }
320 
321     public void setWrappedData(Object data)
322     {
323         if (data == null)
324         {
325             _scrollableResults = null;
326             setRowIndex(-1);
327         }
328         else
329         {
330             _scrollableResults = (ScrollableResults) data;
331             _currentIndex = -1;
332             setRowIndex(0);
333         }
334     }
335 
336     /* A map wrapping the result set and calling
337      * the corresponding operations on the result set,
338      * first setting the correct row index.
339      */
340     private class WrapScrollableResultsMap extends TreeMap
341     {
342         /***
343          * Logger for this class
344          */
345         private final Log log = LogFactory.getLog(WrapScrollableResultsMap.class);
346 
347         private int _currentIndex;
348 
349         //       private ScrollableResults _scrollableResults;
350 
351         public WrapScrollableResultsMap(Comparator comparator)
352         {
353             super(comparator);
354 
355             _currentIndex = HibernateDataModel.this._currentIndex;
356 
357             if (_scrollableResults.setRowNumber(_currentIndex))
358             {
359                 Object[] objects = _scrollableResults.get();
360 
361                 for (int i = 0; i < objects.length; i++)
362                 {
363                     super.put(Integer.toString(i), Integer.toString(i));
364                 }
365             }
366             else
367             {
368                 if (log.isDebugEnabled())
369                 {
370                     log.debug("WrapScrollableResultsMap(Comparator) -  : The current row is not available.");
371                 }
372             }
373         }
374 
375         public void clear()
376         {
377             throw new UnsupportedOperationException("It is not allowed to remove from this map");
378         }
379 
380         public boolean containsValue(Object value)
381         {
382             Set keys = keySet();
383             for (Iterator iterator = keys.iterator(); iterator.hasNext();)
384             {
385                 Object object = get(iterator.next());
386                 if (object == null)
387                 {
388                     return value == null;
389                 }
390                 if (object.equals(value))
391                 {
392                     return true;
393                 }
394 
395             }
396 
397             return false;
398         }
399 
400         public Set entrySet()
401         {
402             return new WrapScrollableResultsEntries(this);
403         }
404 
405         public Object get(Object key)
406         {
407             if (!containsKey(key))
408             {
409                 if (log.isDebugEnabled())
410                 {
411                     log.debug("get(Object) - get(" + key + ") is null.");
412                 }
413                 return null;
414             }
415             return basicGet(key);
416         }
417 
418         private Object basicGet(Object key)
419         {
420             _scrollableResults.setRowNumber(_currentIndex);
421             Integer i = new Integer((String) getUnderlyingKey(key));
422             if (i != null)
423             {
424                 return _scrollableResults.get(i.intValue());
425             }
426 
427             if (log.isDebugEnabled())
428             {
429                 log.debug("basicGet(Object) - Cannot find the target column : i=" + i);
430             }
431             return null;
432         }
433 
434         public Set keySet()
435         {
436             return new WrapScrollableResultsKeys(this);
437         }
438 
439         public Object put(Object key, Object value)
440         {
441             throw new UnsupportedOperationException("It is not allowed to update entries from this set.");
442         }
443 
444         public void putAll(Map map)
445         {
446             for (Iterator i = map.entrySet().iterator(); i.hasNext();)
447             {
448                 Map.Entry entry = (Map.Entry) i.next();
449                 put(entry.getKey(), entry.getValue());
450             }
451 
452         }
453 
454         public Object remove(Object key)
455         {
456             throw new UnsupportedOperationException("It is not allowed to remove entries from this set.");
457         }
458 
459         public Collection values()
460         {
461 
462             return new WrapScrollableResultsValues(this);
463         }
464 
465         Object getUnderlyingKey(Object key)
466         {
467 
468             return super.get(key);
469         }
470 
471         Iterator getUnderlyingKeys()
472         {
473             return super.keySet().iterator();
474         }
475 
476     }
477 
478     private static class WrapScrollableResultsEntries extends AbstractSet
479     {
480         /***
481          * Logger for this class
482          */
483         private static final Log log = LogFactory.getLog(WrapScrollableResultsEntries.class);
484 
485         private WrapScrollableResultsMap _wrapMap;
486 
487         public WrapScrollableResultsEntries(WrapScrollableResultsMap wrapMap)
488         {
489             _wrapMap = wrapMap;
490         }
491 
492         public boolean add(Object o)
493         {
494             throw new UnsupportedOperationException("it is not allowed to add to this set");
495         }
496 
497         public boolean addAll(Collection c)
498         {
499             throw new UnsupportedOperationException("it is not allowed to add to this set");
500         }
501 
502         public void clear()
503         {
504             throw new UnsupportedOperationException("it is not allowed to remove from this set");
505         }
506 
507         public boolean contains(Object o)
508         {
509 
510             if (o == null)
511                 throw new NullPointerException();
512             if (!(o instanceof Map.Entry))
513                 return false;
514 
515             Map.Entry e = (Map.Entry) o;
516             Object key = e.getKey();
517 
518             if (!_wrapMap.containsKey(key))
519             {
520                 return false;
521             }
522 
523             Object value = e.getValue();
524             Object cmpValue = _wrapMap.get(key);
525 
526             return value == null ? cmpValue == null : value.equals(cmpValue);
527         }
528 
529         public boolean isEmpty()
530         {
531             return _wrapMap.isEmpty();
532         }
533 
534         public Iterator iterator()
535         {
536             return new WrapScrollableResultsEntriesIterator(_wrapMap);
537         }
538 
539         public boolean remove(Object o)
540         {
541             throw new UnsupportedOperationException("it is not allowed to remove from this set");
542         }
543 
544         public boolean removeAll(Collection c)
545         {
546             throw new UnsupportedOperationException("it is not allowed to remove from this set");
547         }
548 
549         public boolean retainAll(Collection c)
550         {
551             throw new UnsupportedOperationException("it is not allowed to remove from this set");
552         }
553 
554         public int size()
555         {
556             return _wrapMap.size();
557         }
558     }
559 
560     private static class WrapScrollableResultsEntriesIterator implements Iterator
561     {
562         /***
563          * Logger for this class
564          */
565         private static final Log log = LogFactory.getLog(WrapScrollableResultsEntriesIterator.class);
566 
567         private WrapScrollableResultsMap _wrapMap = null;
568 
569         private Iterator _keyIterator = null;
570 
571         public WrapScrollableResultsEntriesIterator(WrapScrollableResultsMap wrapMap)
572         {
573             _wrapMap = wrapMap;
574             _keyIterator = _wrapMap.keySet().iterator();
575         }
576 
577         public boolean hasNext()
578         {
579             return _keyIterator.hasNext();
580         }
581 
582         public Object next()
583         {
584             return new WrapScrollableResultsEntry(_wrapMap, _keyIterator.next());
585         }
586 
587         public void remove()
588         {
589             throw new UnsupportedOperationException("It is not allowed to remove from this iterator");
590         }
591 
592     }
593 
594     private static class WrapScrollableResultsEntry implements Map.Entry
595     {
596         /***
597          * Logger for this class
598          */
599         private static final Log log = LogFactory.getLog(WrapScrollableResultsEntry.class);
600 
601         private WrapScrollableResultsMap _wrapMap;
602 
603         private Object _entryKey;
604 
605         public WrapScrollableResultsEntry(WrapScrollableResultsMap wrapMap, Object entryKey)
606         {
607             _wrapMap = wrapMap;
608             _entryKey = entryKey;
609         }
610 
611         public boolean equals(Object o)
612         {
613             if (o == null)
614                 return false;
615 
616             if (!(o instanceof Map.Entry))
617                 return false;
618 
619             Map.Entry cmpEntry = (Map.Entry) o;
620 
621             if (_entryKey == null ? cmpEntry.getKey() != null : !_entryKey.equals(cmpEntry.getKey()))
622                 return false;
623 
624             Object value = _wrapMap.get(_entryKey);
625             Object cmpValue = cmpEntry.getValue();
626 
627             return value == null ? cmpValue != null : value.equals(cmpValue);
628         }
629 
630         public Object getKey()
631         {
632             return _entryKey;
633         }
634 
635         public Object getValue()
636         {
637             return _wrapMap.get(_entryKey);
638         }
639 
640         public int hashCode()
641         {
642             int result;
643             result = (_entryKey != null ? _entryKey.hashCode() : 0);
644             result = 29 * result + (_wrapMap.get(_entryKey) != null ? _wrapMap.get(_entryKey).hashCode() : 0);
645 
646             return result;
647         }
648 
649         public Object setValue(Object value)
650         {
651             Object oldValue = _wrapMap.get(_entryKey);
652             _wrapMap.put(_entryKey, value);
653 
654             return oldValue;
655         }
656     }
657 
658     private static class WrapScrollableResultsKeys extends AbstractSet
659     {
660         /***
661          * Logger for this class
662          */
663         private static final Log log = LogFactory.getLog(WrapScrollableResultsKeys.class);
664 
665         private WrapScrollableResultsMap _wrapMap;
666 
667         public WrapScrollableResultsKeys(WrapScrollableResultsMap wrapMap)
668         {
669             _wrapMap = wrapMap;
670         }
671 
672         public boolean add(Object o)
673         {
674             throw new UnsupportedOperationException("It is not allowed to add to this set");
675         }
676 
677         public boolean addAll(Collection c)
678         {
679             throw new UnsupportedOperationException("It is not allowed to add to this set");
680         }
681 
682         public void clear()
683         {
684             throw new UnsupportedOperationException("It is not allowed to remove from this set");
685         }
686 
687         public boolean contains(Object obj)
688         {
689             return _wrapMap.containsKey(obj);
690         }
691 
692         public boolean isEmpty()
693         {
694             return _wrapMap.isEmpty();
695         }
696 
697         public Iterator iterator()
698         {
699 
700             return new WrapScrollableResultsKeysIterator(_wrapMap);
701         }
702 
703         public boolean remove(Object o)
704         {
705             throw new UnsupportedOperationException("It is not allowed to remove from this set");
706         }
707 
708         public boolean removeAll(Collection c)
709         {
710             throw new UnsupportedOperationException("It is not allowed to remove from this set");
711         }
712 
713         public boolean retainAll(Collection c)
714         {
715             throw new UnsupportedOperationException("It is not allowed to remove from this set");
716         }
717 
718         public int size()
719         {
720             return _wrapMap.size();
721         }
722     }
723 
724     private static class WrapScrollableResultsKeysIterator implements Iterator
725     {
726         /***
727          * Logger for this class
728          */
729         private static final Log log = LogFactory.getLog(WrapScrollableResultsKeysIterator.class);
730 
731         private Iterator _keyIterator = null;
732 
733         public WrapScrollableResultsKeysIterator(WrapScrollableResultsMap map)
734         {
735             _keyIterator = map.getUnderlyingKeys();
736         }
737 
738         public boolean hasNext()
739         {
740             return _keyIterator.hasNext();
741         }
742 
743         public Object next()
744         {
745             return _keyIterator.next();
746         }
747 
748         public void remove()
749         {
750             throw new UnsupportedOperationException("it is not allowed to remove from this iterator");
751         }
752 
753     }
754 
755     private static class WrapScrollableResultsValues extends AbstractCollection
756     {
757         /***
758          * Logger for this class
759          */
760         private static final Log log = LogFactory.getLog(WrapScrollableResultsValues.class);
761 
762         private WrapScrollableResultsMap _wrapMap;
763 
764         public WrapScrollableResultsValues(WrapScrollableResultsMap wrapMap)
765         {
766             _wrapMap = wrapMap;
767         }
768 
769         public boolean add(Object o)
770         {
771             throw new UnsupportedOperationException("it is not allowed to add to this collection");
772         }
773 
774         public boolean addAll(Collection c)
775         {
776             throw new UnsupportedOperationException("it is not allowed to add to this collection");
777         }
778 
779         public void clear()
780         {
781             throw new UnsupportedOperationException("it is not allowed to remove from this collection");
782         }
783 
784         public boolean contains(Object value)
785         {
786             return _wrapMap.containsValue(value);
787         }
788 
789         public Iterator iterator()
790         {
791             return new WrapScrollableResultsValuesIterator(_wrapMap);
792         }
793 
794         public boolean remove(Object o)
795         {
796             throw new UnsupportedOperationException();
797         }
798 
799         public boolean removeAll(Collection c)
800         {
801             throw new UnsupportedOperationException("it is not allowed to remove from this collection");
802         }
803 
804         public boolean retainAll(Collection c)
805         {
806             throw new UnsupportedOperationException("it is not allowed to remove from this collection");
807         }
808 
809         public int size()
810         {
811             return _wrapMap.size();
812         }
813 
814     }
815 
816     private static class WrapScrollableResultsValuesIterator implements Iterator
817     {
818         /***
819          * Logger for this class
820          */
821         private static final Log log = LogFactory.getLog(WrapScrollableResultsValuesIterator.class);
822 
823         private WrapScrollableResultsMap _wrapMap;
824 
825         private Iterator _keyIterator;
826 
827         public WrapScrollableResultsValuesIterator(WrapScrollableResultsMap wrapMap)
828         {
829             _wrapMap = wrapMap;
830             _keyIterator = _wrapMap.keySet().iterator();
831         }
832 
833         public boolean hasNext()
834         {
835             return _keyIterator.hasNext();
836         }
837 
838         public Object next()
839         {
840             return _wrapMap.get(_keyIterator.next());
841         }
842 
843         public void remove()
844         {
845             throw new UnsupportedOperationException("it is not allowed to remove from this map");
846         }
847 
848     }
849 
850 }