001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/PropertiesFunDef.java#16 $
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-2006 Julian Hyde and others
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.olap.*;
013    import mondrian.calc.*;
014    import mondrian.calc.impl.GenericCalc;
015    import mondrian.mdx.ResolvedFunCall;
016    
017    /**
018     * Definition of the <code>Properties</code> MDX function.
019     *
020     * @author jhyde
021     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/PropertiesFunDef.java#16 $
022     * @since Mar 23, 2006
023     */
024    class PropertiesFunDef extends FunDefBase {
025        static final ResolverImpl Resolver = new ResolverImpl();
026    
027        public PropertiesFunDef(
028                String name,
029                String signature,
030                String description,
031                Syntax syntax,
032                int returnType,
033                int[] parameterTypes) {
034            super(name, signature, description, syntax, returnType, parameterTypes);
035        }
036    
037        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
038            final MemberCalc memberCalc = compiler.compileMember(call.getArg(0));
039            final StringCalc stringCalc = compiler.compileString(call.getArg(1));
040            return new GenericCalc(call) {
041                public Object evaluate(Evaluator evaluator) {
042                    return properties(
043                            memberCalc.evaluateMember(evaluator),
044                            stringCalc.evaluateString(evaluator));
045                }
046    
047                public Calc[] getCalcs() {
048                    return new Calc[] {memberCalc, stringCalc};
049                }
050            };
051        }
052    
053        static Object properties(Member member, String s) {
054            boolean matchCase = MondrianProperties.instance().CaseSensitive.get();
055            Object o = member.getPropertyValue(s, matchCase);
056            if (o == null) {
057                if (!Util.isValidProperty(member, s)) {
058                    throw new MondrianEvaluationException(
059                            "Property '" + s +
060                            "' is not valid for member '" + member + "'");
061                }
062            }
063            return o;
064        }
065    
066        /**
067         * Resolves calls to the <code>PROPERTIES</code> MDX function.
068         */
069        private static class ResolverImpl extends ResolverBase {
070            private ResolverImpl() {
071                super("Properties",
072                      "<Member>.Properties(<String Expression>)",
073                      "Returns the value of a member property.",
074                      Syntax.Method);
075            }
076    
077            public FunDef resolve(
078                    Exp[] args, Validator validator, int[] conversionCount) {
079                final int[] argTypes = new int[]{Category.Member, Category.String};
080                final Exp propertyNameExp = args[1];
081                final Exp memberExp = args[0];
082                if ((args.length != 2) ||
083                        (memberExp.getCategory() != Category.Member) ||
084                        (propertyNameExp.getCategory() != Category.String)) {
085                    return null;
086                }
087                int returnType = deducePropertyCategory(memberExp, propertyNameExp);
088                return new PropertiesFunDef(
089                    getName(), getSignature(), getDescription(),getSyntax(),
090                    returnType, argTypes);
091            }
092    
093            /**
094             * Deduces the category of a property. This is possible only if the
095             * name is a string literal, and the member's hierarchy is unambigous.
096             * If the type cannot be deduced, returns {@link Category#Value}.
097             *
098             * @param memberExp Expression for the member
099             * @param propertyNameExp Expression for the name of the property
100             * @return Category of the property
101             */
102            private int deducePropertyCategory(
103                Exp memberExp,
104                Exp propertyNameExp)
105            {
106                if (!(propertyNameExp instanceof Literal)) {
107                    return Category.Value;
108                }
109                String propertyName = (String) ((Literal) propertyNameExp).getValue();
110                Hierarchy hierarchy = memberExp.getType().getHierarchy();
111                if (hierarchy == null) {
112                    return Category.Value;
113                }
114                Level[] levels = hierarchy.getLevels();
115                Property property = lookupProperty(
116                    levels[levels.length - 1], propertyName);
117                if (property == null) {
118                    // we'll likely get a runtime error
119                    return Category.Value;
120                } else {
121                    switch (property.getType()) {
122                    case TYPE_BOOLEAN:
123                        return Category.Logical;
124                    case TYPE_NUMERIC:
125                        return Category.Numeric;
126                    case TYPE_STRING:
127                        return Category.String;
128                    default:
129                        throw Util.badValue(property.getType());
130                    }
131                }
132            }
133    
134            public boolean requiresExpression(int k) {
135                return true;
136            }
137        }
138    }
139    
140    // End PropertiesFunDef.java
141