Local variable proposals (Ctrl+SPACE) added
[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.sql.Connection;
14 import java.sql.DatabaseMetaData;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.HashSet;
20 import java.util.List;
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.ISourceRange;
27 import net.sourceforge.phpdt.core.ToolFactory;
28 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
29 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
30 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
31 import net.sourceforge.phpdt.internal.corext.template.php.JavaContext;
32 import net.sourceforge.phpdt.internal.corext.template.php.JavaContextType;
33 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
34 import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal;
35 import net.sourceforge.phpdt.internal.ui.text.java.PHPCompletionProposalComparator;
36 import net.sourceforge.phpdt.internal.ui.text.template.BuiltInEngine;
37 import net.sourceforge.phpdt.internal.ui.text.template.DeclarationEngine;
38 import net.sourceforge.phpdt.internal.ui.text.template.IdentifierEngine;
39 import net.sourceforge.phpdt.internal.ui.text.template.LocalVariableProposal;
40 import net.sourceforge.phpdt.internal.ui.text.template.SQLProposal;
41 import net.sourceforge.phpdt.internal.ui.text.template.contentassist.TemplateEngine;
42 import net.sourceforge.phpdt.ui.IWorkingCopyManager;
43 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
44 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
45 import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
46 import net.sourceforge.phpeclipse.phpeditor.PHPSyntaxRdr;
47 import net.sourceforge.phpeclipse.ui.IPreferenceConstants;
48 import net.sourceforge.phpeclipse.ui.overlaypages.Util;
49
50 import org.eclipse.core.resources.IFile;
51 import org.eclipse.core.resources.IProject;
52 import org.eclipse.jface.text.BadLocationException;
53 import org.eclipse.jface.text.IDocument;
54 import org.eclipse.jface.text.IRegion;
55 import org.eclipse.jface.text.ITextViewer;
56 import org.eclipse.jface.text.Region;
57 import org.eclipse.jface.text.TextPresentation;
58 import org.eclipse.jface.text.contentassist.ICompletionProposal;
59 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
60 import org.eclipse.jface.text.contentassist.IContextInformation;
61 import org.eclipse.jface.text.contentassist.IContextInformationExtension;
62 import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
63 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
64 import org.eclipse.jface.text.templates.TemplateContextType;
65 import org.eclipse.swt.graphics.Image;
66 import org.eclipse.swt.graphics.Point;
67 import org.eclipse.ui.IEditorPart;
68 import org.eclipse.ui.IFileEditorInput;
69
70 import com.quantum.model.Bookmark;
71 import com.quantum.model.BookmarkCollection;
72 import com.quantum.model.NotConnectedException;
73 import com.quantum.util.connection.ConnectionUtil;
74
75 /**
76  * Example PHP completion processor.
77  */
78 public class PHPCompletionProcessor implements IContentAssistProcessor {
79   /**
80    * Simple content assist tip closer. The tip is valid in a range of 5 characters around its popup location.
81    */
82   protected static class Validator implements IContextInformationValidator, IContextInformationPresenter {
83     protected int fInstallOffset;
84
85     /*
86      * @see IContextInformationValidator#isContextInformationValid(int)
87      */
88     public boolean isContextInformationValid(int offset) {
89       return Math.abs(fInstallOffset - offset) < 5;
90     }
91
92     /*
93      * @see IContextInformationValidator#install(IContextInformation, ITextViewer, int)
94      */
95     public void install(IContextInformation info, ITextViewer viewer, int offset) {
96       fInstallOffset = offset;
97     }
98
99     /*
100      * @see org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int, TextPresentation)
101      */
102     public boolean updatePresentation(int documentPosition, TextPresentation presentation) {
103       return false;
104     }
105   };
106
107   private static class ContextInformationWrapper implements IContextInformation, IContextInformationExtension {
108     private final IContextInformation fContextInformation;
109
110     private int fPosition;
111
112     public ContextInformationWrapper(IContextInformation contextInformation) {
113       fContextInformation = contextInformation;
114     }
115
116     /*
117      * @see IContextInformation#getContextDisplayString()
118      */
119     public String getContextDisplayString() {
120       return fContextInformation.getContextDisplayString();
121     }
122
123     /*
124      * @see IContextInformation#getImage()
125      */
126     public Image getImage() {
127       return fContextInformation.getImage();
128     }
129
130     /*
131      * @see IContextInformation#getInformationDisplayString()
132      */
133     public String getInformationDisplayString() {
134       return fContextInformation.getInformationDisplayString();
135     }
136
137     /*
138      * @see IContextInformationExtension#getContextInformationPosition()
139      */
140     public int getContextInformationPosition() {
141       return fPosition;
142     }
143
144     public void setContextInformationPosition(int position) {
145       fPosition = position;
146     }
147   };
148
149   private class TableName {
150     String fTableName;
151
152     TableName() {
153       fTableName = null;
154     }
155
156     /**
157      * @return Returns the tableName.
158      */
159     public String getTableName() {
160       if (fTableName == null) {
161         return "<!--no-table-->";
162       }
163       return fTableName;
164     }
165
166     /**
167      * @param tableName
168      *          The tableName to set.
169      */
170     public void setTableName(String tableName) {
171       fTableName = tableName;
172     }
173   }
174
175   private char[] fProposalAutoActivationSet;
176
177   protected IContextInformationValidator fValidator = new Validator();
178
179   private TemplateEngine fTemplateEngine;
180
181   private PHPCompletionProposalComparator fComparator;
182
183   private int fNumberOfComputedResults = 0;
184
185   private IEditorPart fEditor;
186
187   protected IWorkingCopyManager fManager;
188
189   public PHPCompletionProcessor(IEditorPart editor) {
190     fEditor = editor;
191     fManager = PHPeclipsePlugin.getDefault().getWorkingCopyManager();
192     TemplateContextType contextType = PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType("php"); //$NON-NLS-1$
193     if (contextType != null)
194       fTemplateEngine = new TemplateEngine(contextType);
195     fComparator = new PHPCompletionProposalComparator();
196   }
197
198   /**
199    * Tells this processor to order the proposals alphabetically.
200    * 
201    * @param order
202    *          <code>true</code> if proposals should be ordered.
203    */
204   public void orderProposalsAlphabetically(boolean order) {
205     fComparator.setOrderAlphabetically(order);
206   }
207
208   /**
209    * Sets this processor's set of characters triggering the activation of the completion proposal computation.
210    * 
211    * @param activationSet
212    *          the activation set
213    */
214   public void setCompletionProposalAutoActivationCharacters(char[] activationSet) {
215     fProposalAutoActivationSet = activationSet;
216   }
217
218   /*
219    * (non-Javadoc) Method declared on IContentAssistProcessor
220    */
221   public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) {
222     int contextInformationPosition = guessContextInformationPosition(viewer, documentOffset);
223     return internalComputeCompletionProposals(viewer, documentOffset, contextInformationPosition);
224   }
225
226   private int getLastToken(ITextViewer viewer, int completionPosition, JavaContext context, TableName tableName) {
227     IDocument document = viewer.getDocument();
228     int start = context.getStart();
229     int end = context.getEnd();
230     String startText;
231     int lastSignificantToken = ITerminalSymbols.TokenNameEOF;
232     try {
233       // begin search 2 lines behind of this
234       int j = start;
235       if (j != 0) {
236         char ch;
237         while (j-- > 0) {
238           ch = document.getChar(j);
239           if (ch == '\n') {
240             break;
241           }
242         }
243         while (j-- > 0) {
244           ch = document.getChar(j);
245           if (ch == '\n') {
246             break;
247           }
248         }
249       }
250       if (j != start) {
251         // scan the line for the dereferencing operator '->'
252         startText = document.get(j, start - j);
253         if (Scanner.DEBUG) {
254           System.out.println(startText);
255         }
256         int token = ITerminalSymbols.TokenNameEOF;
257         //        token = getLastSQLToken(startText);
258         tableName.setTableName(getLastSQLTableName(startText));
259         Scanner scanner = ToolFactory.createScanner(false, false, false);
260         scanner.setSource(startText.toCharArray());
261         scanner.setPHPMode(true);
262         int beforeLastToken = ITerminalSymbols.TokenNameEOF;
263         int lastToken = ITerminalSymbols.TokenNameEOF;
264         char[] ident;
265         try {
266           token = scanner.getNextToken();
267           lastToken = token;
268           while (token != ITerminalSymbols.TokenNameERROR && token != ITerminalSymbols.TokenNameEOF) {
269             beforeLastToken = lastToken;
270             if (lastToken == ITerminalSymbols.TokenNameVariable) {
271               ident = scanner.getCurrentTokenSource();
272               if (ident.length == 5 && ident[0] == '$' && ident[1] == 't' && ident[2] == 'h' && ident[3] == 'i' && ident[4] == 's') {
273                 beforeLastToken = ITerminalSymbols.TokenNamethis_PHP_COMPLETION;
274               }
275             }
276             lastToken = token;
277             //                                                          System.out.println(scanner.toStringAction(lastToken));
278             token = scanner.getNextToken();
279           }
280         } catch (InvalidInputException e1) {
281         }
282         switch (lastToken) {
283         case ITerminalSymbols.TokenNameMINUS_GREATER:
284           // dereferencing operator '->' found
285           lastSignificantToken = ITerminalSymbols.TokenNameMINUS_GREATER;
286           if (beforeLastToken == ITerminalSymbols.TokenNameVariable) {
287             lastSignificantToken = ITerminalSymbols.TokenNameVariable;
288           }
289           break;
290         case ITerminalSymbols.TokenNamenew:
291           lastSignificantToken = ITerminalSymbols.TokenNamenew;
292           break;
293         }
294       }
295     } catch (BadLocationException e) {
296     }
297     return lastSignificantToken;
298   }
299
300   String getSQLTableName(String sqlText, int start) {
301     int tableNameStart = -1;
302     int currentCharacterPosition = start + 1;
303     char ch;
304     try {
305       while (true) {
306         ch = sqlText.charAt(currentCharacterPosition++);
307         if (tableNameStart == -1 && Character.isJavaIdentifierStart(ch)) {
308           tableNameStart = currentCharacterPosition - 1;
309         } else {
310           if (!Character.isJavaIdentifierPart(ch)) {
311             return sqlText.substring(tableNameStart, currentCharacterPosition - 1);
312           }
313         }
314       }
315     } catch (IndexOutOfBoundsException e) {
316       if (tableNameStart >= 0) {
317         return sqlText.substring(tableNameStart, currentCharacterPosition - 1);
318       }
319     }
320     return "";
321   }
322
323   private String getLastSQLTableName(String startText) {
324     int token;
325     // scan for sql identifiers
326     char ch = ' ';
327     int currentSQLPosition = startText.length();
328     int identEnd = -1;
329     String ident = null;
330     boolean whiteSpace = true;
331     try {
332       while (true) {
333         ch = startText.charAt(--currentSQLPosition);
334         if (ch >= 'A' && ch <= 'Z') {
335           if (identEnd < 0) {
336             identEnd = currentSQLPosition + 1;
337           }
338         } else if (ch >= 'a' && ch <= 'z') {
339           if (identEnd < 0) {
340             identEnd = currentSQLPosition + 1;
341           }
342         } else if (identEnd >= 0) {
343           ident = startText.substring(currentSQLPosition + 1, identEnd);
344           // select -- from -- where --
345           // update -- set -- where --
346           // insert into -- ( -- ) values ( -- )
347           if (ident.length() >= 4 && ident.length() <= 6) {
348             ident = ident.toLowerCase();
349             switch (ident.length()) {
350             //              case 3 :
351             //                if (ident.equals("set")) {
352             //                  // System.out.println("set");
353             //                  token = ITerminalSymbols.TokenNameSQLset;
354             //                  return token;
355             //                }
356             //                break;
357             case 4:
358               if (ident.equals("from")) {
359                 //                  System.out.println("from");
360                 token = ITerminalSymbols.TokenNameSQLfrom;
361                 return getSQLTableName(startText, identEnd);
362               } else if (ident.equals("into")) {
363                 //                System.out.println("into");
364                 token = ITerminalSymbols.TokenNameSQLinto;
365                 return getSQLTableName(startText, identEnd);
366               }
367               break;
368             //              case 5 :
369             //                if (ident.equals("where")) {
370             //                  // System.out.println("where");
371             //                  token = ITerminalSymbols.TokenNameSQLwhere;
372             //                  return token;
373             //                }
374             //                break;
375             case 6:
376               //                if (ident.equals("select")) {
377               //                  // System.out.println("select");
378               //                  token = ITerminalSymbols.TokenNameSQLselect;
379               //                  return token;
380               //                } else if (ident.equals("insert")) {
381               //                  // System.out.println("insert");
382               //                  token = ITerminalSymbols.TokenNameSQLinsert;
383               //                  return token;
384               //                } else
385               if (ident.equals("update")) {
386                 //                  System.out.println("update");
387                 token = ITerminalSymbols.TokenNameSQLupdate;
388                 return getSQLTableName(startText, identEnd);
389               }
390               //                else if (ident.equals("values")) {
391               //                  // System.out.println("values");
392               //                  token = ITerminalSymbols.TokenNameSQLvalues;
393               //                  return token;
394               //                }
395               break;
396             }
397           }
398           whiteSpace = false;
399           identEnd = -1;
400         } else if (Character.isWhitespace(ch)) {
401         } else {
402           whiteSpace = false;
403         }
404       }
405     } catch (IndexOutOfBoundsException e) {
406     }
407     return "<!--no-table-->";
408   }
409
410   /**
411    * Detect the last significant SQL token in the text before the completion
412    * 
413    * @param startText
414    */
415   private int getLastSQLToken(String startText) {
416     int token;
417     // scan for sql identifiers
418     char ch = ' ';
419     int currentSQLPosition = startText.length();
420     int identEnd = -1;
421     String ident = null;
422     boolean whiteSpace = true;
423     try {
424       while (true) {
425         ch = startText.charAt(--currentSQLPosition);
426         if (ch >= 'A' && ch <= 'Z') {
427           if (identEnd < 0) {
428             identEnd = currentSQLPosition + 1;
429           }
430         } else if (ch >= 'a' && ch <= 'z') {
431           if (identEnd < 0) {
432             identEnd = currentSQLPosition + 1;
433           }
434         } else if (identEnd >= 0) {
435           ident = startText.substring(currentSQLPosition + 1, identEnd);
436           // select -- from -- where --
437           // update -- set -- where --
438           // insert into -- ( -- ) values ( -- )
439           if (ident.length() >= 3 && ident.length() <= 6) {
440             ident = ident.toLowerCase();
441             switch (ident.length()) {
442             case 3:
443               if (ident.equals("set")) {
444                 //                  System.out.println("set");
445                 token = ITerminalSymbols.TokenNameSQLset;
446                 return token;
447               }
448               break;
449             case 4:
450               if (ident.equals("from")) {
451                 //                  System.out.println("from");
452                 token = ITerminalSymbols.TokenNameSQLfrom;
453                 //getSQLTableName();
454                 return token;
455               } else if (ident.equals("into")) {
456                 //                System.out.println("into");
457                 token = ITerminalSymbols.TokenNameSQLinto;
458                 return token;
459               }
460               break;
461             case 5:
462               if (ident.equals("where")) {
463                 //                  System.out.println("where");
464                 token = ITerminalSymbols.TokenNameSQLwhere;
465                 return token;
466               }
467               break;
468             case 6:
469               if (ident.equals("select")) {
470                 //                  System.out.println("select");
471                 token = ITerminalSymbols.TokenNameSQLselect;
472                 return token;  
473               } else if (ident.equals("insert")) {
474                 //                  System.out.println("insert");
475                 token = ITerminalSymbols.TokenNameSQLinsert;
476                 return token;
477               } else if (ident.equals("update")) {
478                 //                  System.out.println("update");
479                 token = ITerminalSymbols.TokenNameSQLupdate;
480                 return token;
481               } else if (ident.equals("values")) {
482                 //                System.out.println("values");
483                 token = ITerminalSymbols.TokenNameSQLvalues;
484                 return token;
485               }
486               break;
487             }
488           }
489           whiteSpace = false;
490           identEnd = -1;
491         } else if (Character.isWhitespace(ch)) {
492         } else {
493           whiteSpace = false;
494         }
495       }
496     } catch (IndexOutOfBoundsException e) {
497     }
498     return ITerminalSymbols.TokenNameEOF;
499   }
500
501   private ICompletionProposal[] internalComputeCompletionProposals(ITextViewer viewer, int offset, int contextOffset) {
502     ICompilationUnit unit = fManager.getWorkingCopy(fEditor.getEditorInput());
503     IDocument document = viewer.getDocument();
504     Object[] identifiers = null;
505     IFile file = null;
506     IProject project = null;
507     if (offset > 0) {
508       PHPEditor editor = null;
509       if (fEditor != null && (fEditor instanceof PHPEditor)) {
510         editor = (PHPEditor) fEditor;
511         file = ((IFileEditorInput) editor.getEditorInput()).getFile();
512         project = file.getProject();
513       }
514     }
515
516     Point selection = viewer.getSelectedRange();
517
518     // remember selected text
519     String selectedText = null;
520     if (selection.y != 0) {
521       try {
522         selectedText = document.get(selection.x, selection.y);
523       } catch (BadLocationException e) {
524       }
525     }
526
527     JavaContextType phpContextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
528         "php"); //$NON-NLS-1$
529     JavaContext context = (JavaContext) phpContextType.createContext(document, offset, selection.y, unit);
530     context.setVariable("selection", selectedText); //$NON-NLS-1$
531     String prefix = context.getKey();
532     boolean emptyPrefix = prefix == null || prefix.equals("");
533     IPHPCompletionProposal[] localVariableResults = new IPHPCompletionProposal[0];
534     if (!emptyPrefix && prefix.length() >= 1 && prefix.charAt(0) == '$') { // php Variable ?
535       HashSet localVariables = getLocalVariableProposals(viewer, project, context, prefix);
536       if (localVariables.size() > 0) {
537         localVariableResults = (IPHPCompletionProposal[]) localVariables.toArray(new IPHPCompletionProposal[localVariables.size()]);
538       }
539     }
540
541     TableName sqlTable = new TableName();
542     int lastSignificantToken = getLastToken(viewer, offset, context, sqlTable);
543     boolean useClassMembers = (lastSignificantToken == ITerminalSymbols.TokenNameMINUS_GREATER)
544         || (lastSignificantToken == ITerminalSymbols.TokenNameVariable) || (lastSignificantToken == ITerminalSymbols.TokenNamenew)
545         || (lastSignificantToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION);
546
547     if (fTemplateEngine != null) {
548       IPHPCompletionProposal[] templateResults = new IPHPCompletionProposal[0];
549       ICompletionProposal[] results;
550       if (!emptyPrefix) {
551         fTemplateEngine.reset();
552         fTemplateEngine.complete(viewer, offset, unit);
553         templateResults = fTemplateEngine.getResults();
554       }
555       IPHPCompletionProposal[] identifierResults = new IPHPCompletionProposal[0];
556       if ((!useClassMembers) && identifiers != null) {
557         IdentifierEngine identifierEngine;
558         JavaContextType contextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
559             "php"); //$NON-NLS-1$
560         if (contextType != null) {
561           identifierEngine = new IdentifierEngine(contextType);
562           identifierEngine.complete(viewer, offset, identifiers, unit);
563           identifierResults = identifierEngine.getResults();
564         }
565       }
566       // declarations stored in file project.index on project level
567       IPHPCompletionProposal[] declarationResults = new IPHPCompletionProposal[0];
568       if (project != null) {
569         DeclarationEngine declarationEngine;
570         JavaContextType contextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
571             "php"); //$NON-NLS-1$
572         if (contextType != null) {
573           IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault().getIndexManager(project);
574           SortedMap sortedMap = indexManager.getIdentifierMap();
575           declarationEngine = new DeclarationEngine(project, contextType, lastSignificantToken, file);
576           declarationEngine.complete(viewer, offset, sortedMap, unit);
577           declarationResults = declarationEngine.getResults();
578         }
579       }
580       // built in function names from phpsyntax.xml
581       ArrayList syntaxbuffer = PHPSyntaxRdr.getSyntaxData();
582       IPHPCompletionProposal[] builtinResults = new IPHPCompletionProposal[0];
583       if ((!useClassMembers) && syntaxbuffer != null) {
584         BuiltInEngine builtinEngine;
585         String proposal;
586         JavaContextType contextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
587             "php"); //$NON-NLS-1$
588         if (contextType != null) {
589           builtinEngine = new BuiltInEngine(contextType);
590           builtinEngine.complete(viewer, offset, syntaxbuffer, unit);
591           builtinResults = builtinEngine.getResults();
592         }
593       }
594       IPHPCompletionProposal[] sqlResults = new IPHPCompletionProposal[0];
595       if (project != null) {
596         sqlResults = getSQLProposals(viewer, project, context, prefix, sqlTable, sqlResults);
597       }
598       // concatenate the result arrays
599       IPHPCompletionProposal[] total;
600       total = new IPHPCompletionProposal[localVariableResults.length + templateResults.length + identifierResults.length
601           + builtinResults.length + declarationResults.length + sqlResults.length];
602       System.arraycopy(templateResults, 0, total, 0, templateResults.length);
603       System.arraycopy(identifierResults, 0, total, templateResults.length, identifierResults.length);
604       System.arraycopy(builtinResults, 0, total, templateResults.length + identifierResults.length, builtinResults.length);
605       System.arraycopy(declarationResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length,
606           declarationResults.length);
607       System.arraycopy(sqlResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length
608           + declarationResults.length, sqlResults.length);
609       System.arraycopy(localVariableResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length
610           + declarationResults.length + sqlResults.length, localVariableResults.length);
611       results = total;
612       fNumberOfComputedResults = (results == null ? 0 : results.length);
613       /*
614        * Order here and not in result collector to make sure that the order applies to all proposals and not just those of the
615        * compilation unit.
616        */
617       return order(results);
618     }
619     return new IPHPCompletionProposal[0];
620   }
621
622   /**
623    * @param viewer
624    * @param project
625    * @param context
626    * @param prefix
627    * @return
628    */
629   private HashSet getLocalVariableProposals(ITextViewer viewer, IProject project, JavaContext context, String prefix) {
630     HashSet localVariables = new HashSet();
631     try {
632       IMethod method = (IMethod) context.findEnclosingElement(IJavaElement.METHOD);
633       int start = context.getStart();
634       int end = context.getEnd();
635       IRegion region = new Region(start, end - start);
636       char[] varName;
637       boolean matchesVarName;
638       if (method != null) {
639         ISourceRange range = method.getSourceRange();
640         char[] source = method.getSource().toCharArray();
641         Scanner scanner = new Scanner();
642         scanner.setSource(source);
643         scanner.phpMode = true;
644         int token = Scanner.TokenNameWHITESPACE;
645         while ((token = scanner.getNextToken()) != Scanner.TokenNameEOF) {
646           if (token == Scanner.TokenNameVariable) {
647             varName = scanner.getCurrentTokenSource();
648             if (varName.length >= prefix.length()) {
649               matchesVarName = true;
650               for (int i = 0; i < prefix.length(); i++) {
651                 if (prefix.charAt(i) != varName[i]) {
652                   matchesVarName = false;
653                   break;
654                 }
655               }
656               if (matchesVarName) {
657                 LocalVariableProposal prop = new LocalVariableProposal(new String(varName), region, viewer);
658                 if (varName.length == prefix.length()) {
659                   prop.setRelevance(98);
660                 }
661                 localVariables.add(prop);
662               }
663             }
664           }
665         }
666       }
667     } catch (Throwable e) {
668       // ignore - Syntax exceptions could occur, if there are syntax errors !
669     }
670     return localVariables;
671   }
672
673   /**
674    * @param viewer
675    * @param project
676    * @param context
677    * @param prefix
678    * @param sqlTable
679    * @param sqlResults
680    * @return
681    */
682   private IPHPCompletionProposal[] getSQLProposals(ITextViewer viewer, IProject project, JavaContext context, String prefix,
683       TableName sqlTable, IPHPCompletionProposal[] sqlResults) {
684     // Get The Database bookmark from the Quantum SQL plugin:
685     BookmarkCollection sqlBookMarks = BookmarkCollection.getInstance();
686     if (sqlBookMarks != null) {
687       String bookmarkString = Util.getMiscProjectsPreferenceValue(project, IPreferenceConstants.PHP_BOOKMARK_DEFAULT);
688       if (bookmarkString != null && !bookmarkString.equals("")) {
689         Bookmark bookmark = sqlBookMarks.find(bookmarkString);
690         ArrayList sqlList = new ArrayList();
691         if (bookmark != null && !bookmark.isConnected()) {
692           new ConnectionUtil().connect(bookmark, null);
693         }
694         if (bookmark != null && bookmark.isConnected()) {
695           try {
696             Connection connection = bookmark.getConnection();
697             DatabaseMetaData metaData = connection.getMetaData();
698
699             if (metaData != null) {
700               int start = context.getStart();
701               int end = context.getEnd();
702               String foundSQLTableName = sqlTable.getTableName();
703               String tableName;
704               String columnName;
705               String prefixWithoutDollar = prefix;
706               boolean isDollarPrefix = false;
707               if (prefix.length() > 0 && prefix.charAt(0) == '$') {
708                 prefixWithoutDollar = prefix.substring(1);
709                 isDollarPrefix = true;
710               }
711               IRegion region = new Region(start, end - start);
712               ResultSet set;
713               if (!isDollarPrefix) {
714                 set = metaData.getTables(null, null, prefixWithoutDollar + "%", null);
715                 while (set.next()) {
716                   //                  String tempSchema = set.getString("TABLE_SCHEM");
717                   //                  tempSchema = (tempSchema == null) ? "" :
718                   // tempSchema.trim();
719                   tableName = set.getString("TABLE_NAME");
720                   tableName = (tableName == null) ? "" : tableName.trim();
721                   if (tableName != null && tableName.length() > 0) {
722                     sqlList.add(new SQLProposal(tableName, context, region, viewer, PHPUiImages.get(PHPUiImages.IMG_TABLE)));
723                   }
724                 }
725                 set.close();
726               }
727               set = metaData.getColumns(null, null, "%", prefixWithoutDollar + "%");
728               SQLProposal sqlProposal;
729               while (set.next()) {
730                 columnName = set.getString("COLUMN_NAME");
731                 columnName = (columnName == null) ? "" : columnName.trim();
732                 tableName = set.getString("TABLE_NAME");
733                 tableName = (tableName == null) ? "" : tableName.trim();
734                 if (tableName != null && tableName.length() > 0 && columnName != null && columnName.length() > 0) {
735                   if (isDollarPrefix) {
736                     sqlProposal = new SQLProposal(tableName, "$" + columnName, context, region, viewer, PHPUiImages
737                         .get(PHPUiImages.IMG_COLUMN));
738                   } else {
739                     sqlProposal = new SQLProposal(tableName, columnName, context, region, viewer, PHPUiImages
740                         .get(PHPUiImages.IMG_COLUMN));
741                   }
742                   if (tableName.equals(foundSQLTableName)) {
743                     sqlProposal.setRelevance(90);
744                   } else if (tableName.indexOf(foundSQLTableName) >= 0) {
745                     sqlProposal.setRelevance(75);
746                   }
747                   sqlList.add(sqlProposal);
748                 }
749               }
750               set.close();
751               sqlResults = new IPHPCompletionProposal[sqlList.size()];
752               for (int i = 0; i < sqlList.size(); i++) {
753                 sqlResults[i] = (SQLProposal) sqlList.get(i);
754               }
755             }
756           } catch (NotConnectedException e) {
757             // ignore this - not mission critical
758           } catch (SQLException e) {
759             e.printStackTrace();
760           }
761         }
762       }
763     }
764     return sqlResults;
765   }
766
767   private int guessContextInformationPosition(ITextViewer viewer, int offset) {
768     int contextPosition = offset;
769     IDocument document = viewer.getDocument();
770     //    try {
771     //
772     //      PHPCodeReader reader= new PHPCodeReader();
773     //      reader.configureBackwardReader(document, offset, true, true);
774     //  
775     //      int nestingLevel= 0;
776     //
777     //      int curr= reader.read();
778     //      while (curr != PHPCodeReader.EOF) {
779     //
780     //        if (')' == (char) curr)
781     //          ++ nestingLevel;
782     //
783     //        else if ('(' == (char) curr) {
784     //          -- nestingLevel;
785     //        
786     //          if (nestingLevel < 0) {
787     //            int start= reader.getOffset();
788     //            if (looksLikeMethod(reader))
789     //              return start + 1;
790     //          }
791     //        }
792     //
793     //        curr= reader.read();
794     //      }
795     //    } catch (IOException e) {
796     //    }
797     return contextPosition;
798   }
799
800   /**
801    * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
802    */
803   public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
804     int contextInformationPosition = guessContextInformationPosition(viewer, offset);
805     List result = addContextInformations(viewer, contextInformationPosition);
806     return (IContextInformation[]) result.toArray(new IContextInformation[result.size()]);
807   }
808
809   private List addContextInformations(ITextViewer viewer, int offset) {
810     ICompletionProposal[] proposals = internalComputeCompletionProposals(viewer, offset, -1);
811     List result = new ArrayList();
812     for (int i = 0; i < proposals.length; i++) {
813       IContextInformation contextInformation = proposals[i].getContextInformation();
814       if (contextInformation != null) {
815         ContextInformationWrapper wrapper = new ContextInformationWrapper(contextInformation);
816         wrapper.setContextInformationPosition(offset);
817         result.add(wrapper);
818       }
819     }
820     return result;
821   }
822
823   /**
824    * Order the given proposals.
825    */
826   private ICompletionProposal[] order(ICompletionProposal[] proposals) {
827     Arrays.sort(proposals, fComparator);
828     return proposals;
829   }
830
831   /*
832    * (non-Javadoc) Method declared on IContentAssistProcessor
833    */
834   public char[] getCompletionProposalAutoActivationCharacters() {
835     return fProposalAutoActivationSet;
836     //    return null; // new char[] { '$' };
837   }
838
839   /*
840    * (non-Javadoc) Method declared on IContentAssistProcessor
841    */
842   public char[] getContextInformationAutoActivationCharacters() {
843     return new char[] {};
844   }
845
846   /*
847    * (non-Javadoc) Method declared on IContentAssistProcessor
848    */
849   public IContextInformationValidator getContextInformationValidator() {
850     return fValidator;
851   }
852
853   /*
854    * (non-Javadoc) Method declared on IContentAssistProcessor
855    */
856   public String getErrorMessage() {
857     return null;
858   }
859 }