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