Organized imports.
[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 (ch >= 'A' && ch <= 'Z') {
343           if (identEnd < 0) {
344             identEnd = currentSQLPosition + 1;
345           }
346         } else if (ch >= 'a' && ch <= 'z') {
347           if (identEnd < 0) {
348             identEnd = currentSQLPosition + 1;
349           }
350         } else if (identEnd >= 0) {
351           ident = startText.substring(currentSQLPosition + 1, identEnd);
352           // select -- from -- where --
353           // update -- set -- where --
354           // insert into -- ( -- ) values ( -- )
355           if (ident.length() >= 4 && ident.length() <= 6) {
356             ident = ident.toLowerCase();
357             switch (ident.length()) {
358             //              case 3 :
359             //                if (ident.equals("set")) {
360             //                  // System.out.println("set");
361             //                  token = ITerminalSymbols.TokenNameSQLset;
362             //                  return token;
363             //                }
364             //                break;
365             case 4:
366               if (ident.equals("from")) {
367                 //                  System.out.println("from");
368                 token = ITerminalSymbols.TokenNameSQLfrom;
369                 return getSQLTableName(startText, identEnd);
370               } else if (ident.equals("into")) {
371                 //                System.out.println("into");
372                 token = ITerminalSymbols.TokenNameSQLinto;
373                 return getSQLTableName(startText, identEnd);
374               }
375               break;
376             //              case 5 :
377             //                if (ident.equals("where")) {
378             //                  // System.out.println("where");
379             //                  token = ITerminalSymbols.TokenNameSQLwhere;
380             //                  return token;
381             //                }
382             //                break;
383             case 6:
384               //                if (ident.equals("select")) {
385               //                  // System.out.println("select");
386               //                  token = ITerminalSymbols.TokenNameSQLselect;
387               //                  return token;
388               //                } else if (ident.equals("insert")) {
389               //                  // System.out.println("insert");
390               //                  token = ITerminalSymbols.TokenNameSQLinsert;
391               //                  return token;
392               //                } else
393               if (ident.equals("update")) {
394                 //                  System.out.println("update");
395                 token = ITerminalSymbols.TokenNameSQLupdate;
396                 return getSQLTableName(startText, identEnd);
397               }
398               //                else if (ident.equals("values")) {
399               //                  // System.out.println("values");
400               //                  token = ITerminalSymbols.TokenNameSQLvalues;
401               //                  return token;
402               //                }
403               break;
404             }
405           }
406           whiteSpace = false;
407           identEnd = -1;
408         } else if (Character.isWhitespace(ch)) {
409         } else {
410           whiteSpace = false;
411         }
412       }
413     } catch (IndexOutOfBoundsException e) {
414     }
415     return "<!--no-table-->";
416   }
417
418   /**
419    * Detect the last significant SQL token in the text before the completion
420    * 
421    * @param startText
422    */
423   private int getLastSQLToken(String startText) {
424     int token;
425     // scan for sql identifiers
426     char ch = ' ';
427     int currentSQLPosition = startText.length();
428     int identEnd = -1;
429     String ident = null;
430     boolean whiteSpace = true;
431     try {
432       while (true) {
433         ch = startText.charAt(--currentSQLPosition);
434         if (ch >= 'A' && ch <= 'Z') {
435           if (identEnd < 0) {
436             identEnd = currentSQLPosition + 1;
437           }
438         } else if (ch >= 'a' && ch <= 'z') {
439           if (identEnd < 0) {
440             identEnd = currentSQLPosition + 1;
441           }
442         } else if (identEnd >= 0) {
443           ident = startText.substring(currentSQLPosition + 1, identEnd);
444           // select -- from -- where --
445           // update -- set -- where --
446           // insert into -- ( -- ) values ( -- )
447           if (ident.length() >= 3 && ident.length() <= 6) {
448             ident = ident.toLowerCase();
449             switch (ident.length()) {
450             case 3:
451               if (ident.equals("set")) {
452                 //                  System.out.println("set");
453                 token = ITerminalSymbols.TokenNameSQLset;
454                 return token;
455               }
456               break;
457             case 4:
458               if (ident.equals("from")) {
459                 //                  System.out.println("from");
460                 token = ITerminalSymbols.TokenNameSQLfrom;
461                 //getSQLTableName();
462                 return token;
463               } else if (ident.equals("into")) {
464                 //                System.out.println("into");
465                 token = ITerminalSymbols.TokenNameSQLinto;
466                 return token;
467               }
468               break;
469             case 5:
470               if (ident.equals("where")) {
471                 //                  System.out.println("where");
472                 token = ITerminalSymbols.TokenNameSQLwhere;
473                 return token;
474               }
475               break;
476             case 6:
477               if (ident.equals("select")) {
478                 //                  System.out.println("select");
479                 token = ITerminalSymbols.TokenNameSQLselect;
480                 return token;
481               } else if (ident.equals("insert")) {
482                 //                  System.out.println("insert");
483                 token = ITerminalSymbols.TokenNameSQLinsert;
484                 return token;
485               } else if (ident.equals("update")) {
486                 //                  System.out.println("update");
487                 token = ITerminalSymbols.TokenNameSQLupdate;
488                 return token;
489               } else if (ident.equals("values")) {
490                 //                System.out.println("values");
491                 token = ITerminalSymbols.TokenNameSQLvalues;
492                 return token;
493               }
494               break;
495             }
496           }
497           whiteSpace = false;
498           identEnd = -1;
499         } else if (Character.isWhitespace(ch)) {
500         } else {
501           whiteSpace = false;
502         }
503       }
504     } catch (IndexOutOfBoundsException e) {
505     }
506     return ITerminalSymbols.TokenNameEOF;
507   }
508
509   private ICompletionProposal[] internalComputeCompletionProposals(ITextViewer viewer, int offset, int contextOffset) {
510     ICompilationUnit unit = fManager.getWorkingCopy(fEditor.getEditorInput());
511     IDocument document = viewer.getDocument();
512     IFile file = null;
513     IProject project = null;
514     if (offset > 0) {
515       PHPEditor editor = null;
516       if (fEditor != null && (fEditor instanceof PHPEditor)) {
517         editor = (PHPEditor) fEditor;
518         file = ((IFileEditorInput) editor.getEditorInput()).getFile();
519         project = file.getProject();
520       }
521     }
522
523     Point selection = viewer.getSelectedRange();
524     // remember selected text
525     String selectedText = null;
526     if (selection.y != 0) {
527       try {
528         selectedText = document.get(selection.x, selection.y);
529       } catch (BadLocationException e) {
530       }
531     }
532
533     JavaContextType phpContextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
534         "php"); //$NON-NLS-1$
535     JavaContext context = (JavaContext) phpContextType.createContext(document, offset, selection.y, unit);
536     context.setVariable("selection", selectedText); //$NON-NLS-1$
537     String prefix = context.getKey();
538
539     HashMap methodVariables = null;
540     HashMap typeVariables = null;
541     HashMap unitVariables = null;
542     ICompilationUnit compilationUnit = (ICompilationUnit) context.findEnclosingElement(IJavaElement.COMPILATION_UNIT);
543     //    if (compilationUnit != null) {
544     //      unitVariables = ((CompilationUnit) compilationUnit).variables;
545     //    }
546     IType type = (IType) context.findEnclosingElement(IJavaElement.TYPE);
547     //    if (type != null) {
548     //      typeVariables = ((SourceType) type).variables;
549     //    }
550     IMethod method = (IMethod) context.findEnclosingElement(IJavaElement.METHOD);
551     //    if (method != null) {
552     //      methodVariables = ((SourceMethod) method).variables;
553     //    }
554
555     boolean emptyPrefix = prefix == null || prefix.equals("");
556     IPHPCompletionProposal[] localVariableResults = new IPHPCompletionProposal[0];
557
558     if (!emptyPrefix && prefix.length() >= 1 && prefix.charAt(0) == '$') { // php Variable ?
559       String lowerCasePrefix = prefix.toLowerCase();
560       HashSet localVariables = new HashSet();
561       if (compilationUnit != null) {
562         unitVariables = getUnitVariables(unitVariables, compilationUnit);
563         getVariableProposals(localVariables, viewer, project, context, unitVariables, lowerCasePrefix, 94);
564       }
565       if (method != null) {
566         methodVariables = getMethodVariables(methodVariables, method);
567         getVariableProposals(localVariables, viewer, project, context, methodVariables, lowerCasePrefix, 99);
568       }
569       if (!localVariables.isEmpty()) {
570         localVariableResults = (IPHPCompletionProposal[]) localVariables.toArray(new IPHPCompletionProposal[localVariables.size()]);
571       }
572     }
573
574     TableName sqlTable = new TableName();
575     ArrayList list = new ArrayList();
576     list.add(null);
577     int lastSignificantToken = getLastToken(list, viewer, offset, context, sqlTable);
578     boolean useClassMembers = (lastSignificantToken == ITerminalSymbols.TokenNameMINUS_GREATER)
579         || (lastSignificantToken == ITerminalSymbols.TokenNameVariable) || (lastSignificantToken == ITerminalSymbols.TokenNamenew)
580         || (lastSignificantToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION);
581
582     if (fTemplateEngine != null) {
583       IPHPCompletionProposal[] templateResults = new IPHPCompletionProposal[0];
584       ICompletionProposal[] results;
585       if (!emptyPrefix) {
586         fTemplateEngine.reset();
587         fTemplateEngine.complete(viewer, offset, unit);
588         templateResults = fTemplateEngine.getResults();
589       }
590       // TODO delete this
591       IPHPCompletionProposal[] identifierResults = new IPHPCompletionProposal[0];
592
593       // declarations stored in file project.index on project level
594       IPHPCompletionProposal[] declarationResults = new IPHPCompletionProposal[0];
595       if (project != null) {
596         DeclarationEngine declarationEngine;
597         JavaContextType contextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
598             "php"); //$NON-NLS-1$
599         if (contextType != null) {
600           IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault().getIndexManager(project);
601           SortedMap sortedMap;
602           declarationEngine = new DeclarationEngine(project, contextType, lastSignificantToken, file);
603           if (lastSignificantToken == ITerminalSymbols.TokenNamethis_PHP_COMPLETION) {
604             // complete '$this->'
605             sortedMap = indexManager.getIdentifiers(file);
606             declarationEngine.completeObject(viewer, offset, sortedMap, unit);
607           } else {
608             String typeRef = null;
609             char[] varName = (char[]) list.get(0);
610             if (varName != null) {
611               if (method != null) {
612                 methodVariables = getMethodVariables(methodVariables, method);
613                 VariableInfo info = (VariableInfo) methodVariables.get(new String(varName));
614                 if (info != null && info.typeIdentifier != null) {
615                   typeRef = new String(info.typeIdentifier);
616                 }
617
618               }
619             }
620             if (typeRef != null) {
621               // complete '$variable->' with type information
622               sortedMap = indexManager.getIdentifiers(typeRef);
623               declarationEngine.completeObject(viewer, offset, sortedMap, unit);
624             } else {
625               // complete '$variable->' without type information
626               sortedMap = indexManager.getIdentifierMap();
627               declarationEngine.complete(viewer, offset, sortedMap, unit);
628             }
629           }
630           declarationResults = declarationEngine.getResults();
631         }
632       }
633       // built in function names from phpsyntax.xml
634       ArrayList syntaxbuffer = PHPSyntaxRdr.getSyntaxData();
635       IPHPCompletionProposal[] builtinResults = new IPHPCompletionProposal[0];
636       if ((!useClassMembers) && syntaxbuffer != null) {
637         BuiltInEngine builtinEngine;
638         String proposal;
639         JavaContextType contextType = (JavaContextType) PHPeclipsePlugin.getDefault().getTemplateContextRegistry().getContextType(
640             "php"); //$NON-NLS-1$
641         if (contextType != null) {
642           builtinEngine = new BuiltInEngine(contextType);
643           builtinEngine.complete(viewer, offset, syntaxbuffer, unit);
644           builtinResults = builtinEngine.getResults();
645         }
646       }
647 //      ICompletionProposal[] sqlResults = new ICompletionProposal[0];
648 //      if (project != null) {
649 //        sqlResults = getSQLProposals(viewer, project, context, prefix, sqlTable);
650 //      }
651       // concatenate the result arrays
652       IPHPCompletionProposal[] total;
653       total = new IPHPCompletionProposal[localVariableResults.length + templateResults.length + identifierResults.length
654           + builtinResults.length + declarationResults.length];// + sqlResults.length];
655       System.arraycopy(templateResults, 0, total, 0, templateResults.length);
656       System.arraycopy(identifierResults, 0, total, templateResults.length, identifierResults.length);
657       System.arraycopy(builtinResults, 0, total, templateResults.length + identifierResults.length, builtinResults.length);
658       System.arraycopy(declarationResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length,
659           declarationResults.length);
660 //      System.arraycopy(sqlResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length
661 //          + declarationResults.length, sqlResults.length);
662 //      System.arraycopy(localVariableResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length
663 //          + declarationResults.length + sqlResults.length, localVariableResults.length);
664       System.arraycopy(localVariableResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length
665           + declarationResults.length, localVariableResults.length);
666       results = total;
667       fNumberOfComputedResults = (results == null ? 0 : results.length);
668       /*
669        * Order here and not in result collector to make sure that the order applies to all proposals and not just those of the
670        * compilation unit.
671        */
672       return order(results);
673     }
674     return new IPHPCompletionProposal[0];
675   }
676  
677   /**
678    * @param unitVariables
679    * @param unit
680    */
681   private HashMap getUnitVariables(HashMap unitVariables, ICompilationUnit unit) {
682     if (unitVariables == null) {
683       try {
684         String unitText = unit.getSource();
685         unitVariables = new HashMap();
686         
687         ProblemReporter problemReporter = new ProblemReporter(DefaultErrorHandlingPolicies.exitAfterAllProblems(),
688             new CompilerOptions(JavaCore.getOptions()), new DefaultProblemFactory());
689         UnitParser parser = new UnitParser(problemReporter);
690         parser.compilationUnit = new CompilationUnitDeclaration(problemReporter, null, unitText.length());
691         parser.parse(unitText, unitVariables);
692         
693       } catch (Exception e) {
694         // TODO Auto-generated catch block
695         e.printStackTrace();
696         PHPeclipsePlugin.log(e);
697       }
698     }
699     return unitVariables;
700   }
701   
702   /**
703    * @param methodVariables
704    * @param method
705    */
706   private HashMap getMethodVariables(HashMap methodVariables, IMethod method) {
707     if (methodVariables == null) {
708       try {
709         String methodText = method.getSource();
710         methodVariables = new HashMap();
711         ProblemReporter problemReporter = new ProblemReporter(DefaultErrorHandlingPolicies.exitAfterAllProblems(),
712             new CompilerOptions(JavaCore.getOptions()), new DefaultProblemFactory());
713         UnitParser parser = new UnitParser(problemReporter);
714         parser.compilationUnit = new CompilationUnitDeclaration(problemReporter, null, methodText.length());
715         parser.parseFunction(methodText, methodVariables);
716       } catch (Exception e) {
717         // TODO Auto-generated catch block
718         e.printStackTrace();
719         PHPeclipsePlugin.log(e);
720       }
721     }
722     return methodVariables;
723   }
724
725   /**
726    * @param viewer
727    * @param project
728    * @param context
729    * @param prefix
730    * @return
731    */
732   private void getVariableProposals(HashSet localVariables, ITextViewer viewer, IProject project, JavaContext context,
733       HashMap variables, String prefix, int relevance) {
734 //    try {
735       int start = context.getStart();
736       int end = context.getEnd();
737       IRegion region = new Region(start, end - start);
738       //      IMethod method = (IMethod) context.findEnclosingElement(IJavaElement.METHOD);
739       //      if (method != null && (method instanceof SourceMethod) && ((SourceMethod) method).variables != null) {
740       //        HashMap map = ((SourceMethod) method).variables;
741       Set set = variables.keySet();
742       Iterator iter = set.iterator();
743       String varName;
744       boolean matchesVarName;
745       while (iter.hasNext()) {
746         varName = (String) iter.next();
747         if (varName.length() >= prefix.length()) {
748           matchesVarName = true;
749           for (int i = 0; i < prefix.length(); i++) {
750             if (prefix.charAt(i) != Character.toLowerCase(varName.charAt(i))) {
751               matchesVarName = false;
752               break;
753             }
754           }
755           if (matchesVarName) {
756             LocalVariableProposal prop;
757             //            if (varName.length == prefix.length()) {
758             //              prop = new LocalVariableProposal(new String(varName), region, viewer, relevance-10);
759             //            } else {
760             prop = new LocalVariableProposal(new String(varName), region, viewer, relevance);
761             //            }
762             localVariables.add(prop);
763           }
764         }
765       }
766       //      }
767
768       //      char[] varName;
769       //      boolean matchesVarName;
770       //      if (method != null) {
771       //        ISourceRange range = method.getSourceRange();
772       //        char[] source = method.getSource().toCharArray();
773       //        Scanner scanner = new Scanner();
774       //        scanner.setSource(source);
775       //        scanner.phpMode = true;
776       //        int token = Scanner.TokenNameWHITESPACE;
777       //        while ((token = scanner.getNextToken()) != Scanner.TokenNameEOF) {
778       //          if (token == Scanner.TokenNameVariable) {
779       //            varName = scanner.getCurrentTokenSource();
780       //            if (varName.length >= prefix.length()) {
781       //              matchesVarName = true;
782       //              for (int i = 0; i < prefix.length(); i++) {
783       //                if (prefix.charAt(i) != varName[i]) {
784       //                  matchesVarName = false;
785       //                  break;
786       //                }
787       //              }
788       //              if (matchesVarName) {
789       //                LocalVariableProposal prop = new LocalVariableProposal(new String(varName), region, viewer);
790       //                if (varName.length == prefix.length()) {
791       //                  prop.setRelevance(98);
792       //                }
793       //                localVariables.add(prop);
794       //              }
795       //            }
796       //          }
797       //        }
798       //      }
799     //    } catch (Throwable e) {
800     //      // ignore - Syntax exceptions could occur, if there are syntax errors !
801     //    }
802   }
803
804   /**
805    * @param viewer
806    * @param project
807    * @param context
808    * @param prefix
809    * @param sqlTable
810    * @param sqlResults
811    * @return
812    */
813 //  private ICompletionProposal[] getSQLProposals(ITextViewer viewer, IProject project, DocumentTemplateContext context,
814 //      String prefix, TableName sqlTable) {
815 //    ICompletionProposal[] sqlResults = new ICompletionProposal[0];
816 //    // Get The Database bookmark from the Quantum SQL plugin:
817 //    //    BookmarkCollection sqlBookMarks = BookmarkCollection.getInstance();
818 //    //    if (sqlBookMarks != null) {
819 //    String bookmarkString = ProjectPrefUtil.getMiscProjectsPreferenceValue(project, IPreferenceConstants.PHP_BOOKMARK_DEFAULT);
820 //    if (bookmarkString != null && !bookmarkString.equals("")) {
821 //      String[] bookmarks = ExternalInterface.getBookmarkNames();
822 //      boolean foundBookmark = false;
823 //      for (int i = 0; i < bookmarks.length; i++) {
824 //        if (bookmarks[i].equals(bookmarkString)) {
825 //          foundBookmark = true;
826 //        }
827 //      }
828 //      if (!foundBookmark) {
829 //        return sqlResults;
830 //      }
831 //      //        Bookmark bookmark = sqlBookMarks.find(bookmarkString);
832 //      ArrayList sqlList = new ArrayList();
833 //      if (!ExternalInterface.isBookmarkConnected(bookmarkString)) {
834 //        ExternalInterface.connectBookmark(bookmarkString, null);
835 //        if (!ExternalInterface.isBookmarkConnected(bookmarkString)) {
836 //          return sqlResults;
837 //        }
838 //      }
839 //      //      if (ExternalInterface.isBookmarkConnected(bookmarkString)) {
840 //      try {
841 //         int start = context.getStart();
842 //        int end = context.getEnd();
843 //        String foundSQLTableName = sqlTable.getTableName();
844 //        String tableName;
845 //        String columnName;
846 //        String prefixWithoutDollar = prefix;
847 //        boolean isDollarPrefix = false;
848 //        if (prefix.length() > 0 && prefix.charAt(0) == '$') {
849 //          prefixWithoutDollar = prefix.substring(1);
850 //          isDollarPrefix = true;
851 //        }
852 //        IRegion region = new Region(start, end - start);
853 //        ResultSet set;
854 //        if (!isDollarPrefix) {
855 //          String[] tableNames = ExternalInterface.getMatchingTableNames(null, bookmarkString, prefixWithoutDollar, null, false);
856 //          for (int i = 0; i < tableNames.length; i++) {
857 //            sqlList.add(new SQLProposal(tableNames[i], context, region, viewer, PHPUiImages.get(PHPUiImages.IMG_TABLE)));
858 //          }
859 //        }
860 //
861 //        String[] columnNames = ExternalInterface.getMatchingColumnNames(null, bookmarkString, prefixWithoutDollar, null, false);
862 //        for (int i = 0; i < columnNames.length; i++) {
863 //          sqlList.add(new SQLProposal(columnNames[i], context, region, viewer, PHPUiImages.get(PHPUiImages.IMG_TABLE)));
864 //        }
865 //        
866 //        sqlResults = new IPHPCompletionProposal[sqlList.size()];
867 //        for (int i = 0; i < sqlList.size(); i++) {
868 //          sqlResults[i] = (SQLProposal) sqlList.get(i);
869 //        }
870 //      } catch (Exception /* NotConnectedException */ e) {
871 //        
872 //      }
873 //      //      }
874 //    }
875 //    //    }
876 //    return sqlResults;
877 //  }
878
879   private boolean looksLikeMethod(PHPCodeReader reader) throws IOException {
880     int curr = reader.read();
881     while (curr != PHPCodeReader.EOF && Character.isWhitespace((char) curr))
882       curr = reader.read();
883
884     if (curr == PHPCodeReader.EOF)
885       return false;
886
887     return Scanner.isPHPIdentifierPart((char) curr) || Scanner.isPHPIdentifierStart((char) curr);
888   }
889
890   private int guessContextInformationPosition(ITextViewer viewer, int offset) {
891     int contextPosition = offset;
892     IDocument document = viewer.getDocument();
893     try {
894
895       PHPCodeReader reader = new PHPCodeReader();
896       reader.configureBackwardReader(document, offset, true, true);
897
898       int nestingLevel = 0;
899
900       int curr = reader.read();
901       while (curr != PHPCodeReader.EOF) {
902
903         if (')' == (char) curr)
904           ++nestingLevel;
905
906         else if ('(' == (char) curr) {
907           --nestingLevel;
908
909           if (nestingLevel < 0) {
910             int start = reader.getOffset();
911             if (looksLikeMethod(reader))
912               return start + 1;
913           }
914         }
915
916         curr = reader.read();
917       }
918     } catch (IOException e) {
919     }
920     return contextPosition;
921   }
922
923   /**
924    * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
925    */
926   public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
927     int contextInformationPosition = guessContextInformationPosition(viewer, offset);
928     List result = addContextInformations(viewer, contextInformationPosition);
929     return (IContextInformation[]) result.toArray(new IContextInformation[result.size()]);
930   }
931
932   private List addContextInformations(ITextViewer viewer, int offset) {
933     ICompletionProposal[] proposals = internalComputeCompletionProposals(viewer, offset, -1);
934     List result = new ArrayList();
935     for (int i = 0; i < proposals.length; i++) {
936       IContextInformation contextInformation = proposals[i].getContextInformation();
937       if (contextInformation != null) {
938         ContextInformationWrapper wrapper = new ContextInformationWrapper(contextInformation);
939         wrapper.setContextInformationPosition(offset);
940         result.add(wrapper);
941       }
942     }
943     return result;
944   }
945
946   /**
947    * Order the given proposals.
948    */
949   private ICompletionProposal[] order(ICompletionProposal[] proposals) {
950     Arrays.sort(proposals, fComparator);
951     //    int len = proposals.length;
952     //    if (len > 10) {
953     //      len = 10;
954     //    }
955     //    for (int i = 0; i < len; i++) {
956     //      System.out.println(proposals[i].getDisplayString());
957     //    }
958     return proposals;
959   }
960
961   /*
962    * (non-Javadoc) Method declared on IContentAssistProcessor
963    */
964   public char[] getCompletionProposalAutoActivationCharacters() {
965     return fProposalAutoActivationSet;
966     //    return null; // new char[] { '$' };
967   }
968
969   /*
970    * (non-Javadoc) Method declared on IContentAssistProcessor
971    */
972   public char[] getContextInformationAutoActivationCharacters() {
973     return new char[] {};
974   }
975
976   /*
977    * (non-Javadoc) Method declared on IContentAssistProcessor
978    */
979   public IContextInformationValidator getContextInformationValidator() {
980     return fValidator;
981   }
982
983   /*
984    * (non-Javadoc) Method declared on IContentAssistProcessor
985    */
986   public String getErrorMessage() {
987     return null;
988   }
989 }