/* * Copyright (c) 2014, 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.core.internal.analysis.model; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.dart.server.generated.AnalysisServer; import com.google.dart.tools.core.DartCore; import com.google.dart.tools.core.DartCoreDebug; import com.google.dart.tools.core.analysis.model.AnalysisServerData; import com.google.dart.tools.core.analysis.model.AnalysisServerHighlightsListener; import com.google.dart.tools.core.analysis.model.AnalysisServerLaunchDataListener; import com.google.dart.tools.core.analysis.model.AnalysisServerNavigationListener; import com.google.dart.tools.core.analysis.model.AnalysisServerOccurrencesListener; import com.google.dart.tools.core.analysis.model.AnalysisServerOutlineListener; import com.google.dart.tools.core.analysis.model.AnalysisServerOverridesListener; import com.google.dart.tools.core.analysis.model.SearchResultsListener; import org.dartlang.analysis.server.protocol.AnalysisError; import org.dartlang.analysis.server.protocol.AnalysisOptions; import org.dartlang.analysis.server.protocol.AnalysisStatus; import org.dartlang.analysis.server.protocol.ExecutionService; import org.dartlang.analysis.server.protocol.HighlightRegion; import org.dartlang.analysis.server.protocol.NavigationRegion; import org.dartlang.analysis.server.protocol.Occurrences; import org.dartlang.analysis.server.protocol.Outline; import org.dartlang.analysis.server.protocol.OverrideMember; import org.dartlang.analysis.server.protocol.SearchResult; import java.util.List; import java.util.Map; import java.util.Set; /** * Instances of {@code AnalysisServerData} manage and provide access to analysis results reported by * {@link AnalysisServer}. * * @coverage dart.tools.core.model */ public class AnalysisServerDataImpl implements AnalysisServerData { private boolean isAnalyzing = false; private final Map<String, Set<AnalysisServerHighlightsListener>> highlightsSubscriptions = Maps.newHashMap(); private final Map<String, Set<AnalysisServerNavigationListener>> navigationSubscriptions = Maps.newHashMap(); private final Map<String, Set<AnalysisServerOccurrencesListener>> occurrencesSubscriptions = Maps.newHashMap(); private final Map<String, Set<AnalysisServerOutlineListener>> outlineSubscriptions = Maps.newHashMap(); private final Map<String, Set<AnalysisServerOverridesListener>> overridesSubscriptions = Maps.newHashMap(); private final Map<String, AnalysisError[]> errorData = Maps.newHashMap(); private final Map<String, NavigationRegion[]> navigationData = Maps.newHashMap(); private final Map<String, Occurrences[]> occurrencesData = Maps.newHashMap(); private final Map<String, SearchResultsListener> searchResultsListeners = Maps.newHashMap(); private final Map<String, List<SearchResultsSet>> searchResultsData = Maps.newHashMap(); private final List<String> executionSubscriptions = Lists.newArrayList(); private final List<AnalysisServerLaunchDataListener> launchDataListeners = Lists.newArrayList(); private AnalysisServer server; @Override public void addHighlightsListener(String file, AnalysisServerHighlightsListener listener) { Set<AnalysisServerHighlightsListener> subscriptions = highlightsSubscriptions.get(file); if (subscriptions == null) { subscriptions = Sets.newHashSet(); highlightsSubscriptions.put(file, subscriptions); } subscriptions.add(listener); } @Override public void addNavigationListener(String file, AnalysisServerNavigationListener listener) { Set<AnalysisServerNavigationListener> subscriptions = navigationSubscriptions.get(file); if (subscriptions == null) { subscriptions = Sets.newHashSet(); navigationSubscriptions.put(file, subscriptions); } subscriptions.add(listener); } @Override public void addOccurrencesListener(String file, AnalysisServerOccurrencesListener listener) { Set<AnalysisServerOccurrencesListener> subscriptions = occurrencesSubscriptions.get(file); if (subscriptions == null) { subscriptions = Sets.newHashSet(); occurrencesSubscriptions.put(file, subscriptions); } subscriptions.add(listener); } @Override public void addOutlineListener(String file, AnalysisServerOutlineListener listener) { Set<AnalysisServerOutlineListener> subscriptions = outlineSubscriptions.get(file); if (subscriptions == null) { subscriptions = Sets.newHashSet(); outlineSubscriptions.put(file, subscriptions); } subscriptions.add(listener); } @Override public void addOverridesListener(String file, AnalysisServerOverridesListener listener) { Set<AnalysisServerOverridesListener> subscriptions = overridesSubscriptions.get(file); if (subscriptions == null) { subscriptions = Sets.newHashSet(); overridesSubscriptions.put(file, subscriptions); } subscriptions.add(listener); } @Override public synchronized void addSearchResultsListener(String searchId, SearchResultsListener listener) { List<SearchResultsSet> resultsSets = searchResultsData.remove(searchId); boolean hasLast = false; if (resultsSets != null) { for (SearchResultsSet searchResultsSet : resultsSets) { listener.computedSearchResults(searchResultsSet.results, searchResultsSet.last); hasLast |= searchResultsSet.last; } } if (!hasLast) { searchResultsListeners.put(searchId, listener); } } @Override public AnalysisError[] getErrors(String file) { AnalysisError[] errors = errorData.get(file); if (errors == null) { return AnalysisError.EMPTY_ARRAY; } return errors; } @Override public NavigationRegion[] getNavigation(String file) { NavigationRegion[] sourceRegions = navigationData.get(file); if (sourceRegions == null) { return NavigationRegion.EMPTY_ARRAY; } return sourceRegions; } @Override public Occurrences[] getOccurrences(String file) { Occurrences[] occurrencesArray = occurrencesData.get(file); if (occurrencesArray == null) { return Occurrences.EMPTY_ARRAY; } return occurrencesArray; } @Override public boolean isAnalyzing() { return isAnalyzing; } @Override public void removeHighlightsListener(String file, AnalysisServerHighlightsListener listener) { Set<AnalysisServerHighlightsListener> subscriptions = highlightsSubscriptions.get(file); if (subscriptions == null) { return; } if (subscriptions.remove(listener)) { if (subscriptions.isEmpty()) { highlightsSubscriptions.remove(file); } } } @Override public void removeNavigationListener(String file, AnalysisServerNavigationListener listener) { Set<AnalysisServerNavigationListener> subscriptions = navigationSubscriptions.get(file); if (subscriptions == null) { return; } if (subscriptions.remove(listener)) { if (subscriptions.isEmpty()) { navigationSubscriptions.remove(file); } } } @Override public void removeOccurrencesListener(String file, AnalysisServerOccurrencesListener listener) { Set<AnalysisServerOccurrencesListener> subscriptions = occurrencesSubscriptions.get(file); if (subscriptions == null) { return; } if (subscriptions.remove(listener)) { if (subscriptions.isEmpty()) { occurrencesSubscriptions.remove(file); } } } @Override public void removeOutlineListener(String file, AnalysisServerOutlineListener listener) { Set<AnalysisServerOutlineListener> subscriptions = outlineSubscriptions.get(file); if (subscriptions == null) { return; } if (subscriptions.remove(listener)) { if (subscriptions.isEmpty()) { outlineSubscriptions.remove(file); } } } @Override public void removeOverridesListener(String file, AnalysisServerOverridesListener listener) { Set<AnalysisServerOverridesListener> subscriptions = overridesSubscriptions.get(file); if (subscriptions == null) { return; } if (subscriptions.remove(listener)) { if (subscriptions.isEmpty()) { overridesSubscriptions.remove(file); } } } @Override public void removeSearchResultsListener(String searchId, SearchResultsListener listener) { searchResultsData.remove(searchId); searchResultsListeners.remove(searchId); } /** * Sets the {@link AnalysisServer} to talk to. */ public void setServer(AnalysisServer server) { this.server = server; } @Override public synchronized void subscribeLaunchData(AnalysisServerLaunchDataListener listener) { if (launchDataListeners.add(listener)) { if (executionSubscriptions.add(ExecutionService.LAUNCH_DATA)) { executionSubscriptions.add(ExecutionService.LAUNCH_DATA); if (server != null) { server.execution_setSubscriptions(executionSubscriptions); } } } } @Override public void unsubscribeLaunchData(AnalysisServerLaunchDataListener listener) { if (launchDataListeners.remove(listener)) { if (executionSubscriptions.remove(ExecutionService.LAUNCH_DATA)) { server.execution_setSubscriptions(executionSubscriptions); } } } @Override public void updateOptions() { server.analysis_updateOptions(new AnalysisOptions( true /* async */, true /* deferred loading */, true /* enums */, DartCoreDebug.ENABLE_NULL_AWARE_OPERATORS, true /* enableSuperMixins */, DartCore.getPlugin().isHintsDart2JSEnabled(), DartCore.getPlugin().isHintsEnabled(), DartCore.getPlugin().isLintsEnabled())); } void internalComputedErrors(String file, AnalysisError[] errors) { errorData.put(file, errors); } void internalComputedHighlights(String file, HighlightRegion[] highlights) { Set<AnalysisServerHighlightsListener> subscriptions = highlightsSubscriptions.get(file); if (subscriptions == null) { return; } subscriptions = ImmutableSet.copyOf(subscriptions); for (AnalysisServerHighlightsListener listener : subscriptions) { listener.computedHighlights(file, highlights); } } void internalComputedLaunchData(String file, String kind, String[] referencedFiles) { List<AnalysisServerLaunchDataListener> listeners = launchDataListeners; listeners = ImmutableList.copyOf(listeners); for (AnalysisServerLaunchDataListener listener : listeners) { listener.computedLaunchData(file, kind, referencedFiles); } } void internalComputedNavigation(String file, NavigationRegion[] targets) { navigationData.put(file, targets); Set<AnalysisServerNavigationListener> subscriptions = navigationSubscriptions.get(file); if (subscriptions != null) { subscriptions = ImmutableSet.copyOf(subscriptions); for (AnalysisServerNavigationListener listener : subscriptions) { listener.computedNavigation(file, targets); } } } void internalComputedOccurrences(String file, Occurrences[] occurrences) { occurrencesData.put(file, occurrences); Set<AnalysisServerOccurrencesListener> subscriptions = occurrencesSubscriptions.get(file); if (subscriptions != null) { subscriptions = ImmutableSet.copyOf(subscriptions); for (AnalysisServerOccurrencesListener listener : subscriptions) { listener.computedOccurrences(file, occurrences); } } } void internalComputedOutline(String file, Outline outline) { Set<AnalysisServerOutlineListener> subscriptions = outlineSubscriptions.get(file); if (subscriptions == null) { return; } subscriptions = ImmutableSet.copyOf(subscriptions); for (AnalysisServerOutlineListener listener : subscriptions) { listener.computedOutline(file, outline); } } void internalComputedOverrides(String file, OverrideMember[] overrides) { Set<AnalysisServerOverridesListener> subscriptions = overridesSubscriptions.get(file); if (subscriptions == null) { return; } subscriptions = ImmutableSet.copyOf(subscriptions); for (AnalysisServerOverridesListener listener : subscriptions) { listener.computedHighlights(file, overrides); } } synchronized void internalComputedSearchResults(String searchId, List<SearchResult> results, boolean last) { SearchResultsListener listener = searchResultsListeners.get(searchId); if (listener != null) { if (last) { searchResultsListeners.remove(searchId); } listener.computedSearchResults(results, last); } } /** * Clears all information associated with the given files. */ void internalFlushResults(List<String> files) { for (String file : files) { highlightsSubscriptions.remove(file); outlineSubscriptions.remove(file); overridesSubscriptions.remove(file); errorData.remove(file); navigationData.remove(file); occurrencesData.remove(file); } } void internalServerStatus(AnalysisStatus status) { isAnalyzing = status != null && status.isAnalyzing(); } }