KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > soot > toolkits > astmetrics > IdentifiersMetric


1 /* Soot - a J*va Optimization Framework
2  * Copyright (C) 1997-1999 Raja Vallee-Rai
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */

19
20 package soot.toolkits.astmetrics;
21
22 import soot.G;
23 import soot.options.*;
24 import polyglot.ast.*;
25 import polyglot.ast.Node;
26 import polyglot.visit.NodeVisitor;
27
28 import java.util.*;
29 import java.io.*;
30
31 /**
32  * @author Michael Batchelder
33  *
34  * Created on 5-Mar-2006
35  */

36 public class IdentifiersMetric extends ASTMetric {
37
38   double nameComplexity = 0;
39   
40   int dictionarySize = 0;
41   ArrayList dictionary;
42   
43   //cache of names so no recomputation
44
HashMap names;
45   /**
46    * @param astNode
47    *
48    * This metric will take a measure of the "complexity" of each identifier used
49    * within the program. An identifier's complexity is computed as follows:
50    *
51    * First the alpha tokens are parsed by splitting on non-alphas and capitals:
52    *
53    * example identifier: getASTNode alpha tokens: get, AST, Node
54    * example identifier: ___Junk$$name alpha tokens: Junk, name)
55    *
56    * The alpha tokens are then counted and a 'token complexity' is formed by the ratio
57    * of total tokens to the number of tokens found in the dictionary:
58    *
59    * example identifier: getASTNode Total: 3, Found: 2, Complexity: 1.5
60    *
61    * Then the 'character complexity' is computed, which is a ratio of total number of
62    * characters to the number of non-complex characters. Non-complex characters are
63    * those which are NOT part of a multiple string of non-alphas.
64    *
65    * example identifier: ___Junk$$name complex char strings: '___', '$$'
66    * number of non-complex (Junk + name): 8, total: 13, Complexity: 1.625
67    *
68    * Finally, the total identifier complexity is the sum of the token and character
69    * complexities multipled by the 'importance' of an identifier:
70    *
71    * Multipliers are as follows:
72    *
73    * Class multiplier = 3;
74    * Method multiplier = 4;
75    * Field multiplier = 2;
76    * Formal multiplier = 1.5;
77    * Local multiplier = 1;
78    *
79    */

80   public IdentifiersMetric(Node astNode) {
81     super(astNode);
82     
83     initializeDictionary();
84   }
85   
86   private void initializeDictionary() {
87     String JavaDoc line;
88     BufferedReader br;
89     dictionary = new ArrayList();
90     names = new HashMap();
91     
92     InputStream is = ClassLoader.getSystemResourceAsStream("mydict.txt");
93     if (is != null)
94     {
95       br = new BufferedReader(new InputStreamReader(is));
96       
97       try {
98         while ((line = br.readLine()) != null)
99           addWord(line);
100       } catch (IOException ioexc) {}
101     }
102     
103     is = ClassLoader.getSystemResourceAsStream("soot/toolkits/astmetrics/dict.txt");
104     if (is != null)
105     {
106       br = new BufferedReader(new InputStreamReader(is));
107     
108       try {
109         while ((line = br.readLine()) != null)
110           addWord(line.trim().toLowerCase());
111       } catch (IOException ioexc) {}
112     }
113       
114     if ((dictionarySize = dictionary.size()) == 0)
115       G.v().out.println("Error reading in dictionary file(s)");
116     else if (Options.v().verbose())
117       G.v().out.println("Read "+dictionarySize+" words in from dictionary file(s)");
118   }
119   
120   private void addWord(String JavaDoc word) {
121     if (dictionarySize == 0 || word.compareTo((String JavaDoc)dictionary.get(dictionarySize - 1)) > 0) {
122       dictionary.add(word);
123     } else {
124       int i = 0;
125       while (i < dictionarySize && word.compareTo((String JavaDoc)dictionary.get(i)) > 0)
126         i++;
127       
128       if (word.compareTo((String JavaDoc)dictionary.get(i)) == 0)
129         return;
130       
131       dictionary.add(i,word);
132     }
133     
134     dictionarySize++;
135   }
136
137   /* (non-Javadoc)
138    * @see soot.toolkits.astmetrics.ASTMetric#reset()
139    */

140   public void reset() {
141     nameComplexity = 0;
142   }
143
144   /* (non-Javadoc)
145    * @see soot.toolkits.astmetrics.ASTMetric#addMetrics(soot.toolkits.astmetrics.ClassData)
146    */

147   public void addMetrics(ClassData data) {
148     data.addMetric(new MetricData("NameComplexity",new Double JavaDoc(nameComplexity)));
149   }
150   
151   public NodeVisitor enter(Node parent, Node n){
152     double multiplier = 1;
153     String JavaDoc name = null;
154     if(n instanceof ClassDecl){
155       name = ((ClassDecl)n).name();
156       multiplier = 3;
157     } else if (n instanceof MethodDecl) {
158       name = ((MethodDecl)n).name();
159       multiplier = 4;
160     } else if (n instanceof FieldDecl) {
161       name = ((FieldDecl)n).name();
162       multiplier = 2;
163     } else if (n instanceof Formal) { // this is locals and formals
164
name = ((Formal)n).name();
165       multiplier = 1.5;
166     } else if (n instanceof LocalDecl) { // this is locals and formals
167
name = ((LocalDecl)n).name();
168     }
169     
170     if (name!=null)
171     {
172       nameComplexity += (double) (multiplier * computeNameComplexity(name));
173     }
174     return enter(n);
175   }
176
177   private double computeNameComplexity(String JavaDoc name) {
178     if (names.containsKey(name))
179       return ((Double JavaDoc)names.get(name)).doubleValue();
180     
181     int index = 0;
182     ArrayList strings = new ArrayList();
183     
184     // throw out non-alpha characters
185
String JavaDoc tmp = "";
186     for (int i = 0; i < name.length(); i++)
187     {
188       char c = name.charAt(i);
189       if ((c > 64 && c < 91) || (c > 96 && c < 123)) {
190         tmp+=c;
191       } else if (tmp.length() > 0) {
192         strings.add(tmp);
193         tmp = "";
194       }
195     }
196     if (tmp.length()>0)
197       strings.add(tmp);
198     
199     ArrayList tokens = new ArrayList();
200     for (int i = 0; i < strings.size(); i++)
201     {
202       tmp = (String JavaDoc)strings.get(i);
203       while (tmp.length() > 0) {
204         int caps = countCaps(tmp);
205         if (caps == 0)
206         {
207           int idx = findCap(tmp);
208           if (idx > 0) {
209             tokens.add(tmp.substring(0,idx));
210             tmp = tmp.substring(idx,tmp.length());
211           } else {
212             tokens.add(tmp.substring(0,tmp.length()));
213             break;
214           }
215         } else if (caps == 1){
216           int idx = findCap(tmp.substring(1)) + 1;
217           if (idx > 0) {
218             tokens.add(tmp.substring(0,idx));
219             tmp = tmp.substring(idx,tmp.length());
220           } else {
221             tokens.add(tmp.substring(0,tmp.length()));
222             break;
223           }
224         } else {
225           if (caps < tmp.length()) {
226             // count seq of capitals as one token
227
tokens.add(tmp.substring(0, caps - 1).toLowerCase());
228             tmp = tmp.substring(caps);
229           } else {
230             tokens.add(tmp.substring(0, caps).toLowerCase());
231             break;
232           }
233         }
234       }
235     }
236     
237     double words = 0;
238     double complexity = 0;
239     for (int i = 0; i < tokens.size(); i++)
240       if (dictionary.contains(tokens.get(i)))
241         words++;
242       
243     if (words>0)
244       complexity = ((double)tokens.size()) / words;
245     
246     names.put(name,new Double JavaDoc(complexity + computeCharComplexity(name)));
247     
248     return complexity;
249   }
250   
251   private double computeCharComplexity(String JavaDoc name) {
252     int count = 0, index = 0, last = 0, lng = name.length();
253     while (index < lng) {
254       char c = name.charAt(index);
255       if ((c < 65 || c > 90) && (c < 97 || c > 122)) {
256         last++;
257       } else {
258         if (last>1)
259           count += last;
260         last = 0;
261       }
262       index++;
263     }
264     
265     double complexity = lng - count;
266     
267     if (complexity > 0)
268       return (((double)lng) / complexity);
269     else return (double)lng;
270   }
271   
272   
273   /*
274    * @author Michael Batchelder
275    *
276    * Created on 6-Mar-2006
277    *
278    * @param name string to parse
279    * @return number of leading non-alpha chars
280    */

281   private int countNonAlphas(String JavaDoc name) {
282     int chars = 0;
283     while (chars < name.length()) {
284       char c = name.charAt(chars);
285       if ((c < 65 || c > 90) && (c < 97 || c > 122))
286         chars++;
287       else
288         break;
289     }
290     
291     return chars;
292   }
293   
294   /*
295    * @author Michael Batchelder
296    *
297    * Created on 6-Mar-2006
298    *
299    * @param name string to parse
300    * @return number of leading capital letters
301    */

302   private int countCaps(String JavaDoc name) {
303     int caps = 0;
304     while (caps < name.length()) {
305       char c = name.charAt(caps);
306       if (c > 64 && c < 91)
307         caps++;
308       else
309         break;
310     }
311     
312     return caps;
313   }
314   
315   /*
316    * @author Michael Batchelder
317    *
318    * Created on 6-Mar-2006
319    *
320    * @param name string to parse
321    * @return index of first capital letter
322    */

323   private int findCap(String JavaDoc name) {
324     int idx = 0;
325     while (idx < name.length()) {
326       char c = name.charAt(idx);
327       if (c > 64 && c < 91)
328         return idx;
329       else
330         idx++;
331     }
332     
333     return -1;
334   }
335 }
336
Popular Tags