001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunInfo.java#14 $
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-2007 Julian Hyde and others
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    
011    package mondrian.olap.fun;
012    
013    import mondrian.olap.FunDef;
014    import mondrian.olap.Syntax;
015    import mondrian.olap.Util;
016    
017    import java.util.*;
018    import java.lang.reflect.Array;
019    
020    /**
021     * Support class for the {@link mondrian.tui.CmdRunner} allowing one to view
022     * available functions and their syntax.
023     *
024     * @author Richard M. Emberson
025     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FunInfo.java#14 $
026     */
027    public class FunInfo implements Comparable<FunInfo> {
028        private final Syntax syntax;
029        private final String name;
030        private final String description;
031        private final int[] returnTypes;
032        private final int[][] parameterTypes;
033        private String[] sigs;
034    
035        static FunInfo make(Resolver resolver) {
036            FunDef funDef = resolver.getFunDef();
037            if (funDef != null) {
038                return new FunInfo(funDef);
039            } else if (resolver instanceof MultiResolver) {
040                return new FunInfo((MultiResolver) resolver);
041            } else {
042                return new FunInfo(resolver);
043            }
044        }
045    
046        FunInfo(FunDef funDef) {
047            this.syntax = funDef.getSyntax();
048            this.name = funDef.getName();
049            assert name != null;
050            assert syntax != null;
051            this.returnTypes = new int[] { funDef.getReturnCategory() };
052            this.parameterTypes = new int[][] { funDef.getParameterCategories() };
053    
054            // use explicit signature if it has one, otherwise generate a set
055            this.sigs = funDef instanceof FunDefBase
056                && ((FunDefBase) funDef).signature != null
057                ? new String[] {((FunDefBase) funDef).signature}
058                : makeSigs(syntax, name, returnTypes, parameterTypes);
059            this.description = funDef.getDescription();
060        }
061    
062        FunInfo(MultiResolver multiResolver) {
063            this.syntax = multiResolver.getSyntax();
064            this.name = multiResolver.getName();
065            assert name != null;
066            assert syntax != null;
067            this.description = multiResolver.getDescription();
068    
069            String[] signatures = multiResolver.getSignatures();
070            this.returnTypes = new int[signatures.length];
071            this.parameterTypes = new int[signatures.length][];
072            for (int i = 0; i < signatures.length; i++) {
073                returnTypes[i] = FunUtil.decodeReturnCategory(signatures[i]);
074                parameterTypes[i] = FunUtil.decodeParameterCategories(signatures[i]);
075            }
076            this.sigs = makeSigs(syntax, name, returnTypes, parameterTypes);
077        }
078    
079        FunInfo(Resolver resolver) {
080            this.syntax = resolver.getSyntax();
081            this.name = resolver.getName();
082            assert name != null;
083            assert syntax != null;
084            this.description = resolver.getDescription();
085            this.returnTypes = null;
086            this.parameterTypes = null;
087            final String signature = resolver.getSignature();
088            this.sigs = signature == null ? new String[0] :
089                    new String[] {signature};
090        }
091    
092        public String[] getSignatures() {
093            return sigs;
094        }
095    
096        private static String[] makeSigs(
097            Syntax syntax,
098            String name,
099            int[] returnTypes,
100            int[][] parameterTypes)
101        {
102            if (parameterTypes == null) {
103                return null;
104            }
105    
106            String[] sigs = new String[parameterTypes.length];
107            for (int i = 0; i < sigs.length; i++) {
108                sigs[i] = syntax.getSignature(
109                        name, returnTypes[i], parameterTypes[i]);
110            }
111            return sigs;
112        }
113    
114        /**
115         * Returns the syntactic type of the function.
116         */
117        public Syntax getSyntax() {
118            return this.syntax;
119        }
120    
121        /**
122         * Returns the name of this function.
123         */
124        public String getName() {
125            return this.name;
126        }
127    
128        /**
129         * Returns the description of this function.
130         */
131        public String getDescription() {
132            return this.description;
133        }
134    
135        /**
136         * Returns the type of value returned by this function. Values are the same
137         * as those returned by {@link mondrian.olap.Exp#getCategory()}.
138         */
139        public int[] getReturnCategories() {
140            return this.returnTypes;
141        }
142    
143        /**
144         * Returns the types of the arguments of this function. Values are the same
145         * as those returned by {@link mondrian.olap.Exp#getCategory()}. The
146         * 0<sup>th</sup> argument of methods and properties are the object they
147         * are applied to. Infix operators have two arguments, and prefix operators
148         * have one argument.
149         */
150        public int[][] getParameterCategories() {
151            return this.parameterTypes;
152        }
153    
154        public int compareTo(FunInfo fi) {
155            int c = this.name.compareTo(fi.name);
156            if (c != 0) {
157                return c;
158            }
159            final List<Object> pcList = toList(this.getParameterCategories());
160            final String pc = pcList.toString();
161            final List otherPcList = toList(fi.getParameterCategories());
162            final String otherPc = otherPcList.toString();
163            return pc.compareTo(otherPc);
164        }
165    
166        public boolean equals(Object obj) {
167            if (obj instanceof FunInfo) {
168                final FunInfo that = (FunInfo) obj;
169                if (!name.equals(that.name)) {
170                    return false;
171                }
172                final List<Object> pcList = toList(this.getParameterCategories());
173                final List<Object> pcList2 = toList(that.getParameterCategories());
174                return pcList.equals(pcList2);
175            } else {
176                return false;
177            }
178        }
179    
180        public int hashCode() {
181            int h = name.hashCode();
182            final List<Object> pcList = toList(this.getParameterCategories());
183            return Util.hash(h, pcList);
184        }
185    
186        private static List<Object> toList(Object a) {
187            final List<Object> list = new ArrayList<Object>();
188            if (a == null) {
189                return list;
190            }
191            final int length = Array.getLength(a);
192            for (int i = 0; i < length; i++) {
193                final Object o = Array.get(a, i);
194                if (o.getClass().isArray()) {
195                    list.add(toList(o));
196                } else {
197                    list.add(o);
198                }
199            }
200            return list;
201        }
202    }
203    
204    // End FunInfo.java