Improved calculation of function/methods sourceEnd for code folding
[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() {
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 (SyntaxError e) {
272         // TODO Auto-generated catch block
273         e.printStackTrace();
274       }
275     }
276
277     synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
278       char[] ident;
279       String identifier;
280       int counter = 0;
281       boolean hasModifiers = false;
282       int phpdocOffset = -1;
283       int phpdocLength = -1;
284       fScanner.setSource(charArray);
285       fScanner.setPHPMode(false);
286       fToken = TokenNameEOF;
287       getNextToken();
288       try {
289         while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
290           phpdocOffset = -1;
291           hasModifiers = false;
292           if (fToken == TokenNameCOMMENT_PHPDOC) {
293             phpdocOffset = fScanner.getCurrentTokenStartPosition();
294             phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
295             getNextToken();
296             while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
297                 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
298               hasModifiers = true;
299               getNextToken();
300             }
301             if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
302               break;
303             }
304           }
305           if (fToken == TokenNamefunction) {
306             getNextToken();
307             if (fToken == TokenNameAND) {
308               getNextToken();
309             }
310             if (fToken == TokenNameIdentifier) {
311               ident = fScanner.getCurrentIdentifierSource();
312               addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
313               getNextToken();
314               parseDeclarations(null, buf, true);
315             }
316           } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
317             getNextToken();
318             if (fToken == TokenNameIdentifier) {
319               ident = fScanner.getCurrentIdentifierSource();
320               addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
321               getNextToken();
322               if (fToken == TokenNameextends) {
323                 getNextToken();
324                 while (fToken == TokenNameIdentifier) {
325                   ident = fScanner.getCurrentIdentifierSource();
326                   // extends ident
327                   addIdentifierInformation('e', ident, buf);
328                   getNextToken();
329                   if (fToken == TokenNameCOMMA) {
330                     getNextToken();
331                   }
332                 }
333               }
334               if (fToken == TokenNameimplements) {
335                 getNextToken();
336                 while (fToken == TokenNameIdentifier) {
337                   ident = fScanner.getCurrentIdentifierSource();
338                   // implements ident
339                   addIdentifierInformation('e', ident, buf);
340                   getNextToken();
341                   if (fToken == TokenNameCOMMA) {
342                     getNextToken();
343                   }
344                 }
345               }
346               //skip fTokens for classname, extends and others until we have
347               // the opening '{'
348               while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
349                 getNextToken();
350               }
351               parseDeclarations(ident, buf, true);
352             }
353           } else if (fToken == TokenNameVariable) {
354             // global variable
355             ident = fScanner.getCurrentIdentifierSource();
356             addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
357             getNextToken();
358           } else if (!hasModifiers && fToken == TokenNameIdentifier) {
359             ident = fScanner.getCurrentIdentifierSource();
360             getNextToken();
361             if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
362                 && ident[5] == 'e') {
363               if (fToken == TokenNameLPAREN) {
364                 getNextToken();
365                 if (fToken == TokenNameStringDoubleQuote) {
366                   ident = fScanner.getCurrentStringLiteralSource();
367                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
368                   getNextToken();
369                 } else if (fToken == TokenNameStringSingleQuote) {
370                   ident = fScanner.getCurrentStringLiteralSource();
371                   addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
372                   getNextToken();
373                 }
374               }
375             }
376           } else {
377             getNextToken();
378           }
379         }
380       } catch (SyntaxError e) {
381         // TODO Auto-generated catch block
382         e.printStackTrace();
383       }
384     }
385   }
386
387   class StringComparator implements Comparator {
388     public int compare(Object o1, Object o2) {
389       String s1 = (String) o1;
390       String s2 = (String) o2;
391       return s1.compareTo(s2);
392       //        return s1.toUpperCase().compareTo(s2.toUpperCase());
393     }
394
395     public boolean equals(Object o) {
396       String s = (String) o;
397       return compare(this, o) == 0;
398     }
399   }
400
401   private HashMap fFileMap;
402
403   private String fFilename;
404
405   private TreeMap fIndentifierMap;
406
407   public IdentifierIndexManager(String filename) {
408     fFilename = filename;
409     initialize();
410     readFile();
411   }
412
413   /**
414    * Check if 2 char arrays are equal
415    * 
416    * @param a
417    * @param b
418    * @return
419    */
420   private static boolean equalCharArrays(char[] a, char[] b) {
421     if (a.length != b.length) {
422       return false;
423     }
424     for (int i = 0; i < b.length; i++) {
425       if (a[i] != b[i]) {
426         return false;
427       }
428     }
429     return true;
430   }
431
432   public LineCreator createLineCreator() {
433     return new LineCreator();
434   }
435
436   /**
437    * Add the information for a given IFile resource
438    *  
439    */
440   public void addFile(IFile fileToParse) {
441     //    InputStream iStream;
442     LineCreator lineCreator = createLineCreator();
443     try {
444       addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
445           lineCreator);
446     } catch (CoreException e1) {
447       // TODO Auto-generated catch block
448       e1.printStackTrace();
449     }
450   }
451
452   /**
453    * @param fileToParse
454    * @param lineCreator
455    * @throws CoreException
456    */
457   public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
458     try {
459       StringBuffer lineBuffer = new StringBuffer();
460       lineBuffer.append(filePath);
461       int lineLength = lineBuffer.length();
462       lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
463       //      if (lineLength != lineBuffer.length()) {
464       // always add the file for Open Include Action
465       addLine(lineBuffer.toString());
466       //      }
467     } catch (IOException e) {
468       e.printStackTrace();
469     } finally {
470       try {
471         if (stream != null) {
472           stream.close();
473         }
474       } catch (IOException e) {
475       }
476     }
477   }
478
479   /**
480    * Adds a line of the index file for function, class, class-method and class-variable names
481    * 
482    * @param line
483    */
484   private void addLine(String line) {
485     addLine(fIndentifierMap, fFileMap, line, null);
486   }
487
488   public TreeMap getIdentifiers(IFile file) {
489     TreeMap treeMap = new TreeMap(new StringComparator());
490     addIdentifiers(treeMap, file);
491     return treeMap;
492   }
493   public TreeMap getIdentifiers(String startClazz) {
494     TreeMap treeMap = new TreeMap(new StringComparator());
495     addIdentifiers(treeMap, startClazz);
496     return treeMap;
497   }
498   
499   public void addIdentifiers(TreeMap treeMap, IFile file) {
500     String line = (String) fFileMap.get(file.getProjectRelativePath().toString());
501     if (line != null) {
502       PHPIdentifierLocation ident;
503       ArrayList allClassNames = new ArrayList();
504       addLine(treeMap, null, line, allClassNames);
505       int i=0;
506       while (i<allClassNames.size()) {
507         String clazz = (String) allClassNames.get(i++);
508         addClassName(treeMap, clazz, allClassNames);
509       }
510     }
511   }
512
513   public void addIdentifiers(TreeMap treeMap, String startClazz) {
514     PHPIdentifierLocation ident;
515     ArrayList allClassNames = new ArrayList();
516     addClassName(treeMap, startClazz, allClassNames);
517     int i=0;
518     while (i<allClassNames.size()) {
519       String clazz = (String) allClassNames.get(i++);
520       addClassName(treeMap, clazz, allClassNames);
521     }
522   }
523
524   /**
525    * @param treeMap
526    * @param clazz
527    * @param allClassNames
528    */
529   private boolean addClassName(TreeMap treeMap, String clazz, List allClassNames) {
530     String line;
531     PHPIdentifierLocation ident;
532     List list = getLocations(clazz);
533     if (list==null) {
534       return false;
535     }
536     boolean result = false;
537     for (int i = 0; i < list.size(); i++) {
538       ident = (PHPIdentifierLocation) list.get(i);
539       if (ident.isClass()) {
540         line = (String) fFileMap.get(ident.getFilename());
541         addLine(treeMap, null, line, allClassNames);
542         result = true;
543       }
544     }
545     return result;
546   }
547
548   /**
549    * Adds a line of the index file for function, class, class-method and class-variable names
550    * 
551    * @param line
552    */
553   public void addLine(TreeMap treeMap, HashMap fileMap, String line, List allClassNames) {
554     StringTokenizer tokenizer;
555     String phpFileName = null;
556     String token;
557     String identifier = null;
558     String classname = null;
559     String offset = null;
560     PHPIdentifierLocation phpIdentifier = null;
561     boolean tokenExists = false;
562     tokenizer = new StringTokenizer(line, "\t");
563     // first token contains the filename:
564     try {
565       if (tokenizer.hasMoreTokens()) {
566         phpFileName = tokenizer.nextToken();
567         //System.out.println(token);
568       } else {
569         return;
570       }
571       // all the other tokens are identifiers:
572       while (tokenizer.hasMoreTokens()) {
573         token = tokenizer.nextToken();
574         //System.out.println(token);
575         switch (token.charAt(0)) {
576         case 'c':
577           // class name
578           identifier = token.substring(1);
579           classname = identifier;
580           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
581           break;
582         case 'd':
583           // define
584           identifier = token.substring(1);
585           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
586           break;
587         case 'e':
588           // extends <class name>
589           // not in map
590           identifier = null;
591           phpIdentifier = null;
592           if (allClassNames != null) {
593             String extName = token.substring(1);
594             if (!allClassNames.contains(extName)) {
595               allClassNames.add(extName);
596             }
597           }
598           break;
599         case 'f':
600           // function name
601           identifier = token.substring(1);
602           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
603           break;
604         case 'g':
605           // global variable
606           identifier = token.substring(1);
607           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
608           break;
609         case 'i':
610           // implements <class name>
611           // not in map
612           identifier = null;
613           phpIdentifier = null;
614           if (allClassNames != null) {
615             String implName = token.substring(1);
616             if (!allClassNames.contains(implName)) {
617               allClassNames.add(implName);
618             }
619           }
620           break;
621         case 'k':
622           // constructor function name
623           identifier = token.substring(1);
624           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
625           break;
626         case 'm':
627           //method inside a class
628           identifier = token.substring(1);
629           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
630           break;
631         case 'v':
632           // variable inside a class
633           identifier = token.substring(1);
634           phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
635           break;
636         case 'o':
637           // offset information
638           identifier = null;
639           if (phpIdentifier != null) {
640             offset = token.substring(1);
641             phpIdentifier.setOffset(Integer.parseInt(offset));
642           }
643           break;
644         case 'p':
645           // PHPdoc offset information
646           identifier = null;
647           if (phpIdentifier != null) {
648             offset = token.substring(1);
649             phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
650           }
651           break;
652         case 'l':
653           // PHPdoc length information
654           identifier = null;
655           if (phpIdentifier != null) {
656             offset = token.substring(1);
657             phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
658           }
659           break;
660         default:
661           PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
662           identifier = null;
663           phpIdentifier = null;
664           classname = null;
665         }
666         if (identifier != null && phpIdentifier != null) {
667           tokenExists = true;
668           ArrayList list = (ArrayList) treeMap.get(identifier);
669           if (list == null) {
670             list = new ArrayList();
671             list.add(phpIdentifier);
672             treeMap.put(identifier, list);
673           } else {
674             boolean flag = false;
675             for (int i = 0; i < list.size(); i++) {
676               if (list.get(i).equals(phpIdentifier)) {
677                 flag = true;
678                 break;
679               }
680             }
681             if (flag == false) {
682               list.add(phpIdentifier);
683             }
684           }
685         }
686       }
687       if (fileMap != null) {
688         fileMap.put(phpFileName, line);
689       }
690     } catch (Throwable e) {
691       // write to workspace/.metadata/.log file
692       PHPeclipsePlugin.log(e);
693     }
694     //    if (tokenExists) {
695
696     //    }
697   }
698
699   /**
700    * Change the information for a given IFile resource
701    *  
702    */
703   public void changeFile(IFile fileToParse) {
704     removeFile(fileToParse);
705     addFile(fileToParse);
706   }
707
708   /**
709    * Get a list of all PHPIdentifierLocation object's associated with an identifier
710    * 
711    * @param identifier
712    * @return
713    */
714   public List getLocations(String identifier) {
715     return (List) fIndentifierMap.get(identifier);
716   }
717
718   /**
719    * Initialize (i.e. clear) the current index information
720    *  
721    */
722   public void initialize() {
723     fIndentifierMap = new TreeMap(new StringComparator());
724     fFileMap = new HashMap();
725   }
726
727   private void readFile() {
728     FileReader fileReader;
729     try {
730       fileReader = new FileReader(fFilename);
731       BufferedReader bufferedReader = new BufferedReader(fileReader);
732       String line;
733       while (bufferedReader.ready()) {
734         // all entries for one file are in a line
735         // separated by tabs !
736         line = bufferedReader.readLine();
737         addLine(line);
738       }
739       fileReader.close();
740     } catch (FileNotFoundException e) {
741       // ignore this
742       // TODO DialogBox which asks the user if she/he likes to build new index?
743     } catch (IOException e) {
744       // TODO Auto-generated catch block
745       e.printStackTrace();
746     }
747   }
748
749   /**
750    * Remove the information for a given IFile resource
751    *  
752    */
753   public void removeFile(IFile fileToParse) {
754     //    String line = (String)
755     // fFileMap.get(fileToParse.getLocation().toString());
756     String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
757     if (line != null) {
758       removeLine(line);
759     }
760   }
761
762   /**
763    * Removes a line of the index file for function, class, class-method and class-variable names
764    * 
765    * @param line
766    */
767   private void removeLine(String line) {
768     StringTokenizer tokenizer;
769     String phpFileName = null;
770     String token;
771     String identifier = null;
772     String classname = null;
773     PHPIdentifier phpIdentifier = null;
774     boolean tokenExists = false;
775     tokenizer = new StringTokenizer(line, "\t");
776     // first token contains the filename:
777     if (tokenizer.hasMoreTokens()) {
778       phpFileName = tokenizer.nextToken();
779       //System.out.println(token);
780     } else {
781       return;
782     }
783     int offset = -1;
784     // all the other tokens are identifiers:
785     while (tokenizer.hasMoreTokens()) {
786       token = tokenizer.nextToken();
787       //System.out.println(token);
788       switch (token.charAt(0)) {
789       case 'c':
790         // class name
791         identifier = token.substring(1);
792         classname = identifier;
793         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
794         break;
795       case 'd':
796         // define
797         identifier = token.substring(1);
798         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
799         break;
800       case 'e':
801         // extends <class name>
802         identifier = null;
803         phpIdentifier = null;
804         break;
805       case 'f':
806         // function name
807         identifier = token.substring(1);
808         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
809         break;
810       case 'g':
811         // global variable
812         identifier = token.substring(1);
813         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
814         break;
815       case 'i':
816         // implements <class name>
817         identifier = null;
818         phpIdentifier = null;
819         break;
820       case 'k':
821         // constructor function name
822         identifier = token.substring(1);
823         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
824         break;
825       case 'm':
826         //method inside a class
827         identifier = token.substring(1);
828         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
829         break;
830       case 'o':
831         // offset information
832         identifier = null;
833         break;
834       case 'p':
835         // PHPdoc offset information
836         identifier = null;
837         break;
838       case 'l':
839         // PHPdoc length information
840         identifier = null;
841         break;
842       case 'v':
843         // variable inside a class
844         identifier = token.substring(1);
845         phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
846         break;
847       default:
848         PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
849         identifier = null;
850         phpIdentifier = null;
851         classname = null;
852       }
853       if (identifier != null && phpIdentifier != null) {
854         ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
855         if (list == null) {
856         } else {
857           for (int i = 0; i < list.size(); i++) {
858             if (list.get(i).equals(phpIdentifier)) {
859               list.remove(i);
860               break;
861             }
862           }
863           if (list.size() == 0) {
864             fIndentifierMap.remove(identifier);
865           }
866         }
867       }
868     }
869     fFileMap.remove(phpFileName);
870   }
871
872   /**
873    * Save the current index information in the projects index file
874    *  
875    */
876   public void writeFile() {
877     FileWriter fileWriter;
878     try {
879       fileWriter = new FileWriter(fFilename);
880       String line;
881       Collection collection = fFileMap.values();
882       Iterator iterator = collection.iterator();
883       while (iterator.hasNext()) {
884         line = (String) iterator.next();
885         fileWriter.write(line + '\n');
886       }
887       fileWriter.close();
888     } catch (FileNotFoundException e) {
889       // ignore exception; project is deleted by user
890     } catch (IOException e) {
891       // TODO Auto-generated catch block
892       e.printStackTrace();
893     }
894   }
895
896   /**
897    * @param fromKey
898    * @param toKey
899    * @return
900    */
901   public SortedMap getIdentifierMap() {
902     return fIndentifierMap;
903   }
904
905   synchronized public List getFileList(String filePattern) {
906     Set set = fFileMap.keySet();
907     if (set.isEmpty()) {
908       return null;
909     }
910     Iterator iter = set.iterator();
911     ArrayList list = new ArrayList();
912     String fileName;
913     int index;
914     while (iter.hasNext()) {
915       fileName = (String) iter.next();
916       if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {
917         list.add(fileName);
918       }
919     }
920     return list;
921   }
922 }