/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.omni.elements;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.dart.server.FindTopLevelDeclarationsConsumer;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.analysis.model.SearchResultsListener;
import com.google.dart.tools.ui.omni.OmniElement;
import com.google.dart.tools.ui.omni.OmniProposalProvider;
import com.google.dart.tools.ui.omni.util.CamelUtil;
import org.apache.commons.lang3.StringUtils;
import org.dartlang.analysis.server.protocol.RequestError;
import org.dartlang.analysis.server.protocol.SearchResult;
import org.eclipse.core.runtime.IProgressMonitor;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* Provider for class elements.
*/
public class TopLevelElementProvider_NEW extends OmniProposalProvider {
/**
* Place holder to indicate that a search is still in progress.
*/
public final class SearchInProgressPlaceHolder extends HeaderElement {
private SearchInProgressPlaceHolder(OmniProposalProvider provider) {
super(provider);
}
@Override
public void execute(String text) {
//no-op
}
@Override
public String getId() {
return "";
}
@Override
public String getLabel() {
return " searching...";
}
}
private static final long WAIT_MS = 20;
private static String getIdentifierCharacters(String str) {
int length = str.length();
StringBuilder buf = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
if (Character.isJavaIdentifierPart(c)) {
buf.append(c);
}
}
return buf.toString();
}
private final Object lock = new Object();
private final List<OmniElement> results = Lists.newArrayList();
private String currentRequestPattern = null;
private CountDownLatch currentRequestLatch;
private boolean searchComplete;
private final List<ProviderCompleteListener> listeners = Lists.newArrayList();
public TopLevelElementProvider_NEW(IProgressMonitor progressMonitor) {
}
@Override
public OmniElement getElementForId(String id) {
OmniElement[] elements = getElements(id);
if (elements.length == 0) {
return null;
}
return elements[0];
}
@Override
public OmniElement[] getElements(String pattern) {
if (StringUtils.isBlank(pattern)) {
return new OmniElement[0];
}
// initiate a new request, if a new pattern
if (!StringUtils.equals(currentRequestPattern, pattern)) {
currentRequestPattern = pattern;
currentRequestLatch = new CountDownLatch(1);
doSearch();
// wait a little, if results are ready quickly
Uninterruptibles.awaitUninterruptibly(currentRequestLatch, WAIT_MS, TimeUnit.MILLISECONDS);
}
// return current results
synchronized (lock) {
return results.toArray(new OmniElement[results.size()]);
}
}
@Override
public String getId() {
return getClass().getName();
}
@Override
public String getName() {
return "Top-level declarations";
}
/**
* Check if search is complete.
*/
public boolean isSearchComplete() {
return searchComplete;
}
/**
* If the current search is not complete yet, the given listener will be notified when the search
* is complete.
*/
public void onComplete(ProviderCompleteListener listener) {
synchronized (lock) {
if (searchComplete) {
return;
}
}
synchronized (listeners) {
listeners.add(listener);
}
}
private void doSearch() {
String text = getIdentifierCharacters(currentRequestPattern);
final String pattern = "^" + CamelUtil.getCamelCaseRegExp(text) + ".*";
final Pattern patternObj = Pattern.compile(pattern);
//
searchComplete = false;
final List<OmniElement> newResults = Lists.newArrayList();
final CountDownLatch latch = currentRequestLatch;
DartCore.getAnalysisServer().search_findTopLevelDeclarations(
pattern,
new FindTopLevelDeclarationsConsumer() {
@Override
public void computedSearchId(String searchId) {
DartCore.getAnalysisServerData().addSearchResultsListener(
searchId,
new SearchResultsListener() {
@Override
public void computedSearchResults(List<SearchResult> searchResults, boolean last) {
if (latch == currentRequestLatch) {
for (SearchResult searchResult : searchResults) {
newResults.add(new TopLevelElement_NEW(
TopLevelElementProvider_NEW.this,
patternObj,
searchResult));
}
if (last) {
synchronized (lock) {
results.clear();
results.addAll(newResults);
searchComplete = true;
currentRequestLatch.getCount();
}
synchronized (listeners) {
for (ProviderCompleteListener listener : listeners) {
listener.complete(TopLevelElementProvider_NEW.this);
}
listeners.clear();
}
}
}
}
});
}
@Override
public void onError(RequestError requestError) {
if (latch == currentRequestLatch) {
synchronized (results) {
results.clear();
searchComplete = true;
currentRequestLatch.getCount();
}
synchronized (listeners) {
for (ProviderCompleteListener listener : listeners) {
listener.complete(TopLevelElementProvider_NEW.this);
}
listeners.clear();
}
}
}
});
}
}