001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.databinding.datagrid.api.pager;
020:
021: import org.apache.beehive.netui.util.Bundle;
022:
023: /**
024: * <p>
025: * The PagerModel is a JavaBean that represents the page state of a data grid. In the default implementation,
026: * the page state consists of three pieces of data:
027: * <ul>
028: * <li>the current row</li>
029: * <li>current page</li>
030: * <li>the href / action used to page through data</li>
031: * </ul>
032: * This pager model implementation is row based which means that the notion of the current page
033: * is based on which row is at the top of a page. Row numbering starts at zero and continues to page size.
034: * For example, in a data grid on its first page and with a page size of 10, the rows 0-9 will be displayed. The
035: * next page would contain rows 10-11 and so on.
036: * </p>
037: * <p>
038: * The pager model provides JavaBean-style access to the properties of a pager. In addition, it provides read-only
039: * access to information about the row to use in order to navigate to a specific page. To navigate to the previous
040: * page, the {@link #getRowForPreviousPage()} will return the row number that will appear at the top of the previous
041: * page. In order to build effective paging UI, it is also often useful to know the absolute page number.
042: * As with row numbers, page numbers are zero based. For example, if a data set displayed in a data grid has 30
043: * records and the grid is on page a page displaying rows 10-19, the current page is 1. When displaying this value
044: * in UI, it is often useful to display it as:
045: * <pre>
046: * Page 2 of 3
047: * </pre>
048: * Random page access can also be accomplished using the {@link #encodeRowForPage(int)} method which will return
049: * the row number to display when jumping to a specific page in a grid.
050: * </p>
051: */
052: public class PagerModel implements java.io.Serializable {
053:
054: public static final int DEFAULT_PAGE_SIZE = 10;
055: public static final int DEFAULT_ROW = 0;
056:
057: private String _pageHref = null;
058: private String _pageAction = null;
059: private Integer _currentRow = null;
060: private Integer _dataSetSize = null;
061: private Integer _explicitPageSize = null;
062: private Integer _defaultPageSize = null;
063:
064: /**
065: * Default constructor. This initializes the current row to the default row value {@link #DEFAULT_ROW}.
066: */
067: public PagerModel() {
068: _currentRow = new Integer(DEFAULT_ROW);
069: }
070:
071: /**
072: * Get the action used when building URLs for navigating to another page.
073: * @return the action name or <code>null</code> if no action name is set
074: */
075: public String getPageAction() {
076: return _pageAction;
077: }
078:
079: /**
080: * Set the action used to navigate to another page.
081: * @param pageAction the action name
082: */
083: public void setPageAction(String pageAction) {
084: _pageAction = pageAction;
085: }
086:
087: /**
088: * Get the href used when building URLs for navigating to another page.
089: * @return the href or <code>null</code> if no href is set
090: */
091: public String getPageHref() {
092: return _pageHref;
093: }
094:
095: /**
096: * Set the href used to navigate to another page.
097: * @param pageHref the href
098: */
099: public void setPageHref(String pageHref) {
100: _pageHref = pageHref;
101: }
102:
103: /**
104: * Get the default page size. If no page size has been set via {@link #setDefaultPageSize(int)} this
105: * value is {@link #DEFAULT_PAGE_SIZE}
106: * @return the default page size
107: */
108: public int getDefaultPageSize() {
109: if (_defaultPageSize != null)
110: return _defaultPageSize.intValue();
111: else
112: return DEFAULT_PAGE_SIZE;
113: }
114:
115: /**
116: * Set the default page size. The default page size is used when no other page size has been set and is useful
117: * when clients wish to occasionally override the page size but wish to have the default page size set
118: * differently than the PagerModel's default.
119: *
120: * @param pageSize the new page size
121: * @throws IllegalArgumentException if the page size is less than 1
122: */
123: public void setDefaultPageSize(int pageSize) {
124: if (pageSize < 1)
125: throw new IllegalArgumentException(
126: Bundle
127: .getErrorString("PagerModel_IllegalDefaultPageSize"));
128:
129: _defaultPageSize = new Integer(pageSize);
130: }
131:
132: /**
133: * Set the data set size. In order to calculate paging state for the last page such as the
134: * state returned for {@link #getRowForLastPage()} the default PagerModel implementation must
135: * know the total size of the data set.
136: *
137: * @return the size
138: */
139: public int getDataSetSize() {
140: if (_dataSetSize == null)
141: return 0;
142: else
143: return _dataSetSize.intValue();
144: }
145:
146: /**
147: * Set the data set size.
148: * @param dataSetSize the size
149: */
150: public void setDataSetSize(int dataSetSize) {
151: _dataSetSize = new Integer(dataSetSize);
152: }
153:
154: /**
155: * Get the current page size.
156: * @return the page size
157: */
158: public int getPageSize() {
159: return _explicitPageSize != null ? _explicitPageSize.intValue()
160: : getDefaultPageSize();
161: }
162:
163: /**
164: * Sets the page size and overrides the default page size if one has been set.
165: * @param pageSize the specific page size
166: */
167: public void setPageSize(int pageSize) {
168: if (pageSize < 1)
169: throw new IllegalArgumentException(Bundle
170: .getErrorString("PagerModel_IllegalPageSize"));
171:
172: _explicitPageSize = new Integer(pageSize);
173: }
174:
175: /**
176: * Get the page number given the current page size and current row. The page number is zero based and should be
177: * adjusted by one when being displayed to users.
178: * @return the page number
179: */
180: public int getPage() {
181: int row = getRow();
182: assert row % getPageSize() == 0 : "Invalid current row \""
183: + row + "\" for page size \"" + getPageSize() + "\"";
184: assert getPageSize() > 0;
185: return row / getPageSize();
186: }
187:
188: /**
189: * Set a specific page. This will change the current row to match the given page value.
190: *
191: * @param page the new page
192: * @throws IllegalArgumentException if the given page is less than zero
193: */
194: public void setPage(int page) {
195: if (page < 0)
196: throw new IllegalArgumentException(Bundle
197: .getErrorString("PagerModel_IllegalPage"));
198:
199: /* todo: need to check that the new 'current' page is in range given the first/last boundaries */
200: _currentRow = new Integer(page * getPageSize());
201: }
202:
203: /**
204: * Get the current row. If no row has been specified, the default row is returned.
205: * @return the current row
206: */
207: public int getRow() {
208: if (_currentRow != null) {
209: int row = _currentRow.intValue();
210:
211: /* if the row is out of range, simply adjust to the last row */
212: if (_dataSetSize != null && (row > _dataSetSize.intValue()))
213: row = _dataSetSize.intValue();
214:
215: if (row % getPageSize() != 0) {
216: int adjustedPage = row - (row % getPageSize());
217: return adjustedPage;
218: } else
219: return row;
220: } else
221: return DEFAULT_ROW;
222: }
223:
224: /**
225: * Set the current row.
226: * @param row the new row
227: * @throws IllegalArgumentException if the given row is less than zero
228: */
229: public void setRow(int row) {
230: if (row < 0)
231: throw new IllegalArgumentException(Bundle
232: .getErrorString("PagerModel_IllegalRow"));
233:
234: _currentRow = new Integer(row);
235: }
236:
237: /**
238: * <p>
239: * Get the last row for the current page of data. This value is useful when displaying paging UI like:
240: * <pre>
241: * Row 11 through 20 of 60
242: * </pre>
243: * The last row on the page is returned as a zero-based number from the beginning of the data set. In the
244: * case above, the value returned is <code>19</code> and is converted to <code>20</code> for readability
245: * by adding one. If the current page is only partially filled, this method will return the value for a partial page.
246: * For example, with a data set of size 4 on a page of size 10, the value <code>3</code> would be returned.
247: * </p>
248: * @return the last row for the current page
249: */
250: public int getLastRowForPage() {
251: int row = getRow();
252: if (_dataSetSize != null) {
253: if (_dataSetSize.intValue() - row < getPageSize())
254: return row + (_dataSetSize.intValue() - row) - 1;
255: else
256: return row + getPageSize() - 1;
257: } else
258: return row + getPageSize() - 1;
259: }
260:
261: /**
262: * Get the row used to display the first page.
263: * @return the row for the first page
264: */
265: public int getRowForFirstPage() {
266: return DEFAULT_ROW;
267: }
268:
269: /**
270: * Get the row used to display the previous page. Note, a return value of less than zero means that the previous
271: * page does not exist as it would scroll off the beginning of the data set and is invalid.
272: * @return the row for the previous page
273: */
274: public int getRowForPreviousPage() {
275: int value = getRow() - getPageSize();
276: return value > -1 ? value : -1;
277: }
278:
279: /**
280: * Get the row used to display the next page. Note, if this value is greater than the size of the data set
281: * it would scroll off the end of the data set and is invalid.
282: * @return the row for the previous page
283: */
284: public int getRowForNextPage() {
285: return getRow() + getPageSize();
286: }
287:
288: /**
289: * Get the row used to display the last page. This requires tha the data set size has been set via
290: * @return the row for the last page
291: * @throws IllegalStateException when the size of the data set has not been set
292: */
293: public int getRowForLastPage() {
294: Integer lastRow = internalGetLastRow();
295: if (lastRow != null)
296: return lastRow.intValue();
297: else
298: throw new IllegalStateException(Bundle
299: .getErrorString("PagerModel_CantCalculateLastPage"));
300: }
301:
302: /**
303: * Get the row needed to jump to the given <code>page</code>
304: * @param page the new page
305: * @return the row used to jump to the new page
306: * @throws IllegalArgumentException if the given page value is less than zero
307: */
308: public int encodeRowForPage(int page) {
309: if (page < 0)
310: throw new IllegalArgumentException(Bundle
311: .getErrorString("PagerModel_IllegalPage"));
312:
313: return page * getPageSize();
314: }
315:
316: /**
317: * Get the total number of pages. This value is useful when displaying the total number of pages in UI like:
318: * <pre>
319: * Page 4 of 10
320: * </pre>
321: * This method returns an absolute count of the number of pages which could be displayed given the
322: * size of the data set and the current page size. This method requires the PagerModel know the
323: * total size of the data set.
324: * @return the number of pages
325: * @throws IllegalStateException when the size of the data set has not been set
326: */
327: public int getPageCount() {
328: if (_dataSetSize != null)
329: return (int) Math.ceil(_dataSetSize.doubleValue()
330: / (double) getPageSize());
331: else
332: throw new IllegalStateException(Bundle
333: .getErrorString("PagerModel_CantCalculateLastPage"));
334: }
335:
336: /**
337: * Get the page number of the first page.
338: * @return the first page
339: */
340: public int getFirstPage() {
341: return 0;
342: }
343:
344: /**
345: * Get the page number of the previous page.
346: * @return the previous page
347: */
348: public int getPreviousPage() {
349: int previousPageRow = getRowForPreviousPage();
350: return previousPageRow == -1 ? previousPageRow
351: : (int) (previousPageRow / getPageSize());
352: }
353:
354: /**
355: * Get the page number for the next page.
356: * @return the next page
357: */
358: public int getNextPage() {
359: return (int) (getRowForNextPage() / getPageSize());
360: }
361:
362: /**
363: * Get the page number for the last page.
364: * @return the last page
365: */
366: public int getLastPage() {
367: return (int) (Math.floor(getRowForLastPage() / getPageSize()));
368: }
369:
370: /**
371: * Internal method used to calculate the last row given a data set size.
372: * @return the last row or <code>null</code> if the last row can not be calculated
373: */
374: private Integer internalGetLastRow() {
375: if (_dataSetSize != null) {
376: /*
377: 29 / 10: 0-9, 10-19, 20-29 _lastRow = 20
378: 30 / 10: 0-9, 10-19, 20-29, 30-39 _lastRow = 30
379: 31 / 10: 0-9, 10-19, 20-29, 30-39 _lastRow = 30
380: 32 / 10: 0-9, 10-19, 20-29, 30-39 _lastRow = 30
381:
382: 29 - (29%10) = 20
383: 30 - (30%10) = 30
384: 30 - (30%10) = 30
385: 36 - (36%10) = 30
386:
387: 12 / 2: 0-1, 2-3, 4-5, 6-7, 8-9, 10-11, 12-13 _lastRow=10
388: 12 / 5: 0-4, 5-9, 10-14 _lastRow=5
389: */
390: int lastRow = getPageSize()
391: * (int) Math.floor((double) (_dataSetSize
392: .intValue() - 1)
393: / (double) getPageSize());
394: return new Integer(lastRow);
395: } else
396: return null;
397: }
398: }
|