Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.model » [javadoc | source]
    1   /*
    2    * $Id: ResultSetDataModel.java,v 1.34.4.2 2008/04/29 19:33:26 rlubke Exp $
    3    */
    4   
    5   /*
    6    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    7    * 
    8    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    9    * 
   10    * The contents of this file are subject to the terms of either the GNU
   11    * General Public License Version 2 only ("GPL") or the Common Development
   12    * and Distribution License("CDDL") (collectively, the "License").  You
   13    * may not use this file except in compliance with the License. You can obtain
   14    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   15    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   16    * language governing permissions and limitations under the License.
   17    * 
   18    * When distributing the software, include this License Header Notice in each
   19    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   20    * Sun designates this particular file as subject to the "Classpath" exception
   21    * as provided by Sun in the GPL Version 2 section of the License file that
   22    * accompanied this code.  If applicable, add the following below the License
   23    * Header, with the fields enclosed by brackets [] replaced by your own
   24    * identifying information: "Portions Copyrighted [year]
   25    * [name of copyright owner]"
   26    * 
   27    * Contributor(s):
   28    * 
   29    * If you wish your version of this file to be governed by only the CDDL or
   30    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   31    * elects to include this software in this distribution under the [CDDL or GPL
   32    * Version 2] license."  If you don't indicate a single choice of license, a
   33    * recipient has the option to distribute your version of this file under
   34    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   35    * its licensees as provided above.  However, if you add GPL Version 2 code
   36    * and therefore, elected the GPL Version 2 license, then the option applies
   37    * only if the new code is made subject to such option by the copyright
   38    * holder.
   39    */
   40   
   41   package javax.faces.model;
   42   
   43   
   44   import java.sql.ResultSet;
   45   import java.sql.ResultSetMetaData;
   46   import java.sql.SQLException;
   47   import java.util.AbstractCollection;
   48   import java.util.AbstractSet;
   49   import java.util.Collection;
   50   import java.util.Comparator;
   51   import java.util.Iterator;
   52   import java.util.Map;
   53   import java.util.Set;
   54   import java.util.TreeMap;
   55   
   56   import javax.faces.FacesException;
   57   
   58   
   59   /**
   60    * <p><strong>ResultSetDataModel</strong> is a convenience implementation of
   61    * {@link DataModel} that wraps a <code>ResultSet</code> of Java objects.
   62    * Note that the specified <code>ResultSet</code> <strong>MUST</strong>
   63    * be scrollable.  In addition, if input components (that will be updating
   64    * model values) reference this object in value binding expressions, the
   65    * specified <code>ResultSet</code> <strong>MUST</strong> be updatable.</p>
   66    */
   67   
   68   public class ResultSetDataModel extends DataModel {
   69   
   70   
   71       // ------------------------------------------------------------ Constructors
   72   
   73   
   74       /**
   75        * <p>Construct a new {@link ResultSetDataModel} with no specified
   76        * wrapped data.</p>
   77        */
   78       public ResultSetDataModel() {
   79   
   80           this(null);
   81   
   82       }
   83   
   84   
   85       /**
   86        * <p>Construct a new {@link ResultSetDataModel} wrapping the specified
   87        * <code>ResultSet</code>.</p>
   88        *
   89        * @param resultSet <code>ResultSet</code> to be wrapped (if any)
   90        */
   91       public ResultSetDataModel(ResultSet resultSet) {
   92   
   93           super();
   94           setWrappedData(resultSet);
   95   
   96       }
   97   
   98   
   99       // ------------------------------------------------------ Instance Variables
  100   
  101   
  102       // The current row index (zero relative)
  103       private int index = -1;
  104   
  105   
  106       // The metadata for the ResultSet we are wrapping (lazily instantiated)
  107       private ResultSetMetaData metadata = null;
  108   
  109   
  110       // The ResultSet we are wrapping
  111       private ResultSet resultSet = null;
  112   
  113   
  114       // Has the row at the current index been updated?
  115       private boolean updated = false;
  116   
  117   
  118       // -------------------------------------------------------------- Properties
  119   
  120   
  121       /**
  122        * <p>Return <code>true</code> if there is <code>wrappedData</code>
  123        * available, and the result of calling <code>absolute()</code> on the
  124        * underlying <code>ResultSet</code>, passing the current value of
  125        * <code>rowIndex</code> plus one (to account for the fact that
  126        * <code>ResultSet</code> uses one-relative indexing), returns
  127        * <code>true</code>.  Otherwise, return <code>false</code>.</p>
  128        *
  129        * @throws FacesException if an error occurs getting the row availability
  130        */ 
  131       public boolean isRowAvailable() {
  132   
  133           if (resultSet == null) {
  134   	    return (false);
  135           } else if (index < 0) {
  136               return (false);
  137           }
  138           try {
  139               if (resultSet.absolute(index + 1)) {
  140                   return (true);
  141               } else {
  142                   return (false);
  143               }
  144           } catch (SQLException e) {
  145               throw new FacesException(e);
  146           }
  147   
  148       }
  149   
  150   
  151       /**
  152        * <p>Return -1, since <code>ResultSet</code> does not provide a
  153        * standard way to determine the number of available rows without
  154        * scrolling through the entire <code>ResultSet</code>, and this can
  155        * be very expensive if the number of rows is large.</p>
  156        *
  157        * @throws FacesException if an error occurs getting the row count
  158        */
  159       public int getRowCount() {
  160   
  161   	return (-1);
  162   
  163       }
  164   
  165   
  166       /**
  167        * <p>If row data is available, return a <code>Map</code> representing
  168        * the values of the columns for the row specified by <code>rowIndex</code>,
  169        * keyed by the corresponding column names.  If no wrapped data is
  170        * available, return <code>null</code>.</p>
  171        *
  172        * <p>If a non-<code>null</code> <code>Map</code> is returned, its behavior
  173        * must correspond to the contract for a mutable <code>Map</code> as
  174        * described in the JavaDocs for <code>AbstractMap</code>, with the
  175        * following exceptions and specialized behavior:</p>
  176        * <ul>
  177   
  178        * <li>The <code>Map</code>, and any supporting objects it returns,
  179        *     must perform all column name comparisons in a
  180        *     case-insensitive manner.  This case-insensitivity must be
  181        *     implemented using a case-insensitive <code>Comparator</code>,
  182        *     such as
  183        *     <code>String.CASE_INSENSITIVE_ORDER</code>.</li>
  184   
  185        * <li>The following methods must throw
  186        *     <code>UnsupportedOperationException</code>:  <code>clear()</code>,
  187        *     <code>remove()</code>.</li>
  188        * <li>The <code>entrySet()</code> method must return a <code>Set</code>
  189        *     that has the following behavior:
  190        *     <ul>
  191        *     <li>Throw <code>UnsupportedOperationException</code> for any attempt
  192        *         to add or remove entries from the <code>Set</code>, either
  193        *         directly or indirectly through an <code>Iterator</code>
  194        *         returned by the <code>Set</code>.</li>
  195        *     <li>Updates to the <code>value</code> of an entry in this
  196        *         <code>set</code> must write through to the corresponding
  197        *         column value in the underlying <code>ResultSet</code>.</li>
  198        *     </ul></li>
  199        * <li>The <code>keySet()</code> method must return a <code>Set</code>
  200        *     that throws <code>UnsupportedOperationException</code> on any
  201        *     attempt to add or remove keys, either directly or through an
  202        *     <code>Iterator</code> returned by the <code>Set</code>.</li>
  203        * <li>The <code>put()</code> method must throw
  204        *     <code>IllegalArgumentException</code> if a key value for which
  205        *     <code>containsKey()</code> returns <code>false</code> is
  206        *     specified.  However, if a key already present in the <code>Map</code>
  207        *     is specified, the specified value must write through to the
  208        *     corresponding column value in the underlying <code>ResultSet</code>.
  209        *     </li>
  210        * <li>The <code>values()</code> method must return a
  211        *     <code>Collection</code> that throws
  212        *     <code>UnsupportedOperationException</code> on any attempt to add
  213        *     or remove values, either directly or through an <code>Iterator</code>
  214        *     returned by the <code>Collection</code>.</li>
  215        * </ul>
  216        *
  217        * @throws FacesException if an error occurs getting the row data
  218        * @throws IllegalArgumentException if now row data is available
  219        *  at the currently specified row index
  220        */ 
  221       public Object getRowData() {
  222   
  223           if (resultSet == null) {
  224   	    return (null);
  225           } else if (!isRowAvailable()) {
  226               throw new NoRowAvailableException();
  227           }
  228           try {
  229               getMetaData();
  230               return (new ResultSetMap(String.CASE_INSENSITIVE_ORDER));
  231           } catch (SQLException e) {
  232               throw new FacesException(e);
  233           }
  234   
  235       }
  236   
  237   
  238       /**
  239        * @throws FacesException {@inheritDoc}     
  240        */ 
  241       public int getRowIndex() {
  242   
  243           return (index);
  244   
  245       }
  246   
  247   
  248       /**
  249        * @throws FacesException {@inheritDoc}
  250        * @throws IllegalArgumentException {@inheritDoc}
  251        */ 
  252       public void setRowIndex(int rowIndex) {
  253   
  254           if (rowIndex < -1) {
  255               throw new IllegalArgumentException();
  256           }
  257   
  258           // Tell the ResultSet that the previous row was updated if necessary
  259           if (updated && (resultSet != null)) {
  260               try {
  261   		if (!resultSet.rowDeleted()) {
  262   		    resultSet.updateRow();
  263   		}
  264                   updated = false;
  265               } catch (SQLException e) {
  266                   throw new FacesException(e);
  267               }
  268           }
  269   
  270           int old = index;
  271           index = rowIndex;
  272   	if (resultSet == null) {
  273   	    return;
  274   	}
  275   	DataModelListener [] listeners = getDataModelListeners();
  276           if ((old != index) && (listeners != null)) {
  277               Object rowData = null;
  278               if (isRowAvailable()) {
  279                   rowData = getRowData();
  280               }
  281               DataModelEvent event =
  282                   new DataModelEvent(this, index, rowData);
  283               int n = listeners.length;
  284               for (int i = 0; i < n; i++) {
  285   		if (null != listeners[i]) {
  286   		    listeners[i].rowSelected(event);
  287   		}
  288               }
  289           }
  290   
  291   
  292       }
  293   
  294   
  295       public Object getWrappedData() {
  296   
  297           return (this.resultSet);
  298   
  299       }
  300   
  301   
  302       /**
  303        * @throws ClassCastException {@inheritDoc}
  304        */
  305       public void setWrappedData(Object data) {
  306   
  307           if (data == null) {
  308               metadata = null;
  309               resultSet = null;
  310               setRowIndex(-1);
  311           } else {
  312               metadata = null;
  313               resultSet = (ResultSet) data;
  314               index = -1;
  315               setRowIndex(0);
  316           }
  317       }
  318   
  319   
  320       // --------------------------------------------------------- Private Methods
  321   
  322   
  323       /**
  324        * <p>Return the <code>ResultSetMetaData</code> for the
  325        * <code>ResultSet</code> we are wrapping, caching it the first time
  326        * it is returned.</p>
  327        *
  328        * @throws FacesException if the <code>ResultSetMetaData</code>
  329        *  cannot be acquired
  330        */
  331       private ResultSetMetaData getMetaData() {
  332   
  333           if (metadata == null) {
  334               try {
  335                   metadata = resultSet.getMetaData();
  336               } catch (SQLException e) {
  337                   throw new FacesException(e);
  338               }
  339           }
  340           return (metadata);
  341   
  342       }
  343   
  344   
  345       /**
  346        * <p>Mark the current row as having been updated, so that we will call
  347        * <code>updateRow()</code> before moving elsewhere.</p>
  348        */
  349       private void updated() {
  350   
  351           this.updated = true;
  352   
  353       }
  354   
  355   
  356       // --------------------------------------------------------- Private Classes
  357   
  358   
  359       // Private implementation of Map that delegates column get and put
  360       // operations to the underlying ResultSet, after setting the required
  361       // row index
  362       private class ResultSetMap extends TreeMap<String,Object> {
  363   
  364           public ResultSetMap(Comparator<String> comparator) throws SQLException {
  365               super(comparator);
  366               index = ResultSetDataModel.this.index;
  367               resultSet.absolute(index + 1);
  368               int n = metadata.getColumnCount();
  369               for (int i = 1; i <= n; i++) {
  370                   super.put(metadata.getColumnName(i),
  371                             metadata.getColumnName(i));
  372               }
  373           }
  374   
  375           // The zero-relative row index of our row
  376           private int index;
  377   
  378           // Removing entries is not allowed
  379           public void clear() {
  380               throw new UnsupportedOperationException();
  381           }
  382   
  383           public boolean containsValue(Object value) {
  384               for (Iterator i = entrySet().iterator(); i .hasNext(); ) {
  385                   Map.Entry entry = (Map.Entry) i.next();
  386                   Object contained = entry.getValue();
  387                    if (value == null) {
  388                       if (contained == null) {
  389                           return (true);
  390                       }
  391                   } else {
  392                       if (value.equals(contained)) {
  393                           return (true);
  394                       }
  395                   }
  396               }
  397               return (false);
  398           }
  399   
  400           public Set<Map.Entry<String,Object>> entrySet() {
  401               return (new ResultSetEntries(this));
  402           }
  403   
  404           public Object get(Object key) {
  405               if (!containsKey(key)) {
  406                   return (null);
  407               }
  408               try {
  409                   resultSet.absolute(index + 1);
  410                   return (resultSet.getObject((String) realKey(key)));
  411               } catch (SQLException e) {
  412                   throw new FacesException(e);
  413               }
  414           }
  415   
  416           public Set<String> keySet() {
  417               return (new ResultSetKeys(this));
  418           }
  419   
  420           public Object put(String key, Object value) {
  421               if (!containsKey(key)) {
  422                   throw new IllegalArgumentException();
  423               }
  424               
  425               try {
  426                   resultSet.absolute(index + 1);
  427                   Object previous = resultSet.getObject((String) realKey(key));
  428                   if ((previous == null) && (value == null)) {
  429                       return (previous);
  430                   } else if ((previous != null) && (value != null) &&
  431                              previous.equals(value)) {
  432                       return (previous);
  433                   }
  434                   resultSet.updateObject((String) realKey(key), value);
  435                   ResultSetDataModel.this.updated();
  436                   return (previous);
  437               } catch (SQLException e) {
  438                   throw new FacesException(e);
  439               }
  440           }
  441   
  442           public void putAll(Map<? extends String, ? extends Object> map) {
  443               for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
  444                   put(entry.getKey(), entry.getValue());
  445               }
  446           }
  447   
  448           // Removing entries is not allowed
  449           public Object remove(Object key) {
  450               throw new UnsupportedOperationException();
  451           }
  452   
  453           public Collection<Object> values() {
  454               return (new ResultSetValues(this));
  455           }
  456   
  457           Object realKey(Object key) {
  458               return (super.get(key));
  459           }
  460   
  461           Iterator<String> realKeys() {
  462               return (super.keySet().iterator());
  463           }
  464   
  465       }
  466   
  467   
  468       // Private implementation of Set that implements the entrySet() behavior
  469       // for ResultSetMap
  470       private static class ResultSetEntries extends AbstractSet<Map.Entry<String,Object>> {
  471   
  472           public ResultSetEntries(ResultSetMap map) {
  473               this.map = map;
  474           }
  475   
  476           private ResultSetMap map;
  477   
  478           // Adding entries is not allowed
  479           public boolean add(Map.Entry<String,Object> o) {
  480               throw new UnsupportedOperationException();
  481           }
  482   
  483           // Adding entries is not allowed
  484           public boolean addAll(Collection c) {
  485               throw new UnsupportedOperationException();
  486           }
  487   
  488           // Removing entries is not allowed
  489           public void clear() {
  490               throw new UnsupportedOperationException();
  491           }
  492   
  493           public boolean contains(Object o) {
  494               if (o == null) {
  495                   throw new NullPointerException();
  496               }
  497               if (!(o instanceof Map.Entry)) {
  498                   return (false);
  499               }
  500               Map.Entry e = (Map.Entry) o;
  501               Object k = e.getKey();
  502               Object v = e.getValue();
  503               if (!map.containsKey(k)) {
  504                   return (false);
  505               }
  506               if (v == null) {
  507                   return (map.get(k) == null);
  508               } else {
  509                   return (v.equals(map.get(k)));
  510               }
  511           }
  512   
  513           public boolean isEmpty() {
  514               return (map.isEmpty());
  515           }
  516   
  517           public Iterator<Map.Entry<String,Object>> iterator() {
  518               return (new ResultSetEntriesIterator(map));
  519           }
  520   
  521           // Removing entries is not allowed
  522           public boolean remove(Object o) {
  523               throw new UnsupportedOperationException();
  524           }
  525   
  526           // Removing entries is not allowed
  527           public boolean removeAll(Collection c) {
  528               throw new UnsupportedOperationException();
  529           }
  530   
  531           // Removing entries is not allowed
  532           public boolean retainAll(Collection c) {
  533               throw new UnsupportedOperationException();
  534           }
  535   
  536           public int size() {
  537               return (map.size());
  538           }
  539   
  540       }
  541   
  542   
  543       // Private implementation of Iterator that implements the iterator()
  544       // behavior for the Set returned by entrySet() from ResultSetMap
  545       private static class ResultSetEntriesIterator implements Iterator<Map.Entry<String,Object>> {
  546   
  547           public ResultSetEntriesIterator(ResultSetMap map) {
  548               this.map = map;
  549               this.keys = map.keySet().iterator();
  550           }
  551   
  552           private ResultSetMap map = null;
  553           private Iterator<String> keys = null;
  554   
  555           public boolean hasNext() {
  556               return (keys.hasNext());
  557           }
  558   
  559           public Map.Entry<String,Object> next() {
  560               String key = keys.next();
  561               return (new ResultSetEntry(map, key));
  562           }
  563   
  564           // Removing entries is not allowed
  565           public void remove() {
  566               throw new UnsupportedOperationException();
  567           }
  568   
  569       }
  570   
  571   
  572       // Private implementation of Map.Entry that implements the behavior for
  573       // a single entry from the Set returned by entrySet() from ResultSetMap
  574       private static class ResultSetEntry implements Map.Entry<String,Object> {
  575   
  576           public ResultSetEntry(ResultSetMap map, String key) {
  577               this.map = map;
  578               this.key = key;
  579           }
  580   
  581           private ResultSetMap map;
  582           private String key;
  583   
  584           public boolean equals(Object o) {
  585               if (o == null) {
  586                   return (false);
  587               }
  588               if (!(o instanceof Map.Entry)) {
  589                   return (false);
  590               }
  591               Map.Entry e = (Map.Entry) o;
  592               if (key == null) {
  593                   if (e.getKey() != null) {
  594                       return (false);
  595                   }
  596               } else {
  597                   if (!key.equals(e.getKey())) {
  598                       return (false);
  599                   }
  600               }
  601               Object v = map.get(key);
  602               if (v == null) {
  603                   if (e.getValue() != null) {
  604                       return (false);
  605                   }
  606               } else {
  607                   if (!v.equals(e.getValue())) {
  608                       return (false);
  609                   }
  610               }
  611               return (true);
  612           }
  613   
  614           public String getKey() {
  615               return (key);
  616           }
  617   
  618           public Object getValue() {
  619               return (map.get(key));
  620           }
  621   
  622           public int hashCode() {
  623               Object value = map.get(key);
  624               return (((key == null) ? 0 : key.hashCode()) ^
  625                       ((value == null) ? 0 : value.hashCode()));
  626           }
  627   
  628           public Object setValue(Object value) {
  629               Object previous = map.get(key);
  630               map.put(key, value);
  631               return (previous);
  632           }
  633   
  634       }
  635   
  636   
  637       // Private implementation of Set that implements the keySet() behavior
  638       // for ResultSetMap
  639       private static class ResultSetKeys extends AbstractSet<String> {
  640   
  641           public ResultSetKeys(ResultSetMap map) {
  642               this.map = map;
  643           }
  644   
  645           private ResultSetMap map;
  646   
  647           // Adding keys is not allowed
  648           public boolean add(String o) {
  649               throw new UnsupportedOperationException();
  650           }
  651   
  652           // Adding keys is not allowed
  653           public boolean addAll(Collection c) {
  654               throw new UnsupportedOperationException();
  655           }
  656   
  657           // Removing keys is not allowed
  658           public void clear() {
  659               throw new UnsupportedOperationException();
  660           }
  661   
  662           public boolean contains(Object o) {
  663               return (map.containsKey(o));
  664           }
  665   
  666           public boolean isEmpty() {
  667               return (map.isEmpty());
  668           }
  669   
  670           public Iterator<String> iterator() {
  671               return (new ResultSetKeysIterator(map));
  672           }
  673   
  674           // Removing keys is not allowed
  675           public boolean remove(Object o) {
  676               throw new UnsupportedOperationException();
  677           }
  678   
  679           // Removing keys is not allowed
  680           public boolean removeAll(Collection c) {
  681               throw new UnsupportedOperationException();
  682           }
  683   
  684           // Removing keys is not allowed
  685           public boolean retainAll(Collection c) {
  686               throw new UnsupportedOperationException();
  687           }
  688   
  689           public int size() {
  690               return (map.size());
  691           }
  692   
  693       }
  694   
  695   
  696       // Private implementation of Iterator that implements the iterator()
  697       // behavior for the Set returned by keySet() from ResultSetMap
  698       private static class ResultSetKeysIterator implements Iterator<String> {
  699   
  700           public ResultSetKeysIterator(ResultSetMap map) {
  701               this.keys = map.realKeys();
  702           }
  703   
  704           private Iterator<String> keys = null;
  705   
  706           public boolean hasNext() {
  707               return (keys.hasNext());
  708           }
  709   
  710           public String next() {
  711               return (keys.next());
  712           }
  713   
  714           // Removing keys is not allowed
  715           public void remove() {
  716               throw new UnsupportedOperationException();
  717           }
  718   
  719       }
  720   
  721   
  722       // Private implementation of Collection that implements the behavior
  723       // for the Collection returned by values() from ResultSetMap
  724       private static class ResultSetValues extends AbstractCollection<Object> {
  725   
  726           public ResultSetValues(ResultSetMap map) {
  727               this.map = map;
  728           }
  729   
  730           private ResultSetMap map;
  731   
  732           public boolean add(Object o) {
  733               throw new UnsupportedOperationException();
  734           }
  735   
  736           public boolean addAll(Collection c) {
  737               throw new UnsupportedOperationException();
  738           }
  739   
  740           public void clear() {
  741               throw new UnsupportedOperationException();
  742           }
  743   
  744           public boolean contains(Object value) {
  745               return (map.containsValue(value));
  746           }
  747   
  748           public Iterator<Object> iterator() {
  749               return (new ResultSetValuesIterator(map));
  750           }
  751   
  752           public boolean remove(Object o) {
  753               throw new UnsupportedOperationException();
  754           }
  755   
  756           public boolean removeAll(Collection c) {
  757               throw new UnsupportedOperationException();
  758           }
  759   
  760           public boolean retainAll(Collection c) {
  761               throw new UnsupportedOperationException();
  762           }
  763   
  764           public int size() {
  765               return (map.size());
  766           }
  767   
  768       }
  769   
  770   
  771       // Private implementation of Iterator that implements the behavior
  772       // for the Iterator returned by values().iterator() from ResultSetMap
  773       private static class ResultSetValuesIterator implements Iterator<Object> {
  774   
  775           public ResultSetValuesIterator(ResultSetMap map) {
  776               this.map = map;
  777               this.keys = map.keySet().iterator();
  778           }
  779   
  780           private ResultSetMap map;
  781           private Iterator<String> keys;
  782   
  783           public boolean hasNext() {
  784               return (keys.hasNext());
  785           }
  786   
  787           public Object next() {
  788               return (map.get(keys.next()));
  789           }
  790   
  791           public void remove() {
  792               throw new UnsupportedOperationException();
  793           }
  794   
  795       }
  796   
  797   
  798   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » javax.faces.model » [javadoc | source]