View Javadoc

1   /*
2    * Copyright 2002,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  
17  package org.apache.commons.jelly.tags.sql;
18  
19  import java.sql.*;
20  import java.util.*;
21  import javax.servlet.jsp.jstl.sql.Result;
22  
23  /***
24   * <p>This class creates a cached version of a <tt>ResultSet</tt>.
25   * It's represented as a <tt>Result</tt> implementation, capable of
26   * returing an array of <tt>Row</tt> objects containing a <tt>Column</tt>
27   * instance for each column in the row.</p>
28   *
29   * <p>Note -- this is a private copy for the RI to avoid making the
30   * corresponding class in javax.servlet.* public.</p>
31   *
32   * @author Hans Bergsten
33   * @author Justyna Horwat
34   */
35  
36  public class ResultImpl implements Result {
37      private List rowMap;
38      private List rowByIndex;
39      private String[] columnNames;
40      private boolean isLimited;
41  
42      /***
43       * This constructor reads the ResultSet and saves a cached
44       * copy.
45       *
46       * @param rs an open <tt>ResultSet</tt>, positioned before the first
47       * row
48       * @param startRow, beginning row to be cached
49       * @param maxRows, query maximum rows limit
50       * @exception if a database error occurs
51       */
52      public ResultImpl(ResultSet rs, int startRow, int maxRows)
53          throws SQLException {
54  
55          rowMap = new ArrayList();
56          rowByIndex = new ArrayList();
57  
58          ResultSetMetaData rsmd = rs.getMetaData();
59          int noOfColumns = rsmd.getColumnCount();
60  
61          // Create the column name array
62          columnNames = new String[noOfColumns];
63          for (int i = 1; i <= noOfColumns; i++) {
64              columnNames[i-1] = rsmd.getColumnName(i);
65          }
66  
67          // Throw away all rows upto startRow
68          for (int i = 0; i < startRow; i++) {
69              rs.next();
70          }
71  
72          // Process the remaining rows upto maxRows
73          int processedRows = 0;
74          while (rs.next()) {
75              if ((maxRows != -1) && (processedRows == maxRows)) {
76                  isLimited = true;
77                  break;
78              }
79              Object[] columns = new Object[noOfColumns];
80              SortedMap columnMap =
81                  new TreeMap(String.CASE_INSENSITIVE_ORDER);
82  
83              // JDBC uses 1 as the lowest index!
84              for (int i = 1; i <= noOfColumns; i++) {
85                  Object value =  rs.getObject(i);
86                  if (rs.wasNull()) {
87                      value = null;
88                  }
89                  columns[i-1] = value;
90                  columnMap.put(columnNames[i-1], value);
91              }
92              rowMap.add(columnMap);
93              rowByIndex.add(columns);
94              processedRows++;
95          }
96      }
97  
98      /***
99       * This constructor is given a List of Maps where each Map represents a Row of data.
100      * This constructor is typically used to create a Mock Object representing a result set.
101      *
102      * @param listOfMaps is a list of Maps where a Map represents a Row keyed by the column name
103      */
104     public ResultImpl(List listOfMaps) {
105 
106         rowMap = new ArrayList();
107         rowByIndex = new ArrayList();
108         isLimited = false;
109 
110         // lets build up a Set of all the unique column names
111         HashSet keySet = new HashSet();
112         for (Iterator iter = listOfMaps.iterator(); iter.hasNext(); ) {
113             Map row = (Map) iter.next();
114             keySet.addAll( row.keySet() );
115         }
116 
117         // Create the column name array
118         int noOfColumns = keySet.size();
119         columnNames = new String[noOfColumns];
120         int i = 0;
121         for (Iterator iter = keySet.iterator(); iter.hasNext(); i++ ) {
122             columnNames[i] = (String) iter.next();
123         }
124 
125         // Now add each row to the result set
126         for (Iterator iter = listOfMaps.iterator(); iter.hasNext(); ) {
127             Map row = (Map) iter.next();
128 
129             Object[] columns = new Object[noOfColumns];
130             SortedMap columnMap =
131                 new TreeMap(String.CASE_INSENSITIVE_ORDER);
132 
133             for (i = 0; i < noOfColumns; i++) {
134                 String columnName = columnNames[i];
135                 Object value = row.get(columnName);
136                 columns[i] = value;
137                 columnMap.put(columnName, value);
138             }
139             rowMap.add(columnMap);
140             rowByIndex.add(columns);
141         }
142     }
143 
144     /***
145      * Returns an array of SortedMap objects. The SortedMap
146      * object key is the ColumnName and the value is the ColumnValue.
147      * SortedMap was created using the CASE_INSENSITIVE_ORDER
148      * Comparator so the key is the case insensitive representation
149      * of the ColumnName.
150      *
151      * @return an array of Map, or null if there are no rows
152      */
153     public SortedMap[] getRows() {
154         if (rowMap == null) {
155             return null;
156         }
157 
158         //should just be able to return SortedMap[] object
159         return (SortedMap []) rowMap.toArray(new SortedMap[0]);
160     }
161 
162 
163     /***
164      * Returns an array of Object[] objects. The first index
165      * designates the Row, the second the Column. The array
166      * stores the value at the specified row and column.
167      *
168      * @return an array of Object[], or null if there are no rows
169      */
170     public Object[][] getRowsByIndex() {
171         if (rowByIndex == null) {
172             return null;
173         }
174 
175         //should just be able to return Object[][] object
176         return (Object [][])rowByIndex.toArray(new Object[0][0]);
177     }
178 
179     /***
180      * Returns an array of String objects. The array represents
181      * the names of the columns arranged in the same order as in
182      * the getRowsByIndex() method.
183      *
184      * @return an array of String[]
185      */
186     public String[] getColumnNames() {
187         return columnNames;
188     }
189 
190     /***
191      * Returns the number of rows in the cached ResultSet
192      *
193      * @return the number of cached rows, or -1 if the Result could
194      *    not be initialized due to SQLExceptions
195      */
196     public int getRowCount() {
197         if (rowMap == null) {
198             return -1;
199         }
200         return rowMap.size();
201     }
202 
203     /***
204      * Returns true of the query was limited by a maximum row setting
205      *
206      * @return true if the query was limited by a MaxRows attribute
207      */
208     public boolean isLimitedByMaxRows() {
209         return isLimited;
210     }
211 
212 }