001 /* 002 // $Id: //open/mondrian/src/main/mondrian/udf/LastNonEmptyUdf.java#9 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2005-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.udf; 011 012 import mondrian.olap.*; 013 import mondrian.olap.type.*; 014 import mondrian.rolap.RolapUtil; 015 import mondrian.spi.UserDefinedFunction; 016 017 import java.util.List; 018 019 /** 020 * Definition of the user-defined function "LastNonEmpty". 021 * 022 * @author jhyde 023 * @version $Id: //open/mondrian/src/main/mondrian/udf/LastNonEmptyUdf.java#9 $ 024 */ 025 public class LastNonEmptyUdf implements UserDefinedFunction { 026 027 public String getName() { 028 return "LastNonEmpty"; 029 } 030 031 public String getDescription() { 032 return "Returns the last member of a set whose value is not empty"; 033 } 034 035 public Syntax getSyntax() { 036 return Syntax.Function; 037 } 038 039 public Type getReturnType(Type[] parameterTypes) { 040 // Return type is the same as the elements of the first parameter. 041 // For example, 042 // LastNonEmpty({[Time].[1997], [Time].[1997].[Q1]}, 043 // [Measures].[Unit Sales]) 044 // will return a member of the [Time] dimension. 045 SetType setType = (SetType) parameterTypes[0]; 046 MemberType memberType = (MemberType) setType.getElementType(); 047 return memberType; 048 } 049 050 public Type[] getParameterTypes() { 051 return new Type[] { 052 // The first argument must be a set of members (of any hierarchy). 053 new SetType(MemberType.Unknown), 054 // The second argument must be a member. 055 MemberType.Unknown, 056 }; 057 } 058 059 public Object execute(Evaluator evaluator, Argument[] arguments) { 060 final Argument memberListExp = arguments[0]; 061 final List memberList = (List) memberListExp.evaluate(evaluator); 062 final Argument exp = arguments[1]; 063 int nullCount = 0; 064 int missCount = 0; 065 for (int i = memberList.size() - 1; i >= 0; --i) { 066 Member member = (Member) memberList.get(i); 067 // Create an evaluator with the member as its context. 068 Evaluator subEvaluator = evaluator.push(member); 069 int missCountBefore = subEvaluator.getMissCount(); 070 final Object o = exp.evaluateScalar(subEvaluator); 071 int missCountAfter = subEvaluator.getMissCount(); 072 if (Util.isNull(o)) { 073 ++nullCount; 074 continue; 075 } 076 if (missCountAfter > missCountBefore) { 077 // There was a cache miss while evaluating the expression, so 078 // the result is bogus. It would be a mistake to give up after 079 // one cache miss, because then it would take us N 080 // evaluate/fetch passes to move back through N members, which 081 // is way too many. 082 // 083 // Carry on until we have seen as many misses as we have seen 084 // null cells. The effect of this policy is that each pass 085 // examines twice as many cells as the previous pass. Thus 086 // we can move back through N members in log2(N) passes. 087 ++missCount; 088 if (missCount < 2 * nullCount + 1) { 089 continue; 090 } 091 } 092 if (o == RolapUtil.valueNotReadyException) { 093 // Value is not in the cache yet, so we don't know whether 094 // it will be empty. Carry on... 095 continue; 096 } 097 if (o instanceof RuntimeException) { 098 RuntimeException runtimeException = (RuntimeException) o; 099 if (o == RolapUtil.valueNotReadyException) { 100 // Value is not in the cache yet, so we don't know whether 101 // it will be empty. Carry on... 102 continue; 103 } 104 return runtimeException; 105 } 106 return member; 107 } 108 // Not found. Return the hierarchy's 'null member'. 109 // It is possible that a MemberType has a Dimension but 110 // no hierarchy, so we have to just get the type's Dimension's 111 // default hierarchy and return it's null member. 112 final Hierarchy hierarchy = memberListExp.getType().getHierarchy(); 113 return (hierarchy == null) 114 ? memberListExp.getType().getDimension(). 115 getHierarchies()[0].getNullMember() 116 : hierarchy.getNullMember(); 117 } 118 119 public String[] getReservedWords() { 120 // This function does not require any reserved words. 121 return null; 122 } 123 } 124 125 // End LastNonEmptyUdf.java