001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2005 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028: package net.sf.jasperreports.engine.data;
029:
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.List;
033: import java.util.Map;
034:
035: import net.sf.jasperreports.engine.JRException;
036: import net.sf.jasperreports.engine.JRField;
037: import net.sf.jasperreports.engine.JRRewindableDataSource;
038: import net.sf.jasperreports.engine.JRRuntimeException;
039: import net.sf.jasperreports.engine.query.JRJpaQueryExecuter;
040:
041: /**
042: * Java Persistence API data source that uses <code>javax.persistence.Query.getResultList()</code>.
043: * <p/>
044: * The query result can be paginated by not retrieving all the rows at once.
045: * <p/>
046: * Fields are mapped to values in the result following these rules:
047: * <ul>
048: * <li>if the query returns a single object/bean (e.g. <code>SELECT m FROM Movie m</code> or
049: * <code>SELECT NEW MovieDescription(m.title, m.genre) FROM Movie m</code>), then the fields are
050: * mapped to bean property names.</li>
051: * <li>if the query returns multiple objects per row (e.g. <code>SELECT m.title, m.gender FROM Movie m</code>),
052: * the fields are mapped using the following syntax: COLUMN_<em>index</em>[.<em>property</em>], with the
053: * indexes starting from 1. Example mappings: COLUMN_1, COLUMN_2, COLUMN_2.title, COLUMN_2.movie.title.</li>
054: * </ul>
055: *
056: * @author Marcel Overdijk (marceloverdijk@hotmail.com)
057: * @version $Id: JRJpaDataSource.java 1744 2007-06-11 14:30:11Z teodord $
058: * @see net.sf.jasperreports.engine.query.JRJpaQueryExecuterFactory#PROPERTY_JPA_QUERY_PAGE_SIZE
059: */
060: public class JRJpaDataSource extends JRAbstractBeanDataSource implements
061: JRRewindableDataSource {
062: private static final String MAPPING_INDEX_PREFIX = "COLUMN_";
063: private static final int MAPPING_INDEX_PREFIX_LENGTH = MAPPING_INDEX_PREFIX
064: .length();
065: private static final String MAPPING_INDEX_PROPERTY_SEP = ".";
066: private static final int MAPPING_INDEX_PROPERTY_SEP_LENGTH = MAPPING_INDEX_PROPERTY_SEP
067: .length();
068:
069: private final JRJpaQueryExecuter queryExecuter;
070: private final int pageSize;
071: private int pageCount;
072: private boolean nextPage;
073: private List returnValues;
074: private Iterator iterator;
075: protected Object currentRow;
076: private Map fieldValueReaders;
077:
078: public JRJpaDataSource(JRJpaQueryExecuter queryExecuter,
079: int pageSize) {
080: super (true);
081:
082: this .queryExecuter = queryExecuter;
083: this .pageSize = pageSize;
084:
085: fieldValueReaders = new HashMap();
086:
087: pageCount = 0;
088: fetchPage();
089: }
090:
091: protected void fetchPage() {
092: if (pageSize <= 0) {
093: returnValues = queryExecuter.getResultList();
094: nextPage = false;
095: } else {
096: returnValues = queryExecuter.getResultList(pageCount
097: * pageSize, pageSize);
098: nextPage = returnValues.size() == pageSize;
099: }
100:
101: ++pageCount;
102:
103: initIterator();
104: }
105:
106: public boolean next() {
107: if (iterator == null) {
108: return false;
109: }
110:
111: boolean hasNext = iterator.hasNext();
112: if (!hasNext && nextPage) {
113: fetchPage();
114: hasNext = iterator != null && iterator.hasNext();
115: }
116:
117: if (hasNext) {
118: currentRow = iterator.next();
119: }
120:
121: return hasNext;
122: }
123:
124: public void moveFirst() {
125: if (pageCount == 1) {
126: initIterator();
127: } else {
128: pageCount = 0;
129: fetchPage();
130: }
131: }
132:
133: private void initIterator() {
134: iterator = returnValues == null ? null : returnValues
135: .iterator();
136: }
137:
138: public Object getFieldValue(JRField field) throws JRException {
139: FieldValueReader reader = getFieldValueReader(field);
140: return reader.getValue();
141: }
142:
143: protected FieldValueReader getFieldValueReader(JRField field) {
144: FieldValueReader reader = (FieldValueReader) fieldValueReaders
145: .get(field.getName());
146: if (reader == null) {
147: String mapping = getPropertyName(field);
148: reader = createReader(mapping);
149: fieldValueReaders.put(field.getName(), reader);
150: }
151: return reader;
152: }
153:
154: private FieldValueReader createReader(String mapping) {
155: FieldValueReader reader = null;
156: if (mapping.startsWith(MAPPING_INDEX_PREFIX)) {
157: int propertySepIdx = mapping.indexOf(
158: MAPPING_INDEX_PROPERTY_SEP,
159: MAPPING_INDEX_PREFIX_LENGTH + 1);
160: if (propertySepIdx < 0) {
161: String indexStr = mapping
162: .substring(MAPPING_INDEX_PREFIX_LENGTH);
163: try {
164: int index = Integer.parseInt(indexStr);
165: reader = new IndexReader(index - 1);
166: } catch (NumberFormatException e) {
167: //ignore
168: }
169: } else {
170: String indexStr = mapping.substring(
171: MAPPING_INDEX_PREFIX_LENGTH, propertySepIdx);
172: try {
173: int index = Integer.parseInt(indexStr);
174: String property = mapping.substring(propertySepIdx
175: + MAPPING_INDEX_PROPERTY_SEP_LENGTH);
176: reader = new IndexPropertyReader(index - 1,
177: property);
178: } catch (NumberFormatException e) {
179: //ignore
180: }
181: }
182: }
183:
184: //default to a property mapping
185: if (reader == null) {
186: reader = new PropertyReader(mapping);
187: }
188:
189: return reader;
190: }
191:
192: protected static interface FieldValueReader {
193: Object getValue() throws JRException;
194: }
195:
196: protected class PropertyReader implements FieldValueReader {
197: private final String property;
198:
199: public PropertyReader(String property) {
200: this .property = property;
201: }
202:
203: public Object getValue() throws JRException {
204: return getBeanProperty(currentRow, property);
205: }
206: }
207:
208: protected class IndexReader implements FieldValueReader {
209: private final int position;
210:
211: public IndexReader(int position) {
212: this .position = position;
213: }
214:
215: public Object getValue() throws JRException {
216: Object[] values = (Object[]) currentRow;
217: if (position < 0 || position >= values.length) {
218: throw new JRRuntimeException("Index " + position
219: + " out of bounds for query result of length "
220: + values.length);
221: }
222: return values[position];
223: }
224: }
225:
226: protected class IndexPropertyReader implements FieldValueReader {
227: private final int position;
228: private final String property;
229:
230: public IndexPropertyReader(int position, String property) {
231: this .position = position;
232: this .property = property;
233: }
234:
235: public Object getValue() throws JRException {
236: Object[] values = (Object[]) currentRow;
237: if (position < 0 || position >= values.length) {
238: throw new JRRuntimeException("Index " + position
239: + " out of bounds for query result of length "
240: + values.length);
241: }
242: return getBeanProperty(values[position], property);
243: }
244: }
245: }
|