/*
* Created on Mar 4, 2008
*
* 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.
*
* Copyright @2008-2013 the original author or authors.
*/
package org.fest.swing.core;
import java.awt.Component;
import java.awt.Container;
import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JLabel;
import org.fest.swing.annotation.RunsInEDT;
import org.fest.swing.exception.ComponentLookupException;
/**
* Looks up AWT and Swing {@code Component}s based on different search criteria, such as a {@code Component}'s name,
* type or label, and custom search criteria as well.
*
* @author Alex Ruiz
*/
@RunsInEDT
public interface ComponentFinder {
/**
* @return the {@code ComponentPrinter} in this finder.
*/
@Nonnull ComponentPrinter printer();
/**
* <p>
* Finds an AWT or Swing {@code Component} by type. If this finder is attached to a {@link Robot}, it will use the
* component lookup scope in the {@code Robot}'s {@link Settings} to determine whether the component to find should be
* showing or not. If this finder is <em>not</em> attached to any {@code Robot}, the component to find does not have
* to be showing.
* </p>
*
* <p>
* Example:
* <pre>
* JTextField textbox = finder.findByType(JTextField.class);
* </pre>
* </p>
*
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull <T extends Component> T findByType(@Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by type. For example:
*
* @param type the type of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByType(Class)
*/
@Nonnull <T extends Component> T findByType(@Nonnull Class<T> type, boolean showing);
/**
* <p>
* Finds an AWT or Swing {@code Component} by type in the hierarchy under the given root. If this finder is attached
* to a {@link Robot}, it will use the component lookup scope in the {@code Robot}'s {@link Settings} to determine
* whether the component to find should be showing or not. If this finder is <em>not</em> attached to any
* {@code Robot}, the component to find does not have to be showing.
* </p>
*
* <p>
* Let's assume we have the following {@code JFrame} containing a {@code JTextField}:
* <pre>
* JFrame myFrame = new JFrame();
* myFrame.add(new JTextField());
* </pre>
* </p>
*
* <p>
* If we want to get a reference to the {@code JTextField} in that particular {@code JFrame} without going through the
* whole AWT component hierarchy, we could simply specify:
* <pre>
* JTextField textbox = finder.findByType(myFrame, JTextField.class);
* </pre>
* </p>
*
* @param root the root used as the starting point of the search.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull <T extends Component> T findByType(@Nonnull Container root, @Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by type in the hierarchy under the given root.
*
* @param root the root used as the starting point of the search.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByType(Container, Class)
*/
@Nonnull <T extends Component> T findByType(@Nonnull Container root, @Nonnull Class<T> type, boolean showing);
/**
* <p>
* Finds an AWT or Swing {@code Component} by by the text of its associated {@code JLabel}. If this finder is attached
* to a {@link Robot}, it will use the component lookup scope in the {@code Robot}'s {@link Settings} to determine
* whether the component to find should be showing or not. If this finder is <em>not</em> attached to any
* {@code Robot}, the component to find does not have to be showing.
* </p>
*
* <p>
* Let's assume we have the {@code JTextField} with a {@code JLabel} with text "Name";
* <pre>
* JLabel label = new JLabel("Name");
* JTextField textbox = new JTextField();
* label.setLabelFor(textBox);
* </pre>
* </p>
*
* <p>
* To get a reference to this {@code JTextField} by the text of its associated {@code JLabel}, we can specify:
* <pre>
* JTextField textBox = (JTextField) finder.findByLabel("Name");
* </pre>
* </p>
*
* <p>
* Please note that you need to cast the result of the lookup to the right type. To avoid casting, please use one of
* following:
* <ol>
* <li>{@link #findByLabel(String, Class)}</li>
* <li>{@link #findByLabel(String, Class, boolean)}</li>
* <li>{@link #findByLabel(Container, String, Class)}</li>
* <li>{@link #findByLabel(Container, String, Class, boolean)}</li>
* </ol>
* </p>
*
* @param label the text of the {@code JLabel} associated to the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull Component findByLabel(@Nullable String label);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel} and type. If this finder is
* attached to a {@link Robot}, it will use the component lookup scope in the {@code Robot}'s {@link Settings} to
* determine whether the component to find should be showing or not. If this finder is <em>not</em> attached to any
* {@code Robot}, the component to find does not have to be showing.
*
* @param label the text of the {@code JLabel} associated to the component to find.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull <T extends Component> T findByLabel(@Nullable String label, @Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel} and type.
*
* @param label the text of the {@code JLabel} associated to the component to find.
* @param type the type of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
*/
@Nonnull <T extends Component> T findByLabel(@Nullable String label, @Nonnull Class<T> type, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by by the text of its associated {@code JLabel}.
*
* @param label the text of the {@code JLabel} associated to the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
*/
@Nonnull Component findByLabel(@Nullable String label, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel}, in the hierarchy under the
* given root. If this finder is attached to a {@link Robot}, it will use the component lookup scope in the
* {@code Robot}'s {@link Settings} to determine whether the component to find should be showing or not. If this
* finder is <em>not</em> attached to any {@code Robot}, the component to find does not have to be showing.
*
* @param root the root used as the starting point of the search.
* @param label the text of the {@code JLabel} associated to the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull Component findByLabel(@Nonnull Container root, @Nullable String label);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel}, in the hierarchy under the
* given root.
*
* @param root the root used as the starting point of the search.
* @param label the text of the {@code JLabel} associated to the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
*/
@Nonnull Component findByLabel(@Nonnull Container root, @Nullable String label, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel} and type, in the hierarchy
* under the given root. If this finder is attached to a {@link Robot}, it will use the component lookup scope in the
* {@code Robot}'s {@link Settings} to determine whether the component to find should be showing or not. If this
* finder is <em>not</em> attached to any {@code Robot}, the component to find does not have to be showing.
*
* @param root the root used as the starting point of the search.
* @param label the text of the {@code JLabel} associated to the component to find.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull <T extends Component> T findByLabel(@Nonnull Container root, @Nullable String label, @Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by the text of its associated {@code JLabel} and type, in the hierarchy
* under the given root.
*
* @param root the root used as the starting point of the search.
* @param label the text of the {@code JLabel} associated to the component to find.
* @param type the type of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByLabel(String)
* @see JLabel#getLabelFor()
* @see JLabel#setLabelFor(Component)
*/
@Nonnull <T extends Component> T findByLabel(
@Nonnull Container root, @Nullable String label, @Nonnull Class<T> type, boolean showing);
/**
* <p>
* Finds an AWT or Swing {@code Component} by name. If this finder is attached to a {@link Robot}, it will use the
* component lookup scope in the {@code Robot}'s {@link Settings} to determine whether the component to find should be
* showing or not. If this finder is <em>not</em> attached to any {@code Robot}, the component to find does not have
* to be showing.
* </p>
*
* <p>
* Let's assume we have the {@code JTextField} with name "myTextBox";
* <pre>
* JTextField textbox = new JTextField();
* textBox.setName("myTextBox");
* </pre>
* </p>
*
* <p>
* To get a reference to this {@code JTextField} by its name, we can specify:
* <pre>
* JTextField textBox = (JTextField) finder.findByName("myTextBox");
* </pre>
* </p>
*
* <p>
* Please note that you need to cast the result of the lookup to the right type. To avoid casting, please use one of
* following:
* <ol>
* <li>{@link #findByName(String, Class)}</li>
* <li>{@link #findByName(String, Class, boolean)}</li>
* <li>{@link #findByName(Container, String, Class)}</li>
* <li>{@link #findByName(Container, String, Class, boolean)}</li>
* </ol>
* </p>
*
* @param name the name of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
*/
@Nonnull Component findByName(@Nullable String name);
/**
* Finds an AWT or Swing {@code Component} by name and type. If this finder is attached to a {@link Robot} , it will
* use the component lookup scope in the {@code Robot}'s {@link Settings} to determine whether the component to find
* should be showing or not. If this finder is <em>not</em> attached to any {@code Robot}, the component to find does
* not have to be showing.
*
* @param name the name of the component to find.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
* @see #findByName(String)
*/
@Nonnull <T extends Component> T findByName(@Nullable String name, @Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by name and type.
*
* @param name the name of the component to find.
* @param type the type of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByName(String)
*/
@Nonnull <T extends Component> T findByName(@Nullable String name, @Nonnull Class<T> type, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by name.
*
* @param name the name of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByName(String)
*/
@Nonnull Component findByName(@Nullable String name, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by name, in the hierarchy under the given root. If this finder is attached
* to a {@link Robot}, it will use the component lookup scope in the {@code Robot}'s {@link Settings} to determine
* whether the component to find should be showing or not. If this finder is <em>not</em> attached to any
* {@code Robot}, the component to find does not have to be showing.
*
* @param root the root used as the starting point of the search.
* @param name the name of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
* @see #findByName(String)
*/
@Nonnull Component findByName(@Nonnull Container root, @Nullable String name);
/**
* Finds an AWT or Swing {@code Component} by name, in the hierarchy under the given root.
*
* @param root the root used as the starting point of the search.
* @param name the name of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByName(String)
*/
@Nonnull Component findByName(@Nonnull Container root, @Nullable String name, boolean showing);
/**
* Finds an AWT or Swing {@code Component} by name and type, in the hierarchy under the given root. If this finder is
* attached to a {@link Robot}, it will use the component lookup scope in the {@code Robot}'s {@link Settings} to
* determine whether the component to find should be showing or not. If this finder is <em>not</em> attached to any
* {@code Robot}, the component to find does not have to be showing.
*
* @param root the root used as the starting point of the search.
* @param name the name of the component to find.
* @param type the type of the component to find.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see Robot#settings()
* @see Settings#componentLookupScope()
* @see ComponentLookupScope
* @see #findByName(String)
*/
@Nonnull <T extends Component> T findByName(@Nonnull Container root, @Nullable String name, @Nonnull Class<T> type);
/**
* Finds an AWT or Swing {@code Component} by name and type, in the hierarchy under the given root.
*
* @param root the root used as the starting point of the search.
* @param name the name of the component to find.
* @param type the type of the component to find.
* @param showing indicates whether the component to find should be visible (or showing) or not.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
* @see #findByName(String)
*/
@Nonnull <T extends Component> T findByName(
@Nonnull Container root, @Nullable String name, @Nonnull Class<T> type, boolean showing);
/**
* Finds an AWT or Swing {@code Component} using the given {@link ComponentMatcher}. The given matcher will be
* evaluated in the event dispatch thread (EDT.) Implementations of {@code ComponentMatcher} do not need to be
* concerned about the event dispatch thread (EDT.)
*
* @param m the matcher to use to find the component of interest.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
*/
@Nonnull Component find(@Nonnull ComponentMatcher m);
/**
* Finds an AWT or Swing {@code Component} using the given {@link GenericTypeMatcher}. The given matcher will be
* evaluated in the event dispatch thread (EDT.) Implementations of {@code GenericTypeMatcher} do not need to be
* concerned about the event dispatch thread (EDT.)
*
* @param m the matcher to use to find the component of interest.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
*/
@Nonnull <T extends Component> T find(@Nonnull GenericTypeMatcher<T> m);
/**
* Finds an AWT or Swing {@code Component} using the given {@link GenericTypeMatcher} in the hierarchy under the given
* root. The given matcher will be evaluated in the event dispatch thread (EDT.) Implementations of
* {@code GenericTypeMatcher} do not need to be concerned about the event dispatch thread (EDT.)
*
* @param root the root used as the starting point of the search.
* @param m the matcher to use to find the component.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
*/
@Nonnull <T extends Component> T find(@Nonnull Container root, @Nonnull GenericTypeMatcher<T> m);
/**
* Finds an AWT or Swing {@code Component} using the given {@link ComponentMatcher} in the hierarchy under the given
* root. The given matcher will be evaluated in the event dispatch thread (EDT.) Implementations of
* {@code ComponentMatcher} do not need to be concerned about the event dispatch thread (EDT.)
*
* @param root the root used as the starting point of the search.
* @param m the matcher to use to find the component.
* @return the found component.
* @throws ComponentLookupException if a matching component could not be found.
* @throws ComponentLookupException if more than one matching component is found.
*/
@Nonnull Component find(@Nullable Container root, @Nonnull ComponentMatcher m);
/**
* Returns all the AWT or Swing {@code Component}s that match the search criteria specified in the given
* {@link ComponentMatcher}.
*
* @param m the matcher to use to find the component.
* @return all the {@code Component}s that match the search criteria specified in the given {@code ComponentMatcher};
* or an empty collection, if there are no matching components.
*/
@Nonnull Collection<Component> findAll(@Nonnull ComponentMatcher m);
/**
* Returns all the AWT or Swing {@code Component}s under the given root that match the search criteria specified in
* the given {@link ComponentMatcher}.
*
* @param root the root used as the starting point of the search.
* @param m the matcher to use to find the component.
* @return all the {@code Component}s under the given root that match the search criteria specified in the given
* {@code ComponentMatcher}; or an empty collection, if there are no matching components.
*/
@Nonnull Collection<Component> findAll(@Nonnull Container root, @Nonnull ComponentMatcher m);
/**
* Returns all the AWT or Swing {@code Component}s that match the search criteria specified in the given
* {@link GenericTypeMatcher}.
*
* @param m the matcher to use to find the component.
* @return all the {@code Component}s that match the search criteria specified in the given {@code GenericTypeMatcher}
* ; or an empty collection, if there are no matching components.
*/
@Nonnull <T extends Component> Collection<T> findAll(@Nonnull GenericTypeMatcher<T> m);
/**
* Returns all the AWT or Swing {@code Component}s under the given root that match the search criteria specified in
* the given {@link GenericTypeMatcher}.
*
* @param root the root used as the starting point of the search.
* @param m the matcher to use to find the component.
* @return all the {@code Component}s under the given root that match the search criteria specified in the given
* {@code GenericTypeMatcher}; or an empty collection, if there are no matching components.
*/
@Nonnull <T extends Component> Collection<T> findAll(@Nonnull Container root, @Nonnull GenericTypeMatcher<T> m);
/**
* Returns whether the message in a {@link ComponentLookupException} should include the current component hierarchy.
* The default value is {@code true}.
*
* @return {@code true} if the component hierarchy is included as part of the {@code ComponentLookupException}
* message, {@code false} otherwise.
*/
boolean includeHierarchyIfComponentNotFound();
/**
* Updates whether the message in a {@link ComponentLookupException} should include the current component hierarchy.
* The default value is {@code true}.
*
* @param newValue the new value to set.
*/
void includeHierarchyIfComponentNotFound(boolean newValue);
}