/*******************************************************************************
* Copyright © 2007, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.ide.ui.internal.contentassist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.edt.compiler.core.ast.Node;
import org.eclipse.edt.compiler.core.ast.NodeTypes;
import org.eclipse.edt.compiler.core.ast.Part;
import org.eclipse.edt.ide.core.internal.errors.EGLPartialParser;
import org.eclipse.edt.ide.core.internal.errors.ParseNode;
import org.eclipse.edt.ide.core.internal.errors.ParseStack;
import org.eclipse.edt.ide.core.internal.errors.TerminalNode;
import org.eclipse.edt.ide.core.internal.errors.TokenStream;
import org.eclipse.edt.ide.core.internal.model.document.EGLDocument;
import org.eclipse.edt.ide.ui.editor.EGLContentAssistInvocationContext;
import org.eclipse.edt.ide.ui.editor.IEGLCompletionProposalComputer;
import org.eclipse.edt.ide.ui.internal.templates.TemplateEngine;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
public abstract class EGLAbstractTemplateCompletionProposalComputer implements IEGLCompletionProposalComputer {
/**
* The engine for the current session, if any
*/
private TemplateEngine fEngine;
private EGLPartialParser parser = new EGLPartialParser();
/*
* @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
*/
public List computeCompletionProposals(EGLContentAssistInvocationContext context, IProgressMonitor monitor) {
if (!(context instanceof EGLContentAssistInvocationContext))
return Collections.EMPTY_LIST;
EGLContentAssistInvocationContext eglContext= (EGLContentAssistInvocationContext) context;
ArrayList result = new ArrayList();
// Set up the token stream
TokenStream tokenStream = new TokenStream(getPrefix(eglContext.getViewer(), eglContext.getInvocationOffset()));
tokenStream.skipPrefix();
// Compute the prefix
ArrayList prefixNodes = new ArrayList();
// First recreate the parse stack
// The parse stack is created up to the shift of the last nonextensible terminal
// (hence the right edge may not be reduced)
ParseStack parseStack = parser.parse(tokenStream);
// Attach what the parser did not parse into the prefix
prefixNodes.addAll(tokenStream.getPrefixNodes());
fEngine= computeCompletionEngine(eglContext);
if (fEngine != null) {
fEngine.reset();
fEngine.complete(tokenStream, eglContext.getViewer(), eglContext.getInvocationOffset(), parseStack, getPrefix(prefixNodes));
ICompletionProposal[] templateResults = fEngine.getResults();
for (int k = 0; k < templateResults.length; k++) {
result.add(templateResults[k]);
}
}
return result;
}
/**
* Compute the engine used to retrieve completion proposals in the given context
*
* @param context the context where proposals will be made
* @return the engine or <code>null</code> if no engine available in the context
*/
protected abstract TemplateEngine computeCompletionEngine(EGLContentAssistInvocationContext context);
/*
* @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
*/
public List computeContextInformation(EGLContentAssistInvocationContext context, IProgressMonitor monitor) {
return Collections.EMPTY_LIST;
}
/*
* @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#getErrorMessage()
*/
public String getErrorMessage() {
return null;
}
/*
* @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionStarted()
*/
public void sessionStarted() {
}
/* (non-Javadoc)
* @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionEnded()
*/
public void sessionEnded() {
if (fEngine != null) {
fEngine.reset();
fEngine= null;
}
}
protected String getPrefix(ITextViewer viewer, int documentOffset) {
// For performance reasons, we don't want the prefix string to start
// from the beginning of the file all the way the location were CA is requested.
// Instead, we start from the part before the current part where CA is requested.
// (If we aren't in a part then we do start from the beginning)
// Find the part containing the offset
EGLDocument document = (EGLDocument) viewer.getDocument();
Node node = document.getNewModelNodeAtOffset(documentOffset);
while(node != null && !(node instanceof Part)) {
node = node.getParent();
}
int previousPartOffset = 0;
if(node != null) {
int currentPartOffset = node.getOffset();
node = document.getNewModelNodeAtOffset(currentPartOffset == 0 ? 0 : currentPartOffset - 1);
while(node != null && !(node instanceof Part)) {
node = node.getParent();
}
if(node != null) {
previousPartOffset = node.getOffset();
}
}
String partialSource = ""; //$NON-NLS-1$
try {
partialSource = document.get(previousPartOffset, documentOffset - previousPartOffset);
} catch (BadLocationException e) {
e.printStackTrace();
}
return partialSource;
}
protected String getPrefix(List prefixNodes) {
// Strip the initial whitespaces
List strippedPrefix = stripInitialWhitespaces(prefixNodes);
// We have to skip the initial whitespaces prefix nodes
StringBuffer buffer = new StringBuffer();
for (Iterator iter = strippedPrefix.iterator(); iter.hasNext();) {
ParseNode node = (ParseNode) iter.next();
buffer.append(getText(node));
}
return buffer.length() == 0 ? "" : buffer.toString(); //$NON-NLS-1$
}
private List stripInitialWhitespaces(List prefixNodes) {
for (int i = 0; i < prefixNodes.size(); i++) {
ParseNode node = (ParseNode) prefixNodes.get(i);
if (!node.isWhiteSpace()) {
return prefixNodes.subList(i, prefixNodes.size());
}
}
// Everything is whitespace, so return an empty list
return new ArrayList();
}
private String getText(ParseNode node) {
if(node.isTerminal()) {
switch(((TerminalNode) node).terminalType) {
case NodeTypes.ERRORSQLSTMTLIT:
return "#sql{}";
}
}
return node.getText();
}
}