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;
154 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
156 hasModifiers = false;
157 if (fToken == TokenNameCOMMENT_PHPDOC) {
158 phpdocOffset = fScanner.getCurrentTokenStartPosition();
159 phpdocLength = fScanner.getCurrentTokenEndPosition()
160 - fScanner.getCurrentTokenStartPosition() + 1;
162 while (fToken == TokenNamestatic
163 || fToken == TokenNamefinal
164 || fToken == TokenNamepublic
165 || fToken == TokenNameprotected
166 || fToken == TokenNameprivate
167 || fToken == TokenNameabstract) {
171 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
175 if (fToken == TokenNamefunction) {
177 if (fToken == TokenNameAND) {
180 if (fToken == TokenNameIdentifier) {
181 ident = fScanner.getCurrentIdentifierSource();
183 && equalCharArrays(parent, ident)) {
184 // constructor function
185 addIdentifierInformation('k', ident, buf,
186 phpdocOffset, phpdocLength);
188 if (parent != null) {
189 // class method function
190 addIdentifierInformation('m', ident, buf,
191 phpdocOffset, phpdocLength);
193 // nested function ?!
194 addIdentifierInformation('f', ident, buf,
195 phpdocOffset, phpdocLength);
199 parseDeclarations(null, buf, true);
201 } else if (fToken == TokenNameclass
202 || fToken == TokenNameinterface) {
204 if (fToken == TokenNameIdentifier) {
205 ident = fScanner.getCurrentIdentifierSource();
206 addIdentifierInformation('c', ident, buf,
207 phpdocOffset, phpdocLength);
209 if (fToken == TokenNameextends) {
211 while (fToken == TokenNameIdentifier) {
213 .getCurrentIdentifierSource();
215 addIdentifierInformation('e', ident, buf);
217 if (fToken == TokenNameCOMMA) {
222 if (fToken == TokenNameimplements) {
224 while (fToken == TokenNameIdentifier) {
226 .getCurrentIdentifierSource();
228 addIdentifierInformation('e', ident, buf);
230 if (fToken == TokenNameCOMMA) {
236 // skip tokens for classname, extends and others
239 while (fToken != TokenNameLBRACE
240 && fToken != TokenNameEOF
241 && fToken != TokenNameERROR) {
244 parseDeclarations(ident, buf, true);
246 } else if (fToken == TokenNamevar || hasModifiers
247 || fToken == TokenNamestatic
248 || fToken == TokenNamefinal
249 || fToken == TokenNamepublic
250 || fToken == TokenNameprotected
251 || fToken == TokenNameprivate) {
252 while (fToken == TokenNamevar
253 || fToken == TokenNamestatic
254 || fToken == TokenNamefinal
255 || fToken == TokenNamepublic
256 || fToken == TokenNameprotected
257 || fToken == TokenNameprivate) {
260 while (fToken == TokenNameVariable) {
261 ident = fScanner.getCurrentIdentifierSource();
262 classVariable = new char[ident.length - 1];
263 System.arraycopy(ident, 1, classVariable, 0,
265 addClassVariableInformation('v', classVariable,
266 buf, phpdocOffset, phpdocLength);
268 if (fToken == TokenNameCOMMA) {
272 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
273 ident = fScanner.getCurrentIdentifierSource();
275 if (ident.length == 6 && ident[0] == 'd'
276 && ident[1] == 'e' && ident[2] == 'f'
277 && ident[3] == 'i' && ident[4] == 'n'
278 && ident[5] == 'e') {
279 if (fToken == TokenNameLPAREN) {
281 if (fToken == TokenNameStringDoubleQuote) {
283 .getCurrentStringLiteralSource();
284 addIdentifierInformation('d', ident, buf,
285 phpdocOffset, phpdocLength);
287 } else if (fToken == TokenNameStringSingleQuote) {
289 .getCurrentStringLiteralSource();
290 addIdentifierInformation('d', ident, buf,
291 phpdocOffset, phpdocLength);
296 } else if (fToken == TokenNameglobal) {
298 while (fToken != TokenNameEOF
299 && fToken != TokenNameERROR
300 && fToken != TokenNameSEMICOLON
301 && fToken != TokenNameLBRACE
302 && fToken != TokenNameRBRACE) {
304 if (fToken == TokenNameVariable) {
305 ident = fScanner.getCurrentIdentifierSource();
306 addIdentifierInformation('g', ident, buf,
307 phpdocOffset, phpdocLength);
310 } else if (fToken == TokenNameLBRACE) {
313 } else if (fToken == TokenNameRBRACE) {
316 if (counter == 0 && goBack) {
323 } catch (InvalidInputException e) {
325 } catch (SyntaxError e) {
326 // TODO Auto-generated catch block
332 synchronized public void parseIdentifiers(char[] charArray,
336 boolean hasModifiers = false;
337 int phpdocOffset = -1;
338 int phpdocLength = -1;
339 fScanner.setSource(charArray);
340 fScanner.setPHPMode(false);
341 fToken = TokenNameEOF;
345 while (fToken != TokenNameEOF) { // && fToken !=
348 hasModifiers = false;
350 case TokenNameCOMMENT_PHPDOC:
351 phpdocOffset = fScanner.getCurrentTokenStartPosition();
352 phpdocLength = fScanner.getCurrentTokenEndPosition()
353 - fScanner.getCurrentTokenStartPosition() + 1;
355 while (fToken == TokenNamestatic
356 || fToken == TokenNamefinal
357 || fToken == TokenNamepublic
358 || fToken == TokenNameprotected
359 || fToken == TokenNameprivate
360 || fToken == TokenNameabstract) {
364 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
369 case TokenNamefunction:
371 if (fToken == TokenNameAND) {
374 if (fToken == TokenNameIdentifier) {
375 ident = fScanner.getCurrentIdentifierSource();
376 addIdentifierInformation('f', ident, buf,
377 phpdocOffset, phpdocLength);
379 if (fToken == TokenNameLPAREN) {
382 if (fToken == TokenNameVariable) {
383 ident = fScanner.getCurrentIdentifierSource();
384 addIdentifierInformation('v', ident, buf,
385 phpdocOffset, phpdocLength);
387 if (fToken == TokenNameCOMMA) {
392 if (fToken == TokenNameCOMMA) {
395 } while (fToken != TokenNameRPAREN );
397 parseDeclarations(null, buf, true);
402 case TokenNameinterface:
404 if (fToken == TokenNameIdentifier) {
405 ident = fScanner.getCurrentIdentifierSource();
406 addIdentifierInformation('c', ident, buf,
407 phpdocOffset, phpdocLength);
409 if (fToken == TokenNameextends) {
411 while (fToken == TokenNameIdentifier) {
413 .getCurrentIdentifierSource();
415 addIdentifierInformation('e', ident, buf);
417 if (fToken == TokenNameCOMMA) {
422 if (fToken == TokenNameimplements) {
424 while (fToken == TokenNameIdentifier) {
426 .getCurrentIdentifierSource();
428 addIdentifierInformation('e', ident, buf);
430 if (fToken == TokenNameCOMMA) {
435 // skip fTokens for classname, extends and others
438 while (fToken != TokenNameLBRACE
439 && fToken != TokenNameEOF
440 && fToken != TokenNameERROR) {
443 parseDeclarations(ident, buf, true);
447 case TokenNameVariable:
449 ident = fScanner.getCurrentIdentifierSource();
450 addIdentifierInformation('g', ident, buf, phpdocOffset,
460 } catch (InvalidInputException e) {
462 } catch (SyntaxError e) {
463 // TODO Auto-generated catch block
469 class StringComparator implements Comparator {
470 public int compare(Object o1, Object o2) {
471 String s1 = (String) o1;
472 String s2 = (String) o2;
473 return s1.compareTo(s2);
474 // return s1.toUpperCase().compareTo(s2.toUpperCase());
477 public boolean equals(Object o) {
478 String s = (String) o;
479 return compare(this, o) == 0;
483 private HashMap fFileMap;
485 private String fFilename;
487 private TreeMap fIndentifierMap;
489 public IdentifierIndexManager(String filename) {
490 fFilename = filename;
496 * Check if 2 char arrays are equal
502 private static boolean equalCharArrays(char[] a, char[] b) {
503 if (a.length != b.length) {
506 for (int i = 0; i < b.length; i++) {
514 public LineCreator createLineCreator() {
515 return new LineCreator();
519 * Add the information for a given IFile resource
522 public void addFile(IFile fileToParse) {
523 LineCreator lineCreator = createLineCreator();
525 addInputStream(new BufferedInputStream(fileToParse.getContents()),
526 fileToParse.getProjectRelativePath().toString(),
527 lineCreator, fileToParse.getCharset());
528 } catch (CoreException e1) {
529 // TODO Auto-generated catch block
530 e1.printStackTrace();
537 * @throws CoreException
539 public void addInputStream(InputStream stream, String filePath,
540 LineCreator lineCreator, String charset) throws CoreException {
542 StringBuffer lineBuffer = new StringBuffer();
543 lineBuffer.append(filePath);
544 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream,
545 -1, charset), lineBuffer);
546 addLine(lineBuffer.toString());
547 } catch (IOException e) {
551 if (stream != null) {
554 } catch (IOException e) {
561 * Adds a line of the index file for function, class, class-method and
562 * class-variable names
566 private void addLine(String line) {
567 addLine(fIndentifierMap, fFileMap, line, null);
570 public TreeMap getIdentifiers(IFile file) {
571 TreeMap treeMap = new TreeMap(new StringComparator());
572 addIdentifiers(treeMap, file);
576 public TreeMap getIdentifiers(String startClazz) {
577 TreeMap treeMap = new TreeMap(new StringComparator());
578 addIdentifiers(treeMap, startClazz);
582 public void addIdentifiers(TreeMap treeMap, IFile file) {
583 String line = (String) fFileMap.get(file.getProjectRelativePath()
586 PHPIdentifierLocation ident;
587 ArrayList allClassNames = new ArrayList();
588 addLine(treeMap, null, line, allClassNames);
590 while (i < allClassNames.size()) {
591 String clazz = (String) allClassNames.get(i++);
592 addClassName(treeMap, clazz, allClassNames);
597 public void addIdentifiers(TreeMap treeMap, String startClazz) {
598 PHPIdentifierLocation ident;
599 ArrayList allClassNames = new ArrayList();
600 addClassName(treeMap, startClazz, allClassNames);
602 while (i < allClassNames.size()) {
603 String clazz = (String) allClassNames.get(i++);
604 addClassName(treeMap, clazz, allClassNames);
611 * @param allClassNames
613 private boolean addClassName(TreeMap treeMap, String clazz,
614 List allClassNames) {
616 PHPIdentifierLocation ident;
617 List list = getLocations(clazz);
621 boolean result = false;
622 for (int i = 0; i < list.size(); i++) {
623 ident = (PHPIdentifierLocation) list.get(i);
624 if (ident.isClass()) {
625 line = (String) fFileMap.get(ident.getFilename());
626 addLine(treeMap, null, line, allClassNames);
634 * Adds a line of the index file for function, class, class-method and
635 * class-variable names
639 public void addLine(TreeMap treeMap, HashMap fileMap, String line,
640 List allClassNames) {
641 StringTokenizer tokenizer;
642 String phpFileName = null;
644 String identifier = null;
645 String classname = null;
646 String offset = null;
647 PHPIdentifierLocation phpIdentifier = null;
648 boolean tokenExists = false;
649 tokenizer = new StringTokenizer(line, "\t");
650 // first token contains the filename:
652 if (tokenizer.hasMoreTokens()) {
653 phpFileName = tokenizer.nextToken();
654 // System.out.println(token);
658 // all the other tokens are identifiers:
659 while (tokenizer.hasMoreTokens()) {
660 token = tokenizer.nextToken();
661 // System.out.println(token);
662 switch (token.charAt(0)) {
665 identifier = token.substring(1);
666 classname = identifier;
667 phpIdentifier = new PHPIdentifierLocation(identifier,
668 PHPIdentifier.CLASS, phpFileName);
672 identifier = token.substring(1);
673 phpIdentifier = new PHPIdentifierLocation(identifier,
674 PHPIdentifier.DEFINE, phpFileName);
677 // extends <class name>
680 phpIdentifier = null;
681 if (allClassNames != null) {
682 String extName = token.substring(1);
683 if (!allClassNames.contains(extName)) {
684 allClassNames.add(extName);
690 identifier = token.substring(1);
691 phpIdentifier = new PHPIdentifierLocation(identifier,
692 PHPIdentifier.FUNCTION, phpFileName);
696 identifier = token.substring(1);
697 phpIdentifier = new PHPIdentifierLocation(identifier,
698 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
701 // implements <class name>
704 phpIdentifier = null;
705 if (allClassNames != null) {
706 String implName = token.substring(1);
707 if (!allClassNames.contains(implName)) {
708 allClassNames.add(implName);
713 // constructor function name
714 identifier = token.substring(1);
715 phpIdentifier = new PHPIdentifierLocation(identifier,
716 PHPIdentifier.CONSTRUCTOR, phpFileName);
719 // method inside a class
720 identifier = token.substring(1);
721 phpIdentifier = new PHPIdentifierLocation(identifier,
722 PHPIdentifier.METHOD, phpFileName, classname);
725 // variable inside a class
726 identifier = token.substring(1);
727 phpIdentifier = new PHPIdentifierLocation(identifier,
728 PHPIdentifier.VARIABLE, phpFileName, classname);
731 // offset information
733 if (phpIdentifier != null) {
734 offset = token.substring(1);
735 phpIdentifier.setOffset(Integer.parseInt(offset));
739 // PHPdoc offset information
741 if (phpIdentifier != null) {
742 offset = token.substring(1);
743 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
747 // PHPdoc length information
749 if (phpIdentifier != null) {
750 offset = token.substring(1);
751 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
755 PHPeclipsePlugin.log(IStatus.ERROR,
756 "Unknown token character in IdentifierIndexManager: "
759 phpIdentifier = null;
762 if (identifier != null && phpIdentifier != null) {
764 ArrayList list = (ArrayList) treeMap.get(identifier);
766 list = new ArrayList();
767 list.add(phpIdentifier);
768 treeMap.put(identifier, list);
770 boolean flag = false;
771 for (int i = 0; i < list.size(); i++) {
772 if (list.get(i).equals(phpIdentifier)) {
778 list.add(phpIdentifier);
783 if (fileMap != null) {
784 fileMap.put(phpFileName, line);
786 } catch (Throwable e) {
787 // write to workspace/.metadata/.log file
788 PHPeclipsePlugin.log(e);
790 // if (tokenExists) {
796 * Change the information for a given IFile resource
799 public void changeFile(IFile fileToParse) {
800 removeFile(fileToParse);
801 addFile(fileToParse);
805 * Get a list of all PHPIdentifierLocation object's associated with an
811 public List getLocations(String identifier) {
812 List list = (List) fIndentifierMap.get(identifier);
816 return new ArrayList();
820 * Initialize (i.e. clear) the current index information
823 public void initialize() {
824 fIndentifierMap = new TreeMap(new StringComparator());
825 fFileMap = new HashMap();
828 private void readFile() {
829 FileReader fileReader;
831 fileReader = new FileReader(fFilename);
832 BufferedReader bufferedReader = new BufferedReader(fileReader);
834 while (bufferedReader.ready()) {
835 // all entries for one file are in a line
836 // separated by tabs !
837 line = bufferedReader.readLine();
841 } catch (FileNotFoundException e) {
843 // TODO DialogBox which asks the user if she/he likes to build new
845 } catch (IOException e) {
846 // TODO Auto-generated catch block
852 * Remove the information for a given IFile resource
855 public void removeFile(IFile fileToParse) {
856 // String line = (String)
857 // fFileMap.get(fileToParse.getLocation().toString());
858 String line = (String) fFileMap.get(fileToParse
859 .getProjectRelativePath().toString());
866 * Removes a line of the index file for function, class, class-method and
867 * class-variable names
871 private void removeLine(String line) {
872 StringTokenizer tokenizer;
873 String phpFileName = null;
875 String identifier = null;
876 String classname = null;
877 PHPIdentifier phpIdentifier = null;
878 boolean tokenExists = false;
879 tokenizer = new StringTokenizer(line, "\t");
880 // first token contains the filename:
881 if (tokenizer.hasMoreTokens()) {
882 phpFileName = tokenizer.nextToken();
883 // System.out.println(token);
888 // all the other tokens are identifiers:
889 while (tokenizer.hasMoreTokens()) {
890 token = tokenizer.nextToken();
891 // System.out.println(token);
892 switch (token.charAt(0)) {
895 identifier = token.substring(1);
896 classname = identifier;
897 phpIdentifier = new PHPIdentifierLocation(identifier,
898 PHPIdentifier.CLASS, phpFileName);
902 identifier = token.substring(1);
903 phpIdentifier = new PHPIdentifierLocation(identifier,
904 PHPIdentifier.DEFINE, phpFileName);
907 // extends <class name>
909 phpIdentifier = null;
913 identifier = token.substring(1);
914 phpIdentifier = new PHPIdentifierLocation(identifier,
915 PHPIdentifier.FUNCTION, phpFileName);
919 identifier = token.substring(1);
920 phpIdentifier = new PHPIdentifierLocation(identifier,
921 PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
924 // implements <class name>
926 phpIdentifier = null;
929 // constructor function name
930 identifier = token.substring(1);
931 phpIdentifier = new PHPIdentifierLocation(identifier,
932 PHPIdentifier.CONSTRUCTOR, phpFileName);
935 // method inside a class
936 identifier = token.substring(1);
937 phpIdentifier = new PHPIdentifierLocation(identifier,
938 PHPIdentifier.METHOD, phpFileName, classname);
941 // offset information
945 // PHPdoc offset information
949 // PHPdoc length information
953 // variable inside a class
954 identifier = token.substring(1);
955 phpIdentifier = new PHPIdentifierLocation(identifier,
956 PHPIdentifier.VARIABLE, phpFileName, classname);
959 PHPeclipsePlugin.log(IStatus.ERROR,
960 "Unknown token character in IdentifierIndexManager: "
963 phpIdentifier = null;
966 if (identifier != null && phpIdentifier != null) {
967 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
970 for (int i = 0; i < list.size(); i++) {
971 if (list.get(i).equals(phpIdentifier)) {
976 if (list.size() == 0) {
977 fIndentifierMap.remove(identifier);
982 fFileMap.remove(phpFileName);
986 * Save the current index information in the projects index file
989 public void writeFile() {
990 FileWriter fileWriter;
992 fileWriter = new FileWriter(fFilename);
994 Collection collection = fFileMap.values();
995 Iterator iterator = collection.iterator();
996 while (iterator.hasNext()) {
997 line = (String) iterator.next();
998 fileWriter.write(line + '\n');
1001 } catch (FileNotFoundException e) {
1002 // ignore exception; project is deleted by user
1003 } catch (IOException e) {
1004 // TODO Auto-generated catch block
1005 e.printStackTrace();
1014 public SortedMap getIdentifierMap() {
1015 return fIndentifierMap;
1018 synchronized public List getFileList(String filePattern) {
1019 Set set = fFileMap.keySet();
1020 if (set.isEmpty()) {
1023 Iterator iter = set.iterator();
1024 ArrayList list = new ArrayList();
1027 while (iter.hasNext()) {
1028 fileName = (String) iter.next();
1029 if ((index = fileName.indexOf(filePattern)) != -1
1030 && fileName.length() == (index + filePattern.length())) {