blob: e9f2f70a086f01598483be7d89b0915dabf5a55c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.jdt.internal.debug.ui.display;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.java.JavaParameterListValidator;
import org.eclipse.jdt.internal.ui.text.template.contentassist.TemplateEngine;
import org.eclipse.jdt.internal.ui.text.template.contentassist.TemplateProposal;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jdt.ui.text.java.CompletionProposalComparator;
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
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.IContextInformationValidator;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.swt.widgets.Shell;
import com.sun.jdi.ClassNotLoadedException;
/**
* Display snippet completion processor.
*/
public class DisplayCompletionProcessor implements IContentAssistProcessor {
private CompletionProposalCollector fCollector;
private IContextInformationValidator fValidator;
private TemplateEngine fTemplateEngine;
private char[] fProposalAutoActivationSet;
private CompletionProposalComparator fComparator;
public DisplayCompletionProcessor() {
TemplateContextType contextType= JavaPlugin.getDefault().getTemplateContextRegistry().getContextType("java"); //$NON-NLS-1$
if (contextType != null) {
fTemplateEngine= new TemplateEngine(contextType);
}
fComparator= new CompletionProposalComparator();
}
/**
* @see IContentAssistProcessor#getErrorMessage()
*/
public String getErrorMessage() {
return fCollector.getErrorMessage();
}
/**
* @see IContentAssistProcessor#getContextInformationValidator()
*/
public IContextInformationValidator getContextInformationValidator() {
if (fValidator == null) {
fValidator= new JavaParameterListValidator();
}
return fValidator;
}
/**
* @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
*/
public char[] getContextInformationAutoActivationCharacters() {
return null;
}
/**
* @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
*/
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
return null;
}
/**
* @see IContentAssistProcessor#computeProposals(ITextViewer, int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) {
IAdaptable context = DebugUITools.getDebugContext();
if (context == null) {
return new ICompletionProposal[0];
}
IJavaStackFrame stackFrame= (IJavaStackFrame)context.getAdapter(IJavaStackFrame.class);
if (stackFrame == null) {
return new ICompletionProposal[0];
}
return computeCompletionProposals(stackFrame, viewer, documentOffset);
}
protected ICompletionProposal[] computeCompletionProposals(IJavaStackFrame stackFrame, ITextViewer viewer, int documentOffset) {
try {
IJavaProject project= getJavaProject(stackFrame);
if (project != null) {
IType receivingType= getReceivingType(project, stackFrame);
if (receivingType == null) {
return new ICompletionProposal[0];
}
IVariable[] variables= stackFrame.getLocalVariables();
char[][][] res= resolveLocalVariables(variables);
char[][] localVariableNames= res[0];
char[][] localVariableTypeNames= res[1];
ITextSelection selection= (ITextSelection)viewer.getSelectionProvider().getSelection();
configureResultCollector(project, selection);
int[] localModifiers= new int[localVariableNames.length];
Arrays.fill(localModifiers, 0);
int insertionPosition = computeInsertionPosition(receivingType, stackFrame);
receivingType.codeComplete(viewer.getDocument().get().toCharArray(), insertionPosition, documentOffset,
localVariableTypeNames, localVariableNames,
localModifiers, stackFrame.isStatic(), fCollector);
IJavaCompletionProposal[] results= fCollector.getJavaCompletionProposals();
if (fTemplateEngine != null) {
fTemplateEngine.reset();
fTemplateEngine.complete(viewer, documentOffset, null);
TemplateProposal[] templateResults= fTemplateEngine.getResults();
// concatenate arrays
IJavaCompletionProposal[] total= new IJavaCompletionProposal[results.length + templateResults.length];
System.arraycopy(templateResults, 0, total, 0, templateResults.length);
System.arraycopy(results, 0, total, templateResults.length, results.length);
results= total;
}
//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);
}
} catch (JavaModelException x) {
handle(viewer, x);
} catch (DebugException de) {
handle(viewer, de);
}
return null;
}
protected int computeInsertionPosition(IType receivingType, IJavaStackFrame stackFrame) throws JavaModelException, DebugException {
int insertion = -1;
if (!receivingType.isBinary() && receivingType.getDeclaringType() == null) {
ICompilationUnit stackCU= getCompilationUnit(stackFrame);
ICompilationUnit typeCU= receivingType.getCompilationUnit();
if (typeCU != null && typeCU.equals(stackCU)) {
if (stackCU != null) {
IDocument doc = new Document(stackCU.getSource());
try {
insertion = doc.getLineOffset(stackFrame.getLineNumber() - 1);
} catch(BadLocationException e) {
JDIDebugUIPlugin.log(e);
}
}
}
}
return insertion;
}
/**
* Returns the compliation unit associated with this
* Java stack frame. Returns <code>null</code> for a binary stack
* frame.
*/
protected ICompilationUnit getCompilationUnit(IJavaStackFrame stackFrame) {
// Get the corresponding element.
ILaunch launch = stackFrame.getLaunch();
if (launch == null) {
return null;
}
ISourceLocator locator= launch.getSourceLocator();
if (locator == null) {
return null;
}
Object sourceElement= locator.getSourceElement(stackFrame);
if (sourceElement instanceof IType) {
return ((IType)sourceElement).getCompilationUnit();
}
if (sourceElement instanceof ICompilationUnit) {
return (ICompilationUnit)sourceElement;
}
return null;
}
protected void handle(ITextViewer viewer, CoreException x) {
Shell shell= viewer.getTextWidget().getShell();
ErrorDialog.openError(shell,
DisplayMessages.getString("DisplayCompletionProcessor.Problems_during_completion_1"), //$NON-NLS-1$
DisplayMessages.getString("DisplayCompletionProcessor.An_exception_occurred_during_code_completion_2"), //$NON-NLS-1$
x.getStatus());
JDIDebugUIPlugin.log(x);
}
protected char[][][] resolveLocalVariables(IVariable[] variables) throws DebugException {
List localVariableNames= new ArrayList();
List localVariableTypeNames= new ArrayList();
for (int i = 0; i < variables.length; i++) {
IVariable variable = variables[i];
try {
localVariableTypeNames.add(getTranslatedTypeName(variable.getReferenceTypeName()).toCharArray());
localVariableNames.add(variable.getName().toCharArray());
} catch (DebugException e) {
// do not throw ClassNotLoadedException
// nothing we can do, just ignore this local variable
if (!(e.getStatus().getException() instanceof ClassNotLoadedException)) {
throw e;
}
}
}
char[][] names= new char[localVariableNames.size()][];
int i= 0;
for (Iterator iter= localVariableNames.iterator(); iter.hasNext();) {
names[i++]= (char[]) iter.next();
}
char[][] typeNames= new char[localVariableNames.size()][];
i= 0;
for (Iterator iter= localVariableTypeNames.iterator(); iter.hasNext();) {
typeNames[i++]= (char[]) iter.next();
}
return new char[][][] {names, typeNames};
}
/**
* Returns the Java project associated with the given stack
* frame, or <code>null</code> if none.
*/
protected IJavaProject getJavaProject(IStackFrame stackFrame) {
// Get the corresponding element.
ILaunch launch = stackFrame.getLaunch();
if (launch == null) {
return null;
}
ISourceLocator locator= launch.getSourceLocator();
if (locator == null)
return null;
Object sourceElement = locator.getSourceElement(stackFrame);
if (sourceElement instanceof IJavaElement) {
return ((IJavaElement) sourceElement).getJavaProject();
}
if (sourceElement instanceof IResource) {
IJavaProject project = JavaCore.create(((IResource)sourceElement).getProject());
if (project.exists()) {
return project;
}
}
return null;
}
/**
* Order the given proposals.
*/
protected IJavaCompletionProposal[] order(IJavaCompletionProposal[] proposals) {
Arrays.sort(proposals, fComparator);
return proposals;
}
/**
* Configures the display result collection for the current code assist session
*/
protected void configureResultCollector(IJavaProject project, ITextSelection selection) {
fCollector = new CompletionProposalCollector(project);
if (selection.getLength() != 0) {
fCollector.setReplacementLength(selection.getLength());
}
}
/**
* Returns an array of simple type names that are
* part of the given type's qualified name. For
* example, if the given name is <code>x.y.A$B</code>,
* an array with <code>["A", "B"]</code> is returned.
*
* @param typeName fully qualified type name
* @return array of nested type names
*/
protected String[] getNestedTypeNames(String typeName) {
int index = typeName.lastIndexOf('.');
if (index >= 0) {
typeName= typeName.substring(index + 1);
}
index = typeName.indexOf('$');
List list = new ArrayList(1);
while (index >= 0) {
list.add(typeName.substring(0, index));
typeName = typeName.substring(index + 1);
index = typeName.indexOf('$');
}
list.add(typeName);
return (String[])list.toArray(new String[list.size()]);
}
/**
* Returns a copy of the type name with '$' replaced by
* '.', or returns <code>null</code> if the given type
* name refers to an anonymous inner class.
*
* @param typeName a fully qualified type name
* @return a copy of the type name with '$' replaced by
* '.', or returns <code>null</code> if the given type
* name refers to an anonymous inner class.
*/
protected String getTranslatedTypeName(String typeName) {
int index = typeName.lastIndexOf('$');
if (index == -1) {
return typeName;
}
if (index + 1 > typeName.length()) {
// invalid name
return typeName;
}
String last = typeName.substring(index + 1);
try {
Integer.parseInt(last);
return null;
} catch (NumberFormatException e) {
return typeName.replace('$', '.');
}
}
/**
* Returns the receiving type of the the given stack frame.
*
* @return receiving type
* @exception DebugException if:<ul>
* <li>A failure occurs while accessing attributes of
* the stack frame</li>
* <li>the resolved type is an inner type</li>
* <li>unable to resolve a type</li>
* </ul>
*/
private IType getReceivingType(IJavaProject project, IJavaStackFrame frame) throws DebugException {
String typeName= frame.getReceivingTypeName();
String sourceName= frame.getSourceName();
if (sourceName == null || !typeName.equals(frame.getDeclaringTypeName())) {
// if there is no debug attribute or the declaring type is not the
// same as the receiving type, we must guess at the receiver's source
// file
int dollarIndex= typeName.indexOf('$');
if (dollarIndex >= 0) {
typeName= typeName.substring(0, dollarIndex);
}
typeName = typeName.replace('.', IPath.SEPARATOR);
typeName+= ".java"; //$NON-NLS-1$
} else {
int index = typeName.lastIndexOf('.');
if (index >= 0) {
typeName = typeName.substring(0, index + 1);
typeName = typeName.replace('.', IPath.SEPARATOR);
} else {
typeName = ""; //$NON-NLS-1$
}
typeName+=sourceName;
}
return getType(project, frame.getReceivingTypeName(), typeName);
}
/**
* Tells this processor to order the proposals alphabetically.
*
* @param order <code>true</code> if proposals should be ordered.
*/
public void orderProposalsAlphabetically(boolean order) {
fComparator.setOrderAlphabetically(order);
}
/**
* @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
*/
public char[] getCompletionProposalAutoActivationCharacters() {
return fProposalAutoActivationSet;
}
/**
* 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;
}
protected CompletionProposalCollector getCollector() {
return fCollector;
}
protected void setCollector(CompletionProposalCollector collector) {
fCollector = collector;
}
protected IType getType(IJavaProject project, String originalTypeName, String typeName) throws DebugException {
int dollarIndex= typeName.indexOf('$');
if (dollarIndex > 0) {
typeName= typeName.substring(0, dollarIndex);
}
IPath sourcePath = new Path(typeName);
IType type = null;
try {
IJavaElement result= project.findElement(sourcePath);
String[] typeNames = getNestedTypeNames(originalTypeName);
if (result != null) {
if (result instanceof IClassFile) {
type = ((IClassFile)result).getType();
} else if (result instanceof ICompilationUnit) {
type = ((ICompilationUnit)result).getType(typeNames[0]);
} else if (result instanceof IType) {
type = (IType)result;
}
}
for (int i = 1; i < typeNames.length; i++) {
String innerTypeName= typeNames[i];
try {
Integer.parseInt(innerTypeName);
return type;
} catch (NumberFormatException e) {
}
type = type.getType(innerTypeName);
}
} catch (JavaModelException e) {
throw new DebugException(e.getStatus());
}
return type;
}
/**
* Returns the templateEngine.
* @return TemplateEngine
*/
public TemplateEngine getTemplateEngine() {
return fTemplateEngine;
}
}