public FunDef getDef(Exp[] args, String funName, Syntax syntax) { // Compute signature first. It makes debugging easier. final String signature = syntax.getSignature(funName, Category.Unknown, ExpBase.getTypes(args)); // Resolve function by its upper-case name first. If there is only one // function with that name, stop immediately. If there is more than // function, use some custom method, which generally involves looking // at the type of one of its arguments. List<Resolver> resolvers = funTable.getResolvers(funName, syntax); assert resolvers != null; final List<Resolver.Conversion> conversionList = new ArrayList<Resolver.Conversion>(); int minConversionCost = Integer.MAX_VALUE; List<FunDef> matchDefs = new ArrayList<FunDef>(); List<Resolver.Conversion> matchConversionList = null; for (Resolver resolver : resolvers) { conversionList.clear(); FunDef def = resolver.resolve(args, this, conversionList); if (def != null) { int conversionCost = sumConversionCost(conversionList); if (conversionCost < minConversionCost) { minConversionCost = conversionCost; matchDefs.clear(); matchDefs.add(def); matchConversionList = new ArrayList<Resolver.Conversion>(conversionList); } else if (conversionCost == minConversionCost) { matchDefs.add(def); } else { // ignore this match -- it required more coercions than // other overloadings we've seen } } } switch (matchDefs.size()) { case 0: throw MondrianResource.instance().NoFunctionMatchesSignature.ex(signature); case 1: break; default: final StringBuilder buf = new StringBuilder(); for (FunDef matchDef : matchDefs) { if (buf.length() > 0) { buf.append(", "); } buf.append(matchDef.getSignature()); } throw MondrianResource.instance() .MoreThanOneFunctionMatchesSignature .ex(signature, buf.toString()); } final FunDef matchDef = matchDefs.get(0); for (Resolver.Conversion conversion : matchConversionList) { conversion.checkValid(); conversion.apply(this, Arrays.asList(args)); } return matchDef; }
/** Returns whether the <code>k</code>th argument to a function call has to be an expression. */ boolean requiresExpression(UnresolvedFunCall funCall, int k) { // The function call has not been resolved yet. In fact, this method // may have been invoked while resolving the child. Consider this: // CrossJoin([Measures].[Unit Sales] * [Measures].[Store Sales]) // // In order to know whether to resolve '*' to the multiplication // operator (which returns a scalar) or the crossjoin operator // (which returns a set) we have to know what kind of expression is // expected. List<Resolver> resolvers = funTable.getResolvers(funCall.getFunName(), funCall.getSyntax()); for (Resolver resolver2 : resolvers) { if (!resolver2.requiresExpression(k)) { // This resolver accepts a set in this argument position, // therefore we don't REQUIRE a scalar expression. return false; } } return true; }