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