/* // $Id: //guest/julian_hyde/mondrian/src/main/mondrian/olap/FunCall.java#1 $ // This software is subject to the terms of the Common Public License // Agreement, available at the following URL: // http://www.opensource.org/licenses/cpl.html. // (C) Copyright 1998-2002 Kana Software, Inc. and others. // All Rights Reserved. // You must accept the terms of that agreement to use this software. // // jhyde, 20 January, 1999 */ package mondrian.olap; import java.util.*; import java.io.*; /** * A <code>FunCall</code> is a function applied to a list of operands. **/ public class FunCall extends ExpBase { /** Name of the function. **/ private String fun; /** The arguments to the function call. Note that for methods, 0-th arg is * 'this'. **/ public Exp[] args; /** Definition, set after resolve. **/ private FunDef funDef; /** * true if invoked as object.METHOD() or object.METHOD(args) or * object.PROPERTY; false if invoked as FUNCTION() or FUNCTION(args). **/ private boolean bInvokedOnObject; FunCall(String fun, Exp firstArg) { this.fun = fun.toUpperCase(); this.args = ExpBase.makeArray(firstArg); } public FunCall(String fun, Exp[] args, boolean bInvokedOnObject) { this.fun = fun; this.args = args; this.bInvokedOnObject = bInvokedOnObject; } public FunCall(String fun, Exp[] args) { this(fun, args, false); } public Object clone() { return new FunCall(fun, ExpBase.cloneArray(args), bInvokedOnObject); } public String getFunName() { return fun; } public final boolean isCallTo(String funName) { return fun.equalsIgnoreCase(funName); } public boolean isCallToSet() { return fun.equalsIgnoreCase("_Set"); } public boolean isCallToTuple() { return fun.equalsIgnoreCase("_Tuple"); } boolean isCallToParentheses() { return fun.equals("_Parentheses"); } public boolean isCallToRange() { return fun.equalsIgnoreCase("_Range"); } public boolean isCallToUminus() { return fun.equalsIgnoreCase("_Uminus"); } public boolean isCallToCrossJoin() { return fun.equalsIgnoreCase("CROSSJOIN"); } public boolean isCallToParameter() { return( fun.equalsIgnoreCase("Parameter") || fun.equalsIgnoreCase("ParamRef")); } public boolean isCallToFilter() { return fun.equalsIgnoreCase("FILTER"); } public Object[] getChildren() { return args; } public void replaceChild(int i, QueryPart with) { args[i] = (Exp) with; } public void removeChild( int iPosition ) { Exp newArgs[] = new Exp[ args.length -1 ]; int j = 0; for( int i = 0; i < args.length; i++ ){ if( i== iPosition ){ ++i; } if( j != newArgs.length ){ //this condition helps for removing last element newArgs[j] = args[i]; } ++j; } args = newArgs; } public boolean usesDimension(Dimension dimension) { return ExpBase.arrayUsesDimension(args, dimension); } boolean invokedOnObject() { return bInvokedOnObject; } private boolean isProperty() { return fun.equalsIgnoreCase("MEMBERS") || fun.equalsIgnoreCase("CHILDREN") || fun.equalsIgnoreCase("PARENT") || fun.equalsIgnoreCase("FIRSTCHILD") || fun.equalsIgnoreCase("LASTCHILD") || fun.equalsIgnoreCase("PREVMEMBER") || fun.equalsIgnoreCase("NEXTMEMBER") || fun.equalsIgnoreCase("CURRENTMEMBER") || fun.equalsIgnoreCase("DEFAULTMEMBER") || fun.equalsIgnoreCase("FIRSTSIBLING") || fun.equalsIgnoreCase("LASTSIBLING") || fun.equalsIgnoreCase("ITEM"); } private void ensureHaveDef() { if (funDef == null) funDef = FunDef.getDef(this); } public FunDef getFunDef() { ensureHaveDef(); return funDef; } public final int getCategory() { ensureHaveDef(); if (isCallToTuple() && args.length == 1) { // Ignore the tuple function if it only has one arg. For example, // ([Gender].members) is a set, // ([Gender].[M]) is a member, // (1 + 2) is a numeric, // but // ([Gender].[M], [Marital Status].[S]) is a tuple. return args[0].getCategory(); } return funDef.type; } public Hierarchy getHierarchy() { ensureHaveDef(); return funDef.getHierarchy(this); } public Exp resolve(Query q) { for (int i = 0; i < args.length; i++) { args[i] = args[i].resolve(q); } if (this.isCallToParameter()) { Parameter param = q.createOrLookupParam(this); return param.resolve(q); } else if (this.isCallTo("StrToTuple") || this.isCallTo("StrToSet")) { if (args.length <= 1) { throw Util.getRes().newMdxFuncArgumentsNum(fun); } for (int j = 1; j < args.length; j++) { if (args[j] instanceof Dimension) { // if arg is a dimension, switch to dimension's default // hierarchy args[j] = ((Dimension) args[j]).getHierarchy(); } else if (args[j] instanceof Hierarchy) { // nothing } else { throw Util.getRes().newMdxFuncNotHier(j+1, fun); } } } return this; } public void unparse(PrintWriter pw, ElementCallback callback) { ensureHaveDef(); if (isCallToSet()) { ExpBase.unparseList(pw, args, "{", "}", callback); } else if (funDef.isInfix()) { args[0].unparse(pw, callback); String s = (isCallToRange() ? ":" : funDef.name); pw.print(" " + s + " "); args[1].unparse(pw, callback); } else if (funDef.isPrefix()) { String s = (isCallToUminus() ? "-" : funDef.name); pw.print(s + " "); args[0].unparse(pw, callback); } else if (isCallToTuple() || isCallToParentheses()) { ExpBase.unparseList(pw, args, "(", ")", callback); } else if (isCallTo("StrToTuple") || isCallTo("StrToSet")) { if (callback.isPlatoMdx()) { // omit extra args (they're for us, not Plato) pw.print(fun); pw.print("("); args[0].unparse(pw, callback); pw.print(")"); } else { // leave extra args ExpBase.unparseList(pw, args, fun + "(", ")", callback); } } else if (funDef.isMethod()) { if (args.length < 1) { throw Util.getRes().newMdxFuncMethodNoArg( fun ); } args[0].unparse(pw, callback); // 'this' pw.print("." + fun + "("); for (int i = 1; i < args.length; i++) { if (i > 1) pw.print(", "); args[i].unparse(pw, callback); } pw.print(")"); } else if (funDef.isProperty()) { if (args.length != 1) { throw Util.getRes().newMdxFuncPropertyArg(fun); } args[0].unparse(pw, callback); // 'this' pw.print("." + fun); } else { ExpBase.unparseList(pw, args, fun + "(", ")", callback); } } /** * See {@link ExpBase#addAtPosition} for description (although this * refinement does most of the work). **/ public int addAtPosition(Exp e, int iPosition) { if (isCallToCrossJoin()) { Exp left = args[0], right = args[1]; int nLeft = left.addAtPosition(e, iPosition), nRight; if (nLeft == -1) { return -1; // added successfully } else if (nLeft == iPosition) { // This node has 'iPosition' hierarchies in its left tree. // Convert // CrossJoin(ltree, rtree) // into // CrossJoin(CrossJoin(ltree, e), rtree) // so that 'e' is the 'iPosition'th hierarchy from the left. args[0] = new FunCall("CrossJoin", new Exp[] {left, e}); return -1; // added successfully } else { Util.assert( nLeft < iPosition, "left tree had enough dimensions, yet still failed to " + "place expression"); nRight = right.addAtPosition(e, iPosition - nLeft); if (nRight == -1) return -1; // added successfully else return nLeft + nRight; // not added } } else if( isCallToTuple() ){ // For all functions besides CrossJoin, the dimensionality is // determined by the first argument alone. (For example, // 'Union(CrossJoin(a, b), CrossJoin(c, d)' has a dimensionality of // 2.) Exp newArgs[] = new Exp[ args.length + 1 ]; if( iPosition == 0 ){ // the expression has to go first newArgs[0] = e; for( int i = 0; i < args.length; i++ ){ newArgs[i+1] = args[i]; } } else if( iPosition < 0 || iPosition >= args.length ){ //the expression has to go last for( int i = 0; i < args.length; i++ ){ newArgs[i] = args[i]; } newArgs[args.length] = e; } else { //the expression has to go in the middle int i = 0; for( i = 0; i < iPosition; i++ ){ newArgs[i] = args[i]; } newArgs[iPosition] = e; for( i =(iPosition + 1); i < newArgs.length; i++ ){ newArgs[i] = args[i-1]; } } args = newArgs; return -1; } else { if( this.isCallToSet() && ( args[0] instanceof FunCall && ((FunCall)args[0]).isCallToTuple() )){ // DO not added to the tuple, return -1 to create new CrossJoin return 1; } return args[0].addAtPosition( e, iPosition ); } } // implement Exp public Object evaluate(Evaluator evaluator) { return evaluator.xx(this); } } // End FunCall.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 1603 | Julian Hyde |
mondrian: Add Andreas' taglib; Rename 'mondrian.rolap.Util.assert' to 'assertTrue', because 'assert' is a keyword in jdk 1.4; Fix 'NON EMPTY'; JUnit framework for tests; Add Oracle dataset. |
||
#2 | 1499 | Julian Hyde |
Mondrian: Re-organize functions and type-checking Add mondrian.olap.fun package |
||
#1 | 1453 | Julian Hyde | mondrian: first source check-in |