/*******************************************************************************
* Copyright (c) 2010, 2015 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
*
* Contributors:
* Patrick Tasse - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.tmf.core.filter.model;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestampFormat;
/**
* Filter node for the comparison operation
*
* @version 1.0
* @author Patrick Tasse
*/
public class TmfFilterCompareNode extends TmfFilterAspectNode {
/** compare node name */
public static final String NODE_NAME = "COMPARE"; //$NON-NLS-1$
/** not attribute name */
public static final String NOT_ATTR = "not"; //$NON-NLS-1$
/** result attribute name */
public static final String RESULT_ATTR = "result"; //$NON-NLS-1$
/** type attribute name */
public static final String TYPE_ATTR = "type"; //$NON-NLS-1$
/** value attribute name */
public static final String VALUE_ATTR = "value"; //$NON-NLS-1$
/**
* Supported comparison types
*/
public static enum Type {
/** numerical comparison type */
NUM,
/** alphanumerical comparison type */
ALPHA,
/** timestamp comparison type */
TIMESTAMP
}
private boolean fNot = false;
private int fResult;
private Type fType = Type.NUM;
private String fValue;
private transient Number fValueNumber;
private transient ITmfTimestamp fValueTimestamp;
private transient TmfTimestampFormat fTimestampFormat = new TmfTimestampFormat("T.SSSSSSSSS"); //$NON-NLS-1$
/**
* @param parent the parent node
*/
public TmfFilterCompareNode(ITmfFilterTreeNode parent) {
super(parent);
}
/**
* @return the NOT state
*/
public boolean isNot() {
return fNot;
}
/**
* @param not the NOT state
*/
public void setNot(boolean not) {
this.fNot = not;
}
/**
* @return the compare result (-1, 0 or 1)
*/
public int getResult() {
return fResult;
}
/**
* @param result the compare result (-1, 0 or 1)
*/
public void setResult(int result) {
this.fResult = (int) Math.signum(result);
}
/**
* @return the comparison type
*/
public Type getType() {
return fType;
}
/**
* @param type the comparison type
*/
public void setType(Type type) {
this.fType = type;
setValue(fValue);
}
/**
* @return the comparison value (in seconds for the TIMESTAMP type)
*/
public String getValue() {
return fValue;
}
/**
* @param value the comparison value (in seconds for the TIMESTAMP type)
*/
public void setValue(String value) {
this.fValue = value;
fValueNumber = null;
fValueTimestamp = null;
if (value == null) {
return;
}
if (fType == Type.NUM) {
fValueNumber = toNumber(value);
} else if (fType == Type.TIMESTAMP) {
fValueTimestamp = toTimestamp(value);
}
}
/**
* @return true if the value is valid for the comparison type
*/
public boolean hasValidValue() {
if (fType == Type.NUM) {
return fValueNumber != null;
} else if (fType == Type.TIMESTAMP) {
return fValue != null && !fValue.isEmpty() && fValueTimestamp != null;
}
return fValue != null;
}
@Override
public String getNodeName() {
return NODE_NAME;
}
@Override
public boolean matches(ITmfEvent event) {
if (event == null || fEventAspect == null) {
return false;
}
Object value = fEventAspect.resolve(event);
if (value == null) {
return false;
}
if (fType == Type.NUM) {
if (fValueNumber instanceof Double) {
Number valueNumber = toNumber(value);
if (valueNumber != null) {
return (Double.compare(valueNumber.doubleValue(), fValueNumber.doubleValue()) == fResult) ^ fNot;
}
} else if (fValueNumber != null) {
Number valueNumber = toNumber(value);
if (valueNumber instanceof Double || valueNumber instanceof Float) {
return (Double.compare(valueNumber.doubleValue(), fValueNumber.doubleValue()) == fResult) ^ fNot;
} else if (valueNumber != null) {
return (Long.compare(valueNumber.longValue(), fValueNumber.longValue()) == fResult) ^ fNot;
}
}
} else if (fType == Type.ALPHA) {
String valueString = value.toString();
int comp = (int) Math.signum(valueString.compareTo(fValue));
return (comp == fResult) ^ fNot;
} else if (fType == Type.TIMESTAMP) {
if (fValueTimestamp != null) {
ITmfTimestamp valueTimestamp = toTimestamp(value);
if (valueTimestamp != null) {
int comp = (int) Math.signum(valueTimestamp.compareTo(fValueTimestamp));
return (comp == fResult) ^ fNot;
}
}
}
return false;
}
private static Number toNumber(Object value) {
if (value instanceof Number) {
return (Number) value;
}
try {
return Long.decode(value.toString());
} catch (NumberFormatException e) {
}
try {
return NumberFormat.getInstance().parse(value.toString());
} catch (ParseException e) {
}
return null;
}
private ITmfTimestamp toTimestamp(Object value) {
if (value instanceof ITmfTimestamp) {
return (ITmfTimestamp) value;
}
try {
return TmfTimestamp.fromNanos(fTimestampFormat.parseValue(value.toString()));
} catch (ParseException e) {
}
return null;
}
@Override
public List<String> getValidChildren() {
return new ArrayList<>(0);
}
@Override
public String toString(boolean explicit) {
String result = (fResult == 0 ? "= " : fResult < 0 ? "< " : "> "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
String open = (fType == Type.NUM ? "" : fType == Type.ALPHA ? "\"" : "["); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
String close = (fType == Type.NUM ? "" : fType == Type.ALPHA ? "\"" : "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return getAspectLabel(explicit) + (fNot ? " not " : " ") + result + open + fValue + close; //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public ITmfFilterTreeNode clone() {
TmfFilterCompareNode clone = (TmfFilterCompareNode) super.clone();
clone.setValue(fValue);
return clone;
}
}