/* // // 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 static org.olap4j.driver.olap4ld.Olap4ldUtil.MDDATASET_NS; import static org.olap4j.driver.olap4ld.Olap4ldUtil.SOAP_NS; import static org.olap4j.driver.olap4ld.Olap4ldUtil.XMLA_NS; import static org.olap4j.driver.olap4ld.Olap4ldUtil.childElements; import static org.olap4j.driver.olap4ld.Olap4ldUtil.findChild; import static org.olap4j.driver.olap4ld.Olap4ldUtil.findChildren; import static org.olap4j.driver.olap4ld.Olap4ldUtil.integerElement; import static org.olap4j.driver.olap4ld.Olap4ldUtil.parse; import static org.olap4j.driver.olap4ld.Olap4ldUtil.stringElement; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Date; import java.sql.Ref; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Time; import java.sql.Timestamp; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Pattern; import org.olap4j.Axis; import org.olap4j.Cell; import org.olap4j.CellSet; import org.olap4j.CellSetAxis; import org.olap4j.CellSetAxisMetaData; import org.olap4j.CellSetMetaData; import org.olap4j.OlapException; import org.olap4j.OlapStatement; import org.olap4j.Position; import org.olap4j.driver.olap4ld.helper.GetFirstOccurrenceVisitor; import org.olap4j.driver.olap4ld.helper.GraphVizVisualisationVisitor; import org.olap4j.driver.olap4ld.helper.LdHelper; import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil; import org.olap4j.driver.olap4ld.helper.PreprocessMdxVisitor; import org.olap4j.driver.olap4ld.linkeddata.BaseCubeOp; import org.olap4j.driver.olap4ld.linkeddata.ConvertCubeOp; import org.olap4j.driver.olap4ld.linkeddata.DiceOp; import org.olap4j.driver.olap4ld.linkeddata.DrillAcrossOp; import org.olap4j.driver.olap4ld.linkeddata.EmbeddedSesameEngine; import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapOp; import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapOperatorQueryPlanVisitor; import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapQueryPlan; import org.olap4j.driver.olap4ld.linkeddata.ProjectionOp; import org.olap4j.driver.olap4ld.linkeddata.QueryException; import org.olap4j.driver.olap4ld.linkeddata.ReconciliationCorrespondence; import org.olap4j.driver.olap4ld.linkeddata.Restrictions; import org.olap4j.driver.olap4ld.linkeddata.RollupOp; import org.olap4j.driver.olap4ld.linkeddata.SliceOp; import org.olap4j.impl.Olap4jUtil; import org.olap4j.mdx.ParseTreeNode; import org.olap4j.mdx.SelectNode; import org.olap4j.metadata.Catalog; import org.olap4j.metadata.Cube; import org.olap4j.metadata.Dimension; import org.olap4j.metadata.Hierarchy; import org.olap4j.metadata.Level; import org.olap4j.metadata.Measure; import org.olap4j.metadata.Member; import org.olap4j.metadata.NamedList; import org.olap4j.metadata.Property; import org.olap4j.metadata.Schema; import org.semanticweb.yars.nx.Node; import org.semanticweb.yars.nx.Resource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; /** * Implementation of {@link org.olap4j.CellSet} for XML/A providers. * * <p> * This class has sub-classes which implement JDBC 3.0 and JDBC 4.0 APIs; it is * instantiated using {@link Factory#newCellSet}. * </p> * * @author jhyde, bkaempgen * @version $Id: XmlaOlap4jCellSet.java 455 2011-05-24 10:01:26Z jhyde $ * @since May 24, 2007 */ abstract class Olap4ldCellSet implements CellSet { // Meta Meta Data final Olap4ldStatement olap4jStatement; protected boolean closed; @SuppressWarnings("unused") private static final List<String> standardProperties = Arrays.asList( "UName", "Caption", "LName", "LNum", "DisplayInfo"); // Metadata private Olap4ldCellSetMetaData metaData; private List<Olap4ldCellSetAxis> axisList; private List<CellSetAxis> immutableAxisList; private Olap4ldCellSetAxis filterAxis; // Data private final Map<Integer, Cell> cellMap = new HashMap<Integer, Cell>(); /** * The value map that the OLAP query populates and from which the cells are * queried. It stores string representations of cell values of indexed * measures in string arrays, keyed-off by a hashcode of the concatenation * of the uniquely identifying list of members. */ private boolean areCellsPopulated = false; private final Map<Integer, String[]> newValueMap = new HashMap<Integer, String[]>(); private LogicalOlapQueryPlan queryplan; // The list of measures queried here as part of metadata private List<Member> measureList; private ArrayList<Integer> dimensionindicesList; /** * Creates an XmlaOlap4jCellSet. * * @param olap4jStatement * Statement */ Olap4ldCellSet(Olap4ldStatement olap4jStatement) { assert olap4jStatement != null; this.olap4jStatement = olap4jStatement; this.closed = false; } /** * Returns the error-handler. * * @return Error handler */ private LdHelper getHelper() { return olap4jStatement.olap4jConnection.helper; } /** * Execute the query plan. * * @throws OlapException */ private void populateCells() throws OlapException { Olap4ldUtil._log.info("Execute logical query plan: " + queryplan.toString()); /* * Now, execute Logical OLAP Operator Query Tree in LinkedDataEngine */ List<Node[]> olapQueryResult = olap4jStatement.olap4jConnection.myLinkedData .executeOlapQuery(queryplan); Olap4ldUtil._log.info("Execute logical query plan: Cache results."); long time = System.currentTimeMillis(); // Important part, we need to allow efficient access to the results cacheDataFromOlapQuery(olapQueryResult); areCellsPopulated = true; // We track the time it takes to prepare the query time = System.currentTimeMillis() - time; Olap4ldUtil._log .info("Execute logical query plan: Cache results finished in " + time + "ms."); } /** * * This is the method to map an MDX query to 1) metadata of a pivot table * and 2) an initial logical olap query plan to populate the pivot table * cells. * * We create: this.metaData = metadata; this.axisList = axisList; * this.immutableAxisList = * Olap4jUtil.cast(Collections.unmodifiableList(axisList)); this.filterAxis * = filterCellSetAxis; * * From this metadata, we can then create a nested set of OLAP operations (= * OLAP query) * * * @param selectNode * @throws OlapException */ public void createMetadata(SelectNode selectNode) throws OlapException { // Any advantage of having a prepared statement? No, since we use NON // EMPTY CLAUSE. // if (olap4jStatement instanceof Olap4ldPreparedStatement) { // this.metaData = ((Olap4ldPreparedStatement) // olap4jStatement).cellSetMetaData; // } else { // I pre-process the select node // I need to replace Identifier.Members with Members(Identifier) PreprocessMdxVisitor<Object> preprocessMdxVisitor = new PreprocessMdxVisitor<Object>(); selectNode.accept(preprocessMdxVisitor); // Visitor to create: metaData, axisList, filterAxis MdxMetadataExtractorVisitor<Object> visitor = new MdxMetadataExtractorVisitor<Object>( olap4jStatement); selectNode.accept(visitor); // Can also return the global cube // Question: Do we actually get back the "global cube" here? Yes. // This is the reason for generating a new cube (global cube): Olap4ld // requires exactly on cube? Cube cube = visitor.getCube(); // Axes Metadata (create MetaData for one specific axis) List<Olap4ldCellSetAxisMetaData> axisMetaDataList = new ArrayList<Olap4ldCellSetAxisMetaData>(); // Axes List<Olap4ldCellSetAxis> axisList = new ArrayList<Olap4ldCellSetAxis>(); // Properties are not computed, yet. List<Olap4ldCellSetMemberProperty> propertyList = new ArrayList<Olap4ldCellSetMemberProperty>(); List<Olap4ldCellSetMemberProperty> properties = propertyList; Axis columnAxis = Axis.COLUMNS; List<Position> columnPositions = visitor.getColumnPositions(); List<Hierarchy> columnHierarchies = new ArrayList<Hierarchy>(); columnHierarchies = visitor.createHierarchyList(columnPositions); // List<Level> columnLevels = new ArrayList<Level>(); // columnLevels = this.createLevelList(columnPositions); // Add axisMetaData to list of axes metadata final Olap4ldCellSetAxisMetaData columnAxisMetaData = new Olap4ldCellSetAxisMetaData( this.olap4jStatement.olap4jConnection, columnAxis, columnHierarchies, properties); axisMetaDataList.add(columnAxisMetaData); // Add cellSetAxis to list of axes final Olap4ldCellSetAxis columnCellSetAxis = new Olap4ldCellSetAxis( this, columnAxis, Collections.unmodifiableList(columnPositions)); axisList.add(columnCellSetAxis); Axis rowAxis = Axis.ROWS; List<Position> rowPositions = visitor.getRowPositions(); List<Hierarchy> rowHierarchies = new ArrayList<Hierarchy>(); rowHierarchies = visitor.createHierarchyList(rowPositions); // List<Level> rowLevels = new ArrayList<Level>(); // rowLevels = this.createLevelList(rowPositions); // Add axisMetaData to list of axes metadata final Olap4ldCellSetAxisMetaData rowAxisMetaData = new Olap4ldCellSetAxisMetaData( this.olap4jStatement.olap4jConnection, rowAxis, rowHierarchies, properties); axisMetaDataList.add(rowAxisMetaData); // Add cellSetAxis to list of axes final Olap4ldCellSetAxis rowCellSetAxis = new Olap4ldCellSetAxis(this, rowAxis, Collections.unmodifiableList(rowPositions)); axisList.add(rowCellSetAxis); Axis filterAxis = Axis.FILTER; List<Position> filterPositions = visitor.getFilterPositions(); // Create filter hierarchies List<Hierarchy> filterHierarchies = new ArrayList<Hierarchy>(); filterHierarchies = visitor.createHierarchyList(filterPositions); // List<Level> filterLevels = new ArrayList<Level>(); // filterLevels = this.createLevelList(filterPositions); // I need to create a filter axis metadata Olap4ldCellSetAxisMetaData filterAxisMetaData = new Olap4ldCellSetAxisMetaData( this.olap4jStatement.olap4jConnection, filterAxis, Collections.<Hierarchy> unmodifiableList(filterHierarchies), Collections.<Olap4ldCellSetMemberProperty> emptyList()); // I need to create a filter axis Olap4ldCellSetAxis filterCellSetAxis = new Olap4ldCellSetAxis(this, filterAxis, Collections.unmodifiableList(filterPositions)); List<Olap4ldCellProperty> cellProperties = new ArrayList<Olap4ldCellProperty>(); // I do not support cell properties, yet. if (!selectNode.getCellPropertyList().isEmpty()) { // pw.println(); // pw.print("CELL PROPERTIES "); // k = 0; // for (IdentifierNode cellProperty : cellPropertyList) { // if (k++ > 0) { // pw.print(", "); // } // cellProperty.unparse(writer); // } } Olap4ldCellSetMetaData metadata = new Olap4ldCellSetMetaData( olap4jStatement, (Olap4ldCube) cube, filterAxisMetaData, axisMetaDataList, cellProperties); this.metaData = metadata; this.axisList = axisList; this.immutableAxisList = Olap4jUtil.cast(Collections .unmodifiableList(axisList)); this.filterAxis = filterCellSetAxis; // We create helper metadata objects. // As a helper set, we use set to collect the measures HashSet<Member> usedMeasureSet = new HashSet<Member>(); // Columns for (Position position : columnPositions) { List<Member> positionmembers = position.getMembers(); for (int i = 0; i < positionmembers.size(); i++) { // Check whether measure already contained. if ((positionmembers.get(i).getMemberType() == Member.Type.MEASURE || positionmembers .get(i).getMemberType() == Member.Type.FORMULA)) { usedMeasureSet.add(positionmembers.get(i)); } } } // Rows for (Position position : rowPositions) { List<Member> positionmembers = position.getMembers(); for (int i = 0; i < positionmembers.size(); i++) { // Check whether measure already contained. if ((positionmembers.get(i).getMemberType() == Member.Type.MEASURE || positionmembers .get(i).getMemberType() == Member.Type.FORMULA)) { usedMeasureSet.add(positionmembers.get(i)); } } } // In filter axis for (Position list : filterPositions) { // For each position, we get a new member for each restriction set List<Member> filterpositionmembers = list.getMembers(); for (int i = 0; i < filterpositionmembers.size(); i++) { // Check whether measure already contained. if ((filterpositionmembers.get(i).getMemberType() == Member.Type.MEASURE || filterpositionmembers .get(i).getMemberType() == Member.Type.FORMULA)) { usedMeasureSet.add(filterpositionmembers.get(i)); } } } // If no measure, then find default if (usedMeasureSet.isEmpty()) { // Default is simply the first we find. Olap4ldUtil._log .config("Get default (first available) measure in cube."); if (!metaData.cube.getMeasures().isEmpty()) { usedMeasureSet.add(metaData.cube.getMeasures().get(0)); } else { throw new UnsupportedOperationException( "There should always be at least one measure in the cube."); } } // UsedMeasureSet we could store. // We store this list of measures for later looking up the number of a // measure. List<Member> myMeasureList = new ArrayList<Member>(); // We have to set an ordering of measures. Iterator<Member> iterator = usedMeasureSet.iterator(); while (iterator.hasNext()) { Member member = (Member) iterator.next(); myMeasureList.add(member); } this.measureList = myMeasureList; // Now that we have retrieved all MDX metadata, we can create the // Logical Olap Query Plan this.queryplan = createInitialLogicalOlapQueryPlan(); } /** * In this implementation, we have not implemented the transformation from * MDC Parse Tree to Logical Olap Query tree via a visitor pattern but via * procedural code. * * Here, we create an initial logical olap query plan from the retrieved * metadata. * * * @return */ private LogicalOlapQueryPlan createInitialLogicalOlapQueryPlan() { try { // We should not transform cube to Node, but search through cubes Restrictions restrictions = new Restrictions(); restrictions.cubeNamePattern = Olap4ldLinkedDataUtil .convertMDXtoURI(metaData.cube.getUniqueName()); List<Node[]> cubes = this.olap4jStatement.olap4jConnection.myLinkedData .getCubes(restrictions); Map<String, Integer> cubemap = Olap4ldLinkedDataUtil .getNodeResultFields(cubes.get(0)); List<Node[]> cube = new ArrayList<Node[]>(); cube.add(cubes.get(0)); for (Node[] aCube : cubes) { if (aCube[cubemap.get("?CUBE_NAME")] .equals(restrictions.cubeNamePattern)) { // We do not need all cubes. cube.add(aCube); } } LogicalOlapOp queryplanroot = null; /* * The set of "available datasets" is given by the FROM clause. * * For every dataset, we create an own logical query plan. * * In case there are several datasets, we drill-across all the * resulting data cubes. * * I think, this process, we need to "formalise". What exactly do we * do here? * * * According to the global cube defintion we should: Only * drill-across over datasets that 1) show one of the queried * measures, 2) does have all the "inquired dimensions". */ String[] datasets = restrictions.cubeNamePattern.toString().split( ","); List<LogicalOlapOp> singlecubequeryplans = new ArrayList<LogicalOlapOp>(); Olap4ldUtil._log.info("Creating start ops:..."); List<LogicalOlapOp> startops = new ArrayList<LogicalOlapOp>(); // Without Convert-Cube or Merge-Cubes for (int i = 0; i < datasets.length; i++) { String dataset = datasets[i]; restrictions = new Restrictions(); restrictions.cubeNamePattern = new Resource(dataset); // Ask for the cube // List<Node[]> singlecube = // this.olap4jStatement.olap4jConnection.myLinkedData // .getCubes(restrictions); // So far, we only take into account the datasets, // directly. Soon, we // should automatically derive new datasets to query // here, also. // Now, I create all possible derived datasets // Cube from (cube, SlicesRollups, Dices, Projections) LogicalOlapOp basecube = new BaseCubeOp( restrictions.cubeNamePattern.toString()); startops.add(basecube); } // // Convert-Cube combinations // // // // Merge-Cubes combinations // int DEPTH = 2; // for (int i = 0; i <= DEPTH; i++) { // XXX: generateMergeLqpsForDepth is now in TestCase // List<LogicalOlapOp> intermediaryops = Olap4ldLinkedDataUtil // .generateMergeLqpsForDepth(i, datasets, EmbeddedSesameEngine // .getReconciliationCorrespondences(true)); // for (LogicalOlapOp logicalOlapOp : intermediaryops) { // startops.add(logicalOlapOp); // } // } /* * Here, we should be loading the correspondences and creating all * possible derived datasets using XSB prolog. */ Olap4ldUtil._log.info("Finished creating " + startops.size() + " start ops."); Olap4ldUtil._log.info("Started collecting non-null query plans."); for (LogicalOlapOp startop : startops) { LogicalOlapOp singlequeryplan = createInitialQueryPlanPerDataSet(startop); if (singlequeryplan != null) { singlecubequeryplans.add(singlequeryplan); } } Olap4ldUtil._log.info("Finished collecting " + singlecubequeryplans.size() + "non-null query plans."); // for (int i = 0; i < datasets.length; i++) { // String dataset = datasets[i]; // // restrictions = new Restrictions(); // restrictions.cubeNamePattern = new Resource(dataset); // // // Ask for the cube // try { // // // Here, the cubes get loaded. // // XXX: Necessary to do that before execution? // List<Node[]> singlecube = // this.olap4jStatement.olap4jConnection.myLinkedData // .getCubes(restrictions); // // // So far, we only take into account the datasets, // // directly. Soon, we // // should automatically derive new datasets to query // // here, also. // // // Now, I create all possible derived datasets // // // For every correspondence // // // // } catch (OlapException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } queryplanroot = null; // queryplanroot is simply // XXX: can we make it bushy? // XXX: Currently, every single dataset is queried, however, // not every // dataset fits the query. /* * In theory, here we are evaluating the views, i.e., the definition * of the global dataset by the single datasets and the automatic * derivation of more single datasets. */ Olap4ldUtil._log.info("Started building drill-across query plan."); for (LogicalOlapOp logicalOlapOp : singlecubequeryplans) { if (queryplanroot == null) { queryplanroot = logicalOlapOp; } else { DrillAcrossOp drillacross = new DrillAcrossOp( queryplanroot, logicalOlapOp); queryplanroot = drillacross; } } Olap4ldUtil._log.info("Finished building drill-across query plan."); LogicalOlapQueryPlan myplan = new LogicalOlapQueryPlan( queryplanroot); try { Olap4ldUtil._log.info("DOT: " + myplan.toDOT() + "."); } catch (QueryException e) { // TODO Auto-generated catch block e.printStackTrace(); } return myplan; } catch (OlapException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } return null; } private List<LogicalOlapOp> generateStartOps(String[] datasets) { Olap4ldUtil._log.info("Creating start ops:..."); List<LogicalOlapOp> startops = new CopyOnWriteArrayList<LogicalOlapOp>(); // Add all datasets for (int i = 0; i < datasets.length; i++) { startops.add(new BaseCubeOp(datasets[i])); } List<ReconciliationCorrespondence> correspondences = EmbeddedSesameEngine .getReconciliationCorrespondences(true); // cc for (ReconciliationCorrespondence convertCorrespondence : correspondences) { // Check whether convert-cube if (convertCorrespondence.getInputmembers2() == null) { for (LogicalOlapOp olapop : startops) { if (!derivedBy(olapop, convertCorrespondence)) { startops.add(new ConvertCubeOp(olapop, convertCorrespondence)); } } } } // mc for (ReconciliationCorrespondence mergingCorrespondence : correspondences) { // Check whether merge-cubes if (mergingCorrespondence.getInputmembers2() != null) { for (LogicalOlapOp olapop1 : startops) { for (LogicalOlapOp olapop2 : startops) { if (!derivedBy(olapop1, mergingCorrespondence) && !derivedBy(olapop2, mergingCorrespondence)) { startops.add(new ConvertCubeOp(olapop1, olapop2, mergingCorrespondence)); } } } } } Olap4ldUtil._log.info("Finished creating " + startops.size() + " start ops."); return startops; } private boolean derivedBy(LogicalOlapOp olapop, ReconciliationCorrespondence reconciliationCorrespondence) { GetFirstOccurrenceVisitor derivedbyvisitor = new GetFirstOccurrenceVisitor( new ConvertCubeOp(null, reconciliationCorrespondence)); try { olapop.accept(derivedbyvisitor); } catch (QueryException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (derivedbyvisitor.getFirstOccurence() != null) { return true; } else { return false; } } /** * Here, we try to create an initial query plan if the datasets fits. * * @param startop * @return */ private LogicalOlapOp createInitialQueryPlanPerDataSet(LogicalOlapOp startop) { Olap4ldUtil._log.info("Started creating query plan..."); // BaseCube operator boolean first; // Cube from (cube, SlicesRollups, Dices, Projections) // LogicalOlapOp basecube = new BaseCubeOp( // restrictions.cubeNamePattern.toString()); // Need to search for first cube starting from inputOp1 (which defines // the schema). LogicalOlapOp convertcube = startop; LogicalOlapOp previouscube = null; while (convertcube instanceof ConvertCubeOp) { // Check whether all inputmembers fit. // For now, we do not allow the same merge-cubes operator used // twice. if (previouscube != null && ((ConvertCubeOp) convertcube).conversioncorrespondence .getname() .equals(((ConvertCubeOp) previouscube).conversioncorrespondence .getname())) { return null; } previouscube = convertcube; convertcube = ((ConvertCubeOp) convertcube).inputOp1; } BaseCubeOp basecube = (BaseCubeOp) convertcube; Restrictions restrictions = new Restrictions(); restrictions.cubeNamePattern = new Resource(basecube.dataseturi); List<Node[]> measures = new ArrayList<Node[]>(); List<Node[]> dimensions = new ArrayList<Node[]>(); List<Node[]> hierarchies = new ArrayList<Node[]>(); List<Node[]> levels = new ArrayList<Node[]>(); List<Node[]> members = new ArrayList<Node[]>(); try { measures = this.olap4jStatement.olap4jConnection.myLinkedData .getMeasures(restrictions); dimensions = this.olap4jStatement.olap4jConnection.myLinkedData .getDimensions(restrictions); hierarchies = this.olap4jStatement.olap4jConnection.myLinkedData .getHierarchies(restrictions); levels = this.olap4jStatement.olap4jConnection.myLinkedData .getLevels(restrictions); members = this.olap4jStatement.olap4jConnection.myLinkedData .getMembers(restrictions); } catch (OlapException e) { // TODO Auto-generated catch block e.printStackTrace(); } Map<String, Integer> measuremap = Olap4ldLinkedDataUtil .getNodeResultFields(measures.get(0)); Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil .getNodeResultFields(dimensions.get(0)); Map<String, Integer> hierarchymap = Olap4ldLinkedDataUtil .getNodeResultFields(hierarchies.get(0)); Map<String, Integer> membermap = Olap4ldLinkedDataUtil .getNodeResultFields(members.get(0)); // Projections from (cube, SlicesRollups, Dices, Projections) ArrayList<Node[]> projections = new ArrayList<Node[]>(); // From measure set, fill projections boolean isFirst = true; // measureList is metadata describing requested measures for (Member member : this.measureList) { // Olap4ldMember member = (Olap4ldMember) member; // All multidimensional elements in MDX query first belong to the // global cube. // XXX: Should do that for all: Instead of transforming with // metaData.cube which is wrong, I // specifically ask // for that measure in that cube. If not existing, not adding. // List<Node[]> membernode = member // .transformMetadataObject2NxNodes(metaData.cube); Node membernameuri = Olap4ldLinkedDataUtil.convertMDXtoURI(member .getUniqueName()); if (isFirst) { isFirst = false; projections.add(measures.get(0)); } for (Node[] measure : measures) { // For measure, we cannot be sure about the encoding. if (measure[measuremap.get("?MEASURE_UNIQUE_NAME")].toString() .equals(membernameuri.toString())) { projections.add(measure); } } } // If projections empty (only contains header), return null since cube // will be "empty". if (projections.size() == 1) { return null; } LogicalOlapOp projection = new ProjectionOp(startop, projections); // Dice operator // XXX: Note: For now, the RollUp operator needs to be higher than Dice // operator // XXX: We only consider filterAxis for dice, for now. // Watch out: We do not want to have measure positions in the dice, // since measures are projected List<Node[]> newfilterAxisSignature = new ArrayList<Node[]>(); List<List<Node[]>> newfilterAxisPositions = new ArrayList<List<Node[]>>(); // With first position we create signature to be diced but only if diced // members available if (!filterAxis.positions.isEmpty()) { List<Member> signaturemembers = filterAxis.positions.get(0) .getMembers(); first = true; for (Member member : signaturemembers) { Olap4ldMember olapmember = (Olap4ldMember) member; // No measures (from global cube) Olap4ldHierarchy hierarchy = olapmember.getHierarchy(); if (!hierarchy.getUniqueName().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { if (first) { first = false; newfilterAxisSignature.add(hierarchies.get(0)); } // Now, we need to find this hierarchy from the global cube // in the local cube. // If we do not find it, the resulting cube will be empty so // we can return null. // Yes, every cube that may not fit, needs to be "removed" // from the query plan. boolean containedInCube = false; Node hierarchynameuri = Olap4ldLinkedDataUtil .convertMDXtoURI(hierarchy.getUniqueName()); for (Node[] ahierarchy : hierarchies) { if (ahierarchy[hierarchymap .get("?HIERARCHY_UNIQUE_NAME")] .equals(hierarchynameuri)) { /* * The problem is that we should take the dataset * specific hierarchy and not the hierarchy of the * first member (which in our example is from * gesis). * * The problem is that we will have only one * consolidated member from a consolidated hierarchy * etc. * * We then need to find the respective member and * hierarchy in the original dataset. * * Currently, I will probably not find the hierarchy * for the dataset, since the hierarchy will have * the name of another datasets dimension. * * Thus, we also replace the single dataset * identifiers with canonical values. */ newfilterAxisSignature.add(ahierarchy); containedInCube = true; } } // If hierarchy is not contained in dimensions of cube, cube // will be "empty" (since dice on that dimension would not // make sense since it is ALL) if (!containedInCube) { return null; } } } } // With all, we create newfilterAxisPositions for (Position position2 : filterAxis.positions) { ArrayList<Node[]> newMembers = new ArrayList<Node[]>(); List<Member> filterpositionmembers = position2.getMembers(); boolean first1 = true; for (Member member : filterpositionmembers) { Olap4ldMember olapmember = (Olap4ldMember) member; // No measures if (member.getMemberType() != Member.Type.MEASURE) { List<Node[]> membernodes = olapmember .transformMetadataObject2NxNodes(metaData.cube); if (first1) { first1 = false; newMembers.add(membernodes.get(0)); } newMembers.add(membernodes.get(1)); } } // Only if not empty (apart from header), we create and add the // position if (newMembers.size() != 1) { // Ordinal important? // Olap4ldPosition aPosition = new Olap4ldPosition(newMembers, // position2.getOrdinal()); newfilterAxisPositions.add(newMembers); } } // If no dice, no dice LogicalOlapOp dice; if (newfilterAxisSignature.isEmpty() || newfilterAxisSignature.size() == 1) { dice = projection; } else { // Dices from (cube, SlicesRollups, Dices, Projections) dice = new DiceOp(projection, newfilterAxisSignature, newfilterAxisPositions); } // Slice operator // 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 // Here, instead of adding all dimensions, only add those that actually // are rolled-up. ArrayList<Node[]> rollupslevels = new ArrayList<Node[]>(); ArrayList<Node[]> rollupshierarchies = new ArrayList<Node[]>(); ArrayList<Node[]> notslicedhierarchies = new ArrayList<Node[]>(); first = true; // Columns /* * For now, we assume that for each hierarchy, we query for members of * one specific level, only. * * XXX Note, also members could be diced, here also, but we do not * recognise it. */ Position position = axisList.get(0).getPositions().get(0); List<Member> positionmembers = position.getMembers(); for (Member member : positionmembers) { if (member.getMemberType() != Member.Type.MEASURE) { Olap4ldHierarchy olaphierarchy = (Olap4ldHierarchy) member .getLevel().getHierarchy(); Olap4ldLevel olaplevel = (Olap4ldLevel) member.getLevel(); if (first) { // Add headers first = false; rollupslevels.add(olaplevel .transformMetadataObject2NxNodes(metaData.cube) .get(0)); rollupshierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(0)); notslicedhierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(0)); } // Only do that if actual roll-up for that, we need to // compare with max_level int level_number = olaplevel.getDepth(); int max_level_number = olaphierarchy.getLevels().size(); if (level_number < max_level_number) { rollupslevels.add(olaplevel .transformMetadataObject2NxNodes(metaData.cube) .get(1)); rollupshierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(1)); } notslicedhierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube).get(1)); } } // Rows /* * For now, we assume that for each hierarchy, we query for members of * one specific level, only. * * XXX Again, note, also members could be diced, here also, we do not * recognise it. */ position = axisList.get(1).getPositions().get(0); positionmembers = position.getMembers(); for (Member member : positionmembers) { if (member.getMemberType() != Member.Type.MEASURE) { Olap4ldHierarchy olaphierarchy = (Olap4ldHierarchy) member .getLevel().getHierarchy(); Olap4ldLevel olaplevel = (Olap4ldLevel) member.getLevel(); if (first) { // Add headers first = false; rollupslevels.add(olaplevel .transformMetadataObject2NxNodes(metaData.cube) .get(0)); rollupshierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(0)); notslicedhierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(0)); } // Only do that if actual roll-up for that, we need to // compare with max_level int level_number = olaplevel.getDepth(); int max_level_number = olaphierarchy.getLevels().size(); if (level_number < max_level_number) { rollupslevels.add(olaplevel .transformMetadataObject2NxNodes(metaData.cube) .get(1)); rollupshierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube) .get(1)); } notslicedhierarchies.add(olaphierarchy .transformMetadataObject2NxNodes(metaData.cube).get(1)); } } // Check whether all notsliced hierarchies are in the cube for (Node[] hierarchy : notslicedhierarchies) { boolean containedincube = false; Node dimensionnameuri = hierarchy[hierarchymap .get("?DIMENSION_UNIQUE_NAME")]; for (Node[] aDimension : dimensions) { if (aDimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")] .toString().equals(dimensionnameuri.toString())) { containedincube = true; } } if (!containedincube) { // If notsliceddimension is not in cube, resulting cube will be // "empty". return null; } } // Find out which Dimensions to slice // Those that are not mentioned in the axes. List<Node[]> slicedDimensions = new ArrayList<Node[]>(); // NamedList<Dimension> realdimensions = metaData.cube.getDimensions(); // No first necessary since we go through realdimensions // Just take first. slicedDimensions.add(dimensions.get(0)); for (Node[] realdimension : dimensions) { if ((realdimension[dimensionmap.get("?DIMENSION_UNIQUE_NAME")] .toString() .equals(Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME))) { continue; } boolean notsliced = false; // For every dimension, check whether in not sliced. for (Node[] notslicedhierarchy : notslicedhierarchies) { Map<String, Integer> notslicedhierarchymap = Olap4ldLinkedDataUtil .getNodeResultFields(notslicedhierarchies.get(0)); if (notslicedhierarchy[notslicedhierarchymap .get("?DIMENSION_UNIQUE_NAME")].toString().equals( realdimension[dimensionmap .get("?DIMENSION_UNIQUE_NAME")].toString())) { notsliced = true; } } if (notsliced) { continue; } else { slicedDimensions.add(realdimension); } } // Test, slicedDimensions and // Slices part of SlicesRollups from (cube, SlicesRollups, Dices, // Projections) LogicalOlapOp slice = new SliceOp(dice, slicedDimensions); // RollupsPart of SlicesRollups from (cube, SlicesRollups, Dices, // Projections) // Rollup contains only hierarchies and levels that are actually higher // than Base level. LogicalOlapOp rollup = new RollupOp(slice, rollupshierarchies, rollupslevels); // Create indices for dimensions dimensionindicesList = new ArrayList<Integer>(); for (Node[] dimension : dimensions) { // Check what number Measure measure = null; int index = 0; for (int i = 0; i < getAxes().size(); i++) { // Assume that first position tells us everything position = getAxes().get(i).getPositions().get(0); for (Member member : position.getMembers()) { if (member.getMemberType() == Member.Type.MEASURE || member.getMemberType() == Member.Type.FORMULA) { if (measure != null) { // There should not be several measures per cell throw new UnsupportedOperationException( "There should not be used several measures per cell!"); } measure = (Measure) member; } else { // If dimension == member.dimension then store // Add index in correct ordering. Node memberdimension = Olap4ldLinkedDataUtil .convertMDXtoURI(member.getDimension() .getUniqueName()); if (memberdimension.equals(dimension[dimensionmap .get("?DIMENSION_UNIQUE_NAME")])) { dimensionindicesList.add(index); } // Count index index++; } } } } Olap4ldUtil._log.info("Finished creating " + rollup + " query plan."); return rollup; } /** * * The purpose of this method is to store the results of an analytical query * in a way so that getCell(...) can be evaluated. * * Basically, we have a pivot table that describes observations from a * multidimensional dataset. For each observation, we have a unique ordering * of dimensions, and a unique combination of members of these dimensions. * * @param olapQueryResult */ private void cacheDataFromOlapQuery(List<Node[]> olapQueryResult) { // Now, insert into hash map /* * We cannot directly fill the cellMap, since there is no direct * correspondence between the result and the "pivot" table: Per result, * we may have several measures given. We may have missing results * (sparseity). * * Every result gives the measures for a specific cell. * * For a sparse result set, it would be best to find out the ordinal * value for each returned value. How can we find the ordinal value? By * finding the coordinates. For each axis there is a coordinate. Can we * find the coordinate by looking at the column values? */ boolean first = true; String concatNr; for (Node[] node : olapQueryResult) { concatNr = ""; if (first) { // The first is not interesting, the variable names are not // expressive first = false; continue; } /* * Each value in a node corresponds to a dimension in the dimension * list, but this dimension list does not correspond to the * positions. * * We now create a hash map which consist of: 1) Numeric * representation of concatenation of all dimension members 2) An * array with the values for each measure, ordered. */ int slicesRollupsSize = 0; for (CellSetAxisMetaData metadata : metaData.getAxesMetaData()) { List<Hierarchy> hierarchies = metadata.getHierarchies(); for (Hierarchy hierarchy : hierarchies) { if (hierarchy.getName().equals( Olap4ldLinkedDataUtil.MEASURE_DIMENSION_NAME)) { // We are only interested in the dimension columns. ; } else { slicesRollupsSize++; } } } for (int i = 0; i < slicesRollupsSize; i++) { concatNr += node[i].toString(); } // No header String[] valueArray = new String[measureList.size()]; // No header for (int e = slicesRollupsSize; e < slicesRollupsSize + measureList.size(); e++) { String nodevalue = node[e].toString(); // For readability reasons, if numeric value, we round to to two // decimal. final String Digits = "(\\p{Digit}+)"; final String HexDigits = "(\\p{XDigit}+)"; // an exponent is 'e' or 'E' followed by an optionally // signed decimal integer. final String Exp = "[eE][+-]?" + Digits; final String fpRegex = ("[\\x00-\\x20]*" + // Optional leading // "whitespace" "[+-]?(" + // Optional sign character "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // A decimal floating-point string representing a finite // positive // number without a leading sign has at most five basic // pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as // input // in addition to strings of floating-point literals, // the // two sub-patterns below are simplifications of the // grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. // Digits ._opt Digits_opt ExponentPart_opt // FloatTypeSuffix_opt "(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.(" + Digits + ")(" + Exp + ")?)|" + // Hexadecimal strings "((" + // 0[xX] HexDigits ._opt BinaryExponent // FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent // FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")[pP][+-]?" + Digits + "))" + "[fFdD]?))" + "[\\x00-\\x20]*");// Optional // trailing // "whitespace" if (Pattern.matches(fpRegex, nodevalue)) { Double doubleValue = Double.valueOf(nodevalue); // Will not // throw // NumberFormatException try { DecimalFormat twoDForm = new DecimalFormat("#.##"); doubleValue = Double.valueOf(twoDForm .format(doubleValue)); } catch (NumberFormatException e1) { } nodevalue = doubleValue.toString(); } valueArray[e - slicesRollupsSize] = nodevalue; } newValueMap.put(concatNr.hashCode(), valueArray); // final Object value = getTypedValue(cell); // final String formattedValue = stringElement(cell, "FmtValue"); // final String formatString = stringElement(cell, "FormatString"); // Olap4jUtil.discard(formatString); // for (Element element : childElements(cell)) { // String tag = element.getLocalName(); // final Property property = metaData.propertiesByTag.get(tag); // if (property != null) { // propertyValues.put(property, element.getTextContent()); // } // } } } /** * Gets response from the XMLA request and populates cell set axes and cells * with it. * * @throws OlapException * on error */ @Deprecated void populate() throws OlapException { byte[] bytes = olap4jStatement.getBytes(); Document doc; try { doc = parse(bytes); } catch (IOException e) { throw getHelper().createException("error creating CellSet", e); } catch (SAXException e) { throw getHelper().createException("error creating CellSet", e); } // <SOAP-ENV:Envelope> // <SOAP-ENV:Header/> // <SOAP-ENV:Body> // <xmla:ExecuteResponse> // <xmla:return> // <root> // (see below) // </root> // <xmla:return> // </xmla:ExecuteResponse> // </SOAP-ENV:Body> // </SOAP-ENV:Envelope> final Element envelope = doc.getDocumentElement(); // if (DEBUG) { // System.out.println(LdOlap4jUtil.toString(doc, true)); // } assert envelope.getLocalName().equals("Envelope"); assert envelope.getNamespaceURI().equals(SOAP_NS); Element body = findChild(envelope, SOAP_NS, "Body"); Element fault = findChild(body, SOAP_NS, "Fault"); if (fault != null) { /* * <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Client.00HSBC01</faultcode> * <faultstring>XMLA connection datasource not found</faultstring> * <faultactor>Mondrian</faultactor> <detail> <XA:error * xmlns:XA="http://mondrian.sourceforge.net"> <code>00HSBC01</code> * <desc>The Mondrian XML: Mondrian Error:Internal error: no catalog * named 'LOCALDB'</desc> </XA:error> </detail> </SOAP-ENV:Fault> */ // TODO: log doc to logfile throw getHelper().createException( "XMLA provider gave exception: " + Olap4ldUtil.prettyPrint(fault)); } Element executeResponse = findChild(body, XMLA_NS, "ExecuteResponse"); Element returnElement = findChild(executeResponse, XMLA_NS, "return"); // <root> has children // <xsd:schema/> // <OlapInfo> // <CubeInfo> // <Cube> // <CubeName>FOO</CubeName> // </Cube> // </CubeInfo> // <AxesInfo> // <AxisInfo/> ... // </AxesInfo> // </OlapInfo> // <Axes> // <Axis> // <Tuples> // </Axis> // ... // </Axes> // <CellData> // <Cell/> // ... // </CellData> final Element root = findChild(returnElement, MDDATASET_NS, "root"); if (olap4jStatement instanceof Olap4ldPreparedStatement) { this.metaData = ((Olap4ldPreparedStatement) olap4jStatement).cellSetMetaData; } else { this.metaData = createMetaData(root); } // todo: use CellInfo element to determine mapping of cell properties // to XML tags /* * <CellInfo> <Value name="VALUE"/> <FmtValue name="FORMATTED_VALUE"/> * <FormatString name="FORMAT_STRING"/> </CellInfo> */ final Element axesNode = findChild(root, MDDATASET_NS, "Axes"); // First pass, gather up a list of member unique names to fetch // all at once. // // NOTE: This approach allows the driver to fetch a large number // of members in one round trip, which is much more efficient. // However, if the axis has a very large number of members, the map // may use too much memory. This is an unresolved issue. final MetadataReader metadataReader = metaData.cube.getMetadataReader(); final Map<String, Olap4ldMember> memberMap = new HashMap<String, Olap4ldMember>(); List<String> uniqueNames = new ArrayList<String>(); for (Element axisNode : findChildren(axesNode, MDDATASET_NS, "Axis")) { final Element tuplesNode = findChild(axisNode, MDDATASET_NS, "Tuples"); for (Element tupleNode : findChildren(tuplesNode, MDDATASET_NS, "Tuple")) { for (Element memberNode : findChildren(tupleNode, MDDATASET_NS, "Member")) { final String uname = stringElement(memberNode, "UName"); uniqueNames.add(uname); } } } // Fetch all members on all axes. Hopefully it can all be done in one // round trip, or they are in cache already. metadataReader.lookupMembersByUniqueName(uniqueNames, memberMap); // Second pass, populate the axis. final Map<Property, Object> propertyValues = new HashMap<Property, Object>(); for (Element axisNode : findChildren(axesNode, MDDATASET_NS, "Axis")) { final String axisName = axisNode.getAttribute("name"); final Axis axis = lookupAxis(axisName); final ArrayList<Position> positions = new ArrayList<Position>(); final Olap4ldCellSetAxis cellSetAxis = new Olap4ldCellSetAxis(this, axis, Collections.unmodifiableList(positions)); if (axis.isFilter()) { filterAxis = cellSetAxis; } else { axisList.add(cellSetAxis); } final Element tuplesNode = findChild(axisNode, MDDATASET_NS, "Tuples"); for (Element tupleNode : findChildren(tuplesNode, MDDATASET_NS, "Tuple")) { final List<Member> members = new ArrayList<Member>(); for (Element memberNode : findChildren(tupleNode, MDDATASET_NS, "Member")) { String hierarchyName = memberNode.getAttribute("Hierarchy"); final String uname = stringElement(memberNode, "UName"); Olap4ldMemberBase member = memberMap.get(uname); if (member == null) { final String caption = stringElement(memberNode, "Caption"); final int lnum = integerElement(memberNode, "LNum"); final Hierarchy hierarchy = lookupHierarchy( metaData.cube, hierarchyName); final Level level = hierarchy.getLevels().get(lnum); member = new XmlaOlap4jSurpriseMember(this, level, hierarchy, lnum, caption, uname); } propertyValues.clear(); for (Element childNode : childElements(memberNode)) { Olap4ldCellSetMemberProperty property = ((Olap4ldCellSetAxisMetaData) cellSetAxis .getAxisMetaData()).lookupProperty( hierarchyName, childNode.getLocalName()); if (property != null) { String value = childNode.getTextContent(); propertyValues.put(property, value); } } if (!propertyValues.isEmpty()) { member = new Olap4ldPositionMember(member, propertyValues); } members.add(member); } positions.add(new Olap4ldPosition(members, positions.size())); } } // If XMLA did not return a filter axis, it means that the WHERE clause // evaluated to zero tuples. (If the query had no WHERE clause, it // would have evaluated to a single tuple with zero positions.) if (filterAxis == null) { filterAxis = new Olap4ldCellSetAxis(this, Axis.FILTER, Collections.<Position> emptyList()); } // Now, we fill the cells final Element cellDataNode = findChild(root, MDDATASET_NS, "CellData"); for (Element cell : findChildren(cellDataNode, MDDATASET_NS, "Cell")) { propertyValues.clear(); final int cellOrdinal = Integer.valueOf(cell .getAttribute("CellOrdinal")); final Object value = getTypedValue(cell); final String formattedValue = stringElement(cell, "FmtValue"); final String formatString = stringElement(cell, "FormatString"); Olap4jUtil.discard(formatString); for (Element element : childElements(cell)) { String tag = element.getLocalName(); final Property property = metaData.propertiesByTag.get(tag); if (property != null) { propertyValues.put(property, element.getTextContent()); } } cellMap.put(cellOrdinal, new Olap4ldCell(this, cellOrdinal, value, formattedValue, propertyValues)); } } /** * Returns the value of a cell, cast to the appropriate Java object type * corresponding to the XML schema (XSD) type of the value. * * <p> * The value type must conform to XSD definitions of the XML element. See <a * href="http://books.xmlschemata.org/relaxng/relax-CHP-19.html">RELAX NG, * Chapter 19</a> for a full list of possible data types. * * <p> * This method does not currently support all types; must numeric types are * supported, but no dates are yet supported. Those not supported fall back * to Strings. * * @param cell * The cell of which we want the casted object. * @return The object with a correct value. * @throws OlapException * if any error is encountered while casting the cell value */ private Object getTypedValue(Element cell) throws OlapException { Element elm = findChild(cell, MDDATASET_NS, "Value"); if (elm == null) { // Cell is null. return null; } // The object type is contained in xsi:type attribute. String type = elm.getAttribute("xsi:type"); try { if (type.equals("xsd:int")) { return Olap4ldUtil.intElement(cell, "Value"); } else if (type.equals("xsd:integer")) { return Olap4ldUtil.integerElement(cell, "Value"); } else if (type.equals("xsd:double")) { return Olap4ldUtil.doubleElement(cell, "Value"); } else if (type.equals("xsd:float")) { return Olap4ldUtil.floatElement(cell, "Value"); } else if (type.equals("xsd:long")) { return Olap4ldUtil.longElement(cell, "Value"); } else if (type.equals("xsd:boolean")) { return Olap4ldUtil.booleanElement(cell, "Value"); } else { return Olap4ldUtil.stringElement(cell, "Value"); } } catch (Exception e) { throw getHelper().createException( "Error while casting a cell value to the correct java type for" + " its XSD type " + type, e); } } /** * Creates metadata for a cell set, given the DOM of the XMLA result. * * @param root * Root node of XMLA result * @return Metadata describing this cell set * @throws OlapException * on error * * */ @Deprecated private Olap4ldCellSetMetaData createMetaData(Element root) throws OlapException { // final Element olapInfo = findChild(root, MDDATASET_NS, "OlapInfo"); // final Element cubeInfo = findChild(olapInfo, MDDATASET_NS, // "CubeInfo"); // final Element cubeNode = findChild(cubeInfo, MDDATASET_NS, "Cube"); // final Element cubeNameNode = findChild(cubeNode, MDDATASET_NS, // "CubeName"); // final String cubeName = gatherText(cubeNameNode); // // // REVIEW: If there are multiple cubes with the same name, we should // // qualify by catalog and schema. Currently we just take the first. // XmlaOlap4jCube cube = lookupCube( // olap4jStatement.olap4jConnection.olap4jDatabaseMetaData, // cubeName); // if (cube == null) { // throw getHelper().createException( // "Internal error: cube '" + cubeName + "' not found"); // } // // REVIEW: We should not modify the connection. It is not safe, // because // // connection might be shared between multiple statements with // different // // cubes. Caller should call // // // // connection.setCatalog( // // // cellSet.getMetaData().getCube().getSchema().getCatalog().getName()) // // // // before doing metadata queries. // try { // this.olap4jStatement.olap4jConnection.setCatalog(cube.getSchema() // .getCatalog().getName()); // } catch (SQLException e) { // throw getHelper().createException( // "Internal error: setting catalog '" // + cube.getSchema().getCatalog().getName() // + "' caused error"); // } // final Element axesInfo = findChild(olapInfo, MDDATASET_NS, // "AxesInfo"); // final List<Element> axisInfos = findChildren(axesInfo, MDDATASET_NS, // "AxisInfo"); // final List<CellSetAxisMetaData> axisMetaDataList = new // ArrayList<CellSetAxisMetaData>(); // XmlaOlap4jCellSetAxisMetaData filterAxisMetaData = null; // for (Element axisInfo : axisInfos) { // final String axisName = axisInfo.getAttribute("name"); // Axis axis = lookupAxis(axisName); // final List<Element> hierarchyInfos = findChildren(axisInfo, // MDDATASET_NS, "HierarchyInfo"); // final List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>(); // /* // * <OlapInfo> <AxesInfo> <AxisInfo name="Axis0"> <HierarchyInfo // * name="Customers"> <UName // * name="[Customers].[MEMBER_UNIQUE_NAME]"/> <Caption // * name="[Customers].[MEMBER_CAPTION]"/> <LName // * name="[Customers].[LEVEL_UNIQUE_NAME]"/> <LNum // * name="[Customers].[LEVEL_NUMBER]"/> <DisplayInfo // * name="[Customers].[DISPLAY_INFO]"/> </HierarchyInfo> </AxisInfo> // * ... </AxesInfo> <CellInfo> <Value name="VALUE"/> <FmtValue // * name="FORMATTED_VALUE"/> <FormatString name="FORMAT_STRING"/> // * </CellInfo> </OlapInfo> // */ // final List<XmlaOlap4jCellSetMemberProperty> propertyList = new // ArrayList<XmlaOlap4jCellSetMemberProperty>(); // for (Element hierarchyInfo : hierarchyInfos) { // final String hierarchyName = hierarchyInfo.getAttribute("name"); // Hierarchy hierarchy = lookupHierarchy(cube, hierarchyName); // hierarchyList.add(hierarchy); // for (Element childNode : childElements(hierarchyInfo)) { // String tag = childNode.getLocalName(); // if (standardProperties.contains(tag)) { // continue; // } // final String propertyUniqueName = childNode // .getAttribute("name"); // final XmlaOlap4jCellSetMemberProperty property = new // XmlaOlap4jCellSetMemberProperty( // propertyUniqueName, hierarchy, tag); // propertyList.add(property); // } // } // final XmlaOlap4jCellSetAxisMetaData axisMetaData = new // XmlaOlap4jCellSetAxisMetaData( // olap4jStatement.olap4jConnection, axis, hierarchyList, // propertyList); // if (axis.isFilter()) { // filterAxisMetaData = axisMetaData; // } else { // axisMetaDataList.add(axisMetaData); // } // } // if (filterAxisMetaData == null) { // filterAxisMetaData = new XmlaOlap4jCellSetAxisMetaData( // olap4jStatement.olap4jConnection, Axis.FILTER, // Collections.<Hierarchy> emptyList(), // Collections.<XmlaOlap4jCellSetMemberProperty> emptyList()); // } // // cellInfo // final Element cellInfo = findChild(olapInfo, MDDATASET_NS, // "CellInfo"); // List<XmlaOlap4jCellProperty> cellProperties = new // ArrayList<XmlaOlap4jCellProperty>(); // for (Element element : childElements(cellInfo)) { // cellProperties.add(new XmlaOlap4jCellProperty(element // .getLocalName(), element.getAttribute("name"))); // } // return new XmlaOlap4jCellSetMetaData(olap4jStatement, cube, // filterAxisMetaData, axisMetaDataList, cellProperties); return null; } /** * Looks up a cube among all of the schemas in all of the catalogs in this * connection. * * <p> * If there are several with the same name, returns the first. * * @param databaseMetaData * Database metadata * @param cubeName * Cube name * @return Cube, or null if not found * @throws OlapException * on error */ @SuppressWarnings("unused") @Deprecated private Olap4ldCube lookupCube(Olap4ldDatabaseMetaData databaseMetaData, String cubeName) throws OlapException { for (Catalog catalog : databaseMetaData.olap4jConnection .getOlapCatalogs()) { for (Schema schema : catalog.getSchemas()) { for (Cube cube : schema.getCubes()) { if (cubeName.equals(cube.getName())) { return (Olap4ldCube) cube; } if (cubeName.equals("[" + cube.getName() + "]")) { return (Olap4ldCube) cube; } } } } return null; } /** * Looks up a hierarchy in a cube with a given name or, failing that, a * given unique name. Throws if not found. * * @param cube * Cube * @param hierarchyName * Name (or unique name) of hierarchy. * @return Hierarchy * @throws OlapException * on error */ @Deprecated private Hierarchy lookupHierarchy(Olap4ldCube cube, String hierarchyName) throws OlapException { Hierarchy hierarchy = cube.getHierarchies().get(hierarchyName); if (hierarchy == null) { for (Hierarchy hierarchy1 : cube.getHierarchies()) { if (hierarchy1.getUniqueName().equals(hierarchyName)) { hierarchy = hierarchy1; break; } } if (hierarchy == null) { throw getHelper().createException( "Internal error: hierarchy '" + hierarchyName + "' not found in cube '" + cube.getName() + "'"); } } return hierarchy; } /** * Looks up an Axis with a given name. * * @param axisName * Name of axis * @return Axis */ @Deprecated private Axis lookupAxis(String axisName) { if (axisName.startsWith("Axis")) { final Integer ordinal = Integer.valueOf(axisName.substring("Axis" .length())); return Axis.Factory.forOrdinal(ordinal); } else { return Axis.FILTER; } } public CellSetMetaData getMetaData() { return metaData; } public Cell getCell(List<Integer> coordinates) { if (!areCellsPopulated) { try { populateCells(); } catch (OlapException e) { // TODO It would be nicer to have proper OlapException given. e.printStackTrace(); } } // We collect the members in an ArrayList List<String> concatNrs = new ArrayList<String>(); Measure measure = null; for (int i = 0; i < coordinates.size(); i++) { Position position = getAxes().get(i).getPositions() .get(coordinates.get(i)); for (Member member : position.getMembers()) { if (member.getMemberType() == Member.Type.MEASURE || member.getMemberType() == Member.Type.FORMULA) { if (measure != null) { // There should not be several measures per cell throw new UnsupportedOperationException( "There should not be used several measures per cell!"); } measure = (Measure) member; } else { concatNrs.add(Olap4ldLinkedDataUtil.convertMDXtoURI( member.getUniqueName()).toString()); } } } /* * We need to know the index of each dimension */ String concatNr = ""; for (int i = 0; i < dimensionindicesList.size(); i++) { if (dimensionindicesList.get(i) != null) { concatNr += concatNrs.get(dimensionindicesList.get(i)); } } /* * We need to know the number of the measure */ int index = 0; // No header for (int i = 0; i < measureList.size() && measure != null; i++) { // Problem in case the same measure is used by several cubes, then // we always use the first. if (measureList.get(i).getUniqueName() .equals(measure.getUniqueName())) { // No header index = i; break; } } /* * Now, we have the members and the measure index */ String[] hashedCells = newValueMap.get(concatNr.hashCode()); // We should be using property values since XMLA requires them: // Template: Now, we fill the cells // final Element cellDataNode = findChild(root, MDDATASET_NS, // "CellData"); // for (Element cell : findChildren(cellDataNode, MDDATASET_NS, "Cell")) // { // propertyValues.clear(); // final int cellOrdinal = Integer.valueOf(cell // .getAttribute("CellOrdinal")); // final Object value = getTypedValue(cell); // final String formattedValue = stringElement(cell, "FmtValue"); // final String formatString = stringElement(cell, "FormatString"); // Olap4jUtil.discard(formatString); // for (Element element : childElements(cell)) { // String tag = element.getLocalName(); // final Property property = metaData.propertiesByTag.get(tag); // if (property != null) { // propertyValues.put(property, element.getTextContent()); // } // } // cellMap.put(cellOrdinal, new Olap4ldCell(this, cellOrdinal, value, // formattedValue, propertyValues)); // } // We need to add properties Map<Property, Object> propertyValues = new HashMap<Property, Object>(); if (hashedCells != null && hashedCells[index] != null) { propertyValues.put(Property.StandardCellProperty.FORMATTED_VALUE, hashedCells[index]); propertyValues.put(Property.StandardCellProperty.VALUE, hashedCells[index]); propertyValues.put(Property.StandardCellProperty.FORMAT_STRING, null); return new Olap4ldCell(this, coordinatesToOrdinal(coordinates), hashedCells[index], hashedCells[index], propertyValues); } else { return new Olap4ldCell(this, coordinatesToOrdinal(coordinates), "", "", Collections.<Property, Object> emptyMap()); } // // // Filter axis we need to consider, also // // Slicer dimensions, for which data is retrieved for a single member // // If several positions are given, we need to take them as an OR // List<Member> filtermembers = new ArrayList<Member>(); // // for (Position position : filterAxis.getPositions()) { // /* // * TODO: Momentarily, this only makes sense if each position // * contains exaclty one measure. // */ // for (Member member : position.getMembers()) { // if (member.getMemberType() == Member.Type.MEASURE) { // if (measure != null) { // // There should not be several measures per cell // throw new UnsupportedOperationException( // "There should not be used several measures per cell!"); // } // measure = (Measure) member; // } else { // // Add to filtermembers (that should be "or"ed // filtermembers.add(member); // } // } // } // // // If no measure, then find default // if (measure == null) { // // Default is simply the first we find. // measure = cube.getMeasures().get(0); // } // // /* // * Dimensions that are not explicitly assigned to an axis are assumed // to // * be slicer dimensions and filter with their default members. // * // * I simply ask for the hierarchies in each axis. For all dimensions // of // * the cube, I find those which are not represented, and take the // * DEFAULT_HIERARCHY, and its DEFAULT_MEMBER. // * // * The problem is, that we currently do not have default members. If // no // * default member is set, we do not consider this dimension, at the // * moment. That means, we take all (do not distinguish), which can // bring // * problems in SPARQL. // * // * TODO: implement this above. // */ // // // Caching // Integer cachedResults = this.getResults(this, pos, cube, members, // filtermembers, measure); // // if (cachedResults == null || cachedResults == 0) { // return new LdOlap4jCell(this, pos, null, "", // Collections.<Property, Object> emptyMap()); // } else { // // Now, create result: We need: a value // Cell cell = this.olap4jStatement.olap4jConnection.myLinkedData // .getOlapCellResult(this, pos, cube, members, filtermembers, // measure); // if (cell == null) { // if (pos < 0 || pos >= maxOrdinal()) { // throw new IndexOutOfBoundsException(); // } else { // // Cell is within bounds, but is not held in the cache // // because // // it has no value. Manufacture a cell with an empty value. // return new LdOlap4jCell(this, pos, null, "", // Collections.<Property, Object> emptyMap()); // } // } // return cell; // } } public Cell getCell(int ordinal) { return getCell(ordinalToCoordinates(ordinal)); } public Cell getCell(Position... positions) { if (positions.length != getAxes().size()) { throw new IllegalArgumentException( "cell coordinates should have dimension " + getAxes().size()); } List<Integer> coords = new ArrayList<Integer>(positions.length); for (Position position : positions) { coords.add(position.getOrdinal()); } return getCell(coords); } // private Integer getResults(LdOlap4jCellSet xmlaOlap4jCellSet, int pos, // LdOlap4jCube cube, List<Member> members, // List<Member> filtermembers, Measure measure) { // // // Take all member unique names (which are the concept uris) and // // concatenate, translate into integer // // and return // String memberConcat = ""; // int memberConcatHashCode = 0; // for (Member member : members) { // // String conceptURIs = LdOlap4jUtil.decodeUriForMdx(member // .getUniqueName()); // memberConcat += conceptURIs; // } // // Measure also concat (!) // memberConcat += measure.getUniqueName(); // memberConcatHashCode = memberConcat.hashCode(); // // Do not take filter members into account, since they will be used with // // OR // // for (Member member : filtermembers) { // // memberConcat += member.getCaption(); // // } // // if (this.hierarchyCellMap == null // || !this.hierarchyCellMap.containsKey(memberConcatHashCode)) { // // /* // * I now have all the hierarchies of involved queries. For quicker // * queries, I will have a large query asking for members of each // * hierarchy and the number of results, and store it in a map of // * Integer (concatenated members to Long) and Integer (Number of // * occurrences). Then, when resolving, I can simply return null for // * each time, there is no occurrence. // */ // // this.hierarchyCellMap = olap4jStatement.olap4jConnection.myLinkedData // .getOlapResultEstimation(this, pos, cube, members, // filtermembers, measure); // } // // if (this.hierarchyCellMap.containsKey(memberConcatHashCode)) { // return this.hierarchyCellMap.get(memberConcatHashCode); // } else { // // This means, we return null, which will be translated into an // // empty value anyway. // return null; // } // } /** * Returns a string describing the maximum coordinates of this cell set; for * example "2, 3" for a cell set with 2 columns and 3 rows. * * @return description of cell set bounds */ private String getBoundsAsString() { StringBuilder buf = new StringBuilder(); int k = 0; for (CellSetAxis axis : getAxes()) { if (k++ > 0) { buf.append(", "); } buf.append(axis.getPositionCount()); } return buf.toString(); } public List<CellSetAxis> getAxes() { return immutableAxisList; } public CellSetAxis getFilterAxis() { return filterAxis; } /** * Returns the ordinal of the last cell in this cell set. This is the * product of the cardinalities of all axes. * * @return ordinal of last cell in cell set */ @SuppressWarnings("unused") private int maxOrdinal() { int modulo = 1; for (CellSetAxis axis : axisList) { modulo *= axis.getPositionCount(); } return modulo; } public List<Integer> ordinalToCoordinates(int ordinal) { List<CellSetAxis> axes = getAxes(); final List<Integer> list = new ArrayList<Integer>(axes.size()); int modulo = 1; for (CellSetAxis axis : axes) { int prevModulo = modulo; modulo *= axis.getPositionCount(); list.add((ordinal % modulo) / prevModulo); } if (ordinal < 0 || ordinal >= modulo) { throw new IndexOutOfBoundsException("Cell ordinal " + ordinal + ") lies outside CellSet bounds (" + getBoundsAsString() + ")"); } return list; } /** * From a list of coordinates that relate to the axes (getAxes), */ public int coordinatesToOrdinal(List<Integer> coordinates) { List<CellSetAxis> axes = getAxes(); if (coordinates.size() != axes.size()) { throw new IllegalArgumentException( "Coordinates have different dimension " + coordinates.size() + " than axes " + axes.size()); } int modulo = 1; int ordinal = 0; int k = 0; for (CellSetAxis axis : axes) { final Integer coordinate = coordinates.get(k++); if (coordinate < 0 || coordinate >= axis.getPositionCount()) { throw new IndexOutOfBoundsException("Coordinate " + coordinate + " of axis " + k + " is out of range (" + getBoundsAsString() + ")"); } // Add to ordinal the actual coordinate of the axis times the size // of the last axis ordinal += coordinate * modulo; // modulo is the number of positions in this axis modulo *= axis.getPositionCount(); } return ordinal; } public boolean next() throws SQLException { throw new UnsupportedOperationException(); } public void close() throws SQLException { this.closed = true; } public boolean wasNull() throws SQLException { throw new UnsupportedOperationException(); } public String getString(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public boolean getBoolean(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public byte getByte(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public short getShort(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public int getInt(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public long getLong(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public float getFloat(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public double getDouble(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { throw new UnsupportedOperationException(); } public byte[] getBytes(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Date getDate(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Time getTime(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Timestamp getTimestamp(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getAsciiStream(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getUnicodeStream(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getBinaryStream(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public String getString(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public boolean getBoolean(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public byte getByte(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public short getShort(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public int getInt(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public long getLong(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public float getFloat(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public double getDouble(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { throw new UnsupportedOperationException(); } public byte[] getBytes(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Date getDate(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Time getTime(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Timestamp getTimestamp(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getAsciiStream(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getUnicodeStream(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public InputStream getBinaryStream(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public SQLWarning getWarnings() throws SQLException { throw new UnsupportedOperationException(); } public void clearWarnings() throws SQLException { throw new UnsupportedOperationException(); } public String getCursorName() throws SQLException { throw new UnsupportedOperationException(); } public Object getObject(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Object getObject(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public int findColumn(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Reader getCharacterStream(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Reader getCharacterStream(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public BigDecimal getBigDecimal(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public BigDecimal getBigDecimal(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public boolean isBeforeFirst() throws SQLException { throw new UnsupportedOperationException(); } public boolean isAfterLast() throws SQLException { throw new UnsupportedOperationException(); } public boolean isFirst() throws SQLException { throw new UnsupportedOperationException(); } public boolean isLast() throws SQLException { throw new UnsupportedOperationException(); } public void beforeFirst() throws SQLException { throw new UnsupportedOperationException(); } public void afterLast() throws SQLException { throw new UnsupportedOperationException(); } public boolean first() throws SQLException { throw new UnsupportedOperationException(); } public boolean last() throws SQLException { throw new UnsupportedOperationException(); } public int getRow() throws SQLException { throw new UnsupportedOperationException(); } public boolean absolute(int row) throws SQLException { throw new UnsupportedOperationException(); } public boolean relative(int rows) throws SQLException { throw new UnsupportedOperationException(); } public boolean previous() throws SQLException { throw new UnsupportedOperationException(); } public void setFetchDirection(int direction) throws SQLException { throw new UnsupportedOperationException(); } public int getFetchDirection() throws SQLException { throw new UnsupportedOperationException(); } public void setFetchSize(int rows) throws SQLException { throw new UnsupportedOperationException(); } public int getFetchSize() throws SQLException { throw new UnsupportedOperationException(); } public int getType() throws SQLException { throw new UnsupportedOperationException(); } public int getConcurrency() throws SQLException { throw new UnsupportedOperationException(); } public boolean rowUpdated() throws SQLException { throw new UnsupportedOperationException(); } public boolean rowInserted() throws SQLException { throw new UnsupportedOperationException(); } public boolean rowDeleted() throws SQLException { throw new UnsupportedOperationException(); } public void updateNull(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public void updateBoolean(int columnIndex, boolean x) throws SQLException { throw new UnsupportedOperationException(); } public void updateByte(int columnIndex, byte x) throws SQLException { throw new UnsupportedOperationException(); } public void updateShort(int columnIndex, short x) throws SQLException { throw new UnsupportedOperationException(); } public void updateInt(int columnIndex, int x) throws SQLException { throw new UnsupportedOperationException(); } public void updateLong(int columnIndex, long x) throws SQLException { throw new UnsupportedOperationException(); } public void updateFloat(int columnIndex, float x) throws SQLException { throw new UnsupportedOperationException(); } public void updateDouble(int columnIndex, double x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { throw new UnsupportedOperationException(); } public void updateString(int columnIndex, String x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBytes(int columnIndex, byte x[]) throws SQLException { throw new UnsupportedOperationException(); } public void updateDate(int columnIndex, Date x) throws SQLException { throw new UnsupportedOperationException(); } public void updateTime(int columnIndex, Time x) throws SQLException { throw new UnsupportedOperationException(); } public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { throw new UnsupportedOperationException(); } public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { throw new UnsupportedOperationException(); } public void updateObject(int columnIndex, Object x) throws SQLException { throw new UnsupportedOperationException(); } public void updateNull(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public void updateBoolean(String columnLabel, boolean x) throws SQLException { throw new UnsupportedOperationException(); } public void updateByte(String columnLabel, byte x) throws SQLException { throw new UnsupportedOperationException(); } public void updateShort(String columnLabel, short x) throws SQLException { throw new UnsupportedOperationException(); } public void updateInt(String columnLabel, int x) throws SQLException { throw new UnsupportedOperationException(); } public void updateLong(String columnLabel, long x) throws SQLException { throw new UnsupportedOperationException(); } public void updateFloat(String columnLabel, float x) throws SQLException { throw new UnsupportedOperationException(); } public void updateDouble(String columnLabel, double x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { throw new UnsupportedOperationException(); } public void updateString(String columnLabel, String x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBytes(String columnLabel, byte x[]) throws SQLException { throw new UnsupportedOperationException(); } public void updateDate(String columnLabel, Date x) throws SQLException { throw new UnsupportedOperationException(); } public void updateTime(String columnLabel, Time x) throws SQLException { throw new UnsupportedOperationException(); } public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { throw new UnsupportedOperationException(); } public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { throw new UnsupportedOperationException(); } public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { throw new UnsupportedOperationException(); } public void updateObject(String columnLabel, Object x) throws SQLException { throw new UnsupportedOperationException(); } public void insertRow() throws SQLException { throw new UnsupportedOperationException(); } public void updateRow() throws SQLException { throw new UnsupportedOperationException(); } public void deleteRow() throws SQLException { throw new UnsupportedOperationException(); } public void refreshRow() throws SQLException { throw new UnsupportedOperationException(); } public void cancelRowUpdates() throws SQLException { throw new UnsupportedOperationException(); } public void moveToInsertRow() throws SQLException { throw new UnsupportedOperationException(); } public void moveToCurrentRow() throws SQLException { throw new UnsupportedOperationException(); } public OlapStatement getStatement() { return olap4jStatement; } public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException { throw new UnsupportedOperationException(); } public Ref getRef(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Blob getBlob(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Clob getClob(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Array getArray(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException { throw new UnsupportedOperationException(); } public Ref getRef(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Blob getBlob(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Clob getClob(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Array getArray(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public Date getDate(int columnIndex, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public Date getDate(String columnLabel, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public Time getTime(int columnIndex, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public Time getTime(String columnLabel, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { throw new UnsupportedOperationException(); } public URL getURL(int columnIndex) throws SQLException { throw new UnsupportedOperationException(); } public URL getURL(String columnLabel) throws SQLException { throw new UnsupportedOperationException(); } public void updateRef(int columnIndex, Ref x) throws SQLException { throw new UnsupportedOperationException(); } public void updateRef(String columnLabel, Ref x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBlob(int columnIndex, Blob x) throws SQLException { throw new UnsupportedOperationException(); } public void updateBlob(String columnLabel, Blob x) throws SQLException { throw new UnsupportedOperationException(); } public void updateClob(int columnIndex, Clob x) throws SQLException { throw new UnsupportedOperationException(); } public void updateClob(String columnLabel, Clob x) throws SQLException { throw new UnsupportedOperationException(); } public void updateArray(int columnIndex, Array x) throws SQLException { throw new UnsupportedOperationException(); } public void updateArray(String columnLabel, Array x) throws SQLException { throw new UnsupportedOperationException(); } // implement Wrapper public <T> T unwrap(Class<T> iface) throws SQLException { throw new UnsupportedOperationException(); } public boolean isWrapperFor(Class<?> iface) throws SQLException { throw new UnsupportedOperationException(); } /** * Implementation of {@link Member} for a member which is not present in the * cube (probably because the member is a calculated member defined in the * query). */ private static class XmlaOlap4jSurpriseMember implements Olap4ldMemberBase { private final Olap4ldCellSet cellSet; private final Level level; private final Hierarchy hierarchy; private final int lnum; private final String caption; private final String uname; /** * Creates an XmlaOlap4jSurpriseMember. * * @param cellSet * Cell set * @param level * Level * @param hierarchy * Hierarchy * @param lnum * Level number * @param caption * Caption * @param uname * Member unique name */ XmlaOlap4jSurpriseMember(Olap4ldCellSet cellSet, Level level, Hierarchy hierarchy, int lnum, String caption, String uname) { this.cellSet = cellSet; this.level = level; this.hierarchy = hierarchy; this.lnum = lnum; this.caption = caption; this.uname = uname; } public final Olap4ldCube getCube() { return cellSet.metaData.cube; } public final Olap4ldConnection getConnection() { return getCatalog().olap4jDatabaseMetaData.olap4jConnection; } public final Olap4ldCatalog getCatalog() { return getCube().olap4jSchema.olap4jCatalog; } public Map<Property, Object> getPropertyValueMap() { return Collections.emptyMap(); } public NamedList<? extends Member> getChildMembers() { return Olap4jUtil.emptyNamedList(); } public int getChildMemberCount() { return 0; } public Member getParentMember() { return null; } public Level getLevel() { return level; } public Hierarchy getHierarchy() { return hierarchy; } public Dimension getDimension() { return hierarchy.getDimension(); } public Type getMemberType() { return Type.UNKNOWN; } public boolean isAll() { return false; // FIXME } public boolean isChildOrEqualTo(Member member) { return false; // FIXME } public boolean isCalculated() { return false; // FIXME } public int getSolveOrder() { return 0; // FIXME } public ParseTreeNode getExpression() { return null; } public List<Member> getAncestorMembers() { return Collections.emptyList(); // FIXME } public boolean isCalculatedInQuery() { return true; // probably } public Object getPropertyValue(Property property) { return null; } public String getPropertyFormattedValue(Property property) { return null; } public void setProperty(Property property, Object value) { throw new UnsupportedOperationException(); } public NamedList<Property> getProperties() { return Olap4jUtil.emptyNamedList(); } public int getOrdinal() { return -1; // FIXME } public boolean isHidden() { return false; } public int getDepth() { return lnum; } public Member getDataMember() { return null; } public String getName() { return caption; } public String getUniqueName() { return uname; } public String getCaption() { return caption; } public String getDescription() { return null; } public boolean isVisible() { return true; } } } // End XmlaOlap4jCellSet.java