1 package net.sourceforge.phpeclipse.builder;
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;
17 import java.util.SortedMap;
18 import java.util.StringTokenizer;
19 import java.util.TreeMap;
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;
29 import org.eclipse.core.resources.IFile;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IStatus;
34 * Manages the identifer index information for a specific project
37 public class IdentifierIndexManager {
38 public class LineCreator implements ITerminalSymbols {
39 private Scanner fScanner;
43 public LineCreator() {
44 fScanner = new Scanner(true, false, false, false, true, null, null,
45 true /* taskCaseSensitive */);
49 * Add the information of the current identifier to the line
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)
57 * Buffer for the current index line
59 private void addIdentifierInformation(char typeOfIdentifier,
60 char[] identifier, StringBuffer line) {
62 line.append(typeOfIdentifier);
63 line.append(identifier);
64 // line.append("\to"); // Offset
65 // line.append(fScanner.getCurrentTokenStartPosition());
69 * Add the information of the current identifier to the line
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)
77 * Buffer for the current index line
79 * the offset of the PHPdoc comment if available
81 * the length of the PHPdoc comment if available
83 private void addIdentifierInformation(char typeOfIdentifier,
84 char[] identifier, StringBuffer line, int phpdocOffset,
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);
99 private void addClassVariableInformation(char typeOfIdentifier,
100 char[] identifier, StringBuffer line, int phpdocOffset,
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);
117 * Get the next token from input
119 private void getNextToken() throws InvalidInputException {
121 fToken = fScanner.getNextToken();
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));
131 // } catch (InvalidInputException e) {
133 // // e.printStackTrace();
135 // fToken = TokenNameERROR;
138 private void parseDeclarations(char[] parent, StringBuffer buf,
141 char[] classVariable;
143 boolean hasModifiers = false;
144 int phpdocOffset = -1;
145 int phpdocLength = -1;
147 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
149 hasModifiers = false;
150 if (fToken == TokenNameCOMMENT_PHPDOC) {
151 phpdocOffset = fScanner.getCurrentTokenStartPosition();
152 phpdocLength = fScanner.getCurrentTokenEndPosition()
153 - fScanner.getCurrentTokenStartPosition() + 1;
155 while (fToken == TokenNamestatic
156 || fToken == TokenNamefinal
157 || fToken == TokenNamepublic
158 || fToken == TokenNameprotected
159 || fToken == TokenNameprivate
160 || fToken == TokenNameabstract) {
164 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
168 if (fToken == TokenNamefunction) {
170 if (fToken == TokenNameAND) {
173 if (fToken == TokenNameIdentifier) {
174 ident = fScanner.getCurrentIdentifierSource();
176 && equalCharArrays(parent, ident)) {
177 // constructor function
178 addIdentifierInformation('k', ident, buf,
179 phpdocOffset, phpdocLength);
181 if (parent != null) {
182 // class method function
183 addIdentifierInformation('m', ident, buf,
184 phpdocOffset, phpdocLength);
186 // nested function ?!
187 addIdentifierInformation('f', ident, buf,
188 phpdocOffset, phpdocLength);
192 parseDeclarations(null, buf, true);
194 } else if (fToken == TokenNameclass
195 || fToken == TokenNameinterface) {
197 if (fToken == TokenNameIdentifier) {
198 ident = fScanner.getCurrentIdentifierSource();
199 addIdentifierInformation('c', ident, buf,
200 phpdocOffset, phpdocLength);
202 if (fToken == TokenNameextends) {
204 while (fToken == TokenNameIdentifier) {
206 .getCurrentIdentifierSource();
208 addIdentifierInformation('e', ident, buf);
210 if (fToken == TokenNameCOMMA) {
215 if (fToken == TokenNameimplements) {
217 while (fToken == TokenNameIdentifier) {
219 .getCurrentIdentifierSource();
221 addIdentifierInformation('e', ident, buf);
223 if (fToken == TokenNameCOMMA) {
228 // skip tokens for classname, extends and others
231 while (fToken != TokenNameLBRACE
232 && fToken != TokenNameEOF
233 && fToken != TokenNameERROR) {
236 parseDeclarations(ident, buf, true);
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) {
252 while (fToken == TokenNameVariable) {
253 ident = fScanner.getCurrentIdentifierSource();
254 classVariable = new char[ident.length - 1];
255 System.arraycopy(ident, 1, classVariable, 0,
257 addClassVariableInformation('v', classVariable,
258 buf, phpdocOffset, phpdocLength);
260 if (fToken == TokenNameCOMMA) {
264 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
265 ident = fScanner.getCurrentIdentifierSource();
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) {
273 if (fToken == TokenNameStringDoubleQuote) {
275 .getCurrentStringLiteralSource();
276 addIdentifierInformation('d', ident, buf,
277 phpdocOffset, phpdocLength);
279 } else if (fToken == TokenNameStringSingleQuote) {
281 .getCurrentStringLiteralSource();
282 addIdentifierInformation('d', ident, buf,
283 phpdocOffset, phpdocLength);
288 } else if (fToken == TokenNameglobal) {
290 while (fToken != TokenNameEOF
291 && fToken != TokenNameERROR
292 && fToken != TokenNameSEMICOLON
293 && fToken != TokenNameLBRACE
294 && fToken != TokenNameRBRACE) {
296 if (fToken == TokenNameVariable) {
297 ident = fScanner.getCurrentIdentifierSource();
298 addIdentifierInformation('g', ident, buf,
299 phpdocOffset, phpdocLength);
302 } else if (fToken == TokenNameLBRACE) {
305 } else if (fToken == TokenNameRBRACE) {
308 if (counter == 0 && goBack) {
315 } catch (InvalidInputException e) {
317 } catch (SyntaxError e) {
318 // TODO Auto-generated catch block
323 synchronized public void parseIdentifiers(char[] charArray,
327 boolean hasModifiers = false;
328 int phpdocOffset = -1;
329 int phpdocLength = -1;
330 fScanner.setSource(charArray);
331 fScanner.setPHPMode(false);
332 fToken = TokenNameEOF;
335 while (fToken != TokenNameEOF) { // && fToken !=
338 hasModifiers = false;
339 if (fToken == TokenNameCOMMENT_PHPDOC) {
340 phpdocOffset = fScanner.getCurrentTokenStartPosition();
341 phpdocLength = fScanner.getCurrentTokenEndPosition()
342 - fScanner.getCurrentTokenStartPosition() + 1;
344 while (fToken == TokenNamestatic
345 || fToken == TokenNamefinal
346 || fToken == TokenNamepublic
347 || fToken == TokenNameprotected
348 || fToken == TokenNameprivate
349 || fToken == TokenNameabstract) {
353 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
357 if (fToken == TokenNamefunction) {
359 if (fToken == TokenNameAND) {
362 if (fToken == TokenNameIdentifier) {
363 ident = fScanner.getCurrentIdentifierSource();
364 addIdentifierInformation('f', ident, buf,
365 phpdocOffset, phpdocLength);
367 parseDeclarations(null, buf, true);
369 } else if (fToken == TokenNameclass
370 || fToken == TokenNameinterface) {
372 if (fToken == TokenNameIdentifier) {
373 ident = fScanner.getCurrentIdentifierSource();
374 addIdentifierInformation('c', ident, buf,
375 phpdocOffset, phpdocLength);
377 if (fToken == TokenNameextends) {
379 while (fToken == TokenNameIdentifier) {
381 .getCurrentIdentifierSource();
383 addIdentifierInformation('e', ident, buf);
385 if (fToken == TokenNameCOMMA) {
390 if (fToken == TokenNameimplements) {
392 while (fToken == TokenNameIdentifier) {
394 .getCurrentIdentifierSource();
396 addIdentifierInformation('e', ident, buf);
398 if (fToken == TokenNameCOMMA) {
403 // skip fTokens for classname, extends and others
406 while (fToken != TokenNameLBRACE
407 && fToken != TokenNameEOF
408 && fToken != TokenNameERROR) {
411 parseDeclarations(ident, buf, true);
413 } else if (fToken == TokenNameVariable) {
415 ident = fScanner.getCurrentIdentifierSource();
416 addIdentifierInformation('g', ident, buf, phpdocOffset,
419 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
420 ident = fScanner.getCurrentIdentifierSource();
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) {
428 if (fToken == TokenNameStringDoubleQuote) {
430 .getCurrentStringLiteralSource();
431 addIdentifierInformation('d', ident, buf,
432 phpdocOffset, phpdocLength);
434 } else if (fToken == TokenNameStringSingleQuote) {
436 .getCurrentStringLiteralSource();
437 addIdentifierInformation('d', ident, buf,
438 phpdocOffset, phpdocLength);
447 } catch (InvalidInputException e) {
449 } catch (SyntaxError e) {
450 // TODO Auto-generated catch block
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());
464 public boolean equals(Object o) {
465 String s = (String) o;
466 return compare(this, o) == 0;
470 private HashMap fFileMap;
472 private String fFilename;
474 private TreeMap fIndentifierMap;
476 public IdentifierIndexManager(String filename) {
477 fFilename = filename;
483 * Check if 2 char arrays are equal
489 private static boolean equalCharArrays(char[] a, char[] b) {
490 if (a.length != b.length) {
493 for (int i = 0; i < b.length; i++) {
501 public LineCreator createLineCreator() {
502 return new LineCreator();
506 * Add the information for a given IFile resource
509 public void addFile(IFile fileToParse) {
510 LineCreator lineCreator = createLineCreator();
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();
524 * @throws CoreException
526 public void addInputStream(InputStream stream, String filePath,
527 LineCreator lineCreator , String charset) throws CoreException {
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) {
538 if (stream != null) {
541 } catch (IOException e) {
548 * Adds a line of the index file for function, class, class-method and
549 * class-variable names
553 private void addLine(String line) {
554 addLine(fIndentifierMap, fFileMap, line, null);
557 public TreeMap getIdentifiers(IFile file) {
558 TreeMap treeMap = new TreeMap(new StringComparator());
559 addIdentifiers(treeMap, file);
563 public TreeMap getIdentifiers(String startClazz) {
564 TreeMap treeMap = new TreeMap(new StringComparator());
565 addIdentifiers(treeMap, startClazz);
569 public void addIdentifiers(TreeMap treeMap, IFile file) {
570 String line = (String) fFileMap.get(file.getProjectRelativePath()
573 PHPIdentifierLocation ident;
574 ArrayList allClassNames = new ArrayList();
575 addLine(treeMap, null, line, allClassNames);
577 while (i < allClassNames.size()) {
578 String clazz = (String) allClassNames.get(i++);
579 addClassName(treeMap, clazz, allClassNames);
584 public void addIdentifiers(TreeMap treeMap, String startClazz) {
585 PHPIdentifierLocation ident;
586 ArrayList allClassNames = new ArrayList();
587 addClassName(treeMap, startClazz, allClassNames);
589 while (i < allClassNames.size()) {
590 String clazz = (String) allClassNames.get(i++);
591 addClassName(treeMap, clazz, allClassNames);
598 * @param allClassNames
600 private boolean addClassName(TreeMap treeMap, String clazz,
601 List allClassNames) {
603 PHPIdentifierLocation ident;
604 List list = getLocations(clazz);
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);
621 * Adds a line of the index file for function, class, class-method and
622 * class-variable names
626 public void addLine(TreeMap treeMap, HashMap fileMap, String line,
627 List allClassNames) {
628 StringTokenizer tokenizer;
629 String phpFileName = null;
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:
639 if (tokenizer.hasMoreTokens()) {
640 phpFileName = tokenizer.nextToken();
641 // System.out.println(token);
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)) {
652 identifier = token.substring(1);
653 classname = identifier;
654 phpIdentifier = new PHPIdentifierLocation(identifier,
655 PHPIdentifier.CLASS, phpFileName);
659 identifier = token.substring(1);
660 phpIdentifier = new PHPIdentifierLocation(identifier,
661 PHPIdentifier.DEFINE, phpFileName);
664 // extends <class name>
667 phpIdentifier = null;
668 if (allClassNames != null) {
669 String extName = token.substring(1);
670 if (!allClassNames.contains(extName)) {
671 allClassNames.add(extName);
677 identifier = token.substring(1);
678 phpIdentifier = new PHPIdentifierLocation(identifier,
679 PHPIdentifier.FUNCTION, phpFileName);
683 identifier = token.substring(1);
684 phpIdentifier = new PHPIdentifierLocation(identifier,
685 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
688 // implements <class name>
691 phpIdentifier = null;
692 if (allClassNames != null) {
693 String implName = token.substring(1);
694 if (!allClassNames.contains(implName)) {
695 allClassNames.add(implName);
700 // constructor function name
701 identifier = token.substring(1);
702 phpIdentifier = new PHPIdentifierLocation(identifier,
703 PHPIdentifier.CONSTRUCTOR, phpFileName);
706 // method inside a class
707 identifier = token.substring(1);
708 phpIdentifier = new PHPIdentifierLocation(identifier,
709 PHPIdentifier.METHOD, phpFileName, classname);
712 // variable inside a class
713 identifier = token.substring(1);
714 phpIdentifier = new PHPIdentifierLocation(identifier,
715 PHPIdentifier.VARIABLE, phpFileName, classname);
718 // offset information
720 if (phpIdentifier != null) {
721 offset = token.substring(1);
722 phpIdentifier.setOffset(Integer.parseInt(offset));
726 // PHPdoc offset information
728 if (phpIdentifier != null) {
729 offset = token.substring(1);
730 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
734 // PHPdoc length information
736 if (phpIdentifier != null) {
737 offset = token.substring(1);
738 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
742 PHPeclipsePlugin.log(IStatus.ERROR,
743 "Unknown token character in IdentifierIndexManager: "
746 phpIdentifier = null;
749 if (identifier != null && phpIdentifier != null) {
751 ArrayList list = (ArrayList) treeMap.get(identifier);
753 list = new ArrayList();
754 list.add(phpIdentifier);
755 treeMap.put(identifier, list);
757 boolean flag = false;
758 for (int i = 0; i < list.size(); i++) {
759 if (list.get(i).equals(phpIdentifier)) {
765 list.add(phpIdentifier);
770 if (fileMap != null) {
771 fileMap.put(phpFileName, line);
773 } catch (Throwable e) {
774 // write to workspace/.metadata/.log file
775 PHPeclipsePlugin.log(e);
777 // if (tokenExists) {
783 * Change the information for a given IFile resource
786 public void changeFile(IFile fileToParse) {
787 removeFile(fileToParse);
788 addFile(fileToParse);
792 * Get a list of all PHPIdentifierLocation object's associated with an
798 public List getLocations(String identifier) {
799 List list = (List) fIndentifierMap.get(identifier);
803 return new ArrayList();
807 * Initialize (i.e. clear) the current index information
810 public void initialize() {
811 fIndentifierMap = new TreeMap(new StringComparator());
812 fFileMap = new HashMap();
815 private void readFile() {
816 FileReader fileReader;
818 fileReader = new FileReader(fFilename);
819 BufferedReader bufferedReader = new BufferedReader(fileReader);
821 while (bufferedReader.ready()) {
822 // all entries for one file are in a line
823 // separated by tabs !
824 line = bufferedReader.readLine();
828 } catch (FileNotFoundException e) {
830 // TODO DialogBox which asks the user if she/he likes to build new
832 } catch (IOException e) {
833 // TODO Auto-generated catch block
839 * Remove the information for a given IFile resource
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());
853 * Removes a line of the index file for function, class, class-method and
854 * class-variable names
858 private void removeLine(String line) {
859 StringTokenizer tokenizer;
860 String phpFileName = null;
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);
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)) {
882 identifier = token.substring(1);
883 classname = identifier;
884 phpIdentifier = new PHPIdentifierLocation(identifier,
885 PHPIdentifier.CLASS, phpFileName);
889 identifier = token.substring(1);
890 phpIdentifier = new PHPIdentifierLocation(identifier,
891 PHPIdentifier.DEFINE, phpFileName);
894 // extends <class name>
896 phpIdentifier = null;
900 identifier = token.substring(1);
901 phpIdentifier = new PHPIdentifierLocation(identifier,
902 PHPIdentifier.FUNCTION, phpFileName);
906 identifier = token.substring(1);
907 phpIdentifier = new PHPIdentifierLocation(identifier,
908 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
911 // implements <class name>
913 phpIdentifier = null;
916 // constructor function name
917 identifier = token.substring(1);
918 phpIdentifier = new PHPIdentifierLocation(identifier,
919 PHPIdentifier.CONSTRUCTOR, phpFileName);
922 // method inside a class
923 identifier = token.substring(1);
924 phpIdentifier = new PHPIdentifierLocation(identifier,
925 PHPIdentifier.METHOD, phpFileName, classname);
928 // offset information
932 // PHPdoc offset information
936 // PHPdoc length information
940 // variable inside a class
941 identifier = token.substring(1);
942 phpIdentifier = new PHPIdentifierLocation(identifier,
943 PHPIdentifier.VARIABLE, phpFileName, classname);
946 PHPeclipsePlugin.log(IStatus.ERROR,
947 "Unknown token character in IdentifierIndexManager: "
950 phpIdentifier = null;
953 if (identifier != null && phpIdentifier != null) {
954 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
957 for (int i = 0; i < list.size(); i++) {
958 if (list.get(i).equals(phpIdentifier)) {
963 if (list.size() == 0) {
964 fIndentifierMap.remove(identifier);
969 fFileMap.remove(phpFileName);
973 * Save the current index information in the projects index file
976 public void writeFile() {
977 FileWriter fileWriter;
979 fileWriter = new FileWriter(fFilename);
981 Collection collection = fFileMap.values();
982 Iterator iterator = collection.iterator();
983 while (iterator.hasNext()) {
984 line = (String) iterator.next();
985 fileWriter.write(line + '\n');
988 } catch (FileNotFoundException e) {
989 // ignore exception; project is deleted by user
990 } catch (IOException e) {
991 // TODO Auto-generated catch block
1001 public SortedMap getIdentifierMap() {
1002 return fIndentifierMap;
1005 synchronized public List getFileList(String filePattern) {
1006 Set set = fFileMap.keySet();
1007 if (set.isEmpty()) {
1010 Iterator iter = set.iterator();
1011 ArrayList list = new ArrayList();
1014 while (iter.hasNext()) {
1015 fileName = (String) iter.next();
1016 if ((index = fileName.indexOf(filePattern)) != -1
1017 && fileName.length() == (index + filePattern.length())) {