Package biz.smart.mdx

Source Code of biz.smart.mdx.MdxQuery

/*
Copyright (c) 2012 Marcin Stepien, http://smart.biz.pl/

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package biz.smart.mdx;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import biz.smart.mdx.olap.OLAPNamedStructure;
import biz.smart.mdx.olap.data.BracketName;
import biz.smart.mdx.olap.data.MdxSet;
import biz.smart.mdx.olap.data.MdxValueExpression;
import biz.smart.mdx.olap.data.Member;
import biz.smart.mdx.olap.function.Crossjoin;
import biz.smart.mdx.olap.function.Crossjoin.CrossjoinResult;
import biz.smart.mdx.olap.model.Cube;
import biz.smart.mdx.olap.model.Hierarchy;
import biz.smart.mdx.olap.model.Measure;
import biz.smart.mdx.validation.EmptyRowsColumnsAxisException;
import biz.smart.mdx.validation.HierarchyInManyAxisException;
import biz.smart.mdx.valuegenerators.MdxValueGenerator;

/**
* Class contains MDX query members for specific query sections
*
*/
public class MdxQuery{
  private LinkedHashMap<String,String> querySectionWithLines = new LinkedHashMap<String,String>();
  private List<String> querySectionSelectColsLines = new ArrayList<String>();
  private List<String> querySectionSelectRowsLines = new ArrayList<String>();
  private String      querySectionFromLine = "";
  private List<String> querySectionWhereLines    = new ArrayList<String>();
 
  private List<Hierarchy> axisColsHierarchies = new ArrayList<Hierarchy>();
  private List<Hierarchy> axisRowsHierarchies = new ArrayList<Hierarchy>();
  private List<Hierarchy> axisSlicerHierarchies = new ArrayList<Hierarchy>();
 
  //section WITH name, value generator
  private LinkedHashMap<BracketName, MdxValueGenerator> postGenerationValues
    = new LinkedHashMap<BracketName, MdxValueGenerator>();
 
  private Measure measure;
  private Cube cube;

  /**
   * @param cube
   * @param measure - default measure
   */
  public MdxQuery(Cube cube, Measure measure){
    assert cube!=null : "cubeName cannot be null";
    assert measure!=null : "measure cannot be null";
   
    this.cube = cube;
    this.measure = measure;
    this.querySectionFromLine=this.cube.toString();
  }
 
  public MdxQuery(MdxQuery mdxQuery) {
    this(mdxQuery.getCube(), mdxQuery.getMeasure());
    querySectionWithLines = new LinkedHashMap<String, String>(mdxQuery.getQuerySectionWithLines());
    querySectionSelectColsLines = new ArrayList<String>(mdxQuery.getQuerySectionSelectColsLines());
    querySectionSelectRowsLines = new ArrayList<String>(mdxQuery.getQuerySectionSelectRowsLines());
    querySectionWhereLines = new ArrayList<String>(mdxQuery.getQuerySectionWhereLines());
    axisColsHierarchies =   new ArrayList<Hierarchy>(mdxQuery.getAxisColsHierarchies());
    axisRowsHierarchies =   new ArrayList<Hierarchy>(mdxQuery.getAxisRowsHierarchies());
    axisSlicerHierarchies = new ArrayList<Hierarchy>(mdxQuery.getAxisSlicerHierarchies());
    postGenerationValues =  new LinkedHashMap<BracketName, MdxValueGenerator>(mdxQuery.getPostGenerationValues());
  }

  private LinkedHashMap<String, String> getQuerySectionWithLines() {
    return querySectionWithLines;
  }
  private List<String> getQuerySectionSelectColsLines() {
    return querySectionSelectColsLines;
  }
  private List<String> getQuerySectionSelectRowsLines() {
    return querySectionSelectRowsLines;
  }
  private List<String> getQuerySectionWhereLines() {
    return querySectionWhereLines;
  }
  private List<Hierarchy> getAxisRowsHierarchies(){
    return axisRowsHierarchies;
  }
  private List<Hierarchy> getAxisColsHierarchies(){
    return axisColsHierarchies;
  }
  private List<Hierarchy> getAxisSlicerHierarchies(){
    return axisSlicerHierarchies;
  }
  private LinkedHashMap<BracketName, MdxValueGenerator> getPostGenerationValues() {
    return postGenerationValues;
  }
  private Object getQuerySectionFromLine() {
    return querySectionFromLine;
  }

  /**
   * rows within WITH mdx section
   * @param variableName - e.g. [variable] will be renamed if variableName already exists in MdxQuery
   * @param sectionWithRow - variableName mdx definition
   * @return computed variableName e.g. [alteredVariable]
   */
  private BracketName addSectionWithRow(BracketName variableName, String sectionWithRow) {
    BracketName entryVariableName = variableName;

    if(querySectionWithLines.containsKey(entryVariableName.toString())){
//      throw new IllegalArgumentException("Mdx query section WITH already defines: "+variableName);
      variableName = new BracketName(computeAlterVariableName(variableName, querySectionWithLines.keySet()));
//      sectionWithRow = sectionWithRow.replaceFirst(entryVariableName.toString(), variableName.toString());
      sectionWithRow = sectionWithRow.replaceFirst(Pattern.quote(entryVariableName.toString()), Matcher.quoteReplacement(variableName.toString()));
    }
    querySectionWithLines.put(variableName.toString(), sectionWithRow);
   
    return variableName;
  }
 
  /**
   * @param memberName
   * @param element
   * @return computed variableName e.g. [alteredVariable].[value]
   */
  private BracketName addSectionWithMember(BracketName memberName, OLAPNamedStructure element){
    if(element.getValueGenerator()!=null){
      postGenerationValues.put(memberName, element.getValueGenerator());
      return memberName;
    }
   
    StringBuffer sb = new StringBuffer();
    sb.append("MEMBER ");
    sb.append(memberName);
    sb.append(" AS ");
    sb.append(element);
    return addSectionWithRow(memberName, sb.toString());
  }
 
//  private BracketName addSectionWithValueExpression(BracketName expressionName, MdxValueExpression expression){
//    StringBuffer sb = new StringBuffer();
//    sb.append("MEMBER ");
//    sb.append(expressionName);
//    sb.append(" AS ");
//    sb.append(expression);
//    return addSectionWithRow(expressionName, sb.toString());
//  }
//  /**
//   * @param member - e.g. [variable] will be renamed if variableName already exists in MdxQuery
//   * @param mdxValue - eg. return type of function OR level value
//   * @return computed memberName e.g. [alteredVariable]
//   */
//  public BracketName addSectionWithMember(Member member, MdxValueExpression mdxValue){
//    StringBuffer sb = new StringBuffer();
//    sb.append("MEMBER ");
//    sb.append(member);
//    sb.append(" AS ");
//    sb.append(mdxValue);
//    return addSectionWithRow(member.getDefinition(), sb.toString());
//  }
  /**
   * @param setName - e.g. [variable] will be renamed if variableName already exists in MdxQuery
   * @param set
   * @return computed variableName e.g. [alteredVariable]
   */
  private BracketName addSectionWithSet(BracketName setName, MdxSet<?> set){
    StringBuffer sb = new StringBuffer();
    sb.append("SET ");
    sb.append(setName);
    sb.append(" AS ");
    sb.append(set);
    return addSectionWithRow(setName, sb.toString());
  }
 
  /**
   * @param set - Set Name will be used if exists
   * @param orientation
   * @return computed Set Name e.g. [alteredSetName]
   */
  public BracketName addSelection(MdxSet set, Axis orientation) {
    BracketName setName = set.getName();
   
    //TODO: check elements names recursively?
    addSectionWithSetsMembers(set);
   
    if(setName==null)
      addSectionSelectRow(set.toString(), orientation);
    else if(setName!=null){
      setName = addSectionWithSet(setName, set);
      addSectionSelectRow(setName.toString(), orientation);
    }
   
    addHierarchies(set.getHierarchies(), orientation);
   
    return setName;
 
 
  /**
   * @param member - Member Name will be used if exists
   * @param orientation
   * @return computed Member Name e.g. [alteredMemberName]
   */
  public BracketName addSelection(Member<?> member, Axis orientation) {
    BracketName name = member.getName();
   
    if(name==null)
      addSectionSelectRow(member.toString(), orientation);
    else if(name!=null){
      name = addSectionWithMember(name, member);
      addSectionSelectRow(name.toString(), orientation);
    }
    addHierarchy(member.getHierarchy(), orientation);
   
    return name;
 
   
  public BracketName addSelection(MdxValueExpression expression, Axis orientation) {
    BracketName name = expression.getName();
   
    if(name==null)
      addSectionSelectRow(expression.toString(), orientation);
    else if(name!=null){
      name = addSectionWithMember(name, expression);
      addSectionSelectRow(name.toString(), orientation);
    }
    addHierarchies(expression.getHierarchies(), orientation);
   
    return name;
 
 
//  public BracketName addSelection(MdxValueExpression valueExpression,
//      Axis orientation) {
//    BracketName expressionName = valueExpression.getName();
//   
//    if(expressionName==null)
//      addSectionSelectRow(valueExpression.toString(), orientation);
//    else if(expressionName!=null){
//      expressionName = addSectionWithValueExpression(expressionName, valueExpression);
//      addSectionSelectRow(expressionName.toString(), orientation);
//    }
//   
//    return expressionName;
//  }
 
 
  public CrossjoinResult addSelection(Crossjoin.CrossjoinResult result,
      Axis orientation) {
    MdxSet<?> set1 = result.getSet1();
    MdxSet<?> set2 = result.getSet2();
   
    addSectionWithSetsMembers(set1);
    addSectionWithSetsMembers(set2);
//    if(set1.getName()!=null){
//      set1.setName(addSectionWithSet(set1.getName(), set1));
//    }
//    if(set2.getName()!=null){
//      set2.setName(addSectionWithSet(set2.getName(), set2));
//    }
    addSectionWithRows(result);
   
//    addSectionSelectRow(result.toString(), orientation);
    crossjoinSectionSelectRow(result.toString(), orientation);
   
    addHierarchies(result.getHierarchies(),orientation);
   
    result = (new Crossjoin(set1, set2)).new CrossjoinResult();
    return result;
  }
  /**
   * Adds all set names of CrossjoinResult
   * @param result
   */
  private void addSectionWithRows(Crossjoin.CrossjoinResult result){
   
    MdxSet<?> set1 = result.getSet1();
    if(set1!=null){
      if(set1.getCrossjoinResult()!=null)
        addSectionWithRows(set1.getCrossjoinResult());
      if(set1.getName()!=null){
        set1.setName(addSectionWithSet(set1.getName(), set1));
      }
    }
   
    MdxSet<?> set2 = result.getSet2();
    if(set2!=null){
      if(set2.getCrossjoinResult()!=null)
        addSectionWithRows(set2.getCrossjoinResult());
      if(set2.getName()!=null){
        set2.setName(addSectionWithSet(set2.getName(), set2));
      }
    }
  }
 
  private void addSectionWithSetsMembers(MdxSet<?> set){ 
    if(set!=null)
      for(OLAPNamedStructure element : set.getElements()){
        BracketName elementName=element.getName();
        if(elementName!=null){
          if(element instanceof MdxSet<?>){
            this.addSectionWithSet(elementName, (MdxSet<?>) element);
          }else if(element instanceof Member){
            this.addSectionWithMember(elementName, element);
          }
        }
        if(element instanceof MdxSet<?>){
          addSectionWithSetsMembers((MdxSet<?>) element);
        }
      }
  }
 
  /**
   * adds selection lines
   * @param addSectionSelectRow - rows within SELECT ON COLUMNS or ROWS mdx section
   * @param orientation
   */
  private void addSectionSelectRow(String addSectionSelectRow, Axis axis) {
   
    List<String> querySectionSelectLines = getQuerySectionSelectLines(axis);
    addComa(querySectionSelectLines);
    querySectionSelectLines.add(addSectionSelectRow);
   
  }
  /**
   * adds crossjoin on selection lines with given addSectionSelectRow
   * @param addSectionSelectRow
   * @param orientation
   */
  private void crossjoinSectionSelectRow(String addSectionSelectRow, Axis axis) {
    List<String> querySectionSelectLines = getQuerySectionSelectLines(axis);
    if(querySectionSelectLines.size()>=1){
      //crossjoin existing lines
      String joindedLines = "";
      for(String line : querySectionSelectLines){
        joindedLines += line;
      }
      addSectionSelectRow =
        "Crossjoin("
        + joindedLines
        +  ", "
        + addSectionSelectRow
        + ")";
    }
    getQuerySectionSelectLines(axis).clear();
    getQuerySectionSelectLines(axis).add(addSectionSelectRow);
   
  }
  private List<String> getQuerySectionSelectLines(Axis axis){
    if(axis.equals(Axis.COLUMN)){
      return querySectionSelectColsLines;
    }else if(axis.equals(Axis.ROW)){
      return querySectionSelectRowsLines;
    }else if(axis.equals(Axis.SLICER)){
      return querySectionWhereLines;
    }
    //default
    return querySectionSelectColsLines;
  }
  private void addComa(List<String>sectionRow){
    if(sectionRow.size()>0){
      String comaAdded=sectionRow.get(sectionRow.size()-1)+",";
      sectionRow.remove(sectionRow.size()-1);
      sectionRow.add(comaAdded);
    }
  }
//  /**
//   * rows within FROM mdx section
//   */
//  public void setSectionFromRow(String sectionFromRow) {
//    querySectionFromRows= sectionFromRow;
//  }
//  /**
//   * rows within WHERE mdx section
//   */
//  public void addSectionWhereRow(String sectionWhereRow) {
//    querySectionWhereRows.add(sectionWhereRow);
//  }
 
//  /**
//   * return declared variable names within WITH Section
//   */
//  TODO: public List<String> getDeclaredWariables() {
//    return querySectionWithRows.;
//  }
 
 
  /*
   *
   * Full MDX Syntax:
   *
    [ WITH <SELECT WITH clause>
       [ , <SELECT WITH clause>...n ]
    ]
    SELECT
         [ *
        | ( <SELECT query axis clause>
                      [ , <SELECT query axis clause>,...n ]
                )
                ]
    FROM
       <SELECT subcube clause>
          [ <SELECT slicer axis clause> ]
          [ <SELECT cell property list clause> ]
   
    <SELECT WITH clause> ::=
         ( CELL CALCULATION <CREATE CELL CALCULATION body clause> )
       | ( [ CALCULATED ] MEMBER <CREATE MEMBER body clause>)
       | ( SET <CREATE SET body clause>)
   
    <SELECT query axis clause> ::=
       [ NON EMPTY ] Set_Expression
       [ <SELECT dimension property list clause> ]
          ON
                Integer_Expression
           | AXIS(Integer)
           | COLUMNS
           | ROWS
           | PAGES
           | SECTIONS
           | CHAPTERS
   
    <SELECT subcube clause> ::=
          Cube_Name
       | [NON VISUAL] (SELECT
                      [ *
           | ( <SELECT query axis clause> [ ,
               <SELECT query axis clause>,...n ] )
             ]
                FROM
             <SELECT subcube clause>
             <SELECT slicer axis clause> )
   
    <SELECT slicer axis clause> ::=
          WHERE Tuple_Expression
   
    <SELECT cell property list clause> ::=
       [ CELL ] PROPERTIES CellProperty_Name
          [ , CellProperty_Name,...n ]
   
    <SELECT dimension property list clause> ::=
       [DIMENSION] PROPERTIES
          (DimensionProperty_Name
             [,DimensionProperty_Name,...n ] )
        | (LevelProperty_Name
             [, LevelProperty_Name,...n ] )
        | (MemberProperty_Name
             [, MemberProperty_Name,...n ] )
   */
  
   /** Basic MDX Syntax:
   *
    [WITH
      [MEMBER <member-name> AS '<value-expression>' |
       SET <set-name> AS '<set-expression>'] . . .]
    SELECT [<axis_specification>
           [, <axis_specification>...]]
      FROM [<cube_specification>]
    [WHERE [<slicer_specification>]]  
            
   *
   */
  public String toString(){
    StringBuffer sb = new StringBuffer();

    //WITH section
    if(getQuerySectionWithLines().size()>0){
      sb.append("WITH\n");
      for(String row : getQuerySectionWithLines().values()){
        sb.append("    ");
        sb.append(row);
        sb.append("\n ");
      }
    }
   
    //SELECT section
    sb.append("SELECT\n");
    sb.append("{\n");
    for(String row : getQuerySectionSelectColsLines()){
      sb.append("    ");
      sb.append(row);
      sb.append("\n ");
    }
    if(getQuerySectionSelectColsLines().size()==0){// def measure
      sb.append("    ");
      sb.append(getMeasure().toString());
      sb.append("\n ");
    }
    sb.append("}\n");
    sb.append("ON COLUMNS,\n");
   
    sb.append("{\n");
    for(String row : getQuerySectionSelectRowsLines()){
      sb.append("    ");
      sb.append(row);
      sb.append("\n ");
    }
    if(this.getQuerySectionSelectRowsLines().size()==0){// def measure
      sb.append("    ");
      sb.append(getMeasure().toString());
      sb.append("\n ");
    }
    sb.append("}\n");
    sb.append("ON ROWS\n");
   
    //FROM section
    sb.append("FROM\n");
    sb.append("    ");
    sb.append(getQuerySectionFromLine());
    sb.append("\n ");
   
    //WHERE section
    if(querySectionWhereLines.size()>0){
      sb.append("WHERE\n");
      sb.append("{\n");
      for(String fromRow : getQuerySectionWhereLines()){
        sb.append("    ");
        sb.append(fromRow);
        sb.append("\n ");
      }
      sb.append("}\n");
    }
   
    return sb.toString();
  }
 
 
  /**
   * @return char used as addition in duplicate member (and set) names cases at WITH section
   *       eg.:
   *       char used in replacement of member name
   *         member [A].[B]
   *       into
   *         member [A].[B.]
   */
  public static char getUniqueChar(){
    char[] charArr = ".".toCharArray();
    return charArr[0];
  }
  /**
   * computes alter variable name:
   * e.g. [Measures].[Udzial] to [Measures].[UdzialA]
   * @param variableName
   * @param variableNamesThatExists
   * @return
   */
  private String computeAlterVariableName(BracketName variableName, Collection<String> variableNamesThatExists){
    StringBuffer sb = new StringBuffer(variableName.toString());
    sb.insert(sb.length()-1, getUniqueChar());
    while(variableNamesThatExists.contains(sb.toString())){
      sb.insert(sb.length()-1, getUniqueChar());
    }
    String alterVariableName = sb.toString();
    return alterVariableName;
  }
 
  public void computeMemberValues() throws NoHierarchyToAnalyzeShare{
    Iterator<Entry<BracketName, MdxValueGenerator>> iter =
      postGenerationValues.entrySet().iterator();
    while(iter.hasNext()){
      Entry<BracketName, MdxValueGenerator> entry = iter.next();
//      addSectionWithRow(entry.getKey(), entry.getValue().getValueExpression(this).toString());
      addSectionWithMember(entry.getKey(), entry.getValue().getValueExpression(this));
    }
  }
 
  public boolean validate()
  throws
  EmptyRowsColumnsAxisException,
  HierarchyInManyAxisException{
   
    if(querySectionSelectColsLines.size()==0 &&
        this.querySectionSelectRowsLines.size()==0)
      throw new EmptyRowsColumnsAxisException();
   
    Map<String,Set<Axis>> doubledHierarchiesInAxis = new HashMap<String,Set<Axis>>();
    for(Hierarchy rowHierarchy : getAxisRowsHierarchies()){
      Set<Axis>doubledHierarchyAxis = new HashSet<Axis>();
      for(Hierarchy colHierarchy : getAxisColsHierarchies()){
        if(colHierarchy.getName().equals(rowHierarchy.getName())){
          doubledHierarchyAxis.add(Axis.ROW);
          doubledHierarchyAxis.add(Axis.COLUMN);
          break;
        }
      }
      for(Hierarchy slicerHierarchy : getAxisSlicerHierarchies()){
        if(slicerHierarchy.getName().equals(rowHierarchy.getName())){
          doubledHierarchyAxis.add(Axis.ROW);
          doubledHierarchyAxis.add(Axis.SLICER);
          break;
        }
      }
      if(doubledHierarchyAxis.size()>0){
        doubledHierarchiesInAxis.put(rowHierarchy.getName(), doubledHierarchyAxis);
      }
    }
    for(Hierarchy colHierarchy : getAxisColsHierarchies()){
      Set<Axis>doubledHierarchyAxis = new HashSet<Axis>();
      for(Hierarchy slicerHierarchy : getAxisSlicerHierarchies()){
        if(slicerHierarchy.getName().equals(colHierarchy.getName())){
          doubledHierarchyAxis.add(Axis.COLUMN);
          doubledHierarchyAxis.add(Axis.SLICER);
          break;
        }
      }
      if(doubledHierarchyAxis.size()>0){
        if(doubledHierarchiesInAxis.containsKey(colHierarchy.getName())){
          doubledHierarchiesInAxis.get(colHierarchy.getName()).addAll(doubledHierarchyAxis);
        }else{
          doubledHierarchiesInAxis.put(colHierarchy.getName(), doubledHierarchyAxis);
        }
      }
    }
    if(doubledHierarchiesInAxis.keySet().size()>0)
      throw new HierarchyInManyAxisException(doubledHierarchiesInAxis);
   
    return true;
  }
 
  public List<Hierarchy> getHierarchies(Axis axis){
    if(axis.equals(Axis.COLUMN)){
      return getAxisColsHierarchies();
    }else if(axis.equals(Axis.ROW)){
      return getAxisRowsHierarchies();
    }else if(axis.equals(Axis.SLICER)){
      return getAxisSlicerHierarchies();
    }
    return null;
  }
 
  /**
   * Appends all of the hierarchies to the end of axis' hierarchy list
   * @param hierarchies
   * @param axis
   */
  private void addHierarchies(List<Hierarchy> hierarchies, Axis axis) {
    if(hierarchies!=null)
      getHierarchies(axis).addAll(hierarchies);
  }
 
  /**
   * Appends the hierarchy to the end of this list
   * @param hierarchy
   * @param axis
   */
  private void addHierarchy(Hierarchy hierarchy, Axis axis) {
    if(hierarchy!=null)
      getHierarchies(axis).add(hierarchy);
  }

  public Measure getMeasure() {
    return measure;
  }
 
  private Cube getCube() {
    return this.cube;
  }

  public Hierarchy getHierarchyDeepest(Axis axis) {
    int size = getHierarchies(axis).size();
    if(size>0)
      return getHierarchies(axis).get(size-1);
    else
//      return this.measure.getHierarchy(); forbidden!
      return null;
  }
 
}
TOP

Related Classes of biz.smart.mdx.MdxQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.