Source Code Cross Referenced for LargeSelect.java in  » Database-ORM » Torque » org » apache » torque » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » Torque » org.apache.torque.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package org.apache.torque.util;
0002:
0003:        /*
0004:         * Licensed to the Apache Software Foundation (ASF) under one
0005:         * or more contributor license agreements.  See the NOTICE file
0006:         * distributed with this work for additional information
0007:         * regarding copyright ownership.  The ASF licenses this file
0008:         * to you under the Apache License, Version 2.0 (the
0009:         * "License"); you may not use this file except in compliance
0010:         * with the License.  You may obtain a copy of the License at
0011:         *
0012:         *   http://www.apache.org/licenses/LICENSE-2.0
0013:         *
0014:         * Unless required by applicable law or agreed to in writing,
0015:         * software distributed under the License is distributed on an
0016:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0017:         * KIND, either express or implied.  See the License for the
0018:         * specific language governing permissions and limitations
0019:         * under the License.
0020:         */
0021:
0022:        import java.io.IOException;
0023:        import java.io.ObjectInputStream;
0024:        import java.io.Serializable;
0025:        import java.lang.reflect.Method;
0026:        import java.sql.Connection;
0027:        import java.sql.SQLException;
0028:        import java.util.ArrayList;
0029:        import java.util.Hashtable;
0030:        import java.util.Iterator;
0031:        import java.util.List;
0032:        import java.util.Set;
0033:
0034:        import org.apache.commons.logging.Log;
0035:        import org.apache.commons.logging.LogFactory;
0036:        import org.apache.torque.Torque;
0037:        import org.apache.torque.TorqueException;
0038:
0039:        import com.workingdogs.village.DataSetException;
0040:        import com.workingdogs.village.QueryDataSet;
0041:
0042:        /**
0043:         * This class can be used to retrieve a large result set from a database query.
0044:         * The query is started and then rows are returned a page at a time.  The <code>
0045:         * LargeSelect</code> is meant to be placed into the Session or User.Temp, so
0046:         * that it can be used in response to several related requests.  Note that in
0047:         * order to use <code>LargeSelect</code> you need to be willing to accept the
0048:         * fact that the result set may become inconsistent with the database if updates
0049:         * are processed subsequent to the queries being executed.  Specifying a memory
0050:         * page limit of 1 will give you a consistent view of the records but the totals
0051:         * may not be accurate and the performance will be terrible.  In most cases
0052:         * the potential for inconsistencies data should not cause any serious problems
0053:         * and performance should be pretty good (but read on for further warnings).
0054:         *
0055:         * <p>The idea here is that the full query result would consume too much memory
0056:         * and if displayed to a user the page would be too long to be useful.  Rather
0057:         * than loading the full result set into memory, a window of data (the memory
0058:         * limit) is loaded and retrieved a page at a time.  If a request occurs for
0059:         * data that falls outside the currently loaded window of data then a new query
0060:         * is executed to fetch the required data.  Performance is optimized by
0061:         * starting a thread to execute the database query and fetch the results.  This
0062:         * will perform best when paging forwards through the data, but a minor
0063:         * optimization where the window is moved backwards by two rather than one page
0064:         * is included for when a user pages past the beginning of the window.
0065:         *
0066:         * <p>As the query is performed in in steps, it is often the case that the total
0067:         * number of records and pages of data is unknown.  <code>LargeSelect</code>
0068:         * provides various methods for indicating how many records and pages it is
0069:         * currently aware of and for presenting this information to users.
0070:         *
0071:         * <p><code>LargeSelect</code> utilises the <code>Criteria</code> methods
0072:         * <code>setOffset()</code> and <code>setLimit()</code> to limit the amount of
0073:         * data retrieved from the database - these values are either passed through to
0074:         * the DBMS when supported (efficient with the caveat below) or handled by
0075:         * the Village API when it is not (not so efficient).  At time of writing
0076:         * <code>Criteria</code> will only pass the offset and limit through to MySQL
0077:         * and PostgreSQL (with a few changes to <code>DBOracle</code> and <code>
0078:         * BasePeer</code> Oracle support can be implemented by utilising the <code>
0079:         * rownum</code> pseudo column).
0080:         *
0081:         * <p>As <code>LargeSelect</code> must re-execute the query each time the user
0082:         * pages out of the window of loaded data, you should consider the impact of
0083:         * non-index sort orderings and other criteria that will require the DBMS to
0084:         * execute the entire query before filtering down to the offset and limit either
0085:         * internally or via Village.
0086:         *
0087:         * <p>The memory limit defaults to 5 times the page size you specify, but
0088:         * alternative constructors and the class method <code>setMemoryPageLimit()
0089:         * </code> allow you to override this for a specific instance of
0090:         * <code>LargeSelect</code> or future instances respectively.
0091:         *
0092:         * <p>Some of the constructors allow you to specify the name of the class to use
0093:         * to build the returnd rows.  This works by using reflection to find <code>
0094:         * addSelectColumns(Criteria)</code> and <code>populateObjects(List)</code>
0095:         * methods to add the necessary select columns to the criteria (only if it
0096:         * doesn't already contain any) and to convert query results from Village
0097:         * <code>Record</code> objects to a class defined within the builder class.
0098:         * This allows you to use any of the Torque generated Peer classes, but also
0099:         * makes it fairly simple to construct business object classes that can be used
0100:         * for this purpose (simply copy and customise the <code>addSelectColumns()
0101:         * </code>, <code>populateObjects()</code>, <code>row2Object()</code> and <code>
0102:         * populateObject()</code> methods from an existing Peer class).
0103:         *
0104:         * <p>Typically you will create a <code>LargeSelect</code> using your <code>
0105:         * Criteria</code> (perhaps created from the results of a search parameter
0106:         * page), page size, memory page limit and return class name (for which you may
0107:         * have defined a business object class before hand) and place this in user.Temp
0108:         * thus:
0109:         *
0110:         * <pre>
0111:         *     data.getUser().setTemp("someName", largeSelect);
0112:         * </pre>
0113:         *
0114:         * <p>In your template you will then use something along the lines of:
0115:         *
0116:         * <pre>
0117:         *    #set($largeSelect = $data.User.getTemp("someName"))
0118:         *    #set($searchop = $data.Parameters.getString("searchop"))
0119:         *    #if($searchop.equals("prev"))
0120:         *      #set($recs = $largeSelect.PreviousResults)
0121:         *    #else
0122:         *      #if($searchop.equals("goto"))
0123:         *        #set($recs = $largeSelect.getPage($data.Parameters.getInt("page", 1)))
0124:         *      #else
0125:         *        #set($recs = $largeSelect.NextResults)
0126:         *      #end
0127:         *    #end
0128:         * </pre>
0129:         *
0130:         * <p>...to move through the records.  <code>LargeSelect</code> implements a
0131:         * number of convenience methods that make it easy to add all of the necessary
0132:         * bells and whistles to your template.
0133:         *
0134:         * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
0135:         * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
0136:         * @version $Id: LargeSelect.java 534001 2007-05-01 10:46:05Z tv $
0137:         */
0138:        public class LargeSelect implements  Runnable, Serializable {
0139:            /** Serial version */
0140:            private static final long serialVersionUID = -1166842932571491942L;
0141:
0142:            /** The number of records that a page consists of.  */
0143:            private int pageSize;
0144:            /** The maximum number of records to maintain in memory. */
0145:            private int memoryLimit;
0146:
0147:            /** The record number of the first record in memory. */
0148:            private transient int blockBegin = 0;
0149:            /** The record number of the last record in memory. */
0150:            private transient int blockEnd;
0151:            /** How much of the memory block is currently occupied with result data. */
0152:            private volatile int currentlyFilledTo = -1;
0153:
0154:            /** The SQL query that this <code>LargeSelect</code> represents. */
0155:            private String query;
0156:            /** The database name to get from Torque. */
0157:            private String dbName;
0158:
0159:            /** The memory store of records. */
0160:            private transient List results = null;
0161:
0162:            /** The thread that executes the query. */
0163:            private transient Thread thread = null;
0164:            /**
0165:             * A flag used to kill the thread when the currently executing query is no
0166:             * longer required.
0167:             */
0168:            private transient volatile boolean killThread = false;
0169:            /** A flag that indicates whether or not the query thread is running. */
0170:            private transient volatile boolean threadRunning = false;
0171:            /**
0172:             * An indication of whether or not the current query has completed
0173:             * processing.
0174:             */
0175:            private transient volatile boolean queryCompleted = false;
0176:            /**
0177:             * An indication of whether or not the totals (records and pages) are at
0178:             * their final values.
0179:             */
0180:            private transient boolean totalsFinalized = false;
0181:
0182:            /** The cursor position in the result set. */
0183:            private int position;
0184:            /** The total number of pages known to exist. */
0185:            private int totalPages = -1;
0186:            /** The total number of records known to exist. */
0187:            private int totalRecords = 0;
0188:
0189:            /** The criteria used for the query. */
0190:            private Criteria criteria = null;
0191:            /** The last page of results that were returned. */
0192:            private transient List lastResults;
0193:
0194:            /**
0195:             * The class that is possibly used to construct the criteria and used
0196:             * to transform the Village Records into the desired OM or business objects.
0197:             */
0198:            private Class returnBuilderClass = null;
0199:            /**
0200:             * A reference to the method in the return builder class that will
0201:             * convert the Village Records to the desired class.
0202:             */
0203:            private transient Method populateObjectsMethod = null;
0204:
0205:            /**
0206:             * The default value ("&gt;") used to indicate that the total number of
0207:             * records or pages is unknown.
0208:             */
0209:            public static final String DEFAULT_MORE_INDICATOR = "&gt;";
0210:
0211:            /**
0212:             * The value used to indicate that the total number of records or pages is
0213:             * unknown (default: "&gt;"). You can use <code>setMoreIndicator()</code>
0214:             * to change this to whatever value you like (e.g. "more than").
0215:             */
0216:            private static String moreIndicator = DEFAULT_MORE_INDICATOR;
0217:
0218:            /**
0219:             * The default value for the maximum number of pages of data to be retained
0220:             * in memory.
0221:             */
0222:            public static final int DEFAULT_MEMORY_LIMIT_PAGES = 5;
0223:
0224:            /**
0225:             * The maximum number of pages of data to be retained in memory.  Use
0226:             * <code>setMemoryPageLimit()</code> to provide your own value.
0227:             */
0228:            private static int memoryPageLimit = DEFAULT_MEMORY_LIMIT_PAGES;
0229:
0230:            /**
0231:             * The number of milliseconds to sleep when the result of a query
0232:             * is not yet available.
0233:             */
0234:            private static final int QUERY_NOT_COMPLETED_SLEEP_TIME = 500;
0235:
0236:            /**
0237:             * The number of milliseconds to sleep before retrying to stop a query.
0238:             */
0239:            private static final int QUERY_STOP_SLEEP_TIME = 100;
0240:
0241:            /** A place to store search parameters that relate to this query. */
0242:            private Hashtable params = null;
0243:
0244:            /** Logging */
0245:            private static Log log = LogFactory.getLog(LargeSelect.class);
0246:
0247:            /**
0248:             * Creates a LargeSelect whose results are returned as a <code>List</code>
0249:             * containing a maximum of <code>pageSize</code> Village <code>Record</code>
0250:             * objects at a time, maintaining a maximum of
0251:             * <code>LargeSelect.memoryPageLimit</code> pages of results in memory.
0252:             *
0253:             * @param criteria object used by BasePeer to build the query.  In order to
0254:             * allow this class to utilise database server implemented offsets and
0255:             * limits (when available), the provided criteria must not have any limit or
0256:             * offset defined.
0257:             * @param pageSize number of rows to return in one block.
0258:             * @throws IllegalArgumentException if <code>criteria</code> uses one or
0259:             * both of offset and limit, or if <code>pageSize</code> is less than 1;
0260:             */
0261:            public LargeSelect(Criteria criteria, int pageSize) {
0262:                this (criteria, pageSize, LargeSelect.memoryPageLimit);
0263:            }
0264:
0265:            /**
0266:             * Creates a LargeSelect whose results are returned as a <code>List</code>
0267:             * containing a maximum of <code>pageSize</code> Village <code>Record</code>
0268:             * objects at a time, maintaining a maximum of <code>memoryPageLimit</code>
0269:             * pages of results in memory.
0270:             *
0271:             * @param criteria object used by BasePeer to build the query.  In order to
0272:             * allow this class to utilise database server implemented offsets and
0273:             * limits (when available), the provided criteria must not have any limit or
0274:             * offset defined.
0275:             * @param pageSize number of rows to return in one block.
0276:             * @param memoryPageLimit maximum number of pages worth of rows to be held
0277:             * in memory at one time.
0278:             * @throws IllegalArgumentException if <code>criteria</code> uses one or
0279:             * both of offset and limit, or if <code>pageSize</code> or
0280:             * <code>memoryLimitPages</code> are less than 1;
0281:             */
0282:            public LargeSelect(Criteria criteria, int pageSize,
0283:                    int memoryPageLimit) {
0284:                init(criteria, pageSize, memoryPageLimit);
0285:            }
0286:
0287:            /**
0288:             * Creates a LargeSelect whose results are returned as a <code>List</code>
0289:             * containing a maximum of <code>pageSize</code> objects of the type
0290:             * defined within the class named <code>returnBuilderClassName</code> at a
0291:             * time, maintaining a maximum of <code>LargeSelect.memoryPageLimit</code>
0292:             * pages of results in memory.
0293:             *
0294:             * @param criteria object used by BasePeer to build the query.  In order to
0295:             * allow this class to utilise database server implemented offsets and
0296:             * limits (when available), the provided criteria must not have any limit or
0297:             * offset defined.  If the criteria does not include the definition of any
0298:             * select columns the <code>addSelectColumns(Criteria)</code> method of
0299:             * the class named as <code>returnBuilderClassName</code> will be used to
0300:             * add them.
0301:             * @param pageSize number of rows to return in one block.
0302:             * @param returnBuilderClassName The name of the class that will be used to
0303:             * build the result records (may implement <code>addSelectColumns(Criteria)
0304:             * </code> and must implement <code>populateObjects(List)</code>).
0305:             * @throws IllegalArgumentException if <code>criteria</code> uses one or
0306:             * both of offset and limit, if <code>pageSize</code> is less than 1, or if
0307:             * problems are experienced locating and invoking either one or both of
0308:             * <code>addSelectColumns(Criteria)</code> and <code> populateObjects(List)
0309:             * </code> in the class named <code>returnBuilderClassName</code>.
0310:             */
0311:            public LargeSelect(Criteria criteria, int pageSize,
0312:                    String returnBuilderClassName) {
0313:                this (criteria, pageSize, LargeSelect.memoryPageLimit,
0314:                        returnBuilderClassName);
0315:            }
0316:
0317:            /**
0318:             * Creates a LargeSelect whose results are returned as a <code>List</code>
0319:             * containing a maximum of <code>pageSize</code> objects of the type
0320:             * defined within the class named <code>returnBuilderClassName</code> at a
0321:             * time, maintaining a maximum of <code>memoryPageLimit</code> pages of
0322:             * results in memory.
0323:             *
0324:             * @param criteria object used by BasePeer to build the query.  In order to
0325:             * allow this class to utilise database server implemented offsets and
0326:             * limits (when available), the provided criteria must not have any limit or
0327:             * offset defined.  If the criteria does not include the definition of any
0328:             * select columns the <code>addSelectColumns(Criteria)</code> method of
0329:             * the class named as <code>returnBuilderClassName</code> will be used to
0330:             * add them.
0331:             * @param pageSize number of rows to return in one block.
0332:             * @param memoryPageLimit maximum number of pages worth of rows to be held
0333:             * in memory at one time.
0334:             * @param returnBuilderClassName The name of the class that will be used to
0335:             * build the result records (may implement <code>addSelectColumns(Criteria)
0336:             * </code> and must implement <code>populateObjects(List)</code>).
0337:             * @throws IllegalArgumentException if <code>criteria</code> uses one or
0338:             * both of offset and limit, if <code>pageSize</code> or <code>
0339:             * memoryLimitPages</code> are less than 1, or if problems are experienced
0340:             * locating and invoking either one or both of <code>
0341:             * addSelectColumns(Criteria)</code> and <code> populateObjects(List)</code>
0342:             * in the class named <code>returnBuilderClassName</code>.
0343:             */
0344:            public LargeSelect(Criteria criteria, int pageSize,
0345:                    int memoryPageLimit, String returnBuilderClassName) {
0346:                try {
0347:                    this .returnBuilderClass = Class
0348:                            .forName(returnBuilderClassName);
0349:
0350:                    // Add the select columns if necessary.
0351:                    if (criteria.getSelectColumns().size() == 0) {
0352:                        Class[] argTypes = { Criteria.class };
0353:                        Method selectColumnAdder = returnBuilderClass
0354:                                .getMethod("addSelectColumns", argTypes);
0355:                        Object[] theArgs = { criteria };
0356:                        selectColumnAdder.invoke(returnBuilderClass
0357:                                .newInstance(), theArgs);
0358:                    }
0359:                } catch (Exception e) {
0360:                    throw new IllegalArgumentException(
0361:                            "The class named as returnBuilderClassName does not "
0362:                                    + "provide the necessary facilities - see javadoc.");
0363:                }
0364:
0365:                init(criteria, pageSize, memoryPageLimit);
0366:            }
0367:
0368:            /**
0369:             * Access the populateObjects method.
0370:             *
0371:             * @throws SecurityException if the security manager does not allow
0372:             *         access to the method.
0373:             * @throws NoSuchMethodException if the poulateObjects method does not
0374:             *         exist.
0375:             */
0376:            private Method getPopulateObjectsMethod()
0377:                    throws NoSuchMethodException {
0378:                if (null == populateObjectsMethod) {
0379:                    Class[] argTypes = { List.class };
0380:                    populateObjectsMethod = returnBuilderClass.getMethod(
0381:                            "populateObjects", argTypes);
0382:                }
0383:                return populateObjectsMethod;
0384:            }
0385:
0386:            /**
0387:             * Called by the constructors to start the query.
0388:             *
0389:             * @param criteria Object used by <code>BasePeer</code> to build the query.
0390:             * In order to allow this class to utilise database server implemented
0391:             * offsets and limits (when available), the provided criteria must not have
0392:             * any limit or offset defined.
0393:             * @param pageSize number of rows to return in one block.
0394:             * @param memoryLimitPages maximum number of pages worth of rows to be held
0395:             * in memory at one time.
0396:             * @throws IllegalArgumentException if <code>criteria</code> uses one or
0397:             * both of offset and limit and if <code>pageSize</code> or
0398:             * <code>memoryLimitPages</code> are less than 1;
0399:             */
0400:            private void init(Criteria criteria, int pageSize,
0401:                    int memoryLimitPages) {
0402:                if (criteria.getOffset() != 0 || criteria.getLimit() != -1) {
0403:                    throw new IllegalArgumentException(
0404:                            "criteria must not use Offset and/or Limit.");
0405:                }
0406:
0407:                if (pageSize < 1) {
0408:                    throw new IllegalArgumentException(
0409:                            "pageSize must be greater than zero.");
0410:                }
0411:
0412:                if (memoryLimitPages < 1) {
0413:                    throw new IllegalArgumentException(
0414:                            "memoryPageLimit must be greater than zero.");
0415:                }
0416:
0417:                this .pageSize = pageSize;
0418:                this .memoryLimit = pageSize * memoryLimitPages;
0419:                this .criteria = criteria;
0420:                dbName = criteria.getDbName();
0421:                blockEnd = blockBegin + memoryLimit - 1;
0422:                startQuery(pageSize);
0423:            }
0424:
0425:            /**
0426:             * Retrieve a specific page, if it exists.
0427:             *
0428:             * @param pageNumber the number of the page to be retrieved - must be
0429:             * greater than zero.  An empty <code>List</code> will be returned if
0430:             * <code>pageNumber</code> exceeds the total number of pages that exist.
0431:             * @return a <code>List</code> of query results containing a maximum of
0432:             * <code>pageSize</code> results.
0433:             * @throws IllegalArgumentException when <code>pageNo</code> is not
0434:             * greater than zero.
0435:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0436:             * method runs into problems or a sleep is unexpectedly interrupted.
0437:             */
0438:            public List getPage(int pageNumber) throws TorqueException {
0439:                if (pageNumber < 1) {
0440:                    throw new IllegalArgumentException(
0441:                            "pageNumber must be greater than zero.");
0442:                }
0443:                return getResults((pageNumber - 1) * pageSize);
0444:            }
0445:
0446:            /**
0447:             * Gets the next page of rows.
0448:             *
0449:             * @return a <code>List</code> of query results containing a maximum of
0450:             * <code>pageSize</code> reslts.
0451:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0452:             * method runs into problems or a sleep is unexpectedly interrupted.
0453:             */
0454:            public List getNextResults() throws TorqueException {
0455:                if (!getNextResultsAvailable()) {
0456:                    return getCurrentPageResults();
0457:                }
0458:                return getResults(position);
0459:            }
0460:
0461:            /**
0462:             * Provide access to the results from the current page.
0463:             *
0464:             * @return a <code>List</code> of query results containing a maximum of
0465:             * <code>pageSize</code> reslts.
0466:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0467:             * method runs into problems or a sleep is unexpectedly interrupted.
0468:             */
0469:            public List getCurrentPageResults() throws TorqueException {
0470:                return null == lastResults && position > 0 ? getResults(position)
0471:                        : lastResults;
0472:            }
0473:
0474:            /**
0475:             * Gets the previous page of rows.
0476:             *
0477:             * @return a <code>List</code> of query results containing a maximum of
0478:             * <code>pageSize</code> reslts.
0479:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0480:             * method runs into problems or a sleep is unexpectedly interrupted.
0481:             */
0482:            public List getPreviousResults() throws TorqueException {
0483:                if (!getPreviousResultsAvailable()) {
0484:                    return getCurrentPageResults();
0485:                }
0486:
0487:                int start;
0488:                if (position - 2 * pageSize < 0) {
0489:                    start = 0;
0490:                } else {
0491:                    start = position - 2 * pageSize;
0492:                }
0493:                return getResults(start);
0494:            }
0495:
0496:            /**
0497:             * Gets a page of rows starting at a specified row.
0498:             *
0499:             * @param start the starting row.
0500:             * @return a <code>List</code> of query results containing a maximum of
0501:             * <code>pageSize</code> reslts.
0502:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0503:             * method runs into problems or a sleep is unexpectedly interrupted.
0504:             */
0505:            private List getResults(int start) throws TorqueException {
0506:                return getResults(start, pageSize);
0507:            }
0508:
0509:            /**
0510:             * Gets a block of rows starting at a specified row and containing a
0511:             * specified number of rows.
0512:             *
0513:             * @param start the starting row.
0514:             * @param size the number of rows.
0515:             * @return a <code>List</code> of query results containing a maximum of
0516:             * <code>pageSize</code> reslts.
0517:             * @throws IllegalArgumentException if <code>size &gt; memoryLimit</code> or
0518:             * <code>start</code> and <code>size</code> result in a situation that is
0519:             * not catered for.
0520:             * @throws TorqueException if invoking the <code>populateObjects()<code>
0521:             * method runs into problems or a sleep is unexpectedly interrupted.
0522:             */
0523:            private synchronized List getResults(int start, int size)
0524:                    throws TorqueException {
0525:                if (log.isDebugEnabled()) {
0526:                    log.debug("getResults(start: " + start + ", size: " + size
0527:                            + ") invoked.");
0528:                }
0529:
0530:                if (size > memoryLimit) {
0531:                    throw new IllegalArgumentException("size (" + size
0532:                            + ") exceeds memory limit (" + memoryLimit + ").");
0533:                }
0534:
0535:                // Request was for a block of rows which should be in progess.
0536:                // If the rows have not yet been returned, wait for them to be
0537:                // retrieved.
0538:                if (start >= blockBegin && (start + size - 1) <= blockEnd) {
0539:                    if (log.isDebugEnabled()) {
0540:                        log.debug("getResults(): Sleeping until "
0541:                                + "start+size-1 (" + (start + size - 1)
0542:                                + ") > currentlyFilledTo (" + currentlyFilledTo
0543:                                + ") && !queryCompleted (!" + queryCompleted
0544:                                + ")");
0545:                    }
0546:                    while (((start + size - 1) > currentlyFilledTo)
0547:                            && !queryCompleted) {
0548:                        try {
0549:                            Thread.sleep(QUERY_NOT_COMPLETED_SLEEP_TIME);
0550:                        } catch (InterruptedException e) {
0551:                            throw new TorqueException(
0552:                                    "Unexpected interruption", e);
0553:                        }
0554:                    }
0555:                }
0556:
0557:                // Going in reverse direction, trying to limit db hits so assume user
0558:                // might want at least 2 sets of data.
0559:                else if (start < blockBegin && start >= 0) {
0560:                    if (log.isDebugEnabled()) {
0561:                        log.debug("getResults(): Paging backwards as start ("
0562:                                + start + ") < blockBegin (" + blockBegin
0563:                                + ") && start >= 0");
0564:                    }
0565:                    stopQuery();
0566:                    if (memoryLimit >= 2 * size) {
0567:                        blockBegin = start - size;
0568:                        if (blockBegin < 0) {
0569:                            blockBegin = 0;
0570:                        }
0571:                    } else {
0572:                        blockBegin = start;
0573:                    }
0574:                    blockEnd = blockBegin + memoryLimit - 1;
0575:                    startQuery(size);
0576:                    // Re-invoke getResults() to provide the wait processing.
0577:                    return getResults(start, size);
0578:                }
0579:
0580:                // Assume we are moving on, do not retrieve any records prior to start.
0581:                else if ((start + size - 1) > blockEnd) {
0582:                    if (log.isDebugEnabled()) {
0583:                        log
0584:                                .debug("getResults(): Paging past end of loaded data as "
0585:                                        + "start+size-1 ("
0586:                                        + (start + size - 1)
0587:                                        + ") > blockEnd (" + blockEnd + ")");
0588:                    }
0589:                    stopQuery();
0590:                    blockBegin = start;
0591:                    blockEnd = blockBegin + memoryLimit - 1;
0592:                    startQuery(size);
0593:                    // Re-invoke getResults() to provide the wait processing.
0594:                    return getResults(start, size);
0595:                }
0596:
0597:                else {
0598:                    throw new IllegalArgumentException(
0599:                            "Parameter configuration not " + "accounted for.");
0600:                }
0601:
0602:                int fromIndex = start - blockBegin;
0603:                int toIndex = fromIndex
0604:                        + Math.min(size, results.size() - fromIndex);
0605:
0606:                if (log.isDebugEnabled()) {
0607:                    log
0608:                            .debug("getResults(): Retrieving records from results elements "
0609:                                    + "start-blockBegin ("
0610:                                    + fromIndex
0611:                                    + ") through "
0612:                                    + "fromIndex + Math.min(size, results.size() - fromIndex) ("
0613:                                    + toIndex + ")");
0614:                }
0615:
0616:                List returnResults;
0617:
0618:                synchronized (results) {
0619:                    returnResults = new ArrayList(results.subList(fromIndex,
0620:                            toIndex));
0621:                }
0622:
0623:                if (null != returnBuilderClass) {
0624:                    // Invoke the populateObjects() method
0625:                    Object[] theArgs = { returnResults };
0626:                    try {
0627:                        returnResults = (List) getPopulateObjectsMethod()
0628:                                .invoke(returnBuilderClass.newInstance(),
0629:                                        theArgs);
0630:                    } catch (Exception e) {
0631:                        throw new TorqueException("Unable to populate results",
0632:                                e);
0633:                    }
0634:                }
0635:                position = start + size;
0636:                lastResults = returnResults;
0637:                return returnResults;
0638:            }
0639:
0640:            /**
0641:             * A background thread that retrieves the rows.
0642:             */
0643:            public void run() {
0644:                boolean dbSupportsNativeLimit;
0645:                boolean dbSupportsNativeOffset;
0646:                try {
0647:                    dbSupportsNativeLimit = (Torque.getDB(dbName)
0648:                            .supportsNativeLimit());
0649:                    dbSupportsNativeOffset = (Torque.getDB(dbName)
0650:                            .supportsNativeOffset());
0651:                } catch (TorqueException e) {
0652:                    log.error("run() : Exiting :", e);
0653:                    // we cannot execute further because Torque is not initialized
0654:                    // correctly
0655:                    return;
0656:                }
0657:
0658:                int size;
0659:                if (dbSupportsNativeLimit && dbSupportsNativeOffset) {
0660:                    // retrieve one page at a time
0661:                    size = pageSize;
0662:                } else {
0663:                    // retrieve the whole block at once and add the offset,
0664:                    // and add one record to check if we have reached the end of the
0665:                    // data
0666:                    size = blockBegin + memoryLimit + 1;
0667:                }
0668:                /* The connection to the database. */
0669:                Connection conn = null;
0670:                /** Used to retrieve query results from Village. */
0671:                QueryDataSet qds = null;
0672:
0673:                try {
0674:                    // Add 1 to memory limit to check if the query ends on a page break.
0675:                    results = new ArrayList(memoryLimit + 1);
0676:
0677:                    // Use the criteria to limit the rows that are retrieved to the
0678:                    // block of records that fit in the predefined memoryLimit.
0679:                    if (dbSupportsNativeLimit) {
0680:                        if (dbSupportsNativeOffset) {
0681:                            criteria.setOffset(blockBegin);
0682:                            // Add 1 to memory limit to check if the query ends on a
0683:                            // page break.
0684:                            criteria.setLimit(memoryLimit + 1);
0685:                        } else {
0686:                            criteria.setLimit(blockBegin + memoryLimit + 1);
0687:                        }
0688:                    }
0689:
0690:                    /* 
0691:                     * Fix criterions relating to booleanint or booleanchar columns
0692:                     * The defaultTableMap parameter in this call is null because we have
0693:                     * no default peer class inside LargeSelect. This means that all
0694:                     * columns not fully qualified will not be modified.
0695:                     */
0696:                    BasePeer.correctBooleans(criteria, null);
0697:
0698:                    query = BasePeer.createQueryString(criteria);
0699:
0700:                    // Get a connection to the db.
0701:                    conn = Torque.getConnection(dbName);
0702:
0703:                    // Execute the query.
0704:                    if (log.isDebugEnabled()) {
0705:                        log.debug("run(): query = " + query);
0706:                        log.debug("run(): memoryLimit = " + memoryLimit);
0707:                        log.debug("run(): blockBegin = " + blockBegin);
0708:                        log.debug("run(): blockEnd = " + blockEnd);
0709:                    }
0710:                    qds = new QueryDataSet(conn, query);
0711:
0712:                    // Continue getting rows one page at a time until the memory limit
0713:                    // is reached, all results have been retrieved, or the rest
0714:                    // of the results have been determined to be irrelevant.
0715:                    while (!killThread && !qds.allRecordsRetrieved()
0716:                            && currentlyFilledTo + pageSize <= blockEnd) {
0717:                        // This caters for when memoryLimit is not a multiple of
0718:                        //  pageSize which it never is because we always add 1 above.
0719:                        // not applicable if the db has no native limit where this
0720:                        // was already considered
0721:                        if ((currentlyFilledTo + pageSize) >= blockEnd
0722:                                && dbSupportsNativeLimit) {
0723:                            // Add 1 to check if the query ends on a page break.
0724:                            size = blockEnd - currentlyFilledTo + 1;
0725:                        }
0726:
0727:                        if (log.isDebugEnabled()) {
0728:                            log
0729:                                    .debug("run(): Invoking BasePeer.getSelectResults(qds, "
0730:                                            + size + ", false)");
0731:                        }
0732:
0733:                        List tempResults = BasePeer.getSelectResults(qds, size,
0734:                                false);
0735:
0736:                        int startIndex = dbSupportsNativeOffset ? 0
0737:                                : blockBegin;
0738:
0739:                        synchronized (results) {
0740:                            for (int i = startIndex, n = tempResults.size(); i < n; i++) {
0741:                                results.add(tempResults.get(i));
0742:                            }
0743:                        }
0744:
0745:                        if (dbSupportsNativeLimit && dbSupportsNativeOffset) {
0746:                            currentlyFilledTo += tempResults.size();
0747:                        } else {
0748:                            currentlyFilledTo = tempResults.size() - 1
0749:                                    - blockBegin;
0750:                        }
0751:
0752:                        boolean perhapsLastPage = true;
0753:
0754:                        // If the extra record was indeed found then we know we are not
0755:                        // on the last page but we must now get rid of it.
0756:                        if ((dbSupportsNativeLimit && (results.size() == memoryLimit + 1))
0757:                                || (!dbSupportsNativeLimit && currentlyFilledTo >= memoryLimit)) {
0758:                            synchronized (results) {
0759:                                results.remove(currentlyFilledTo--);
0760:                            }
0761:                            perhapsLastPage = false;
0762:                        }
0763:
0764:                        if (results.size() > 0
0765:                                && blockBegin + currentlyFilledTo >= totalRecords) {
0766:                            // Add 1 because index starts at 0
0767:                            totalRecords = blockBegin + currentlyFilledTo + 1;
0768:                        }
0769:
0770:                        // if the db has limited the datasets, we must retrieve all
0771:                        // datasets. If not, we are always finished because we fetch
0772:                        // the whole block at once.
0773:                        if (qds.allRecordsRetrieved() || !dbSupportsNativeLimit) {
0774:                            queryCompleted = true;
0775:                            // The following ugly condition ensures that the totals are
0776:                            // not finalized when a user does something like requesting
0777:                            // a page greater than what exists in the database.
0778:                            if (perhapsLastPage
0779:                                    && getCurrentPageNumber() <= getTotalPages()) {
0780:                                totalsFinalized = true;
0781:                            }
0782:                        }
0783:                        qds.clearRecords();
0784:                    }
0785:
0786:                    if (log.isDebugEnabled()) {
0787:                        log
0788:                                .debug("run(): While loop terminated because either:");
0789:                        log.debug("run(): 1. qds.allRecordsRetrieved(): "
0790:                                + qds.allRecordsRetrieved());
0791:                        log.debug("run(): 2. killThread: " + killThread);
0792:                        log
0793:                                .debug("run(): 3. !(currentlyFilledTo + size <= blockEnd): !"
0794:                                        + (currentlyFilledTo + pageSize <= blockEnd));
0795:                        log.debug("run(): - currentlyFilledTo: "
0796:                                + currentlyFilledTo);
0797:                        log.debug("run(): - size: " + pageSize);
0798:                        log.debug("run(): - blockEnd: " + blockEnd);
0799:                        log.debug("run(): - results.size(): " + results.size());
0800:                    }
0801:                } catch (TorqueException e) {
0802:                    log.error(e);
0803:                } catch (SQLException e) {
0804:                    log.error(e);
0805:                } catch (DataSetException e) {
0806:                    log.error(e);
0807:                } finally {
0808:                    try {
0809:                        if (qds != null) {
0810:                            qds.close();
0811:                        }
0812:                        Torque.closeConnection(conn);
0813:                    } catch (SQLException e) {
0814:                        log.error(e);
0815:                    } catch (DataSetException e) {
0816:                        log.error(e);
0817:                    }
0818:                    threadRunning = false;
0819:                }
0820:            }
0821:
0822:            /**
0823:             * Starts a new thread to retrieve the result set.
0824:             *
0825:             * @param initialSize the initial size for each block.
0826:             */
0827:            private synchronized void startQuery(int initialSize) {
0828:                if (!threadRunning) {
0829:                    pageSize = initialSize;
0830:                    currentlyFilledTo = -1;
0831:                    queryCompleted = false;
0832:                    thread = new Thread(this );
0833:                    thread.start();
0834:                    threadRunning = true;
0835:                }
0836:            }
0837:
0838:            /**
0839:             * Used to stop filling the memory with the current block of results, if it
0840:             * has been determined that they are no longer relevant.
0841:             *
0842:             * @throws TorqueException if a sleep is interrupted.
0843:             */
0844:            private synchronized void stopQuery() throws TorqueException {
0845:                if (threadRunning) {
0846:                    killThread = true;
0847:                    while (thread.isAlive()) {
0848:                        try {
0849:                            Thread.sleep(QUERY_STOP_SLEEP_TIME);
0850:                        } catch (InterruptedException e) {
0851:                            throw new TorqueException(
0852:                                    "Unexpected interruption", e);
0853:                        }
0854:                    }
0855:                    killThread = false;
0856:                }
0857:            }
0858:
0859:            /**
0860:             * Retrieve the number of the current page.
0861:             *
0862:             * @return the current page number.
0863:             */
0864:            public int getCurrentPageNumber() {
0865:                return position / pageSize;
0866:            }
0867:
0868:            /**
0869:             * Retrieve the total number of search result records that are known to
0870:             * exist (this will be the actual value when the query has completeted (see
0871:             * <code>getTotalsFinalized()</code>).  The convenience method
0872:             * <code>getRecordProgressText()</code> may be more useful for presenting to
0873:             * users.
0874:             *
0875:             * @return the number of result records known to exist (not accurate until
0876:             * <code>getTotalsFinalized()</code> returns <code>true</code>).
0877:             */
0878:            public int getTotalRecords() {
0879:                return totalRecords;
0880:            }
0881:
0882:            /**
0883:             * Provide an indication of whether or not paging of results will be
0884:             * required.
0885:             *
0886:             * @return <code>true</code> when multiple pages of results exist.
0887:             */
0888:            public boolean getPaginated() {
0889:                // Handle a page memory limit of 1 page.
0890:                if (!getTotalsFinalized()) {
0891:                    return true;
0892:                }
0893:                return blockBegin + currentlyFilledTo + 1 > pageSize;
0894:            }
0895:
0896:            /**
0897:             * Retrieve the total number of pages of search results that are known to
0898:             * exist (this will be the actual value when the query has completeted (see
0899:             * <code>getQyeryCompleted()</code>).  The convenience method
0900:             * <code>getPageProgressText()</code> may be more useful for presenting to
0901:             * users.
0902:             *
0903:             * @return the number of pages of results known to exist (not accurate until
0904:             * <code>getTotalsFinalized()</code> returns <code>true</code>).
0905:             */
0906:            public int getTotalPages() {
0907:                if (totalPages > -1) {
0908:                    return totalPages;
0909:                }
0910:
0911:                int tempPageCount = getTotalRecords() / pageSize
0912:                        + (getTotalRecords() % pageSize > 0 ? 1 : 0);
0913:
0914:                if (getTotalsFinalized()) {
0915:                    totalPages = tempPageCount;
0916:                }
0917:
0918:                return tempPageCount;
0919:            }
0920:
0921:            /**
0922:             * Retrieve the page size.
0923:             *
0924:             * @return the number of records returned on each invocation of
0925:             * <code>getNextResults()</code>/<code>getPreviousResults()</code>.
0926:             */
0927:            public int getPageSize() {
0928:                return pageSize;
0929:            }
0930:
0931:            /**
0932:             * Provide access to indicator that the total values for the number of
0933:             * records and pages are now accurate as opposed to known upper limits.
0934:             *
0935:             * @return <code>true</code> when the totals are known to have been fully
0936:             * computed.
0937:             */
0938:            public boolean getTotalsFinalized() {
0939:                return totalsFinalized;
0940:            }
0941:
0942:            /**
0943:             * Provide a way of changing the more pages/records indicator.
0944:             *
0945:             * @param moreIndicator the indicator to use in place of the default
0946:             * ("&gt;").
0947:             */
0948:            public static void setMoreIndicator(String moreIndicator) {
0949:                LargeSelect.moreIndicator = moreIndicator;
0950:            }
0951:
0952:            /**
0953:             * Retrieve the more pages/records indicator.
0954:             */
0955:            public static String getMoreIndicator() {
0956:                return LargeSelect.moreIndicator;
0957:            }
0958:
0959:            /**
0960:             * Sets the multiplier that will be used to compute the memory limit when a
0961:             * constructor with no memory page limit is used - the memory limit will be
0962:             * this number multiplied by the page size.
0963:             *
0964:             * @param memoryPageLimit the maximum number of pages to be in memory
0965:             * at one time.
0966:             */
0967:            public static void setMemoryPageLimit(int memoryPageLimit) {
0968:                LargeSelect.memoryPageLimit = memoryPageLimit;
0969:            }
0970:
0971:            /**
0972:             * Retrieves the multiplier that will be used to compute the memory limit
0973:             * when a constructor with no memory page limit is used - the memory limit
0974:             * will be this number multiplied by the page size.
0975:             */
0976:            public static int getMemoryPageLimit() {
0977:                return LargeSelect.memoryPageLimit;
0978:            }
0979:
0980:            /**
0981:             * A convenience method that provides text showing progress through the
0982:             * selected rows on a page basis.
0983:             *
0984:             * @return progress text in the form of "1 of &gt; 5" where "&gt;" can be
0985:             * configured using <code>setMoreIndicator()</code>.
0986:             */
0987:            public String getPageProgressText() {
0988:                StringBuffer result = new StringBuffer();
0989:                result.append(getCurrentPageNumber());
0990:                result.append(" of ");
0991:                if (!totalsFinalized) {
0992:                    result.append(moreIndicator);
0993:                    result.append(" ");
0994:                }
0995:                result.append(getTotalPages());
0996:                return result.toString();
0997:            }
0998:
0999:            /**
1000:             * Provides a count of the number of rows to be displayed on the current
1001:             * page - for the last page this may be less than the configured page size.
1002:             *
1003:             * @return the number of records that are included on the current page of
1004:             * results.
1005:             * @throws TorqueException if invoking the <code>populateObjects()<code>
1006:             * method runs into problems or a sleep is unexpectedly interrupted.
1007:             */
1008:            public int getCurrentPageSize() throws TorqueException {
1009:                if (null == getCurrentPageResults()) {
1010:                    return 0;
1011:                }
1012:                return getCurrentPageResults().size();
1013:            }
1014:
1015:            /**
1016:             * Provide the record number of the first row included on the current page.
1017:             *
1018:             * @return The record number of the first row of the current page.
1019:             */
1020:            public int getFirstRecordNoForPage() {
1021:                if (getCurrentPageNumber() < 1) {
1022:                    return 0;
1023:                }
1024:                return (getCurrentPageNumber() - 1) * getPageSize() + 1;
1025:            }
1026:
1027:            /**
1028:             * Provide the record number of the last row included on the current page.
1029:             *
1030:             * @return the record number of the last row of the current page.
1031:             * @throws TorqueException if invoking the <code>populateObjects()<code>
1032:             * method runs into problems or a sleep is unexpectedly interrupted.
1033:             */
1034:            public int getLastRecordNoForPage() throws TorqueException {
1035:                if (0 == getCurrentPageNumber()) {
1036:                    return 0;
1037:                }
1038:                return (getCurrentPageNumber() - 1) * getPageSize()
1039:                        + getCurrentPageSize();
1040:            }
1041:
1042:            /**
1043:             * A convenience method that provides text showing progress through the
1044:             * selected rows on a record basis.
1045:             *
1046:             * @return progress text in the form of "26 - 50 of &gt; 250" where "&gt;"
1047:             * can be configured using <code>setMoreIndicator()</code>.
1048:             * @throws TorqueException if invoking the <code>populateObjects()<code>
1049:             * method runs into problems or a sleep is unexpectedly interrupted.
1050:             */
1051:            public String getRecordProgressText() throws TorqueException {
1052:                StringBuffer result = new StringBuffer();
1053:                result.append(getFirstRecordNoForPage());
1054:                result.append(" - ");
1055:                result.append(getLastRecordNoForPage());
1056:                result.append(" of ");
1057:                if (!totalsFinalized) {
1058:                    result.append(moreIndicator);
1059:                    result.append(" ");
1060:                }
1061:                result.append(getTotalRecords());
1062:                return result.toString();
1063:            }
1064:
1065:            /**
1066:             * Indicates if further result pages are available.
1067:             *
1068:             * @return <code>true</code> when further results are available.
1069:             */
1070:            public boolean getNextResultsAvailable() {
1071:                if (!totalsFinalized
1072:                        || getCurrentPageNumber() < getTotalPages()) {
1073:                    return true;
1074:                }
1075:                return false;
1076:            }
1077:
1078:            /**
1079:             * Indicates if previous results pages are available.
1080:             *
1081:             * @return <code>true</code> when previous results are available.
1082:             */
1083:            public boolean getPreviousResultsAvailable() {
1084:                if (getCurrentPageNumber() <= 1) {
1085:                    return false;
1086:                }
1087:                return true;
1088:            }
1089:
1090:            /**
1091:             * Indicates if any results are available.
1092:             *
1093:             * @return <code>true</code> of any results are available.
1094:             */
1095:            public boolean hasResultsAvailable() {
1096:                return getTotalRecords() > 0;
1097:            }
1098:
1099:            /**
1100:             * Clear the query result so that the query is reexecuted when the next page
1101:             * is retrieved.  You may want to invoke this method if you are returning to
1102:             * a page after performing an operation on an item in the result set.
1103:             *
1104:             * @throws TorqueException if a sleep is interrupted.
1105:             */
1106:            public synchronized void invalidateResult() throws TorqueException {
1107:                stopQuery();
1108:                blockBegin = 0;
1109:                blockEnd = 0;
1110:                currentlyFilledTo = -1;
1111:                results = null;
1112:                // TODO Perhaps store the oldPosition and immediately restart the
1113:                // query.
1114:                // oldPosition = position;
1115:                position = 0;
1116:                totalPages = -1;
1117:                totalRecords = 0;
1118:                queryCompleted = false;
1119:                totalsFinalized = false;
1120:                lastResults = null;
1121:            }
1122:
1123:            /**
1124:             * Retrieve a search parameter.  This acts as a convenient place to store
1125:             * parameters that relate to the LargeSelect to make it easy to get at them
1126:             * in order to repopulate search parameters on a form when the next page of
1127:             * results is retrieved - they in no way effect the operation of
1128:             * LargeSelect.
1129:             *
1130:             * @param name the search parameter key to retrieve.
1131:             * @return the value of the search parameter.
1132:             */
1133:            public String getSearchParam(String name) {
1134:                return getSearchParam(name, null);
1135:            }
1136:
1137:            /**
1138:             * Retrieve a search parameter.  This acts as a convenient place to store
1139:             * parameters that relate to the LargeSelect to make it easy to get at them
1140:             * in order to repopulate search parameters on a form when the next page of
1141:             * results is retrieved - they in no way effect the operation of
1142:             * LargeSelect.
1143:             *
1144:             * @param name the search parameter key to retrieve.
1145:             * @param defaultValue the default value to return if the key is not found.
1146:             * @return the value of the search parameter.
1147:             */
1148:            public String getSearchParam(String name, String defaultValue) {
1149:                if (null == params) {
1150:                    return defaultValue;
1151:                }
1152:                String value = (String) params.get(name);
1153:                return null == value ? defaultValue : value;
1154:            }
1155:
1156:            /**
1157:             * Set a search parameter.  If the value is <code>null</code> then the
1158:             * key will be removed from the parameters.
1159:             *
1160:             * @param name the search parameter key to set.
1161:             * @param value the value of the search parameter to store.
1162:             */
1163:            public void setSearchParam(String name, String value) {
1164:                if (null == value) {
1165:                    removeSearchParam(name);
1166:                } else {
1167:                    if (null != name) {
1168:                        if (null == params) {
1169:                            params = new Hashtable();
1170:                        }
1171:                        params.put(name, value);
1172:                    }
1173:                }
1174:            }
1175:
1176:            /**
1177:             * Remove a value from the search parameters.
1178:             *
1179:             * @param name the search parameter key to remove.
1180:             */
1181:            public void removeSearchParam(String name) {
1182:                if (null != params) {
1183:                    params.remove(name);
1184:                }
1185:            }
1186:
1187:            /**
1188:             * Deserialize this LargeSelect instance.
1189:             *
1190:             * @param inputStream The serialization input stream.
1191:             * @throws IOException
1192:             * @throws ClassNotFoundException
1193:             */
1194:            private void readObject(ObjectInputStream inputStream)
1195:                    throws IOException, ClassNotFoundException {
1196:                inputStream.defaultReadObject();
1197:
1198:                // avoid NPE because of Tomcat de-serialization of sessions
1199:                if (Torque.isInit()) {
1200:                    startQuery(pageSize);
1201:                }
1202:            }
1203:
1204:            /**
1205:             * Provide something useful for debugging purposes.
1206:             *
1207:             * @return some basic information about this instance of LargeSelect.
1208:             */
1209:            public String toString() {
1210:                StringBuffer result = new StringBuffer();
1211:                result.append("LargeSelect - TotalRecords: ");
1212:                result.append(getTotalRecords());
1213:                result.append(" TotalsFinalised: ");
1214:                result.append(getTotalsFinalized());
1215:                result.append("\nParameters:");
1216:                if (null == params || params.size() == 0) {
1217:                    result.append(" No parameters have been set.");
1218:                } else {
1219:                    Set keys = params.keySet();
1220:                    for (Iterator iter = keys.iterator(); iter.hasNext();) {
1221:                        String key = (String) iter.next();
1222:                        String val = (String) params.get(key);
1223:                        result.append("\n ").append(key).append(": ").append(
1224:                                val);
1225:                    }
1226:                }
1227:                return result.toString();
1228:            }
1229:
1230:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.