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