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