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