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