001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2005-2005 Julian Hyde 008// Copyright (C) 2005-2011 Pentaho and others 009// Copyright (C) 2004-2005 SAS Institute, Inc. 010// All Rights Reserved. 011*/ 012package mondrian.olap.fun; 013 014import mondrian.calc.*; 015import mondrian.calc.impl.AbstractListCalc; 016import mondrian.mdx.ResolvedFunCall; 017import mondrian.olap.*; 018import mondrian.rolap.RolapEvaluator; 019 020 021/** 022 * Definition of the <code>NonEmptyCrossJoin</code> MDX function. 023 * 024 * @author jhyde 025 * @since Mar 23, 2006 026 * 027 * author 16 December, 2004 028 */ 029public class NonEmptyCrossJoinFunDef extends CrossJoinFunDef { 030 static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver( 031 "NonEmptyCrossJoin", 032 "NonEmptyCrossJoin(<Set1>, <Set2>)", 033 "Returns the cross product of two sets, excluding empty tuples and tuples without associated fact table data.", 034 new String[]{"fxxx"}, 035 NonEmptyCrossJoinFunDef.class); 036 037 public NonEmptyCrossJoinFunDef(FunDef dummyFunDef) { 038 super(dummyFunDef); 039 } 040 041 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 042 final ListCalc listCalc1 = compiler.compileList(call.getArg(0)); 043 final ListCalc listCalc2 = compiler.compileList(call.getArg(1)); 044 return new AbstractListCalc( 045 call, new Calc[] {listCalc1, listCalc2}, false) 046 { 047 public TupleList evaluateList(Evaluator evaluator) { 048 SchemaReader schemaReader = evaluator.getSchemaReader(); 049 050 // Evaluate the arguments in non empty mode, but remove from 051 // the slicer any members that will be overridden by args to 052 // the NonEmptyCrossjoin function. For example, in 053 // 054 // SELECT NonEmptyCrossJoin( 055 // [Store].[USA].Children, 056 // [Product].[Beer].Children) 057 // FROM [Sales] 058 // WHERE [Store].[Mexico] 059 // 060 // we want all beers, not just those sold in Mexico. 061 final int savepoint = evaluator.savepoint(); 062 try { 063 evaluator.setNonEmpty(true); 064 for (Member member 065 : ((RolapEvaluator) evaluator).getSlicerMembers()) 066 { 067 if (getType().getElementType().usesHierarchy( 068 member.getHierarchy(), true)) 069 { 070 evaluator.setContext( 071 member.getHierarchy().getAllMember()); 072 } 073 } 074 075 NativeEvaluator nativeEvaluator = 076 schemaReader.getNativeSetEvaluator( 077 call.getFunDef(), call.getArgs(), evaluator, this); 078 if (nativeEvaluator != null) { 079 evaluator.restore(savepoint); 080 return 081 (TupleList) nativeEvaluator.execute( 082 ResultStyle.LIST); 083 } 084 085 final TupleList list1 = listCalc1.evaluateList(evaluator); 086 if (list1.isEmpty()) { 087 evaluator.restore(savepoint); 088 return list1; 089 } 090 final TupleList list2 = listCalc2.evaluateList(evaluator); 091 TupleList result = mutableCrossJoin(list1, list2); 092 093 // remove any remaining empty crossings from the result 094 result = nonEmptyList(evaluator, result, call); 095 return result; 096 } finally { 097 evaluator.restore(savepoint); 098 } 099 } 100 101 public boolean dependsOn(Hierarchy hierarchy) { 102 if (super.dependsOn(hierarchy)) { 103 return true; 104 } 105 // Member calculations generate members, which mask the actual 106 // expression from the inherited context. 107 if (listCalc1.getType().usesHierarchy(hierarchy, true)) { 108 return false; 109 } 110 if (listCalc2.getType().usesHierarchy(hierarchy, true)) { 111 return false; 112 } 113 // The implicit value expression, executed to figure out 114 // whether a given tuple is empty, depends upon all dimensions. 115 return true; 116 } 117 }; 118 } 119 120} 121 122// End NonEmptyCrossJoinFunDef.java