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