/*
//
// Licensed to Benedikt Kämpgen under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Benedikt Kämpgen licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
package org.olap4j.driver.olap4ld;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.olap4j.OlapException;
import org.olap4j.Position;
import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil;
import org.olap4j.mdx.AxisNode;
import org.olap4j.mdx.CallNode;
import org.olap4j.mdx.CubeNode;
import org.olap4j.mdx.DimensionNode;
import org.olap4j.mdx.DrillThroughNode;
import org.olap4j.mdx.HierarchyNode;
import org.olap4j.mdx.IdentifierNode;
import org.olap4j.mdx.IdentifierSegment;
import org.olap4j.mdx.LevelNode;
import org.olap4j.mdx.LiteralNode;
import org.olap4j.mdx.MemberNode;
import org.olap4j.mdx.ParameterNode;
import org.olap4j.mdx.ParseTreeNode;
import org.olap4j.mdx.ParseTreeVisitor;
import org.olap4j.mdx.PropertyValueNode;
import org.olap4j.mdx.SelectNode;
import org.olap4j.mdx.Syntax;
import org.olap4j.mdx.WithMemberNode;
import org.olap4j.mdx.WithSetNode;
import org.olap4j.metadata.Cube;
import org.olap4j.metadata.Datatype;
import org.olap4j.metadata.Dimension;
import org.olap4j.metadata.Hierarchy;
import org.olap4j.metadata.Level;
import org.olap4j.metadata.Measure;
import org.olap4j.metadata.Measure.Aggregator;
import org.olap4j.metadata.Member;
import org.olap4j.metadata.NamedList;
/**
* @author b-kaempgen
*
* * In olap4ld, the following process is executed, if an MDX query is
* issued:
*
* * The MDX query is parsed and validated. A
* "Parse tree model for an MDX SELECT statement" is created: SelectNode
* implements ParseTreeNode
*
* * executeOlapSparqlQuery(SelectNode selectNode) is executed on this
* SelectNode
*
* * A MdxMethodVisitor is instantiated and goes through the entire MDX
* tree to create the needed information for the SPARQL OLAP query.
*
* An MdxMethodVisitor has as Input a SelectNode and as Output
*
*
* MdxMethodVisitor goes through a MDX parse tree and returns the
* multidimensional element (e.g., cube, level, list of list of
* members,...)
*
* Momentarily, it can be used to: * Start from a cube node to retrieve
* the cube * Start from an axis node to retrieve the list of list of
* members ** From the list of list of members, we can retrieve the
* hierarchy list and measures
*
*/
public class MdxMetadataExtractorVisitor<Object> implements ParseTreeVisitor<Object> {
private Olap4ldStatement olap4jStatement;
private Object current = null;
private List<Member> withMembers = new ArrayList<Member>();
private Cube cube;
private List<Position> columnPositions = new ArrayList<Position>();
private List<Position> rowPositions = new ArrayList<Position>();
private List<Position> filterPositions = new ArrayList<Position>();
public MdxMetadataExtractorVisitor(Olap4ldStatement olap4jStatement) {
this.olap4jStatement = olap4jStatement;
}
/**
* This is the starting point of going through a MDX select parse tree.
*/
public Object visit(SelectNode selectNode) {
// We do not implement with, yet.
if (!selectNode.getWithList().isEmpty()) {
for (ParseTreeNode with : selectNode.getWithList()) {
/*
* For each with, we need to create a new calculated measure and
* set the expression
*
* For that, however, we need to identify the elements in the
* expression, so that the OLAP engine does not need to identify
* them, any more.
*/
// We do not need to use it, since the visitor stores it in
// itself.
// TODO: This is a hack. Instead, I want to use calculated
// measures with their expression.
// TODO: Accept should work also.
with.accept(this);
}
}
// I know, that getFrom gives me a cubeNode, therefore, I know the cube
this.cube = (Cube) selectNode.getFrom().accept(this);
// Filter (create MetaData for the filter axis)
AxisNode filter = selectNode.getFilterAxis();
if (filter.getExpression() != null) {
/*
* If a set of tuples is supplied as the slicer expression, MDX will
* attempt to evaluate the set, aggregating the result cells in
* every tuple along the set. In other words, MDX will attempt to
* use the Aggregate function on the set, aggregating each measure
* by its associated aggregation function. The following examples
* show a valid WHERE clause using a set of tuples:
*
* WHERE { ([Time].[1st half], [Route].[nonground]), ([Time].[1st
* half], [Route].[ground]) }
*
* If the «slicer_specification» cannot be resolved into a single
* tuple, an error will occur.
*
* For us, a tuple is a position in the axis.
*/
// I should create a visitor that goes through the AxisNode (axis)
// and returns with positions
// MdxMethodVisitor<Object> ldMethodVisitor = new
// MdxMethodVisitor<Object>(
// olap4jStatement);
Object result = filter.accept(this);
List<Object> listResult = (List<Object>) result;
/*
* From the method visitor, we get a list of (possible list of)
* members
*
* This means, we either get one tuple (position) or a list of positions.
*/
filterPositions = this.createPositionsList(listResult);
// pw.println();
// pw.print("WHERE ");
// filterAxis.unparse(writer);
}
// When going through the axes: Prepare groupbylist, a set of levels
// Prepare list of levels (excluding Measures!)
// Go through tuple get dimensions, if not measure
// ArrayList<Level> groupbylist = new ArrayList<Level>();
List<AxisNode> axisList = selectNode.getAxisList();
// First is columns
AxisNode columnAxis = axisList.get(0);
Object result = columnAxis.accept(this);
List<Object> listResult = (List<Object>) result;
/*
* From the method visitor, we get a list of (possible list of) members
*/
columnPositions = this.createPositionsList(listResult);
// Second is rows
AxisNode rowAxis = axisList.get(1);
result = rowAxis.accept(this);
List<Object> listResult2 = (List<Object>) result;
/*
* From the method visitor, we get a list of (possible list of) members
*/
rowPositions = this.createPositionsList(listResult2);
if (axisList.size() > 2) {
throw new UnsupportedOperationException(
"Only columns and rows are supported in MDX query!");
}
return null;
}
/**
* TODO: From the parsed list of lists of members, it should be possible to
* create the hierarchy list. Note, we do also include the measures
* hierarchy, here, since the hierarchies give an impression of the look of
* the pivot table.
*
* @param list
* @return
*/
public List<Hierarchy> createHierarchyList(List<Position> axisPositions) {
/*
* The list of hierarchies used in an axis, the dimensionality in terms
* of hierarchies
*/
List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>();
// We have a list of tuples (lists)
// List of "positions"
if (!axisPositions.isEmpty()) {
Position position = axisPositions.get(0);
List<Member> list1 = position.getMembers();
for (Member object2 : list1) {
// Now, should be member
if (object2 instanceof Member) {
if (object2 instanceof Measure) {
// Measures are used, also.
hierarchyList.add(((Member) object2).getHierarchy());
} else {
hierarchyList.add(((Member) object2).getHierarchy());
}
} else {
throw new UnsupportedOperationException(
"Inside the list we should only have members.");
}
}
}
return hierarchyList;
}
@Override
public Object visit(AxisNode axis) {
// TODO Auto-generated method stub
return axis.getExpression().accept(this);
}
/**
* In our case, a calcMember node always returns one measure with a new name
* and an expression of a call node with two member nodes.
*/
public Object visit(WithMemberNode calcMemberNode) {
List<IdentifierSegment> identifierList = calcMemberNode.getIdentifier()
.getSegmentList();
String[] identifierArray = new String[identifierList.size()];
for (int i = 0; i < identifierList.size(); i++) {
identifierArray[i] = identifierList.get(i).toString();
}
String calculatedMemberName = Olap4ldLinkedDataUtil.implodeArray(
identifierArray, ".");
String operatorName = ((CallNode) calcMemberNode.getExpression())
.getOperatorName();
Member member1 = (Member) ((CallNode) calcMemberNode.getExpression())
.getArgList().get(0).accept(this);
Member member2 = (Member) ((CallNode) calcMemberNode.getExpression())
.getArgList().get(1).accept(this);
MemberNode memberNode1 = new MemberNode(null, member1);
MemberNode memberNode2 = new MemberNode(null, member2);
List<ParseTreeNode> args = new ArrayList<ParseTreeNode>();
args.add(memberNode1);
args.add(memberNode2);
CallNode callNode = new CallNode(null, operatorName, Syntax.Internal,
args);
// We need to set a level, we take the same level as the first member
// We need to set ordinal: Ordinal should be Level.Members.size +
// withMembers.size
// TODO: LEVEL_CARDINALITY is not solved, yet.
try {
Member calculatedMeasure = new Olap4ldMeasure(
(Olap4ldLevel) member1.getLevel(), calculatedMemberName,
calculatedMemberName, calculatedMemberName, "", null,
Aggregator.CALCULATED, callNode, Datatype.INTEGER, true,
member1.getLevel().getMembers().size() + withMembers.size());
withMembers.add(calculatedMeasure);
return null;
} catch (OlapException e) {
// TODO Auto-generated catch block
throw new UnsupportedOperationException(
"Probably no members of the level were found.");
}
}
@Override
public Object visit(WithSetNode calcSetNode) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(CallNode call) {
try {
/*
* Crossjoin returns the results in the order of the first set
* expression, then within that, by the second second expression.
*/
if (call.getOperatorName().toLowerCase().equals("crossjoin")) {
return callCrossJoin(call);
}
if (call.getOperatorName().toLowerCase().equals("members")) {
return callMembers(call);
}
/*
* The sorting functions (Order, TopCount, BottomCount, TopPercent,
* Hierarchize, etc.) use a stable sorting algorithm. Metadata methods
* such as <Hierarchy>.Members return their results in natural order.
*/
if (call.getOperatorName().toLowerCase().equals("hierarchize")) {
return callHierarchize(call);
}
if (call.getOperatorName().equals("{}")) {
return callBraces(call);
}
if (call.getOperatorName().toLowerCase().equals("filter")) {
return callFilter(call);
}
if (call.getOperatorName().toLowerCase().equals("cast")) {
return callCast(call);
}
if (call.getOperatorName().toLowerCase().equals("currentmember")) {
return callCurrentMember(call);
}
if (call.getOperatorName().toLowerCase().equals("name")) {
return callName(call);
}
if (call.getOperatorName().toLowerCase().equals("<")
|| call.getOperatorName().toLowerCase().equals("<=")
|| call.getOperatorName().toLowerCase().equals(">")
|| call.getOperatorName().toLowerCase().equals(">=")) {
return callNumericComparator(call);
}
if (call.getOperatorName().toLowerCase().equals("+")
|| call.getOperatorName().toLowerCase().equals("-")
|| call.getOperatorName().toLowerCase().equals("*")
|| call.getOperatorName().toLowerCase().equals("/")) {
return callNumBinOp(call);
}
if (call.getOperatorName().toLowerCase().equals("and")) {
return callBooleanAND(call);
}
if (call.getOperatorName().toLowerCase().equals(":")) {
return callColon(call);
}
} catch (OlapException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
throw new UnsupportedOperationException("The "
+ call.getOperatorName().toLowerCase()
+ " method is not supported by olap4ld, yet.");
}
private Object callColon(CallNode call) {
// Here, we have two Identifier nodes, each having their own name, but
// we need to create a new i-node
return null;
}
private Object callCurrentMember(CallNode call) {
return current;
}
private Object callBooleanAND(CallNode call) {
Boolean one = (Boolean) call.getArgList().get(0).accept(this);
Boolean two = (Boolean) call.getArgList().get(1).accept(this);
return (Object) new Boolean(one.booleanValue() && two.booleanValue());
}
/**
* This operator should seldomly be called since most calculations will be
* done in the engine and not when interpreting the MDX.
*
* @param call
* @return
*/
private Object callNumBinOp(CallNode call) {
// If we cannot cast to double, we return null.
// Also, we need to decode to uri representation
Double one = new Double(Olap4ldLinkedDataUtil.convertMDXtoURI(call
.getArgList().get(0).accept(this).toString()).toString());
Double two = new Double(Olap4ldLinkedDataUtil.convertMDXtoURI(call
.getArgList().get(1).accept(this).toString()).toString());
if (call.getOperatorName().equals("+")) {
return (Object) new Double(one.doubleValue() + two.doubleValue());
}
if (call.getOperatorName().equals("-")) {
return (Object) new Double(one.doubleValue() - two.doubleValue());
}
if (call.getOperatorName().equals("*")) {
return (Object) new Double(one.doubleValue() * two.doubleValue());
}
if (call.getOperatorName().equals("/")) {
return (Object) new Double(one.doubleValue() / two.doubleValue());
}
throw new UnsupportedOperationException(
"Binary numeric operator unknown.");
}
/**
* OLAP4J seems to work with java.math.BigDecimal, therefore we need to
* assume that we get those.
*
* @param call
* @return
*/
private Object callNumericComparator(CallNode call) {
Double one = (Double) call.getArgList().get(0).accept(this);
Double two = (Double) call.getArgList().get(1).accept(this);
if (call.getOperatorName().equals("<")) {
return (Object) new Boolean(one.doubleValue() < two.doubleValue());
}
if (call.getOperatorName().equals("<=")) {
return (Object) new Boolean(one.doubleValue() <= two.doubleValue());
}
if (call.getOperatorName().equals(">")) {
return (Object) new Boolean(one.doubleValue() > two.doubleValue());
}
if (call.getOperatorName().equals(">=")) {
return (Object) new Boolean(one.doubleValue() >= two.doubleValue());
}
throw new UnsupportedOperationException("Numeric comparator unknown.");
}
private Object callName(CallNode call) {
// Only one element
Object arg = call.getArgList().get(0).accept(this);
if (arg instanceof Member) {
Member member = (Member) arg;
return (Object) member.getName();
}
throw new UnsupportedOperationException(
"So far Name only for members supported!");
}
/**
* Cast operator
*
* The Cast operator converts scalar expressions to other types. The syntax
* is
*
* Cast(<Expression> AS <Type>)
*
* where <Type> is one of:
*
* BOOLEAN NUMERIC DECIMAL STRING
*
* @param call
* @return
*/
@SuppressWarnings("unchecked")
private Object callCast(CallNode call) {
// TODO: How is cast args given?
// The second parameter gives us the cast
String caster = (String) call.getArgList().get(1).accept(this);
String value = (String) call.getArgList().get(0).accept(this);
// We have to translate MDX first to real value since for the square
// brackets
value = Olap4ldLinkedDataUtil.convertMDXtoURI(value).toString();
if (caster.equals("NUMERIC")) {
return (Object) (new Double(value));
}
throw new UnsupportedOperationException(
"So far Name only for NUMERIC casts supported!");
}
@SuppressWarnings("unchecked")
private Object callCrossJoin(CallNode call) {
// Will be two lists
List<Object> listArg1 = (List<Object>) call.getArgList().get(0)
.accept(this);
List<List<Object>> result = new ArrayList<List<Object>>();
List<Object> listArg2 = (List<Object>) call.getArgList().get(1)
.accept(this);
// Would return one list of lists
for (Object objectArg1 : listArg1) {
for (Object objectArg2 : listArg2) {
List<Object> newList = new ArrayList<Object>();
// Possibly, object and object1 are tuples of members.
if (objectArg1 instanceof List<?>) {
ArrayList<Object> mylist = (ArrayList<Object>) objectArg1;
for (Object myobject : mylist) {
newList.add(myobject);
}
} else {
newList.add(objectArg1);
}
if (objectArg2 instanceof List<?>) {
ArrayList<Object> mylist = (ArrayList<Object>) objectArg2;
for (Object myobject : mylist) {
newList.add(myobject);
}
} else {
newList.add(objectArg2);
}
result.add(newList);
}
}
return (Object) result;
}
/**
* The filter function will have as first argument a list of elements. As
* second argument, it will have a logical expression that needs to be valid
* in order to have an element included.
*
* The filter function returns a new list of elements that have been
* filtered.
*
* @param call
* @return
*/
@SuppressWarnings("unchecked")
private Object callFilter(CallNode call) {
List<ParseTreeNode> args = call.getArgList();
List<Object> listToFilter = (List<Object>) args.get(0).accept(this);
List<Object> elementList = new ArrayList<Object>();
for (Object object : listToFilter) {
// For now, always members
current = (Object) object;
/*
* Now, that I know which element to filter, I can go through the
* second part and replace currentmember with this member
*/
Object object1 = args.get(1).accept(this);
// This object should be a Boolean value
Boolean bool = (Boolean) object1;
if (bool) {
elementList.add(object);
}
// listToFilter.remove(object);
}
return (Object) elementList;
}
@SuppressWarnings("unchecked")
private Object callChildren(CallNode call) throws OlapException {
/*
* Children can only be for a member
*/
Object object = call.getArgList().get(0).accept(this);
if (object instanceof Member) {
Member member = (Member) object;
NamedList<? extends Member> children = member.getChildMembers();
return (Object) children;
} else {
throw new OlapException("Children can only be executed on members!");
}
}
@SuppressWarnings("unchecked")
private Object callMembers(CallNode call) throws OlapException {
/*
* Members can be for a dimension, hierarchy, level
*/
Object object = call.getArgList().get(0).accept(this);
List<Member> allMembers = new ArrayList<Member>();
if (object instanceof Dimension) {
Dimension dimension = (Dimension) object;
// Would return a list of Members
NamedList<Hierarchy> hierarchies = dimension.getHierarchies();
for (int i = 0; i < hierarchies.size(); i++) {
Hierarchy hierarchy = hierarchies.get(i);
NamedList<Level> levels = hierarchy.getLevels();
for (int j = 0; j < levels.size(); j++) {
Level level = levels.get(j);
List<Member> members = level.getMembers();
for (Member member : members) {
allMembers.add(member);
}
}
}
return (Object) allMembers;
}
if (object instanceof Hierarchy) {
Hierarchy hierarchy = (Hierarchy) object;
NamedList<Level> levels = hierarchy.getLevels();
for (int j = 0; j < levels.size(); j++) {
Level level = levels.get(j);
List<Member> members = level.getMembers();
for (Member member : members) {
allMembers.add(member);
}
}
return (Object) allMembers;
}
if (object instanceof Level) {
Level level = (Level) object;
// Would return a list of Members
return (Object) level.getMembers();
}
return null;
}
@SuppressWarnings("unchecked")
private Object callHierarchize(CallNode call) {
List<Object> list = (List<Object>) call.getArgList().get(0)
.accept(this);
for (Object object : list) {
if (object instanceof List) {
// First order for the first, then the second...
// TODO
}
// First order
// TODO
}
// Would return a sorted list
return (Object) list;
}
@SuppressWarnings("unchecked")
private Object callBraces(CallNode call) {
List<Object> result = new ArrayList<Object>();
// Single objects
for (ParseTreeNode parseTreeObj : call.getArgList()) {
Object object = parseTreeObj.accept(this);
// A list should not be put in another list.
/*
* TODO: Is this correct? What if I have List and Member?
*/
if (object instanceof List) {
List<Object> list = (List<Object>) object;
for (Object object1 : list) {
result.add(object1);
}
} else {
result.add(object);
}
}
// Now, I want to return a list of the arguments
return (Object) result;
}
@SuppressWarnings("unchecked")
@Override
public Object visit(IdentifierNode id) {
//
List<IdentifierSegment> segmentList = id.getSegmentList();
// If we have withMembers, then look there, first.
if (!withMembers.isEmpty()) {
String[] identifierArray = new String[segmentList.size()];
for (int i = 0; i < segmentList.size(); i++) {
identifierArray[i] = segmentList.get(i).toString();
}
String calculatedMemberName = Olap4ldLinkedDataUtil.implodeArray(
identifierArray, ".");
for (Member member : withMembers) {
if (member.getUniqueName().equals(calculatedMemberName)) {
return (Object) member;
}
}
}
// Assume that only one segment
/*
* TODO: .members is then seen as part of the name which is not ideal.
*/
int segmentIndex = 0;
// .name() removes the square brackets if contained, therefore, we use
// toString()
String segmentName = segmentList.get(segmentIndex).toString();
segmentIndex++;
// Now, we check whether name is available
try {
NamedList<Cube> cubes = this.olap4jStatement.olap4jConnection
.getOlapSchema().getCubes();
Cube queriedcube = null;
// Here, we need to make sure that we search for a cube
// which we know if a cube is not already given (== null).
if (this.cube == null) {
queriedcube = cubes.get(segmentName);
}
// Watch out cubes.contains has as parameter a cube!
if (queriedcube != null) {
return (Object) queriedcube;
} else {
boolean first = true;
for (Cube cube1 : cubes) {
// If cube already given by the query, use it.
if (this.cube != null && first) {
cube1 = this.cube;
first = false;
} else if (queriedcube != null && !first) {
throw new OlapException(
"If a cube is given, we should find the identifier in there!");
}
NamedList<Dimension> dimensions = cube1.getDimensions();
Dimension dimension = dimensions.get(segmentName);
/*
* Two possibilities: Either search for the single
* identifier or unique identifier of segments
*/
if (dimension == null) {
// Go through dimensions
for (Dimension dimension1 : dimensions) {
NamedList<Hierarchy> hierarchies = dimension1
.getHierarchies();
Hierarchy hierarchy = hierarchies.get(segmentName);
if (hierarchy != null) {
return (Object) hierarchy;
} else {
// Go through hierarchies
for (Hierarchy hierarchy1 : hierarchies) {
NamedList<Level> levels = hierarchy1
.getLevels();
Level level = levels.get(segmentName);
if (level != null) {
return (Object) level;
} else {
// Go through levels
for (Level level1 : levels) {
List<Member> members = level1
.getMembers();
// Go through members
for (Member member : members) {
if (member.getName().equals(
segmentName)) {
return (Object) member;
}
}
}
}
}
}
}
// More detailed segment list
} else if (dimension != null
&& segmentIndex >= segmentList.size()) {
return (Object) dimension;
} else if (dimension != null) {
segmentName = segmentList.get(segmentIndex).toString();
Hierarchy hierarchy = dimension.getHierarchies().get(
segmentName);
segmentIndex++;
if (hierarchy == null) {
throw new OlapException(
"MDX identifier not properly given:"
+ segmentName);
} else if (hierarchy != null
&& segmentIndex >= segmentList.size()) {
return (Object) hierarchy;
} else {
segmentName = segmentList.get(segmentIndex)
.toString();
Level level = hierarchy.getLevels()
.get(segmentName);
segmentIndex++;
if (level == null) {
throw new OlapException(
"MDX identifier not properly given:"
+ segmentName);
} else if (level != null
&& segmentIndex >= segmentList.size()) {
return (Object) level;
} else {
segmentName = segmentList.get(segmentIndex)
.toString();
for (Member member : level.getMembers()) {
if (member.getName().equals(segmentName)) {
return (Object) member;
}
}
}
}
}
}
}
} catch (OlapException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String identifier = "";
for (int i = 0; i < segmentList.size(); i++) {
identifier += segmentList.get(i).toString();
}
throw new UnsupportedOperationException(
"There should be an OLAP element for each identifier, also this one:"
+ identifier);
}
@Override
public Object visit(ParameterNode parameterNode) {
// TODO Auto-generated method stub
return null;
}
@SuppressWarnings("unchecked")
public Object visit(CubeNode cubeNode) {
return (Object) cubeNode.getCube();
}
@Override
public Object visit(DimensionNode dimensionNode) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(HierarchyNode hierarchyNode) {
// TODO Auto-generated method stub
return null;
}
@SuppressWarnings("unchecked")
public Object visit(LevelNode levelNode) {
// TODO Auto-generated method stub
return (Object) levelNode.getLevel();
}
@SuppressWarnings("unchecked")
public Object visit(MemberNode memberNode) {
// TODO Auto-generated method stub
return (Object) memberNode.getMember();
}
/**
* Numeric literal value use BigDecimal, in which case we transform it into
* double.
*/
@Override
public Object visit(LiteralNode literalNode) {
Object value = (Object) literalNode.getValue();
if (value instanceof BigDecimal) {
return (Object) (new Double(((BigDecimal) value).doubleValue()));
}
if (value instanceof String) {
return (Object) ((String) value);
}
throw new UnsupportedOperationException(
"So far, only BigDecimal and Strings as literalNodes supported.");
}
@Override
public Object visit(PropertyValueNode propertyValueNode) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(DrillThroughNode drillThroughNode) {
// TODO Auto-generated method stub
return null;
}
/**
* Transforms the list of lists of members into a position list.
*
* @param list
* @return
*/
@SuppressWarnings("unchecked")
private List<Position> createPositionsList(List<Object> list) {
// I go through list, and check whether again a list
List<Position> positions = new ArrayList<Position>();
// List of "positions"
for (Object object : list) {
List<Member> positionMembers = new ArrayList<Member>();
if (object instanceof List) {
List<Object> list1 = (List<Object>) object;
for (Object object2 : list1) {
// Now, should be member
if (object2 instanceof Member) {
positionMembers.add((Member) object2);
} else {
throw new UnsupportedOperationException(
"Inside the list we should only have members.");
}
}
} else if (object instanceof Member) {
positionMembers.add((Member) object);
} else {
throw new UnsupportedOperationException(
"Inside the list we should only have members.");
}
Olap4ldPosition aPosition = new Olap4ldPosition(positionMembers,
positions.size());
positions.add(aPosition);
}
return positions;
}
public List<Position> getColumnPositions() throws OlapException {
if (this.columnPositions == null) {
throw new OlapException("We do not have column positions!");
}
return this.columnPositions;
}
public List<Position> getRowPositions() throws OlapException {
if (this.rowPositions == null) {
throw new OlapException("We do not have row positions!");
}
return this.rowPositions;
}
public List<Position> getFilterPositions() throws OlapException {
if (this.filterPositions == null) {
throw new OlapException("We do not have filter positions!");
}
return this.filterPositions;
}
public Cube getCube() throws OlapException {
if (this.cube == null) {
throw new OlapException("We do not have a cube!");
}
return this.cube;
}
}