package com.cadrlife.devsearch.agent.service.analysis;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class JavaReferenceFinder {
private static final Logger LOG = LoggerFactory.getLogger(JavaReferenceFinder.class);
static final String DOC_INDEX = "doc";
private Client esClient;
private String codeIndex;
TimeValue keepAlive = new TimeValue(5, TimeUnit.MINUTES);
Cache<String, Set<String>> classNamesByPackageCache = CacheBuilder.newBuilder().maximumSize(30).build();
@Inject
public JavaReferenceFinder(Client esClient, @Named("elasticsearch.code.index") String codeIndex) {
this.esClient = esClient;
this.codeIndex = codeIndex;
}
public void populateReferences(JavaAnalysis analysis, String content) {
extractImportsToReferences((Iterable) analysis.getImports(), analysis.getReferences());
addSamePackageReferences(analysis.getJavaPackage(), analysis.getName(), content, analysis.getReferences());
}
void addSamePackageReferences(final String javaPackage, String javaName, final String contents, final List<String> references) {
// System.out.println("finding all classes with package '" + javaPackage + "'");
try {
Set<String> classNames = classNamesByPackageCache.get(javaPackage, new Callable<Set<String>>() {
@Override
public Set<String> call() throws Exception {
// System.out.println("Cache miss");
Set<String> classNames = new HashSet<>();
SearchResponse response = esClient.prepareSearch(codeIndex).addField("javaName").setSearchType(SearchType.SCAN).setTypes(DOC_INDEX)
.setQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("javaPackage", javaPackage))
).setSize(50).setScroll(keepAlive).execute().actionGet();
while (true) {
response = esClient.prepareSearchScroll(response.getScrollId()).setScroll(keepAlive).execute().actionGet();
for (SearchHit hit : response.getHits()) {
String javaName = hit.field("javaName").getValue();
classNames.add(javaName);
// System.out.println(" " + javaName);
// references.add(IdUtil.docRef(javaPackage + "." + javaName, hit.getId()));
}
if (response.getHits().getHits().length == 0) {
break;
}
}
return classNames;
}
});
for (String className : classNames) {
if (!javaName.equals(className) && contents.matches("(?s).*\\b" + className + "\\b.*")) {
// System.out.println("*** MATCH " + className + " used by " + javaName);
references.add(javaRef(javaPackage, className));
}
}
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
private String javaRef(String javaPackage, String className) {
return javaPackage + "." + className;
}
void extractImportsToReferences(Iterable javaImports, List<String> references) {
MultiSearchRequestBuilder multiSearchRequest = esClient.prepareMultiSearch();
List<String> potentialJavaRefs = new ArrayList<>();
for (Object javaImport : javaImports) {
String[] pieces = javaImport.toString().split("\\.");
String javaName = pieces[pieces.length-1];
try {
String javaPackage = javaImport.toString().substring(0, javaImport.toString().length() - javaName.length() - 1);
// System.out.println("Name " + javaName + " pak " + javaPackage);
potentialJavaRefs.add(javaRef(javaPackage, javaName));
multiSearchRequest.add(createJavaRefSearch(javaPackage, javaName));
} catch (StringIndexOutOfBoundsException e) {
System.out.println("Failed to analyze java import " + javaImport);
}
}
if (!potentialJavaRefs.isEmpty()) {
MultiSearchResponse.Item[] responseItems = multiSearchRequest.execute().actionGet().getResponses();
for (int i = 0; i < responseItems.length; i++) {
String potentialJavaRef = potentialJavaRefs.get(i);
if (responseItems[i].isFailure()) {
LOG.error("Search for java ref {}: {}", potentialJavaRef, responseItems[i].getFailureMessage());
} else if (responseItems[i].getResponse().getHits().getTotalHits() > 0) {
references.add(potentialJavaRef);
}
}
}
}
private SearchRequestBuilder createJavaRefSearch(String javaPackage, String javaName) {
// String javaRef = javaRef(javaPackage, javaName);
return esClient.prepareSearch(codeIndex).setTypes(DOC_INDEX)
.setQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.termQuery("javaPackage", javaPackage))
.must(QueryBuilders.termQuery("javaName", javaName))
).setNoFields().setSize(0);
// return searchRequestBuilder;
}
// MultiSearchResponse multiSearchResponse = esClient.prepareMultiSearch().add(search).execute().actionGet();
//
// SearchResponse response = .execute().actionGet();
// for (SearchHit hit : response.getHits()) {
//
// references.add(java);
// }
// }
}