/******************************************************************************* * Copyright (c) 2005, 2012 eBay Inc. * 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 * *******************************************************************************/ package org.eclipse.vjet.eclipse.codeassist.keywords; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import org.eclipse.vjet.dsf.jst.IJstType; import org.eclipse.vjet.dsf.jstojava.translator.robust.completion.JstCompletion; import org.eclipse.vjet.eclipse.core.ClassFileConstants; import org.eclipse.vjet.eclipse.core.IImportDeclaration; import org.eclipse.vjet.eclipse.core.IJSSourceModule; import org.eclipse.vjet.eclipse.core.IJSType; import org.eclipse.vjet.eclipse.internal.core.util.Util; import org.eclipse.dltk.mod.compiler.env.ISourceModule; import org.eclipse.dltk.mod.core.CompletionProposal; import org.eclipse.dltk.mod.core.DLTKCore; import org.eclipse.dltk.mod.core.IModelElement; import org.eclipse.dltk.mod.core.IScriptFolder; import org.eclipse.dltk.mod.core.IScriptProject; import org.eclipse.dltk.mod.core.IType; import org.eclipse.dltk.mod.core.ModelException; import org.eclipse.dltk.mod.internal.core.DefaultWorkingCopyOwner; import org.eclipse.dltk.mod.internal.core.ModelElementRequestor; import org.eclipse.dltk.mod.internal.core.NameLookup; import org.eclipse.dltk.mod.internal.core.ScriptProject; /** * Base class for all package/type completions * * * */ public class BaseCompletionHandler implements ICompletionHandler { public Class getCompletionClass() { return null; } /** * populate {@link list} with package/type completion proposals * * @param sourceModule - current {@link ISourceModule} * @param position - completion position * @param completion - {@link JstCompletion} * @param list - {@link List} of completion proposals */ public void complete(ISourceModule sourceModule, int position, JstCompletion completion, List<CompletionProposal> list) { // completePackages(sourceModule, position, completion, list); // completeTypes(sourceModule, position, completion, list); String wordBefore = completion.getToken(); if (wordBefore.length() == 0) { return; } if (!(sourceModule instanceof IModelElement)) { // should not happen return; } NameLookup lookup = getNameLookup((IModelElement) sourceModule); if (lookup == null) { return; } IScriptFolder[] paths = lookup.findScriptFolders(wordBefore, true); if (paths != null) { String[] packageProposals = createPackageProposals(paths); if (packageProposals.length > 0) { addProposals(list, packageProposals, position, CompletionProposal.PACKAGE_REF, getRelevance(CompletionProposal.PACKAGE_REF), wordBefore); } } String currentPath = ""; int lastDotPosition = wordBefore.lastIndexOf('.'); if (lastDotPosition != -1) { currentPath = wordBefore.substring(0, lastDotPosition); } else { // get current path from type // if (sourceModule instanceof JSSourceModule) { // String folderPath = ((JSSourceModule) sourceModule).getParent() // .getResource().getProjectRelativePath().toString(); // currentPath = folderPath.replace('/', '.'); // } } IScriptFolder[] currentPackage = lookup.findScriptFolders(currentPath, false); ModelElementRequestor modelRequestor = new ModelElementRequestor(); if (currentPackage != null && currentPackage.length > 0) { lookup.seekTypes(wordBefore .substring(wordBefore.lastIndexOf('.') + 1), currentPackage[0], true, NameLookup.ACCEPT_ALL, modelRequestor); // Add also types that were already imported if (lastDotPosition == -1) { searchBetweenImported(sourceModule, lookup, modelRequestor, wordBefore); } if (modelRequestor.getTypes() != null && modelRequestor.getTypes().length > 0) { List<IType> typeProposals = createTypeProposals( getType(sourceModule), modelRequestor); addProposals(list, wordBefore, typeProposals, CompletionProposal.TYPE_REF, getRelevance(CompletionProposal.TYPE_REF), position); } } } protected void completeTypes(ISourceModule sourceModule, int position, JstCompletion completion, List<CompletionProposal> list) { NameLookup lookup = getNameLookup((IModelElement) sourceModule); if (lookup == null) { return; } String wordBefore = completion.getToken(); if (wordBefore.length() == 0) { return; } String currentPath = ""; int lastDotPosition = wordBefore.lastIndexOf('.'); if (lastDotPosition != -1) { currentPath = wordBefore.substring(0, lastDotPosition); } else { // get current path from type // if (sourceModule instanceof JSSourceModule) { // String folderPath = ((JSSourceModule) sourceModule).getParent() // .getResource().getProjectRelativePath().toString(); // currentPath = folderPath.replace('/', '.'); // } } IScriptFolder[] currentPackage = lookup.findScriptFolders(currentPath, false); ModelElementRequestor modelRequestor = new ModelElementRequestor(); if (currentPackage != null && currentPackage.length > 0) { lookup.seekTypes(wordBefore .substring(wordBefore.lastIndexOf('.') + 1), currentPackage[0], true, NameLookup.ACCEPT_ALL, modelRequestor); // Add also types that were already imported if (lastDotPosition == -1) { searchBetweenImported(sourceModule, lookup, modelRequestor, wordBefore); } if (modelRequestor.getTypes() != null && modelRequestor.getTypes().length > 0) { List<IType> typeProposals = createTypeProposals( getType(sourceModule), modelRequestor); addProposals(list, wordBefore, typeProposals, CompletionProposal.TYPE_REF, getRelevance(CompletionProposal.TYPE_REF), position); } } } public void completePackages(ISourceModule sourceModule, int position, JstCompletion completion, List<CompletionProposal> list) { String wordBefore = completion.getToken(); if (wordBefore.length() == 0) { return; } if (!(sourceModule instanceof IModelElement)) { // should not happen return; } NameLookup lookup = getNameLookup((IModelElement) sourceModule); if (lookup == null) { return; } IScriptFolder[] paths = lookup.findScriptFolders(wordBefore, true); if (paths != null) { String[] packageProposals = createPackageProposals(paths); if (packageProposals.length > 0) { addProposals(list, packageProposals, position, CompletionProposal.PACKAGE_REF, getRelevance(CompletionProposal.PACKAGE_REF), wordBefore); } } } /** * Adds type completion proposals from imports * * @param sm - current {@link ISourceModule} * @param lookup - {@link NameLookup} * @param modelRequestor - {@link ModelElementRequestor} * @param wordBefore - word before cursor position */ private void searchBetweenImported(ISourceModule sm, NameLookup lookup, ModelElementRequestor modelRequestor, String wordBefore) { IJSSourceModule sourceModule = (IJSSourceModule) sm; try { IModelElement[] imports = sourceModule.getImportContainer() .getChildren(); IType[] alreadyAddedTypes = modelRequestor.getTypes(); List<IType> alreadyAdded = new ArrayList<IType>( alreadyAddedTypes.length); for (IType type : alreadyAddedTypes) { alreadyAdded.add(type); } for (IModelElement modelElement : imports) { IImportDeclaration importDeclaration = (IImportDeclaration) modelElement; String fullName = importDeclaration.getElementName(); int dotIndex = fullName.lastIndexOf('.'); String lastSegment = fullName.substring(dotIndex + 1); IType type = null; if ("*".equals(lastSegment)) { IScriptFolder[] folders = lookup.findScriptFolders(fullName .substring(0, dotIndex), false); if (folders != null && folders.length > 0) { IModelElement[] children = folders[0].getChildren(); for (IModelElement modelElement2 : children) { if (modelElement2.getElementType() == IModelElement.SOURCE_MODULE) { IType[] types = ((IJSSourceModule) modelElement2) .getTypes(); if (types != null && types.length > 0) { checkAndAdd(types[0], wordBefore, alreadyAdded, modelRequestor); } } } } } else if (lastSegment.startsWith(wordBefore)) { type = lookup.findType(importDeclaration.getElementName(), false, NameLookup.ACCEPT_ALL); checkAndAdd(type, wordBefore, alreadyAdded, modelRequestor); } } } catch (ModelException e) { } } private static void checkAndAdd(IType type, String prefix, Collection<IType> alreadyAdded, ModelElementRequestor modelRequestor) { if (type != null) { if (type.getElementName().startsWith(prefix)) { if (!alreadyAdded.contains(type)) { alreadyAdded.add(type); modelRequestor.acceptType(type); } } } } private void addProposals(List<CompletionProposal> keywords, String[] array, int position, int type, int relevance, String wordBefore) { for (String name : array) { CompletionProposal data = CompletionProposal.create(type, position); data.setName(name.toCharArray()); data.setCompletion(name.toCharArray()); data.setRelevance(relevance); data.setReplaceRange(position - wordBefore.length(), position); keywords.add(data); } } private void addProposals(List<CompletionProposal> keywords, IType[] proposals, int position, int type, int relevance) { for (IType itype : proposals) { final String typeName; typeName = itype.getFullyQualifiedName().replace('/', '.'); CompletionProposal data = CompletionProposal.create(type, position); char[] shortName = itype.getElementName().toCharArray(); data.setName(typeName.toCharArray()); data.setCompletion(shortName); data.setRelevance(relevance); setFlags(itype, data); keywords.add(data); } } private void setFlags(IType itype, CompletionProposal data) { if (itype instanceof IJSType) { try { IJSType jsType = (IJSType) itype; if (jsType.isInterface()) { data.setFlags(ClassFileConstants.AccInterface); } if (jsType.isEnum()) { data.setFlags(ClassFileConstants.AccEnum); } IJstType jstType = Util.toJstType(jsType); if(jstType!=null && jstType.isMixin()) { data.setFlags(ClassFileConstants.AccModule); } } catch (ModelException e) { DLTKCore.error(e.toString(), e); } } } private void addProposals(List<CompletionProposal> keywords, String wordBefore, List<IType> proposals, int type, int relevance, int position) { List<IType> filteredPropodals = new ArrayList<IType>(); for (IType name : proposals) { final String typeName = name.getFullyQualifiedName().replace('/', '.'); // if (name.getFullyQualifiedName() == null // || !typeName.startsWith(wordBefore)) { // continue; // } filteredPropodals.add(name); } addProposals(keywords, filteredPropodals .toArray(new IType[filteredPropodals.size()]), position, type, relevance); } private NameLookup getNameLookup(IModelElement modelElement) { IScriptProject project = (IScriptProject) modelElement .getAncestor(IModelElement.SCRIPT_PROJECT); NameLookup lookup = null; try { lookup = ((ScriptProject) project) .newNameLookup(DefaultWorkingCopyOwner.PRIMARY); } catch (ModelException e) { DLTKCore.error(e.toString(), e); } return lookup; } /** * @param paths - array of {@link IScriptFolder} * @return String array of package names */ protected String[] createPackageProposals(IScriptFolder[] paths) { List<String> packageProposals = new LinkedList<String>(); for (IScriptFolder path : paths) { packageProposals.add(path.getElementName().replace('/', '.')); } return packageProposals.toArray(new String[packageProposals.size()]); } /** * @param currentType * @param modelRequestor * @return {@link List} of accessible types */ protected List<IType> createTypeProposals(IType currentType, ModelElementRequestor modelRequestor) { if (currentType != null && modelRequestor.getTypes() != null) { List<IType> proposals = new ArrayList<IType>(modelRequestor .getTypes().length); for (IType type : modelRequestor.getTypes()) { if (checkType(type, currentType)) { proposals.add(type); } } return proposals; } return null; } protected int getRelevance(int completionType) { switch (completionType) { case CompletionProposal.PACKAGE_REF: return 10; case CompletionProposal.TYPE_REF: return 200; default: return 1; } } private IType getType(ISourceModule sourceModule) { try { IType[] types = ((IJSSourceModule) sourceModule).getTypes(); if (types.length > 0) { return types[0]; } } catch (ModelException e) { DLTKCore.error(e.toString(), e); } catch (IndexOutOfBoundsException i) { DLTKCore.error(i.toString(), i); } return null; } protected boolean checkType(IType type, IType currentType) { if (type.equals(currentType)) { return false; } return true; } }