package org.fluxtream.core.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Chris Bartley (bartley@cmu.edu)
*/
public final class TagFilter {
private static final Logger LOG = Logger.getLogger(TagFilter.class);
public enum FilteringStrategy {
ALL("all"), ANY("any"), NONE("none"), UNTAGGED("untagged");
@NotNull
private final String name;
/**
* Returns the <code>FilteringStrategy</code> corresponing to the given <code>name</code> (case-insenstive), or
* returns the {@link #getDefault() default} <code>FilteringStrategy</code> if no such strategy exists.
*/
@Nullable
public static FilteringStrategy findByName(@Nullable final String name) {
if (name != null) {
try {
return FilteringStrategy.valueOf(name.toUpperCase(Locale.ENGLISH));
}
catch (IllegalArgumentException ignored) {
LOG.info("Unknown FilteringStrategy name [" + name + "], returning default");
}
}
return getDefault();
}
/**
* Returns the {@link #ANY} <code>FilteringStrategy</code>.
*/
@NotNull
public static FilteringStrategy getDefault() {
return ANY;
}
private FilteringStrategy(@NotNull final String name) {
this.name = name;
}
@NotNull
public String getName() {
return name;
}
}
/**
* Creates a <code>TagFilter</code> from the given {@link Collection} of tags and {@link FilteringStrategy} using the
* following rules:
* <ul>
* <li>
* If the {@link FilteringStrategy} is <code>null</code>, the {@link FilteringStrategy#getDefault() default}
* is used instead.
* </li>
* <li>
* If the given {@link Collection} of tags is non-<code>null</code>, then a cleansed {@link Collection} of
* unique tags is created by only adding tags which are non-empty after being
* {@link Tag#cleanse(String) cleansed}.
* </li>
* <li>
* If the {@link FilteringStrategy} is {@link FilteringStrategy#UNTAGGED}, then the given {@link Collection}
* of <code>tags</code> is ignored and a <code>TagFilter</code> is created with an empty {@link Collection} of
* tags.
* </li>
* <li>
* If the {@link FilteringStrategy} is <i>not</i> {@link FilteringStrategy#UNTAGGED}, but the given cleansed
* {@link Collection} of <code>tags</code> is <code>null</code> or empty, then this method returns
* <code>null</code>.
* </li>
* <li>
* If the {@link FilteringStrategy} is <i>not</i> {@link FilteringStrategy#UNTAGGED}, and the given
* {@link Collection} of <code>tags</code> is non-<code>null</code> and non-empty, then this method creates a
* <code>TagFilter</code> using the given unique, cleansed <code>tags</code> and {@link FilteringStrategy}.
* </li>
* </ul>
*
*/
@Nullable
public static TagFilter create(@Nullable final Collection<String> tags,
@Nullable final FilteringStrategy requestedFilteringStrategy) {
// make sure the filtering strategy is non-null, choosing the default if necessary
final FilteringStrategy filteringStrategy = requestedFilteringStrategy == null ? FilteringStrategy.getDefault() : requestedFilteringStrategy;
if (FilteringStrategy.UNTAGGED.equals(filteringStrategy)) {
// filter for untagged items
return new TagFilter(null, FilteringStrategy.UNTAGGED);
}
else {
if (tags != null) {
final Set<String> cleansedTags = new HashSet<String>();
for (final String tag : tags) {
final String cleansedTag = Tag.cleanse(tag);
if (cleansedTag.length() > 0) {
cleansedTags.add(cleansedTag);
}
}
if (!cleansedTags.isEmpty()) {
return new TagFilter(cleansedTags, filteringStrategy);
}
}
}
return null;
}
@NotNull
private Set<String> tags = new HashSet<String>();
@NotNull
private final FilteringStrategy filteringStrategy;
private TagFilter(@Nullable final Collection<String> tags, @NotNull final FilteringStrategy filteringStrategy) {
if (tags != null) {
this.tags.addAll(tags);
}
this.filteringStrategy = filteringStrategy;
}
/** Returns an {@link Collections#unmodifiableSet(Set) unmodifiable set} of the tags in this TagFilter. */
@NotNull
public Set<String> getTags() {
return Collections.unmodifiableSet(tags);
}
/** Returns the {@link FilteringStrategy}. */
@NotNull
public FilteringStrategy getFilteringStrategy() {
return filteringStrategy;
}
@NotNull
public String getWhereClause() {
List<String> likeClauses = null;
if (!FilteringStrategy.UNTAGGED.equals(filteringStrategy)) {
likeClauses = new ArrayList<String>();
final String notClause = FilteringStrategy.NONE.equals(filteringStrategy) ? "NOT " : "";
for (final String tag : tags) {
likeClauses.add("facet.tags " + notClause + "like '%," + tag + ",%'");
}
}
switch (filteringStrategy) {
case ANY:
return StringUtils.join(likeClauses, " OR ");
case ALL:
return StringUtils.join(likeClauses, " AND ");
case NONE:
return "facet.tags is NULL OR (" + StringUtils.join(likeClauses, " AND ") + ")";
case UNTAGGED:
return "facet.tags is NULL";
}
return "";
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TagFilter{");
sb.append("tags=").append(tags);
sb.append(", filteringStrategy=").append(filteringStrategy);
sb.append('}');
return sb.toString();
}
}