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