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