package de.lmu.ifi.dbs.elki.datasource.filter;

import java.util.ArrayList;
import java.util.regex.Pattern;

import de.lmu.ifi.dbs.elki.data.LabelList;
import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
import de.lmu.ifi.dbs.elki.datasource.bundle.MultipleObjectsBundle;
import de.lmu.ifi.dbs.elki.logging.Logging;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.PatternParameter;

/**
 * A filter to sort the data set by some label.
 * 
 * @author Erich Schubert
 */
public class FilterByLabelFilter implements ObjectFilter {
  /**
   * Class logger
   */
  private static final Logging logger = Logging.getLogger(FilterByLabelFilter.class);

  /**
   * The filter pattern
   */
  private final Pattern pattern;

  /**
   * Constructor.
   * 
   * @param pattern Filter pattern
   */
  public FilterByLabelFilter(Pattern pattern) {
    super();
    this.pattern = pattern;
  }

  @Override
  public MultipleObjectsBundle filter(final MultipleObjectsBundle objects) {
    if(logger.isDebugging()) {
      logger.debug("Filtering the data set");
    }

    // Identify a label column
    final int lblcol;
    {
      int lblc = -1;
      for(int i = 0; i < objects.metaLength(); i++) {
        if(TypeUtil.GUESSED_LABEL.isAssignableFromType(objects.meta(i))) {
          lblc = i;
          break;
        }
      }
      lblcol = lblc; // make static
    }

    MultipleObjectsBundle bundle = new MultipleObjectsBundle();
    for(int j = 0; j < objects.metaLength(); j++) {
      bundle.appendColumn(objects.meta(j), new ArrayList<Object>());
    }
    for(int i = 0; i < objects.dataLength(); i++) {
      Object l = objects.data(i, lblcol);
      if(l instanceof LabelList) {
        boolean good = false;
        for(String label : (LabelList) l) {
          if(pattern.matcher(label).matches()) {
            good = true;
            break;
          }
        }
        if(!good) {
          continue;
        }
      }
      else {
        if(!pattern.matcher(l.toString()).matches()) {
          continue;
        }
      }
      bundle.appendSimple(objects.getRow(i));
    }
    return bundle;
  }

  /**
   * Parameterization class.
   * 
   * @author Erich Schubert
   * 
   * @apiviz.exclude
   */
  public static class Parameterizer extends AbstractParameterizer {
    /**
     * Parameter that specifies the filter pattern (regular expression).
     * <p>
     * Key: {@code -patternfilter}
     * </p>
     */
    public static final OptionID LABELFILTER_PATTERN_ID = OptionID.getOrCreateOptionID("patternfilter", "The filter pattern to use.");

    /**
     * The pattern configured.
     */
    Pattern pattern = null;

    @Override
    protected void makeOptions(Parameterization config) {
      super.makeOptions(config);
      final PatternParameter patternP = new PatternParameter(LABELFILTER_PATTERN_ID);
      if(config.grab(patternP)) {
        pattern = patternP.getValue();
      }
    }

    @Override
    protected Object makeInstance() {
      return new FilterByLabelFilter(pattern);
    }
  }
}
@@ -528,16 +528,85 @@
 zation;%0A
+import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag;%0A
 import d
@@ -1009,16 +1009,85 @@
 pattern;
+%0A  %0A  /**%0A   * Inversion flag%0A   */%0A  private final boolean inverted;
 %0A%0A  /**%0A
@@ -1137,32 +1137,68 @@
  Filter pattern%0A
+   * @param inverted Inversion flag%0A
    */%0A  public F
@@ -1231,16 +1231,34 @@
  pattern
+, boolean inverted
 ) %7B%0A    
@@ -1294,16 +1294,46 @@
 attern;%0A
+    this.inverted = inverted;%0A
   %7D%0A%0A  @
@@ -2333,13 +2333,24 @@
  if(
-!
 good
+ == inverted
 ) %7B%0A
@@ -2864,16 +2864,24 @@
 rnfilter
+.pattern
 %7D%0A     *
@@ -2995,16 +2995,24 @@
 rnfilter
+.pattern
 %22, %22The 
@@ -3057,66 +3057,419 @@
   * 
-The pattern configured.%0A     */%0A    Pattern pattern = null
+Flag to use the pattern in inverted mode%0A     * %3Cp%3E%0A     * Key: %7B@code -patternfilter.invert%7D%0A     * %3C/p%3E%0A     */%0A    public static final OptionID LABELFILTER_PATTERN_INVERT_ID = OptionID.getOrCreateOptionID(%22patternfilter.invert%22, %22Flag to invert pattern.%22);%0A%0A    /**%0A     * The pattern configured.%0A     */%0A    Pattern pattern = null;%0A%0A    /**%0A     * Inversion flag%0A     */%0A    private boolean inverted = false
 ;%0A%0A 
@@ -3735,24 +3735,178 @@
 ();%0A      %7D%0A
+      final Flag invertedF = new Flag(LABELFILTER_PATTERN_INVERT_ID);%0A      if(config.grab(invertedF)) %7B%0A        inverted = invertedF.getValue();%0A      %7D%0A
     %7D%0A%0A    @
@@ -3996,16 +3996,26 @@
 (pattern
+, inverted
 );%0A    %7D

Add inversion flag to label filter.