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