1 /*
2  * Copyright 2015-2018 HuntLabs.cn
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 module hunt.sql.ast.expr.SQLMethodInvokeExpr;
17 
18 
19 
20 import hunt.sql.SQLUtils;
21 import hunt.sql.ast;
22 // import hunt.sql.dialect.oracle.visitor.OracleASTVisitor;
23 import hunt.sql.visitor.SQLASTVisitor;
24 import hunt.sql.util.FnvHash;
25 import hunt.collection;
26 import hunt.sql.ast.expr.SQLIdentifierExpr;
27 import hunt.sql.ast.expr.SQLDateExpr;
28 import hunt.util.StringBuilder;
29 
30 public class SQLMethodInvokeExpr : SQLExprImpl , SQLReplaceable//, Serializable 
31 {
32     private string              name;
33     private SQLExpr             owner;
34     private List!SQLExpr parameters;
35 
36     private SQLExpr             from;
37     private SQLExpr             using;
38     private SQLExpr             _for;
39 
40     private string              trimOption;
41 
42     private long                nameHashCode64;
43 
44     public this(){
45         parameters       = new ArrayList!SQLExpr();
46     }
47 
48     public this(string methodName){
49         this();
50         this.name = methodName;
51     }
52 
53     public this(string methodName, long nameHashCode64){
54         this();
55         this.name = methodName;
56         this.nameHashCode64 = nameHashCode64;
57     }
58 
59     public this(string methodName, SQLExpr owner){
60         this();
61         this.name = methodName;
62         setOwner(owner);
63     }
64 
65     public this(string methodName, SQLExpr owner, SQLExpr[] params...){
66         this();
67         this.name = methodName;
68         setOwner(owner);
69         foreach (SQLExpr param ; params) {
70             this.addParameter(param);
71         }
72     }
73 
74     public long methodNameHashCode64() {
75         if (nameHashCode64 == 0
76                 && name !is null) {
77             nameHashCode64 = FnvHash.hashCode64(name);
78         }
79         return nameHashCode64;
80     }
81 
82     public string getMethodName() {
83         return this.name;
84     }
85 
86     public void setMethodName(string methodName) {
87         this.name = methodName;
88         this.nameHashCode64 = 0L;
89     }
90 
91     public SQLExpr getOwner() {
92         return this.owner;
93     }
94 
95     public void setOwner(SQLExpr owner) {
96         if (owner !is null) {
97             owner.setParent(this);
98         }
99         this.owner = owner;
100     }
101 
102     public SQLExpr getFrom() {
103         return from;
104     }
105 
106     public void setFrom(SQLExpr x) {
107         if (x !is null) {
108             x.setParent(this);
109         }
110         this.from = x;
111     }
112 
113     public List!SQLExpr getParameters() {
114         return this.parameters;
115     }
116     
117     public void addParameter(SQLExpr param) {
118         if (param !is null) {
119             param.setParent(this);
120         }
121         this.parameters.add(param);
122     }
123 
124     public void addArgument(SQLExpr arg) {
125         if (arg !is null) {
126             arg.setParent(this);
127         }
128         this.parameters.add(arg);
129     }
130 
131     override public void output(StringBuilder buf) {
132         if (this.owner !is null) {
133             this.owner.output(buf);
134             buf.append(".");
135         }
136 
137         buf.append(this.name);
138         buf.append("(");
139         for (int i = 0, size = this.parameters.size(); i < size; ++i) {
140             if (i != 0) {
141                 buf.append(", ");
142             }
143 
144             this.parameters.get(i).output(buf);
145         }
146         buf.append(")");
147     }
148 
149     override  protected void accept0(SQLASTVisitor visitor) {
150         if (visitor.visit(this)) {
151             acceptChild(visitor, this.owner);
152             acceptChild!SQLExpr(visitor, this.parameters);
153             acceptChild(visitor, this.from);
154             acceptChild(visitor, this.using);
155             acceptChild(visitor, this._for);
156         }
157 
158         visitor.endVisit(this);
159     }
160 
161     override public List!SQLObject getChildren() {
162         if (this.owner is null) {
163             return cast(List!SQLObject)this.parameters;
164         }
165 
166         List!SQLObject children = new ArrayList!SQLObject();
167         children.add(owner);
168         children.addAll(cast(List!SQLObject)(this.parameters));
169         return children;
170     }
171 
172     // protected void accept0(OracleASTVisitor visitor) {
173     //     if (visitor.visit(this)) {
174     //         acceptChild(visitor, this.owner);
175     //         acceptChild(visitor, this.parameters);
176     //         acceptChild(visitor, this.from);
177     //         acceptChild(visitor, this.using);
178     //         acceptChild(visitor, this._for);
179     //     }
180 
181     //     visitor.endVisit(this);
182     // }
183 
184    override
185     public bool opEquals(Object o) {
186         if (this == o) return true;
187         if (o is null || typeid(this) != typeid(o)) return false;
188 
189         SQLMethodInvokeExpr that = cast(SQLMethodInvokeExpr) o;
190 
191         if (name !is null ? !(name == that.name) : that.name !is null) return false;
192         if (owner !is null ? !(cast(Object)(owner)).opEquals(cast(Object)(that.owner)) : that.owner !is null) return false;
193         if (parameters !is null ? !(cast(Object)(parameters)).opEquals(cast(Object)(that.parameters)) : that.parameters !is null) return false;
194         return from !is null ? (cast(Object)(from)).opEquals(cast(Object)(that.from)) : that.from is null;
195 
196     }
197 
198    override
199     public size_t toHash() @trusted nothrow {
200         size_t result = name !is null ? hashOf(name) : 0;
201         result = 31 * result + (owner !is null ? (cast(Object)owner).toHash() : 0);
202         result = 31 * result + (parameters !is null ? (cast(Object)parameters).toHash() : 0);
203         result = 31 * result + (from !is null ? (cast(Object)from).toHash() : 0);
204         return result;
205     }
206 
207     override public SQLMethodInvokeExpr clone() {
208         SQLMethodInvokeExpr x = new SQLMethodInvokeExpr();
209 
210         x.name = name;
211 
212         if (owner !is null) {
213             x.setOwner(owner.clone());
214         }
215 
216         foreach (SQLExpr param ; parameters) {
217             x.addParameter(param.clone());
218         }
219 
220         if (from !is null) {
221             x.setFrom(from.clone());
222         }
223 
224         if (using !is null) {
225             x.setUsing(using.clone());
226         }
227 
228         return x;
229     }
230 
231    override
232     public bool replace(SQLExpr expr, SQLExpr target) {
233         if (target is null) {
234             return false;
235         }
236 
237         for (int i = 0; i < parameters.size(); ++i) {
238             if (parameters.get(i) == expr) {
239                 parameters.set(i, target);
240                 target.setParent(this);
241                 return true;
242             }
243         }
244 
245         if (from == expr) {
246             setFrom(target);
247             return true;
248         }
249 
250         if (using == expr) {
251             setUsing(target);
252             return true;
253         }
254 
255         if (_for == expr) {
256             setFor(target);
257             return true;
258         }
259 
260         return false;
261     }
262 
263     public bool match(string owner, string function_p) {
264         if (function_p is null) {
265             return false;
266         }
267 
268         if (!SQLUtils.nameEquals(function_p, name)) {
269             return false;
270         }
271 
272         if (owner is null && this.owner is null) {
273             return true;
274         }
275 
276         if (owner is null || this.owner is null) {
277             return false;
278         }
279         auto obj = cast(SQLIdentifierExpr) this.owner;
280         if (obj !is null) {
281             return SQLUtils.nameEquals(obj.name, owner);
282         }
283 
284         return false;
285     }
286 
287     override public SQLDataType  computeDataType() {
288         if (SQLUtils.nameEquals("to_date", name)
289                 || SQLUtils.nameEquals("add_months", name)) {
290             return SQLDateExpr.DEFAULT_DATA_TYPE;
291         }
292 
293         if (parameters.size() == 1) {
294             if (SQLUtils.nameEquals("trunc", name)) {
295                 return parameters.get(0).computeDataType();
296             }
297         } else if (parameters.size() == 2) {
298             SQLExpr param0 = parameters.get(0);
299             SQLExpr param1 = parameters.get(1);
300             if (SQLUtils.nameEquals("nvl", name) || SQLUtils.nameEquals("ifnull", name)) {
301                 SQLDataType dataType = param0.computeDataType();
302                 if (dataType !is null) {
303                     return dataType;
304                 }
305 
306                 return param1.computeDataType();
307             }
308         }
309         return null;
310     }
311 
312     public SQLExpr getUsing() {
313         return using;
314     }
315 
316     public void setUsing(SQLExpr x) {
317         if (x !is null) {
318             x.setParent(this);
319         }
320         this.using = x;
321     }
322 
323     public SQLExpr getFor() {
324         return _for;
325     }
326 
327     public void setFor(SQLExpr x) {
328         if (x !is null) {
329             x.setParent(this);
330         }
331         this._for = x;
332     }
333 
334     public string getTrimOption() {
335         return trimOption;
336     }
337 
338     public void setTrimOption(string trimOption) {
339         this.trimOption = trimOption;
340     }
341 }