The methods to get variables do not instantiate ArrayList each time, only one is...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / MethodDeclaration.java
1 package net.sourceforge.phpdt.internal.compiler.ast;
2
3 import net.sourceforge.phpdt.internal.compiler.parser.OutlineableWithChildren;
4 import net.sourceforge.phpdt.internal.compiler.parser.Outlineable;
5 import net.sourceforge.phpdt.internal.compiler.ast.declarations.VariableUsage;
6 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
7 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
8 import org.eclipse.jface.resource.ImageDescriptor;
9 import org.eclipse.jface.text.Position;
10 import org.eclipse.core.runtime.CoreException;
11
12 import java.util.Hashtable;
13 import java.util.Enumeration;
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import test.PHPParserSuperclass;
18
19 /**
20  * A Method declaration.
21  * @author Matthieu Casanova
22  */
23 public class MethodDeclaration extends Statement implements OutlineableWithChildren {
24
25   /** The name of the method. */
26   public String name;
27   public Hashtable arguments;
28
29
30   public Statement[] statements;
31   public int bodyStart;
32   public int bodyEnd = -1;
33   /** Tell if the method is a class constructor. */
34   public boolean isConstructor;
35
36   /** The parent object. */
37   private Object parent;
38   /** The outlineable children (those will be in the node array too. */
39   private ArrayList children = new ArrayList();
40
41   /** Tell if the method returns a reference. */
42   public boolean reference;
43
44   private Position position;
45
46   public MethodDeclaration(final Object parent,
47                            final String name,
48                            final Hashtable arguments,
49                            final boolean reference,
50                            final int sourceStart,
51                            final int sourceEnd,
52                            final int bodyStart,
53                            final int bodyEnd) {
54     super(sourceStart, sourceEnd);
55     this.name = name;
56     this.arguments = arguments;
57     this.parent = parent;
58     this.reference = reference;
59     this.bodyStart = bodyStart;
60     this.bodyEnd = bodyEnd;
61     position = new Position(sourceStart, sourceEnd);
62   }
63
64   /**
65    * Return method into String, with a number of tabs
66    * @param tab the number of tabs
67    * @return the String containing the method
68    */
69   public String toString(final int tab) {
70     final StringBuffer buff = new StringBuffer(tabString(tab));
71     buff.append(toStringHeader());
72     buff.append(toStringStatements(tab + 1));
73     return buff.toString();
74   }
75
76   public String toStringHeader() {
77     return "function " + toString();
78   }
79
80   /**
81    * Return the statements of the method into Strings
82    * @param tab the number of tabs
83    * @return the String containing the statements
84    */
85   public String toStringStatements(final int tab) {
86     final StringBuffer buff = new StringBuffer(" {"); //$NON-NLS-1$
87     if (statements != null) {
88       for (int i = 0; i < statements.length; i++) {
89         buff.append("\n").append(statements[i].toString(tab)); //$NON-NLS-1$
90         if (!(statements[i] instanceof Block)) {
91           buff.append(";"); //$NON-NLS-1$
92         }
93       }
94     }
95     buff.append("\n").append(tabString(tab == 0 ? 0 : tab - 1)).append("}"); //$NON-NLS-2$ //$NON-NLS-1$
96     return buff.toString();
97   }
98
99   /**
100    * Get the image of a class.
101    * @return the image that represents a php class
102    */
103   public ImageDescriptor getImage() {
104     return PHPUiImages.DESC_FUN;
105   }
106
107   public void setParent(final Object parent) {
108     this.parent = parent;
109   }
110
111   public Object getParent() {
112     return parent;
113   }
114
115   public boolean add(final Outlineable o) {
116     return children.add(o);
117   }
118
119   public Outlineable get(final int index) {
120     return (Outlineable) children.get(index);
121   }
122
123   public int size() {
124     return children.size();
125   }
126
127   public String toString() {
128     final StringBuffer buff = new StringBuffer();
129     if (reference) {
130       buff.append("&");//$NON-NLS-1$
131     }
132     buff.append(name).append("(");//$NON-NLS-1$
133
134     if (arguments != null) {
135       final Enumeration values = arguments.elements();
136       int i = 0;
137       while (values.hasMoreElements()) {
138         final VariableDeclaration o = (VariableDeclaration) values.nextElement();
139         buff.append(o.toStringExpression());
140         if (i != (arguments.size() - 1)) {
141           buff.append(", "); //$NON-NLS-1$
142         }
143         i++;
144       }
145     }
146     buff.append(")"); //$NON-NLS-1$
147     return buff.toString();
148   }
149
150   public Position getPosition() {
151     return position;
152   }
153
154   public List getList() {
155     return children;
156   }
157
158   /** no outside variables. */
159   public void getOutsideVariable(final List list) {
160   }
161
162   public void getModifiedVariable(final List list) {
163   }
164
165   public void getUsedVariable(final List list) {
166   }
167
168   /**
169    * Get global variables (not parameters).
170    */
171   public void getGlobalVariable(final List list) {
172     if (statements != null) {
173       for (int i = 0; i < statements.length; i++) {
174         statements[i].getOutsideVariable(list);
175       }
176     }
177   }
178
179   private void getParameters(final List list) {
180     if (arguments != null) {
181       final Enumeration vars = arguments.elements();
182       while (vars.hasMoreElements()) {
183         final VariableDeclaration variable = (VariableDeclaration) vars.nextElement();
184         list.add(new VariableUsage(variable.name(), variable.sourceStart));
185       }
186     }
187   }
188
189   /**
190    * get the modified variables.
191    */
192   private void getAssignedVariableInCode(final List list) {
193     if (statements != null) {
194       for (int i = 0; i < statements.length; i++) {
195         statements[i].getModifiedVariable(list);
196       }
197     }
198   }
199
200   /**
201    * Get the variables used.
202    */
203   private void getUsedVariableInCode(final List list) {
204     if (statements != null) {
205       for (int i = 0; i < statements.length; i++) {
206         statements[i].getUsedVariable(list);
207       }
208     }
209   }
210
211   private boolean isVariableDeclaredBefore(final List list, final VariableUsage var) {
212     final String name = var.getName();
213     final int pos = var.getStartOffset();
214     for (int i = 0; i < list.size(); i++) {
215       final VariableUsage variableUsage = (VariableUsage) list.get(i);
216       if (variableUsage.getName().equals(name) && variableUsage.getStartOffset() < pos) {
217         return true;
218       }
219     }
220     return false;
221   }
222
223   /** This method will analyze the code. */
224   public void analyzeCode() {
225     if (statements != null) {
226       for (int i = 0; i < statements.length; i++) {
227         statements[i].analyzeCode();
228
229       }
230     }
231
232     final List globalsVars = new ArrayList();
233     getGlobalVariable(globalsVars);
234     final List modifiedVars = new ArrayList();
235     getAssignedVariableInCode(modifiedVars);
236     final List parameters = new ArrayList();
237     getParameters(parameters);
238
239     final List declaredVars = new ArrayList(globalsVars.size() + modifiedVars.size());
240     declaredVars.addAll(globalsVars);
241     declaredVars.addAll(modifiedVars);
242     declaredVars.addAll(parameters);
243
244     final List usedVars = new ArrayList();
245     getUsedVariableInCode(usedVars);
246     final List readOrWriteVars = new ArrayList(modifiedVars.size() + usedVars.size());
247     readOrWriteVars.addAll(modifiedVars);
248     readOrWriteVars.addAll(usedVars);
249
250     //look for used variables that were not declared before
251     findUnusedParameters(readOrWriteVars, parameters);
252     findUnknownUsedVars(usedVars, declaredVars);
253   }
254
255   /**
256    * This method will add a warning on all unused parameters.
257    * @param vars the used variable list
258    * @param parameters the declared variable list
259    */
260   private void findUnusedParameters(final List vars, final List parameters) {
261     for (int i = 0; i < parameters.size(); i++) {
262       final VariableUsage param = ((VariableUsage) parameters.get(i));
263       if (!isVariableInList(param.getName(), vars)) {
264         try {
265           PHPParserSuperclass.setMarker(
266                   "warning, the parameter " + param.getName() + " seems to be never used in your method",
267                   param.getStartOffset(),
268                   param.getStartOffset() + param.getName().length(),
269                   PHPParserSuperclass.WARNING,
270                   "");
271         } catch (CoreException e) {
272           PHPeclipsePlugin.log(e);
273         }
274       }
275     }
276   }
277
278   private boolean isVariableInList(final String name, final List list) {
279     for (int i = 0; i < list.size(); i++) {
280       if (((VariableUsage) list.get(i)).getName().equals(name)) {
281         return true;
282       }
283     }
284     return false;
285   }
286
287   /**
288    * This method will add a warning on all used variables in a method that aren't declared before.
289    * @param usedVars the used variable list
290    * @param declaredVars the declared variable list
291    */
292   private void findUnknownUsedVars(final List usedVars, final List declaredVars) {
293     for (int i = 0; i < usedVars.size(); i++) {
294       final VariableUsage variableUsage = (VariableUsage) usedVars.get(i);
295       if (variableUsage.getName().equals("this")) continue; // this is a special variable
296       if (!isVariableDeclaredBefore(declaredVars, variableUsage)) {
297         try {
298           PHPParserSuperclass.setMarker(
299                   "warning, usage of a variable that seems to be unassigned yet : " + variableUsage.getName(),
300                   variableUsage.getStartOffset(),
301                   variableUsage.getStartOffset() + variableUsage.getName().length(),
302                   PHPParserSuperclass.WARNING,
303                   "");
304         } catch (CoreException e) {
305           PHPeclipsePlugin.log(e);
306         }
307       }
308     }
309   }
310 }