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