fix #774 infinite loop in net.sourceforge.phpeclipse.builder.IdentifierIndexManager...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPCompletionProcessor.java
1 /**********************************************************************
2  Copyright (c) 2000, 2002 IBM Corp. and others.
3  All rights reserved. This program and the accompanying materials
4  are made available under the terms of the Common Public License v1.0
5  which accompanies this distribution, and is available at
6  http://www.eclipse.org/legal/cpl-v10.html
7
8  Contributors:
9  IBM Corporation - Initial implementation
10  **********************************************************************/
11 package net.sourceforge.phpeclipse.phpeditor.php;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.SortedMap;
22
23 import net.sourceforge.phpdt.core.ICompilationUnit;
24 import net.sourceforge.phpdt.core.IJavaElement;
25 import net.sourceforge.phpdt.core.IMethod;
26 import net.sourceforge.phpdt.core.IType;
27 import net.sourceforge.phpdt.core.JavaCore;
28 import net.sourceforge.phpdt.core.ToolFactory;
29 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
30 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
31 import net.sourceforge.phpdt.internal.compiler.DefaultErrorHandlingPolicies;
32 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
33 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
34 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
35 import net.sourceforge.phpdt.internal.compiler.parser.SyntaxError;
36 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
37 import net.sourceforge.phpdt.internal.compiler.parser.VariableInfo;
38 import net.sourceforge.phpdt.internal.compiler.problem.DefaultProblemFactory;
39 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
40 import net.sourceforge.phpdt.internal.corext.template.php.JavaContext;
41 import net.sourceforge.phpdt.internal.corext.template.php.JavaContextType;
42 import net.sourceforge.phpdt.internal.ui.text.PHPCodeReader;
43 import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal;
44 import net.sourceforge.phpdt.internal.ui.text.java.JavaParameterListValidator;
45 import net.sourceforge.phpdt.internal.ui.text.java.PHPCompletionProposalComparator;
46 import net.sourceforge.phpdt.internal.ui.text.template.BuiltInEngine;
47 import net.sourceforge.phpdt.internal.ui.text.template.DeclarationEngine;
48 import net.sourceforge.phpdt.internal.ui.text.template.LocalVariableProposal;
49 import net.sourceforge.phpdt.internal.ui.text.template.contentassist.TemplateEngine;
50 import net.sourceforge.phpdt.ui.IWorkingCopyManager;
51 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
52 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
53 import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
54 import net.sourceforge.phpeclipse.phpeditor.PHPSyntaxRdr;
55
56 import org.eclipse.core.resources.IFile;
57 import org.eclipse.core.resources.IProject;
58 import org.eclipse.jface.text.BadLocationException;
59 import org.eclipse.jface.text.IDocument;
60 import org.eclipse.jface.text.IRegion;
61 import org.eclipse.jface.text.ITextViewer;
62 import org.eclipse.jface.text.Region;
63 import org.eclipse.jface.text.contentassist.ICompletionProposal;
64 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
65 import org.eclipse.jface.text.contentassist.IContextInformation;
66 import org.eclipse.jface.text.contentassist.IContextInformationExtension;
67 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
68 import org.eclipse.jface.text.templates.TemplateContextType;
69 import org.eclipse.swt.graphics.Image;
70 import org.eclipse.swt.graphics.Point;
71 import org.eclipse.ui.IEditorInput;
72 import org.eclipse.ui.IEditorPart;
73 import org.eclipse.ui.IFileEditorInput;
74
75 /**
76  * Example PHP completion processor.
77  */
78 public class PHPCompletionProcessor implements IContentAssistProcessor {
79         /**
80          * Simple content assist tip closer. The tip is valid in a range of 5
81          * characters around its popup location.
82          */
83         // protected static class Validator implements IContextInformationValidator,
84         // IContextInformationPresenter {
85         // protected int fInstallOffset;
86         //
87         // /*
88         // * @see IContextInformationValidator#isContextInformationValid(int)
89         // */
90         // public boolean isContextInformationValid(int offset) {
91         // return Math.abs(fInstallOffset - offset) < 5;
92         // }
93         //
94         // /*
95         // * @see IContextInformationValidator#install(IContextInformation,
96         // ITextViewer, int)
97         // */
98         // public void install(IContextInformation info, ITextViewer viewer, int
99         // offset) {
100         // fInstallOffset = offset;
101         // }
102         //
103         // /*
104         // * @see
105         // org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int,
106         // TextPresentation)
107         // */
108         // public boolean updatePresentation(int documentPosition, TextPresentation
109         // presentation) {
110         // return false;
111         // }
112         // };
113         private static class ContextInformationWrapper implements
114                         IContextInformation, IContextInformationExtension {
115                 private final IContextInformation fContextInformation;
116
117                 private int fPosition;
118
119                 public ContextInformationWrapper(IContextInformation contextInformation) {
120                         fContextInformation = contextInformation;
121                 }
122
123                 /*
124                  * @see IContextInformation#getContextDisplayString()
125                  */
126                 public String getContextDisplayString() {
127                         return fContextInformation.getContextDisplayString();
128                 }
129
130                 /*
131                  * @see IContextInformation#getImage()
132                  */
133                 public Image getImage() {
134                         return fContextInformation.getImage();
135                 }
136
137                 /*
138                  * @see IContextInformation#getInformationDisplayString()
139                  */
140                 public String getInformationDisplayString() {
141                         return fContextInformation.getInformationDisplayString();
142                 }
143
144                 /*
145                  * @see IContextInformationExtension#getContextInformationPosition()
146                  */
147                 public int getContextInformationPosition() {
148                         return fPosition;
149                 }
150
151                 public void setContextInformationPosition(int position) {
152                         fPosition = position;
153                 }
154         };
155
156         // private class TableName {
157         // String fTableName;
158         //
159         // TableName() {
160         // fTableName = null;
161         // }
162         //
163         // /**
164         // * @return Returns the tableName.
165         // */
166         // public String getTableName() {
167         // if (fTableName == null) {
168         // return "<!--no-table-->";
169         // }
170         // return fTableName;
171         // }
172         //
173         // /**
174         // * @param tableName
175         // * The tableName to set.
176         // */
177         // public void setTableName(String tableName) {
178         // fTableName = tableName;
179         // }
180         // }
181
182         private char[] fProposalAutoActivationSet;
183
184         protected IContextInformationValidator fValidator = null;
185
186         private TemplateEngine fTemplateEngine;
187
188         private PHPCompletionProposalComparator fComparator;
189
190         private IEditorPart fEditor;
191
192         protected IWorkingCopyManager fManager;
193
194         public PHPCompletionProcessor(IEditorPart editor) {
195                 fEditor = editor;
196                 fManager = PHPeclipsePlugin.getDefault().getWorkingCopyManager();
197                 TemplateContextType contextType = PHPeclipsePlugin.getDefault()
198                                 .getTemplateContextRegistry().getContextType("php"); //$NON-NLS-1$
199                 if (contextType != null)
200                         fTemplateEngine = new TemplateEngine(contextType);
201                 fComparator = new PHPCompletionProposalComparator();
202         }
203
204         /**
205          * Tells this processor to order the proposals alphabetically.
206          * 
207          * @param order
208          *            <code>true</code> if proposals should be ordered.
209          */
210         public void orderProposalsAlphabetically(boolean order) {
211                 fComparator.setOrderAlphabetically(order);
212         }
213
214         /**
215          * Sets this processor's set of characters triggering the activation of the
216          * completion proposal computation.
217          * 
218          * @param activationSet
219          *            the activation set
220          */
221         public void setCompletionProposalAutoActivationCharacters(
222                         char[] activationSet) {
223                 fProposalAutoActivationSet = activationSet;
224         }
225
226         /*
227          * (non-Javadoc) Method declared on IContentAssistProcessor
228          */
229         public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
230                         int documentOffset) {
231                 int contextInformationPosition = guessContextInformationPosition(
232                                 viewer, documentOffset);
233                 return internalComputeCompletionProposals(viewer, documentOffset,
234                                 contextInformationPosition);
235         }
236
237         private int getLastToken(List list, ITextViewer viewer,
238                         int completionPosition, JavaContext context) {
239                 // TableName tableName) {
240                 IDocument document = viewer.getDocument();
241                 int start = context.getStart();
242                 // int end = context.getEnd();
243                 String startText;
244                 int lastSignificantToken = ITerminalSymbols.TokenNameEOF;
245                 try {
246                         // begin search 2 lines behind of this
247                         int j = start;
248                         if (j != 0) {
249                                 char ch;
250                                 while (j > 0) {
251                                         ch = document.getChar(--j);
252                                         if (ch == '\n') {
253                                                 break;
254                                         }
255                                 }
256                                 while (j > 0) {
257                                         ch = document.getChar(--j);
258                                         if (ch == '\n') {
259                                                 break;
260                                         }
261                                 }
262                         }
263                         if (j != start) {
264                                 // scan the line for the dereferencing operator '->'
265                                 startText = document.get(j, start - j);
266                                 if (Scanner.DEBUG) {
267                                         System.out.println(startText);
268                                 }
269                                 int token = ITerminalSymbols.TokenNameEOF;
270                                 // token = getLastSQLToken(startText);
271                                 // tableName.setTableName(getLastSQLTableName(startText));
272                                 Scanner scanner = ToolFactory
273                                                 .createScanner(false, false, false);
274                                 scanner.setSource(startText.toCharArray());
275                                 scanner.setPHPMode(true);
276                                 int beforeLastToken = ITerminalSymbols.TokenNameEOF;
277                                 int lastToken = ITerminalSymbols.TokenNameEOF;
278                                 char[] ident = null;
279                                 try {
280                                         token = scanner.getNextToken();
281                                         lastToken = token;
282                                         while (token != ITerminalSymbols.TokenNameERROR
283                                                         && token != ITerminalSymbols.TokenNameEOF) {
284                                                 beforeLastToken = lastToken;
285                                                 if (token == ITerminalSymbols.TokenNameVariable) {
286                                                         ident = scanner.getCurrentTokenSource();
287                                                         if (ident.length == 5 && ident[0] == '$'
288                                                                         && ident[1] == 't' && ident[2] == 'h'
289                                                                         && ident[3] == 'i' && ident[4] == 's') {
290                                                                 token = ITerminalSymbols.TokenNamethis_PHP_COMPLETION;
291                                                         }
292                                                 }
293                                                 lastToken = token;
294                                                 // System.out.println(scanner.toStringAction(lastToken));
295                                                 token = scanner.getNextToken();
296                                         }
297                                 } catch (InvalidInputException e1) {
298                                 } catch (SyntaxError e) {
299                                 }
300                                 switch (lastToken) {
301                                 case ITerminalSymbols.TokenNameMINUS_GREATER:
302                                         // dereferencing operator '->' found
303                                         lastSignificantToken = ITerminalSymbols.TokenNameMINUS_GREATER;
304                                         if (beforeLastToken == ITerminalSymbols.TokenNameVariable) {
305                                                 lastSignificantToken = ITerminalSymbols.TokenNameVariable;
306                                                 list.set(0, ident);
307                                         } else if (beforeLastToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION) {
308                                                 lastSignificantToken = ITerminalSymbols.TokenNamethis_PHP_COMPLETION;
309                                                 list.set(0, ident);
310                                         }
311                                         break;
312                                 case ITerminalSymbols.TokenNamenew:
313                                         lastSignificantToken = ITerminalSymbols.TokenNamenew;
314                                         break;
315                                 }
316                         }
317                 } catch (BadLocationException e) {
318                 }
319                 return lastSignificantToken;
320         }
321
322         String getSQLTableName(String sqlText, int start) {
323                 int tableNameStart = -1;
324                 int currentCharacterPosition = start + 1;
325                 char ch;
326                 try {
327                         while (true) {
328                                 ch = sqlText.charAt(currentCharacterPosition++);
329                                 if (tableNameStart == -1 && Scanner.isPHPIdentifierStart(ch)) {
330                                         tableNameStart = currentCharacterPosition - 1;
331                                 } else {
332                                         if (!Scanner.isPHPIdentifierPart(ch)) {
333                                                 return sqlText.substring(tableNameStart,
334                                                                 currentCharacterPosition - 1);
335                                         }
336                                 }
337                         }
338                 } catch (IndexOutOfBoundsException e) {
339                         if (tableNameStart >= 0) {
340                                 return sqlText.substring(tableNameStart,
341                                                 currentCharacterPosition - 1);
342                         }
343                 }
344                 return "";
345         }
346
347         // private String getLastSQLTableName(String startText) {
348         // // scan for sql identifiers
349         // char ch = ' ';
350         // int currentSQLPosition = startText.length();
351         // int identEnd = -1;
352         // String ident = null;
353         // try {
354         // while (true) {
355         // ch = startText.charAt(--currentSQLPosition);
356         // if (Scanner.isSQLIdentifierPart(ch)) {
357         // // if (ch >= 'A' && ch <= 'Z') {
358         // if (identEnd < 0) {
359         // identEnd = currentSQLPosition + 1;
360         // }
361         // // } else if (ch >= 'a' && ch <= 'z') {
362         // // if (identEnd < 0) {
363         // // identEnd = currentSQLPosition + 1;
364         // // }
365         // } else if (identEnd >= 0) {
366         // ident = startText.substring(currentSQLPosition + 1, identEnd);
367         // // select -- from -- where --
368         // // update -- set -- where --
369         // // insert into -- ( -- ) values ( -- )
370         // if (ident.length() >= 4 && ident.length() <= 6) {
371         // ident = ident.toLowerCase();
372         // switch (ident.length()) {
373         // // case 3 :
374         // // if (ident.equals("set")) {
375         // // // System.out.println("set");
376         // // token = ITerminalSymbols.TokenNameSQLset;
377         // // return token;
378         // // }
379         // // break;
380         // case 4:
381         // if (ident.equals("from")) {
382         // // System.out.println("from");
383         // return getSQLTableName(startText, identEnd);
384         // } else if (ident.equals("into")) {
385         // // System.out.println("into");
386         // return getSQLTableName(startText, identEnd);
387         // }
388         // break;
389         // case 6:
390         // if (ident.equals("update")) {
391         // // System.out.println("update");
392         // return getSQLTableName(startText, identEnd);
393         // }
394         // break;
395         // }
396         // }
397         // identEnd = -1;
398         // } else if (Character.isWhitespace(ch)) {
399         // }
400         // }
401         // } catch (IndexOutOfBoundsException e) {
402         // }
403         // return "<!--no-table-->";
404         // }
405
406         /**
407          * Detect the last significant SQL token in the text before the completion
408          * 
409          * @param startText
410          */
411         // private int getLastSQLToken(String startText) {
412         // int token;
413         // // scan for sql identifiers
414         // char ch = ' ';
415         // int currentSQLPosition = startText.length();
416         // int identEnd = -1;
417         // String ident = null;
418         // try {
419         // while (true) {
420         // ch = startText.charAt(--currentSQLPosition);
421         // if (ch >= 'A' && ch <= 'Z') {
422         // if (identEnd < 0) {
423         // identEnd = currentSQLPosition + 1;
424         // }
425         // } else if (ch >= 'a' && ch <= 'z') {
426         // if (identEnd < 0) {
427         // identEnd = currentSQLPosition + 1;
428         // }
429         // } else if (identEnd >= 0) {
430         // ident = startText.substring(currentSQLPosition + 1, identEnd);
431         // // select -- from -- where --
432         // // update -- set -- where --
433         // // insert into -- ( -- ) values ( -- )
434         // if (ident.length() >= 3 && ident.length() <= 6) {
435         // ident = ident.toLowerCase();
436         // switch (ident.length()) {
437         // case 3:
438         // if (ident.equals("set")) {
439         // // System.out.println("set");
440         // token = ITerminalSymbols.TokenNameSQLset;
441         // return token;
442         // }
443         // break;
444         // case 4:
445         // if (ident.equals("from")) {
446         // // System.out.println("from");
447         // token = ITerminalSymbols.TokenNameSQLfrom;
448         // // getSQLTableName();
449         // return token;
450         // } else if (ident.equals("into")) {
451         // // System.out.println("into");
452         // token = ITerminalSymbols.TokenNameSQLinto;
453         // return token;
454         // }
455         // break;
456         // case 5:
457         // if (ident.equals("where")) {
458         // // System.out.println("where");
459         // token = ITerminalSymbols.TokenNameSQLwhere;
460         // return token;
461         // }
462         // break;
463         // case 6:
464         // if (ident.equals("select")) {
465         // // System.out.println("select");
466         // token = ITerminalSymbols.TokenNameSQLselect;
467         // return token;
468         // } else if (ident.equals("insert")) {
469         // // System.out.println("insert");
470         // token = ITerminalSymbols.TokenNameSQLinsert;
471         // return token;
472         // } else if (ident.equals("update")) {
473         // // System.out.println("update");
474         // token = ITerminalSymbols.TokenNameSQLupdate;
475         // return token;
476         // } else if (ident.equals("values")) {
477         // // System.out.println("values");
478         // token = ITerminalSymbols.TokenNameSQLvalues;
479         // return token;
480         // }
481         // break;
482         // }
483         // }
484         // identEnd = -1;
485         // }
486         // }
487         // } catch (IndexOutOfBoundsException e) {
488         // }
489         // return ITerminalSymbols.TokenNameEOF;
490         // }
491         private ICompletionProposal[] internalComputeCompletionProposals(
492                         ITextViewer viewer, int offset, int contextOffset) {
493                 ICompilationUnit unit = fManager.getWorkingCopy(fEditor
494                                 .getEditorInput());
495                 IDocument document = viewer.getDocument();
496                 IFile file = null;
497                 IProject project = null;
498                 if (offset > 0) {
499                         PHPEditor editor = null;
500                         if (fEditor != null && (fEditor instanceof PHPEditor)) {
501                                 editor = (PHPEditor) fEditor;
502                                 IEditorInput editorInput = editor.getEditorInput();
503                                 if (editorInput instanceof IFileEditorInput) {
504                                         file = ((IFileEditorInput) editorInput).getFile();
505                                         project = file.getProject();
506                                 } else {
507                                         return new ICompletionProposal[0];
508                                 }
509                         }
510                 }
511
512                 Point selection = viewer.getSelectedRange();
513                 // remember selected text
514                 String selectedText = null;
515                 if (selection.y != 0) {
516                         try {
517                                 selectedText = document.get(selection.x, selection.y);
518                         } catch (BadLocationException e) {
519                         }
520                 }
521
522                 if (offset > 2 && fProposalAutoActivationSet != null) {
523                         // restrict auto activation for '>' character to '->' token
524
525                         try {
526                                 char ch = document.getChar(offset - 1);
527                                 if (ch == '>') {
528                                         for (int i = 0; i < fProposalAutoActivationSet.length; i++) {
529                                                 ch = fProposalAutoActivationSet[i];
530                                                 if (ch == '>') { // auto activation enabled
531                                                         ch = document.getChar(offset - 2);
532                                                         if (ch != '-') {
533                                                                 return new IPHPCompletionProposal[0];
534                                                         }
535                                                         break;
536                                                 }
537                                         }
538                                 }
539                         } catch (BadLocationException e) {
540                                 e.printStackTrace();
541                         }
542                 }
543
544                 JavaContextType phpContextType = (JavaContextType) PHPeclipsePlugin
545                                 .getDefault().getTemplateContextRegistry()
546                                 .getContextType("php"); //$NON-NLS-1$
547                 JavaContext context = (JavaContext) phpContextType.createContext(
548                                 document, offset, selection.y, unit);
549                 context.setVariable("selection", selectedText); //$NON-NLS-1$
550                 String prefix = context.getKey();
551
552                 HashMap methodVariables = null;
553                 // HashMap typeVariables = null;
554                 HashMap unitVariables = null;
555                 ICompilationUnit compilationUnit = (ICompilationUnit) context
556                                 .findEnclosingElement(IJavaElement.COMPILATION_UNIT);
557                 // if (compilationUnit != null) {
558                 // unitVariables = ((CompilationUnit) compilationUnit).variables;
559                 // }
560                 IType type = (IType) context.findEnclosingElement(IJavaElement.TYPE);
561                 if (type != null) {
562                         // typeVariables = ((SourceType) type).variables;
563                 }
564                 IMethod method = (IMethod) context
565                                 .findEnclosingElement(IJavaElement.METHOD);
566                 // if (method != null) {
567                 // methodVariables = ((SourceMethod) method).variables;
568                 // }
569
570                 boolean emptyPrefix = prefix == null || prefix.equals("");
571                 IPHPCompletionProposal[] localVariableResults = new IPHPCompletionProposal[0];
572
573                 if (!emptyPrefix && prefix.length() >= 1 && prefix.charAt(0) == '$') {
574                         // php Variable ?
575                         String lowerCasePrefix = prefix.toLowerCase();
576                         HashSet localVariables = new HashSet();
577                         if (compilationUnit != null) {
578                                 unitVariables = getUnitVariables(unitVariables, compilationUnit);
579                                 getVariableProposals(localVariables, viewer, project, context,
580                                                 unitVariables, lowerCasePrefix, 94);
581                         }
582                         if (method != null) {
583                                 methodVariables = getMethodVariables(methodVariables, method);
584                                 getVariableProposals(localVariables, viewer, project, context,
585                                                 methodVariables, lowerCasePrefix, 99);
586                         }
587                         if (!localVariables.isEmpty()) {
588                                 localVariableResults = (IPHPCompletionProposal[]) localVariables
589                                                 .toArray(new IPHPCompletionProposal[localVariables
590                                                                 .size()]);
591                         }
592                 }
593
594                 // TableName sqlTable = new TableName();
595                 ArrayList list = new ArrayList();
596                 list.add(null);
597                 int lastSignificantToken = getLastToken(list, viewer, offset, context); // ,
598                                                                                                                                                                 // sqlTable);
599                 boolean useClassMembers = (lastSignificantToken == ITerminalSymbols.TokenNameMINUS_GREATER)
600                                 || (lastSignificantToken == ITerminalSymbols.TokenNameVariable)
601                                 || (lastSignificantToken == ITerminalSymbols.TokenNamenew)
602                                 || (lastSignificantToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION);
603
604                 if (fTemplateEngine != null) {
605                         IPHPCompletionProposal[] templateResults = new IPHPCompletionProposal[0];
606                         ICompletionProposal[] results;
607                         if (!emptyPrefix) {
608                                 fTemplateEngine.reset();
609                                 fTemplateEngine.complete(viewer, offset, unit);
610                                 templateResults = fTemplateEngine.getResults();
611                         }
612                         // TODO delete this
613                         IPHPCompletionProposal[] identifierResults = new IPHPCompletionProposal[0];
614
615                         // declarations stored in file project.index on project level
616                         IPHPCompletionProposal[] declarationResults = new IPHPCompletionProposal[0];
617                         if (project != null) {
618                                 DeclarationEngine declarationEngine;
619                                 JavaContextType contextType = (JavaContextType) PHPeclipsePlugin
620                                                 .getDefault().getTemplateContextRegistry()
621                                                 .getContextType("php"); //$NON-NLS-1$
622                                 if (contextType != null) {
623                                         IdentifierIndexManager indexManager = PHPeclipsePlugin
624                                                         .getDefault().getIndexManager(project);
625                                         SortedMap sortedMap;
626                                         declarationEngine = new DeclarationEngine(project,
627                                                         contextType, lastSignificantToken, file);
628                                         if (lastSignificantToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION) {
629                                                 // complete '$this->'
630                                                 sortedMap = indexManager.getIdentifiers(file);
631                                                 declarationEngine.completeObject(viewer, offset,
632                                                                 sortedMap, unit);
633                                         } else {
634                                                 String typeRef = null;
635                                                 char[] varName = (char[]) list.get(0);
636                                                 if (varName != null) {
637                                                         if (method != null) {
638                                                                 methodVariables = getMethodVariables(
639                                                                                 methodVariables, method);
640                                                                 VariableInfo info = (VariableInfo) methodVariables
641                                                                                 .get(new String(varName));
642                                                                 if (info != null && info.typeIdentifier != null) {
643                                                                         typeRef = new String(info.typeIdentifier);
644                                                                 }
645                                                         }
646                                                 }
647                                                 if (typeRef != null) {
648                                                         // complete '$variable->' with type information
649                                                         sortedMap = indexManager.getIdentifiers(typeRef);
650                                                         declarationEngine.completeObject(viewer, offset,
651                                                                         sortedMap, unit);
652                                                 } else {
653                                                         // complete '$variable->' without type information
654                                                         sortedMap = indexManager.getIdentifierMap();
655                                                         declarationEngine.complete(viewer, offset,
656                                                                         sortedMap, unit);
657                                                 }
658                                         }
659                                         declarationResults = declarationEngine.getResults();
660                                 }
661                         }
662                         // built in function names from phpsyntax.xml
663                         ArrayList syntaxbuffer = PHPSyntaxRdr.getSyntaxData();
664                         IPHPCompletionProposal[] builtinResults = new IPHPCompletionProposal[0];
665                         if ((!useClassMembers) && syntaxbuffer != null) {
666                                 BuiltInEngine builtinEngine;
667                                 JavaContextType contextType = (JavaContextType) PHPeclipsePlugin
668                                                 .getDefault().getTemplateContextRegistry()
669                                                 .getContextType("php"); //$NON-NLS-1$
670                                 if (contextType != null) {
671                                         builtinEngine = new BuiltInEngine(contextType);
672                                         builtinEngine.complete(viewer, offset, syntaxbuffer, unit);
673                                         builtinResults = builtinEngine.getResults();
674                                 }
675                         }
676                         // ICompletionProposal[] sqlResults = new ICompletionProposal[0];
677                         // if (project != null) {
678                         // sqlResults = getSQLProposals(viewer, project, context, prefix,
679                         // sqlTable);
680                         // }
681                         // concatenate the result arrays
682                         IPHPCompletionProposal[] total;
683                         total = new IPHPCompletionProposal[localVariableResults.length
684                                         + templateResults.length + identifierResults.length
685                                         + builtinResults.length + declarationResults.length];// +
686                         // sqlResults.length];
687                         System.arraycopy(templateResults, 0, total, 0,
688                                         templateResults.length);
689                         System.arraycopy(identifierResults, 0, total,
690                                         templateResults.length, identifierResults.length);
691                         System.arraycopy(builtinResults, 0, total, templateResults.length
692                                         + identifierResults.length, builtinResults.length);
693                         System.arraycopy(declarationResults, 0, total,
694                                         templateResults.length + identifierResults.length
695                                                         + builtinResults.length, declarationResults.length);
696                         // System.arraycopy(sqlResults, 0, total, templateResults.length +
697                         // identifierResults.length + builtinResults.length
698                         // + declarationResults.length, sqlResults.length);
699                         // System.arraycopy(localVariableResults, 0, total,
700                         // templateResults.length
701                         // + identifierResults.length + builtinResults.length
702                         // + declarationResults.length + sqlResults.length,
703                         // localVariableResults.length);
704                         System
705                                         .arraycopy(localVariableResults, 0, total,
706                                                         templateResults.length + identifierResults.length
707                                                                         + builtinResults.length
708                                                                         + declarationResults.length,
709                                                         localVariableResults.length);
710                         results = total;
711                         // fNumberOfComputedResults = (results == null ? 0 :
712                         // results.length);
713                         /*
714                          * Order here and not in result collector to make sure that the
715                          * order applies to all proposals and not just those of the
716                          * compilation unit.
717                          */
718                         return order(results);
719                 }
720                 return new IPHPCompletionProposal[0];
721         }
722
723         /**
724          * @param unitVariables
725          * @param unit
726          */
727         private HashMap getUnitVariables(HashMap unitVariables,
728                         ICompilationUnit unit) {
729                 if (unitVariables == null) {
730                         try {
731                                 String unitText = unit.getSource();
732                                 unitVariables = new HashMap();
733
734                                 ProblemReporter problemReporter = new ProblemReporter(
735                                                 DefaultErrorHandlingPolicies.exitAfterAllProblems(),
736                                                 new CompilerOptions(JavaCore.getOptions()),
737                                                 new DefaultProblemFactory());
738                                 UnitParser parser = new UnitParser(problemReporter);
739                                 parser.compilationUnit = new CompilationUnitDeclaration(
740                                                 problemReporter, null, unitText.length());
741                                 parser.parse(unitText, unitVariables);
742
743                         } catch (Exception e) {
744                                 // TODO Auto-generated catch block
745                                 e.printStackTrace();
746                                 PHPeclipsePlugin.log(e);
747                         }
748                 }
749                 return unitVariables;
750         }
751
752         /**
753          * @param methodVariables
754          * @param method
755          */
756         private HashMap getMethodVariables(HashMap methodVariables, IMethod method) {
757                 if (methodVariables == null) {
758                         try {
759                                 String methodText = method.getSource();
760                                 methodVariables = new HashMap();
761                                 ProblemReporter problemReporter = new ProblemReporter(
762                                                 DefaultErrorHandlingPolicies.exitAfterAllProblems(),
763                                                 new CompilerOptions(JavaCore.getOptions()),
764                                                 new DefaultProblemFactory());
765                                 UnitParser parser = new UnitParser(problemReporter);
766                                 parser.compilationUnit = new CompilationUnitDeclaration(
767                                                 problemReporter, null, methodText.length());
768                                 parser.parseFunction(methodText, methodVariables);
769                         } catch (Exception e) {
770                                 // TODO Auto-generated catch block
771                                 e.printStackTrace();
772                                 PHPeclipsePlugin.log(e);
773                         }
774                 }
775                 return methodVariables;
776         }
777
778         /**
779          * @param viewer
780          * @param project
781          * @param context
782          * @param prefix
783          * @return
784          */
785         private void getVariableProposals(HashSet localVariables,
786                         ITextViewer viewer, IProject project, JavaContext context,
787                         HashMap variables, String prefix, int relevance) {
788                 // try {
789                 int start = context.getStart();
790                 int end = context.getEnd();
791                 IRegion region = new Region(start, end - start);
792                 // IMethod method = (IMethod)
793                 // context.findEnclosingElement(IJavaElement.METHOD);
794                 // if (method != null && (method instanceof SourceMethod) &&
795                 // ((SourceMethod)
796                 // method).variables != null) {
797                 // HashMap map = ((SourceMethod) method).variables;
798                 Set set = variables.keySet();
799                 Iterator iter = set.iterator();
800                 String varName;
801                 boolean matchesVarName;
802                 while (iter.hasNext()) {
803                         varName = (String) iter.next();
804                         if (varName.length() >= prefix.length()) {
805                                 matchesVarName = true;
806                                 for (int i = 0; i < prefix.length(); i++) {
807                                         if (prefix.charAt(i) != Character.toLowerCase(varName
808                                                         .charAt(i))) {
809                                                 matchesVarName = false;
810                                                 break;
811                                         }
812                                 }
813                                 if (matchesVarName) {
814                                         LocalVariableProposal prop;
815                                         // if (varName.length == prefix.length()) {
816                                         // prop = new LocalVariableProposal(new String(varName),
817                                         // region,
818                                         // viewer, relevance-10);
819                                         // } else {
820                                         prop = new LocalVariableProposal(new String(varName),
821                                                         region, viewer, relevance);
822                                         // }
823                                         localVariables.add(prop);
824                                 }
825                         }
826                 }
827                 // }
828
829                 // char[] varName;
830                 // boolean matchesVarName;
831                 // if (method != null) {
832                 // ISourceRange range = method.getSourceRange();
833                 // char[] source = method.getSource().toCharArray();
834                 // Scanner scanner = new Scanner();
835                 // scanner.setSource(source);
836                 // scanner.phpMode = true;
837                 // int token = Scanner.TokenNameWHITESPACE;
838                 // while ((token = scanner.getNextToken()) != Scanner.TokenNameEOF) {
839                 // if (token == Scanner.TokenNameVariable) {
840                 // varName = scanner.getCurrentTokenSource();
841                 // if (varName.length >= prefix.length()) {
842                 // matchesVarName = true;
843                 // for (int i = 0; i < prefix.length(); i++) {
844                 // if (prefix.charAt(i) != varName[i]) {
845                 // matchesVarName = false;
846                 // break;
847                 // }
848                 // }
849                 // if (matchesVarName) {
850                 // LocalVariableProposal prop = new LocalVariableProposal(new
851                 // String(varName), region, viewer);
852                 // if (varName.length == prefix.length()) {
853                 // prop.setRelevance(98);
854                 // }
855                 // localVariables.add(prop);
856                 // }
857                 // }
858                 // }
859                 // }
860                 // }
861                 // } catch (Throwable e) {
862                 // // ignore - Syntax exceptions could occur, if there are syntax errors
863                 // !
864                 // }
865         }
866
867         /**
868          * @param viewer
869          * @param project
870          * @param context
871          * @param prefix
872          * @param sqlTable
873          * @param sqlResults
874          * @return
875          */
876         // private ICompletionProposal[] getSQLProposals(ITextViewer viewer,
877         // IProject
878         // project, DocumentTemplateContext context,
879         // String prefix, TableName sqlTable) {
880         // ICompletionProposal[] sqlResults = new ICompletionProposal[0];
881         // // Get The Database bookmark from the Quantum SQL plugin:
882         // // BookmarkCollection sqlBookMarks = BookmarkCollection.getInstance();
883         // // if (sqlBookMarks != null) {
884         // String bookmarkString =
885         // ProjectPrefUtil.getMiscProjectsPreferenceValue(project,
886         // IPreferenceConstants.PHP_BOOKMARK_DEFAULT);
887         // if (bookmarkString != null && !bookmarkString.equals("")) {
888         // String[] bookmarks = ExternalInterface.getBookmarkNames();
889         // boolean foundBookmark = false;
890         // for (int i = 0; i < bookmarks.length; i++) {
891         // if (bookmarks[i].equals(bookmarkString)) {
892         // foundBookmark = true;
893         // }
894         // }
895         // if (!foundBookmark) {
896         // return sqlResults;
897         // }
898         // // Bookmark bookmark = sqlBookMarks.find(bookmarkString);
899         // ArrayList sqlList = new ArrayList();
900         // if (!ExternalInterface.isBookmarkConnected(bookmarkString)) {
901         // ExternalInterface.connectBookmark(bookmarkString, null);
902         // if (!ExternalInterface.isBookmarkConnected(bookmarkString)) {
903         // return sqlResults;
904         // }
905         // }
906         // // if (ExternalInterface.isBookmarkConnected(bookmarkString)) {
907         // try {
908         // int start = context.getStart();
909         // int end = context.getEnd();
910         // String foundSQLTableName = sqlTable.getTableName();
911         // String tableName;
912         // String columnName;
913         // String prefixWithoutDollar = prefix;
914         // boolean isDollarPrefix = false;
915         // if (prefix.length() > 0 && prefix.charAt(0) == '$') {
916         // prefixWithoutDollar = prefix.substring(1);
917         // isDollarPrefix = true;
918         // }
919         // IRegion region = new Region(start, end - start);
920         // ResultSet set;
921         // if (!isDollarPrefix) {
922         // String[] tableNames = ExternalInterface.getMatchingTableNames(null,
923         // bookmarkString, prefixWithoutDollar, null, false);
924         // for (int i = 0; i < tableNames.length; i++) {
925         // sqlList.add(new SQLProposal(tableNames[i], context, region, viewer,
926         // PHPUiImages.get(PHPUiImages.IMG_TABLE)));
927         // }
928         // }
929         //
930         // String[] columnNames = ExternalInterface.getMatchingColumnNames(null,
931         // bookmarkString, prefixWithoutDollar, null, false);
932         // for (int i = 0; i < columnNames.length; i++) {
933         // sqlList.add(new SQLProposal(columnNames[i], context, region, viewer,
934         // PHPUiImages.get(PHPUiImages.IMG_TABLE)));
935         // }
936         //
937         // sqlResults = new IPHPCompletionProposal[sqlList.size()];
938         // for (int i = 0; i < sqlList.size(); i++) {
939         // sqlResults[i] = (SQLProposal) sqlList.get(i);
940         // }
941         // } catch (Exception /* NotConnectedException */ e) {
942         //
943         // }
944         // // }
945         // }
946         // // }
947         // return sqlResults;
948         // }
949         private boolean looksLikeMethod(PHPCodeReader reader) throws IOException {
950                 int curr = reader.read();
951                 while (curr != PHPCodeReader.EOF && Character.isWhitespace((char) curr))
952                         curr = reader.read();
953
954                 if (curr == PHPCodeReader.EOF)
955                         return false;
956
957                 return Scanner.isPHPIdentifierPart((char) curr);
958         }
959
960         private int guessContextInformationPosition(ITextViewer viewer, int offset) {
961                 int contextPosition = offset;
962                 IDocument document = viewer.getDocument();
963                 try {
964
965                         PHPCodeReader reader = new PHPCodeReader();
966                         reader.configureBackwardReader(document, offset, true, true);
967
968                         int nestingLevel = 0;
969
970                         int curr = reader.read();
971                         while (curr != PHPCodeReader.EOF) {
972
973                                 if (')' == (char) curr)
974                                         ++nestingLevel;
975
976                                 else if ('(' == (char) curr) {
977                                         --nestingLevel;
978
979                                         if (nestingLevel < 0) {
980                                                 int start = reader.getOffset();
981                                                 if (looksLikeMethod(reader))
982                                                         return start + 1;
983                                         }
984                                 }
985
986                                 curr = reader.read();
987                         }
988                 } catch (IOException e) {
989                 }
990                 return contextPosition;
991         }
992
993         /**
994          * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
995          */
996         public IContextInformation[] computeContextInformation(ITextViewer viewer,
997                         int offset) {
998                 int contextInformationPosition = guessContextInformationPosition(
999                                 viewer, offset);
1000                 List result = addContextInformations(viewer, contextInformationPosition);
1001                 return (IContextInformation[]) result
1002                                 .toArray(new IContextInformation[result.size()]);
1003         }
1004
1005         private List addContextInformations(ITextViewer viewer, int offset) {
1006                 ICompletionProposal[] proposals = internalComputeCompletionProposals(
1007                                 viewer, offset, -1);
1008                 List result = new ArrayList();
1009                 for (int i = 0; i < proposals.length; i++) {
1010                         IContextInformation contextInformation = proposals[i]
1011                                         .getContextInformation();
1012                         if (contextInformation != null) {
1013                                 ContextInformationWrapper wrapper = new ContextInformationWrapper(
1014                                                 contextInformation);
1015                                 wrapper.setContextInformationPosition(offset);
1016                                 result.add(wrapper);
1017                         }
1018                 }
1019                 return result;
1020         }
1021
1022         /**
1023          * Order the given proposals.
1024          */
1025         private ICompletionProposal[] order(ICompletionProposal[] proposals) {
1026                 Arrays.sort(proposals, fComparator);
1027                 // int len = proposals.length;
1028                 // if (len > 10) {
1029                 // len = 10;
1030                 // }
1031                 // for (int i = 0; i < len; i++) {
1032                 // System.out.println(proposals[i].getDisplayString());
1033                 // }
1034                 return proposals;
1035         }
1036
1037         /*
1038          * (non-Javadoc) Method declared on IContentAssistProcessor
1039          */
1040         public char[] getCompletionProposalAutoActivationCharacters() {
1041                 return fProposalAutoActivationSet;
1042                 // return null; // new char[] { '$' };
1043         }
1044
1045         /*
1046          * (non-Javadoc) Method declared on IContentAssistProcessor
1047          */
1048         public char[] getContextInformationAutoActivationCharacters() {
1049                 return null;
1050         }
1051
1052         /*
1053          * (non-Javadoc) Method declared on IContentAssistProcessor
1054          */
1055         public IContextInformationValidator getContextInformationValidator() {
1056                 if (fValidator == null)
1057                         fValidator = new JavaParameterListValidator();
1058                 return fValidator;
1059         }
1060
1061         /*
1062          * (non-Javadoc) Method declared on IContentAssistProcessor
1063          */
1064         public String getErrorMessage() {
1065                 return null;
1066         }
1067 }