/********************************************************************** Copyright (c) 2000, 2002 IBM Corp. and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html Contributors: IBM Corporation - Initial implementation Klaus Hartlage - www.eclipseproject.de **********************************************************************/ package net.sourceforge.phpeclipse.phpeditor.php; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.SortedMap; import net.sourceforge.phpdt.core.ToolFactory; import net.sourceforge.phpdt.core.compiler.ITerminalSymbols; import net.sourceforge.phpdt.core.compiler.InvalidInputException; import net.sourceforge.phpdt.internal.compiler.parser.Scanner; import net.sourceforge.phpdt.internal.corext.template.ContextType; import net.sourceforge.phpdt.internal.corext.template.ContextTypeRegistry; import net.sourceforge.phpdt.internal.corext.template.php.CompilationUnitContextType; import net.sourceforge.phpdt.internal.corext.template.php.PHPUnitContext; import net.sourceforge.phpdt.internal.ui.PHPUiImages; import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal; import net.sourceforge.phpdt.internal.ui.text.java.PHPCompletionProposalComparator; import net.sourceforge.phpdt.internal.ui.text.template.BuiltInEngine; import net.sourceforge.phpdt.internal.ui.text.template.DeclarationEngine; import net.sourceforge.phpdt.internal.ui.text.template.IdentifierEngine; import net.sourceforge.phpdt.internal.ui.text.template.IdentifierProposal; import net.sourceforge.phpdt.internal.ui.text.template.TemplateEngine; import net.sourceforge.phpeclipse.IPreferenceConstants; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import net.sourceforge.phpeclipse.builder.IdentifierIndexManager; import net.sourceforge.phpeclipse.phpeditor.PHPEditor; import net.sourceforge.phpeclipse.phpeditor.PHPSyntaxRdr; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationExtension; import org.eclipse.jface.text.contentassist.IContextInformationPresenter; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.swt.graphics.Image; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import net.sourceforge.phpeclipse.overlaypages.Util; import com.quantum.model.Bookmark; import com.quantum.model.BookmarkCollection; import com.quantum.model.NotConnectedException; import com.quantum.util.connection.ConnectionUtil; /** * Example PHP completion processor. */ public class PHPCompletionProcessor implements IContentAssistProcessor { /** * Simple content assist tip closer. The tip is valid in a range of 5 * characters around its popup location. */ protected static class Validator implements IContextInformationValidator, IContextInformationPresenter { protected int fInstallOffset; /* * @see IContextInformationValidator#isContextInformationValid(int) */ public boolean isContextInformationValid(int offset) { return Math.abs(fInstallOffset - offset) < 5; } /* * @see IContextInformationValidator#install(IContextInformation, * ITextViewer, int) */ public void install(IContextInformation info, ITextViewer viewer, int offset) { fInstallOffset = offset; } /* * @see org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int, * TextPresentation) */ public boolean updatePresentation(int documentPosition, TextPresentation presentation) { return false; } }; private static class ContextInformationWrapper implements IContextInformation, IContextInformationExtension { private final IContextInformation fContextInformation; private int fPosition; public ContextInformationWrapper(IContextInformation contextInformation) { fContextInformation = contextInformation; } /* * @see IContextInformation#getContextDisplayString() */ public String getContextDisplayString() { return fContextInformation.getContextDisplayString(); } /* * @see IContextInformation#getImage() */ public Image getImage() { return fContextInformation.getImage(); } /* * @see IContextInformation#getInformationDisplayString() */ public String getInformationDisplayString() { return fContextInformation.getInformationDisplayString(); } /* * @see IContextInformationExtension#getContextInformationPosition() */ public int getContextInformationPosition() { return fPosition; } public void setContextInformationPosition(int position) { fPosition = position; } }; private char[] fProposalAutoActivationSet; protected IContextInformationValidator fValidator = new Validator(); private TemplateEngine fTemplateEngine; private PHPCompletionProposalComparator fComparator; private int fNumberOfComputedResults = 0; public PHPCompletionProcessor() { ContextType contextType = ContextTypeRegistry.getInstance().getContextType( "php"); //$NON-NLS-1$ if (contextType != null) fTemplateEngine = new TemplateEngine(contextType); fComparator = new PHPCompletionProposalComparator(); } /** * Tells this processor to order the proposals alphabetically. * * @param order * true if proposals should be ordered. */ public void orderProposalsAlphabetically(boolean order) { fComparator.setOrderAlphabetically(order); } /** * Sets this processor's set of characters triggering the activation of the * completion proposal computation. * * @param activationSet * the activation set */ public void setCompletionProposalAutoActivationCharacters(char[] activationSet) { fProposalAutoActivationSet = activationSet; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) { int contextInformationPosition = guessContextInformationPosition(viewer, documentOffset); return internalComputeCompletionProposals(viewer, documentOffset, contextInformationPosition); } private int getLastToken(ITextViewer viewer, int completionPosition, PHPUnitContext context) { IDocument document = viewer.getDocument(); int start = context.getStart(); int end = context.getEnd(); String startText; int lastSignificantToken = ITerminalSymbols.TokenNameEOF; try { // begin search 2 lines behind of this int j = start; if (j != 0) { char ch; while (j-- > 0) { ch = document.getChar(j); if (ch == '\n') { break; } } while (j-- > 0) { ch = document.getChar(j); if (ch == '\n') { break; } } } if (j != start) { // scan the line for the dereferencing operator '->' startText = document.get(j, start - j); if (Scanner.DEBUG) { System.out.println(startText); } int token = ITerminalSymbols.TokenNameEOF; token = getLastSQLToken(startText); Scanner scanner = ToolFactory.createScanner(false, false, false); scanner.setSource(startText.toCharArray()); scanner.setPHPMode(true); int beforeLastToken = ITerminalSymbols.TokenNameEOF; int lastToken = ITerminalSymbols.TokenNameEOF; try { token = scanner.getNextToken(); lastToken = token; while (token != ITerminalSymbols.TokenNameERROR && token != ITerminalSymbols.TokenNameEOF) { beforeLastToken = lastToken; lastToken = token; // System.out.println(scanner.toStringAction(lastToken)); token = scanner.getNextToken(); } } catch (InvalidInputException e1) { } switch (lastToken) { case ITerminalSymbols.TokenNameMINUS_GREATER : // dereferencing operator '->' found lastSignificantToken = ITerminalSymbols.TokenNameMINUS_GREATER; if (beforeLastToken == ITerminalSymbols.TokenNameVariable) { lastSignificantToken = ITerminalSymbols.TokenNameVariable; } break; case ITerminalSymbols.TokenNamenew : lastSignificantToken = ITerminalSymbols.TokenNamenew; break; } } } catch (BadLocationException e) { } return lastSignificantToken; } /** * Detect the last significant SQL token in the text before the completion * * @param startText */ private int getLastSQLToken(String startText) { int token; // scan for sql identifiers char ch = ' '; int currentSQLPosition = startText.length(); int identEnd = -1; String ident = null; boolean whiteSpace = true; try { while (true) { ch = startText.charAt(--currentSQLPosition); if (ch >= 'A' && ch <= 'Z') { if (identEnd < 0) { identEnd = currentSQLPosition + 1; } } else if (ch >= 'a' && ch <= 'z') { if (identEnd < 0) { identEnd = currentSQLPosition + 1; } } else if (identEnd >= 0) { ident = startText.substring(currentSQLPosition + 1, identEnd); // select -- from -- where -- // update -- set -- where -- // insert into -- ( -- ) values ( -- ) if (ident.length() >= 3 && ident.length() <= 6) { ident = ident.toLowerCase(); switch (ident.length()) { case 3 : if (ident.equals("set")) { // System.out.println("set"); token = ITerminalSymbols.TokenNameSQLset; return token; } break; case 4 : if (ident.equals("from")) { // System.out.println("from"); token = ITerminalSymbols.TokenNameSQLfrom; return token; } else if (ident.equals("into")) { // System.out.println("into"); token = ITerminalSymbols.TokenNameSQLinto; return token; } break; case 5 : if (ident.equals("where")) { // System.out.println("where"); token = ITerminalSymbols.TokenNameSQLwhere; return token; } break; case 6 : if (ident.equals("select")) { // System.out.println("select"); token = ITerminalSymbols.TokenNameSQLselect; return token; } else if (ident.equals("insert")) { // System.out.println("insert"); token = ITerminalSymbols.TokenNameSQLinsert; return token; } else if (ident.equals("update")) { // System.out.println("update"); token = ITerminalSymbols.TokenNameSQLupdate; return token; } else if (ident.equals("values")) { // System.out.println("values"); token = ITerminalSymbols.TokenNameSQLvalues; return token; } break; } } whiteSpace = false; identEnd = -1; } else if (Character.isWhitespace(ch)) { } else { whiteSpace = false; } } } catch (IndexOutOfBoundsException e) { } return ITerminalSymbols.TokenNameEOF; } private ICompletionProposal[] internalComputeCompletionProposals( ITextViewer viewer, int offset, int contextOffset) { IDocument document = viewer.getDocument(); Object[] identifiers = null; IFile file = null; IProject project = null; if (offset > 0) { PHPEditor editor = null; // JavaOutlinePage outlinePage = null; IEditorPart targetEditor = PHPeclipsePlugin.getActiveWorkbenchWindow() .getActivePage().getActiveEditor(); if (targetEditor != null && (targetEditor instanceof PHPEditor)) { editor = (PHPEditor) targetEditor; file = ((IFileEditorInput) editor.getEditorInput()).getFile(); project = file.getProject(); // outlinePage = editor.getfOutlinePage(); // TODO: get the identifiers from the new model // if (outlinePage instanceof PHPContentOutlinePage) { // identifiers = ((PHPContentOutlinePage) outlinePage).getVariables(); // } } } ContextType phpContextType = ContextTypeRegistry.getInstance() .getContextType("php"); //$NON-NLS-1$ ((CompilationUnitContextType) phpContextType).setContextParameters( document, offset, 0); PHPUnitContext context = (PHPUnitContext) phpContextType.createContext(); String prefix = context.getKey(); int lastSignificantToken = getLastToken(viewer, offset, context); boolean useClassMembers = (lastSignificantToken == ITerminalSymbols.TokenNameMINUS_GREATER) || (lastSignificantToken == ITerminalSymbols.TokenNameVariable) || (lastSignificantToken == ITerminalSymbols.TokenNamenew); boolean emptyPrefix = prefix == null || prefix.equals(""); if (fTemplateEngine != null) { IPHPCompletionProposal[] templateResults = new IPHPCompletionProposal[0]; ICompletionProposal[] results; if (!emptyPrefix) { fTemplateEngine.reset(); fTemplateEngine.complete(viewer, offset); //, unit); templateResults = fTemplateEngine.getResults(); } IPHPCompletionProposal[] identifierResults = new IPHPCompletionProposal[0]; if ((!useClassMembers) && identifiers != null) { IdentifierEngine identifierEngine; ContextType contextType = ContextTypeRegistry.getInstance() .getContextType("php"); //$NON-NLS-1$ if (contextType != null) { identifierEngine = new IdentifierEngine(contextType); identifierEngine.complete(viewer, offset, identifiers); identifierResults = identifierEngine.getResults(); } } // declarations stored in file project.index on project level IPHPCompletionProposal[] declarationResults = new IPHPCompletionProposal[0]; if (project != null) { DeclarationEngine declarationEngine; ContextType contextType = ContextTypeRegistry.getInstance() .getContextType("php"); //$NON-NLS-1$ if (contextType != null) { IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault() .getIndexManager(project); SortedMap sortedMap = indexManager.getIdentifierMap(); declarationEngine = new DeclarationEngine(contextType, lastSignificantToken, file); declarationEngine.complete(viewer, offset, sortedMap); declarationResults = declarationEngine.getResults(); } } // built in function names from phpsyntax.xml ArrayList syntaxbuffer = PHPSyntaxRdr.getSyntaxData(); IPHPCompletionProposal[] builtinResults = new IPHPCompletionProposal[0]; if ((!useClassMembers) && syntaxbuffer != null) { BuiltInEngine builtinEngine; String proposal; ContextType contextType = ContextTypeRegistry.getInstance() .getContextType("php"); //$NON-NLS-1$ if (contextType != null) { builtinEngine = new BuiltInEngine(contextType); builtinEngine.complete(viewer, offset, syntaxbuffer); builtinResults = builtinEngine.getResults(); } } IPHPCompletionProposal[] sqlResults = new IPHPCompletionProposal[0]; if (project != null) { // Get The Database bookmark from the Quantum SQL plugin: BookmarkCollection sqlBookMarks = BookmarkCollection.getInstance(); if (sqlBookMarks != null) { String bookmarkString = Util.getMiscProjectsPreferenceValue(project, IPreferenceConstants.PHP_BOOKMARK_DEFAULT); if (bookmarkString != null && !bookmarkString.equals("")) { Bookmark bookmark = sqlBookMarks.find(bookmarkString); ArrayList list = new ArrayList(); if (bookmark != null && !bookmark.isConnected()) { new ConnectionUtil().connect(bookmark, null); } if (bookmark != null && bookmark.isConnected()) { try { Connection connection = bookmark.getConnection(); DatabaseMetaData metaData = connection.getMetaData(); if (metaData != null) { int start = context.getStart(); int end = context.getEnd(); IRegion region = new Region(start, end - start); ResultSet set = metaData.getTables(null, null, prefix + "%", null); while (set.next()) { // String tempSchema = set.getString("TABLE_SCHEM"); // tempSchema = (tempSchema == null) ? "" : // tempSchema.trim(); String tableName = set.getString("TABLE_NAME"); tableName = (tableName == null) ? "" : tableName.trim(); if (tableName != null && tableName.length() > 0) { list.add(tableName); } } set.close(); set = metaData.getColumns(null, null, "%", prefix + "%"); while (set.next()) { String tableName = set.getString("COLUMN_NAME"); tableName = (tableName == null) ? "" : tableName.trim(); if (tableName != null && tableName.length() > 0) { list.add(tableName); } tableName = set.getString("TABLE_NAME"); tableName = (tableName == null) ? "" : tableName.trim(); if (tableName != null && tableName.length() > 0) { list.add(tableName); } } set.close(); sqlResults = new IPHPCompletionProposal[list.size()]; for (int i = 0; i < list.size(); i++) { sqlResults[i] = new IdentifierProposal( (String) list.get(i), context, region, viewer, PHPUiImages.get(PHPUiImages.IMG_FUN), PHPUiImages .get(PHPUiImages.IMG_VAR)); } } } catch (NotConnectedException e) { // ignore this - not mission critical } catch (SQLException e) { e.printStackTrace(); } } } } } // concatenate the result arrays IPHPCompletionProposal[] total; total = new IPHPCompletionProposal[templateResults.length + identifierResults.length + builtinResults.length + declarationResults.length + sqlResults.length]; System.arraycopy(templateResults, 0, total, 0, templateResults.length); System.arraycopy(identifierResults, 0, total, templateResults.length, identifierResults.length); System.arraycopy(builtinResults, 0, total, templateResults.length + identifierResults.length, builtinResults.length); System.arraycopy(declarationResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length, declarationResults.length); System.arraycopy(sqlResults, 0, total, templateResults.length + identifierResults.length + builtinResults.length + declarationResults.length, sqlResults.length); results = total; fNumberOfComputedResults = (results == null ? 0 : results.length); /* * Order here and not in result collector to make sure that the order * applies to all proposals and not just those of the compilation unit. */ return order(results); } return new IPHPCompletionProposal[0]; } private int guessContextInformationPosition(ITextViewer viewer, int offset) { int contextPosition = offset; IDocument document = viewer.getDocument(); // try { // // PHPCodeReader reader= new PHPCodeReader(); // reader.configureBackwardReader(document, offset, true, true); // // int nestingLevel= 0; // // int curr= reader.read(); // while (curr != PHPCodeReader.EOF) { // // if (')' == (char) curr) // ++ nestingLevel; // // else if ('(' == (char) curr) { // -- nestingLevel; // // if (nestingLevel < 0) { // int start= reader.getOffset(); // if (looksLikeMethod(reader)) // return start + 1; // } // } // // curr= reader.read(); // } // } catch (IOException e) { // } return contextPosition; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ // public IContextInformation[] computeContextInformation(ITextViewer viewer, // int documentOffset) { // IContextInformation[] result = new IContextInformation[5]; // for (int i = 0; i < result.length; i++) // result[i] = new // ContextInformation(MessageFormat.format(PHPEditorMessages.getString("CompletionProcessor.ContextInfo.display.pattern"), // new Object[] { new Integer(i), new Integer(documentOffset)}), // //$NON-NLS-1$ // MessageFormat.format(PHPEditorMessages.getString("CompletionProcessor.ContextInfo.value.pattern"), // new Object[] { new Integer(i), new Integer(documentOffset - 5), new // Integer(documentOffset + 5)})); //$NON-NLS-1$ // return result; // } /** * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int) */ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { int contextInformationPosition = guessContextInformationPosition(viewer, offset); List result = addContextInformations(viewer, contextInformationPosition); return (IContextInformation[]) result .toArray(new IContextInformation[result.size()]); } private List addContextInformations(ITextViewer viewer, int offset) { ICompletionProposal[] proposals = internalComputeCompletionProposals( viewer, offset, -1); List result = new ArrayList(); for (int i = 0; i < proposals.length; i++) { IContextInformation contextInformation = proposals[i] .getContextInformation(); if (contextInformation != null) { ContextInformationWrapper wrapper = new ContextInformationWrapper( contextInformation); wrapper.setContextInformationPosition(offset); result.add(wrapper); } } return result; } /** * Order the given proposals. */ private ICompletionProposal[] order(ICompletionProposal[] proposals) { Arrays.sort(proposals, fComparator); return proposals; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ public char[] getCompletionProposalAutoActivationCharacters() { return fProposalAutoActivationSet; // return null; // new char[] { '$' }; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ public char[] getContextInformationAutoActivationCharacters() { return new char[]{}; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ public IContextInformationValidator getContextInformationValidator() { return fValidator; } /* * (non-Javadoc) Method declared on IContentAssistProcessor */ public String getErrorMessage() { return null; } }