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