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() throws InvalidInputException {
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 (InvalidInputException e) {
273 } catch (SyntaxError e) {
274 // TODO Auto-generated catch block
279 synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
283 boolean hasModifiers = false;
284 int phpdocOffset = -1;
285 int phpdocLength = -1;
286 fScanner.setSource(charArray);
287 fScanner.setPHPMode(false);
288 fToken = TokenNameEOF;
291 while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
293 hasModifiers = false;
294 if (fToken == TokenNameCOMMENT_PHPDOC) {
295 phpdocOffset = fScanner.getCurrentTokenStartPosition();
296 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
298 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
299 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
303 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
307 if (fToken == TokenNamefunction) {
309 if (fToken == TokenNameAND) {
312 if (fToken == TokenNameIdentifier) {
313 ident = fScanner.getCurrentIdentifierSource();
314 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
316 parseDeclarations(null, buf, true);
318 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
320 if (fToken == TokenNameIdentifier) {
321 ident = fScanner.getCurrentIdentifierSource();
322 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
324 if (fToken == TokenNameextends) {
326 while (fToken == TokenNameIdentifier) {
327 ident = fScanner.getCurrentIdentifierSource();
329 addIdentifierInformation('e', ident, buf);
331 if (fToken == TokenNameCOMMA) {
336 if (fToken == TokenNameimplements) {
338 while (fToken == TokenNameIdentifier) {
339 ident = fScanner.getCurrentIdentifierSource();
341 addIdentifierInformation('e', ident, buf);
343 if (fToken == TokenNameCOMMA) {
348 //skip fTokens for classname, extends and others until we have
350 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
353 parseDeclarations(ident, buf, true);
355 } else if (fToken == TokenNameVariable) {
357 ident = fScanner.getCurrentIdentifierSource();
358 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
360 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
361 ident = fScanner.getCurrentIdentifierSource();
363 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
364 && ident[5] == 'e') {
365 if (fToken == TokenNameLPAREN) {
367 if (fToken == TokenNameStringDoubleQuote) {
368 ident = fScanner.getCurrentStringLiteralSource();
369 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
371 } else if (fToken == TokenNameStringSingleQuote) {
372 ident = fScanner.getCurrentStringLiteralSource();
373 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
382 } catch (InvalidInputException e) {
384 } catch (SyntaxError e) {
385 // TODO Auto-generated catch block
391 class StringComparator implements Comparator {
392 public int compare(Object o1, Object o2) {
393 String s1 = (String) o1;
394 String s2 = (String) o2;
395 return s1.compareTo(s2);
396 // return s1.toUpperCase().compareTo(s2.toUpperCase());
399 public boolean equals(Object o) {
400 String s = (String) o;
401 return compare(this, o) == 0;
405 private HashMap fFileMap;
407 private String fFilename;
409 private TreeMap fIndentifierMap;
411 public IdentifierIndexManager(String filename) {
412 fFilename = filename;
418 * Check if 2 char arrays are equal
424 private static boolean equalCharArrays(char[] a, char[] b) {
425 if (a.length != b.length) {
428 for (int i = 0; i < b.length; i++) {
436 public LineCreator createLineCreator() {
437 return new LineCreator();
441 * Add the information for a given IFile resource
444 public void addFile(IFile fileToParse) {
445 // InputStream iStream;
446 LineCreator lineCreator = createLineCreator();
448 addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
450 } catch (CoreException e1) {
451 // TODO Auto-generated catch block
452 e1.printStackTrace();
459 * @throws CoreException
461 public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
463 StringBuffer lineBuffer = new StringBuffer();
464 lineBuffer.append(filePath);
465 int lineLength = lineBuffer.length();
466 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
467 // if (lineLength != lineBuffer.length()) {
468 // always add the file for Open Include Action
469 addLine(lineBuffer.toString());
471 } catch (IOException e) {
475 if (stream != null) {
478 } catch (IOException e) {
484 * Adds a line of the index file for function, class, class-method and class-variable names
488 private void addLine(String line) {
489 addLine(fIndentifierMap, fFileMap, line, null);
492 public TreeMap getIdentifiers(IFile file) {
493 TreeMap treeMap = new TreeMap(new StringComparator());
494 addIdentifiers(treeMap, file);
498 public TreeMap getIdentifiers(String startClazz) {
499 TreeMap treeMap = new TreeMap(new StringComparator());
500 addIdentifiers(treeMap, startClazz);
504 public void addIdentifiers(TreeMap treeMap, IFile file) {
505 String line = (String) fFileMap.get(file.getProjectRelativePath().toString());
507 PHPIdentifierLocation ident;
508 ArrayList allClassNames = new ArrayList();
509 addLine(treeMap, null, line, allClassNames);
511 while (i < allClassNames.size()) {
512 String clazz = (String) allClassNames.get(i++);
513 addClassName(treeMap, clazz, allClassNames);
518 public void addIdentifiers(TreeMap treeMap, String startClazz) {
519 PHPIdentifierLocation ident;
520 ArrayList allClassNames = new ArrayList();
521 addClassName(treeMap, startClazz, allClassNames);
523 while (i < allClassNames.size()) {
524 String clazz = (String) allClassNames.get(i++);
525 addClassName(treeMap, clazz, allClassNames);
532 * @param allClassNames
534 private boolean addClassName(TreeMap treeMap, String clazz, List allClassNames) {
536 PHPIdentifierLocation ident;
537 List list = getLocations(clazz);
541 boolean result = false;
542 for (int i = 0; i < list.size(); i++) {
543 ident = (PHPIdentifierLocation) list.get(i);
544 if (ident.isClass()) {
545 line = (String) fFileMap.get(ident.getFilename());
546 addLine(treeMap, null, line, allClassNames);
554 * Adds a line of the index file for function, class, class-method and class-variable names
558 public void addLine(TreeMap treeMap, HashMap fileMap, String line, List allClassNames) {
559 StringTokenizer tokenizer;
560 String phpFileName = null;
562 String identifier = null;
563 String classname = null;
564 String offset = null;
565 PHPIdentifierLocation phpIdentifier = null;
566 boolean tokenExists = false;
567 tokenizer = new StringTokenizer(line, "\t");
568 // first token contains the filename:
570 if (tokenizer.hasMoreTokens()) {
571 phpFileName = tokenizer.nextToken();
572 //System.out.println(token);
576 // all the other tokens are identifiers:
577 while (tokenizer.hasMoreTokens()) {
578 token = tokenizer.nextToken();
579 //System.out.println(token);
580 switch (token.charAt(0)) {
583 identifier = token.substring(1);
584 classname = identifier;
585 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
589 identifier = token.substring(1);
590 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
593 // extends <class name>
596 phpIdentifier = null;
597 if (allClassNames != null) {
598 String extName = token.substring(1);
599 if (!allClassNames.contains(extName)) {
600 allClassNames.add(extName);
606 identifier = token.substring(1);
607 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
611 identifier = token.substring(1);
612 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
615 // implements <class name>
618 phpIdentifier = null;
619 if (allClassNames != null) {
620 String implName = token.substring(1);
621 if (!allClassNames.contains(implName)) {
622 allClassNames.add(implName);
627 // constructor function name
628 identifier = token.substring(1);
629 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
632 //method inside a class
633 identifier = token.substring(1);
634 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
637 // variable inside a class
638 identifier = token.substring(1);
639 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
642 // offset information
644 if (phpIdentifier != null) {
645 offset = token.substring(1);
646 phpIdentifier.setOffset(Integer.parseInt(offset));
650 // PHPdoc offset information
652 if (phpIdentifier != null) {
653 offset = token.substring(1);
654 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
658 // PHPdoc length information
660 if (phpIdentifier != null) {
661 offset = token.substring(1);
662 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
666 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
668 phpIdentifier = null;
671 if (identifier != null && phpIdentifier != null) {
673 ArrayList list = (ArrayList) treeMap.get(identifier);
675 list = new ArrayList();
676 list.add(phpIdentifier);
677 treeMap.put(identifier, list);
679 boolean flag = false;
680 for (int i = 0; i < list.size(); i++) {
681 if (list.get(i).equals(phpIdentifier)) {
687 list.add(phpIdentifier);
692 if (fileMap != null) {
693 fileMap.put(phpFileName, line);
695 } catch (Throwable e) {
696 // write to workspace/.metadata/.log file
697 PHPeclipsePlugin.log(e);
699 // if (tokenExists) {
705 * Change the information for a given IFile resource
708 public void changeFile(IFile fileToParse) {
709 removeFile(fileToParse);
710 addFile(fileToParse);
714 * Get a list of all PHPIdentifierLocation object's associated with an identifier
719 public List getLocations(String identifier) {
720 return (List) fIndentifierMap.get(identifier);
724 * Initialize (i.e. clear) the current index information
727 public void initialize() {
728 fIndentifierMap = new TreeMap(new StringComparator());
729 fFileMap = new HashMap();
732 private void readFile() {
733 FileReader fileReader;
735 fileReader = new FileReader(fFilename);
736 BufferedReader bufferedReader = new BufferedReader(fileReader);
738 while (bufferedReader.ready()) {
739 // all entries for one file are in a line
740 // separated by tabs !
741 line = bufferedReader.readLine();
745 } catch (FileNotFoundException e) {
747 // TODO DialogBox which asks the user if she/he likes to build new index?
748 } catch (IOException e) {
749 // TODO Auto-generated catch block
755 * Remove the information for a given IFile resource
758 public void removeFile(IFile fileToParse) {
759 // String line = (String)
760 // fFileMap.get(fileToParse.getLocation().toString());
761 String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
768 * Removes a line of the index file for function, class, class-method and class-variable names
772 private void removeLine(String line) {
773 StringTokenizer tokenizer;
774 String phpFileName = null;
776 String identifier = null;
777 String classname = null;
778 PHPIdentifier phpIdentifier = null;
779 boolean tokenExists = false;
780 tokenizer = new StringTokenizer(line, "\t");
781 // first token contains the filename:
782 if (tokenizer.hasMoreTokens()) {
783 phpFileName = tokenizer.nextToken();
784 //System.out.println(token);
789 // all the other tokens are identifiers:
790 while (tokenizer.hasMoreTokens()) {
791 token = tokenizer.nextToken();
792 //System.out.println(token);
793 switch (token.charAt(0)) {
796 identifier = token.substring(1);
797 classname = identifier;
798 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
802 identifier = token.substring(1);
803 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
806 // extends <class name>
808 phpIdentifier = null;
812 identifier = token.substring(1);
813 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
817 identifier = token.substring(1);
818 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
821 // implements <class name>
823 phpIdentifier = null;
826 // constructor function name
827 identifier = token.substring(1);
828 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
831 //method inside a class
832 identifier = token.substring(1);
833 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
836 // offset information
840 // PHPdoc offset information
844 // PHPdoc length information
848 // variable inside a class
849 identifier = token.substring(1);
850 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
853 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
855 phpIdentifier = null;
858 if (identifier != null && phpIdentifier != null) {
859 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
862 for (int i = 0; i < list.size(); i++) {
863 if (list.get(i).equals(phpIdentifier)) {
868 if (list.size() == 0) {
869 fIndentifierMap.remove(identifier);
874 fFileMap.remove(phpFileName);
878 * Save the current index information in the projects index file
881 public void writeFile() {
882 FileWriter fileWriter;
884 fileWriter = new FileWriter(fFilename);
886 Collection collection = fFileMap.values();
887 Iterator iterator = collection.iterator();
888 while (iterator.hasNext()) {
889 line = (String) iterator.next();
890 fileWriter.write(line + '\n');
893 } catch (FileNotFoundException e) {
894 // ignore exception; project is deleted by user
895 } catch (IOException e) {
896 // TODO Auto-generated catch block
906 public SortedMap getIdentifierMap() {
907 return fIndentifierMap;
910 synchronized public List getFileList(String filePattern) {
911 Set set = fFileMap.keySet();
915 Iterator iter = set.iterator();
916 ArrayList list = new ArrayList();
919 while (iter.hasNext()) {
920 fileName = (String) iter.next();
921 if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {