KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > Arity


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.ruby;
20
21 import java.util.List JavaDoc;
22
23 import org.jruby.ast.ArgsCatNode;
24 import org.jruby.ast.ArgsNode;
25 import org.jruby.ast.ArgumentNode;
26 import org.jruby.ast.CallNode;
27 import org.jruby.ast.DefnNode;
28 import org.jruby.ast.DefsNode;
29 import org.jruby.ast.FCallNode;
30 import org.jruby.ast.ListNode;
31 import org.jruby.ast.LocalAsgnNode;
32 import org.jruby.ast.Node;
33 import org.jruby.ast.SplatNode;
34 import org.jruby.ast.VCallNode;
35
36
37 /**
38  * The arity of a method is the number of arguments it takes.
39  *
40  * JRuby already has an Arity class (org.jruby.runtime.Arity), but
41  * it doesn't have all we need - such as a maximum number of arguments.
42  *
43  * @todo I handle optional arguments and splats (*), but what about blocks?
44  * def foo(arg1, arg2 = deflt, *rest, &block)
45  *
46  * @author Tor Norbye
47  */

48 public final class Arity {
49     /**
50      * Represents unknown arity, for example the arity of a method that
51      * we're referring to as a symbol, such as "private :foo".
52      */

53     public static final Arity UNKNOWN = new Arity(-1, -1);
54
55     /**
56      * Minimum number of arguments required by this method
57      */

58     private int min;
59
60     /**
61      * Maximum number of arguments required by this method. Use {@link Integer#MAX_VALUE} to denote no
62      * upper bound (e.g. *args).
63      */

64     private int max;
65
66     private Arity(int min, int max) {
67         // Examples:
68
// call: foo(bar,baz) -> Arity(2,2)
69
// call: foo(bar,*baz) -> Arity(1,Integer.MAX_VALUE)
70
// method: foo(bar,baz) -> Arity(2,2)
71
// method: foo(bar,baz=5) -> Arity(1,2)
72
this.min = min;
73         this.max = max;
74     }
75
76     public static Arity getCallArity(Node call) {
77         assert call instanceof CallNode || call instanceof VCallNode || call instanceof FCallNode;
78
79         Arity arity = new Arity(0, 0);
80         arity.initializeFromCall(call);
81
82         if (arity.min == -1) {
83             return UNKNOWN;
84         } else {
85             return arity;
86         }
87     }
88
89     public static boolean callHasArguments(Node call) {
90         assert call instanceof CallNode || call instanceof VCallNode || call instanceof FCallNode;
91
92         return getCallArity(call).min > 0;
93     }
94
95     @SuppressWarnings JavaDoc("unchecked")
96     private void initializeFromCall(Node node) {
97         if (node instanceof FCallNode) {
98             Node argsNode = ((FCallNode)node).getArgsNode();
99
100             initializeFromCall(argsNode);
101         } else if (node instanceof LocalAsgnNode) {
102             if (max < Integer.MAX_VALUE) {
103                 max++;
104             }
105         } else if (node instanceof ArgsCatNode) {
106             ArgsCatNode args = (ArgsCatNode)node;
107             initializeFromCall(args.getFirstNode());
108             // ArgsCatNode seems to be used only to append splats
109
// but I don't get a splat node. So just pretend I did.
110
//initializeFromCall(args.getSecondNode());
111
max = Integer.MAX_VALUE;
112         } else if (node instanceof ArgsNode) {
113             ArgsNode args = (ArgsNode)node;
114
115             if (args != null) {
116                 int value = args.getArity().getValue();
117
118                 if (value < 0) {
119                     value = -(1 + value);
120                 }
121
122                 min = max = value;
123             }
124         } else if (node instanceof SplatNode) {
125             // Flexible number of arguments
126
max = Integer.MAX_VALUE;
127         } else if (node instanceof CallNode) {
128             Node argsNode = ((CallNode)node).getArgsNode();
129             initializeFromCall(argsNode);
130         } else if (node instanceof VCallNode) {
131             List JavaDoc<Node> children = node.childNodes();
132
133             for (Node child : children) {
134                 initializeFromCall(child);
135             }
136         } else if (node instanceof ListNode) {
137             List JavaDoc<Node> children = node.childNodes();
138
139             for (Node child : children) {
140                 if (child instanceof VCallNode) {
141                     // The argument is a method call - just count it as a regular argument
142
min++;
143
144                     if (max < Integer.MAX_VALUE) {
145                         max++;
146                     }
147                 } else {
148                     initializeFromCall(child);
149                 }
150             }
151         } else {
152             //assert (node instanceof LocalVarNode || node instanceof SymbolNode || node instanceof FixnumNode || ...
153
min++;
154
155             if (max < Integer.MAX_VALUE) {
156                 max++;
157             }
158         }
159     }
160
161     @SuppressWarnings JavaDoc("unchecked")
162     public static Arity getDefArity(Node method) {
163         assert method instanceof DefsNode || method instanceof DefnNode;
164
165         Arity arity = new Arity(0, 0);
166
167         List JavaDoc<Node> nodes = (List JavaDoc<Node>)method.childNodes();
168
169         for (Node c : nodes) {
170             if (c instanceof ArgsNode) {
171                 arity.initializeFromDef(c);
172
173                 break;
174             }
175         }
176
177         if (arity.min == -1) {
178             return UNKNOWN;
179         } else {
180             return arity;
181         }
182     }
183
184     @SuppressWarnings JavaDoc("unchecked")
185     private void initializeFromDef(Node node) {
186         if (node instanceof ArgsNode) {
187             ArgsNode argsNode = (ArgsNode)node;
188
189             if (argsNode.getArgs() != null) {
190                 initializeFromDef(argsNode.getArgs());
191             }
192
193             if (argsNode.getOptArgs() != null) {
194                 initializeFromDef(argsNode.getOptArgs());
195             }
196
197             if (argsNode.getRestArg() > 0) {
198                 max = Integer.MAX_VALUE;
199             }
200
201             // TODO:
202
//argsNode.getBlockArgNode()
203
} else if (node instanceof ArgumentNode) {
204             min++;
205             max++;
206         } else if (node instanceof LocalAsgnNode) {
207             max++;
208         } else if (node instanceof ListNode) {
209             List JavaDoc<Node> children = node.childNodes();
210
211             for (Node child : children) {
212                 initializeFromDef(child);
213             }
214         }
215     }
216
217     /**
218      * Return true iff the given call arity matches the given method arity.
219      *
220      * This isn't fool-proof; if you're passing around "*args" I treat
221      * these as unknown sizes that will match - but at runtime it can fail.
222      * For example, def jump(*args) test(*args) end; def test(foo) end; jump
223      * will look compatible but fail since you will wind up calling test with
224      * 0 arguments...
225      */

226     public static boolean matches(Arity call, Arity method) {
227         // If we don't know the arity for either side, consider it a match.
228
// This may mean we get false positives (e.g. a method call doesn't
229
// really a hit a method, so we may think it is used where it is not)
230
if ((call == UNKNOWN) || (method == UNKNOWN)) {
231             return true;
232         }
233
234         if (call.min < method.min) {
235             return false;
236         }
237
238         if (call.max == Integer.MAX_VALUE) {
239             // Unknown number of args - assume it's okay
240
return true;
241         }
242
243         if (call.max > method.max) {
244             return false;
245         }
246
247         return true;
248     }
249
250     @Override JavaDoc
251     public String JavaDoc toString() {
252         return "Arity(" + min + ":" + ((max == Integer.MAX_VALUE) ? "unlimited" : max) + ")";
253     }
254
255     public static Arity createTestArity(int min, int max) {
256         return new Arity(min, max);
257     }
258 }
259
Popular Tags