blob: 4131a08478b874b82f05fe13fb6058ec9cf4bb8a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.corext.template.java;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DefaultPositionUpdater;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateBuffer;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.TemplateException;
import org.eclipse.jface.text.templates.TemplateTranslator;
import org.eclipse.jface.text.templates.TemplateVariable;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.NamingConventions;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.template.java.CompilationUnitCompletion.LocalVariable;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Strings;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.template.contentassist.MultiVariable;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
/**
* A context for java source.
*/
public class JavaContext extends CompilationUnitContext {
/** A code completion requester for guessing local variable names. */
private CompilationUnitCompletion fCompletion;
/**
* Creates a java template context.
*
* @param type the context type.
* @param document the document.
* @param completionOffset the completion offset within the document.
* @param completionLength the completion length.
* @param compilationUnit the compilation unit (may be <code>null</code>).
*/
public JavaContext(TemplateContextType type, IDocument document, int completionOffset, int completionLength, ICompilationUnit compilationUnit) {
super(type, document, completionOffset, completionLength, compilationUnit);
}
/**
* Creates a java template context.
*
* @param type the context type.
* @param document the document.
* @param completionPosition the position defining the completion offset and length
* @param compilationUnit the compilation unit (may be <code>null</code>).
* @since 3.2
*/
public JavaContext(TemplateContextType type, IDocument document, Position completionPosition, ICompilationUnit compilationUnit) {
super(type, document, completionPosition, compilationUnit);
}
/**
* Returns the indentation level at the position of code completion.
*
* @return the indentation level at the position of the code completion
*/
private int getIndentation() {
int start= getStart();
IDocument document= getDocument();
try {
IRegion region= document.getLineInformationOfOffset(start);
String lineContent= document.get(region.getOffset(), region.getLength());
ICompilationUnit compilationUnit= getCompilationUnit();
IJavaProject project= compilationUnit == null ? null : compilationUnit.getJavaProject();
return Strings.computeIndentUnits(lineContent, project);
} catch (BadLocationException e) {
return 0;
}
}
/*
* @see TemplateContext#evaluate(Template template)
*/
public TemplateBuffer evaluate(Template template) throws BadLocationException, TemplateException {
if (!canEvaluate(template))
throw new TemplateException(JavaTemplateMessages.Context_error_cannot_evaluate);
TemplateTranslator translator= new TemplateTranslator() {
/*
* @see org.eclipse.jface.text.templates.TemplateTranslator#createVariable(java.lang.String, java.lang.String, int[])
*/
protected TemplateVariable createVariable(String type, String name, int[] offsets) {
return new MultiVariable(type, name, offsets);
}
};
TemplateBuffer buffer= translator.translate(template);
getContextType().resolve(buffer, this);
IPreferenceStore prefs= JavaPlugin.getDefault().getPreferenceStore();
boolean useCodeFormatter= prefs.getBoolean(PreferenceConstants.TEMPLATES_USE_CODEFORMATTER);
IJavaProject project= getCompilationUnit() != null ? getCompilationUnit().getJavaProject() : null;
JavaFormatter formatter= new JavaFormatter(TextUtilities.getDefaultLineDelimiter(getDocument()), getIndentation(), useCodeFormatter, project);
formatter.format(buffer, this);
return buffer;
}
/*
* @see TemplateContext#canEvaluate(Template templates)
*/
public boolean canEvaluate(Template template) {
if (fForceEvaluation)
return true;
String key= getKey();
return
template.matches(key, getContextType().getId()) &&
key.length() != 0 && template.getName().toLowerCase().startsWith(key.toLowerCase());
}
/*
* @see DocumentTemplateContext#getCompletionPosition();
*/
public int getStart() {
if (fIsManaged && getCompletionLength() > 0)
return super.getStart();
try {
IDocument document= getDocument();
int start= getCompletionOffset();
int end= getCompletionOffset() + getCompletionLength();
while (start != 0 && Character.isUnicodeIdentifierPart(document.getChar(start - 1)))
start--;
while (start != end && Character.isWhitespace(document.getChar(start)))
start++;
if (start == end)
start= getCompletionOffset();
return start;
} catch (BadLocationException e) {
return super.getStart();
}
}
/*
* @see org.eclipse.jdt.internal.corext.template.DocumentTemplateContext#getEnd()
*/
public int getEnd() {
if (fIsManaged || getCompletionLength() == 0)
return super.getEnd();
try {
IDocument document= getDocument();
int start= getCompletionOffset();
int end= getCompletionOffset() + getCompletionLength();
while (start != end && Character.isWhitespace(document.getChar(end - 1)))
end--;
return end;
} catch (BadLocationException e) {
return super.getEnd();
}
}
/*
* @see org.eclipse.jdt.internal.corext.template.DocumentTemplateContext#getKey()
*/
public String getKey() {
if (getCompletionLength() == 0)
return super.getKey();
try {
IDocument document= getDocument();
int start= getStart();
int end= getCompletionOffset();
return start <= end
? document.get(start, end - start)
: ""; //$NON-NLS-1$
} catch (BadLocationException e) {
return super.getKey();
}
}
/**
* Returns the character before the start position of the completion.
*
* @return the character before the start position of the completion
*/
public char getCharacterBeforeStart() {
int start= getStart();
try {
return start == 0
? ' '
: getDocument().getChar(start - 1);
} catch (BadLocationException e) {
return ' ';
}
}
private static void handleException(Shell shell, Exception e) {
String title= JavaTemplateMessages.JavaContext_error_title;
if (e instanceof CoreException)
ExceptionHandler.handle((CoreException)e, shell, title, null);
else if (e instanceof InvocationTargetException)
ExceptionHandler.handle((InvocationTargetException)e, shell, title, null);
else {
JavaPlugin.log(e);
MessageDialog.openError(shell, title, e.getMessage());
}
}
private CompilationUnitCompletion getCompletion() {
ICompilationUnit compilationUnit= getCompilationUnit();
if (fCompletion == null) {
fCompletion= new CompilationUnitCompletion(compilationUnit);
if (compilationUnit != null) {
try {
compilationUnit.codeComplete(getStart(), fCompletion);
} catch (JavaModelException e) {
// ignore
}
}
}
return fCompletion;
}
/**
* Returns the names of local arrays.
*
* @return the names of local arrays
*/
public String[] getArrays() {
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] localArrays= completion.findLocalArrays();
String[] ret= new String[localArrays.length];
for (int i= 0; i < ret.length; i++) {
ret[i]= localArrays[i].getName();
}
return ret;
}
/**
* Returns the names of the types of the local arrays grouped based on local
* variables.
*
* @return the names of the types of the local arrays
*/
public String[][] getArrayTypes() {
// TODO propose super types?
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] localArrays= completion.findLocalArrays();
String[][] ret= new String[localArrays.length][];
for (int i= 0; i < localArrays.length; i++) {
ret[i]= localArrays[i].getMemberTypeNames();
}
return ret;
}
/**
* Returns proposals for a variable name of a local array element grouped
* based on local array-typed variables.
*
* @return proposals for a variable name
*/
public String[][] getArrayElements() {
ICompilationUnit cu= getCompilationUnit();
if (cu == null) {
return new String[0][];
}
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] localArrays= completion.findLocalArrays();
return suggestElementNames(localArrays, true);
}
/**
* Returns an array index name. 'i', 'j', 'k' are tried until no name
* collision with an existing local variable occurs. If all names collide,
* <code>null</code> is returned.
*
* @return a name for an index variable or <code>null</code>
*/
public String getIndex() {
CompilationUnitCompletion completion= getCompletion();
String[] proposals= {"i", "j", "k"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
for (int i= 0; i != proposals.length; i++) {
String proposal = proposals[i];
if (!completion.existsLocalName(proposal))
return proposal;
}
return null;
}
/**
* Returns the names of local collections.
*
* @return the names of local collection
*/
public String[] getCollections() {
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] localCollections= completion.findLocalCollections();
String[] ret= new String[localCollections.length];
for (int i= 0; i < ret.length; i++) {
ret[i]= localCollections[i].getName();
}
return ret;
}
/**
* Returns the names of local iterables.
*
* @return the names of local iterables
*/
public String[] getIterables() {
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] localCollections= completion.findLocalIterables();
String[] ret= new String[localCollections.length];
for (int i= 0; i < ret.length; i++) {
ret[i]= localCollections[i].getName();
}
return ret;
}
/**
* Returns the names of the types of the local iterables grouped based on
* local variables.
*
* @return the names of the types of the local iterables
*/
public String[][] getIterableTypes() {
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] iterables= completion.findLocalIterables();
String[][] ret= new String[iterables.length][];
for (int i= 0; i < iterables.length; i++) {
ret[i]= iterables[i].getMemberTypeNames();
}
return ret;
}
/**
* Returns proposals for a variable name of a local iterable element
* grouped based on local array and collection variables.
*
* @return proposals for a variable name
*/
public String[][] getIterableElements() {
ICompilationUnit cu= getCompilationUnit();
if (cu == null) {
return new String[0][];
}
CompilationUnitCompletion completion= getCompletion();
LocalVariable[] iterables= completion.findLocalIterables();
return suggestElementNames(iterables, false);
}
private String[][] suggestElementNames(LocalVariable[] iterables, boolean excludeIndex) throws IllegalArgumentException {
String[] excludes= computeExcludes(excludeIndex);
String[][] ret= new String[iterables.length][];
for (int i= 0; i < iterables.length; i++) {
ret[i]= suggestVariableName(iterables[i], excludes);
}
return ret;
}
private String[] computeExcludes(boolean excludeIndex) {
String[] excludes= getCompletion().getLocalVariableNames();
if (excludeIndex) {
String index= getIndex();
if (index != null) {
String[] allExcludes= new String[excludes.length + 1];
System.arraycopy(excludes, 0, allExcludes, 0, excludes.length);
allExcludes[excludes.length]= index;
excludes= allExcludes;
}
}
return excludes;
}
private String[] suggestVariableName(LocalVariable iterable, String[] excludes) throws IllegalArgumentException {
IJavaProject project= getCompilationUnit().getJavaProject();
String memberTypeSig= iterable.getMemberTypeSignature();
int memberDimensions= Signature.getArrayCount(memberTypeSig);
String elementTypeSig= Signature.getElementType(memberTypeSig);
String erasure= Signature.getTypeErasure(elementTypeSig);
String fullName= Signature.toString(erasure);
String memberPackage= Signature.getQualifier(fullName);
String memberTypeName= Signature.getSimpleName(fullName);
String[] proposals= NamingConventions.suggestLocalVariableNames(project, memberPackage, memberTypeName, memberDimensions, excludes);
return proposals;
}
/**
* Returns an iterator name ('iter'). If 'iter' already exists as local
* variable, <code>null</code> is returned.
*
* @return an iterator name or <code>null</code>
*/
public String getIterator() {
CompilationUnitCompletion completion= getCompletion();
String[] proposals= {"iter"}; //$NON-NLS-1$
for (int i= 0; i != proposals.length; i++) {
String proposal = proposals[i];
if (!completion.existsLocalName(proposal))
return proposal;
}
return null;
}
public void addIteratorImport() {
ICompilationUnit cu= getCompilationUnit();
if (cu == null) {
return;
}
try {
Position position= new Position(getCompletionOffset(), getCompletionLength());
IDocument document= getDocument();
final String category= "__template_position_importer" + System.currentTimeMillis(); //$NON-NLS-1$
IPositionUpdater updater= new DefaultPositionUpdater(category);
document.addPositionCategory(category);
document.addPositionUpdater(updater);
document.addPosition(position);
try {
ImportRewrite rewrite= StubUtility.createImportRewrite(cu, true);
rewrite.addImport("java.util.Iterator"); //$NON-NLS-1$
JavaModelUtil.applyEdit(cu, rewrite.rewriteImports(null), false, null);
setCompletionOffset(position.getOffset());
setCompletionLength(position.getLength());
} catch (CoreException e) {
handleException(null, e);
} finally {
document.removePosition(position);
document.removePositionUpdater(updater);
document.removePositionCategory(category);
}
} catch (BadLocationException e) {
handleException(null, e);
} catch (BadPositionCategoryException e) {
handleException(null, e);
}
}
/**
* Evaluates a 'java' template in the context of a compilation unit
*
* @param template the template to be evaluated
* @param compilationUnit the compilation unit in which to evaluate the template
* @param position the position inside the compilation unit for which to evaluate the template
* @return the evaluated template
* @throws CoreException in case the template is of an unknown context type
* @throws BadLocationException in case the position is invalid in the compilation unit
* @throws TemplateException in case the evaluation fails
*/
public static String evaluateTemplate(Template template, ICompilationUnit compilationUnit, int position) throws CoreException, BadLocationException, TemplateException {
TemplateContextType contextType= JavaPlugin.getDefault().getTemplateContextRegistry().getContextType(JavaContextType.NAME);
if (contextType == null)
throw new CoreException(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IStatus.ERROR, JavaTemplateMessages.JavaContext_error_message, null));
IDocument document= new Document();
if (compilationUnit != null && compilationUnit.exists())
document.set(compilationUnit.getSource());
JavaContext context= new JavaContext(contextType, document, position, 0, compilationUnit);
context.setForceEvaluation(true);
TemplateBuffer buffer= context.evaluate(template);
if (buffer == null)
return null;
return buffer.getString();
}
}