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    */
020    package org.olap4j.mdx;
021    
022    import org.olap4j.type.Type;
023    
024    import java.util.Arrays;
025    import 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     */
048    public 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