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.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
18 import java.util.SortedMap;
19 import java.util.StringTokenizer;
20 import java.util.TreeMap;
22 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
23 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
24 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
25 import net.sourceforge.phpdt.internal.compiler.parser.SyntaxError;
26 import net.sourceforge.phpdt.internal.compiler.util.Util;
27 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
28 import net.sourceforge.phpeclipse.obfuscator.PHPIdentifier;
30 import org.eclipse.core.resources.IFile;
31 import org.eclipse.core.runtime.CoreException;
32 import org.eclipse.core.runtime.IStatus;
35 * Manages the identifer index information for a specific project
38 public class IdentifierIndexManager {
39 public class LineCreator implements ITerminalSymbols {
40 private Scanner fScanner;
44 public LineCreator() {
45 fScanner = new Scanner(true, false, false, false, true, null, null, 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, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
56 * Buffer for the current index line
58 private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line) {
60 line.append(typeOfIdentifier);
61 line.append(identifier);
62 // line.append("\to"); // Offset
63 // line.append(fScanner.getCurrentTokenStartPosition());
67 * Add the information of the current identifier to the line
69 * @param typeOfIdentifier
70 * the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
74 * Buffer for the current index line
76 * the offset of the PHPdoc comment if available
78 * the length of the PHPdoc comment if available
80 private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
83 line.append(typeOfIdentifier);
84 line.append(identifier);
85 line.append("\to"); // Offset
86 line.append(fScanner.getCurrentTokenStartPosition());
87 if (phpdocOffset >= 0) {
88 line.append("\tp"); // phpdoc offset
89 line.append(phpdocOffset);
90 line.append("\tl"); // phpdoc length
91 line.append(phpdocLength);
95 private void addClassVariableInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
98 line.append(typeOfIdentifier);
99 line.append(identifier);
100 line.append("\to"); // Offset
101 // we don't store the '$' in the index for class variables:
102 line.append(fScanner.getCurrentTokenStartPosition() + 1);
103 if (phpdocOffset >= 0) {
104 line.append("\tp"); // phpdoc offset
105 line.append(phpdocOffset);
106 line.append("\tl"); // phpdoc length
107 line.append(phpdocLength);
112 * Get the next token from input
114 private void getNextToken() {
116 fToken = fScanner.getNextToken();
118 int currentEndPosition = fScanner.getCurrentTokenEndPosition();
119 int currentStartPosition = fScanner.getCurrentTokenStartPosition();
120 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
121 System.out.println(fScanner.toStringAction(fToken));
124 } catch (InvalidInputException e) {
126 // e.printStackTrace();
128 fToken = TokenNameERROR;
131 private void parseDeclarations(char[] parent, StringBuffer buf, boolean goBack) {
133 char[] classVariable;
135 boolean hasModifiers = false;
136 int phpdocOffset = -1;
137 int phpdocLength = -1;
139 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
141 hasModifiers = false;
142 if (fToken == TokenNameCOMMENT_PHPDOC) {
143 phpdocOffset = fScanner.getCurrentTokenStartPosition();
144 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
146 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
147 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
151 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
155 if (fToken == TokenNamefunction) {
157 if (fToken == TokenNameAND) {
160 if (fToken == TokenNameIdentifier) {
161 ident = fScanner.getCurrentIdentifierSource();
162 if (parent != null && equalCharArrays(parent, ident)) {
163 // constructor function
164 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
166 if (parent != null) {
167 // class method function
168 addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
170 // nested function ?!
171 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
175 parseDeclarations(null, buf, true);
177 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
179 if (fToken == TokenNameIdentifier) {
180 ident = fScanner.getCurrentIdentifierSource();
181 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
183 if (fToken == TokenNameextends) {
185 while (fToken == TokenNameIdentifier) {
186 ident = fScanner.getCurrentIdentifierSource();
188 addIdentifierInformation('e', ident, buf);
190 if (fToken == TokenNameCOMMA) {
195 if (fToken == TokenNameimplements) {
197 while (fToken == TokenNameIdentifier) {
198 ident = fScanner.getCurrentIdentifierSource();
200 addIdentifierInformation('e', ident, buf);
202 if (fToken == TokenNameCOMMA) {
207 //skip tokens for classname, extends and others until we have
209 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
212 parseDeclarations(ident, buf, true);
214 } else if (fToken == TokenNamevar || hasModifiers || fToken == TokenNamestatic || fToken == TokenNamefinal
215 || fToken == TokenNamepublic || fToken == TokenNameprotected || fToken == TokenNameprivate) {
216 while (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
217 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
220 while (fToken == TokenNameVariable) {
221 ident = fScanner.getCurrentIdentifierSource();
222 classVariable = new char[ident.length - 1];
223 System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
224 addClassVariableInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
226 if (fToken == TokenNameCOMMA) {
230 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
231 ident = fScanner.getCurrentIdentifierSource();
233 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
234 && ident[5] == 'e') {
235 if (fToken == TokenNameLPAREN) {
237 if (fToken == TokenNameStringDoubleQuote) {
238 ident = fScanner.getCurrentStringLiteralSource();
239 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
241 } else if (fToken == TokenNameStringSingleQuote) {
242 ident = fScanner.getCurrentStringLiteralSource();
243 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
248 } else if (fToken == TokenNameglobal) {
250 while (fToken != TokenNameEOF && fToken != TokenNameERROR && fToken != TokenNameSEMICOLON && fToken != TokenNameLBRACE
251 && fToken != TokenNameRBRACE) {
253 if (fToken == TokenNameVariable) {
254 ident = fScanner.getCurrentIdentifierSource();
255 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
258 } else if (fToken == TokenNameLBRACE) {
261 } else if (fToken == TokenNameRBRACE) {
264 if (counter == 0 && goBack) {
271 } catch (SyntaxError e) {
272 // TODO Auto-generated catch block
277 synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
281 boolean hasModifiers = false;
282 int phpdocOffset = -1;
283 int phpdocLength = -1;
284 fScanner.setSource(charArray);
285 fScanner.setPHPMode(false);
286 fToken = TokenNameEOF;
289 while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
291 hasModifiers = false;
292 if (fToken == TokenNameCOMMENT_PHPDOC) {
293 phpdocOffset = fScanner.getCurrentTokenStartPosition();
294 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
296 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
297 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
301 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
305 if (fToken == TokenNamefunction) {
307 if (fToken == TokenNameAND) {
310 if (fToken == TokenNameIdentifier) {
311 ident = fScanner.getCurrentIdentifierSource();
312 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
314 parseDeclarations(null, buf, true);
316 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
318 if (fToken == TokenNameIdentifier) {
319 ident = fScanner.getCurrentIdentifierSource();
320 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
322 if (fToken == TokenNameextends) {
324 while (fToken == TokenNameIdentifier) {
325 ident = fScanner.getCurrentIdentifierSource();
327 addIdentifierInformation('e', ident, buf);
329 if (fToken == TokenNameCOMMA) {
334 if (fToken == TokenNameimplements) {
336 while (fToken == TokenNameIdentifier) {
337 ident = fScanner.getCurrentIdentifierSource();
339 addIdentifierInformation('e', ident, buf);
341 if (fToken == TokenNameCOMMA) {
346 //skip fTokens for classname, extends and others until we have
348 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
351 parseDeclarations(ident, buf, true);
353 } else if (fToken == TokenNameVariable) {
355 ident = fScanner.getCurrentIdentifierSource();
356 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
358 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
359 ident = fScanner.getCurrentIdentifierSource();
361 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
362 && ident[5] == 'e') {
363 if (fToken == TokenNameLPAREN) {
365 if (fToken == TokenNameStringDoubleQuote) {
366 ident = fScanner.getCurrentStringLiteralSource();
367 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
369 } else if (fToken == TokenNameStringSingleQuote) {
370 ident = fScanner.getCurrentStringLiteralSource();
371 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
380 } catch (SyntaxError e) {
381 // TODO Auto-generated catch block
387 class StringComparator implements Comparator {
388 public int compare(Object o1, Object o2) {
389 String s1 = (String) o1;
390 String s2 = (String) o2;
391 return s1.compareTo(s2);
392 // return s1.toUpperCase().compareTo(s2.toUpperCase());
395 public boolean equals(Object o) {
396 String s = (String) o;
397 return compare(this, o) == 0;
401 private HashMap fFileMap;
403 private String fFilename;
405 private TreeMap fIndentifierMap;
407 public IdentifierIndexManager(String filename) {
408 fFilename = filename;
414 * Check if 2 char arrays are equal
420 private static boolean equalCharArrays(char[] a, char[] b) {
421 if (a.length != b.length) {
424 for (int i = 0; i < b.length; i++) {
432 public LineCreator createLineCreator() {
433 return new LineCreator();
437 * Add the information for a given IFile resource
440 public void addFile(IFile fileToParse) {
441 // InputStream iStream;
442 LineCreator lineCreator = createLineCreator();
444 addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
446 } catch (CoreException e1) {
447 // TODO Auto-generated catch block
448 e1.printStackTrace();
455 * @throws CoreException
457 public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
459 StringBuffer lineBuffer = new StringBuffer();
460 lineBuffer.append(filePath);
461 int lineLength = lineBuffer.length();
462 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
463 // if (lineLength != lineBuffer.length()) {
464 // always add the file for Open Include Action
465 addLine(lineBuffer.toString());
467 } catch (IOException e) {
471 if (stream != null) {
474 } catch (IOException e) {
480 * Adds a line of the index file for function, class, class-method and class-variable names
484 private void addLine(String line) {
485 addLine(fIndentifierMap, fFileMap, line, null);
488 public TreeMap getIdentifiers(IFile file) {
489 TreeMap treeMap = new TreeMap(new StringComparator());
490 addIdentifiers(treeMap, file);
493 public TreeMap getIdentifiers(String startClazz) {
494 TreeMap treeMap = new TreeMap(new StringComparator());
495 addIdentifiers(treeMap, startClazz);
499 public void addIdentifiers(TreeMap treeMap, IFile file) {
500 String line = (String) fFileMap.get(file.getProjectRelativePath().toString());
502 PHPIdentifierLocation ident;
503 ArrayList allClassNames = new ArrayList();
504 addLine(treeMap, null, line, allClassNames);
506 while (i<allClassNames.size()) {
507 String clazz = (String) allClassNames.get(i++);
508 addClassName(treeMap, clazz, allClassNames);
513 public void addIdentifiers(TreeMap treeMap, String startClazz) {
514 PHPIdentifierLocation ident;
515 ArrayList allClassNames = new ArrayList();
516 addClassName(treeMap, startClazz, allClassNames);
518 while (i<allClassNames.size()) {
519 String clazz = (String) allClassNames.get(i++);
520 addClassName(treeMap, clazz, allClassNames);
527 * @param allClassNames
529 private boolean addClassName(TreeMap treeMap, String clazz, List allClassNames) {
531 PHPIdentifierLocation ident;
532 List list = getLocations(clazz);
536 boolean result = false;
537 for (int i = 0; i < list.size(); i++) {
538 ident = (PHPIdentifierLocation) list.get(i);
539 if (ident.isClass()) {
540 line = (String) fFileMap.get(ident.getFilename());
541 addLine(treeMap, null, line, allClassNames);
549 * Adds a line of the index file for function, class, class-method and class-variable names
553 public void addLine(TreeMap treeMap, HashMap fileMap, String line, List allClassNames) {
554 StringTokenizer tokenizer;
555 String phpFileName = null;
557 String identifier = null;
558 String classname = null;
559 String offset = null;
560 PHPIdentifierLocation phpIdentifier = null;
561 boolean tokenExists = false;
562 tokenizer = new StringTokenizer(line, "\t");
563 // first token contains the filename:
565 if (tokenizer.hasMoreTokens()) {
566 phpFileName = tokenizer.nextToken();
567 //System.out.println(token);
571 // all the other tokens are identifiers:
572 while (tokenizer.hasMoreTokens()) {
573 token = tokenizer.nextToken();
574 //System.out.println(token);
575 switch (token.charAt(0)) {
578 identifier = token.substring(1);
579 classname = identifier;
580 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
584 identifier = token.substring(1);
585 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
588 // extends <class name>
591 phpIdentifier = null;
592 if (allClassNames != null) {
593 String extName = token.substring(1);
594 if (!allClassNames.contains(extName)) {
595 allClassNames.add(extName);
601 identifier = token.substring(1);
602 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
606 identifier = token.substring(1);
607 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
610 // implements <class name>
613 phpIdentifier = null;
614 if (allClassNames != null) {
615 String implName = token.substring(1);
616 if (!allClassNames.contains(implName)) {
617 allClassNames.add(implName);
622 // constructor function name
623 identifier = token.substring(1);
624 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
627 //method inside a class
628 identifier = token.substring(1);
629 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
632 // variable inside a class
633 identifier = token.substring(1);
634 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
637 // offset information
639 if (phpIdentifier != null) {
640 offset = token.substring(1);
641 phpIdentifier.setOffset(Integer.parseInt(offset));
645 // PHPdoc offset information
647 if (phpIdentifier != null) {
648 offset = token.substring(1);
649 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
653 // PHPdoc length information
655 if (phpIdentifier != null) {
656 offset = token.substring(1);
657 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
661 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
663 phpIdentifier = null;
666 if (identifier != null && phpIdentifier != null) {
668 ArrayList list = (ArrayList) treeMap.get(identifier);
670 list = new ArrayList();
671 list.add(phpIdentifier);
672 treeMap.put(identifier, list);
674 boolean flag = false;
675 for (int i = 0; i < list.size(); i++) {
676 if (list.get(i).equals(phpIdentifier)) {
682 list.add(phpIdentifier);
687 if (fileMap != null) {
688 fileMap.put(phpFileName, line);
690 } catch (Throwable e) {
691 // write to workspace/.metadata/.log file
692 PHPeclipsePlugin.log(e);
694 // if (tokenExists) {
700 * Change the information for a given IFile resource
703 public void changeFile(IFile fileToParse) {
704 removeFile(fileToParse);
705 addFile(fileToParse);
709 * Get a list of all PHPIdentifierLocation object's associated with an identifier
714 public List getLocations(String identifier) {
715 return (List) fIndentifierMap.get(identifier);
719 * Initialize (i.e. clear) the current index information
722 public void initialize() {
723 fIndentifierMap = new TreeMap(new StringComparator());
724 fFileMap = new HashMap();
727 private void readFile() {
728 FileReader fileReader;
730 fileReader = new FileReader(fFilename);
731 BufferedReader bufferedReader = new BufferedReader(fileReader);
733 while (bufferedReader.ready()) {
734 // all entries for one file are in a line
735 // separated by tabs !
736 line = bufferedReader.readLine();
740 } catch (FileNotFoundException e) {
742 // TODO DialogBox which asks the user if she/he likes to build new index?
743 } catch (IOException e) {
744 // TODO Auto-generated catch block
750 * Remove the information for a given IFile resource
753 public void removeFile(IFile fileToParse) {
754 // String line = (String)
755 // fFileMap.get(fileToParse.getLocation().toString());
756 String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
763 * Removes a line of the index file for function, class, class-method and class-variable names
767 private void removeLine(String line) {
768 StringTokenizer tokenizer;
769 String phpFileName = null;
771 String identifier = null;
772 String classname = null;
773 PHPIdentifier phpIdentifier = null;
774 boolean tokenExists = false;
775 tokenizer = new StringTokenizer(line, "\t");
776 // first token contains the filename:
777 if (tokenizer.hasMoreTokens()) {
778 phpFileName = tokenizer.nextToken();
779 //System.out.println(token);
784 // all the other tokens are identifiers:
785 while (tokenizer.hasMoreTokens()) {
786 token = tokenizer.nextToken();
787 //System.out.println(token);
788 switch (token.charAt(0)) {
791 identifier = token.substring(1);
792 classname = identifier;
793 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
797 identifier = token.substring(1);
798 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
801 // extends <class name>
803 phpIdentifier = null;
807 identifier = token.substring(1);
808 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
812 identifier = token.substring(1);
813 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
816 // implements <class name>
818 phpIdentifier = null;
821 // constructor function name
822 identifier = token.substring(1);
823 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
826 //method inside a class
827 identifier = token.substring(1);
828 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
831 // offset information
835 // PHPdoc offset information
839 // PHPdoc length information
843 // variable inside a class
844 identifier = token.substring(1);
845 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
848 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
850 phpIdentifier = null;
853 if (identifier != null && phpIdentifier != null) {
854 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
857 for (int i = 0; i < list.size(); i++) {
858 if (list.get(i).equals(phpIdentifier)) {
863 if (list.size() == 0) {
864 fIndentifierMap.remove(identifier);
869 fFileMap.remove(phpFileName);
873 * Save the current index information in the projects index file
876 public void writeFile() {
877 FileWriter fileWriter;
879 fileWriter = new FileWriter(fFilename);
881 Collection collection = fFileMap.values();
882 Iterator iterator = collection.iterator();
883 while (iterator.hasNext()) {
884 line = (String) iterator.next();
885 fileWriter.write(line + '\n');
888 } catch (FileNotFoundException e) {
889 // ignore exception; project is deleted by user
890 } catch (IOException e) {
891 // TODO Auto-generated catch block
901 public SortedMap getIdentifierMap() {
902 return fIndentifierMap;
905 synchronized public List getFileList(String filePattern) {
906 Set set = fFileMap.keySet();
910 Iterator iter = set.iterator();
911 ArrayList list = new ArrayList();
914 while (iter.hasNext()) {
915 fileName = (String) iter.next();
916 if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {