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