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 parseDeclarations(char[] parent, StringBuffer buf,
134 char[] classVariable;
136 boolean hasModifiers = false;
137 int phpdocOffset = -1;
138 int phpdocLength = -1;
140 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
142 hasModifiers = false;
143 if (fToken == TokenNameCOMMENT_PHPDOC) {
144 phpdocOffset = fScanner.getCurrentTokenStartPosition();
145 phpdocLength = fScanner.getCurrentTokenEndPosition()
146 - fScanner.getCurrentTokenStartPosition() + 1;
148 while (fToken == TokenNamestatic
149 || fToken == TokenNamefinal
150 || fToken == TokenNamepublic
151 || fToken == TokenNameprotected
152 || fToken == TokenNameprivate
153 || fToken == TokenNameabstract) {
157 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
161 if (fToken == TokenNamefunction) {
163 if (fToken == TokenNameAND) {
166 if (fToken == TokenNameIdentifier) {
167 ident = fScanner.getCurrentIdentifierSource();
169 && equalCharArrays(parent, ident)) {
170 // constructor function
171 addIdentifierInformation('k', ident, buf,
172 phpdocOffset, phpdocLength);
174 if (parent != null) {
175 // class method function
176 addIdentifierInformation('m', ident, buf,
177 phpdocOffset, phpdocLength);
179 // nested function ?!
180 addIdentifierInformation('f', ident, buf,
181 phpdocOffset, phpdocLength);
185 parseDeclarations(null, buf, true);
187 } else if (fToken == TokenNameclass
188 || fToken == TokenNameinterface) {
190 if (fToken == TokenNameIdentifier) {
191 ident = fScanner.getCurrentIdentifierSource();
192 addIdentifierInformation('c', ident, buf,
193 phpdocOffset, phpdocLength);
195 if (fToken == TokenNameextends) {
197 while (fToken == TokenNameIdentifier) {
199 .getCurrentIdentifierSource();
201 addIdentifierInformation('e', ident, buf);
203 if (fToken == TokenNameCOMMA) {
208 if (fToken == TokenNameimplements) {
210 while (fToken == TokenNameIdentifier) {
212 .getCurrentIdentifierSource();
214 addIdentifierInformation('e', ident, buf);
216 if (fToken == TokenNameCOMMA) {
221 // skip tokens for classname, extends and others
224 while (fToken != TokenNameLBRACE
225 && fToken != TokenNameEOF
226 && fToken != TokenNameERROR) {
229 parseDeclarations(ident, buf, true);
231 } else if (fToken == TokenNamevar || hasModifiers
232 || fToken == TokenNamestatic
233 || fToken == TokenNamefinal
234 || fToken == TokenNamepublic
235 || fToken == TokenNameprotected
236 || fToken == TokenNameprivate) {
237 while (fToken == TokenNamevar
238 || fToken == TokenNamestatic
239 || fToken == TokenNamefinal
240 || fToken == TokenNamepublic
241 || fToken == TokenNameprotected
242 || fToken == TokenNameprivate) {
245 while (fToken == TokenNameVariable) {
246 ident = fScanner.getCurrentIdentifierSource();
247 classVariable = new char[ident.length - 1];
248 System.arraycopy(ident, 1, classVariable, 0,
250 addClassVariableInformation('v', classVariable,
251 buf, phpdocOffset, phpdocLength);
253 if (fToken == TokenNameCOMMA) {
257 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
258 ident = fScanner.getCurrentIdentifierSource();
260 if (ident.length == 6 && ident[0] == 'd'
261 && ident[1] == 'e' && ident[2] == 'f'
262 && ident[3] == 'i' && ident[4] == 'n'
263 && ident[5] == 'e') {
264 if (fToken == TokenNameLPAREN) {
266 if (fToken == TokenNameStringDoubleQuote) {
268 .getCurrentStringLiteralSource();
269 addIdentifierInformation('d', ident, buf,
270 phpdocOffset, phpdocLength);
272 } else if (fToken == TokenNameStringSingleQuote) {
274 .getCurrentStringLiteralSource();
275 addIdentifierInformation('d', ident, buf,
276 phpdocOffset, phpdocLength);
281 } else if (fToken == TokenNameglobal) {
283 while (fToken != TokenNameEOF
284 && fToken != TokenNameERROR
285 && fToken != TokenNameSEMICOLON
286 && fToken != TokenNameLBRACE
287 && fToken != TokenNameRBRACE) {
289 if (fToken == TokenNameVariable) {
290 ident = fScanner.getCurrentIdentifierSource();
291 addIdentifierInformation('g', ident, buf,
292 phpdocOffset, phpdocLength);
295 } else if (fToken == TokenNameLBRACE) {
298 } else if (fToken == TokenNameRBRACE) {
301 if (counter == 0 && goBack) {
308 } catch (InvalidInputException e) {
310 } catch (SyntaxError e) {
311 // TODO Auto-generated catch block
316 synchronized public void parseIdentifiers(char[] charArray,
320 boolean hasModifiers = false;
321 int phpdocOffset = -1;
322 int phpdocLength = -1;
323 fScanner.setSource(charArray);
324 fScanner.setPHPMode(false);
325 fToken = TokenNameEOF;
328 while (fToken != TokenNameEOF) { // && fToken !=
331 hasModifiers = false;
332 if (fToken == TokenNameCOMMENT_PHPDOC) {
333 phpdocOffset = fScanner.getCurrentTokenStartPosition();
334 phpdocLength = fScanner.getCurrentTokenEndPosition()
335 - fScanner.getCurrentTokenStartPosition() + 1;
337 while (fToken == TokenNamestatic
338 || fToken == TokenNamefinal
339 || fToken == TokenNamepublic
340 || fToken == TokenNameprotected
341 || fToken == TokenNameprivate
342 || fToken == TokenNameabstract) {
346 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
350 if (fToken == TokenNamefunction) {
352 if (fToken == TokenNameAND) {
355 if (fToken == TokenNameIdentifier) {
356 ident = fScanner.getCurrentIdentifierSource();
357 addIdentifierInformation('f', ident, buf,
358 phpdocOffset, phpdocLength);
360 parseDeclarations(null, buf, true);
362 } else if (fToken == TokenNameclass
363 || fToken == TokenNameinterface) {
365 if (fToken == TokenNameIdentifier) {
366 ident = fScanner.getCurrentIdentifierSource();
367 addIdentifierInformation('c', ident, buf,
368 phpdocOffset, phpdocLength);
370 if (fToken == TokenNameextends) {
372 while (fToken == TokenNameIdentifier) {
374 .getCurrentIdentifierSource();
376 addIdentifierInformation('e', ident, buf);
378 if (fToken == TokenNameCOMMA) {
383 if (fToken == TokenNameimplements) {
385 while (fToken == TokenNameIdentifier) {
387 .getCurrentIdentifierSource();
389 addIdentifierInformation('e', ident, buf);
391 if (fToken == TokenNameCOMMA) {
396 // skip fTokens for classname, extends and others
399 while (fToken != TokenNameLBRACE
400 && fToken != TokenNameEOF
401 && fToken != TokenNameERROR) {
404 parseDeclarations(ident, buf, true);
406 } else if (fToken == TokenNameVariable) {
408 ident = fScanner.getCurrentIdentifierSource();
409 addIdentifierInformation('g', ident, buf, phpdocOffset,
412 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
413 ident = fScanner.getCurrentIdentifierSource();
415 if (ident.length == 6 && ident[0] == 'd'
416 && ident[1] == 'e' && ident[2] == 'f'
417 && ident[3] == 'i' && ident[4] == 'n'
418 && ident[5] == 'e') {
419 if (fToken == TokenNameLPAREN) {
421 if (fToken == TokenNameStringDoubleQuote) {
423 .getCurrentStringLiteralSource();
424 addIdentifierInformation('d', ident, buf,
425 phpdocOffset, phpdocLength);
427 } else if (fToken == TokenNameStringSingleQuote) {
429 .getCurrentStringLiteralSource();
430 addIdentifierInformation('d', ident, buf,
431 phpdocOffset, phpdocLength);
440 } catch (InvalidInputException e) {
442 } catch (SyntaxError e) {
443 // TODO Auto-generated catch block
449 class StringComparator implements Comparator {
450 public int compare(Object o1, Object o2) {
451 String s1 = (String) o1;
452 String s2 = (String) o2;
453 return s1.compareTo(s2);
454 // return s1.toUpperCase().compareTo(s2.toUpperCase());
457 public boolean equals(Object o) {
458 String s = (String) o;
459 return compare(this, o) == 0;
463 private HashMap fFileMap;
465 private String fFilename;
467 private TreeMap fIndentifierMap;
469 public IdentifierIndexManager(String filename) {
470 fFilename = filename;
476 * Check if 2 char arrays are equal
482 private static boolean equalCharArrays(char[] a, char[] b) {
483 if (a.length != b.length) {
486 for (int i = 0; i < b.length; i++) {
494 public LineCreator createLineCreator() {
495 return new LineCreator();
499 * Add the information for a given IFile resource
502 public void addFile(IFile fileToParse) {
503 LineCreator lineCreator = createLineCreator();
505 addInputStream(new BufferedInputStream(fileToParse.getContents()),
506 fileToParse.getProjectRelativePath().toString(),
507 lineCreator, fileToParse.getCharset());
508 } catch (CoreException e1) {
509 // TODO Auto-generated catch block
510 e1.printStackTrace();
517 * @throws CoreException
519 public void addInputStream(InputStream stream, String filePath,
520 LineCreator lineCreator, String charset) throws CoreException {
522 StringBuffer lineBuffer = new StringBuffer();
523 lineBuffer.append(filePath);
524 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream,
525 -1, charset), lineBuffer);
526 addLine(lineBuffer.toString());
527 } catch (IOException e) {
531 if (stream != null) {
534 } catch (IOException e) {
541 * Adds a line of the index file for function, class, class-method and
542 * class-variable names
546 private void addLine(String line) {
547 addLine(fIndentifierMap, fFileMap, line, null);
550 public TreeMap getIdentifiers(IFile file) {
551 TreeMap treeMap = new TreeMap(new StringComparator());
552 addIdentifiers(treeMap, file);
556 public TreeMap getIdentifiers(String startClazz) {
557 TreeMap treeMap = new TreeMap(new StringComparator());
558 addIdentifiers(treeMap, startClazz);
562 public void addIdentifiers(TreeMap treeMap, IFile file) {
563 String line = (String) fFileMap.get(file.getProjectRelativePath()
566 PHPIdentifierLocation ident;
567 ArrayList allClassNames = new ArrayList();
568 addLine(treeMap, null, line, allClassNames);
570 while (i < allClassNames.size()) {
571 String clazz = (String) allClassNames.get(i++);
572 addClassName(treeMap, clazz, allClassNames);
577 public void addIdentifiers(TreeMap treeMap, String startClazz) {
578 PHPIdentifierLocation ident;
579 ArrayList allClassNames = new ArrayList();
580 addClassName(treeMap, startClazz, allClassNames);
582 while (i < allClassNames.size()) {
583 String clazz = (String) allClassNames.get(i++);
584 addClassName(treeMap, clazz, allClassNames);
591 * @param allClassNames
593 private boolean addClassName(TreeMap treeMap, String clazz,
594 List allClassNames) {
596 PHPIdentifierLocation ident;
597 List list = getLocations(clazz);
601 boolean result = false;
602 for (int i = 0; i < list.size(); i++) {
603 ident = (PHPIdentifierLocation) list.get(i);
604 if (ident.isClass()) {
605 line = (String) fFileMap.get(ident.getFilename());
606 addLine(treeMap, null, line, allClassNames);
614 * Adds a line of the index file for function, class, class-method and
615 * class-variable names
619 public void addLine(TreeMap treeMap, HashMap fileMap, String line,
620 List allClassNames) {
621 StringTokenizer tokenizer;
622 String phpFileName = null;
624 String identifier = null;
625 String classname = null;
626 String offset = null;
627 PHPIdentifierLocation phpIdentifier = null;
628 boolean tokenExists = false;
629 tokenizer = new StringTokenizer(line, "\t");
630 // first token contains the filename:
632 if (tokenizer.hasMoreTokens()) {
633 phpFileName = tokenizer.nextToken();
634 // System.out.println(token);
638 // all the other tokens are identifiers:
639 while (tokenizer.hasMoreTokens()) {
640 token = tokenizer.nextToken();
641 // System.out.println(token);
642 switch (token.charAt(0)) {
645 identifier = token.substring(1);
646 classname = identifier;
647 phpIdentifier = new PHPIdentifierLocation(identifier,
648 PHPIdentifier.CLASS, phpFileName);
652 identifier = token.substring(1);
653 phpIdentifier = new PHPIdentifierLocation(identifier,
654 PHPIdentifier.DEFINE, phpFileName);
657 // extends <class name>
660 phpIdentifier = null;
661 if (allClassNames != null) {
662 String extName = token.substring(1);
663 if (!allClassNames.contains(extName)) {
664 allClassNames.add(extName);
670 identifier = token.substring(1);
671 phpIdentifier = new PHPIdentifierLocation(identifier,
672 PHPIdentifier.FUNCTION, phpFileName);
676 identifier = token.substring(1);
677 phpIdentifier = new PHPIdentifierLocation(identifier,
678 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
681 // implements <class name>
684 phpIdentifier = null;
685 if (allClassNames != null) {
686 String implName = token.substring(1);
687 if (!allClassNames.contains(implName)) {
688 allClassNames.add(implName);
693 // constructor function name
694 identifier = token.substring(1);
695 phpIdentifier = new PHPIdentifierLocation(identifier,
696 PHPIdentifier.CONSTRUCTOR, phpFileName);
699 // method inside a class
700 identifier = token.substring(1);
701 phpIdentifier = new PHPIdentifierLocation(identifier,
702 PHPIdentifier.METHOD, phpFileName, classname);
705 // variable inside a class
706 identifier = token.substring(1);
707 phpIdentifier = new PHPIdentifierLocation(identifier,
708 PHPIdentifier.VARIABLE, phpFileName, classname);
711 // offset information
713 if (phpIdentifier != null) {
714 offset = token.substring(1);
715 phpIdentifier.setOffset(Integer.parseInt(offset));
719 // PHPdoc offset information
721 if (phpIdentifier != null) {
722 offset = token.substring(1);
723 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
727 // PHPdoc length information
729 if (phpIdentifier != null) {
730 offset = token.substring(1);
731 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
735 PHPeclipsePlugin.log(IStatus.ERROR,
736 "Unknown token character in IdentifierIndexManager: "
739 phpIdentifier = null;
742 if (identifier != null && phpIdentifier != null) {
744 ArrayList list = (ArrayList) treeMap.get(identifier);
746 list = new ArrayList();
747 list.add(phpIdentifier);
748 treeMap.put(identifier, list);
750 boolean flag = false;
751 for (int i = 0; i < list.size(); i++) {
752 if (list.get(i).equals(phpIdentifier)) {
758 list.add(phpIdentifier);
763 if (fileMap != null) {
764 fileMap.put(phpFileName, line);
766 } catch (Throwable e) {
767 // write to workspace/.metadata/.log file
768 PHPeclipsePlugin.log(e);
770 // if (tokenExists) {
776 * Change the information for a given IFile resource
779 public void changeFile(IFile fileToParse) {
780 removeFile(fileToParse);
781 addFile(fileToParse);
785 * Get a list of all PHPIdentifierLocation object's associated with an
791 public List getLocations(String identifier) {
792 List list = (List) fIndentifierMap.get(identifier);
796 return new ArrayList();
800 * Initialize (i.e. clear) the current index information
803 public void initialize() {
804 fIndentifierMap = new TreeMap(new StringComparator());
805 fFileMap = new HashMap();
808 private void readFile() {
809 FileReader fileReader;
811 fileReader = new FileReader(fFilename);
812 BufferedReader bufferedReader = new BufferedReader(fileReader);
814 while (bufferedReader.ready()) {
815 // all entries for one file are in a line
816 // separated by tabs !
817 line = bufferedReader.readLine();
821 } catch (FileNotFoundException e) {
823 // TODO DialogBox which asks the user if she/he likes to build new
825 } catch (IOException e) {
826 // TODO Auto-generated catch block
832 * Remove the information for a given IFile resource
835 public void removeFile(IFile fileToParse) {
836 // String line = (String)
837 // fFileMap.get(fileToParse.getLocation().toString());
838 String line = (String) fFileMap.get(fileToParse
839 .getProjectRelativePath().toString());
846 * Removes a line of the index file for function, class, class-method and
847 * class-variable names
851 private void removeLine(String line) {
852 StringTokenizer tokenizer;
853 String phpFileName = null;
855 String identifier = null;
856 String classname = null;
857 PHPIdentifier phpIdentifier = null;
858 boolean tokenExists = false;
859 tokenizer = new StringTokenizer(line, "\t");
860 // first token contains the filename:
861 if (tokenizer.hasMoreTokens()) {
862 phpFileName = tokenizer.nextToken();
863 // System.out.println(token);
868 // all the other tokens are identifiers:
869 while (tokenizer.hasMoreTokens()) {
870 token = tokenizer.nextToken();
871 // System.out.println(token);
872 switch (token.charAt(0)) {
875 identifier = token.substring(1);
876 classname = identifier;
877 phpIdentifier = new PHPIdentifierLocation(identifier,
878 PHPIdentifier.CLASS, phpFileName);
882 identifier = token.substring(1);
883 phpIdentifier = new PHPIdentifierLocation(identifier,
884 PHPIdentifier.DEFINE, phpFileName);
887 // extends <class name>
889 phpIdentifier = null;
893 identifier = token.substring(1);
894 phpIdentifier = new PHPIdentifierLocation(identifier,
895 PHPIdentifier.FUNCTION, phpFileName);
899 identifier = token.substring(1);
900 phpIdentifier = new PHPIdentifierLocation(identifier,
901 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
904 // implements <class name>
906 phpIdentifier = null;
909 // constructor function name
910 identifier = token.substring(1);
911 phpIdentifier = new PHPIdentifierLocation(identifier,
912 PHPIdentifier.CONSTRUCTOR, phpFileName);
915 // method inside a class
916 identifier = token.substring(1);
917 phpIdentifier = new PHPIdentifierLocation(identifier,
918 PHPIdentifier.METHOD, phpFileName, classname);
921 // offset information
925 // PHPdoc offset information
929 // PHPdoc length information
933 // variable inside a class
934 identifier = token.substring(1);
935 phpIdentifier = new PHPIdentifierLocation(identifier,
936 PHPIdentifier.VARIABLE, phpFileName, classname);
939 PHPeclipsePlugin.log(IStatus.ERROR,
940 "Unknown token character in IdentifierIndexManager: "
943 phpIdentifier = null;
946 if (identifier != null && phpIdentifier != null) {
947 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
950 for (int i = 0; i < list.size(); i++) {
951 if (list.get(i).equals(phpIdentifier)) {
956 if (list.size() == 0) {
957 fIndentifierMap.remove(identifier);
962 fFileMap.remove(phpFileName);
966 * Save the current index information in the projects index file
969 public void writeFile() {
970 FileWriter fileWriter;
972 fileWriter = new FileWriter(fFilename);
974 Collection collection = fFileMap.values();
975 Iterator iterator = collection.iterator();
976 while (iterator.hasNext()) {
977 line = (String) iterator.next();
978 fileWriter.write(line + '\n');
981 } catch (FileNotFoundException e) {
982 // ignore exception; project is deleted by user
983 } catch (IOException e) {
984 // TODO Auto-generated catch block
994 public SortedMap getIdentifierMap() {
995 return fIndentifierMap;
998 synchronized public List getFileList(String filePattern) {
999 Set set = fFileMap.keySet();
1000 if (set.isEmpty()) {
1003 Iterator iter = set.iterator();
1004 ArrayList list = new ArrayList();
1007 while (iter.hasNext()) {
1008 fileName = (String) iter.next();
1009 if ((index = fileName.indexOf(filePattern)) != -1
1010 && fileName.length() == (index + filePattern.length())) {