/*******************************************************************************
* Copyright (c) 2016 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.analysis.xml.core.pattern.stateprovider;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import java.io.File;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgressListener;
import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.model.TmfXmlPatternSegmentBuilder;
import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.segment.TmfXmlPatternSegment;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfAnalysisModuleWithStateSystems;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import com.google.common.collect.ImmutableList;
/**
* Analysis module for pattern matching within traces. This module creates two
* sub-analyses : A state system analysis that will execute the pattern on the
* trace and a segment store analysis that will build a segment store with the
* segments generated by the state system analysis.
*
* @author Jean-Christian Kouame
*/
public class XmlPatternAnalysis extends TmfAbstractAnalysisModule implements ITmfAnalysisModuleWithStateSystems, ISegmentStoreProvider {
/**
* Segment store supplementary file extension
*/
public static final @NonNull String SEGMENT_STORE_EXTENSION = ".dat"; //$NON-NLS-1$
/**
* state system supplementary file extension
*/
private static final @NonNull String STATE_SYSTEM_EXTENSION = ".ht"; //$NON-NLS-1$
private static final String SEGMENT_STORE_SUFFIX = " segment store"; //$NON-NLS-1$
private static final String STATE_SYSTEM_SUFFIX = " state system"; //$NON-NLS-1$
private final CountDownLatch fInitialized = new CountDownLatch(1);
private XmlPatternStateSystemModule fStateSystemModule;
private XmlPatternSegmentStoreModule fSegmentStoreModule;
private boolean fInitializationSucceeded;
/**
* Constructor
*/
public XmlPatternAnalysis() {
super();
fSegmentStoreModule = new XmlPatternSegmentStoreModule(this);
fStateSystemModule = new XmlPatternStateSystemModule(fSegmentStoreModule);
}
@Override
public @Nullable ISegmentStore<@NonNull ISegment> getSegmentStore() {
return fSegmentStoreModule.getSegmentStore();
}
@Override
public @Nullable ITmfStateSystem getStateSystem(@NonNull String id) {
return fStateSystemModule.getStateSystem(id);
}
@Override
public @NonNull Iterable<@NonNull ITmfStateSystem> getStateSystems() {
return fStateSystemModule.getStateSystems();
}
@Override
public boolean waitForInitialization() {
try {
fInitialized.await();
} catch (InterruptedException e) {
return false;
}
return fInitializationSucceeded;
}
@Override
protected boolean executeAnalysis(@NonNull IProgressMonitor monitor) throws TmfAnalysisException {
ITmfTrace trace = getTrace();
if (trace == null) {
/* This analysis was cancelled in the meantime */
analysisReady(false);
return false;
}
File segmentStoreFile = getSupplementaryFile(getSegmentStoreFileName());
File stateSystemFile = getSupplementaryFile(getStateSystemFileName());
if (segmentStoreFile == null || stateSystemFile == null) {
analysisReady(false);
return false;
}
if (!segmentStoreFile.exists()) {
fStateSystemModule.cancel();
stateSystemFile.delete();
}
IStatus segmentStoreStatus = fSegmentStoreModule.schedule();
IStatus stateSystemStatus = fStateSystemModule.schedule();
if (!(segmentStoreStatus.isOK() && stateSystemStatus.isOK())) {
cancelSubAnalyses();
analysisReady(false);
return false;
}
/* Wait until the state system module is initialized */
if (!fStateSystemModule.waitForInitialization()) {
analysisReady(false);
cancelSubAnalyses();
return false;
}
ITmfStateSystem stateSystem = fStateSystemModule.getStateSystem();
if (stateSystem == null) {
analysisReady(false);
throw new IllegalStateException("Initialization of the state system module succeeded but the statesystem is null"); //$NON-NLS-1$
}
analysisReady(true);
return fStateSystemModule.waitForCompletion(monitor) && fSegmentStoreModule.waitForCompletion(monitor);
}
@Override
protected void canceling() {
cancelSubAnalyses();
}
private void cancelSubAnalyses() {
fStateSystemModule.cancel();
fSegmentStoreModule.cancel();
}
@Override
public void dispose() {
/*
* The sub-analyses are not registered to the trace directly, so we need
* to tell them when the trace is disposed.
*/
super.dispose();
fStateSystemModule.dispose();
fSegmentStoreModule.dispose();
}
@Override
public void setId(@NonNull String id) {
super.setId(id);
fStateSystemModule.setId(id);
fSegmentStoreModule.setId(id);
}
@Override
public void setName(@NonNull String name) {
super.setName(name);
fStateSystemModule.setName(name + STATE_SYSTEM_SUFFIX);
fSegmentStoreModule.setName(name + SEGMENT_STORE_SUFFIX);
}
@Override
public boolean setTrace(ITmfTrace trace) throws TmfAnalysisException {
if (!super.setTrace(trace)) {
return false;
}
/*
* Since these sub-analyzes are not built from an extension point, we
* have to assign the trace ourselves. Very important to do so before
* calling schedule()!
*/
return fSegmentStoreModule.setTrace(trace) && fStateSystemModule.setTrace(trace);
}
/**
* Sets the file path of the XML file and the id of pattern analysis in the
* file
*
* @param file
* The full path to the XML file
*/
public void setXmlFile(IPath file) {
fStateSystemModule.setXmlFile(file);
}
/**
* Make the module available and set whether the initialization succeeded or
* not. If not, no state system is available and
* {@link #waitForInitialization()} should return false.
*
* @param success
* True if the initialization went well, false otherwise
*/
private void analysisReady(boolean succeeded) {
fInitializationSucceeded = succeeded;
fInitialized.countDown();
}
private @Nullable File getSupplementaryFile(String filename) {
ITmfTrace trace = getTrace();
if (trace == null) {
return null;
}
String directory = TmfTraceManager.getSupplementaryFileDir(trace);
File file = new File(directory + filename);
return file;
}
private String getStateSystemFileName() {
return fStateSystemModule.getId() + STATE_SYSTEM_EXTENSION;
}
private String getSegmentStoreFileName() {
return fSegmentStoreModule.getId() + SEGMENT_STORE_EXTENSION;
}
@Override
public void addListener(@NonNull IAnalysisProgressListener listener) {
fSegmentStoreModule.addListener(listener);
}
@Override
public void removeListener(@NonNull IAnalysisProgressListener listener) {
fSegmentStoreModule.removeListener(listener);
}
@Override
public Iterable<ISegmentAspect> getSegmentAspects() {
return ImmutableList.of(PatternSegmentNameAspect.INSTANCE, PatternSegmentContentAspect.INSTANCE);
}
private static class PatternSegmentNameAspect implements ISegmentAspect {
public static final @NonNull ISegmentAspect INSTANCE = new PatternSegmentNameAspect();
private PatternSegmentNameAspect() {}
@Override
public String getHelpText() {
return checkNotNull(Messages.PatternSegmentNameAspect_HelpText);
}
@Override
public String getName() {
return checkNotNull(Messages.PatternSegmentNameAspect_Name);
}
@Override
public @Nullable Comparator<?> getComparator() {
return null;
}
@Override
public @Nullable String resolve(ISegment segment) {
if (segment instanceof TmfXmlPatternSegment) {
return ((TmfXmlPatternSegment) segment).getName()
.substring(TmfXmlPatternSegmentBuilder.PATTERN_SEGMENT_NAME_PREFIX.length());
}
return EMPTY_STRING;
}
}
private static class PatternSegmentContentAspect implements ISegmentAspect {
public static final @NonNull ISegmentAspect INSTANCE = new PatternSegmentContentAspect();
private PatternSegmentContentAspect() {}
@Override
public String getHelpText() {
return checkNotNull(Messages.PatternSegmentContentAspect_HelpText);
}
@Override
public String getName() {
return checkNotNull(Messages.PatternSegmentContentAspect_Content);
}
@Override
public @Nullable Comparator<?> getComparator() {
return null;
}
@Override
public @Nullable String resolve(ISegment segment) {
if (segment instanceof TmfXmlPatternSegment) {
List<String> values = ((TmfXmlPatternSegment) segment).getContent().entrySet().stream().map(c -> c.getKey() + '=' + c.getValue()).collect(Collectors.toList());
return String.join(", ", values); //$NON-NLS-1$
}
return EMPTY_STRING;
}
}
}