blob: bf74cd8db8261c0c0cf995e028e0ddaac16d59c2 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2017 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.internal.tmf.core.model.xy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.core.model.TmfXyResponseFactory;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
import org.eclipse.tracecompass.internal.tmf.core.model.tree.TmfTreeCompositeDataProvider;
import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderManager;
import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfCommonXAxisModel;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfTreeXYDataProvider;
import org.eclipse.tracecompass.tmf.core.model.xy.ITmfXyModel;
import org.eclipse.tracecompass.tmf.core.model.xy.IYModel;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
/**
* Represents a base implementation of {@link ITmfTreeXYDataProvider} that
* supports experiments. Clients of this data provider must provide a list of
* {@link ITmfTreeXYDataProvider} for each trace in the experiment which
* supports the provider. From the list of sub data provider, this data provider
* will merge all responses into one.
*
* @param <M>
* The type of {@link ITmfTreeDataModel} that this composite's tree
* provider must return.
* @param <P>
* The type of {@link ITmfTreeXYDataProvider} that this composite
* must encapsulate
* @author Yonni Chen
* @since 4.0
*/
public class TmfTreeXYCompositeDataProvider<M extends ITmfTreeDataModel, P extends ITmfTreeXYDataProvider<M>>
extends TmfTreeCompositeDataProvider<M, P> implements ITmfTreeXYDataProvider<M> {
private final String fTitle;
/**
* Constructor
*
* @param providers
* A factory that creates a list of data provider. Each data provider
* should be associated to a different trace.
* @param title
* Chart's title
* @param id
* the provider's ID
*/
public TmfTreeXYCompositeDataProvider(List<P> providers, String title, String id) {
super(providers, id);
fTitle = title;
}
/**
* Return a composite {@link ITmfTreeXYDataProvider} from a list of traces.
*
* @param traces
* A list of traces from which to generate a provider.
* @param title
* Chart's title
* @param id
* the provider's ID
* @return null if the non of the traces returns a provider, the provider if the
* lists only return one, else a {@link TmfTreeXYCompositeDataProvider}
* encapsulating the providers
*/
public static @Nullable ITmfTreeXYDataProvider<ITmfTreeDataModel> create(Collection<ITmfTrace> traces, String title, String id) {
return create(traces, title, id, null);
}
/**
* Return a composite {@link ITmfTreeXYDataProvider} from a list of traces.
*
* @param traces
* A list of traces from which to generate a provider.
* @param title
* Chart's title
* @param id
* the provider's ID
* @param secondaryId
* The provider's secondaryId
* @return null if the non of the traces returns a provider, the provider if the
* lists only return one, else a {@link TmfTreeXYCompositeDataProvider}
* encapsulating the providers
*/
public static @Nullable ITmfTreeXYDataProvider<ITmfTreeDataModel> create(Collection<ITmfTrace> traces, String title, String id, @Nullable String secondaryId) {
String providerId = secondaryId == null ? id : id + ':' + secondaryId;
List<@NonNull ITmfTreeXYDataProvider<ITmfTreeDataModel>> providers = new ArrayList<>();
for (ITmfTrace child : traces) {
ITmfTreeXYDataProvider<ITmfTreeDataModel> provider = DataProviderManager.getInstance().getDataProvider(child, providerId, ITmfTreeXYDataProvider.class);
if (provider != null) {
providers.add(provider);
}
}
if (providers.isEmpty()) {
return null;
} else if (providers.size() == 1) {
return providers.get(0);
}
return new TmfTreeXYCompositeDataProvider<>(providers, title, providerId);
}
@Deprecated
@Override
public TmfModelResponse<ITmfXyModel> fetchXY(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
Map<String, Object> parameters = FetchParametersUtils.timeQueryToMap(filter);
return fetchXY(parameters, monitor);
}
@Override
public TmfModelResponse<ITmfXyModel> fetchXY(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
/**
* <pre>
* Response status according to the provider's reponse statuses:
*
* * Cancelled -> The monitor is cancelled
* * Failed -> At least one provider has failed
* * Running -> At least one of the providers is running
* * Completed -> All providers have completed
* </pre>
*/
List<P> providers = getProviders();
// Get all the responses
Collection<TmfModelResponse<ITmfXyModel>> responses = getXyResponses(fetchParameters, monitor, providers);
if (monitor != null && monitor.isCanceled()) {
return TmfXyResponseFactory.createCancelledResponse(CommonStatusMessage.TASK_CANCELLED);
}
// If one response is failed, return a failed response with a concatenation of the messages
String failedMsg = handleFailedStatus(responses);
if (failedMsg != null) {
return TmfXyResponseFactory.createFailedResponse(failedMsg);
}
boolean allCommon = Iterables.all(providers, ITmfCommonXAxisModel.class::isInstance);
// The query is considered complete if all providers are completed
boolean isComplete = Iterables.all(responses, response -> response.getStatus() == ITmfResponse.Status.COMPLETED);
if (allCommon) {
ImmutableMap.Builder<String, IYModel> series = ImmutableMap.builder();
responses.forEach(response -> {
ITmfCommonXAxisModel model = (ITmfCommonXAxisModel) response.getModel();
if (model != null) {
series.putAll(model.getYData());
}
});
TimeQueryFilter filter = FetchParametersUtils.createTimeQuery(fetchParameters);
if (filter == null) {
return TmfXyResponseFactory.createFailedResponse(CommonStatusMessage.INCORRECT_QUERY_PARAMETERS);
}
return TmfXyResponseFactory.create(fTitle, filter.getTimesRequested(), series.build(), isComplete);
}
ImmutableMap.Builder<String, ISeriesModel> series = ImmutableMap.builder();
responses.forEach(response -> {
ITmfXyModel model = response.getModel();
if (model != null) {
series.putAll(model.getData());
}
});
return TmfXyResponseFactory.create(fTitle, series.build(), isComplete);
}
private static @Nullable String handleFailedStatus(Collection<TmfModelResponse<ITmfXyModel>> responses) {
if (Iterables.any(responses, response -> response.getStatus() == ITmfResponse.Status.FAILED)) {
// All requests have failed, return a concatenation of their errors
return responses.stream().map(TmfModelResponse::getStatusMessage)
.collect(Collectors.joining("\n")); //$NON-NLS-1$
}
// At least one is good, return null
return null;
}
private Collection<TmfModelResponse<ITmfXyModel>> getXyResponses(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor, List<P> providers) {
List<TmfModelResponse<ITmfXyModel>> responses = new ArrayList<>();
for (P dataProvider : providers) {
TmfModelResponse<ITmfXyModel> response = dataProvider.fetchXY(fetchParameters, monitor);
responses.add(response);
if (monitor != null && monitor.isCanceled()) {
return responses;
}
}
return responses;
}
}