001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunTableImpl.java#8 $ 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) 2002-2006 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.olap.fun; 011 012 import mondrian.mdx.UnresolvedFunCall; 013 import mondrian.olap.*; 014 import mondrian.resource.MondrianResource; 015 016 import java.util.*; 017 018 /** 019 * Abstract implementation of {@link FunTable}. 020 * 021 * <p>The derived class must implement {@link #defineFunctions()} to define 022 * each function which will be recognized by this table. This method is called 023 * from the constructor, after which point, no further functions can be added. 024 */ 025 public abstract class FunTableImpl implements FunTable { 026 /** 027 * Maps the upper-case name of a function plus its 028 * {@link mondrian.olap.Syntax} to an array of 029 * {@link mondrian.olap.Validator} objects for that name. 030 */ 031 protected final Map<String, List<Resolver>> mapNameToResolvers = 032 new HashMap<String, List<Resolver>>(); 033 private final Set<String> reservedWords = new HashSet<String>(); 034 private final Set<String> propertyWords = new HashSet<String>(); 035 /** used during initialization */ 036 protected final List<Resolver> resolverList = new ArrayList<Resolver>(); 037 protected final List<FunInfo> funInfoList = new ArrayList<FunInfo>(); 038 039 protected FunTableImpl() { 040 } 041 042 /** 043 * Initializes the function table. 044 */ 045 public void init() { 046 defineFunctions(); 047 organizeFunctions(); 048 } 049 050 protected static String makeResolverKey(String name, Syntax syntax) { 051 return name.toUpperCase() + "$" + syntax; 052 } 053 054 protected void define(FunDef funDef) { 055 define(new SimpleResolver(funDef)); 056 } 057 058 protected void define(Resolver resolver) { 059 addFunInfo(resolver); 060 if (resolver.getSyntax() == Syntax.Property) { 061 defineProperty(resolver.getName()); 062 } 063 resolverList.add(resolver); 064 final String[] reservedWords = resolver.getReservedWords(); 065 for (String reservedWord : reservedWords) { 066 defineReserved(reservedWord); 067 } 068 } 069 070 protected void addFunInfo(Resolver resolver) { 071 this.funInfoList.add(FunInfo.make(resolver)); 072 } 073 074 public FunDef getDef( 075 Exp[] args, Validator validator, String funName, Syntax syntax) { 076 String key = makeResolverKey(funName, syntax); 077 078 // Resolve function by its upper-case name first. If there is only one 079 // function with that name, stop immediately. If there is more than 080 // function, use some custom method, which generally involves looking 081 // at the type of one of its arguments. 082 String signature = syntax.getSignature(funName, 083 Category.Unknown, ExpBase.getTypes(args)); 084 List<Resolver> resolvers = mapNameToResolvers.get(key); 085 if (resolvers == null) { 086 resolvers = Collections.emptyList(); 087 } 088 089 int[] conversionCount = new int[] {0}; 090 int minConversions = Integer.MAX_VALUE; 091 int matchCount = 0; 092 FunDef matchDef = null; 093 for (Resolver resolver : resolvers) { 094 conversionCount[0] = 0; 095 FunDef def = resolver.resolve(args, validator, conversionCount); 096 if (def != null) { 097 int conversions = conversionCount[0]; 098 if (conversions < minConversions) { 099 minConversions = conversions; 100 matchCount = 1; 101 matchDef = def; 102 } else if (conversions == minConversions) { 103 matchCount++; 104 } else { 105 // ignore this match -- it required more coercions than 106 // other overloadings we've seen 107 } 108 } 109 } 110 switch (matchCount) { 111 case 0: 112 throw MondrianResource.instance().NoFunctionMatchesSignature.ex( 113 signature); 114 case 1: 115 final String matchKey = makeResolverKey(matchDef.getName(), 116 matchDef.getSyntax()); 117 Util.assertTrue(matchKey.equals(key), matchKey); 118 return matchDef; 119 default: 120 throw MondrianResource.instance().MoreThanOneFunctionMatchesSignature.ex(signature); 121 } 122 } 123 124 public boolean requiresExpression( 125 UnresolvedFunCall call, 126 int k, 127 Validator validator) { 128 // The function call has not been resolved yet. In fact, this method 129 // may have been invoked while resolving the child. Consider this: 130 // CrossJoin([Measures].[Unit Sales] * [Measures].[Store Sales]) 131 // 132 // In order to know whether to resolve '*' to the multiplication 133 // operator (which returns a scalar) or the crossjoin operator (which 134 // returns a set) we have to know what kind of expression is expected. 135 String key = makeResolverKey(call.getFunName(), call.getSyntax()); 136 List<Resolver> resolvers = mapNameToResolvers.get(key); 137 if (resolvers == null) { 138 resolvers = Collections.emptyList(); 139 } 140 for (Resolver resolver2 : resolvers) { 141 if (!resolver2.requiresExpression(k)) { 142 // This resolver accepts a set in this argument position, 143 // therefore we don't REQUIRE a scalar expression. 144 return false; 145 } 146 } 147 return true; 148 } 149 150 public List<String> getReservedWords() { 151 return new ArrayList<String>(reservedWords); 152 } 153 154 public boolean isReserved(String s) { 155 return reservedWords.contains(s.toUpperCase()); 156 } 157 158 /** 159 * Defines a reserved word. 160 * @see #isReserved 161 */ 162 protected void defineReserved(String s) { 163 reservedWords.add(s.toUpperCase()); 164 } 165 166 public List<Resolver> getResolvers() { 167 final List<Resolver> list = new ArrayList<Resolver>(); 168 for (List<Resolver> resolvers : mapNameToResolvers.values()) { 169 list.addAll(resolvers); 170 } 171 return list; 172 } 173 174 public boolean isProperty(String s) { 175 return propertyWords.contains(s.toUpperCase()); 176 } 177 178 /** 179 * Defines a word matching a property function name. 180 * @see #isProperty 181 */ 182 protected void defineProperty(String s) { 183 propertyWords.add(s.toUpperCase()); 184 } 185 186 public List<FunInfo> getFunInfoList() { 187 return Collections.unmodifiableList(this.funInfoList); 188 } 189 190 /** 191 * Indexes the collection of functions. 192 */ 193 protected void organizeFunctions() { 194 Collections.sort(funInfoList); 195 // Map upper-case function names to resolvers. 196 for (Resolver resolver : resolverList) { 197 String key = makeResolverKey(resolver.getName(), 198 resolver.getSyntax()); 199 List<Resolver> list = mapNameToResolvers.get(key); 200 if (list == null) { 201 list = new ArrayList<Resolver>(); 202 mapNameToResolvers.put(key, list); 203 } 204 list.add(resolver); 205 } 206 } 207 208 /** 209 * This method is called from the constructor, to define the set of 210 * functions and reserved words recognized. 211 * 212 * <p>Each function is declared by calling {@link #define}. Each reserved 213 * word is declared by calling {@link #defineReserved(String)}. 214 * 215 * <p>Derived class can override this method to add more functions. 216 */ 217 protected abstract void defineFunctions(); 218 } 219 220 // End FunTableImpl.java