/*
* Copyright 2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jdave.wicket;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Component.IVisitor;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
/**
* @author Joni Freeman
* @author Timo Rantalaiho
*/
public class Selector {
// Note that the redundant type parameters in several methods are needed
// because of this javac bug:
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954
public <T extends Component, X extends Component> X first(final MarkupContainer root,
final Class<T> componentType, final Matcher<?> matcher) {
return this.<T, X> selectFirst(root, componentType,
new ComponentsModelMatchesTo<T>(matcher));
}
public <T extends Component, X extends Component> List<X> all(final MarkupContainer root,
final Class<T> componentType, final Matcher<?> matcher) {
return selectAll(root, componentType, new ComponentsModelMatchesTo<T>(matcher));
}
public <T extends Component, X extends Component> X first(final MarkupContainer root,
final Class<T> componentType, final String wicketId, final Matcher<?> modelMatcher) {
final Matcher<T> bothMatcher = combine(modelMatcher, wicketId);
return this.<T, X> selectFirst(root, componentType, bothMatcher);
}
public <T extends Component, X extends Component> List<X> all(final MarkupContainer root,
final Class<T> componentType, final String wicketId, final Matcher<?> modelMatcher) {
final Matcher<T> bothMatcher = combine(modelMatcher, wicketId);
return selectAll(root, componentType, bothMatcher);
}
private <T extends Component, X extends Component> List<X> selectAll(
final MarkupContainer root, final Class<T> componentType,
final Matcher<T> componentMatcher) {
final CollectingVisitor<T, X> visitor = new CollectingVisitor<T, X>(componentMatcher);
return visitor.selectFrom(root, componentType, IVisitor.CONTINUE_TRAVERSAL);
}
private <T extends Component, X extends Component> X selectFirst(final MarkupContainer root,
final Class<T> componentType, final Matcher<T> componentMatcher) {
final CollectingVisitor<T, X> visitor = new CollectingVisitor<T, X>(componentMatcher);
final List<X> firstMatch = visitor.selectFrom(root, componentType, IVisitor.STOP_TRAVERSAL);
if (firstMatch.isEmpty()) {
return null;
}
return firstMatch.get(0);
}
@SuppressWarnings("unchecked")
private <T extends Component> Matcher<T> combine(final Matcher<?> modelMatcher,
final String wicketId) {
return Matchers.allOf(new ComponentsModelMatchesTo<T>(modelMatcher),
new WicketIdEqualsTo<T>(wicketId));
}
/**
* Not thread safe.
*/
private class CollectingVisitor<T extends Component, X extends Component> implements
IVisitor<T> {
private final Matcher<T> componentMatcher;
private final List<X> matches = new ArrayList<X>();
private Object actionOnMatch;
public CollectingVisitor(final Matcher<T> componentMatcher) {
this.componentMatcher = componentMatcher;
}
@SuppressWarnings( { "unchecked" })
public Object component(final Component component) {
if (componentMatcher.matches(component)) {
matches.add((X) component);
return actionOnMatch;
}
return CONTINUE_TRAVERSAL;
}
public List<X> selectFrom(final MarkupContainer root, final Class<T> componentType,
final Object actionOnMatch) {
this.actionOnMatch = actionOnMatch;
root.visitChildren(componentType, this);
return matches;
}
}
}