![JAR search and dependency download from the Maven repository](/logo.png)
ca.odell.glazedlists.matchers.CompositeMatcherEditor Maven / Gradle / Ivy
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.matchers;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A {@link MatcherEditor} composed of zero or more delegate
* {@link MatcherEditor}s.
*
* @author Rob Eden
* @author Jesse Wilson
*/
public class CompositeMatcherEditor extends AbstractMatcherEditor {
/** require all matchers in the {@link MatcherEditor} to match */
public static final int AND = 42;
/** require any matchers in the {@link MatcherEditor} to match */
public static final int OR = 24;
/** the delegates */
private EventList> matcherEditors;
/** whether to match with AND or OR */
private int mode = AND;
/** listeners for each delegate */
private List matcherEditorListeners = new ArrayList();
/**
* Create a {@link CompositeMatcherEditor} that creates Matchers from the union
* of the specified {@link EventList} of {@link MatcherEditor}s. The {@link EventList}
* must not contain any null
values and all elements must
* implement {@link MatcherEditor}.
*/
public CompositeMatcherEditor(EventList> matcherEditors) {
this.matcherEditors = matcherEditors;
// prepare the initial set
for(Iterator> i = matcherEditors.iterator(); i.hasNext(); ) {
matcherEditorListeners.add(new DelegateMatcherEditorListener(i.next()));
}
// handle changes to the list of matchers
matcherEditors.addListEventListener(new MatcherEditorsListListener());
// use the initial matcher
fireChanged(rebuildMatcher());
}
/**
* Create a {@link CompositeMatcherEditor}.
*/
public CompositeMatcherEditor() {
this(new BasicEventList>());
}
/**
* Get the {@link EventList} of {@link MatcherEditor}s that make up this
* {@link CompositeMatcherEditor}. The {@link EventList}
* must never contain any null
values and all elements must
* implement {@link MatcherEditor}.
*/
public EventList> getMatcherEditors() {
return matcherEditors;
}
/**
* Rebuild the CompositeMatcher modelled by this editor.
*/
private Matcher rebuildMatcher() {
final Matcher[] matchers = new Matcher[matcherEditors.size()];
for (int i = 0, n = matcherEditors.size(); i < n; i++) {
matchers[i] = matcherEditors.get(i).getMatcher();
}
if(mode == AND) return Matchers.and(matchers);
else if(mode == OR) return Matchers.or(matchers);
else throw new IllegalStateException();
}
/**
* Handle changes to the MatcherEditors.
*/
private class MatcherEditorsListListener implements ListEventListener> {
@Override
public void listChanged(ListEvent> listChanges) {
// update listeners for the list change
boolean inserts = false;
boolean deletes = false;
boolean wasEmpty = matcherEditorListeners.isEmpty();
while (listChanges.next()) {
int index = listChanges.getIndex();
int type = listChanges.getType();
// when a MatcherEditor is added, listen to it
if(type == ListEvent.INSERT) {
MatcherEditor inserted = matcherEditors.get(index);
matcherEditorListeners.add(new DelegateMatcherEditorListener(inserted));
inserts = true;
// when a MatcherEditor is removed, stop listening to it
} else if(type == ListEvent.DELETE) {
DelegateMatcherEditorListener listener = matcherEditorListeners.remove(index);
listener.stopListening();
deletes = true;
// when a MatcherEditor is updated, update the listener
} else if(type == ListEvent.UPDATE) {
MatcherEditor updated = matcherEditors.get(index);
DelegateMatcherEditorListener listener = matcherEditorListeners.get(index);
listener.setMatcherEditor(updated);
inserts = true;
deletes = true;
}
}
boolean isEmpty = matcherEditorListeners.isEmpty();
// fire events
if(mode == AND) {
if(inserts && deletes) {
fireChanged(rebuildMatcher());
} else if(inserts) {
fireConstrained(rebuildMatcher());
} else if(deletes) {
if(isEmpty) fireMatchAll();
else fireRelaxed(rebuildMatcher());
}
} else if(mode == OR) {
if(inserts && deletes) {
fireChanged(rebuildMatcher());
} else if(inserts) {
if(wasEmpty) fireConstrained(rebuildMatcher());
else fireRelaxed(rebuildMatcher());
} else if(deletes) {
if(isEmpty) fireMatchAll();
else fireConstrained(rebuildMatcher());
}
} else {
throw new IllegalStateException();
}
}
}
/**
* Set the match mode for this {@link CompositeMatcherEditor}.
*
* @param mode either CompositeMatcherEditor.AND
to match all
* CompositeMatcherEditor.OR
to match any.
*/
public void setMode(int mode) {
if(this.mode == mode) return;
int oldMode = this.mode;
this.mode = mode;
// requiring all to requiring any is a relax
if(oldMode == AND && mode == OR) {
if(matcherEditors.isEmpty()) {
fireMatchNone();
} else if(matcherEditors.size() > 1) {
fireRelaxed(rebuildMatcher());
}
// requiring any to requiring all is a constrain
} else if(oldMode == OR && mode == AND) {
if(matcherEditors.isEmpty()) {
fireMatchAll();
} else if(matcherEditors.size() > 1) {
fireConstrained(rebuildMatcher());
}
// we don't support this mode
} else {
throw new IllegalArgumentException();
}
}
/**
* Get the match mode for this {@link CompositeMatcherEditor}.
*
* @return either CompositeMatcherEditor.AND
for match all
* CompositeMatcherEditor.OR
for match any.
*/
public int getMode() {
return mode;
}
/**
* Listens to a specific MatcherEditor and fires events as that MatcherEditor changes.
*/
private class DelegateMatcherEditorListener implements Listener {
/** the matcher editor this listens to */
private MatcherEditor source;
/**
* This implementation of this method simply delegates the handling of
* the given matcherEvent
to one of the protected methods
* defined by this class. This clearly separates the logic for each
* type of Matcher change.
*
* @param matcherEvent a MatcherEvent describing the change in the
* Matcher produced by the MatcherEditor
*/
@Override
public void changedMatcher(MatcherEditor.Event matcherEvent) {
switch (matcherEvent.getType()) {
case Event.CONSTRAINED: this.constrained(); break;
case Event.RELAXED: this.relaxed(); break;
case Event.CHANGED: this.changed(); break;
case Event.MATCH_ALL: this.matchAll(); break;
case Event.MATCH_NONE: this.matchNone(); break;
}
}
/**
* Create a new listener for the specified MatcherEditor. Listening is
* started automatically and should be stopped using {@link #stopListening()}.
*/
private DelegateMatcherEditorListener(MatcherEditor source) {
this.source = source;
source.addMatcherEditorListener(this);
}
private void matchAll() {
if(matcherEditors.size() == 1) fireMatchAll(); // optimization
else fireRelaxed(rebuildMatcher());
}
private void matchNone() {
if(matcherEditors.size() == 1) fireMatchNone(); // optimization
else fireConstrained(rebuildMatcher());
}
private void changed() {
fireChanged(rebuildMatcher());
}
private void constrained() {
fireConstrained(rebuildMatcher());
}
private void relaxed() {
fireRelaxed(rebuildMatcher());
}
/**
* Start listening to events from the MatcherEditor.
*/
public void setMatcherEditor(MatcherEditor source) {
if(this.source == source) return;
stopListening();
this.source = source;
source.addMatcherEditorListener(this);
}
/**
* Stop listening to events from the MatcherEditor.
*/
public void stopListening() {
source.removeMatcherEditorListener(this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy