blob: 568199453c7a19147f84092121d13d2902229b38 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 École Polytechnique de Montréal
*
* 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.filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser.FilterCu;
import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser.IFilterStrings;
import org.eclipse.tracecompass.internal.tmf.core.Activator;
import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
import org.eclipse.tracecompass.tmf.core.event.aspect.TmfBaseAspects;
import org.eclipse.tracecompass.tmf.core.event.aspect.TmfEventFieldAspect;
import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAndNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterAspectNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterCompareNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterContainsNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterEqualsNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterMatchesNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterOrNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode;
import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterTreeNode;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
/**
* Helper class to convert to/from {@link ITmfFilter} and filter regexes
*
* @author Geneviève Bastien
*/
@NonNullByDefault
public final class TmfFilterHelper {
private static final String SPACE = " "; //$NON-NLS-1$
private static final String QUOTE = "\""; //$NON-NLS-1$
private TmfFilterHelper() {
// nothing to do
}
/**
* Build an event filter from the regex string in parameter
*
* @param regexes
* The filter regex
* @param trace
* The trace this filter applies to
* @return An event filter
*/
public static @Nullable ITmfFilter buildFilterFromRegex(Collection<String> regexes, ITmfTrace trace) {
FilterCu compile = FilterCu.compile(IFilterStrings.mergeFilters(regexes));
if (compile == null) {
Activator.logInfo("buildFilterFromRegex: Invalid regex"); //$NON-NLS-1$
return null;
}
return compile.getEventFilter(trace);
}
/**
* Get the regex that corresponds to this filter. The regex should be in the
* filter language described in the
* {@link org.eclipse.tracecompass.tmf.filter.parser} plugin. And as it may
* be used to filter anything, so it may not be the direct string
* representing of the original filter. For instance, a ITmfFilter specific
* for events will do a smart conversion, so that the parameters of the
* filter do not match only events, but are generic enough to match any data
* source.
*
* The default implementation of this method returns a regex that will
* return true at all time.
*
* @param filter
* The filter object to convert to regex
*
* @return The regex String, using the
* {@link org.eclipse.tracecompass.tmf.filter.parser} syntax
* @since 4.1
*/
public static String getRegexFromFilter(ITmfFilter filter) {
if (filter instanceof TmfFilterRootNode) {
return getRegexFromRootNode((TmfFilterRootNode) filter);
}
if (filter instanceof TmfFilterAndNode) {
return getRegexFromAndNode((TmfFilterAndNode) filter);
}
if (filter instanceof TmfFilterOrNode) {
return getRegexFromOrNode((TmfFilterOrNode) filter);
}
if (filter instanceof TmfFilterCompareNode) {
return getRegexFromCompareNode((TmfFilterCompareNode) filter);
}
if (filter instanceof TmfFilterContainsNode) {
return getRegexFromContainsNode((TmfFilterContainsNode) filter);
}
if (filter instanceof TmfFilterEqualsNode) {
return getRegexFromEqualsNode((TmfFilterEqualsNode) filter);
}
if (filter instanceof TmfFilterMatchesNode) {
return getRegexFromMatchesNode((TmfFilterMatchesNode) filter);
}
if (filter instanceof TmfFilterNode) {
return joinChildrenRegex((TmfFilterNode) filter, IFilterStrings.AND);
}
// Return an empty regex by default
return ""; //$NON-NLS-1$
}
private static String getRegexFromMatchesNode(TmfFilterMatchesNode filter) {
// Special case if the filter value is a wildcard, maps to present regex
if (filter.getRegex().equals(".*")) { //$NON-NLS-1$
String regex = quote(getFilterAspectName(filter)) + SPACE + IFilterStrings.PRESENT;
return filter.isNot() ? not(regex) : regex;
}
String regex = quote(getFilterAspectName(filter)) + SPACE + IFilterStrings.MATCHES + SPACE + quote(filter.getRegex());
return filter.isNot() ? not(regex) : regex;
}
private static String getRegexFromEqualsNode(TmfFilterEqualsNode filter) {
return quote(getFilterAspectName(filter)) + SPACE + (filter.isNot() ? IFilterStrings.NOT_EQUAL : IFilterStrings.EQUAL) + SPACE + quote(filter.getValue());
}
private static String getRegexFromContainsNode(TmfFilterContainsNode filter) {
String regex = quote(getFilterAspectName(filter)) + SPACE + IFilterStrings.CONTAINS + SPACE + quote(filter.getValue());
return filter.isNot() ? not(regex) : regex;
}
private static String getRegexFromCompareNode(TmfFilterCompareNode filter) {
String regex = quote(getFilterAspectName(filter)) + SPACE + getCompareSign(filter.getResult()) + SPACE + quote(filter.getValue());
return filter.isNot() ? not(regex) : regex;
}
private static String not(String string) {
return "!(" + string + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
private static String quote(@Nullable String string) {
if (string == null) {
throw new NullPointerException("unexpected null string in the filter"); //$NON-NLS-1$
}
return QUOTE + string + QUOTE;
}
private static String getCompareSign(int result) {
if (result < 0) {
return IFilterStrings.LT;
}
if (result == 0) {
return IFilterStrings.EQUAL;
}
return IFilterStrings.GT;
}
private static String getFilterAspectName(TmfFilterAspectNode filter) {
ITmfEventAspect<?> aspect = filter.getEventAspect();
// The event filter is on the content, just use a wildcard for field
if (aspect == TmfBaseAspects.getContentsAspect()) {
return IFilterStrings.WILDCARD;
}
String name = aspect.getName();
if (aspect instanceof TmfEventFieldAspect) {
TmfEventFieldAspect eventAspect = (TmfEventFieldAspect) aspect;
String fieldPath = eventAspect.getFieldPath();
if (fieldPath != null) {
name = fieldPath;
} else {
// Could be an empty field aspect, so if the name if the same as
// the content aspect, return a wildcard
if (eventAspect.getName().equals(TmfBaseAspects.getContentsAspect().getName())) {
return IFilterStrings.WILDCARD;
}
}
}
// Keep only the last element of the path, after the last slash
int slashPos = name.lastIndexOf("/"); //$NON-NLS-1$
return slashPos < 0 ? name : name.substring(slashPos);
}
private static String getRegexFromOrNode(TmfFilterOrNode filter) {
String regex = joinChildrenRegex(filter, IFilterStrings.OR);
return filter.isNot() ? not(regex) : regex;
}
private static String getRegexFromAndNode(TmfFilterAndNode filter) {
String regex = joinChildrenRegex(filter, IFilterStrings.AND);
return filter.isNot() ? not(regex) : regex;
}
private static String getRegexFromRootNode(TmfFilterRootNode filter) {
return joinChildrenRegex(filter, IFilterStrings.AND);
}
private static String joinChildrenRegex(TmfFilterTreeNode filter, String joinString) {
List<String> regexes = new ArrayList<>();
for (ITmfFilter childFilter : filter.getChildren()) {
String regex = getRegexFromFilter(childFilter);
if (!regex.isEmpty()) {
regexes.add(regex);
}
}
return Objects.requireNonNull(StringUtils.join(regexes, SPACE + joinString + SPACE));
}
}