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);
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 int phpdocOffset = -1;
117 int phpdocLength = -1;
119 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
121 if (fToken == TokenNameCOMMENT_PHPDOC) {
122 phpdocOffset = fScanner.getCurrentTokenStartPosition();
123 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
125 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
129 if (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
130 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
131 while (fToken == TokenNamevar || fToken == TokenNamestatic || fToken == TokenNamefinal || fToken == TokenNamepublic
132 || fToken == TokenNameprotected || fToken == TokenNameprivate) {
135 if (fToken == TokenNameVariable) {
136 ident = fScanner.getCurrentIdentifierSource();
137 classVariable = new char[ident.length - 1];
138 System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
139 addClassVariableInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
142 } else if (fToken == TokenNamefunction) {
144 if (fToken == TokenNameAND) {
147 if (fToken == TokenNameIdentifier) {
148 ident = fScanner.getCurrentIdentifierSource();
149 if (parent != null && equalCharArrays(parent, ident)) {
150 // constructor function
151 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
153 if (parent != null) {
154 // class method function
155 addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
157 // nested function ?!
158 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
162 parseDeclarations(null, buf, true);
164 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
166 if (fToken == TokenNameIdentifier) {
167 ident = fScanner.getCurrentIdentifierSource();
168 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
170 //skip tokens for classname, extends and others until we have
172 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
175 parseDeclarations(ident, buf, true);
177 } else if (fToken == TokenNameIdentifier) {
178 ident = fScanner.getCurrentIdentifierSource();
180 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
181 && ident[5] == 'e') {
182 if (fToken == TokenNameLPAREN) {
184 if (fToken == TokenNameStringDoubleQuote) {
185 ident = fScanner.getCurrentStringLiteralSource();
186 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
191 } else if (fToken == TokenNameglobal) {
193 while (fToken != TokenNameEOF && fToken != TokenNameERROR && fToken != TokenNameSEMICOLON && fToken != TokenNameLBRACE
194 && fToken != TokenNameRBRACE) {
196 if (fToken == TokenNameVariable) {
197 ident = fScanner.getCurrentIdentifierSource();
198 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
201 } else if (fToken == TokenNameLBRACE) {
204 } else if (fToken == TokenNameRBRACE) {
207 if (counter == 0 && goBack) {
214 } catch (SyntaxError e) {
215 // TODO Auto-generated catch block
220 synchronized public void parseIdentifiers(char[] charArray, StringBuffer buf) {
224 int phpdocOffset = -1;
225 int phpdocLength = -1;
226 fScanner.setSource(charArray);
227 fScanner.setPHPMode(false);
228 fToken = TokenNameEOF;
231 while (fToken != TokenNameEOF) { // && fToken != TokenNameERROR) {
233 if (fToken == TokenNameCOMMENT_PHPDOC) {
234 phpdocOffset = fScanner.getCurrentTokenStartPosition();
235 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
237 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
241 if (fToken == TokenNamefunction) {
243 if (fToken == TokenNameAND) {
246 if (fToken == TokenNameIdentifier) {
247 ident = fScanner.getCurrentIdentifierSource();
248 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
250 parseDeclarations(null, buf, true);
252 } else if (fToken == TokenNameclass || fToken == TokenNameinterface) {
254 if (fToken == TokenNameIdentifier) {
255 ident = fScanner.getCurrentIdentifierSource();
256 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
258 //skip fTokens for classname, extends and others until we have
260 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
263 parseDeclarations(ident, buf, true);
265 } else if (fToken == TokenNameVariable) {
267 ident = fScanner.getCurrentIdentifierSource();
268 addIdentifierInformation('g', ident, buf, phpdocOffset, phpdocLength);
270 } else if (fToken == TokenNameIdentifier) {
271 ident = fScanner.getCurrentIdentifierSource();
273 if (ident.length == 6 && ident[0] == 'd' && ident[1] == 'e' && ident[2] == 'f' && ident[3] == 'i' && ident[4] == 'n'
274 && ident[5] == 'e') {
275 if (fToken == TokenNameLPAREN) {
277 if (fToken == TokenNameStringDoubleQuote) {
278 ident = fScanner.getCurrentStringLiteralSource();
279 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
288 } catch (SyntaxError e) {
289 // TODO Auto-generated catch block
295 class StringComparator implements Comparator {
296 public int compare(Object o1, Object o2) {
297 String s1 = (String) o1;
298 String s2 = (String) o2;
299 return s1.compareTo(s2);
300 // return s1.toUpperCase().compareTo(s2.toUpperCase());
303 public boolean equals(Object o) {
304 String s = (String) o;
305 return compare(this, o) == 0;
309 private HashMap fFileMap;
311 private String fFilename;
313 private TreeMap fIndentifierMap;
315 public IdentifierIndexManager(String filename) {
316 fFilename = filename;
322 * Check if 2 char arrays are equal
328 private static boolean equalCharArrays(char[] a, char[] b) {
329 if (a.length != b.length) {
332 for (int i = 0; i < b.length; i++) {
340 public LineCreator createLineCreator() {
341 return new LineCreator();
345 * Add the information for a given IFile resource
348 public void addFile(IFile fileToParse) {
349 // InputStream iStream;
350 LineCreator lineCreator = createLineCreator();
352 addInputStream(new BufferedInputStream(fileToParse.getContents()), fileToParse.getProjectRelativePath().toString(),
354 } catch (CoreException e1) {
355 // TODO Auto-generated catch block
356 e1.printStackTrace();
363 * @throws CoreException
365 public void addInputStream(InputStream stream, String filePath, LineCreator lineCreator) throws CoreException {
367 StringBuffer lineBuffer = new StringBuffer();
368 lineBuffer.append(filePath);
369 int lineLength = lineBuffer.length();
370 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
371 // if (lineLength != lineBuffer.length()) {
372 // always add the file for Open Include Action
373 addLine(lineBuffer.toString());
375 } catch (IOException e) {
379 if (stream != null) {
382 } catch (IOException e) {
388 * Adds a line of the index file for function, class, class-method and class-variable names
392 private void addLine(String line) {
393 StringTokenizer tokenizer;
394 String phpFileName = null;
396 String identifier = null;
397 String classname = null;
398 String offset = null;
399 PHPIdentifierLocation phpIdentifier = null;
400 boolean tokenExists = false;
401 tokenizer = new StringTokenizer(line, "\t");
402 // first token contains the filename:
403 if (tokenizer.hasMoreTokens()) {
404 phpFileName = tokenizer.nextToken();
405 //System.out.println(token);
409 // all the other tokens are identifiers:
410 while (tokenizer.hasMoreTokens()) {
411 token = tokenizer.nextToken();
412 //System.out.println(token);
413 switch (token.charAt(0)) {
416 identifier = token.substring(1);
417 classname = identifier;
418 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
422 identifier = token.substring(1);
423 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
427 identifier = token.substring(1);
428 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
432 identifier = token.substring(1);
433 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
436 // constructor function name
437 identifier = token.substring(1);
438 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
441 //method inside a class
442 identifier = token.substring(1);
443 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
446 // variable inside a class
447 identifier = token.substring(1);
448 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
451 // offset information
453 if (phpIdentifier != null) {
454 offset = token.substring(1);
455 phpIdentifier.setOffset(Integer.parseInt(offset));
459 // PHPdoc offset information
461 if (phpIdentifier != null) {
462 offset = token.substring(1);
463 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
467 // PHPdoc length information
469 if (phpIdentifier != null) {
470 offset = token.substring(1);
471 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
475 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
477 phpIdentifier = null;
480 if (identifier != null && phpIdentifier != null) {
482 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
484 list = new ArrayList();
485 list.add(phpIdentifier);
486 fIndentifierMap.put(identifier, list);
488 boolean flag = false;
489 for (int i = 0; i < list.size(); i++) {
490 if (list.get(i).equals(phpIdentifier)) {
496 list.add(phpIdentifier);
501 // if (tokenExists) {
502 fFileMap.put(phpFileName, line);
507 * Change the information for a given IFile resource
510 public void changeFile(IFile fileToParse) {
511 removeFile(fileToParse);
512 addFile(fileToParse);
516 * Get a list of all PHPIdentifierLocation object's associated with an identifier
521 public List getLocations(String identifier) {
522 return (List) fIndentifierMap.get(identifier);
526 * Initialize (i.e. clear) the current index information
529 public void initialize() {
530 fIndentifierMap = new TreeMap(new StringComparator());
531 fFileMap = new HashMap();
534 private void readFile() {
535 FileReader fileReader;
537 fileReader = new FileReader(fFilename);
538 BufferedReader bufferedReader = new BufferedReader(fileReader);
540 while (bufferedReader.ready()) {
541 // all entries for one file are in a line
542 // separated by tabs !
543 line = bufferedReader.readLine();
547 } catch (FileNotFoundException e) {
549 // TODO DialogBox which asks the user if she/he likes to build new index?
550 } catch (IOException e) {
551 // TODO Auto-generated catch block
557 * Remove the information for a given IFile resource
560 public void removeFile(IFile fileToParse) {
561 // String line = (String)
562 // fFileMap.get(fileToParse.getLocation().toString());
563 String line = (String) fFileMap.get(fileToParse.getProjectRelativePath().toString());
570 * Removes a line of the index file for function, class, class-method and class-variable names
574 private void removeLine(String line) {
575 StringTokenizer tokenizer;
576 String phpFileName = null;
578 String identifier = null;
579 String classname = null;
580 PHPIdentifier phpIdentifier = null;
581 boolean tokenExists = false;
582 tokenizer = new StringTokenizer(line, "\t");
583 // first token contains the filename:
584 if (tokenizer.hasMoreTokens()) {
585 phpFileName = tokenizer.nextToken();
586 //System.out.println(token);
591 // all the other tokens are identifiers:
592 while (tokenizer.hasMoreTokens()) {
593 token = tokenizer.nextToken();
594 //System.out.println(token);
595 switch (token.charAt(0)) {
598 identifier = token.substring(1);
599 classname = identifier;
600 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
604 identifier = token.substring(1);
605 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
609 identifier = token.substring(1);
610 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
614 identifier = token.substring(1);
615 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.GLOBAL_VARIABLE, phpFileName);
618 // constructor function name
619 identifier = token.substring(1);
620 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
623 //method inside a class
624 identifier = token.substring(1);
625 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
628 // offset information
632 // PHPdoc offset information
636 // PHPdoc length information
640 // variable inside a class
641 identifier = token.substring(1);
642 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
645 PHPeclipsePlugin.log(IStatus.ERROR, "Unknown token character in IdentifierIndexManager: " + token.charAt(0));
647 phpIdentifier = null;
650 if (identifier != null && phpIdentifier != null) {
651 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
654 for (int i = 0; i < list.size(); i++) {
655 if (list.get(i).equals(phpIdentifier)) {
660 if (list.size() == 0) {
661 fIndentifierMap.remove(identifier);
666 fFileMap.remove(phpFileName);
670 * Save the current index information in the projects index file
673 public void writeFile() {
674 FileWriter fileWriter;
676 fileWriter = new FileWriter(fFilename);
678 Collection collection = fFileMap.values();
679 Iterator iterator = collection.iterator();
680 while (iterator.hasNext()) {
681 line = (String) iterator.next();
682 fileWriter.write(line + '\n');
685 } catch (FileNotFoundException e) {
686 // ignore exception; project is deleted by user
687 } catch (IOException e) {
688 // TODO Auto-generated catch block
698 public SortedMap getIdentifierMap() {
699 return fIndentifierMap;
702 synchronized public List getFileList(String filePattern) {
703 Set set = fFileMap.keySet();
707 Iterator iter = set.iterator();
708 ArrayList list = new ArrayList();
711 while (iter.hasNext()) {
712 fileName = (String) iter.next();
713 if ((index = fileName.indexOf(filePattern)) != -1 && fileName.length() == (index + filePattern.length())) {