package rocks.inspectit.shared.cs.ci.business.expression.impl;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.lang.ArrayUtils;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import rocks.inspectit.shared.all.cmr.service.ICachedDataService;
import rocks.inspectit.shared.all.communication.data.InvocationSequenceData;
import rocks.inspectit.shared.cs.ci.business.expression.AbstractExpression;
import rocks.inspectit.shared.cs.ci.business.valuesource.PatternMatchingType;
import rocks.inspectit.shared.cs.ci.business.valuesource.StringValueSource;
/**
* Definition of a string matching expression.
*
* @author Alexander Wert
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "string-matching")
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class StringMatchingExpression extends AbstractExpression {
/**
* Matching type to use for comparing the string value against a snippet.
*/
@XmlAttribute(name = "matching-type", required = true)
private PatternMatchingType matchingType = PatternMatchingType.CONTAINS;
/**
* String value to check against.
*/
@XmlAttribute(name = "snippet", required = true)
private String snippet = "";
/**
* Source of the string value to be compared against the snippet.
*/
@XmlElementRef(required = true)
private StringValueSource stringValueSource;
/**
* Indicates whether this value source apply to any node in the {@link InvocationSequenceData}
* or only at the root element. If this variable is false, the string value extraction will be
* only applied on the root element of the invocation sequence, otherwise the invocation
* sequence is searched for a corresponding node.
*/
@XmlAttribute(name = "search-in-trace")
private Boolean searchNodeInTrace = Boolean.FALSE;
/**
* If search-in-trace attribute is set to true, this attribute defines the maximum depth in the
* trace to search for the string matching. -1 means no limit.
*/
@XmlAttribute(name = "max-search-depth")
private Integer maxSearchDepth = Integer.valueOf(-1);
/**
* Default Constructor.
*/
public StringMatchingExpression() {
}
/**
* Constructor.
*
* @param matchingType
* matching type that defines how the string value is compared to the URL of the
* evaluation context
* @param snippet
* string value to compare against
*/
public StringMatchingExpression(PatternMatchingType matchingType, String snippet) {
this.matchingType = matchingType;
this.snippet = snippet;
}
/**
* Gets {@link #matchingType}.
*
* @return {@link #matchingType}
*/
public PatternMatchingType getMatchingType() {
return matchingType;
}
/**
* Sets {@link #matchingType}.
*
* @param matchingType
* New value for {@link #matchingType}
*/
public void setMatchingType(PatternMatchingType matchingType) {
this.matchingType = matchingType;
}
/**
* Gets {@link #snippet}.
*
* @return {@link #snippet}
*/
public String getSnippet() {
return snippet;
}
/**
* Sets {@link #snippet}.
*
* @param snippet
* New value for {@link #snippet}
*/
public void setSnippet(String snippet) {
this.snippet = snippet;
}
/**
* Gets {@link #stringValueSource}.
*
* @return {@link #stringValueSource}
*/
public StringValueSource getStringValueSource() {
return stringValueSource;
}
/**
* Sets {@link #stringValueSource}.
*
* @param stringValueSource
* New value for {@link #stringValueSource}
*/
public void setStringValueSource(StringValueSource stringValueSource) {
this.stringValueSource = stringValueSource;
}
/**
* Gets {@link #searchNodeInTrace}.
*
* @return {@link #searchNodeInTrace}
*/
public boolean isSearchNodeInTrace() {
return searchNodeInTrace.booleanValue();
}
/**
* Sets {@link #searchNodeInTrace}.
*
* @param searchNodeInTrace
* New value for {@link #searchNodeInTrace}
*/
public void setSearchNodeInTrace(boolean searchNodeInTrace) {
this.searchNodeInTrace = Boolean.valueOf(searchNodeInTrace);
}
/**
* Gets {@link #maxSearchDepth}.
*
* @return {@link #maxSearchDepth}
*/
public int getMaxSearchDepth() {
return maxSearchDepth.intValue();
}
/**
* Sets {@link #maxSearchDepth}.
*
* @param maxSearchDepth
* New value for {@link #maxSearchDepth}
*/
public void setMaxSearchDepth(int maxSearchDepth) {
this.maxSearchDepth = Integer.valueOf(maxSearchDepth);
}
/**
* {@inheritDoc}
*/
@Override
public boolean evaluate(InvocationSequenceData invocSequence, ICachedDataService cachedDataService) {
return evaluate(invocSequence, cachedDataService, 0);
}
/**
* Recursive evaluation in the invocation sequence structure if search in trace is activated.
*
* @param invocSequence
* {@link InvocationSequenceData} forming the evaluation context
* @param cachedDataService
* {@link ICachedDataService} instance for retrieval of method names etc.
* @param depth
* current search depth in the invocation sequence tree structure
* @return Returns evaluation result.
*/
private boolean evaluate(InvocationSequenceData invocSequence, ICachedDataService cachedDataService, int depth) {
String[] strArray = getStringValueSource().getStringValues(invocSequence, cachedDataService);
if (ArrayUtils.isNotEmpty(strArray)) {
for (String element : strArray) {
if ((null != element) && evaluateString(element)) {
return true;
}
}
}
if (isSearchNodeInTrace() && ((getMaxSearchDepth() < 0) || (depth < getMaxSearchDepth()))) {
for (InvocationSequenceData childNode : invocSequence.getNestedSequences()) {
if (evaluate(childNode, cachedDataService, depth + 1)) {
return true;
}
}
}
return false;
}
/**
* Evaluates the string array against the snippet in the {@link StringMatchingExpression}
* instance.
*
* @param stringValue
* string to check
* @return boolean evaluation result
*/
private boolean evaluateString(String stringValue) {
switch (getMatchingType()) {
case CONTAINS:
return stringValue.contains(getSnippet());
case ENDS_WITH:
return stringValue.endsWith(getSnippet());
case STARTS_WITH:
return stringValue.startsWith(getSnippet());
case EQUALS:
return stringValue.equals(getSnippet());
case REGEX:
return stringValue.matches(getSnippet());
default:
return false;
}
}
}