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 * the offset of the PHPdoc comment if available
59 * the length of the PHPdoc comment if available
61 private void addIdentifierInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
64 line.append(typeOfIdentifier);
65 line.append(identifier);
66 line.append("\to"); // Offset
67 line.append(fScanner.getCurrentTokenStartPosition());
68 if (phpdocOffset >= 0) {
69 line.append("\tp"); // phpdoc offset
70 line.append(phpdocOffset);
71 line.append("\tl"); // phpdoc length
72 line.append(phpdocLength);
76 private void addClassVariableInformation(char typeOfIdentifier, char[] identifier, StringBuffer line, int phpdocOffset,
79 line.append(typeOfIdentifier);
80 line.append(identifier);
81 line.append("\to"); // Offset
82 // we don't store the '$' in the index for class variables:
83 line.append(fScanner.getCurrentTokenStartPosition() + 1);
84 if (phpdocOffset >= 0) {
85 line.append("\tp"); // phpdoc offset
86 line.append(phpdocOffset);
87 line.append("\tl"); // phpdoc length
88 line.append(phpdocLength);
93 * Get the next token from input
95 private void getNextToken() {
97 fToken = fScanner.getNextToken();
99 int currentEndPosition = fScanner.getCurrentTokenEndPosition();
100 int currentStartPosition = fScanner.getCurrentTokenStartPosition();
101 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
102 System.out.println(fScanner.toStringAction(fToken));
105 } catch (InvalidInputException e) {
107 // e.printStackTrace();
109 fToken = TokenNameERROR;
112 private void parseDeclarations(char[] parent, StringBuffer buf, boolean goBack) {
114 char[] classVariable;
116 boolean hasModifiers = false;
117 int phpdocOffset = -1;
118 int phpdocLength = -1;
120 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
122 hasModifiers = false;
123 if (fToken == TokenNameCOMMENT_PHPDOC) {
124 phpdocOffset = fScanner.getCurrentTokenStartPosition();
125 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
127 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
128 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
132 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
136 if (fToken == TokenNamefunction) {
138 if (fToken == TokenNameAND) {
141 if (fToken == TokenNameIdentifier) {
142 ident = fScanner.getCurrentIdentifierSource();
143 if (parent != null && equalCharArrays(parent, ident)) {
144 // constructor function
145 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
147 if (parent != null) {
148 // class method function
149 addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
151 // nested function ?!
152 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
156 parseDeclarations(null, buf, true);
158 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
160 if (fToken == TokenNameIdentifier) {
161 ident = fScanner.getCurrentIdentifierSource();
162 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
164 //skip tokens for classname, extends and others until we have
166 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
169 parseDeclarations(ident, buf, true);
171 } else if (fToken == TokenNamevar || hasModifiers || fToken == TokenNamestatic || fToken == TokenNamefinal
172 || fToken == TokenNamepublic || fToken == TokenNameprotected || fToken == TokenNameprivate) {
173 while (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
174 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
177 while (fToken == TokenNameVariable) {
178 ident = fScanner.getCurrentIdentifierSource();
179 classVariable = new char[ident.length - 1];
180 System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
181 addClassVariableInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
183 if (fToken == TokenNameCOMMA) {
187 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
188 ident = fScanner.getCurrentIdentifierSource();
190 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
191 && ident[5] == 'e') {
192 if (fToken == TokenNameLPAREN) {
194 if (fToken == TokenNameStringDoubleQuote) {
195 ident = fScanner.getCurrentStringLiteralSource();
196 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
198 } else if (fToken == TokenNameStringSingleQuote) {
199 ident = fScanner.getCurrentStringLiteralSource();
200 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
205 } else if (fToken == TokenNameglobal) {
207 while (fToken != TokenNameEOF && fToken != TokenNameERROR && fToken != TokenNameSEMICOLON && fToken != TokenNameLBRACE
208 && fToken != TokenNameRBRACE) {
210 if (fToken == TokenNameVariable) {
211 ident = fScanner.getCurrentIdentifierSource();
212 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
215 } else if (fToken == TokenNameLBRACE) {
218 } else if (fToken == TokenNameRBRACE) {
221 if (counter == 0 && goBack) {
228 } catch (SyntaxError e) {
229 // TODO Auto-generated catch block
234 synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
238 boolean hasModifiers = false;
239 int phpdocOffset = -1;
240 int phpdocLength = -1;
241 fScanner.setSource(charArray);
242 fScanner.setPHPMode(false);
243 fToken = TokenNameEOF;
246 while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
248 hasModifiers = false;
249 if (fToken == TokenNameCOMMENT_PHPDOC) {
250 phpdocOffset = fScanner.getCurrentTokenStartPosition();
251 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
253 while (fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
254 || fToken == TokenNameprotected || fToken == TokenNameprivate || fToken == TokenNameabstract) {
258 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
262 if (fToken == TokenNamefunction) {
264 if (fToken == TokenNameAND) {
267 if (fToken == TokenNameIdentifier) {
268 ident = fScanner.getCurrentIdentifierSource();
269 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
271 parseDeclarations(null, buf, true);
273 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
275 if (fToken == TokenNameIdentifier) {
276 ident = fScanner.getCurrentIdentifierSource();
277 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
279 //skip fTokens for classname, extends and others until we have
281 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
284 parseDeclarations(ident, buf, true);
286 } else if (fToken == TokenNameVariable) {
288 ident = fScanner.getCurrentIdentifierSource();
289 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
291 } else if (!hasModifiers && fToken == TokenNameIdentifier) {
292 ident = fScanner.getCurrentIdentifierSource();
294 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
295 && ident[5] == 'e') {
296 if (fToken == TokenNameLPAREN) {
298 if (fToken == TokenNameStringDoubleQuote) {
299 ident = fScanner.getCurrentStringLiteralSource();
300 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
302 } else if (fToken == TokenNameStringSingleQuote) {
303 ident = fScanner.getCurrentStringLiteralSource();
304 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
313 } catch (SyntaxError e) {
314 // TODO Auto-generated catch block
320 class StringComparator implements Comparator {
321 public int compare(Object o1, Object o2) {
322 String s1 = (String) o1;
323 String s2 = (String) o2;
324 return s1.compareTo(s2);
325 // return s1.toUpperCase().compareTo(s2.toUpperCase());
328 public boolean equals(Object o) {
329 String s = (String) o;
330 return compare(this, o) == 0;
334 private HashMap fFileMap;
336 private String fFilename;
338 private TreeMap fIndentifierMap;
340 public IdentifierIndexManager(String filename) {
341 fFilename = filename;
347 * Check if 2 char arrays are equal
353 private static boolean equalCharArrays(char[] a, char[] b) {
354 if (a.length != b.length) {
357 for (int i = 0; i < b.length; i++) {
365 public LineCreator createLineCreator() {
366 return new LineCreator();
370 * Add the information for a given IFile resource
373 public void addFile(IFile fileToParse) {
374 // InputStream iStream;
375 LineCreator lineCreator = createLineCreator();
377 addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
379 } catch (CoreException e1) {
380 // TODO Auto-generated catch block
381 e1.printStackTrace();
388 * @throws CoreException
390 public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
392 StringBuffer lineBuffer = new StringBuffer();
393 lineBuffer.append(filePath);
394 int lineLength = lineBuffer.length();
395 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
396 // if (lineLength != lineBuffer.length()) {
397 // always add the file for Open Include Action
398 addLine(lineBuffer.toString());
400 } catch (IOException e) {
404 if (stream != null) {
407 } catch (IOException e) {
413 * Adds a line of the index file for function, class, class-method and class-variable names
417 private void addLine(String line) {
418 StringTokenizer tokenizer;
419 String phpFileName = null;
421 String identifier = null;
422 String classname = null;
423 String offset = null;
424 PHPIdentifierLocation phpIdentifier = null;
425 boolean tokenExists = false;
426 tokenizer = new StringTokenizer(line, "\t");
427 // first token contains the filename:
429 if (tokenizer.hasMoreTokens()) {
430 phpFileName = tokenizer.nextToken();
431 //System.out.println(token);
435 // all the other tokens are identifiers:
436 while (tokenizer.hasMoreTokens()) {
437 token = tokenizer.nextToken();
438 //System.out.println(token);
439 switch (token.charAt(0)) {
442 identifier = token.substring(1);
443 classname = identifier;
444 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
448 identifier = token.substring(1);
449 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
453 identifier = token.substring(1);
454 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
458 identifier = token.substring(1);
459 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
462 // constructor function name
463 identifier = token.substring(1);
464 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
467 //method inside a class
468 identifier = token.substring(1);
469 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
472 // variable inside a class
473 identifier = token.substring(1);
474 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
477 // offset information
479 if (phpIdentifier != null) {
480 offset = token.substring(1);
481 phpIdentifier.setOffset(Integer.parseInt(offset));
485 // PHPdoc offset information
487 if (phpIdentifier != null) {
488 offset = token.substring(1);
489 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
493 // PHPdoc length information
495 if (phpIdentifier != null) {
496 offset = token.substring(1);
497 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
501 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
503 phpIdentifier = null;
506 if (identifier != null && phpIdentifier != null) {
508 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
510 list = new ArrayList();
511 list.add(phpIdentifier);
512 fIndentifierMap.put(identifier, list);
514 boolean flag = false;
515 for (int i = 0; i < list.size(); i++) {
516 if (list.get(i).equals(phpIdentifier)) {
522 list.add(phpIdentifier);
527 fFileMap.put(phpFileName, line);
528 } catch (Throwable e) {
529 // write to workspace/.metadata/.log file
530 PHPeclipsePlugin.log(e);
532 // if (tokenExists) {
538 * Change the information for a given IFile resource
541 public void changeFile(IFile fileToParse) {
542 removeFile(fileToParse);
543 addFile(fileToParse);
547 * Get a list of all PHPIdentifierLocation object's associated with an identifier
552 public List getLocations(String identifier) {
553 return (List) fIndentifierMap.get(identifier);
557 * Initialize (i.e. clear) the current index information
560 public void initialize() {
561 fIndentifierMap = new TreeMap(new StringComparator());
562 fFileMap = new HashMap();
565 private void readFile() {
566 FileReader fileReader;
568 fileReader = new FileReader(fFilename);
569 BufferedReader bufferedReader = new BufferedReader(fileReader);
571 while (bufferedReader.ready()) {
572 // all entries for one file are in a line
573 // separated by tabs !
574 line = bufferedReader.readLine();
578 } catch (FileNotFoundException e) {
580 // TODO DialogBox which asks the user if she/he likes to build new index?
581 } catch (IOException e) {
582 // TODO Auto-generated catch block
588 * Remove the information for a given IFile resource
591 public void removeFile(IFile fileToParse) {
592 // String line = (String)
593 // fFileMap.get(fileToParse.getLocation().toString());
594 String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
601 * Removes a line of the index file for function, class, class-method and class-variable names
605 private void removeLine(String line) {
606 StringTokenizer tokenizer;
607 String phpFileName = null;
609 String identifier = null;
610 String classname = null;
611 PHPIdentifier phpIdentifier = null;
612 boolean tokenExists = false;
613 tokenizer = new StringTokenizer(line, "\t");
614 // first token contains the filename:
615 if (tokenizer.hasMoreTokens()) {
616 phpFileName = tokenizer.nextToken();
617 //System.out.println(token);
622 // all the other tokens are identifiers:
623 while (tokenizer.hasMoreTokens()) {
624 token = tokenizer.nextToken();
625 //System.out.println(token);
626 switch (token.charAt(0)) {
629 identifier = token.substring(1);
630 classname = identifier;
631 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
635 identifier = token.substring(1);
636 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
640 identifier = token.substring(1);
641 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
645 identifier = token.substring(1);
646 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
649 // constructor function name
650 identifier = token.substring(1);
651 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
654 //method inside a class
655 identifier = token.substring(1);
656 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
659 // offset information
663 // PHPdoc offset information
667 // PHPdoc length information
671 // variable inside a class
672 identifier = token.substring(1);
673 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
676 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
678 phpIdentifier = null;
681 if (identifier != null && phpIdentifier != null) {
682 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
685 for (int i = 0; i < list.size(); i++) {
686 if (list.get(i).equals(phpIdentifier)) {
691 if (list.size() == 0) {
692 fIndentifierMap.remove(identifier);
697 fFileMap.remove(phpFileName);
701 * Save the current index information in the projects index file
704 public void writeFile() {
705 FileWriter fileWriter;
707 fileWriter = new FileWriter(fFilename);
709 Collection collection = fFileMap.values();
710 Iterator iterator = collection.iterator();
711 while (iterator.hasNext()) {
712 line = (String) iterator.next();
713 fileWriter.write(line + '\n');
716 } catch (FileNotFoundException e) {
717 // ignore exception; project is deleted by user
718 } catch (IOException e) {
719 // TODO Auto-generated catch block
729 public SortedMap getIdentifierMap() {
730 return fIndentifierMap;
733 synchronized public List getFileList(String filePattern) {
734 Set set = fFileMap.keySet();
738 Iterator iter = set.iterator();
739 ArrayList list = new ArrayList();
742 while (iter.hasNext()) {
743 fileName = (String) iter.next();
744 if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {