/******************************************************************************* * Copyright (c) 2010 xored software, 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 * * Contributors: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.javascript.core.tests.contentassist; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import org.eclipse.dltk.codeassist.ICompletionEngine; import org.eclipse.dltk.compiler.env.IModuleSource; import org.eclipse.dltk.core.CompletionProposal; import org.eclipse.dltk.core.DLTKLanguageManager; import org.eclipse.dltk.core.tests.TestCompletionRequestor; import org.eclipse.dltk.core.tests.util.StringList; import org.eclipse.dltk.javascript.core.JavaScriptNature; import org.eclipse.dltk.javascript.internal.core.codeassist.JSCompletionEngine; import org.eclipse.dltk.javascript.internal.core.codeassist.JavaScriptCompletionEngine2; import org.eclipse.dltk.javascript.typeinfo.ITypeNames; import org.eclipse.dltk.javascript.typeinfo.MemberPredicate; import org.eclipse.dltk.javascript.typeinfo.MemberPredicates; import org.eclipse.dltk.javascript.typeinfo.TypeMemberQuery; import org.eclipse.dltk.javascript.typeinfo.model.Member; import org.eclipse.dltk.javascript.typeinfo.model.Type; import org.eclipse.dltk.javascript.typeinfo.model.TypeInfoModelLoader; @SuppressWarnings("restriction") public abstract class AbstractCompletionTest extends AbstractContentAssistTest { /** * Returns classes of the allowed completion engine. Return empty collection * to allow all of them. */ protected Collection<Class<? extends ICompletionEngine>> getAllowedEngines() { return Collections .<Class<? extends ICompletionEngine>> singleton(JavaScriptCompletionEngine2.class); } /** * Creates the {@link ICompletionEngine} among the allowed ones. If multiple * engines are allowed then compound one is constructed. * * @param results * @param globalOptions * @return * @see #getAllowedEngines() */ protected ICompletionEngine createEngine(List<CompletionProposal> results, int globalOptions) { final ICompletionEngine[] engines = DLTKLanguageManager .getCompletionEngines(JavaScriptNature.NATURE_ID); if (engines == null) { throw new IllegalStateException("No completion engines"); } final Collection<Class<? extends ICompletionEngine>> allowedEngines = getAllowedEngines(); final List<ICompletionEngine> selection = new ArrayList<ICompletionEngine>(); for (ICompletionEngine engine : engines) { if (isAllowed(engine, allowedEngines)) { selection.add(engine); if (engine instanceof JSCompletionEngine) { ((JSCompletionEngine) engine) .setGlobalOptions(globalOptions); } } } if (selection.isEmpty()) { throw new IllegalStateException("No allowed completion engines"); } final ICompletionEngine engine = selection.size() == 1 ? selection .get(0) : new CompoundCompletionEngine( selection.toArray(new ICompletionEngine[selection.size()])); engine.setRequestor(new TestCompletionRequestor(results)); return engine; } protected ICompletionEngine createEngine() { return createEngine(Collections.<CompletionProposal> emptyList(), JSCompletionEngine.OPTION_NONE); } private boolean isAllowed(ICompletionEngine engine, Collection<Class<? extends ICompletionEngine>> allowedEngines) { if (allowedEngines.isEmpty()) { return true; } final Set<Class<?>> processedTypes = new HashSet<Class<?>>(); final Queue<Class<?>> queue = new LinkedList<Class<?>>(); queue.add(engine.getClass()); processedTypes.addAll(queue); while (!queue.isEmpty()) { final Class<?> clazz = queue.remove(); if (allowedEngines.contains(clazz)) { return true; } final Class<?> superClass = clazz.getSuperclass(); if (superClass != null && processedTypes.add(superClass)) { queue.add(superClass); } for (Class<?> intf : clazz.getInterfaces()) { if (processedTypes.add(intf)) { queue.add(intf); } } } return false; } protected static boolean compareProposalNames( List<CompletionProposal> proposals, String[] names) { if (names.length != proposals.size()) { return false; } Collections.sort(proposals, new Comparator<CompletionProposal>() { @Override public int compare(CompletionProposal pr, CompletionProposal pr1) { return pr.getName().compareTo(pr1.getName()); } }); Arrays.sort(names); for (int i = 0, size = proposals.size(); i < size; ++i) { if (!names[i].equals(proposals.get(i).getName())) { return false; } } return true; } protected static StringList exractProposalNames( List<CompletionProposal> proposals, boolean withKinds) { final StringList list = new StringList(proposals.size()); for (int i = 0, size = proposals.size(); i < size; ++i) { final CompletionProposal proposal = proposals.get(i); String name = proposal.getName(); if (withKinds && proposal.getKind() == CompletionProposal.METHOD_REF) { name += "()"; } list.add(name); } return list; } protected void basicTest(IModuleSource module, int position, String[] compNames) { List<CompletionProposal> results = new ArrayList<CompletionProposal>(); ICompletionEngine c = createEngine(results, JSCompletionEngine.OPTION_NONE); c.complete(module, position, 0); if (!compareProposalNames(results, compNames)) { assertEquals(new StringList(compNames).sort().toString(), exractProposalNames(results, false).sort().toString()); } } protected void testWithKinds(IModuleSource module, int position, String[] compNames) { List<CompletionProposal> results = new ArrayList<CompletionProposal>(); ICompletionEngine c = createEngine(results, JSCompletionEngine.OPTION_NONE); c.complete(module, position, 0); if (!compareProposalNames(results, compNames)) { assertEquals(new StringList(compNames).sort().toString(), exractProposalNames(results, true).sort().toString()); } } private static Type getType(String typeName) { return TypeInfoModelLoader.getInstance().getType(typeName, true); } private static List<String> loadMembers(final String typeName, MemberPredicate predicate) { final List<String> names = new ArrayList<String>(); final Type type = getType(typeName); for (Member member : new TypeMemberQuery(type, predicate)) { if (!names.contains(member.getName())) names.add(member.getName()); } return Collections.unmodifiableList(names); } private static final class Key { private final String name; private final Object predicate; public Key(String name, Object predicate) { this.name = name; this.predicate = predicate; } @Override public int hashCode() { return name.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Key) { final Key other = (Key) obj; return name.equals(other.name) && predicate.equals(other.predicate); } return false; } } private static final Map<Key, List<String>> members = new HashMap<Key, List<String>>(); public static List<String> getMembers(String typeName, MemberPredicate predicate) { final Key key = new Key(typeName, predicate); List<String> m = members.get(key); if (m == null) { m = loadMembers(typeName, predicate); members.put(key, m); } return m; } protected static List<String> getMembersOfObject() { return getMembers(ITypeNames.OBJECT, MemberPredicates.NON_STATIC); } protected static List<String> getMembersOfArray() { return getMembers(ITypeNames.ARRAY, MemberPredicates.NON_STATIC); } protected static List<String> getMembersOfFunction() { return getMembers(ITypeNames.FUNCTION, MemberPredicates.NON_STATIC); } protected static List<String> getMembersOfNumber() { return getMembers(ITypeNames.NUMBER, MemberPredicates.NON_STATIC); } protected static List<String> getMembersOfString() { return getMembers(ITypeNames.STRING, MemberPredicates.NON_STATIC); } protected static List<String> getMembersOfXML() { return getMembers(ITypeNames.XML, MemberPredicates.NON_STATIC); } protected static String[] concat(List<String> values, String... addition) { List<String> result = new ArrayList<String>(values.size() + addition.length); result.addAll(values); Collections.addAll(result, addition); return result.toArray(new String[result.size()]); } protected static String[] concat(List<String> a, List<String> b, String... addition) { List<String> result = new ArrayList<String>(a.size() + b.size() + addition.length); result.addAll(a); result.addAll(b); Collections.addAll(result, addition); return result.toArray(new String[result.size()]); } }