Reguläre Ausdrücke im javax.swing.RowFilter

digitalFreeman

Grünschnabel
Moin,

zunächst möchte ich einmal die Rahmenbedingungen meines Problems schildern. :)
Seit Java 1.6 gibt es die Klasse javax.swing.RowFilter, so dass in einem JTable unkompliziert die dargestellten Zeilen nach deren Inhalt gefiltert werden können.

Existieren in dem jTable beispielsweise die Zeilen {"Mary", "Alison", "Kathy", "Sharon", "Philip", "Isaac"} und man verwendet als Filter den String "s", so werden in dem jTable nur noch die Zeilen {"Alison", "Isaac"} dargestellt. Durch Erweiterung des Filterstrings zu "sa" bleibt nur noch {"Isaac"} übrig. Ein entsprechendes Beispiel stellt uns Sun freundlicherweise in Form der TableFilterDemo zur Verfügung.

Soweit die Vorgeschichte. Nun folgt die Beschreibung des eigentlichen Problems:

Das Filtern geschieht durch Aufruf der Methode RowFilter.regexFilter(String regex, int... indices). Bei dem übergebenen String regex handelt es sich um einen regulären Ausdruck. An dieser Stelle sei auf die Klasse Pattern verwiesen. Diese definiert die Muster für reguläre Ausdrücke. Wenn wir zum Beispiel den speziellen Konstruktor "(?i)" vor unserem Filterstring stellen, wird nicht mehr zwischen Groß- und Kleinschreibung unterschieden (case-insensitive). Die Suche nach "(?i)s" liefert dann statt {"Alison", "Isaac"} (s.oben) zusätzlich {"Sharon"}. Alternativ gibt es auch noch den Konstruktor "(?s)" [dotAll], bei dem ein "." für beliebige Zeichen stehen kann. Filtern wir nach "(?is)s.a" erhalten wir als Ergebnis {"Sharon","Isaac"}.

Ich möchte nun aber den Filter etwas flexibler gestalten. Erweitern wir die Vornamen aus dem DoppelArray "Object[][] data" aus der TableFilterDemo.java folgendermaßen:
Code:
private Object[][] data = {
            {"Mary Kate", "Campione",
             "Snowboarding", new Integer(5), new Boolean(false)},
            {"Alison Hunt", "Huml",
             "Rowing", new Integer(3), new Boolean(true)},
            {"Kathy Bloom", "Walrath",
             "Knitting", new Integer(2), new Boolean(false)},
            {"Sharon Stone", "Zakhour",
             "Speed reading", new Integer(20), new Boolean(true)},
            {"Philip Cipher", "Milne",
             "Pool", new Integer(10), new Boolean(false)},
            {"Isaac Newton", "Rabinovitch", 
                "Nitpicking", new Integer(1000), new Boolean(false)}
        };

Ich bräuchte eine flexible Suche, bei der ich beispielsweise "newton isaac" eingebe/filtere und als Ergebnis die Zeile {"Isaac Newton"} angezeigt bekomme. Ich bräuchte also ein "logisches Und", welches prüft, ob sowohl Substring1 und Substring2 enthalten sind. In der JavaDoc zu Pattern konnte ich bisher nur ein "Oder" finden ("|"). Ein Filtern nach "sa|hi" gibt {"Philip","Isaac"} zurück.

Hat jemand Erfahrung mit regulären Ausdrücken und kann mir sagen, wie ich ein "logisches Und" realiseren kann, bei dem die Reihenfolge der Substrings im Filterstring egal ist? Wäre super, wenn ich bei der Eingabe "n issac" als Ergebnis {"Isaac Newton"} erhalten würde, aber keine Zeilen, die nur "n" oder "isaac" enthalten.

Viele Grüße,
dF
 
Das Problem wurde gelöst durch Permutation der Substrings eines Suchstrings.
Ist der Suchstring beispielsweise "eine kurze anfrage", werden die Teilbegriffe permutiert und es wird nach Einträgen in den Zellen gesucht, die die Substrings in beliebiger Reihenfolge enthalten.
Es wird also nach den 3*2*1 (=3!) Möglichkeiten gesucht:
"eine kurze anfrage"
"eine anfrage kurze"
"kurze eine anfrage"
"kurze anfrage eine"
"anfrage kurze eine"
"anfrage eine kurze"

Damit die Substrings nicht in exakt dieser Reihenfolge auftauchen müssen, wurden die leerzeichen " " durch ".*". Ebenso wurde den Permutationen ein ".*" vor- und nachgestellt. Die Wortgruppen und die möglichen Permutationen wurden zusätzlich noch geklammert.
Die Permutationen müssen außerdem durch Oder-Verknüpfungen verknüpft werden.
Es resultiert also quasi folgender regulärer Ausdruck, der die von mir gesuchte Suchanfrage ermöglicht:
(.*eine.*kurze.*anfrage.*)|(.*eine.*anfrage.*kurze.*)|(.*kurze.*eine.*anfrage.*)|(.*kurze.*anfrage.*eine.*)|(.*anfrage.*eine.*kurze.*)|(.*anfrage.*kurze.*eine.*)

Die Permutationen benötigen viel Rechenzeit und bei mehr als 6 Substrings ist das schon spürbar (,weswegen bisher nur bis zu 5 Substrings permutiert werden).

Sollte jemandem irgendwann eine elegantere Lösung einfallen, würde ich mich freuen, diese zu erfahren.
 
Mir scheint es schneller zu sein einen andFilter zu verwenden.

Für die TableFilterDemo ändere mal die Methode newFilter auf:
Java:
private void newFilter() {
    String[] split = filterText.getText( ).split(" ");
    ArrayList<RowFilter<MyTableModel, Object>> filters = new ArrayList<RowFilter<MyTableModel, Object>>(split.length);
    for (String filterStrg : split) {
        try {
            RowFilter<MyTableModel, Object> rf = RowFilter.regexFilter("(?i)" +
                    filterStrg,
                    0);
            filters.add(rf);
        }
        catch (java.util.regex.PatternSyntaxException e) {
            e.printStackTrace( );
        }
    }

    RowFilter<MyTableModel, Object> rf = RowFilter.andFilter(filters);

    sorter.setRowFilter(rf);
}
 
hups... den andFilter() habe ich übersehen. Funktioniert exakt so, wie es sein sollte, nur dass es viel schöner ist, als erst die Permutationen etc aufzustellen.

Danke für die Hilfe!
 

Neue Beiträge

Zurück