some bugfixes
[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(true, false);
38     }
39
40     /**
41      * Add the information of the current identifier to the line
42      * 
43      * @param typeOfIdentifier the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod, 'v'ariable)
44      * @param identifier current identifier
45      * @param line Buffer for the current index line
46      * @param phpdocOffset the offset of the PHPdoc comment if available
47      * @param phpdocLength the length of the PHPdoc comment if available
48      */
49     private void addIdentifierInformation(
50       char typeOfIdentifier,
51       char[] identifier,
52       StringBuffer line,
53       int phpdocOffset,
54       int phpdocLength) {
55
56       line.append('\t');
57       line.append(typeOfIdentifier);
58       line.append(identifier);
59       line.append("\to"); // Offset 
60       line.append(fScanner.getCurrentTokenStartPosition());
61       if (phpdocOffset >= 0) {
62         line.append("\tp"); // phpdoc offset
63         line.append(phpdocOffset);
64         line.append("\tl"); // phpdoc length
65         line.append(phpdocLength);
66       }
67
68     }
69     /**
70      * Get the next token from input
71      */
72     private void getNextToken() {
73       try {
74         fToken = fScanner.getNextToken();
75         if (Scanner.DEBUG) {
76           int currentEndPosition = fScanner.getCurrentTokenEndPosition();
77           int currentStartPosition = fScanner.getCurrentTokenStartPosition();
78
79           System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
80           System.out.println(fScanner.toStringAction(fToken));
81         }
82         return;
83       } catch (InvalidInputException e) {
84         // ignore errors
85       }
86       fToken = TokenNameERROR;
87     }
88
89     private void parseDeclarations(StringBuffer buf, boolean goBack) {
90       char[] ident;
91                         char[] classVariable;
92       int counter = 0;
93       int phpdocOffset = -1;
94       int phpdocLength = -1;
95
96       try {
97         while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
98           phpdocOffset = -1;
99           if (fToken == TokenNameCOMMENT_PHPDOC) {
100             phpdocOffset = fScanner.getCurrentTokenStartPosition();
101             phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
102             getNextToken();
103             if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
104               break;
105             }
106           }
107           if (fToken == TokenNamevar) {
108             getNextToken();
109             if (fToken == TokenNameVariable) {
110               ident = fScanner.getCurrentIdentifierSource();
111                                                         classVariable = new char[ident.length-1];
112                                                         System.arraycopy(ident, 1, classVariable, 0, ident.length-1);
113               addIdentifierInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
114               getNextToken();
115             }
116           } else if (fToken == TokenNamefunction) {
117             getNextToken();
118             if (fToken == TokenNameAND) {
119               getNextToken();
120             }
121             if (fToken == TokenNameIdentifier) {
122               ident = fScanner.getCurrentIdentifierSource();
123               addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
124               getNextToken();
125               parseDeclarations(buf, true);
126             }
127           } else if (fToken == TokenNameclass) {
128             getNextToken();
129             if (fToken == TokenNameIdentifier) {
130               ident = fScanner.getCurrentIdentifierSource();
131               addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
132               getNextToken();
133
134               //skip tokens for classname, extends and others until we have the opening '{'
135               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
136                 getNextToken();
137               }
138               parseDeclarations(buf, true);
139             }
140           } else if (fToken == TokenNamedefine) {
141             getNextToken();
142             if (fToken == TokenNameLPAREN) {
143               getNextToken();
144               if (fToken == TokenNameStringLiteral) {
145                 ident = fScanner.getCurrentStringLiteralSource();
146                 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
147                 getNextToken();
148               }
149             }
150           } else if ((fToken == TokenNameLBRACE) || (fToken == TokenNameDOLLAR_LBRACE)) {
151             getNextToken();
152             counter++;
153           } else if (fToken == TokenNameRBRACE) {
154             getNextToken();
155             --counter;
156             if (counter == 0 && goBack) {
157               return;
158             }
159           } else {
160             getNextToken();
161           }
162         }
163       } catch (SyntaxError e) {
164         // TODO Auto-generated catch block
165         e.printStackTrace();
166       }
167     }
168
169     public void parseIdentifiers(char[] charArray, StringBuffer buf) {
170       char[] ident;
171       String identifier;
172       int counter = 0;
173       int phpdocOffset = -1;
174       int phpdocLength = -1;
175
176       fScanner.setSource(charArray);
177       fScanner.setPHPMode(false);
178       fToken = TokenNameEOF;
179       getNextToken();
180
181       try {
182         while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
183           phpdocOffset = -1;
184           if (fToken == TokenNameCOMMENT_PHPDOC) {
185             phpdocOffset = fScanner.getCurrentTokenStartPosition();
186             phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
187             getNextToken();
188             if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
189               break;
190             }
191           }
192           if (fToken == TokenNamefunction) {
193             getNextToken();
194             if (fToken == TokenNameAND) {
195               getNextToken();
196             }
197             if (fToken == TokenNameIdentifier) {
198               ident = fScanner.getCurrentIdentifierSource();
199               addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
200               getNextToken();
201               parseDeclarations(buf, true);
202             }
203           } else if (fToken == TokenNameclass) {
204             getNextToken();
205             if (fToken == TokenNameIdentifier) {
206               ident = fScanner.getCurrentIdentifierSource();
207               addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
208               getNextToken();
209
210               //skip fTokens for classname, extends and others until we have the opening '{'
211               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
212                 getNextToken();
213               }
214
215               parseDeclarations(buf, true);
216
217             }
218           } else if (fToken == TokenNamedefine) {
219             getNextToken();
220             if (fToken == TokenNameLPAREN) {
221               getNextToken();
222               if (fToken == TokenNameStringLiteral) {
223                 ident = fScanner.getCurrentStringLiteralSource();
224                 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
225                 getNextToken();
226               }
227             }
228           } else {
229             getNextToken();
230           }
231         }
232       } catch (SyntaxError e) {
233         // TODO Auto-generated catch block
234         e.printStackTrace();
235       }
236     }
237   }
238
239   private HashMap fFileMap;
240   private String fFilename;
241   private HashMap fIndentifierMap;
242
243   public IdentifierIndexManager(String filename) {
244     fFilename = filename;
245     initialize();
246     readFile();
247   }
248
249   /**
250    * Add the information for a given IFile resource
251    *
252    */
253   public void addFile(IFile fileToParse) {
254     InputStream iStream;
255     LineCreator lineCreator = new LineCreator();
256     try {
257       iStream = fileToParse.getContents();
258
259       StringBuffer buf = new StringBuffer();
260       int c0;
261       try {
262         while ((c0 = iStream.read()) != (-1)) {
263           buf.append((char) c0);
264         }
265       } catch (IOException e) {
266         return;
267       }
268
269       StringBuffer lineBuffer = new StringBuffer();
270       //      lineBuffer.append(fileToParse.getLocation().toString());
271       lineBuffer.append(fileToParse.getFullPath().toString());
272       int lineLength = lineBuffer.length();
273       lineCreator.parseIdentifiers(buf.toString().toCharArray(), lineBuffer);
274       if (lineLength != lineBuffer.length()) {
275         addLine(lineBuffer.toString());
276       }
277     } catch (CoreException e1) {
278       // TODO Auto-generated catch block
279       e1.printStackTrace();
280     }
281   }
282
283   /**
284    * Adds a line of the index file for function, class, class-method and class-variable names
285    * 
286    * @param line
287    */
288   private void addLine(String line) {
289     StringTokenizer tokenizer;
290     String phpFileName = null;
291     String token;
292     String identifier = null;
293     String classname = null;
294     String offset = null;
295     PHPIdentifierLocation phpIdentifier = null;
296     boolean tokenExists = false;
297
298     tokenizer = new StringTokenizer(line, "\t");
299     // first token contains the filename:
300     if (tokenizer.hasMoreTokens()) {
301       phpFileName = tokenizer.nextToken();
302       //System.out.println(token);
303     } else {
304       return;
305     }
306     // all the other tokens are identifiers:
307     while (tokenizer.hasMoreTokens()) {
308       token = tokenizer.nextToken();
309       //System.out.println(token);
310       switch (token.charAt(0)) {
311         case 'c' : // class name
312           identifier = token.substring(1);
313           classname = identifier;
314           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
315           break;
316         case 'd' : // define
317           identifier = token.substring(1);
318           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
319           break;
320         case 'f' : // function name
321           identifier = token.substring(1);
322           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
323           break;
324         case 'm' : //method inside a class
325           identifier = token.substring(1);
326           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
327           break;
328         case 'v' : // variable inside a class
329           identifier = token.substring(1);
330           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
331           break;
332         case 'o' : // offset information
333           identifier = null;
334           if (phpIdentifier != null) {
335             offset = token.substring(1);
336             phpIdentifier.setOffset(Integer.parseInt(offset));
337           }
338           break;
339         case 'p' : // PHPdoc offset information
340           identifier = null;
341           if (phpIdentifier != null) {
342             offset = token.substring(1);
343             phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
344           }
345           break;
346         case 'l' : // PHPdoc length information
347           identifier = null;
348           if (phpIdentifier != null) {
349             offset = token.substring(1);
350             phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
351           }
352           break;
353         default :
354           identifier = null;
355           phpIdentifier = null;
356           classname = null;
357       }
358       if (identifier != null && phpIdentifier != null) {
359         tokenExists = true;
360         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
361         if (list == null) {
362           list = new ArrayList();
363           list.add(phpIdentifier);
364           fIndentifierMap.put(identifier, list);
365         } else {
366           boolean flag = false;
367           for (int i = 0; i < list.size(); i++) {
368             if (list.get(i).equals(phpIdentifier)) {
369               flag = true;
370               break;
371             }
372           }
373           if (flag == false) {
374             list.add(phpIdentifier);
375           }
376         }
377       }
378     }
379     if (tokenExists) {
380       fFileMap.put(phpFileName, line);
381     }
382   }
383
384   /**
385    * Change the information for a given IFile resource
386    *
387    */
388   public void changeFile(IFile fileToParse) {
389     removeFile(fileToParse);
390     addFile(fileToParse);
391   }
392
393   /**
394    * Get a list of all PHPIdentifierLocation object's associated with an identifier
395    * 
396    * @param identifier
397    * @return
398    */
399   public List getLocations(String identifier) {
400     return (List) fIndentifierMap.get(identifier);
401   }
402
403   /**
404    * Initialize (i.e. clear) the current index information
405    *
406    */
407   public void initialize() {
408     fIndentifierMap = new HashMap();
409     fFileMap = new HashMap();
410   }
411
412   private void readFile() {
413
414     FileReader fileReader;
415     try {
416       fileReader = new FileReader(fFilename);
417
418       BufferedReader bufferedReader = new BufferedReader(fileReader);
419
420       String line;
421       while (bufferedReader.ready()) {
422         // all entries for one file are in a line
423         // separated by tabs !
424         line = bufferedReader.readLine();
425         addLine(line);
426       }
427
428       fileReader.close();
429     } catch (FileNotFoundException e) {
430       // ignore this
431       // TODO DialogBox which asks the user if she/he likes to build new index?
432     } catch (IOException e) {
433       // TODO Auto-generated catch block
434       e.printStackTrace();
435     }
436
437   }
438
439   /**
440    * Remove the information for a given IFile resource
441    *
442    */
443   public void removeFile(IFile fileToParse) {
444     //    String line = (String) fFileMap.get(fileToParse.getLocation().toString());
445     String line = (String) fFileMap.get(fileToParse.getFullPath().toString());
446     if (line != null) {
447       removeLine(line);
448     }
449   }
450
451   /**
452    * Removes a line of the index file for function, class, class-method and class-variable names
453    * 
454    * @param line
455    */
456   private void removeLine(String line) {
457     StringTokenizer tokenizer;
458     String phpFileName = null;
459     String token;
460     String identifier = null;
461     String classname = null;
462     PHPIdentifier phpIdentifier = null;
463     boolean tokenExists = false;
464
465     tokenizer = new StringTokenizer(line, "\t");
466     // first token contains the filename:
467     if (tokenizer.hasMoreTokens()) {
468       phpFileName = tokenizer.nextToken();
469       //System.out.println(token);
470     } else {
471       return;
472     }
473     // all the other tokens are identifiers:
474     while (tokenizer.hasMoreTokens()) {
475       token = tokenizer.nextToken();
476       //System.out.println(token);
477       switch (token.charAt(0)) {
478         case 'c' : // class name
479           identifier = token.substring(1);
480           classname = identifier;
481           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
482           break;
483         case 'd' : // define
484           identifier = token.substring(1);
485           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
486           break;
487         case 'f' : // function name
488           identifier = token.substring(1);
489           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
490           break;
491         case 'm' : //method inside a class
492           identifier = token.substring(1);
493           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
494           break;
495         case 'v' : // variable inside a class
496           identifier = token.substring(1);
497           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
498           break;
499         default :
500           identifier = null;
501           phpIdentifier = null;
502           classname = null;
503       }
504       if (identifier != null && phpIdentifier != null) {
505         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
506         if (list == null) {
507         } else {
508           for (int i = 0; i < list.size(); i++) {
509             if (list.get(i).equals(phpIdentifier)) {
510               list.remove(i);
511               break;
512             }
513           }
514           if (list.size() == 0) {
515             fIndentifierMap.remove(identifier);
516           }
517         }
518       }
519     }
520     fFileMap.remove(phpFileName);
521   }
522
523   /**
524    * Save the current index information in the projects index file
525    *
526    */
527   public void writeFile() {
528     FileWriter fileWriter;
529     try {
530       fileWriter = new FileWriter(fFilename);
531       String line;
532       Collection collection = fFileMap.values();
533       Iterator iterator = collection.iterator();
534       while (iterator.hasNext()) {
535         line = (String) iterator.next();
536         fileWriter.write(line + '\n');
537       }
538       fileWriter.close();
539     } catch (FileNotFoundException e) {
540         // ignore exception; project is deleted by user
541     } catch (IOException e) {
542       // TODO Auto-generated catch block
543       e.printStackTrace();
544     }
545   }
546 }