/*******************************************************************************
* Copyright (c) 2014 Formal Mind GmbH.
* 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:
* Ingo Weigelt - initial API and implementation
* Michael Jastram - adding SUPPORTED_OPERATIONS
* Michael Jastram - adding getters, so that we can reconstruct the GUI
******************************************************************************/
package org.eclipse.rmf.reqif10.search.filter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.rmf.reqif10.AttributeDefinition;
import org.eclipse.rmf.reqif10.AttributeDefinitionString;
import org.eclipse.rmf.reqif10.SpecElementWithAttributes;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
/**
* Filter for String-based values.
*/
public abstract class AbstractTextFilter extends AbstractAttributeFilter {
public enum InternalAttribute {
IDENTIFIER, DESC, LONG_NAME
}
// TODO cross-check this with supported operators.
public static final ImmutableSet<Operator> SUPPORTED_OPERATORS = Sets
.immutableEnumSet(Operator.EQUALS, Operator.NOT_EQUALS,
Operator.CONTAINS, Operator.NOT_CONTAINS, Operator.REGEXP, Operator.IS_SET, Operator.IS_NOT_SET);
protected Operator operator;
protected String filterValue;
protected AttributeDefinition attributeDefinition;
protected InternalAttribute internalAttribute;
protected boolean caseSensitive;
protected boolean isInternal;
/**
* Constructor used to create a filter for an {@link AttributeDefinitionString}
*
* @param operator the filter operator to use
* @param value the value to match
* @param attributeDefinition the attributeDefinition that defines the value of a SpecObject that should be matched
* @param caseSensitive
*/
public AbstractTextFilter(Operator operator, String value,
AttributeDefinition attributeDefinition, boolean caseSensitive) {
this(operator, value, null, attributeDefinition, caseSensitive);
this.isInternal = false;
if (null == attributeDefinition){
throw new IllegalArgumentException("AttributeDefinition can not be null");
}
}
/**
* Constructor used to create a filter for an {@link InternalAttribute}
*
* @param operator the filter operator to use
* @param value the value to match
* @param attributeDefinition the attributeDefinition that defines the value of a SpecObject that should be matched
* @param caseSensitive
*/
public AbstractTextFilter(Operator operator, String value,
InternalAttribute internalFeature, boolean caseSensitive) {
this(operator, value, internalFeature, null, caseSensitive);
this.isInternal = true;
if (null == internalFeature){
throw new IllegalArgumentException("AttributeDefinition can not be null");
}
}
protected AbstractTextFilter(Operator operator, String value,
InternalAttribute internalFeature, AttributeDefinition attributeDefinition, boolean caseSensitive){
if (!getSupportedOperators().contains(operator)){
throw new IllegalArgumentException(
"This filter does not support the " + operator.toString()
+ " operation");
}
if (null == value && operator != Operator.IS_SET && operator != Operator.IS_NOT_SET ){
throw new IllegalArgumentException(
"Value can not be null");
}
if (internalFeature != null && attributeDefinition == null){
isInternal = true;
}else if(attributeDefinition != null && internalFeature == null){
isInternal = false;
}else{
throw new IllegalArgumentException(
"internalFeature and attribute definition can not be null or set at the same time");
}
this.operator = operator;
this.filterValue = (null == value ? "" : value);
this.internalAttribute = internalFeature;
this.attributeDefinition = attributeDefinition;
this.caseSensitive = caseSensitive;
}
@Override
public boolean match(SpecElementWithAttributes element) {
if (operator == Operator.IS_SET || operator == Operator.IS_NOT_SET){
return super.match(element);
}
String theValue;
// retrieve the value to check depending on this is a filter on an
// internal Attribute or a value
if (isInternal) {
theValue = getInternalAttributeValue(element);
} else {
theValue = getAttributeValue(element);
}
if (theValue == null){
/* Check if there is any default value for this attribute */
theValue = getDefaultValue(element);
}
/* 1. handle empty attribute case */
if (theValue == null){
switch (operator) {
case EQUALS:
case CONTAINS:
return false;
case NOT_EQUALS:
case NOT_CONTAINS:
return true;
case REGEXP:
// apply regexp to the empty string
return matchRegexp("");
default:
break;
}
}
/* 2. handle non-empty attribute case */
switch (operator) {
case EQUALS:
return (caseSensitive ? theValue.equals(filterValue)
: theValue.equalsIgnoreCase(filterValue));
case NOT_EQUALS:
return (caseSensitive ? !theValue.equals(filterValue)
: !theValue.equalsIgnoreCase(filterValue));
case CONTAINS:
return (caseSensitive ? theValue.contains(filterValue)
: theValue.toLowerCase().contains(filterValue.toLowerCase()));
case NOT_CONTAINS:
return (caseSensitive ? !theValue.contains(filterValue)
: !theValue.toLowerCase().contains(filterValue.toLowerCase()));
case REGEXP:
// return (caseSensitive ? theValue.matches(filterValue)
// : theValue.toLowerCase().matches(filterValue.toLowerCase()));
return matchRegexp(theValue);
default:
return super.match(element);
}
}
/**
* return the default Value of the AttributeDefinition if the attribute is defined in elements specType
* Otherwise return null
*
* @param element
* @return the default Value of the AttributeDefinition if the attribute is defined in elements specType
* Otherwise null
*/
protected abstract String getDefaultValue(SpecElementWithAttributes element);
/**
* returns the value of the Internal Attribute that is defined by this.internalAttribute
*
* @return
*/
protected abstract String getInternalAttributeValue(SpecElementWithAttributes element);
/**
* returns the value of the element that is defined by this.attributeDefinition
*
* @param element
* @return
*/
protected abstract String getAttributeValue(SpecElementWithAttributes element);
/**
* Has to return the instance of this.SUPPORTED_OPERATORS
* @return
*/
public abstract ImmutableSet<Operator> getSupportedOperators();
/**
* matches this.filterValue to the String value
* takes this.caseSensitive into account
*
* @return
*/
protected boolean matchRegexp(String value){
Pattern p;
if (caseSensitive){
p = Pattern.compile(filterValue, Pattern.DOTALL);
}else{
p = Pattern.compile(filterValue, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
}
Matcher matcher = p.matcher(value);
return matcher.find();
}
public boolean isCaseSensitive() {
return caseSensitive;
}
@Override
public String getFilterValue1() {
return filterValue;
}
@Override
public String getFilterValue2() {
return null;
}
@Override
public Operator getOperator() {
return operator;
}
@Override
public Object getAttribute() {
return isInternal ? internalAttribute : attributeDefinition;
}
}