001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2006 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.olap;
029:
030: import java.io.StringReader;
031: import java.util.Arrays;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: import net.sf.jasperreports.engine.JRDataSource;
038: import net.sf.jasperreports.engine.JRDataset;
039: import net.sf.jasperreports.engine.JRException;
040: import net.sf.jasperreports.engine.JRField;
041: import net.sf.jasperreports.engine.JRRuntimeException;
042: import net.sf.jasperreports.olap.mapping.AxisPosition;
043: import net.sf.jasperreports.olap.mapping.DataMapping;
044: import net.sf.jasperreports.olap.mapping.Mapping;
045: import net.sf.jasperreports.olap.mapping.MappingLexer;
046: import net.sf.jasperreports.olap.mapping.MappingMetadata;
047: import net.sf.jasperreports.olap.mapping.MappingParser;
048: import net.sf.jasperreports.olap.mapping.MemberDepth;
049: import net.sf.jasperreports.olap.mapping.MemberMapping;
050: import net.sf.jasperreports.olap.mapping.MemberProperty;
051: import net.sf.jasperreports.olap.mapping.Tuple;
052: import net.sf.jasperreports.olap.mapping.TuplePosition;
053: import net.sf.jasperreports.olap.result.JROlapCell;
054: import net.sf.jasperreports.olap.result.JROlapHierarchy;
055: import net.sf.jasperreports.olap.result.JROlapHierarchyLevel;
056: import net.sf.jasperreports.olap.result.JROlapMember;
057: import net.sf.jasperreports.olap.result.JROlapMemberTuple;
058: import net.sf.jasperreports.olap.result.JROlapResult;
059: import net.sf.jasperreports.olap.result.JROlapResultAxis;
060:
061: import org.apache.commons.logging.Log;
062: import org.apache.commons.logging.LogFactory;
063:
064: import antlr.ANTLRException;
065:
066: /**
067: * @author Lucian Chirita (lucianc@users.sourceforge.net)
068: * @version $Id: JROlapDataSource.java 1797 2007-07-30 09:38:35Z teodord $
069: */
070: public class JROlapDataSource implements JRDataSource, MappingMetadata {
071: private static final Log log = LogFactory
072: .getLog(JROlapDataSource.class);
073:
074: protected final JROlapResult olapResult;
075: protected JROlapResultAxis[] axes;
076: protected final JROlapHierarchy[][] queryHierarchies;
077: protected final int hierarchiesCount;
078:
079: protected Map fieldMatchers;
080: protected int[][] fieldsMaxDepths;
081: protected boolean[] iteratePositions;
082: protected boolean iterate;
083:
084: protected boolean dataField;
085:
086: protected Map fieldValues;
087: protected int[] axisPositions;
088: protected boolean first;
089: protected int[][] maxDepths;
090:
091: public JROlapDataSource(JRDataset dataset, JROlapResult result) {
092: this .olapResult = result;
093: axes = result.getAxes();
094:
095: queryHierarchies = new JROlapHierarchy[axes.length][];
096: fieldsMaxDepths = new int[axes.length][];
097: maxDepths = new int[axes.length][];
098: int hCount = 0;
099: for (int i = 0; i < axes.length; i++) {
100: queryHierarchies[i] = axes[i].getHierarchiesOnAxis();
101:
102: hCount += queryHierarchies[i].length;
103: fieldsMaxDepths[i] = new int[queryHierarchies[i].length];
104: maxDepths[i] = new int[queryHierarchies[i].length];
105: }
106: hierarchiesCount = hCount;
107:
108: axisPositions = new int[axes.length];
109:
110: init(dataset);
111: }
112:
113: public boolean next() throws JRException {
114: boolean next;
115: boolean matchMaxLevel;
116: do {
117: if (iterate) {
118: next = nextPositions();
119: } else {
120: next = first;
121: first = false;
122: }
123:
124: if (!next) {
125: break;
126: }
127:
128: resetMaxDepths();
129: for (Iterator it = fieldMatchers.entrySet().iterator(); it
130: .hasNext();) {
131: Map.Entry entry = (Map.Entry) it.next();
132: Object fieldName = entry.getKey();
133: FieldMatcher matcher = (FieldMatcher) entry.getValue();
134: if (matcher.matches()) {
135: Object value = matcher.value();
136: fieldValues.put(fieldName, value);
137: }
138: }
139:
140: matchMaxLevel = true;
141: axes_loop: for (int i = 0; i < axes.length; i++) {
142: if (iteratePositions[i]) {
143: for (int j = 0; j < fieldsMaxDepths[i].length; j++) {
144: if (maxDepths[i][j] < fieldsMaxDepths[i][j]) {
145: matchMaxLevel = false;
146: break axes_loop;
147: }
148: }
149: }
150: }
151: } while (!matchMaxLevel);
152:
153: return next;
154: }
155:
156: private void resetMaxDepths() {
157: for (int i = 0; i < axes.length; ++i) {
158: if (iteratePositions[i]) {
159: for (int j = 0; j < maxDepths[i].length; j++) {
160: maxDepths[i][j] = 0;
161: }
162: }
163: }
164: }
165:
166: protected boolean nextPositions() {
167: boolean next;
168: int i = 0;
169: for (; i < axes.length; ++i) {
170: if (iteratePositions[i]) {
171: ++axisPositions[i];
172: if (axisPositions[i] >= axes[i].getTupleCount()) {
173: axisPositions[i] = 0;
174: } else {
175: break;
176: }
177: }
178: }
179:
180: next = i < axes.length;
181: return next;
182: }
183:
184: public Object getFieldValue(JRField jrField) throws JRException {
185: return fieldValues.get(jrField.getName());
186: }
187:
188: private void init(JRDataset dataset) {
189: iteratePositions = new boolean[axes.length];
190:
191: fieldMatchers = new HashMap();
192:
193: dataField = false;
194: JRField[] fields = dataset.getFields();
195: if (fields != null) {
196: for (int i = 0; i < fields.length; i++) {
197: JRField field = fields[i];
198: String fieldMapping = getFieldMapping(field);
199:
200: MappingLexer lexer = new MappingLexer(new StringReader(
201: fieldMapping));
202: MappingParser parser = new MappingParser(lexer);
203: parser.setMappingMetadata(this );
204: Mapping mapping;
205: try {
206: mapping = parser.mapping();
207: } catch (ANTLRException e) {
208: log.error("Error parsing field mapping", e);
209: throw new JRRuntimeException(e);
210: }
211:
212: if (mapping == null) {
213: throw new JRRuntimeException(
214: "Invalid field mapping \"" + fieldMapping
215: + "\".");
216: }
217:
218: processMappingMembers(mapping);
219:
220: FieldMatcher fieldMatcher = createFieldMatcher(mapping);
221: fieldMatchers.put(field.getName(), fieldMatcher);
222: }
223: }
224:
225: if (!dataField) {
226: Arrays.fill(iteratePositions, true);
227: }
228:
229: initIterate();
230: }
231:
232: private void processMappingMembers(Mapping mapping) {
233: for (Iterator it = mapping.memberMappings(); it.hasNext();) {
234: net.sf.jasperreports.olap.mapping.Member member = (net.sf.jasperreports.olap.mapping.Member) it
235: .next();
236: processMemberInfo(member);
237: }
238: }
239:
240: private FieldMatcher createFieldMatcher(Mapping mapping) {
241: FieldMatcher fieldMatcher;
242: if (mapping instanceof MemberMapping) {
243: fieldMatcher = new MemberFieldMatcher(
244: (MemberMapping) mapping);
245: } else if (mapping instanceof DataMapping) {
246: dataField = true;
247: fieldMatcher = new DataFieldMatcher((DataMapping) mapping);
248: } else {
249: throw new JRRuntimeException("internal error");
250: }
251:
252: return fieldMatcher;
253: }
254:
255: protected String getFieldMapping(JRField field) {
256: return field.getDescription();
257: }
258:
259: private void initIterate() {
260: int firstPos = 0;
261: while (firstPos < axes.length && !iteratePositions[firstPos]) {
262: ++firstPos;
263: }
264:
265: if (firstPos < axes.length) {
266: iterate = true;
267: axisPositions[firstPos] = -1;
268: } else {
269: iterate = false;
270: first = true;
271: }
272:
273: fieldValues = new HashMap();
274: }
275:
276: protected void processMemberInfo(
277: net.sf.jasperreports.olap.mapping.Member member) {
278: MemberDepth memberDepth = member.getDepth();
279: if (memberDepth != null) {
280: int depth = memberDepth.getDepth();
281: int axis = member.getAxis().getIdx();
282: int idx = member.getPosition().getIdx();
283:
284: if (depth > fieldsMaxDepths[axis][idx]) {
285: fieldsMaxDepths[axis][idx] = depth;
286: }
287: }
288: }
289:
290: public int getDimensionIndex(
291: net.sf.jasperreports.olap.mapping.Axis axis,
292: String dimension) {
293: JROlapHierarchy[] hierarchies = axes[axis.getIdx()]
294: .getHierarchiesOnAxis();
295: int dimensionIndex = -1;
296: for (int i = 0; i < hierarchies.length; i++) {
297: JROlapHierarchy hierarchy = hierarchies[i];
298: if (hierarchy.getDimensionName().equals(dimension)) {
299: dimensionIndex = i;
300: }
301: }
302:
303: if (dimensionIndex == -1) {
304: throw new JRRuntimeException("Could not find dimension \""
305: + dimension + "\" on axis " + axis.getIdx() + ".");
306: }
307:
308: return dimensionIndex;
309: }
310:
311: public int getLevelDepth(TuplePosition pos, String levelName) {
312: JROlapHierarchy hierarchy = axes[pos.getAxis().getIdx()]
313: .getHierarchiesOnAxis()[pos.getIdx()];
314: JROlapHierarchyLevel[] levels = hierarchy.getLevels();
315: int levelIndex = -1;
316: for (int i = 0; i < levels.length; i++) {
317: JROlapHierarchyLevel level = levels[i];
318: if (level != null && level.getName().equals(levelName)) {
319: levelIndex = level.getDepth();
320: break;
321: }
322: }
323:
324: if (levelIndex == -1) {
325: throw new JRRuntimeException("Could not find level \""
326: + levelName + "\" on hierarchy "
327: + hierarchy.getDimensionName() + ".");
328: }
329:
330: return levelIndex;
331: }
332:
333: protected void setMatchMemberDepth(
334: net.sf.jasperreports.olap.mapping.Member memberInfo,
335: JROlapMember member) {
336: int memberDepth = member.getDepth();
337: int axis = memberInfo.getAxis().getIdx();
338: int pos = memberInfo.getPosition().getIdx();
339: if (maxDepths[axis][pos] < memberDepth) {
340: maxDepths[axis][pos] = memberDepth;
341: }
342: }
343:
344: protected abstract class FieldMatcher {
345: public abstract boolean matches();
346:
347: public abstract Object value();
348:
349: public final JROlapMember member(
350: net.sf.jasperreports.olap.mapping.Member memberInfo,
351: int[] positions) {
352: int axisIdx = memberInfo.getAxis().getIdx();
353: JROlapResultAxis axis = axes[axisIdx];
354: JROlapMemberTuple tuple = axis.getTuple(positions[axisIdx]);
355: JROlapMember[] members = tuple.getMembers();
356: int pos = memberInfo.getPosition().getIdx();
357: return members[pos];
358: }
359: }
360:
361: protected class MemberFieldMatcher extends FieldMatcher {
362: final net.sf.jasperreports.olap.mapping.Member memberInfo;
363: final MemberProperty property;
364: JROlapMember member;
365:
366: MemberFieldMatcher(MemberMapping mapping) {
367: this .memberInfo = mapping.getMember();
368: this .property = mapping.getProperty();
369: }
370:
371: public boolean matches() {
372: member = member(memberInfo, axisPositions);
373: setMatchMemberDepth(memberInfo, member);
374: member = memberInfo.ancestorMatch(member);
375: return member != null;
376: }
377:
378: public Object value() {
379: Object value;
380: if (property != null) {
381: value = member.getPropertyValue(property.getName());
382: } else if (memberInfo.getDepth() == null) {
383: value = member.getMondrianMember();
384: } else {
385: value = member.getName();
386: }
387: return value;
388: }
389: }
390:
391: protected class DataFieldMatcher extends FieldMatcher {
392: private final boolean formatted;
393: private final int[] dataPositions;
394: private final net.sf.jasperreports.olap.mapping.Member[] members;
395: private int[] positions;
396:
397: public DataFieldMatcher(DataMapping mapping) {
398: this .formatted = mapping.isFormatted();
399:
400: List mappingPositions = mapping.getPositions();
401: if (mappingPositions == null) {
402: this .dataPositions = null;
403: positions = axisPositions;
404: } else {
405: if (mappingPositions.size() != axes.length) {
406: throw new JRRuntimeException(
407: "Incorrect data mapping: the number of positions doesn't match the number of axes.");
408: }
409:
410: this .dataPositions = new int[axes.length];
411: int c = 0;
412: for (Iterator iter = mappingPositions.iterator(); iter
413: .hasNext(); ++c) {
414: AxisPosition position = (AxisPosition) iter.next();
415: int pos;
416: if (position.isSpecified()) {
417: pos = position.getPosition();
418: } else {
419: pos = -1;
420: iteratePositions[c] = true;
421: }
422: dataPositions[c] = pos;
423: }
424: }
425:
426: List filter = mapping.getFilter();
427: if (filter == null || filter.isEmpty()) {
428: this .members = null;
429: } else {
430: this .members = new net.sf.jasperreports.olap.mapping.Member[filter
431: .size()];
432: filter.toArray(this .members);
433: }
434: }
435:
436: public boolean matches() {
437: if (dataPositions != null) {
438: setPositions();
439: }
440:
441: boolean matches = true;
442: if (members != null) {
443: for (int i = 0; i < members.length; i++) {
444: net.sf.jasperreports.olap.mapping.Member memberInfo = members[i];
445: JROlapMember member = member(memberInfo, positions);
446: setMatchMemberDepth(memberInfo, member);
447: if (!memberInfo.matches(member)) {
448: matches = false;
449: break;
450: }
451: }
452: }
453:
454: return matches;
455: }
456:
457: public Object value() {
458: JROlapCell cell = olapResult.getCell(positions);
459:
460: if (cell != null && cell.isError()) {
461: throw new JRRuntimeException(
462: "OLAP cell calculation returned error.");
463: }
464:
465: Object value;
466: if (cell == null || cell.isNull()) {
467: value = null;
468: } else if (formatted) {
469: value = cell.getFormattedValue();
470: } else {
471: value = cell.getValue();
472: }
473:
474: return value;
475: }
476:
477: void setPositions() {
478: positions = new int[axes.length];
479: for (int i = 0; i < axes.length; i++) {
480: int dataPosition = dataPositions[i];
481: positions[i] = dataPosition == -1 ? axisPositions[i]
482: : dataPosition;
483: }
484: }
485: }
486:
487: public int getTuplePosition(int axisIndex, Tuple tuple) {
488: if (axisIndex > axes.length) {
489: throw new JRRuntimeException(
490: "OLAP result doesn't contain Axis(" + axisIndex
491: + ").");
492: }
493:
494: String[] memberUniqueNames = tuple.getMemberUniqueNames();
495: JROlapResultAxis axis = axes[axisIndex];
496: int tupleCount = axis.getTupleCount();
497:
498: int pos = -1;
499: for (int i = 0; i < tupleCount; i++) {
500: JROlapMemberTuple memberTuple = axis.getTuple(i);
501: JROlapMember[] resMembers = memberTuple.getMembers();
502: if (resMembers.length == memberUniqueNames.length) {
503: boolean eq = true;
504: for (int j = 0; eq && j < resMembers.length; ++j) {
505: if (!memberUniqueNames[j].equals(resMembers[j]
506: .getUniqueName())) {
507: eq = false;
508: }
509: }
510:
511: if (eq) {
512: pos = i;
513: break;
514: }
515: }
516: }
517:
518: if (pos == -1) {
519: StringBuffer sb = new StringBuffer();
520: sb.append('(');
521: for (int i = 0; i < memberUniqueNames.length; i++) {
522: if (i > 0) {
523: sb.append(',');
524: }
525: sb.append(memberUniqueNames[i]);
526: }
527: throw new JRRuntimeException("No such tuple " + sb
528: + " on axis " + axisIndex + ".");
529: }
530:
531: return pos;
532: }
533: }
|