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