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