Extended the builder with an index file generator for PHP class- and function-names.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / builder / IdentifierIndexManager.java
1 package net.sourceforge.phpeclipse.builder;
2
3 import java.io.BufferedReader;
4 import java.io.FileNotFoundException;
5 import java.io.FileReader;
6 import java.io.FileWriter;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.HashMap;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.StringTokenizer;
15
16 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
17 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
18 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
19 import net.sourceforge.phpdt.internal.compiler.parser.SyntaxError;
20 import net.sourceforge.phpeclipse.mover.obfuscator.PHPIdentifier;
21
22 import org.eclipse.core.resources.IFile;
23 import org.eclipse.core.runtime.CoreException;
24
25 /**
26  * Manages the identifer index information for a specific project
27  *
28  */
29 public class IdentifierIndexManager {
30
31   public class LineCreator implements ITerminalSymbols {
32
33     private Scanner fScanner;
34     private int fToken;
35
36     public LineCreator() {
37       fScanner = new Scanner(false, false);
38     }
39     /**
40      * gets the next token from input
41      */
42     private void getNextToken() {
43
44       try {
45         fToken = fScanner.getNextToken();
46         if (Scanner.DEBUG) {
47           int currentEndPosition = fScanner.getCurrentTokenEndPosition();
48           int currentStartPosition = fScanner.getCurrentTokenStartPosition();
49
50           System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
51           System.out.println(fScanner.toStringAction(fToken));
52         }
53         return;
54       } catch (InvalidInputException e) {
55         // ignore errors
56       }
57       fToken = TokenNameERROR;
58     }
59
60     private void parseDeclarations(StringBuffer buf, boolean goBack) {
61       char[] ident;
62       int counter = 0;
63
64       try {
65         while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
66           if (fToken == TokenNamevar) {
67             getNextToken();
68             if (fToken == TokenNameVariable) {
69               ident = fScanner.getCurrentIdentifierSource();
70               buf.append("\tv");
71               buf.append(ident);
72
73               getNextToken();
74             }
75           } else if (fToken == TokenNamefunction) {
76             getNextToken();
77             if (fToken == TokenNameAND) {
78               getNextToken();
79             }
80             if (fToken == TokenNameIdentifier) {
81               ident = fScanner.getCurrentIdentifierSource();
82               buf.append("\tm");
83               buf.append(ident);
84               getNextToken();
85               parseDeclarations(buf, true);
86             }
87           } else if (fToken == TokenNameclass) {
88             getNextToken();
89             if (fToken == TokenNameIdentifier) {
90               ident = fScanner.getCurrentIdentifierSource();
91               buf.append("\tc");
92               buf.append(ident);
93               getNextToken();
94
95               //skip tokens for classname, extends and others until we have the opening '{'
96               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
97                 getNextToken();
98               }
99               parseDeclarations(buf, true);
100             }
101           } else if ((fToken == TokenNameLBRACE) || (fToken == TokenNameDOLLAR_LBRACE)) {
102             getNextToken();
103             counter++;
104           } else if (fToken == TokenNameRBRACE) {
105             getNextToken();
106             --counter;
107             if (counter == 0 && goBack) {
108               return;
109             }
110           } else {
111             getNextToken();
112           }
113         }
114       } catch (SyntaxError e) {
115         // TODO Auto-generated catch block
116         e.printStackTrace();
117       }
118     }
119
120     public void parseIdentifiers(char[] charArray, StringBuffer buf) {
121       char[] ident;
122       String identifier;
123       int counter = 0;
124
125       fScanner.setSource(charArray);
126       fScanner.setPHPMode(false);
127       fToken = TokenNameEOF;
128       getNextToken();
129
130       try {
131         while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
132           if (fToken == TokenNamefunction) {
133             getNextToken();
134             if (fToken == TokenNameAND) {
135               getNextToken();
136             }
137             if (fToken == TokenNameIdentifier) {
138               ident = fScanner.getCurrentIdentifierSource();
139               buf.append("\tf");
140               buf.append(ident);
141               getNextToken();
142               parseDeclarations(buf, true);
143             }
144           } else if (fToken == TokenNameclass) {
145             getNextToken();
146             if (fToken == TokenNameIdentifier) {
147               ident = fScanner.getCurrentIdentifierSource();
148               buf.append("\tc");
149               buf.append(ident);
150               getNextToken();
151
152               //skip fTokens for classname, extends and others until we have the opening '{'
153               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
154                 getNextToken();
155               }
156
157               parseDeclarations(buf, true);
158
159             }
160           } else {
161             getNextToken();
162           }
163         }
164       } catch (SyntaxError e) {
165         // TODO Auto-generated catch block
166         e.printStackTrace();
167       }
168     }
169   }
170
171   private HashMap fFileMap;
172   private String fFilename;
173   private HashMap fIndentifierMap;
174
175   public IdentifierIndexManager(String filename) {
176     fFilename = filename;
177     initialize();
178     readFile();
179   }
180
181   /**
182    * Add the information for a given IFile resource
183    *
184    */
185   public void addFile(IFile fileToParse) {
186     InputStream iStream;
187     LineCreator lineCreator = new LineCreator();
188     try {
189       iStream = fileToParse.getContents();
190
191       StringBuffer buf = new StringBuffer();
192       int c0;
193       try {
194         while ((c0 = iStream.read()) != (-1)) {
195           buf.append((char) c0);
196         }
197       } catch (IOException e) {
198         return;
199       }
200
201       StringBuffer lineBuffer = new StringBuffer();
202       //      lineBuffer.append(fileToParse.getLocation().toString());
203       lineBuffer.append(fileToParse.getFullPath().toString());
204       int lineLength = lineBuffer.length();
205       lineCreator.parseIdentifiers(buf.toString().toCharArray(), lineBuffer);
206       if (lineLength != lineBuffer.length()) {
207         addLine(lineBuffer.toString());
208       }
209     } catch (CoreException e1) {
210       // TODO Auto-generated catch block
211       e1.printStackTrace();
212     }
213   }
214
215   /**
216    * Adds a line of the index file for function, class, class-method and class-variable names
217    * 
218    * @param line
219    */
220   private void addLine(String line) {
221     StringTokenizer tokenizer;
222     String phpFileName = null;
223     String token;
224     String identifier = null;
225     String classname = null;
226     PHPIdentifier phpIdentifier = null;
227     boolean tokenExists = false;
228
229     tokenizer = new StringTokenizer(line, "\t");
230     // first token contains the filename:
231     if (tokenizer.hasMoreTokens()) {
232       phpFileName = tokenizer.nextToken();
233       //System.out.println(token);
234     } else {
235       return;
236     }
237     // all the other tokens are identifiers:
238     while (tokenizer.hasMoreTokens()) {
239       token = tokenizer.nextToken();
240       //System.out.println(token);
241       switch (token.charAt(0)) {
242         case 'c' : // class name
243           identifier = token.substring(1);
244           classname = identifier;
245           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
246           break;
247         case 'f' : // function name
248           identifier = token.substring(1);
249           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
250           break;
251         case 'm' : //method inside a class
252           identifier = token.substring(1);
253           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
254           break;
255         case 'v' : // variable inside a class
256           identifier = token.substring(1);
257           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
258           break;
259         default :
260           identifier = null;
261           phpIdentifier = null;
262           classname = null;
263       }
264       if (identifier != null && phpIdentifier != null) {
265         tokenExists = true;
266         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
267         if (list == null) {
268           list = new ArrayList();
269           list.add(phpIdentifier);
270           fIndentifierMap.put(identifier, list);
271         } else {
272           boolean flag = false;
273           for (int i = 0; i < list.size(); i++) {
274             if (list.get(i).equals(phpIdentifier)) {
275               flag = true;
276               break;
277             }
278           }
279           if (flag == false) {
280             list.add(phpIdentifier);
281           }
282         }
283       }
284     }
285     if (tokenExists) {
286       fFileMap.put(phpFileName, line);
287     }
288   }
289
290   /**
291    * Change the information for a given IFile resource
292    *
293    */
294   public void changeFile(IFile fileToParse) {
295     removeFile(fileToParse);
296     addFile(fileToParse);
297   }
298
299   /**
300    * Get a list of all PHPIdentifierLocation object's associated with an identifier
301    * 
302    * @param identifier
303    * @return
304    */
305   public List getLocations(String identifier) {
306     return (List) fIndentifierMap.get(identifier);
307   }
308
309   /**
310    * Initialize (i.e. clear) the current index information
311    *
312    */
313   public void initialize() {
314     fIndentifierMap = new HashMap();
315     fFileMap = new HashMap();
316   }
317
318   private void readFile() {
319
320     FileReader fileReader;
321     try {
322       fileReader = new FileReader(fFilename);
323
324       BufferedReader bufferedReader = new BufferedReader(fileReader);
325
326       String line;
327       while (bufferedReader.ready()) {
328         // all entries for one file are in a line
329         // separated by tabs !
330         line = bufferedReader.readLine();
331         addLine(line);
332       }
333
334       fileReader.close();
335     } catch (FileNotFoundException e) {
336       // ignore this
337       // TODO DialogBox which asks the user if she/he likes to build new index?
338     } catch (IOException e) {
339       // TODO Auto-generated catch block
340       e.printStackTrace();
341     }
342
343   }
344
345   /**
346    * Remove the information for a given IFile resource
347    *
348    */
349   public void removeFile(IFile fileToParse) {
350     //    String line = (String) fFileMap.get(fileToParse.getLocation().toString());
351     String line = (String) fFileMap.get(fileToParse.getFullPath().toString());
352     if (line != null) {
353       removeLine(line);
354     }
355   }
356
357   /**
358    * Removes a line of the index file for function, class, class-method and class-variable names
359    * 
360    * @param line
361    */
362   private void removeLine(String line) {
363     StringTokenizer tokenizer;
364     String phpFileName = null;
365     String token;
366     String identifier = null;
367     String classname = null;
368     PHPIdentifier phpIdentifier = null;
369     boolean tokenExists = false;
370
371     tokenizer = new StringTokenizer(line, "\t");
372     // first token contains the filename:
373     if (tokenizer.hasMoreTokens()) {
374       phpFileName = tokenizer.nextToken();
375       //System.out.println(token);
376     } else {
377       return;
378     }
379     // all the other tokens are identifiers:
380     while (tokenizer.hasMoreTokens()) {
381       token = tokenizer.nextToken();
382       //System.out.println(token);
383       switch (token.charAt(0)) {
384         case 'c' : // class name
385           identifier = token.substring(1);
386           classname = identifier;
387           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
388           break;
389         case 'f' : // function name
390           identifier = token.substring(1);
391           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
392           break;
393         case 'm' : //method inside a class
394           identifier = token.substring(1);
395           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
396           break;
397         case 'v' : // variable inside a class
398           identifier = token.substring(1);
399           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
400           break;
401         default :
402           identifier = null;
403           phpIdentifier = null;
404           classname = null;
405       }
406       if (identifier != null && phpIdentifier != null) {
407         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
408         if (list == null) {
409         } else {
410           for (int i = 0; i < list.size(); i++) {
411             if (list.get(i).equals(phpIdentifier)) {
412               list.remove(i);
413               break;
414             }
415           }
416           if (list.size() == 0) {
417             fIndentifierMap.remove(identifier);
418           }
419         }
420       }
421     }
422     fFileMap.remove(phpFileName);
423   }
424
425   /**
426    * Save the current index information in the projects index file
427    *
428    */
429   public void writeFile() {
430     FileWriter fileWriter;
431     try {
432       fileWriter = new FileWriter(fFilename);
433       String line;
434       Collection collection = fFileMap.values();
435       Iterator iterator = collection.iterator();
436       while (iterator.hasNext()) {
437         line = (String) iterator.next();
438         fileWriter.write(line + '\n');
439       }
440       fileWriter.close();
441     } catch (IOException e) {
442       // TODO Auto-generated catch block
443       e.printStackTrace();
444     }
445   }
446 }