001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Portions Copyright Apache Software Foundation.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common Development
010: * and Distribution License("CDDL") (collectively, the "License"). You
011: * may not use this file except in compliance with the License. You can obtain
012: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
013: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
014: * language governing permissions and limitations under the License.
015: *
016: * When distributing the software, include this License Header Notice in each
017: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
018: * Sun designates this particular file as subject to the "Classpath" exception
019: * as provided by Sun in the GPL Version 2 section of the License file that
020: * accompanied this code. If applicable, add the following below the License
021: * Header, with the fields enclosed by brackets [] replaced by your own
022: * identifying information: "Portions Copyrighted [year]
023: * [name of copyright owner]"
024: *
025: * Contributor(s):
026: *
027: * If you wish your version of this file to be governed by only the CDDL or
028: * only the GPL Version 2, indicate your decision by adding "[Contributor]
029: * elects to include this software in this distribution under the [CDDL or GPL
030: * Version 2] license." If you don't indicate a single choice of license, a
031: * recipient has the option to distribute your version of this file under
032: * either the CDDL, the GPL Version 2 or to extend the choice of license to
033: * its licensees as provided above. However, if you add GPL Version 2 code
034: * and therefore, elected the GPL Version 2 license, then the option applies
035: * only if the new code is made subject to such option by the copyright
036: * holder.
037: */
038:
039: package javax.servlet.jsp.jstl.sql;
040:
041: import java.io.Serializable;
042: import java.sql.ResultSet;
043: import java.sql.ResultSetMetaData;
044: import java.sql.SQLException;
045: import java.util.ArrayList;
046: import java.util.List;
047: import java.util.SortedMap;
048: import java.util.TreeMap;
049:
050: /**
051: * <p>This class creates a cached version of a <tt>ResultSet</tt>.
052: * It's represented as a <tt>Result</tt> implementation, capable of
053: * returing an array of <tt>Row</tt> objects containing a <tt>Column</tt>
054: * instance for each column in the row. It is not part of the JSTL
055: * API; it serves merely as a back-end to ResultSupport's static methods.
056: * Thus, we scope its access to the package.
057: *
058: * @author Hans Bergsten
059: * @author Justyna Horwat
060: */
061:
062: class ResultImpl implements Result, Serializable {
063: private List rowMap;
064: private List rowByIndex;
065: private String[] columnNames;
066: private boolean isLimited;
067:
068: /**
069: * This constructor reads the ResultSet and saves a cached
070: * copy.
071: * It's important to note that this object will be serializable only
072: * if the objects returned by the ResultSet are serializable too.
073: *
074: * @param rs an open <tt>ResultSet</tt>, positioned before the first
075: * row
076: * @param startRow beginning row to be cached
077: * @param maxRows query maximum rows limit
078: * @exception java.sql.SQLException if a database error occurs
079: */
080: public ResultImpl(ResultSet rs, int startRow, int maxRows)
081: throws SQLException {
082: rowMap = new ArrayList();
083: rowByIndex = new ArrayList();
084:
085: ResultSetMetaData rsmd = rs.getMetaData();
086: int noOfColumns = rsmd.getColumnCount();
087:
088: // Create the column name array
089: columnNames = new String[noOfColumns];
090: for (int i = 1; i <= noOfColumns; i++) {
091: columnNames[i - 1] = rsmd.getColumnName(i);
092: }
093:
094: // Throw away all rows upto startRow
095: for (int i = 0; i < startRow; i++) {
096: rs.next();
097: }
098:
099: // Process the remaining rows upto maxRows
100: int processedRows = 0;
101: while (rs.next()) {
102: if ((maxRows != -1) && (processedRows == maxRows)) {
103: isLimited = true;
104: break;
105: }
106: Object[] columns = new Object[noOfColumns];
107: SortedMap columnMap = new TreeMap(
108: String.CASE_INSENSITIVE_ORDER);
109:
110: // JDBC uses 1 as the lowest index!
111: for (int i = 1; i <= noOfColumns; i++) {
112: Object value = rs.getObject(i);
113: if (rs.wasNull()) {
114: value = null;
115: }
116: columns[i - 1] = value;
117: columnMap.put(columnNames[i - 1], value);
118: }
119: rowMap.add(columnMap);
120: rowByIndex.add(columns);
121: processedRows++;
122: }
123: }
124:
125: /**
126: * Returns an array of SortedMap objects. The SortedMap
127: * object key is the ColumnName and the value is the ColumnValue.
128: * SortedMap was created using the CASE_INSENSITIVE_ORDER
129: * Comparator so the key is the case insensitive representation
130: * of the ColumnName.
131: *
132: * @return an array of Map, or null if there are no rows
133: */
134: public SortedMap[] getRows() {
135: if (rowMap == null) {
136: return null;
137: }
138:
139: //should just be able to return SortedMap[] object
140: return (SortedMap[]) rowMap.toArray(new SortedMap[0]);
141: }
142:
143: /**
144: * Returns an array of Object[] objects. The first index
145: * designates the Row, the second the Column. The array
146: * stores the value at the specified row and column.
147: *
148: * @return an array of Object[], or null if there are no rows
149: */
150: public Object[][] getRowsByIndex() {
151: if (rowByIndex == null) {
152: return null;
153: }
154:
155: //should just be able to return Object[][] object
156: return (Object[][]) rowByIndex.toArray(new Object[0][0]);
157: }
158:
159: /**
160: * Returns an array of String objects. The array represents
161: * the names of the columns arranged in the same order as in
162: * the getRowsByIndex() method.
163: *
164: * @return an array of String[]
165: */
166: public String[] getColumnNames() {
167: return columnNames;
168: }
169:
170: /**
171: * Returns the number of rows in the cached ResultSet
172: *
173: * @return the number of cached rows, or -1 if the Result could
174: * not be initialized due to SQLExceptions
175: */
176: public int getRowCount() {
177: if (rowMap == null) {
178: return -1;
179: }
180: return rowMap.size();
181: }
182:
183: /**
184: * Returns true if the query was limited by a maximum row setting
185: *
186: * @return true if the query was limited by a MaxRows attribute
187: */
188: public boolean isLimitedByMaxRows() {
189: return isLimited;
190: }
191:
192: }
|