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);
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,
71 * 'm'ethod(class), 'v'ariable(class) 'g'lobal variable)
75 * Buffer for the current index line
77 * the offset of the PHPdoc comment if available
79 * the length of the PHPdoc comment if available
81 private void addIdentifierInformation(char typeOfIdentifier,
82 char[] identifier, StringBuffer line, int phpdocOffset,
85 line.append(typeOfIdentifier);
86 line.append(identifier);
87 line.append("\to"); // Offset
88 line.append(fScanner.getCurrentTokenStartPosition());
89 if (phpdocOffset >= 0) {
90 line.append("\tp"); // phpdoc offset
91 line.append(phpdocOffset);
92 line.append("\tl"); // phpdoc length
93 line.append(phpdocLength);
97 private void addClassVariableInformation(char typeOfIdentifier,
98 char[] identifier, StringBuffer line, int phpdocOffset,
101 line.append(typeOfIdentifier);
102 line.append(identifier);
103 line.append("\to"); // Offset
104 // we don't store the '$' in the index for class variables:
105 line.append(fScanner.getCurrentTokenStartPosition() + 1);
106 if (phpdocOffset >= 0) {
107 line.append("\tp"); // phpdoc offset
108 line.append(phpdocOffset);
109 line.append("\tl"); // phpdoc length
110 line.append(phpdocLength);
115 * Get the next token from input
117 private void getNextToken() throws InvalidInputException {
119 fToken = fScanner.getNextToken();
121 int currentEndPosition = fScanner.getCurrentTokenEndPosition();
122 int currentStartPosition = fScanner
123 .getCurrentTokenStartPosition();
124 System.out.print(currentStartPosition + ","
125 + currentEndPosition + ": ");
126 System.out.println(fScanner.toStringAction(fToken));
131 private void skipComments()
135 while (fToken == TokenNameCOMMENT_BLOCK || fToken == TokenNameCOMMENT_PHPDOC) {
138 } catch (InvalidInputException e1) {
139 // TODO Auto-generated catch block
140 e1.printStackTrace();
144 private void parseDeclarations(char[] parent, StringBuffer buf,
147 char[] classVariable;
149 boolean hasModifiers = false;
150 int phpdocOffset = -1;
151 int phpdocLength = -1;
153 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
155 hasModifiers = false;
156 if (fToken == TokenNameCOMMENT_PHPDOC) {
157 phpdocOffset = fScanner.getCurrentTokenStartPosition();
158 phpdocLength = fScanner.getCurrentTokenEndPosition()
159 - fScanner.getCurrentTokenStartPosition() + 1;
161 while (fToken == TokenNamestatic
162 || fToken == TokenNamefinal
163 || fToken == TokenNamepublic
164 || fToken == TokenNameprotected
165 || fToken == TokenNameprivate
166 || fToken == TokenNameabstract) {
170 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
174 if (fToken == TokenNamefunction) {
176 if (fToken == TokenNameAND) {
179 if (fToken == TokenNameIdentifier) {
180 ident = fScanner.getCurrentIdentifierSource();
182 && equalCharArrays(parent, ident)) {
183 // constructor function
184 addIdentifierInformation('k', ident, buf,
185 phpdocOffset, phpdocLength);
187 if (parent != null) {
188 // class method function
189 addIdentifierInformation('m', ident, buf,
190 phpdocOffset, phpdocLength);
192 // nested function ?!
193 addIdentifierInformation('f', ident, buf,
194 phpdocOffset, phpdocLength);
198 parseDeclarations(null, buf, true);
200 } else if (fToken == TokenNameclass
201 || fToken == TokenNameinterface) {
203 if (fToken == TokenNameIdentifier) {
204 ident = fScanner.getCurrentIdentifierSource();
205 addIdentifierInformation('c', ident, buf,
206 phpdocOffset, phpdocLength);
208 if (fToken == TokenNameextends) {
210 while (fToken == TokenNameIdentifier) {
212 .getCurrentIdentifierSource();
214 addIdentifierInformation('e', ident, buf);
216 if (fToken == TokenNameCOMMA) {
221 if (fToken == TokenNameimplements) {
223 while (fToken == TokenNameIdentifier) {
225 .getCurrentIdentifierSource();
227 addIdentifierInformation('e', ident, buf);
229 if (fToken == TokenNameCOMMA) {
235 // skip tokens for classname, extends and others
238 while (fToken != TokenNameLBRACE
239 && fToken != TokenNameEOF
240 && fToken != TokenNameERROR) {
243 parseDeclarations(ident, buf, true);
245 } else if (fToken == TokenNamevar || hasModifiers
246 || fToken == TokenNamestatic
247 || fToken == TokenNamefinal
248 || fToken == TokenNamepublic
249 || fToken == TokenNameprotected
250 || fToken == TokenNameprivate) {
251 while (fToken == TokenNamevar
252 || fToken == TokenNamestatic
253 || fToken == TokenNamefinal
254 || fToken == TokenNamepublic
255 || fToken == TokenNameprotected
256 || fToken == TokenNameprivate) {
259 while (fToken == TokenNameVariable) {
260 ident = fScanner.getCurrentIdentifierSource();
261 classVariable = new char[ident.length - 1];
262 System.arraycopy(ident, 1, classVariable, 0,
264 addClassVariableInformation('v', classVariable,
265 buf, phpdocOffset, phpdocLength);
267 if (fToken == TokenNameCOMMA) {
271 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
272 ident = fScanner.getCurrentIdentifierSource();
274 if (ident.length == 6 && ident[0] == 'd'
275 && ident[1] == 'e' && ident[2] == 'f'
276 && ident[3] == 'i' && ident[4] == 'n'
277 && ident[5] == 'e') {
278 if (fToken == TokenNameLPAREN) {
280 if (fToken == TokenNameStringDoubleQuote) {
282 .getCurrentStringLiteralSource();
283 addIdentifierInformation('d', ident, buf,
284 phpdocOffset, phpdocLength);
286 } else if (fToken == TokenNameStringSingleQuote) {
288 .getCurrentStringLiteralSource();
289 addIdentifierInformation('d', ident, buf,
290 phpdocOffset, phpdocLength);
295 } else if (fToken == TokenNameglobal) {
297 while (fToken != TokenNameEOF
298 && fToken != TokenNameERROR
299 && fToken != TokenNameSEMICOLON
300 && fToken != TokenNameLBRACE
301 && fToken != TokenNameRBRACE) {
303 if (fToken == TokenNameVariable) {
304 ident = fScanner.getCurrentIdentifierSource();
305 addIdentifierInformation('g', ident, buf,
306 phpdocOffset, phpdocLength);
309 } else if (fToken == TokenNameLBRACE) {
312 } else if (fToken == TokenNameRBRACE) {
315 if (counter == 0 && goBack) {
322 } catch (InvalidInputException e) {
324 } catch (SyntaxError e) {
325 // TODO Auto-generated catch block
330 synchronized public void parseIdentifiers(char[] charArray,
334 boolean hasModifiers = false;
335 int phpdocOffset = -1;
336 int phpdocLength = -1;
337 fScanner.setSource(charArray);
338 fScanner.setPHPMode(false);
339 fToken = TokenNameEOF;
342 while (fToken != TokenNameEOF) { // && fToken !=
345 hasModifiers = false;
347 case TokenNameCOMMENT_PHPDOC:
348 phpdocOffset = fScanner.getCurrentTokenStartPosition();
349 phpdocLength = fScanner.getCurrentTokenEndPosition()
350 - fScanner.getCurrentTokenStartPosition() + 1;
352 while (fToken == TokenNamestatic
353 || fToken == TokenNamefinal
354 || fToken == TokenNamepublic
355 || fToken == TokenNameprotected
356 || fToken == TokenNameprivate
357 || fToken == TokenNameabstract) {
361 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
366 case TokenNamefunction:
368 if (fToken == TokenNameAND) {
371 if (fToken == TokenNameIdentifier) {
372 ident = fScanner.getCurrentIdentifierSource();
373 addIdentifierInformation('f', ident, buf,
374 phpdocOffset, phpdocLength);
376 if (fToken == TokenNameLPAREN) {
379 if (fToken == TokenNameVariable) {
380 ident = fScanner.getCurrentIdentifierSource();
381 addIdentifierInformation('v', ident, buf,
382 phpdocOffset, phpdocLength);
384 if (fToken == TokenNameCOMMA) {
389 } while ((fToken != TokenNameRPAREN) && (fToken != 0));
391 parseDeclarations(null, buf, true);
396 case TokenNameinterface:
398 if (fToken == TokenNameIdentifier) {
399 ident = fScanner.getCurrentIdentifierSource();
400 addIdentifierInformation('c', ident, buf,
401 phpdocOffset, phpdocLength);
403 if (fToken == TokenNameextends) {
405 while (fToken == TokenNameIdentifier) {
407 .getCurrentIdentifierSource();
409 addIdentifierInformation('e', ident, buf);
411 if (fToken == TokenNameCOMMA) {
416 if (fToken == TokenNameimplements) {
418 while (fToken == TokenNameIdentifier) {
420 .getCurrentIdentifierSource();
422 addIdentifierInformation('e', ident, buf);
424 if (fToken == TokenNameCOMMA) {
429 // skip fTokens for classname, extends and others
432 while (fToken != TokenNameLBRACE
433 && fToken != TokenNameEOF
434 && fToken != TokenNameERROR) {
437 parseDeclarations(ident, buf, true);
441 case TokenNameVariable:
443 ident = fScanner.getCurrentIdentifierSource();
444 addIdentifierInformation('g', ident, buf, phpdocOffset,
454 } catch (InvalidInputException e) {
456 } catch (SyntaxError e) {
457 // TODO Auto-generated catch block
463 class StringComparator implements Comparator {
464 public int compare(Object o1, Object o2) {
465 String s1 = (String) o1;
466 String s2 = (String) o2;
467 return s1.compareTo(s2);
468 // return s1.toUpperCase().compareTo(s2.toUpperCase());
471 public boolean equals(Object o) {
472 //String s = (String) o;
473 return compare(this, o) == 0;
477 private HashMap fFileMap;
479 private String fFilename;
481 private TreeMap fIndentifierMap;
483 public IdentifierIndexManager(String filename) {
484 fFilename = filename;
490 * Check if 2 char arrays are equal
496 private static boolean equalCharArrays(char[] a, char[] b) {
497 if (a.length != b.length) {
500 for (int i = 0; i < b.length; i++) {
508 public LineCreator createLineCreator() {
509 return new LineCreator();
513 * Add the information for a given IFile resource
516 public void addFile(IFile fileToParse) {
517 LineCreator lineCreator = createLineCreator();
519 addInputStream(new BufferedInputStream(fileToParse.getContents()),
520 fileToParse.getProjectRelativePath().toString(),
521 lineCreator, fileToParse.getCharset());
522 } catch (CoreException e1) {
523 // TODO Auto-generated catch block
524 e1.printStackTrace();
531 * @throws CoreException
533 public void addInputStream(InputStream stream, String filePath,
534 LineCreator lineCreator, String charset) throws CoreException {
536 StringBuffer lineBuffer = new StringBuffer();
537 lineBuffer.append(filePath);
538 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream,
539 -1, charset), lineBuffer);
540 addLine(lineBuffer.toString());
541 } catch (IOException e) {
545 if (stream != null) {
548 } catch (IOException e) {
555 * Adds a line of the index file for function, class, class-method and
556 * class-variable names
560 private void addLine(String line) {
561 addLine(fIndentifierMap, fFileMap, line, null);
564 public TreeMap getIdentifiers(IFile file) {
565 TreeMap treeMap = new TreeMap(new StringComparator());
566 addIdentifiers(treeMap, file);
570 public TreeMap getIdentifiers(String startClazz) {
571 TreeMap treeMap = new TreeMap(new StringComparator());
572 addIdentifiers(treeMap, startClazz);
576 public void addIdentifiers(TreeMap treeMap, IFile file) {
577 String line = (String) fFileMap.get(file.getProjectRelativePath()
580 //PHPIdentifierLocation ident;
581 ArrayList allClassNames = new ArrayList();
582 addLine(treeMap, null, line, allClassNames);
584 while (i < allClassNames.size()) {
585 String clazz = (String) allClassNames.get(i++);
586 addClassName(treeMap, clazz, allClassNames);
591 public void addIdentifiers(TreeMap treeMap, String startClazz) {
592 //PHPIdentifierLocation ident;
593 ArrayList allClassNames = new ArrayList();
594 addClassName(treeMap, startClazz, allClassNames);
596 while (i < allClassNames.size()) {
597 String clazz = (String) allClassNames.get(i++);
598 addClassName(treeMap, clazz, allClassNames);
605 * @param allClassNames
607 private boolean addClassName(TreeMap treeMap, String clazz,
608 List allClassNames) {
610 PHPIdentifierLocation ident;
611 List list = getLocations(clazz);
615 boolean result = false;
616 for (int i = 0; i < list.size(); i++) {
617 ident = (PHPIdentifierLocation) list.get(i);
618 if (ident.isClass()) {
619 line = (String) fFileMap.get(ident.getFilename());
620 addLine(treeMap, null, line, allClassNames);
628 * Adds a line of the index file for function, class, class-method and
629 * class-variable names
633 public void addLine(TreeMap treeMap, HashMap fileMap, String line,
634 List allClassNames) {
635 StringTokenizer tokenizer;
636 String phpFileName = null;
638 String identifier = null;
639 String classname = null;
640 String offset = null;
641 PHPIdentifierLocation phpIdentifier = null;
642 boolean tokenExists = false;
643 tokenizer = new StringTokenizer(line, "\t");
644 // first token contains the filename:
646 if (tokenizer.hasMoreTokens()) {
647 phpFileName = tokenizer.nextToken();
648 // System.out.println(token);
652 // all the other tokens are identifiers:
653 while (tokenizer.hasMoreTokens()) {
654 token = tokenizer.nextToken();
655 // System.out.println(token);
656 switch (token.charAt(0)) {
659 identifier = token.substring(1);
660 classname = identifier;
661 phpIdentifier = new PHPIdentifierLocation(identifier,
662 PHPIdentifier.CLASS, phpFileName);
666 identifier = token.substring(1);
667 phpIdentifier = new PHPIdentifierLocation(identifier,
668 PHPIdentifier.DEFINE, phpFileName);
671 // extends <class name>
674 phpIdentifier = null;
675 if (allClassNames != null) {
676 String extName = token.substring(1);
677 if (!allClassNames.contains(extName)) {
678 allClassNames.add(extName);
684 identifier = token.substring(1);
685 phpIdentifier = new PHPIdentifierLocation(identifier,
686 PHPIdentifier.FUNCTION, phpFileName);
690 identifier = token.substring(1);
691 phpIdentifier = new PHPIdentifierLocation(identifier,
692 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
695 // implements <class name>
698 phpIdentifier = null;
699 if (allClassNames != null) {
700 String implName = token.substring(1);
701 if (!allClassNames.contains(implName)) {
702 allClassNames.add(implName);
707 // constructor function name
708 identifier = token.substring(1);
709 phpIdentifier = new PHPIdentifierLocation(identifier,
710 PHPIdentifier.CONSTRUCTOR, phpFileName);
713 // method inside a class
714 identifier = token.substring(1);
715 phpIdentifier = new PHPIdentifierLocation(identifier,
716 PHPIdentifier.METHOD, phpFileName, classname);
719 // variable inside a class
720 identifier = token.substring(1);
721 phpIdentifier = new PHPIdentifierLocation(identifier,
722 PHPIdentifier.VARIABLE, phpFileName, classname);
725 // offset information
727 if (phpIdentifier != null) {
728 offset = token.substring(1);
729 phpIdentifier.setOffset(Integer.parseInt(offset));
733 // PHPdoc offset information
735 if (phpIdentifier != null) {
736 offset = token.substring(1);
737 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
741 // PHPdoc length information
743 if (phpIdentifier != null) {
744 offset = token.substring(1);
745 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
749 PHPeclipsePlugin.log(IStatus.ERROR,
750 "Unknown token character in IdentifierIndexManager: "
753 phpIdentifier = null;
756 if (identifier != null && phpIdentifier != null) {
758 ArrayList list = (ArrayList) treeMap.get(identifier);
760 list = new ArrayList();
761 list.add(phpIdentifier);
762 treeMap.put(identifier, list);
764 boolean flag = false;
765 for (int i = 0; i < list.size(); i++) {
766 if (list.get(i).equals(phpIdentifier)) {
772 list.add(phpIdentifier);
777 if (fileMap != null) {
778 fileMap.put(phpFileName, line);
780 } catch (Throwable e) {
781 // write to workspace/.metadata/.log file
782 PHPeclipsePlugin.log(e);
784 // if (tokenExists) {
790 * Change the information for a given IFile resource
793 public void changeFile(IFile fileToParse) {
794 removeFile(fileToParse);
795 addFile(fileToParse);
799 * Get a list of all PHPIdentifierLocation object's associated with an
805 public List getLocations(String identifier) {
806 List list = (List) fIndentifierMap.get(identifier);
810 return new ArrayList();
814 * Initialize (i.e. clear) the current index information
817 public void initialize() {
818 fIndentifierMap = new TreeMap(new StringComparator());
819 fFileMap = new HashMap();
822 private void readFile() {
823 FileReader fileReader;
825 fileReader = new FileReader(fFilename);
826 BufferedReader bufferedReader = new BufferedReader(fileReader);
828 while (bufferedReader.ready()) {
829 // all entries for one file are in a line
830 // separated by tabs !
831 line = bufferedReader.readLine();
835 } catch (FileNotFoundException e) {
837 // TODO DialogBox which asks the user if she/he likes to build new
839 } catch (IOException e) {
840 // TODO Auto-generated catch block
846 * Remove the information for a given IFile resource
849 public void removeFile(IFile fileToParse) {
850 // String line = (String)
851 // fFileMap.get(fileToParse.getLocation().toString());
852 String line = (String) fFileMap.get(fileToParse
853 .getProjectRelativePath().toString());
860 * Removes a line of the index file for function, class, class-method and
861 * class-variable names
865 private void removeLine(String line) {
866 StringTokenizer tokenizer;
867 String phpFileName = null;
869 String identifier = null;
870 String classname = null;
871 PHPIdentifier phpIdentifier = null;
872 boolean tokenExists = false;
873 tokenizer = new StringTokenizer(line, "\t");
874 // first token contains the filename:
875 if (tokenizer.hasMoreTokens()) {
876 phpFileName = tokenizer.nextToken();
877 // System.out.println(token);
882 // all the other tokens are identifiers:
883 while (tokenizer.hasMoreTokens()) {
884 token = tokenizer.nextToken();
885 // System.out.println(token);
886 switch (token.charAt(0)) {
889 identifier = token.substring(1);
890 classname = identifier;
891 phpIdentifier = new PHPIdentifierLocation(identifier,
892 PHPIdentifier.CLASS, phpFileName);
896 identifier = token.substring(1);
897 phpIdentifier = new PHPIdentifierLocation(identifier,
898 PHPIdentifier.DEFINE, phpFileName);
901 // extends <class name>
903 phpIdentifier = null;
907 identifier = token.substring(1);
908 phpIdentifier = new PHPIdentifierLocation(identifier,
909 PHPIdentifier.FUNCTION, phpFileName);
913 identifier = token.substring(1);
914 phpIdentifier = new PHPIdentifierLocation(identifier,
915 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
918 // implements <class name>
920 phpIdentifier = null;
923 // constructor function name
924 identifier = token.substring(1);
925 phpIdentifier = new PHPIdentifierLocation(identifier,
926 PHPIdentifier.CONSTRUCTOR, phpFileName);
929 // method inside a class
930 identifier = token.substring(1);
931 phpIdentifier = new PHPIdentifierLocation(identifier,
932 PHPIdentifier.METHOD, phpFileName, classname);
935 // offset information
939 // PHPdoc offset information
943 // PHPdoc length information
947 // variable inside a class
948 identifier = token.substring(1);
949 phpIdentifier = new PHPIdentifierLocation(identifier,
950 PHPIdentifier.VARIABLE, phpFileName, classname);
953 PHPeclipsePlugin.log(IStatus.ERROR,
954 "Unknown token character in IdentifierIndexManager: "
957 phpIdentifier = null;
960 if (identifier != null && phpIdentifier != null) {
961 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
964 for (int i = 0; i < list.size(); i++) {
965 if (list.get(i).equals(phpIdentifier)) {
970 if (list.size() == 0) {
971 fIndentifierMap.remove(identifier);
976 fFileMap.remove(phpFileName);
980 * Save the current index information in the projects index file
983 public void writeFile() {
984 FileWriter fileWriter;
986 fileWriter = new FileWriter(fFilename);
988 Collection collection = fFileMap.values();
989 Iterator iterator = collection.iterator();
990 while (iterator.hasNext()) {
991 line = (String) iterator.next();
992 fileWriter.write(line + '\n');
995 } catch (FileNotFoundException e) {
996 // ignore exception; project is deleted by user
997 } catch (IOException e) {
998 // TODO Auto-generated catch block
1008 public SortedMap getIdentifierMap() {
1009 return fIndentifierMap;
1012 synchronized public List getFileList(String filePattern) {
1013 Set set = fFileMap.keySet();
1014 if (set.isEmpty()) {
1017 Iterator iter = set.iterator();
1018 ArrayList list = new ArrayList();
1021 while (iter.hasNext()) {
1022 fileName = (String) iter.next();
1023 if ((index = fileName.indexOf(filePattern)) != -1
1024 && fileName.length() == (index + filePattern.length())) {