/*******************************************************************************
* Copyright (c) 2000, 2014 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
* Lars Vogel <lars.vogel@gmail.com> - [templates][content assist] Ctrl+Space without any starting letter shows to no templates - https://bugs.eclipse.org/406463
* Lukas Hanke <hanke@yatta.de> - [templates][content assist] Content assist for 'for' loop should suggest member variables - https://bugs.eclipse.org/117215
*******************************************************************************/
package _org.eclipse.jdt.internal.corext.template.java;
import melnorme.lang.ide.core.ISourceFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateContextType;
/**
* A context for Java source.
*/
public class JavaContext extends CompilationUnitContext {
// /** A code completion requester for guessing local variable names. */
// private CompilationUnitCompletion fCompletion;
/**
* The list of used local names.
* @since 3.3
*/
// private Set<String> fUsedNames= new HashSet<String>();
// private Map<String, MultiVariable> fVariables= new HashMap<String, MultiVariable>();
// private ImportRewrite fImportRewrite;
// private Set<String> fCompatibleContextTypeIds;
/**
* 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, ISourceFile 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, ISourceFile compilationUnit) {
super(type, document, completionPosition, compilationUnit);
}
// /**
// * Adds a context type that is also compatible. That means the context can also process templates of that context type.
// *
// * @param contextTypeId the context type to accept
// */
// public void addCompatibleContextType(String contextTypeId) {
// if (fCompatibleContextTypeIds == null)
// fCompatibleContextTypeIds= new HashSet<String>();
// fCompatibleContextTypeIds.add(contextTypeId);
// }
// /**
// * 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());
// IJavaProject project= getJavaProject();
// return Strings.computeIndentUnits(lineContent, project);
// } catch (BadLocationException e) {
// return 0;
// }
// }
//
// /*
// * @see TemplateContext#evaluate(Template template)
// */
// @Override
// public TemplateBuffer evaluate(Template template) throws BadLocationException, TemplateException {
// clear();
//
// if (!canEvaluate(template))
// throw new TemplateException(JavaTemplateMessages.Context_error_cannot_evaluate);
//
// TemplateTranslator translator= new TemplateTranslator() {
// @Override
// protected TemplateVariable createVariable(TemplateVariableType type, String name, int[] offsets) {
//// TemplateVariableResolver resolver= getContextType().getResolver(type.getName());
//// return resolver.createVariable();
//
// MultiVariable variable= new JavaVariable(type, name, offsets);
// fVariables.put(name, variable);
// return variable;
// }
// };
// TemplateBuffer buffer= translator.translate(template);
//
// getContextType().resolve(buffer, this);
//
// rewriteImports();
//
// IPreferenceStore prefs= JavaPlugin.getDefault().getPreferenceStore();
// boolean useCodeFormatter= prefs.getBoolean(PreferenceConstants.TEMPLATES_USE_CODEFORMATTER);
//
// IJavaProject project= getJavaProject();
// JavaFormatter formatter= new JavaFormatter(TextUtilities.getDefaultLineDelimiter(getDocument()), getIndentation(), useCodeFormatter, project);
// formatter.format(buffer, this);
//
// clear();
//
// return buffer;
// }
// private void clear() {
// fUsedNames.clear();
// fImportRewrite= null;
// }
/*
* @see TemplateContext#canEvaluate(Template templates)
*/
@Override
public boolean canEvaluate(Template template) {
if (!hasCompatibleContextType(template))
return false;
if (fForceEvaluation)
return true;
String key= getKey();
return (key.length() > 0 || !isAfterDot()) && template.getName().toLowerCase().startsWith(key.toLowerCase());
}
private boolean isAfterDot() {
try {
IDocument document= getDocument();
int offset= getCompletionOffset();
return document.get(offset - 1, 1).charAt(0) == '.';
} catch (BadLocationException e) {
return false;
}
}
private boolean hasCompatibleContextType(Template template) {
String key= getKey();
if (template.matches(key, getContextType().getId()))
return true;
// if (fCompatibleContextTypeIds == null)
// return false;
//
// Iterator<String> iter= fCompatibleContextTypeIds.iterator();
// while (iter.hasNext()) {
// if (template.matches(key, iter.next()))
// return true;
// }
//
return false;
}
/*
* @see DocumentTemplateContext#getCompletionPosition();
*/
@Override
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()
*/
@Override
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()
*/
@Override
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);
// String message= e.getMessage();
// if (message == null) {
// message= JavaTemplateMessages.JavaContext_unexpected_error_message;
// }
// MessageDialog.openError(shell, title, message);
// }
// }
// 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 arrays available in the current {@link CompilationUnit}'s scope.
// *
// * @return the names of local arrays available in the current {@link CompilationUnit}'s scope
// */
// public Variable[] getArrays() {
// Variable[] arrays= getCompletion().findArraysInCurrentScope();
// arrange(arrays);
// return arrays;
// }
//
// /**
// * Sorts already used variables behind any that are not yet used.
// *
// * @param variables the variables to sort
// * @since 3.3
// */
// private void arrange(Variable[] variables) {
// Arrays.sort(variables, new Comparator<Variable>() {
// public int compare(Variable o1, Variable o2) {
// return rank(o1) - rank(o2);
// }
//
// private int rank(Variable l) {
// return fUsedNames.contains(l.getName()) ? 1 : 0;
// }
// });
// }
//
// /**
// * Returns the names of local variables matching <code>type</code>.
// *
// * @param type the type of the variables
// * @return the names of local variables matching <code>type</code>
// * @since 3.3
// */
// public Variable[] getLocalVariables(String type) {
// Variable[] localVariables= getCompletion().findLocalVariables(type);
// arrange(localVariables);
// return localVariables;
// }
//
// /**
// * Returns the names of fields matching <code>type</code>.
// *
// * @param type the type of the fields
// * @return the names of fields matching <code>type</code>
// * @since 3.3
// */
// public Variable[] getFields(String type) {
// Variable[] fields= getCompletion().findFieldVariables(type);
// arrange(fields);
// return fields;
// }
//
// /**
// * Returns the names of iterables or arrays available in the current {@link CompilationUnit}'s scope.
// *
// * @return the names of iterables or arrays available in the current {@link CompilationUnit}'s scope
// */
// public Variable[] getIterables() {
// Variable[] iterables= getCompletion().findIterablesInCurrentScope();
// arrange(iterables);
// return iterables;
// }
//
// public void markAsUsed(String name) {
// fUsedNames.add(name);
// }
//
// public String[] suggestVariableNames(String type) throws IllegalArgumentException {
// String[] excludes= computeExcludes();
// // TODO erasure, arrays, etc.
// String[] result= suggestVariableName(type, excludes);
// return result;
// }
//
// String[] computeExcludes() {
// String[] excludes= getCompletion().getLocalVariableNames();
// if (!fUsedNames.isEmpty()) {
// String[] allExcludes= new String[fUsedNames.size() + excludes.length];
// System.arraycopy(excludes, 0, allExcludes, 0, excludes.length);
// System.arraycopy(fUsedNames.toArray(), 0, allExcludes, 0, fUsedNames.size());
// excludes= allExcludes;
// }
// return excludes;
// }
//
// private String[] suggestVariableName(String type, String[] excludes) throws IllegalArgumentException {
// int dim=0;
// while (type.endsWith("[]")) {//$NON-NLS-1$
// dim++;
// type= type.substring(0, type.length() - 2);
// }
//
// IJavaProject project= getJavaProject();
// if (project != null)
// return StubUtility.getVariableNameSuggestions(NamingConventions.VK_LOCAL, project, type, dim, Arrays.asList(excludes), true);
//
// // fallback if we lack proper context: roll-our own lowercasing
// return new String[] {Signature.getSimpleName(type).toLowerCase()};
// }
//
// /**
// * Adds an import for type with type name <code>type</code> if possible.
// * Returns a string which can be used to reference the type.
// *
// * @param type the fully qualified name of the type to import
// * @return returns a type to which the type binding can be assigned to.
// * The returned type contains is unqualified when an import could be added or was already known.
// * It is fully qualified, if an import conflict prevented the import.
// * @since 3.4
// */
// public String addImport(String type) {
// if (isReadOnly())
// return type;
//
// ICompilationUnit cu= getCompilationUnit();
// if (cu == null)
// return type;
//
// try {
// boolean qualified= type.indexOf('.') != -1;
// if (!qualified) {
// IJavaSearchScope searchScope= SearchEngine.createJavaSearchScope(new IJavaElement[] { cu.getJavaProject() });
// SimpleName nameNode= null;
// TypeNameMatch[] matches= findAllTypes(type, searchScope, nameNode, null, cu);
// if (matches.length != 1) // only add import if we have a single match
// return type;
// type= matches[0].getFullyQualifiedName();
// }
//
// CompilationUnit root= getASTRoot(cu);
// if (fImportRewrite == null) {
// if (root == null) {
// fImportRewrite= StubUtility.createImportRewrite(cu, true);
// } else {
// fImportRewrite= StubUtility.createImportRewrite(root, true);
// }
// }
//
// ImportRewriteContext context;
// if (root == null)
// context= null;
// else
// context= new ContextSensitiveImportRewriteContext(root, getCompletionOffset(), fImportRewrite);
//
// return fImportRewrite.addImport(type, context);
// } catch (JavaModelException e) {
// handleException(null, e);
// return type;
// }
// }
//
// /**
// * Adds a static import for the member with name <code>qualifiedMemberName</code>. The member is
// * either a static field or a static method or a '*' to import all static members of a type.
// *
// * @param qualifiedMemberName the fully qualified name of the member to import or a qualified type
// * name plus a '.*' suffix.
// * @return returns either the simple member name if the import was successful or else the qualified name.
// * @since 3.4
// */
// public String addStaticImport(String qualifiedMemberName) {
// if (isReadOnly())
// return qualifiedMemberName;
//
// ICompilationUnit cu= getCompilationUnit();
// if (cu == null)
// return qualifiedMemberName;
//
// int memberOffset= qualifiedMemberName.lastIndexOf('.');
// if (memberOffset == -1)
// return qualifiedMemberName;
//
// String typeName= qualifiedMemberName.substring(0, memberOffset);
// String memberName= qualifiedMemberName.substring(memberOffset + 1, qualifiedMemberName.length());
// try {
// boolean isField;
// if ("*".equals(memberName)) { //$NON-NLS-1$
// isField= true;
// } else {
// IJavaProject javaProject= cu.getJavaProject();
//
// IType type= javaProject.findType(typeName);
// if (type == null)
// return qualifiedMemberName;
//
// IField field= type.getField(memberName);
// if (field.exists()) {
// isField= true;
// } else if (hasMethod(type, memberName)) {
// isField= false;
// } else {
// return qualifiedMemberName;
// }
// }
//
// CompilationUnit root= getASTRoot(cu);
// if (fImportRewrite == null) {
// if (root == null) {
// fImportRewrite= StubUtility.createImportRewrite(cu, true);
// } else {
// fImportRewrite= StubUtility.createImportRewrite(root, true);
// }
// }
//
// ImportRewriteContext context;
// if (root == null)
// context= null;
// else
// context= new ContextSensitiveImportRewriteContext(root, getCompletionOffset(), fImportRewrite);
//
// return fImportRewrite.addStaticImport(typeName, memberName, isField, context);
// } catch (JavaModelException e) {
// handleException(null, e);
// return typeName;
// }
// }
//
// /**
// * Does <code>type</code> contain a method with <code>name</code>?
// *
// * @param type the type to inspect
// * @param name the name of the method to search for
// * @return true if has such a method
// * @throws JavaModelException if methods could not be retrieved
// * @since 3.4
// */
// private boolean hasMethod(IType type, String name) throws JavaModelException {
// IMethod[] methods= type.getMethods();
// for (int i= 0; i < methods.length; i++) {
// if (name.equals(methods[i].getElementName()))
// return true;
// }
//
// return false;
// }
//
// private void rewriteImports() {
// if (fImportRewrite == null)
// return;
//
// if (isReadOnly())
// return;
//
// ICompilationUnit cu= getCompilationUnit();
// if (cu == null)
// return;
//
// try {
// Position position= new Position(getCompletionOffset(), 0);
// 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 {
// JavaModelUtil.applyEdit(cu, fImportRewrite.rewriteImports(null), false, null);
//
// setCompletionOffset(position.getOffset());
// } 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);
// }
// }
//
// private CompilationUnit getASTRoot(ICompilationUnit compilationUnit) {
// return SharedASTProvider.getAST(compilationUnit, SharedASTProvider.WAIT_NO, new NullProgressMonitor());
// }
//
// /*
// * Finds a type by the simple name. From AddImportsOperation
// */
// private TypeNameMatch[] findAllTypes(String simpleTypeName, IJavaSearchScope searchScope, SimpleName nameNode, IProgressMonitor monitor, ICompilationUnit cu) throws JavaModelException {
// boolean is50OrHigher= JavaModelUtil.is50OrHigher(cu.getJavaProject());
//
// int typeKinds= SimilarElementsRequestor.ALL_TYPES;
// if (nameNode != null) {
// typeKinds= ASTResolving.getPossibleTypeKinds(nameNode, is50OrHigher);
// }
//
// ArrayList<TypeNameMatch> typeInfos= new ArrayList<TypeNameMatch>();
// TypeNameMatchCollector requestor= new TypeNameMatchCollector(typeInfos);
// new SearchEngine().searchAllTypeNames(null, 0, simpleTypeName.toCharArray(), SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, getSearchForConstant(typeKinds), searchScope, requestor, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor);
//
// ArrayList<TypeNameMatch> typeRefsFound= new ArrayList<TypeNameMatch>(typeInfos.size());
// for (int i= 0, len= typeInfos.size(); i < len; i++) {
// TypeNameMatch curr= typeInfos.get(i);
// if (curr.getPackageName().length() > 0) { // do not suggest imports from the default package
// if (isOfKind(curr, typeKinds, is50OrHigher) && isVisible(curr, cu)) {
// typeRefsFound.add(curr);
// }
// }
// }
// return typeRefsFound.toArray(new TypeNameMatch[typeRefsFound.size()]);
// }
//
// private int getSearchForConstant(int typeKinds) {
// final int CLASSES= SimilarElementsRequestor.CLASSES;
// final int INTERFACES= SimilarElementsRequestor.INTERFACES;
// final int ENUMS= SimilarElementsRequestor.ENUMS;
// final int ANNOTATIONS= SimilarElementsRequestor.ANNOTATIONS;
//
// switch (typeKinds & (CLASSES | INTERFACES | ENUMS | ANNOTATIONS)) {
// case CLASSES: return IJavaSearchConstants.CLASS;
// case INTERFACES: return IJavaSearchConstants.INTERFACE;
// case ENUMS: return IJavaSearchConstants.ENUM;
// case ANNOTATIONS: return IJavaSearchConstants.ANNOTATION_TYPE;
// case CLASSES | INTERFACES: return IJavaSearchConstants.CLASS_AND_INTERFACE;
// case CLASSES | ENUMS: return IJavaSearchConstants.CLASS_AND_ENUM;
// default: return IJavaSearchConstants.TYPE;
// }
// }
//
// private boolean isOfKind(TypeNameMatch curr, int typeKinds, boolean is50OrHigher) {
// int flags= curr.getModifiers();
// if (Flags.isAnnotation(flags)) {
// return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ANNOTATIONS) != 0);
// }
// if (Flags.isEnum(flags)) {
// return is50OrHigher && ((typeKinds & SimilarElementsRequestor.ENUMS) != 0);
// }
// if (Flags.isInterface(flags)) {
// return (typeKinds & SimilarElementsRequestor.INTERFACES) != 0;
// }
// return (typeKinds & SimilarElementsRequestor.CLASSES) != 0;
// }
//
//
// private boolean isVisible(TypeNameMatch curr, ICompilationUnit cu) {
// int flags= curr.getModifiers();
// if (Flags.isPrivate(flags)) {
// return false;
// }
// if (Flags.isPublic(flags) || Flags.isProtected(flags)) {
// return true;
// }
// return curr.getPackageName().equals(cu.getParent().getElementName());
// }
//
// /**
// * 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(template.getContextTypeId());
// if (!(contextType instanceof CompilationUnitContextType))
// 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());
//
// CompilationUnitContext context= ((CompilationUnitContextType) contextType).createContext(document, position, 0, compilationUnit);
// context.setForceEvaluation(true);
//
// TemplateBuffer buffer= context.evaluate(template);
// if (buffer == null)
// return null;
// return buffer.getString();
// }
//
// TemplateVariable getTemplateVariable(String name) {
// TemplateVariable variable= fVariables.get(name);
// if (variable != null && !variable.isResolved())
// getContextType().resolve(variable, this);
// return variable;
// }
// /**
// * Adds a multi-variable guess dependency.
// *
// * @param master the master variable - <code>slave</code> needs to be updated when
// * <code>master</code> changes
// * @param slave the dependent variable
// * @since 3.3
// */
// public void addDependency(MultiVariable master, MultiVariable slave) {
// MultiVariableGuess guess= getMultiVariableGuess();
// if (guess == null) {
// guess= new MultiVariableGuess();
// setMultiVariableGuess(guess);
// }
//
// guess.addDependency(master, slave);
// }
}