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