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, true /* taskCaseSensitive */);
48 * Add the information of the current identifier to the line
50 * @param typeOfIdentifier
51 * the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
55 * Buffer for the current index line
57 private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line) {
59 line.append(typeOfIdentifier);
60 line.append(identifier);
61 // line.append("\to"); // Offset
62 // line.append(fScanner.getCurrentTokenStartPosition());
66 * Add the information of the current identifier to the line
68 * @param typeOfIdentifier
69 * the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
73 * Buffer for the current index line
75 * the offset of the PHPdoc comment if available
77 * the length of the PHPdoc comment if available
79 private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
82 line.append(typeOfIdentifier);
83 line.append(identifier);
84 line.append("\to"); // Offset
85 line.append(fScanner.getCurrentTokenStartPosition());
86 if (phpdocOffset >= 0) {
87 line.append("\tp"); // phpdoc offset
88 line.append(phpdocOffset);
89 line.append("\tl"); // phpdoc length
90 line.append(phpdocLength);
94 private void addClassVariableInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
97 line.append(typeOfIdentifier);
98 line.append(identifier);
99 line.append("\to"); // Offset
100 // we don't store the '$' in the index for class variables:
101 line.append(fScanner.getCurrentTokenStartPosition() + 1);
102 if (phpdocOffset >= 0) {
103 line.append("\tp"); // phpdoc offset
104 line.append(phpdocOffset);
105 line.append("\tl"); // phpdoc length
106 line.append(phpdocLength);
111 * Get the next token from input
113 private void getNextToken() throws InvalidInputException {
115 fToken = fScanner.getNextToken();
117 int currentEndPosition = fScanner.getCurrentTokenEndPosition();
118 int currentStartPosition = fScanner.getCurrentTokenStartPosition();
119 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
120 System.out.println(fScanner.toStringAction(fToken));
123 // } catch (InvalidInputException e) {
125 // // e.printStackTrace();
127 // fToken = TokenNameERROR;
130 private void parseDeclarations(char[] parent, StringBuffer buf, boolean goBack) {
132 char[] classVariable;
134 boolean hasModifiers = false;
135 int phpdocOffset = -1;
136 int phpdocLength = -1;
138 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
140 hasModifiers = false;
141 if (fToken == TokenNameCOMMENT_PHPDOC) {
142 phpdocOffset = fScanner.getCurrentTokenStartPosition();
143 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
145 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
146 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
150 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
154 if (fToken == TokenNamefunction) {
156 if (fToken == TokenNameAND) {
159 if (fToken == TokenNameIdentifier) {
160 ident = fScanner.getCurrentIdentifierSource();
161 if (parent != null && equalCharArrays(parent, ident)) {
162 // constructor function
163 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
165 if (parent != null) {
166 // class method function
167 addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
169 // nested function ?!
170 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
174 parseDeclarations(null, buf, true);
176 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
178 if (fToken == TokenNameIdentifier) {
179 ident = fScanner.getCurrentIdentifierSource();
180 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
182 if (fToken == TokenNameextends) {
184 while (fToken == TokenNameIdentifier) {
185 ident = fScanner.getCurrentIdentifierSource();
187 addIdentifierInformation('e', ident, buf);
189 if (fToken == TokenNameCOMMA) {
194 if (fToken == TokenNameimplements) {
196 while (fToken == TokenNameIdentifier) {
197 ident = fScanner.getCurrentIdentifierSource();
199 addIdentifierInformation('e', ident, buf);
201 if (fToken == TokenNameCOMMA) {
206 //skip tokens for classname, extends and others until we have
208 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
211 parseDeclarations(ident, buf, true);
213 } else if (fToken == TokenNamevar || hasModifiers || fToken == TokenNamestatic || fToken == TokenNamefinal
214 || fToken == TokenNamepublic || fToken == TokenNameprotected || fToken == TokenNameprivate) {
215 while (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
216 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
219 while (fToken == TokenNameVariable) {
220 ident = fScanner.getCurrentIdentifierSource();
221 classVariable = new char[ident.length - 1];
222 System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
223 addClassVariableInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
225 if (fToken == TokenNameCOMMA) {
229 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
230 ident = fScanner.getCurrentIdentifierSource();
232 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
233 && ident[5] == 'e') {
234 if (fToken == TokenNameLPAREN) {
236 if (fToken == TokenNameStringDoubleQuote) {
237 ident = fScanner.getCurrentStringLiteralSource();
238 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
240 } else if (fToken == TokenNameStringSingleQuote) {
241 ident = fScanner.getCurrentStringLiteralSource();
242 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
247 } else if (fToken == TokenNameglobal) {
249 while (fToken != TokenNameEOF && fToken != TokenNameERROR && fToken != TokenNameSEMICOLON && fToken != TokenNameLBRACE
250 && fToken != TokenNameRBRACE) {
252 if (fToken == TokenNameVariable) {
253 ident = fScanner.getCurrentIdentifierSource();
254 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
257 } else if (fToken == TokenNameLBRACE) {
260 } else if (fToken == TokenNameRBRACE) {
263 if (counter == 0 && goBack) {
270 } catch (InvalidInputException e) {
272 } catch (SyntaxError e) {
273 // TODO Auto-generated catch block
278 synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
282 boolean hasModifiers = false;
283 int phpdocOffset = -1;
284 int phpdocLength = -1;
285 fScanner.setSource(charArray);
286 fScanner.setPHPMode(false);
287 fToken = TokenNameEOF;
290 while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
292 hasModifiers = false;
293 if (fToken == TokenNameCOMMENT_PHPDOC) {
294 phpdocOffset = fScanner.getCurrentTokenStartPosition();
295 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
297 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
298 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
302 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
306 if (fToken == TokenNamefunction) {
308 if (fToken == TokenNameAND) {
311 if (fToken == TokenNameIdentifier) {
312 ident = fScanner.getCurrentIdentifierSource();
313 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
315 parseDeclarations(null, buf, true);
317 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
319 if (fToken == TokenNameIdentifier) {
320 ident = fScanner.getCurrentIdentifierSource();
321 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
323 if (fToken == TokenNameextends) {
325 while (fToken == TokenNameIdentifier) {
326 ident = fScanner.getCurrentIdentifierSource();
328 addIdentifierInformation('e', ident, buf);
330 if (fToken == TokenNameCOMMA) {
335 if (fToken == TokenNameimplements) {
337 while (fToken == TokenNameIdentifier) {
338 ident = fScanner.getCurrentIdentifierSource();
340 addIdentifierInformation('e', ident, buf);
342 if (fToken == TokenNameCOMMA) {
347 //skip fTokens for classname, extends and others until we have
349 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
352 parseDeclarations(ident, buf, true);
354 } else if (fToken == TokenNameVariable) {
356 ident = fScanner.getCurrentIdentifierSource();
357 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
359 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
360 ident = fScanner.getCurrentIdentifierSource();
362 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
363 && ident[5] == 'e') {
364 if (fToken == TokenNameLPAREN) {
366 if (fToken == TokenNameStringDoubleQuote) {
367 ident = fScanner.getCurrentStringLiteralSource();
368 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
370 } else if (fToken == TokenNameStringSingleQuote) {
371 ident = fScanner.getCurrentStringLiteralSource();
372 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
381 } catch (InvalidInputException e) {
383 } catch (SyntaxError e) {
384 // TODO Auto-generated catch block
390 class StringComparator implements Comparator {
391 public int compare(Object o1, Object o2) {
392 String s1 = (String) o1;
393 String s2 = (String) o2;
394 return s1.compareTo(s2);
395 // return s1.toUpperCase().compareTo(s2.toUpperCase());
398 public boolean equals(Object o) {
399 String s = (String) o;
400 return compare(this, o) == 0;
404 private HashMap fFileMap;
406 private String fFilename;
408 private TreeMap fIndentifierMap;
410 public IdentifierIndexManager(String filename) {
411 fFilename = filename;
417 * Check if 2 char arrays are equal
423 private static boolean equalCharArrays(char[] a, char[] b) {
424 if (a.length != b.length) {
427 for (int i = 0; i < b.length; i++) {
435 public LineCreator createLineCreator() {
436 return new LineCreator();
440 * Add the information for a given IFile resource
443 public void addFile(IFile fileToParse) {
444 // InputStream iStream;
445 LineCreator lineCreator = createLineCreator();
447 addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
449 } catch (CoreException e1) {
450 // TODO Auto-generated catch block
451 e1.printStackTrace();
458 * @throws CoreException
460 public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
462 StringBuffer lineBuffer = new StringBuffer();
463 lineBuffer.append(filePath);
464 int lineLength = lineBuffer.length();
465 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
466 // if (lineLength != lineBuffer.length()) {
467 // always add the file for Open Include Action
468 addLine(lineBuffer.toString());
470 } catch (IOException e) {
474 if (stream != null) {
477 } catch (IOException e) {
483 * Adds a line of the index file for function, class, class-method and class-variable names
487 private void addLine(String line) {
488 addLine(fIndentifierMap, fFileMap, line, null);
491 public TreeMap getIdentifiers(IFile file) {
492 TreeMap treeMap = new TreeMap(new StringComparator());
493 addIdentifiers(treeMap, file);
497 public TreeMap getIdentifiers(String startClazz) {
498 TreeMap treeMap = new TreeMap(new StringComparator());
499 addIdentifiers(treeMap, startClazz);
503 public void addIdentifiers(TreeMap treeMap, IFile file) {
504 String line = (String) fFileMap.get(file.getProjectRelativePath().toString());
506 PHPIdentifierLocation ident;
507 ArrayList allClassNames = new ArrayList();
508 addLine(treeMap, null, line, allClassNames);
510 while (i < allClassNames.size()) {
511 String clazz = (String) allClassNames.get(i++);
512 addClassName(treeMap, clazz, allClassNames);
517 public void addIdentifiers(TreeMap treeMap, String startClazz) {
518 PHPIdentifierLocation ident;
519 ArrayList allClassNames = new ArrayList();
520 addClassName(treeMap, startClazz, allClassNames);
522 while (i < allClassNames.size()) {
523 String clazz = (String) allClassNames.get(i++);
524 addClassName(treeMap, clazz, allClassNames);
531 * @param allClassNames
533 private boolean addClassName(TreeMap treeMap, String clazz, List allClassNames) {
535 PHPIdentifierLocation ident;
536 List list = getLocations(clazz);
540 boolean result = false;
541 for (int i = 0; i < list.size(); i++) {
542 ident = (PHPIdentifierLocation) list.get(i);
543 if (ident.isClass()) {
544 line = (String) fFileMap.get(ident.getFilename());
545 addLine(treeMap, null, line, allClassNames);
553 * Adds a line of the index file for function, class, class-method and class-variable names
557 public void addLine(TreeMap treeMap, HashMap fileMap, String line, List allClassNames) {
558 StringTokenizer tokenizer;
559 String phpFileName = null;
561 String identifier = null;
562 String classname = null;
563 String offset = null;
564 PHPIdentifierLocation phpIdentifier = null;
565 boolean tokenExists = false;
566 tokenizer = new StringTokenizer(line, "\t");
567 // first token contains the filename:
569 if (tokenizer.hasMoreTokens()) {
570 phpFileName = tokenizer.nextToken();
571 //System.out.println(token);
575 // all the other tokens are identifiers:
576 while (tokenizer.hasMoreTokens()) {
577 token = tokenizer.nextToken();
578 //System.out.println(token);
579 switch (token.charAt(0)) {
582 identifier = token.substring(1);
583 classname = identifier;
584 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
588 identifier = token.substring(1);
589 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
592 // extends <class name>
595 phpIdentifier = null;
596 if (allClassNames != null) {
597 String extName = token.substring(1);
598 if (!allClassNames.contains(extName)) {
599 allClassNames.add(extName);
605 identifier = token.substring(1);
606 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
610 identifier = token.substring(1);
611 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
614 // implements <class name>
617 phpIdentifier = null;
618 if (allClassNames != null) {
619 String implName = token.substring(1);
620 if (!allClassNames.contains(implName)) {
621 allClassNames.add(implName);
626 // constructor function name
627 identifier = token.substring(1);
628 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
631 //method inside a class
632 identifier = token.substring(1);
633 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
636 // variable inside a class
637 identifier = token.substring(1);
638 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
641 // offset information
643 if (phpIdentifier != null) {
644 offset = token.substring(1);
645 phpIdentifier.setOffset(Integer.parseInt(offset));
649 // PHPdoc offset information
651 if (phpIdentifier != null) {
652 offset = token.substring(1);
653 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
657 // PHPdoc length information
659 if (phpIdentifier != null) {
660 offset = token.substring(1);
661 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
665 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
667 phpIdentifier = null;
670 if (identifier != null && phpIdentifier != null) {
672 ArrayList list = (ArrayList) treeMap.get(identifier);
674 list = new ArrayList();
675 list.add(phpIdentifier);
676 treeMap.put(identifier, list);
678 boolean flag = false;
679 for (int i = 0; i < list.size(); i++) {
680 if (list.get(i).equals(phpIdentifier)) {
686 list.add(phpIdentifier);
691 if (fileMap != null) {
692 fileMap.put(phpFileName, line);
694 } catch (Throwable e) {
695 // write to workspace/.metadata/.log file
696 PHPeclipsePlugin.log(e);
698 // if (tokenExists) {
704 * Change the information for a given IFile resource
707 public void changeFile(IFile fileToParse) {
708 removeFile(fileToParse);
709 addFile(fileToParse);
713 * Get a list of all PHPIdentifierLocation object's associated with an identifier
718 public List getLocations(String identifier) {
719 List list=(List) fIndentifierMap.get(identifier);
723 return new ArrayList();
727 * Initialize (i.e. clear) the current index information
730 public void initialize() {
731 fIndentifierMap = new TreeMap(new StringComparator());
732 fFileMap = new HashMap();
735 private void readFile() {
736 FileReader fileReader;
738 fileReader = new FileReader(fFilename);
739 BufferedReader bufferedReader = new BufferedReader(fileReader);
741 while (bufferedReader.ready()) {
742 // all entries for one file are in a line
743 // separated by tabs !
744 line = bufferedReader.readLine();
748 } catch (FileNotFoundException e) {
750 // TODO DialogBox which asks the user if she/he likes to build new index?
751 } catch (IOException e) {
752 // TODO Auto-generated catch block
758 * Remove the information for a given IFile resource
761 public void removeFile(IFile fileToParse) {
762 // String line = (String)
763 // fFileMap.get(fileToParse.getLocation().toString());
764 String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
771 * Removes a line of the index file for function, class, class-method and class-variable names
775 private void removeLine(String line) {
776 StringTokenizer tokenizer;
777 String phpFileName = null;
779 String identifier = null;
780 String classname = null;
781 PHPIdentifier phpIdentifier = null;
782 boolean tokenExists = false;
783 tokenizer = new StringTokenizer(line, "\t");
784 // first token contains the filename:
785 if (tokenizer.hasMoreTokens()) {
786 phpFileName = tokenizer.nextToken();
787 //System.out.println(token);
792 // all the other tokens are identifiers:
793 while (tokenizer.hasMoreTokens()) {
794 token = tokenizer.nextToken();
795 //System.out.println(token);
796 switch (token.charAt(0)) {
799 identifier = token.substring(1);
800 classname = identifier;
801 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
805 identifier = token.substring(1);
806 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
809 // extends <class name>
811 phpIdentifier = null;
815 identifier = token.substring(1);
816 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
820 identifier = token.substring(1);
821 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
824 // implements <class name>
826 phpIdentifier = null;
829 // constructor function name
830 identifier = token.substring(1);
831 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
834 //method inside a class
835 identifier = token.substring(1);
836 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
839 // offset information
843 // PHPdoc offset information
847 // PHPdoc length information
851 // variable inside a class
852 identifier = token.substring(1);
853 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
856 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
858 phpIdentifier = null;
861 if (identifier != null && phpIdentifier != null) {
862 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
865 for (int i = 0; i < list.size(); i++) {
866 if (list.get(i).equals(phpIdentifier)) {
871 if (list.size() == 0) {
872 fIndentifierMap.remove(identifier);
877 fFileMap.remove(phpFileName);
881 * Save the current index information in the projects index file
884 public void writeFile() {
885 FileWriter fileWriter;
887 fileWriter = new FileWriter(fFilename);
889 Collection collection = fFileMap.values();
890 Iterator iterator = collection.iterator();
891 while (iterator.hasNext()) {
892 line = (String) iterator.next();
893 fileWriter.write(line + '\n');
896 } catch (FileNotFoundException e) {
897 // ignore exception; project is deleted by user
898 } catch (IOException e) {
899 // TODO Auto-generated catch block
909 public SortedMap getIdentifierMap() {
910 return fIndentifierMap;
913 synchronized public List getFileList(String filePattern) {
914 Set set = fFileMap.keySet();
918 Iterator iter = set.iterator();
919 ArrayList list = new ArrayList();
922 while (iter.hasNext()) {
923 fileName = (String) iter.next();
924 if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {