001 /*
002 * Created on Nov 15, 2007
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 *
014 * Copyright @2007-2010 the original author or authors.
015 */
016 package org.fest.swing.core;
017
018 import static java.util.Collections.emptyList;
019 import static org.fest.util.Strings.concat;
020 import static org.fest.util.Systems.LINE_SEPARATOR;
021
022 import java.awt.Component;
023 import java.awt.Container;
024 import java.util.Collection;
025 import java.util.concurrent.atomic.AtomicReference;
026
027 import org.fest.assertions.BasicDescription;
028 import org.fest.assertions.Description;
029 import org.fest.swing.exception.ComponentLookupException;
030 import org.fest.swing.timing.Condition;
031
032 /**
033 * Understands a condition that is satisfied if a GUI component that matches certain search criteria can be found.
034 *
035 * @author Yvonne Wang
036 * @author Alex Ruiz
037 */
038 public final class ComponentFoundCondition extends Condition {
039
040 private final ComponentFinder finder;
041 private final ComponentMatcher matcher;
042 private final Container root;
043
044 private Component found;
045
046 private final AtomicReference<ComponentLookupException> notFoundError = new AtomicReference<ComponentLookupException>();
047
048 /**
049 * Creates a new <code>{@link ComponentFoundCondition}</code>
050 * @param description the description of this condition.
051 * @param finder performs the component search.
052 * @param matcher specifies the condition that the component we are looking for needs to match.
053 */
054 public ComponentFoundCondition(String description, ComponentFinder finder, ComponentMatcher matcher) {
055 this(description, finder, matcher, null);
056 }
057
058 /**
059 * Creates a new <code>{@link ComponentFoundCondition}</code>
060 * @param description the description of this condition.
061 * @param finder performs the component search.
062 * @param matcher specifies the condition that the component we are looking for needs to match.
063 * @param root the root used as the starting point of the search.
064 */
065 public ComponentFoundCondition(String description, ComponentFinder finder, ComponentMatcher matcher, Container root) {
066 this(new BasicDescription(description), finder, matcher, root);
067 }
068
069 /**
070 * Creates a new <code>{@link ComponentFoundCondition}</code>
071 * @param description the description of this condition.
072 * @param finder performs the component search.
073 * @param matcher specifies the condition that the component we are looking for needs to match.
074 */
075 public ComponentFoundCondition(Description description, ComponentFinder finder, ComponentMatcher matcher) {
076 this(description, finder, matcher, null);
077 }
078
079 /**
080 * Creates a new <code>{@link ComponentFoundCondition}</code>
081 * @param description the description of this condition.
082 * @param finder performs the component search.
083 * @param matcher specifies the condition that the component we are looking for needs to match.
084 * @param root the root used as the starting point of the search.
085 */
086 public ComponentFoundCondition(Description description, ComponentFinder finder, ComponentMatcher matcher, Container root) {
087 super(description);
088 this.finder = finder;
089 this.matcher = matcher;
090 this.root = root;
091 }
092
093 /**
094 * Returns <code>true</code> if a component that matches the search criteria in this condition's
095 * <code>{@link ComponentMatcher}</code> can be found. Otherwise, this method returns <code>false</code>.
096 * @return <code>true</code> if a matching component can be found, <code>false</code> otherwise.
097 */
098 public boolean test() {
099 boolean matchFound = false;
100 try {
101 found = finder.find(root, matcher);
102 matchFound = true;
103 } catch (ComponentLookupException e) {
104 notFoundError.set(e);
105 }
106 resetMatcher(matchFound);
107 if (matchFound) notFoundError.set(null);
108 return matchFound;
109 }
110
111 private void resetMatcher(boolean matchFound) {
112 if (!(matcher instanceof ResettableComponentMatcher)) return;
113 ((ResettableComponentMatcher)matcher).reset(matchFound);
114 }
115
116 /**
117 * Returns the component hierarchy to be added to this condition's description in case of a component lookup failure.
118 * @return the component hierarchy to be added to this condition's description in case of a component lookup failure.
119 */
120 @Override protected String descriptionAddendum() {
121 ComponentLookupException error = notFoundError.get();
122 if (error == null) return EMPTY_TEXT;
123 return concat(LINE_SEPARATOR, error.getMessage());
124 }
125
126 /**
127 * Returns the component found (if any.)
128 * @return the component found.
129 */
130 public Component found() { return found; }
131
132 /**
133 * Returns all the components that satisfied the search criteria specified by this condition's
134 * <code>{@link ComponentMatcher}</code>.
135 * @return all the components that satisfied the search criteria specified by this condition's
136 * {@code ComponentMatcher}.
137 */
138 public Collection<? extends Component> duplicatesFound() {
139 ComponentLookupException error = notFoundError.get();
140 if (error == null) return emptyList();
141 return error.found();
142 }
143 }