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    }