Prefix search types by simple name.

Change-Id: I6e21c04ab774078194c2eb1d83b6ecaccc250398
Signed-off-by: Alexander Rookey <atrookey@google.com>
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java
index f378a33..4940d07 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java
@@ -11,9 +11,12 @@
 package org.eclipse.jdt.core.tests.nd.indexer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Semaphore;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.core.resources.ResourcesPlugin;
@@ -78,7 +81,7 @@
 	 */
 	public void testInterruptedException() throws Exception {
 		createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true);
-		// Create an index
+		// Create an indexfa
 		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
 		Indexer indexer = new Indexer(index.getNd(), root);
 		indexer.rescan(SubMonitor.convert(null));
@@ -140,7 +143,7 @@
 			int type = child.getElementType();
 
 			if (type == IJavaElement.CLASS_FILE) {
-				result.add((IClassFile)child);
+				result.add((IClassFile) child);
 			} else if (child instanceof IParent) {
 				IParent parent = (IParent) child;
 
@@ -211,4 +214,69 @@
 		}
 		assertTrue("No classes found in the index", foundAtLeastOneClass);
 	}
+
+	public void testFindTypesBySimpleName() throws CoreException {
+		createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true);
+		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+		Indexer indexer = new Indexer(index.getNd(), root);
+
+		indexer.rescan(SubMonitor.convert(null));
+
+		try (IReader reader = IndexerTest.index.getNd().acquireReadLock()) {
+			List<Object> javaUtilList = IndexerTest.index.findTypesBySimpleName("ArrayList".toCharArray()).stream()
+					.map(new Function<NdTypeId, String>() {
+						@Override
+						public String apply(NdTypeId typeId) {
+							return typeId.toString();
+						}
+					}).collect(Collectors.toList());
+			System.out.println(javaUtilList);
+			assertTrue("Test failed", javaUtilList.contains("Ljava/util/ArrayList;"));
+		}
+	}
+
+	public void testFindTypesBySimpleNameFirstWord() throws CoreException {
+		createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true);
+		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+		Indexer indexer = new Indexer(index.getNd(), root);
+
+		indexer.rescan(SubMonitor.convert(null));
+
+		try (IReader reader = IndexerTest.index.getNd().acquireReadLock()) {
+			List<Object> javaUtilList = IndexerTest.index.findTypesBySimpleName("Array".toCharArray()).stream()
+					.map(new Function<NdTypeId, String>() {
+						@Override
+						public String apply(NdTypeId typeId) {
+							return typeId.toString();
+						}
+					}).collect(Collectors.toList());
+			System.out.println(javaUtilList);
+			assertTrue("Test failed",
+					javaUtilList.containsAll(Arrays.asList("Ljava/sql/Array;", "Ljava/lang/reflect/Array;",
+							"Ljava/util/concurrent/ArrayBlockingQueue;", "Ljava/util/ArrayDeque;",
+							"Ljava/lang/ArrayIndexOutOfBoundsException;", "Ljava/util/ArrayList;",
+							"Ljava/util/ArrayPrefixHelpers;", "Ljava/util/Arrays;",
+							"Ljava/util/ArraysParallelSortHelpers;", "Ljava/lang/ArrayStoreException;")));
+		}
+	}
+
+	public void testFindTypesBySimpleNameFirstLetterCount10() throws CoreException {
+		createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true);
+		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+		Indexer indexer = new Indexer(index.getNd(), root);
+
+		indexer.rescan(SubMonitor.convert(null));
+
+		try (IReader reader = IndexerTest.index.getNd().acquireReadLock()) {
+			List<Object> javaUtilList = IndexerTest.index.findTypesBySimpleName("A".toCharArray(), 10).stream()
+					.map(new Function<NdTypeId, String>() {
+						@Override
+						public String apply(NdTypeId typeId) {
+							return typeId.toString();
+						}
+					}).collect(Collectors.toList());
+			System.out.println(javaUtilList);
+			assertTrue("Test failed", javaUtilList.size() == 10);
+		}
+	}
 }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java
index f265fcf..c7f6a3a 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java
@@ -274,6 +274,23 @@
 		return result;
 	}
 
+	public List<T> findAll(final Nd nd, long address, final SearchCriteria searchCriteria, final int count) {
+		final List<T> result = new ArrayList<T>();
+		get(nd, address).accept(new SearchCriteriaToBtreeVisitorAdapter(searchCriteria, nd) {
+
+			int remainingCount = count;
+
+			@SuppressWarnings("unchecked")
+			@Override
+			protected boolean acceptResult(long resultAddress) {
+				result.add((T) NdNode.load(nd, resultAddress));
+				this.remainingCount--;
+				return this.remainingCount > 0;
+			}
+		});
+		return result;
+	}
+
 	/**
 	 * Returns the entire contents of the index as a single list.
 	 */
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java
index 778d5da..35af94f 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java
@@ -131,6 +131,16 @@
 		return TYPES.findBest(this.nd, this.address, searchCriteria, this.anyResult);
 	}
 
+	public List<NdTypeId> findTypesBySimpleName(char[] query) {
+		SearchCriteria searchCriteria = SearchCriteria.create(query).prefix(true);
+		return SIMPLE_INDEX.findAll(this.nd, this.address, searchCriteria);
+	}
+
+	public List<NdTypeId> findTypesBySimpleName(char[] query, int count) {
+		SearchCriteria searchCriteria = SearchCriteria.create(query).prefix(true);
+		return SIMPLE_INDEX.findAll(this.nd, this.address, searchCriteria, count);
+	}
+
 	public boolean visitFieldDescriptorsStartingWith(char[] fieldDescriptorPrefix, FieldSearchIndex.Visitor<NdTypeId> visitor) {
 		SearchCriteria searchCriteria = SearchCriteria.create(fieldDescriptorPrefix).prefix(true);
 		return TYPES.visitAll(this.nd, this.address, searchCriteria, visitor);