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;
16 import java.util.SortedMap;
17 import java.util.StringTokenizer;
18 import java.util.TreeMap;
20 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
21 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
22 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
23 import net.sourceforge.phpdt.internal.compiler.parser.SyntaxError;
24 import net.sourceforge.phpdt.internal.compiler.util.Util;
25 import net.sourceforge.phpeclipse.obfuscator.PHPIdentifier;
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.runtime.CoreException;
31 * Manages the identifer index information for a specific project
34 public class IdentifierIndexManager {
36 public class LineCreator implements ITerminalSymbols {
38 private Scanner fScanner;
41 public LineCreator() {
42 fScanner = new Scanner(true, false);
46 * Add the information of the current identifier to the line
48 * @param typeOfIdentifier the type of the identifier ('c'lass, 'd'efine, 'f'unction, 'm'ethod, 'v'ariable)
49 * @param identifier current identifier
50 * @param line Buffer for the current index line
51 * @param phpdocOffset the offset of the PHPdoc comment if available
52 * @param phpdocLength the length of the PHPdoc comment if available
54 private void addIdentifierInformation(
55 char typeOfIdentifier,
62 line.append(typeOfIdentifier);
63 line.append(identifier);
64 line.append("\to"); // Offset
65 line.append(fScanner.getCurrentTokenStartPosition());
66 if (phpdocOffset >= 0) {
67 line.append("\tp"); // phpdoc offset
68 line.append(phpdocOffset);
69 line.append("\tl"); // phpdoc length
70 line.append(phpdocLength);
75 * Get the next token from input
77 private void getNextToken() {
79 fToken = fScanner.getNextToken();
81 int currentEndPosition = fScanner.getCurrentTokenEndPosition();
82 int currentStartPosition = fScanner.getCurrentTokenStartPosition();
84 System.out.print(currentStartPosition + "," + currentEndPosition + ": ");
85 System.out.println(fScanner.toStringAction(fToken));
88 } catch (InvalidInputException e) {
91 fToken = TokenNameERROR;
94 private void parseDeclarations(char[] parent, StringBuffer buf, boolean goBack) {
98 int phpdocOffset = -1;
99 int phpdocLength = -1;
102 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
104 if (fToken == TokenNameCOMMENT_PHPDOC) {
105 phpdocOffset = fScanner.getCurrentTokenStartPosition();
106 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
108 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
112 if (fToken == TokenNamevar) {
114 if (fToken == TokenNameVariable) {
115 ident = fScanner.getCurrentIdentifierSource();
116 classVariable = new char[ident.length - 1];
117 System.arraycopy(ident, 1, classVariable, 0, ident.length - 1);
118 addIdentifierInformation('v', classVariable, buf, phpdocOffset, phpdocLength);
121 } else if (fToken == TokenNamefunction) {
123 if (fToken == TokenNameAND) {
126 if (fToken == TokenNameIdentifier) {
127 ident = fScanner.getCurrentIdentifierSource();
128 if (parent != null && equalCharArrays(parent, ident)) {
129 // constructor function
130 addIdentifierInformation('k', ident, buf, phpdocOffset, phpdocLength);
132 if (parent != null) {
133 // class method function
134 addIdentifierInformation('m', ident, buf, phpdocOffset, phpdocLength);
136 // nested function ?!
137 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
141 parseDeclarations(null, buf, true);
143 } else if (fToken == TokenNameclass) {
145 if (fToken == TokenNameIdentifier) {
146 ident = fScanner.getCurrentIdentifierSource();
147 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
150 //skip tokens for classname, extends and others until we have the opening '{'
151 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
154 parseDeclarations(ident, buf, true);
156 } else if (fToken == TokenNamedefine) {
158 if (fToken == TokenNameLPAREN) {
160 if (fToken == TokenNameStringLiteral) {
161 ident = fScanner.getCurrentStringLiteralSource();
162 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
166 } else if ((fToken == TokenNameLBRACE) || (fToken == TokenNameDOLLAR_LBRACE)) {
169 } else if (fToken == TokenNameRBRACE) {
172 if (counter == 0 && goBack) {
179 } catch (SyntaxError e) {
180 // TODO Auto-generated catch block
185 public void parseIdentifiers(char[] charArray, StringBuffer buf) {
189 int phpdocOffset = -1;
190 int phpdocLength = -1;
192 fScanner.setSource(charArray);
193 fScanner.setPHPMode(false);
194 fToken = TokenNameEOF;
198 while (fToken != TokenNameEOF && fToken != TokenNameERROR) {
200 if (fToken == TokenNameCOMMENT_PHPDOC) {
201 phpdocOffset = fScanner.getCurrentTokenStartPosition();
202 phpdocLength = fScanner.getCurrentTokenEndPosition() - fScanner.getCurrentTokenStartPosition() + 1;
204 if (fToken == TokenNameEOF || fToken == TokenNameERROR) {
208 if (fToken == TokenNamefunction) {
210 if (fToken == TokenNameAND) {
213 if (fToken == TokenNameIdentifier) {
214 ident = fScanner.getCurrentIdentifierSource();
215 addIdentifierInformation('f', ident, buf, phpdocOffset, phpdocLength);
217 parseDeclarations(null, buf, true);
219 } else if (fToken == TokenNameclass) {
221 if (fToken == TokenNameIdentifier) {
222 ident = fScanner.getCurrentIdentifierSource();
223 addIdentifierInformation('c', ident, buf, phpdocOffset, phpdocLength);
226 //skip fTokens for classname, extends and others until we have the opening '{'
227 while (fToken != TokenNameLBRACE && fToken != TokenNameEOF && fToken != TokenNameERROR) {
231 parseDeclarations(ident, buf, true);
234 } else if (fToken == TokenNamedefine) {
236 if (fToken == TokenNameLPAREN) {
238 if (fToken == TokenNameStringLiteral) {
239 ident = fScanner.getCurrentStringLiteralSource();
240 addIdentifierInformation('d', ident, buf, phpdocOffset, phpdocLength);
248 } catch (SyntaxError e) {
249 // TODO Auto-generated catch block
255 class StringComparator implements Comparator {
256 public int compare(Object o1, Object o2) {
257 String s1 = (String) o1;
258 String s2 = (String) o2;
259 return s1.compareTo(s2);
260 // return s1.toUpperCase().compareTo(s2.toUpperCase());
262 public boolean equals(Object o) {
263 String s = (String) o;
264 return compare(this, o) == 0;
268 private HashMap fFileMap;
269 private String fFilename;
270 private TreeMap fIndentifierMap;
272 public IdentifierIndexManager(String filename) {
273 fFilename = filename;
279 * Check if 2 char arrays are equal
285 private static boolean equalCharArrays(char[] a, char[] b) {
286 if (a.length != b.length) {
289 for (int i = 0; i < b.length; i++) {
298 * Add the information for a given IFile resource
301 public void addFile(IFile fileToParse) {
302 // InputStream iStream;
303 LineCreator lineCreator = new LineCreator();
305 // iStream = fileToParse.getContents();
307 // StringBuffer buf = new StringBuffer();
310 // while ((c0 = iStream.read()) != (-1)) {
311 // buf.append((char) c0);
313 // } catch (IOException e) {
316 InputStream stream = null;
318 stream = new BufferedInputStream(fileToParse.getContents());
320 StringBuffer lineBuffer = new StringBuffer();
321 lineBuffer.append(fileToParse.getFullPath().toString());
322 int lineLength = lineBuffer.length();
323 // lineCreator.parseIdentifiers(buf.toString().toCharArray(), lineBuffer);
324 lineCreator.parseIdentifiers(Util.getInputStreamAsCharArray(stream, -1, null), lineBuffer);
325 if (lineLength != lineBuffer.length()) {
326 addLine(lineBuffer.toString());
328 } catch (IOException e) {
332 if (stream != null) {
335 } catch (IOException e) {
338 } catch (CoreException e1) {
339 // TODO Auto-generated catch block
340 e1.printStackTrace();
345 * Adds a line of the index file for function, class, class-method and class-variable names
349 private void addLine(String line) {
350 StringTokenizer tokenizer;
351 String phpFileName = null;
353 String identifier = null;
354 String classname = null;
355 String offset = null;
356 PHPIdentifierLocation phpIdentifier = null;
357 boolean tokenExists = false;
359 tokenizer = new StringTokenizer(line, "\t");
360 // first token contains the filename:
361 if (tokenizer.hasMoreTokens()) {
362 phpFileName = tokenizer.nextToken();
363 //System.out.println(token);
367 // all the other tokens are identifiers:
368 while (tokenizer.hasMoreTokens()) {
369 token = tokenizer.nextToken();
370 //System.out.println(token);
371 switch (token.charAt(0)) {
372 case 'c' : // class name
373 identifier = token.substring(1);
374 classname = identifier;
375 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
378 identifier = token.substring(1);
379 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
381 case 'f' : // function name
382 identifier = token.substring(1);
383 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
385 case 'k' : // constructor function name
386 identifier = token.substring(1);
387 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
389 case 'm' : //method inside a class
390 identifier = token.substring(1);
391 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
393 case 'v' : // variable inside a class
394 identifier = token.substring(1);
395 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
397 case 'o' : // offset information
399 if (phpIdentifier != null) {
400 offset = token.substring(1);
401 phpIdentifier.setOffset(Integer.parseInt(offset));
404 case 'p' : // PHPdoc offset information
406 if (phpIdentifier != null) {
407 offset = token.substring(1);
408 phpIdentifier.setPHPDocOffset(Integer.parseInt(offset));
411 case 'l' : // PHPdoc length information
413 if (phpIdentifier != null) {
414 offset = token.substring(1);
415 phpIdentifier.setPHPDocLength(Integer.parseInt(offset));
420 phpIdentifier = null;
423 if (identifier != null && phpIdentifier != null) {
425 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
427 list = new ArrayList();
428 list.add(phpIdentifier);
429 fIndentifierMap.put(identifier, list);
431 boolean flag = false;
432 for (int i = 0; i < list.size(); i++) {
433 if (list.get(i).equals(phpIdentifier)) {
439 list.add(phpIdentifier);
445 fFileMap.put(phpFileName, line);
450 * Change the information for a given IFile resource
453 public void changeFile(IFile fileToParse) {
454 removeFile(fileToParse);
455 addFile(fileToParse);
459 * Get a list of all PHPIdentifierLocation object's associated with an identifier
464 public List getLocations(String identifier) {
465 return (List) fIndentifierMap.get(identifier);
469 * Initialize (i.e. clear) the current index information
472 public void initialize() {
473 fIndentifierMap = new TreeMap(new StringComparator());
474 fFileMap = new HashMap();
477 private void readFile() {
479 FileReader fileReader;
481 fileReader = new FileReader(fFilename);
483 BufferedReader bufferedReader = new BufferedReader(fileReader);
486 while (bufferedReader.ready()) {
487 // all entries for one file are in a line
488 // separated by tabs !
489 line = bufferedReader.readLine();
494 } catch (FileNotFoundException e) {
496 // TODO DialogBox which asks the user if she/he likes to build new index?
497 } catch (IOException e) {
498 // TODO Auto-generated catch block
505 * Remove the information for a given IFile resource
508 public void removeFile(IFile fileToParse) {
509 // String line = (String) fFileMap.get(fileToParse.getLocation().toString());
510 String line = (String) fFileMap.get(fileToParse.getFullPath().toString());
517 * Removes a line of the index file for function, class, class-method and class-variable names
521 private void removeLine(String line) {
522 StringTokenizer tokenizer;
523 String phpFileName = null;
525 String identifier = null;
526 String classname = null;
527 PHPIdentifier phpIdentifier = null;
528 boolean tokenExists = false;
530 tokenizer = new StringTokenizer(line, "\t");
531 // first token contains the filename:
532 if (tokenizer.hasMoreTokens()) {
533 phpFileName = tokenizer.nextToken();
534 //System.out.println(token);
538 // all the other tokens are identifiers:
539 while (tokenizer.hasMoreTokens()) {
540 token = tokenizer.nextToken();
541 //System.out.println(token);
542 switch (token.charAt(0)) {
543 case 'c' : // class name
544 identifier = token.substring(1);
545 classname = identifier;
546 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CLASS, phpFileName);
549 identifier = token.substring(1);
550 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.DEFINE, phpFileName);
552 case 'f' : // function name
553 identifier = token.substring(1);
554 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.FUNCTION, phpFileName);
556 case 'k' : // constructor function name
557 identifier = token.substring(1);
558 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.CONSTRUCTOR, phpFileName);
560 case 'm' : //method inside a class
561 identifier = token.substring(1);
562 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.METHOD, phpFileName, classname);
564 case 'v' : // variable inside a class
565 identifier = token.substring(1);
566 phpIdentifier = new PHPIdentifierLocation(identifier, PHPIdentifier.VARIABLE, phpFileName, classname);
570 phpIdentifier = null;
573 if (identifier != null && phpIdentifier != null) {
574 ArrayList list = (ArrayList) fIndentifierMap.get(identifier);
577 for (int i = 0; i < list.size(); i++) {
578 if (list.get(i).equals(phpIdentifier)) {
583 if (list.size() == 0) {
584 fIndentifierMap.remove(identifier);
589 fFileMap.remove(phpFileName);
593 * Save the current index information in the projects index file
596 public void writeFile() {
597 FileWriter fileWriter;
599 fileWriter = new FileWriter(fFilename);
601 Collection collection = fFileMap.values();
602 Iterator iterator = collection.iterator();
603 while (iterator.hasNext()) {
604 line = (String) iterator.next();
605 fileWriter.write(line + '\n');
608 } catch (FileNotFoundException e) {
609 // ignore exception; project is deleted by user
610 } catch (IOException e) {
611 // TODO Auto-generated catch block
621 public SortedMap getIdentifierMap() {
622 return fIndentifierMap;