/* * JasperReports - Free Java Reporting Library. * Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved. * http://www.jaspersoft.com * * Unless you have purchased a commercial license agreement from Jaspersoft, * the following license terms apply: * * This program is part of JasperReports. * * JasperReports is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JasperReports is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JasperReports. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.jasperreports.olap; import java.io.StringReader; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRDataset; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRField; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.olap.mapping.AxisPosition; import net.sf.jasperreports.olap.mapping.DataMapping; import net.sf.jasperreports.olap.mapping.Mapping; import net.sf.jasperreports.olap.mapping.MappingLexer; import net.sf.jasperreports.olap.mapping.MappingMetadata; import net.sf.jasperreports.olap.mapping.MappingParser; import net.sf.jasperreports.olap.mapping.MemberDepth; import net.sf.jasperreports.olap.mapping.MemberMapping; import net.sf.jasperreports.olap.mapping.MemberProperty; import net.sf.jasperreports.olap.mapping.Tuple; import net.sf.jasperreports.olap.mapping.TuplePosition; import net.sf.jasperreports.olap.result.JROlapCell; import net.sf.jasperreports.olap.result.JROlapHierarchy; import net.sf.jasperreports.olap.result.JROlapHierarchyLevel; import net.sf.jasperreports.olap.result.JROlapMember; import net.sf.jasperreports.olap.result.JROlapMemberTuple; import net.sf.jasperreports.olap.result.JROlapResult; import net.sf.jasperreports.olap.result.JROlapResultAxis; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import antlr.ANTLRException; /** * @author Lucian Chirita (lucianc@users.sourceforge.net) * @version $Id: JROlapDataSource.java 3678 2010-04-02 12:13:06Z shertage $ */ public class JROlapDataSource implements JRDataSource, MappingMetadata { private static final Log log = LogFactory.getLog(JROlapDataSource.class); protected final JROlapResult olapResult; protected JROlapResultAxis[] axes; protected final JROlapHierarchy[][] queryHierarchies; protected final int hierarchiesCount; protected Map fieldMatchers; protected int[][] fieldsMaxDepths; protected boolean[] iteratePositions; protected boolean iterate; protected boolean dataField; protected Map fieldValues; protected int[] axisPositions; protected boolean first; protected int[][] maxDepths; private DateFormat dateFormat = new SimpleDateFormat(); // Mpenningroth 21-Nov-2008 added to deal with empty results. // Sometimes non empty can cause no results, but the init was // causing an error trying to locate a mapping to a tuple (there are // no tuples.) This deals with that situation. private boolean noTuples; public JROlapDataSource(JRDataset dataset, JROlapResult result) { this.olapResult = result; axes = result.getAxes(); queryHierarchies = new JROlapHierarchy[axes.length][]; fieldsMaxDepths = new int[axes.length][]; maxDepths = new int[axes.length][]; int hCount = 0; noTuples = false; for (int i = 0; i < axes.length; i++) { noTuples = (noTuples || axes[i].getTupleCount() == 0); queryHierarchies[i] = axes[i].getHierarchiesOnAxis(); hCount += queryHierarchies[i].length; fieldsMaxDepths[i] = new int[queryHierarchies[i].length]; maxDepths[i] = new int[queryHierarchies[i].length]; } hierarchiesCount = hCount; axisPositions = new int[axes.length]; if (!noTuples) { init(dataset); } } public boolean next() throws JRException { boolean next; boolean matchMaxLevel; if (noTuples) { return false; } do { if (iterate) { next = nextPositions(); } else { next = first; first = false; } if (!next) { break; } resetMaxDepths(); for (Iterator it = fieldMatchers.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); Object fieldName = entry.getKey(); FieldMatcher matcher = (FieldMatcher) entry.getValue(); if (matcher.matches()) { Object value = matcher.value(); fieldValues.put(fieldName, value); } } matchMaxLevel = true; axes_loop: for (int i = 0; i < axes.length; i++) { if (iteratePositions[i]) { for (int j = 0; j < fieldsMaxDepths[i].length; j++) { if (maxDepths[i][j] < fieldsMaxDepths[i][j]) { matchMaxLevel = false; break axes_loop; } } } } } while (!matchMaxLevel); return next; } private void resetMaxDepths() { for (int i = 0; i < axes.length; ++i) { if (iteratePositions[i]) { for (int j = 0; j < maxDepths[i].length; j++) { maxDepths[i][j] = 0; } } } } protected boolean nextPositions() { boolean next; int i = 0; for (; i < axes.length; ++i) { if (iteratePositions[i]) { ++axisPositions[i]; if (axisPositions[i] >= axes[i].getTupleCount()) { axisPositions[i] = 0; } else { break; } } } next = i < axes.length; return next; } /** * Convert the value of the data type of the Field * @param jrField the Field whose type has to be converted * @return value of field in the requested type * */ public Object getFieldValue(JRField jrField) throws JRException { Class valueClass = jrField.getValueClass(); Object value = fieldValues.get(jrField.getName()); try { /* * Everything in the result is a string, apart from Member */ if (valueClass.equals(mondrian.olap.Member.class)) { if (!(value instanceof mondrian.olap.Member)) { throw new JRException("Field '" + jrField.getName() + "' is of class '" + value.getClass() + "' and can not be converted to class " + valueClass.getName()); } return value; } /* * Convert the rest from String */ String fieldValue = (String) value; if (fieldValue == null) { return null; } if (Number.class.isAssignableFrom(valueClass)){ fieldValue = fieldValue.trim(); } if (fieldValue.length() == 0){ fieldValue = "0"; } if (valueClass.equals(String.class)) { return fieldValue; } else if (valueClass.equals(Boolean.class)) { return fieldValue.equalsIgnoreCase("true") ? Boolean.TRUE : Boolean.FALSE; } else if (valueClass.equals(Byte.class)) { return new Byte(fieldValue); } else if (valueClass.equals(Integer.class)) { return Integer.valueOf(fieldValue); } else if (valueClass.equals(Long.class)) { return new Long(fieldValue); } else if (valueClass.equals(Short.class)) { return new Short(fieldValue); } else if (valueClass.equals(Double.class)) { return new Double(fieldValue); } else if (valueClass.equals(Float.class)) { return new Float(fieldValue); } else if (valueClass.equals(java.math.BigDecimal.class)) { return new java.math.BigDecimal(fieldValue); } else if (valueClass.equals(java.util.Date.class)) { return dateFormat.parse(fieldValue); } else if (valueClass.equals(java.sql.Timestamp.class)) { return new java.sql.Timestamp(dateFormat.parse(fieldValue).getTime()); } else if (valueClass.equals(java.sql.Time.class)) { return new java.sql.Time(dateFormat.parse(fieldValue).getTime()); } else if (valueClass.equals(java.lang.Number.class)) { return new Double(fieldValue); } else { throw new JRException("Field '" + jrField.getName() + "', string value '" + fieldValue + "' is of class '" + fieldValues.get(jrField.getName()).getClass() + "' and can not be converted to class " + valueClass.getName()); } } catch (Exception e) { throw new JRException("Unable to get value for field '" + jrField.getName() + "' of class '" + valueClass.getName() + "'", e); } } private void init(JRDataset dataset) { iteratePositions = new boolean[axes.length]; fieldMatchers = new HashMap(); dataField = false; JRField[] fields = dataset.getFields(); if (fields != null) { for (int i = 0; i < fields.length; i++) { JRField field = fields[i]; String fieldMapping = getFieldMapping(field); MappingLexer lexer = new MappingLexer(new StringReader(fieldMapping)); MappingParser parser = new MappingParser(lexer); parser.setMappingMetadata(this); Mapping mapping; try { mapping = parser.mapping(); } catch (ANTLRException e) { log.error("Error parsing field mapping", e); throw new JRRuntimeException(e); } if (mapping == null) { throw new JRRuntimeException("Invalid field mapping \"" + fieldMapping + "\"."); } processMappingMembers(mapping); FieldMatcher fieldMatcher = createFieldMatcher(mapping); fieldMatchers.put(field.getName(), fieldMatcher); } } if (!dataField) { Arrays.fill(iteratePositions, true); } initIterate(); } private void processMappingMembers(Mapping mapping) { for (Iterator it = mapping.memberMappings(); it.hasNext();) { net.sf.jasperreports.olap.mapping.Member member = (net.sf.jasperreports.olap.mapping.Member) it.next(); processMemberInfo(member); } } private FieldMatcher createFieldMatcher(Mapping mapping) { FieldMatcher fieldMatcher; if (mapping instanceof MemberMapping) { fieldMatcher = new MemberFieldMatcher((MemberMapping) mapping); } else if (mapping instanceof DataMapping) { dataField = true; fieldMatcher = new DataFieldMatcher((DataMapping) mapping); } else { throw new JRRuntimeException("internal error"); } return fieldMatcher; } protected String getFieldMapping(JRField field) { return field.getDescription(); } private void initIterate() { int firstPos = 0; while (firstPos < axes.length && !iteratePositions[firstPos]) { ++firstPos; } if (firstPos < axes.length) { iterate = true; axisPositions[firstPos] = -1; } else { iterate = false; first = true; } fieldValues = new HashMap(); } protected void processMemberInfo(net.sf.jasperreports.olap.mapping.Member member) { MemberDepth memberDepth = member.getDepth(); if (memberDepth != null) { int depth = memberDepth.getDepth(); int axis = member.getAxis().getIdx(); int idx = member.getPosition().getIdx(); if (depth > fieldsMaxDepths[axis][idx]) { fieldsMaxDepths[axis][idx] = depth; } } } public int getDimensionIndex(net.sf.jasperreports.olap.mapping.Axis axis, String dimension) { JROlapHierarchy[] hierarchies = axes[axis.getIdx()].getHierarchiesOnAxis(); int dimensionIndex = -1; for (int i = 0; i < hierarchies.length; i++) { JROlapHierarchy hierarchy = hierarchies[i]; if (dimension.equals(hierarchy.getDimensionName())) { dimensionIndex = i; } } // MPenningroth 21-April-2009 deal with case when dimension is <dimension>.<hierarchy> form if (dimensionIndex == -1 && dimension.indexOf('.')!= -1 ) { String hierName = "[" + dimension + "]"; for (int i = 0; i < hierarchies.length; i++) { JROlapHierarchy hierarchy = hierarchies[i]; if (hierName.equals(hierarchy.getHierarchyUniqueName())) { dimensionIndex = i; } } } if (dimensionIndex == -1) { throw new JRRuntimeException("Could not find dimension \"" + dimension + "\" on axis " + axis.getIdx() + "."); } return dimensionIndex; } public int getLevelDepth(TuplePosition pos, String levelName) { JROlapHierarchy hierarchy = axes[pos.getAxis().getIdx()].getHierarchiesOnAxis()[pos.getIdx()]; JROlapHierarchyLevel[] levels = hierarchy.getLevels(); int levelIndex = -1; for (int i = 0; i < levels.length; i++) { JROlapHierarchyLevel level = levels[i]; if (level != null && level.getName().equals(levelName)) { levelIndex = level.getDepth(); break; } } if (levelIndex == -1) { throw new JRRuntimeException("Could not find level \"" + levelName + "\" on hierarchy #" + pos.getIdx() + " (dimension " + hierarchy.getDimensionName() + ") on axis #" + pos.getAxis().getIdx()); } return levelIndex; } protected void setMatchMemberDepth(net.sf.jasperreports.olap.mapping.Member memberInfo, JROlapMember member) { int memberDepth = member.getDepth(); int axis = memberInfo.getAxis().getIdx(); int pos = memberInfo.getPosition().getIdx(); if (maxDepths[axis][pos] < memberDepth) { maxDepths[axis][pos] = memberDepth; } } protected abstract class FieldMatcher { public abstract boolean matches(); public abstract Object value(); public final JROlapMember member(net.sf.jasperreports.olap.mapping.Member memberInfo, int[] positions) { int axisIdx = memberInfo.getAxis().getIdx(); JROlapResultAxis axis = axes[axisIdx]; JROlapMemberTuple tuple = axis.getTuple(positions[axisIdx]); JROlapMember[] members = tuple.getMembers(); int pos = memberInfo.getPosition().getIdx(); return members[pos]; } } protected class MemberFieldMatcher extends FieldMatcher { final net.sf.jasperreports.olap.mapping.Member memberInfo; final MemberProperty property; JROlapMember member; MemberFieldMatcher(MemberMapping mapping) { this.memberInfo = mapping.getMember(); this.property = mapping.getProperty(); } public boolean matches() { member = member(memberInfo, axisPositions); setMatchMemberDepth(memberInfo, member); member = memberInfo.ancestorMatch(member); return member != null; } public Object value() { Object value; if (memberInfo.getDepth() == null) { // The actual member object of the given dimension return member.getMondrianMember(); } else if (property != null) { // member property value value = member.getPropertyValue(property.getName()); } else { // Level name value = member.getName(); } return value.toString(); } } protected class DataFieldMatcher extends FieldMatcher { private final boolean formatted; private final int[] dataPositions; private final net.sf.jasperreports.olap.mapping.Member[] members; private int[] positions; public DataFieldMatcher(DataMapping mapping) { this.formatted = mapping.isFormatted(); List mappingPositions = mapping.getPositions(); if (mappingPositions == null) { this.dataPositions = null; positions = axisPositions; } else { if (mappingPositions.size() != axes.length) { throw new JRRuntimeException("Incorrect data mapping: the number of positions doesn't match the number of axes."); } this.dataPositions = new int[axes.length]; int c = 0; for (Iterator iter = mappingPositions.iterator(); iter.hasNext(); ++c) { AxisPosition position = (AxisPosition) iter.next(); int pos; if (position.isSpecified()) { pos = position.getPosition(); } else { pos = -1; iteratePositions[c] = true; } dataPositions[c] = pos; } } List filter = mapping.getFilter(); if (filter == null || filter.isEmpty()) { this.members = null; } else { this.members = new net.sf.jasperreports.olap.mapping.Member[filter.size()]; filter.toArray(this.members); } } public boolean matches() { if (dataPositions != null) { setPositions(); } boolean matches = true; if (members != null) { for (int i = 0; i < members.length; i++) { net.sf.jasperreports.olap.mapping.Member memberInfo = members[i]; JROlapMember member = member(memberInfo, positions); setMatchMemberDepth(memberInfo, member); if (!memberInfo.matches(member)) { matches = false; break; } } } return matches; } public Object value() { JROlapCell cell = olapResult.getCell(positions); if (cell != null && cell.isError()) { throw new JRRuntimeException("OLAP cell calculation returned error."); } Object value; if (cell == null || cell.isNull()) { value = null; } else if (formatted) { value = cell.getFormattedValue(); } else { value = cell.getValue().toString(); } return value; } void setPositions() { positions = new int[axes.length]; for (int i = 0; i < axes.length; i++) { int dataPosition = dataPositions[i]; positions[i] = dataPosition == -1 ? axisPositions[i] : dataPosition; } } } public int getTuplePosition(int axisIndex, Tuple tuple) { if (axisIndex > axes.length) { throw new JRRuntimeException("OLAP result doesn't contain Axis(" + axisIndex + ")."); } String[] memberUniqueNames = tuple.getMemberUniqueNames(); JROlapResultAxis axis = axes[axisIndex]; int tupleCount = axis.getTupleCount(); int pos = -1; for (int i = 0; i < tupleCount; i++) { JROlapMemberTuple memberTuple = axis.getTuple(i); JROlapMember[] resMembers = memberTuple.getMembers(); if (resMembers.length == memberUniqueNames.length) { boolean eq = true; for (int j = 0; eq && j < resMembers.length; ++j) { if (!memberUniqueNames[j].equals(resMembers[j].getUniqueName())) { eq = false; } } if (eq) { pos = i; break; } } } if (pos == -1) { StringBuffer sb = new StringBuffer(); sb.append('('); for (int i = 0; i < memberUniqueNames.length; i++) { if (i > 0) { sb.append(','); } sb.append(memberUniqueNames[i]); } throw new JRRuntimeException("No such tuple " + sb + " on axis " + axisIndex + "."); } return pos; } }