Fixed bug with unterminated strings at end of file
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / builder / IdentifierIndexManager.java
1 package net.sourceforge.phpeclipse.builder;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedReader;
5 import java.io.FileNotFoundException;
6 import java.io.FileReader;
7 import java.io.FileWriter;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Comparator;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Set;
18 import java.util.SortedMap;
19 import java.util.StringTokenizer;
20 import java.util.TreeMap;
21
22 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
23 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
24 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
25 import net.sourceforge.phpdt.internal.compiler.parser.SyntaxError;
26 import net.sourceforge.phpdt.internal.compiler.util.Util;
27 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
28 import net.sourceforge.phpeclipse.obfuscator.PHPIdentifier;
29
30 import org.eclipse.core.resources.IFile;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IStatus;
33
34 /**
35  * Manages the identifer index information for a specific project
36  *  
37  */
38 public class IdentifierIndexManager {
39   public class LineCreator implements ITerminalSymbols {
40     private Scanner fScanner;
41
42     private int fToken;
43
44     public LineCreator() {
45       fScanner = new Scanner(true, false, false, false, true, null, null, true /* taskCaseSensitive */);
46     }
47
48     /**
49      * Add the information of the current identifier to the line
50      * 
51      * @param typeOfIdentifier
52      *          the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
53      * @param identifier
54      *          current identifier
55      * @param line
56      *          Buffer for the current index line
57      */
58     private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line) {
59       line.append('\t');
60       line.append(typeOfIdentifier);
61       line.append(identifier);
62       //      line.append("\to"); // Offset
63       //      line.append(fScanner.getCurrentTokenStartPosition());
64     }
65
66     /**
67      * Add the information of the current identifier to the line
68      * 
69      * @param typeOfIdentifier
70      *          the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
71      * @param identifier
72      *          current identifier
73      * @param line
74      *          Buffer for the current index line
75      * @param phpdocOffset
76      *          the offset of the PHPdoc comment if available
77      * @param phpdocLength
78      *          the length of the PHPdoc comment if available
79      */
80     private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
81         int phpdocLength) {
82       line.append('\t');
83       line.append(typeOfIdentifier);
84       line.append(identifier);
85       line.append("\to"); // Offset
86       line.append(fScanner.getCurrentTokenStartPosition());
87       if (phpdocOffset >= 0) {
88         line.append("\tp"); // phpdoc offset
89         line.append(phpdocOffset);
90         line.append("\tl"); // phpdoc length
91         line.append(phpdocLength);
92       }
93     }
94
95     private void addClassVariableInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
96         int phpdocLength) {
97       line.append('\t');
98       line.append(typeOfIdentifier);
99       line.append(identifier);
100       line.append("\to"); // Offset
101       // we don't store the '$' in the index for class variables:
102       line.append(fScanner.getCurrentTokenStartPosition() + 1);
103       if (phpdocOffset >= 0) {
104         line.append("\tp"); // phpdoc offset
105         line.append(phpdocOffset);
106         line.append("\tl"); // phpdoc length
107         line.append(phpdocLength);
108       }
109     }
110
111     /**
112      * Get the next token from input
113      */
114     private void getNextToken() throws InvalidInputException {
115       //      try {
116       fToken = fScanner.getNextToken();
117       if (Scanner.DEBUG) {
118         int currentEndPosition = fScanner.getCurrentTokenEndPosition();
119         int currentStartPosition = fScanner.getCurrentTokenStartPosition();
120         System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
121         System.out.println(fScanner.toStringAction(fToken));
122       }
123       return;
124       //      } catch (InvalidInputException e) {
125       //        // ignore errors
126       //        // e.printStackTrace();
127       //      }
128       //      fToken = TokenNameERROR;
129     }
130
131     private void parseDeclarations(char[] parent, StringBuffer buf, boolean goBack) {
132       char[] ident;
133       char[] classVariable;
134       int counter = 0;
135       boolean hasModifiers = false;
136       int phpdocOffset = -1;
137       int phpdocLength = -1;
138       try {
139         while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
140           phpdocOffset = -1;
141           hasModifiers = false;
142           if (fToken == TokenNameCOMMENT_PHPDOC) {
143             phpdocOffset = fScanner.getCurrentTokenStartPosition();
144             phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
145             getNextToken();
146             while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
147                 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
148               hasModifiers = true;
149               getNextToken();
150             }
151             if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
152               break;
153             }
154           }
155           if (fToken == TokenNamefunction) {
156             getNextToken();
157             if (fToken == TokenNameAND) {
158               getNextToken();
159             }
160             if (fToken == TokenNameIdentifier) {
161               ident = fScanner.getCurrentIdentifierSource();
162               if (parent != null && equalCharArrays(parent, ident)) {
163                 // constructor function
164                 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
165               } else {
166                 if (parent != null) {
167                   // class method function
168                   addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
169                 } else {
170                   // nested function ?!
171                   addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
172                 }
173               }
174               getNextToken();
175               parseDeclarations(null, buf, true);
176             }
177           } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
178             getNextToken();
179             if (fToken == TokenNameIdentifier) {
180               ident = fScanner.getCurrentIdentifierSource();
181               addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
182               getNextToken();
183               if (fToken == TokenNameextends) {
184                 getNextToken();
185                 while (fToken == TokenNameIdentifier) {
186                   ident = fScanner.getCurrentIdentifierSource();
187                   // extends ident
188                   addIdentifierInformation('e', ident, buf);
189                   getNextToken();
190                   if (fToken == TokenNameCOMMA) {
191                     getNextToken();
192                   }
193                 }
194               }
195               if (fToken == TokenNameimplements) {
196                 getNextToken();
197                 while (fToken == TokenNameIdentifier) {
198                   ident = fScanner.getCurrentIdentifierSource();
199                   // implements ident
200                   addIdentifierInformation('e', ident, buf);
201                   getNextToken();
202                   if (fToken == TokenNameCOMMA) {
203                     getNextToken();
204                   }
205                 }
206               }
207               //skip tokens for classname, extends and others until we have
208               // the opening '{'
209               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
210                 getNextToken();
211               }
212               parseDeclarations(ident, buf, true);
213             }
214           } else if (fToken == TokenNamevar || hasModifiers || fToken == TokenNamestatic || fToken == TokenNamefinal
215               || fToken == TokenNamepublic || fToken == TokenNameprotected || fToken == TokenNameprivate) {
216             while (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
217                 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
218               getNextToken();
219             }
220             while (fToken == TokenNameVariable) {
221               ident = fScanner.getCurrentIdentifierSource();
222               classVariable = new char[ident.length - 1];
223               System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
224               addClassVariableInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
225               getNextToken();
226               if (fToken == TokenNameCOMMA) {
227                 getNextToken();
228               }
229             }
230           } else if (!hasModifiers && fToken == TokenNameIdentifier) {
231             ident = fScanner.getCurrentIdentifierSource();
232             getNextToken();
233             if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
234                 && ident[5] == 'e') {
235               if (fToken == TokenNameLPAREN) {
236                 getNextToken();
237                 if (fToken == TokenNameStringDoubleQuote) {
238                   ident = fScanner.getCurrentStringLiteralSource();
239                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
240                   getNextToken();
241                 } else if (fToken == TokenNameStringSingleQuote) {
242                   ident = fScanner.getCurrentStringLiteralSource();
243                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
244                   getNextToken();
245                 }
246               }
247             }
248           } else if (fToken == TokenNameglobal) {
249             // global variable
250             while (fToken != TokenNameEOF && fToken != TokenNameERROR && fToken != TokenNameSEMICOLON && fToken != TokenNameLBRACE
251                 && fToken != TokenNameRBRACE) {
252               getNextToken();
253               if (fToken == TokenNameVariable) {
254                 ident = fScanner.getCurrentIdentifierSource();
255                 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
256               }
257             }
258           } else if (fToken == TokenNameLBRACE) {
259             getNextToken();
260             counter++;
261           } else if (fToken == TokenNameRBRACE) {
262             getNextToken();
263             --counter;
264             if (counter == 0 && goBack) {
265               return;
266             }
267           } else {
268             getNextToken();
269           }
270         }
271       } catch (InvalidInputException e) {
272         // ignore errors
273       } catch (SyntaxError e) {
274         // TODO Auto-generated catch block
275         e.printStackTrace();
276       }
277     }
278
279     synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
280       char[] ident;
281       String identifier;
282       int counter = 0;
283       boolean hasModifiers = false;
284       int phpdocOffset = -1;
285       int phpdocLength = -1;
286       fScanner.setSource(charArray);
287       fScanner.setPHPMode(false);
288       fToken = TokenNameEOF;
289       try {
290         getNextToken();
291         while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
292           phpdocOffset = -1;
293           hasModifiers = false;
294           if (fToken == TokenNameCOMMENT_PHPDOC) {
295             phpdocOffset = fScanner.getCurrentTokenStartPosition();
296             phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
297             getNextToken();
298             while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
299                 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
300               hasModifiers = true;
301               getNextToken();
302             }
303             if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
304               break;
305             }
306           }
307           if (fToken == TokenNamefunction) {
308             getNextToken();
309             if (fToken == TokenNameAND) {
310               getNextToken();
311             }
312             if (fToken == TokenNameIdentifier) {
313               ident = fScanner.getCurrentIdentifierSource();
314               addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
315               getNextToken();
316               parseDeclarations(null, buf, true);
317             }
318           } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
319             getNextToken();
320             if (fToken == TokenNameIdentifier) {
321               ident = fScanner.getCurrentIdentifierSource();
322               addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
323               getNextToken();
324               if (fToken == TokenNameextends) {
325                 getNextToken();
326                 while (fToken == TokenNameIdentifier) {
327                   ident = fScanner.getCurrentIdentifierSource();
328                   // extends ident
329                   addIdentifierInformation('e', ident, buf);
330                   getNextToken();
331                   if (fToken == TokenNameCOMMA) {
332                     getNextToken();
333                   }
334                 }
335               }
336               if (fToken == TokenNameimplements) {
337                 getNextToken();
338                 while (fToken == TokenNameIdentifier) {
339                   ident = fScanner.getCurrentIdentifierSource();
340                   // implements ident
341                   addIdentifierInformation('e', ident, buf);
342                   getNextToken();
343                   if (fToken == TokenNameCOMMA) {
344                     getNextToken();
345                   }
346                 }
347               }
348               //skip fTokens for classname, extends and others until we have
349               // the opening '{'
350               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
351                 getNextToken();
352               }
353               parseDeclarations(ident, buf, true);
354             }
355           } else if (fToken == TokenNameVariable) {
356             // global variable
357             ident = fScanner.getCurrentIdentifierSource();
358             addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
359             getNextToken();
360           } else if (!hasModifiers && fToken == TokenNameIdentifier) {
361             ident = fScanner.getCurrentIdentifierSource();
362             getNextToken();
363             if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
364                 && ident[5] == 'e') {
365               if (fToken == TokenNameLPAREN) {
366                 getNextToken();
367                 if (fToken == TokenNameStringDoubleQuote) {
368                   ident = fScanner.getCurrentStringLiteralSource();
369                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
370                   getNextToken();
371                 } else if (fToken == TokenNameStringSingleQuote) {
372                   ident = fScanner.getCurrentStringLiteralSource();
373                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
374                   getNextToken();
375                 }
376               }
377             }
378           } else {
379             getNextToken();
380           }
381         }
382       } catch (InvalidInputException e) {
383         // ignore errors
384       } catch (SyntaxError e) {
385         // TODO Auto-generated catch block
386         e.printStackTrace();
387       }
388     }
389   }
390
391   class StringComparator implements Comparator {
392     public int compare(Object o1, Object o2) {
393       String s1 = (String) o1;
394       String s2 = (String) o2;
395       return s1.compareTo(s2);
396       //        return s1.toUpperCase().compareTo(s2.toUpperCase());
397     }
398
399     public boolean equals(Object o) {
400       String s = (String) o;
401       return compare(this, o) == 0;
402     }
403   }
404
405   private HashMap fFileMap;
406
407   private String fFilename;
408
409   private TreeMap fIndentifierMap;
410
411   public IdentifierIndexManager(String filename) {
412     fFilename = filename;
413     initialize();
414     readFile();
415   }
416
417   /**
418    * Check if 2 char arrays are equal
419    * 
420    * @param a
421    * @param b
422    * @return
423    */
424   private static boolean equalCharArrays(char[] a, char[] b) {
425     if (a.length != b.length) {
426       return false;
427     }
428     for (int i = 0; i < b.length; i++) {
429       if (a[i] != b[i]) {
430         return false;
431       }
432     }
433     return true;
434   }
435
436   public LineCreator createLineCreator() {
437     return new LineCreator();
438   }
439
440   /**
441    * Add the information for a given IFile resource
442    *  
443    */
444   public void addFile(IFile fileToParse) {
445     //    InputStream iStream;
446     LineCreator lineCreator = createLineCreator();
447     try {
448       addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
449           lineCreator);
450     } catch (CoreException e1) {
451       // TODO Auto-generated catch block
452       e1.printStackTrace();
453     }
454   }
455
456   /**
457    * @param fileToParse
458    * @param lineCreator
459    * @throws CoreException
460    */
461   public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
462     try {
463       StringBuffer lineBuffer = new StringBuffer();
464       lineBuffer.append(filePath);
465       int lineLength = lineBuffer.length();
466       lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
467       //      if (lineLength != lineBuffer.length()) {
468       // always add the file for Open Include Action
469       addLine(lineBuffer.toString());
470       //      }
471     } catch (IOException e) {
472       e.printStackTrace();
473     } finally {
474       try {
475         if (stream != null) {
476           stream.close();
477         }
478       } catch (IOException e) {
479       }
480     }
481   }
482
483   /**
484    * Adds a line of the index file for function, class, class-method and class-variable names
485    * 
486    * @param line
487    */
488   private void addLine(String line) {
489     addLine(fIndentifierMap, fFileMap, line, null);
490   }
491
492   public TreeMap getIdentifiers(IFile file) {
493     TreeMap treeMap = new TreeMap(new StringComparator());
494     addIdentifiers(treeMap, file);
495     return treeMap;
496   }
497
498   public TreeMap getIdentifiers(String startClazz) {
499     TreeMap treeMap = new TreeMap(new StringComparator());
500     addIdentifiers(treeMap, startClazz);
501     return treeMap;
502   }
503
504   public void addIdentifiers(TreeMap treeMap, IFile file) {
505     String line = (String) fFileMap.get(file.getProjectRelativePath().toString());
506     if (line != null) {
507       PHPIdentifierLocation ident;
508       ArrayList allClassNames = new ArrayList();
509       addLine(treeMap, null, line, allClassNames);
510       int i = 0;
511       while (i < allClassNames.size()) {
512         String clazz = (String) allClassNames.get(i++);
513         addClassName(treeMap, clazz, allClassNames);
514       }
515     }
516   }
517
518   public void addIdentifiers(TreeMap treeMap, String startClazz) {
519     PHPIdentifierLocation ident;
520     ArrayList allClassNames = new ArrayList();
521     addClassName(treeMap, startClazz, allClassNames);
522     int i = 0;
523     while (i < allClassNames.size()) {
524       String clazz = (String) allClassNames.get(i++);
525       addClassName(treeMap, clazz, allClassNames);
526     }
527   }
528
529   /**
530    * @param treeMap
531    * @param clazz
532    * @param allClassNames
533    */
534   private boolean addClassName(TreeMap treeMap, String clazz, List allClassNames) {
535     String line;
536     PHPIdentifierLocation ident;
537     List list = getLocations(clazz);
538     if (list == null) {
539       return false;
540     }
541     boolean result = false;
542     for (int i = 0; i < list.size(); i++) {
543       ident = (PHPIdentifierLocation) list.get(i);
544       if (ident.isClass()) {
545         line = (String) fFileMap.get(ident.getFilename());
546         addLine(treeMap, null, line, allClassNames);
547         result = true;
548       }
549     }
550     return result;
551   }
552
553   /**
554    * Adds a line of the index file for function, class, class-method and class-variable names
555    * 
556    * @param line
557    */
558   public void addLine(TreeMap treeMap, HashMap fileMap, String line, List allClassNames) {
559     StringTokenizer tokenizer;
560     String phpFileName = null;
561     String token;
562     String identifier = null;
563     String classname = null;
564     String offset = null;
565     PHPIdentifierLocation phpIdentifier = null;
566     boolean tokenExists = false;
567     tokenizer = new StringTokenizer(line, "\t");
568     // first token contains the filename:
569     try {
570       if (tokenizer.hasMoreTokens()) {
571         phpFileName = tokenizer.nextToken();
572         //System.out.println(token);
573       } else {
574         return;
575       }
576       // all the other tokens are identifiers:
577       while (tokenizer.hasMoreTokens()) {
578         token = tokenizer.nextToken();
579         //System.out.println(token);
580         switch (token.charAt(0)) {
581         case 'c':
582           // class name
583           identifier = token.substring(1);
584           classname = identifier;
585           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
586           break;
587         case 'd':
588           // define
589           identifier = token.substring(1);
590           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
591           break;
592         case 'e':
593           // extends <class name>
594           // not in map
595           identifier = null;
596           phpIdentifier = null;
597           if (allClassNames != null) {
598             String extName = token.substring(1);
599             if (!allClassNames.contains(extName)) {
600               allClassNames.add(extName);
601             }
602           }
603           break;
604         case 'f':
605           // function name
606           identifier = token.substring(1);
607           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
608           break;
609         case 'g':
610           // global variable
611           identifier = token.substring(1);
612           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
613           break;
614         case 'i':
615           // implements <class name>
616           // not in map
617           identifier = null;
618           phpIdentifier = null;
619           if (allClassNames != null) {
620             String implName = token.substring(1);
621             if (!allClassNames.contains(implName)) {
622               allClassNames.add(implName);
623             }
624           }
625           break;
626         case 'k':
627           // constructor function name
628           identifier = token.substring(1);
629           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
630           break;
631         case 'm':
632           //method inside a class
633           identifier = token.substring(1);
634           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
635           break;
636         case 'v':
637           // variable inside a class
638           identifier = token.substring(1);
639           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
640           break;
641         case 'o':
642           // offset information
643           identifier = null;
644           if (phpIdentifier != null) {
645             offset = token.substring(1);
646             phpIdentifier.setOffset(Integer.parseInt(offset));
647           }
648           break;
649         case 'p':
650           // PHPdoc offset information
651           identifier = null;
652           if (phpIdentifier != null) {
653             offset = token.substring(1);
654             phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
655           }
656           break;
657         case 'l':
658           // PHPdoc length information
659           identifier = null;
660           if (phpIdentifier != null) {
661             offset = token.substring(1);
662             phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
663           }
664           break;
665         default:
666           PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
667           identifier = null;
668           phpIdentifier = null;
669           classname = null;
670         }
671         if (identifier != null && phpIdentifier != null) {
672           tokenExists = true;
673           ArrayList list = (ArrayList) treeMap.get(identifier);
674           if (list == null) {
675             list = new ArrayList();
676             list.add(phpIdentifier);
677             treeMap.put(identifier, list);
678           } else {
679             boolean flag = false;
680             for (int i = 0; i < list.size(); i++) {
681               if (list.get(i).equals(phpIdentifier)) {
682                 flag = true;
683                 break;
684               }
685             }
686             if (flag == false) {
687               list.add(phpIdentifier);
688             }
689           }
690         }
691       }
692       if (fileMap != null) {
693         fileMap.put(phpFileName, line);
694       }
695     } catch (Throwable e) {
696       // write to workspace/.metadata/.log file
697       PHPeclipsePlugin.log(e);
698     }
699     //    if (tokenExists) {
700
701     //    }
702   }
703
704   /**
705    * Change the information for a given IFile resource
706    *  
707    */
708   public void changeFile(IFile fileToParse) {
709     removeFile(fileToParse);
710     addFile(fileToParse);
711   }
712
713   /**
714    * Get a list of all PHPIdentifierLocation object's associated with an identifier
715    * 
716    * @param identifier
717    * @return
718    */
719   public List getLocations(String identifier) {
720     return (List) fIndentifierMap.get(identifier);
721   }
722
723   /**
724    * Initialize (i.e. clear) the current index information
725    *  
726    */
727   public void initialize() {
728     fIndentifierMap = new TreeMap(new StringComparator());
729     fFileMap = new HashMap();
730   }
731
732   private void readFile() {
733     FileReader fileReader;
734     try {
735       fileReader = new FileReader(fFilename);
736       BufferedReader bufferedReader = new BufferedReader(fileReader);
737       String line;
738       while (bufferedReader.ready()) {
739         // all entries for one file are in a line
740         // separated by tabs !
741         line = bufferedReader.readLine();
742         addLine(line);
743       }
744       fileReader.close();
745     } catch (FileNotFoundException e) {
746       // ignore this
747       // TODO DialogBox which asks the user if she/he likes to build new index?
748     } catch (IOException e) {
749       // TODO Auto-generated catch block
750       e.printStackTrace();
751     }
752   }
753
754   /**
755    * Remove the information for a given IFile resource
756    *  
757    */
758   public void removeFile(IFile fileToParse) {
759     //    String line = (String)
760     // fFileMap.get(fileToParse.getLocation().toString());
761     String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
762     if (line != null) {
763       removeLine(line);
764     }
765   }
766
767   /**
768    * Removes a line of the index file for function, class, class-method and class-variable names
769    * 
770    * @param line
771    */
772   private void removeLine(String line) {
773     StringTokenizer tokenizer;
774     String phpFileName = null;
775     String token;
776     String identifier = null;
777     String classname = null;
778     PHPIdentifier phpIdentifier = null;
779     boolean tokenExists = false;
780     tokenizer = new StringTokenizer(line, "\t");
781     // first token contains the filename:
782     if (tokenizer.hasMoreTokens()) {
783       phpFileName = tokenizer.nextToken();
784       //System.out.println(token);
785     } else {
786       return;
787     }
788     int offset = -1;
789     // all the other tokens are identifiers:
790     while (tokenizer.hasMoreTokens()) {
791       token = tokenizer.nextToken();
792       //System.out.println(token);
793       switch (token.charAt(0)) {
794       case 'c':
795         // class name
796         identifier = token.substring(1);
797         classname = identifier;
798         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
799         break;
800       case 'd':
801         // define
802         identifier = token.substring(1);
803         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
804         break;
805       case 'e':
806         // extends <class name>
807         identifier = null;
808         phpIdentifier = null;
809         break;
810       case 'f':
811         // function name
812         identifier = token.substring(1);
813         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
814         break;
815       case 'g':
816         // global variable
817         identifier = token.substring(1);
818         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
819         break;
820       case 'i':
821         // implements <class name>
822         identifier = null;
823         phpIdentifier = null;
824         break;
825       case 'k':
826         // constructor function name
827         identifier = token.substring(1);
828         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
829         break;
830       case 'm':
831         //method inside a class
832         identifier = token.substring(1);
833         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
834         break;
835       case 'o':
836         // offset information
837         identifier = null;
838         break;
839       case 'p':
840         // PHPdoc offset information
841         identifier = null;
842         break;
843       case 'l':
844         // PHPdoc length information
845         identifier = null;
846         break;
847       case 'v':
848         // variable inside a class
849         identifier = token.substring(1);
850         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
851         break;
852       default:
853         PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
854         identifier = null;
855         phpIdentifier = null;
856         classname = null;
857       }
858       if (identifier != null && phpIdentifier != null) {
859         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
860         if (list == null) {
861         } else {
862           for (int i = 0; i < list.size(); i++) {
863             if (list.get(i).equals(phpIdentifier)) {
864               list.remove(i);
865               break;
866             }
867           }
868           if (list.size() == 0) {
869             fIndentifierMap.remove(identifier);
870           }
871         }
872       }
873     }
874     fFileMap.remove(phpFileName);
875   }
876
877   /**
878    * Save the current index information in the projects index file
879    *  
880    */
881   public void writeFile() {
882     FileWriter fileWriter;
883     try {
884       fileWriter = new FileWriter(fFilename);
885       String line;
886       Collection collection = fFileMap.values();
887       Iterator iterator = collection.iterator();
888       while (iterator.hasNext()) {
889         line = (String) iterator.next();
890         fileWriter.write(line + '\n');
891       }
892       fileWriter.close();
893     } catch (FileNotFoundException e) {
894       // ignore exception; project is deleted by user
895     } catch (IOException e) {
896       // TODO Auto-generated catch block
897       e.printStackTrace();
898     }
899   }
900
901   /**
902    * @param fromKey
903    * @param toKey
904    * @return
905    */
906   public SortedMap getIdentifierMap() {
907     return fIndentifierMap;
908   }
909
910   synchronized public List getFileList(String filePattern) {
911     Set set = fFileMap.keySet();
912     if (set.isEmpty()) {
913       return null;
914     }
915     Iterator iter = set.iterator();
916     ArrayList list = new ArrayList();
917     String fileName;
918     int index;
919     while (iter.hasNext()) {
920       fileName = (String) iter.next();
921       if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {
922         list.add(fileName);
923       }
924     }
925     return list;
926   }
927 }