001/*
002// $Id: CallNode.java 482 2012-01-05 23:27:27Z jhyde $
003//
004// Licensed to Julian Hyde under one or more contributor license
005// agreements. See the NOTICE file distributed with this work for
006// additional information regarding copyright ownership.
007//
008// Julian Hyde licenses this file to you under the Apache License,
009// Version 2.0 (the "License"); you may not use this file except in
010// compliance with the License. You may obtain a copy of the License at:
011//
012// http://www.apache.org/licenses/LICENSE-2.0
013//
014// Unless required by applicable law or agreed to in writing, software
015// distributed under the License is distributed on an "AS IS" BASIS,
016// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017// See the License for the specific language governing permissions and
018// limitations under the License.
019*/
020package org.olap4j.mdx;
021
022import org.olap4j.type.Type;
023
024import java.util.Arrays;
025import java.util.List;
026
027/**
028 * A parse tree node representing a call to a function or operator.
029 *
030 * <p>Examples of calls include:<ul>
031 * <li><code>5 + 2</code>, a call to the infix arithmetic operator '+'</li>
032 * <li><code>[Measures].[Unit Sales] IS NULL</code>, a call applying the
033 *   {@link Syntax#Postfix postfix} operator
034 *   <code>IS NULL</code> to a member expression</li>
035 * <li><code>CrossJoin({[Gender].Children}, {[Store]})</code>, a call to the
036 *   <code>CrossJoin</code> function</li>
037 * <li><code>[Gender].Children</code>, a call to the <code>Children</code>
038 *   operator, which has {@link Syntax#Property property syntax}</li>
039 * <li><code>[Gender].Properties("FORMAT_STRING")</code>, a call to the
040 *   <code>Properties</code> operator, which has
041 *   {@link Syntax#Method method syntax}</li>
042 * </ul>
043 *
044 * @author jhyde
045 * @version $Id: CallNode.java 482 2012-01-05 23:27:27Z jhyde $
046 * @since Jan 6, 2006
047 */
048public class CallNode implements ParseTreeNode {
049
050    private final String name;
051    private final Syntax syntax;
052    private final List<ParseTreeNode> argList;
053    private final ParseRegion region;
054    private Type type;
055
056    /**
057     * Creates a CallNode.
058     *
059     * <p>The <code>syntax</code> argument determines whether this is a prefix,
060     * infix or postfix operator, a function call, and so forth.
061     *
062     * <p>The list of arguments <code>args</code> must be specified, even if
063     * there are zero arguments, and each argument must be not null.
064     *
065     * <p>The type is initially null, but can be set using {@link #setType}
066     * after validation.
067     *
068     * @param region Region of source code
069     * @param name Name of operator or function
070     * @param syntax Syntax of call
071     * @param args List of zero or more arguments
072     */
073    public CallNode(
074        ParseRegion region,
075        String name,
076        Syntax syntax,
077        List<ParseTreeNode> args)
078    {
079        this.region = region;
080        assert name != null;
081        assert syntax != null;
082        assert args != null;
083        this.name = name;
084        this.syntax = syntax;
085        this.argList = args;
086
087        // Check special syntaxes.
088        switch (syntax) {
089        case Braces:
090            assert name.equals("{}");
091            break;
092        case Parentheses:
093            assert name.equals("()");
094            break;
095        case Internal:
096            assert name.startsWith("$");
097            break;
098        case Empty:
099            assert name.equals("");
100            break;
101        default:
102            assert !name.startsWith("$")
103                && !name.equals("{}")
104                && !name.equals("()");
105            break;
106        }
107    }
108
109    /**
110     * Creates an CallNode using a variable number of arguments.
111     *
112     * <p>The <code>syntax</code> argument determines whether this is a prefix,
113     * infix or postfix operator, a function call, and so forth.
114     *
115     * <p>The list of arguments <code>args</code> must be specified, even if
116     * there are zero arguments, and each argument must be not null.
117     *
118     * @param region Region of source code
119     * @param name Name of operator or function
120     * @param syntax Syntax of call
121     * @param args List of zero or more arguments
122     */
123    public CallNode(
124        ParseRegion region,
125        String name,
126        Syntax syntax,
127        ParseTreeNode... args)
128    {
129        this(region, name, syntax, Arrays.asList(args));
130    }
131
132    public ParseRegion getRegion() {
133        return region;
134    }
135
136    /**
137     * Sets the type of this CallNode.
138     *
139     * <p>Typically, this method would be called by the validator when it has
140     * deduced the argument types, chosen between any overloaded functions
141     * or operators, and determined the result type of the function or
142     * operator.
143     *
144     * @param type Result type of this call
145     */
146    public void setType(Type type) {
147        this.type = type;
148    }
149
150    public Type getType() {
151        return type;
152    }
153
154    public void unparse(ParseTreeWriter writer) {
155        syntax.unparse(name, argList, writer);
156    }
157
158    public <T> T accept(ParseTreeVisitor<T> visitor) {
159        final T o = visitor.visit(this);
160        // visit the call's arguments
161        for (ParseTreeNode arg : argList) {
162            arg.accept(visitor);
163        }
164        return o;
165    }
166
167    /**
168     * Returns the name of the function or operator.
169     *
170     * @return name of the function or operator
171     */
172    public String getOperatorName() {
173        return name;
174    }
175
176    /**
177     * Returns the syntax of this call.
178     *
179     * @return the syntax of the call
180     */
181    public Syntax getSyntax() {
182        return syntax;
183    }
184
185    /**
186     * Returns the list of arguments to this call.
187     *
188     * @return list of arguments
189     */
190    public List<ParseTreeNode> getArgList() {
191        return argList;
192    }
193
194    public CallNode deepCopy() {
195        return new CallNode(
196            this.region,
197            this.name,
198            this.syntax,
199            MdxUtil.deepCopyList(argList));
200    }
201
202    @Override
203    public int hashCode() {
204        final int prime = 31;
205        int result = 1;
206        result = prime * result + ((argList == null) ? 0 : argList.hashCode());
207        result = prime * result + ((name == null) ? 0 : name.hashCode());
208        result = prime * result + ((syntax == null) ? 0 : syntax.hashCode());
209        return result;
210    }
211
212    @Override
213    public boolean equals(Object obj) {
214        if (this == obj) {
215            return true;
216        }
217        if (obj == null) {
218            return false;
219        }
220        if (getClass() != obj.getClass()) {
221            return false;
222        }
223        CallNode other = (CallNode) obj;
224        if (argList == null) {
225            if (other.argList != null) {
226                return false;
227            }
228        } else if (!argList.equals(other.argList)) {
229            return false;
230        }
231        if (name == null) {
232            if (other.name != null) {
233                return false;
234            }
235        } else if (!name.equals(other.name)) {
236            return false;
237        }
238        if (syntax == null) {
239            if (other.syntax != null) {
240                return false;
241            }
242        } else if (!syntax.equals(other.syntax)) {
243            return false;
244        }
245        return true;
246    }
247}
248
249// End CallNode.java