001    /*
002     * Created on May 14, 2007
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * 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 is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     *
013     * Copyright @2007-2010 the original author or authors.
014     */
015    package org.fest.swing.core;
016    
017    import static org.fest.assertions.Assertions.assertThat;
018    import static org.fest.swing.edt.GuiActionRunner.execute;
019    import static org.fest.swing.format.Formatting.format;
020    import static org.fest.swing.hierarchy.NewHierarchy.ignoreExistingComponents;
021    import static org.fest.util.Strings.concat;
022    import static org.fest.util.Systems.LINE_SEPARATOR;
023    
024    import java.awt.Component;
025    import java.awt.Container;
026    import java.io.ByteArrayOutputStream;
027    import java.io.PrintStream;
028    import java.util.Collection;
029    
030    import javax.swing.JLabel;
031    
032    import org.fest.swing.annotation.RunsInEDT;
033    import org.fest.swing.edt.GuiTask;
034    import org.fest.swing.exception.ComponentLookupException;
035    import org.fest.swing.hierarchy.*;
036    
037    /**
038     * Understands GUI <code>{@link java.awt.Component}</code> lookup.
039     *
040     * @author Alex Ruiz
041     */
042    public final class BasicComponentFinder implements ComponentFinder {
043    
044      private final ComponentHierarchy hierarchy;
045      private final ComponentPrinter printer;
046      private final Settings settings;
047    
048      private final FinderDelegate finderDelegate = new FinderDelegate();
049    
050      private boolean includeHierarchyInComponentLookupException;
051    
052      /**
053       * Creates a new <code>{@link BasicComponentFinder}</code> with a new AWT hierarchy. <code>{@link Component}</code>s
054       * created before the created <code>{@link BasicComponentFinder}</code> cannot be accessed by the created
055       * <code>{@link BasicComponentFinder}</code>.
056       * @return the created finder.
057       */
058      public static ComponentFinder finderWithNewAwtHierarchy() {
059        return new BasicComponentFinder(ignoreExistingComponents());
060      }
061    
062      /**
063       * Creates a new <code>{@link BasicComponentFinder}</code> that has access to all the GUI components in the AWT
064       * hierarchy.
065       * @return the created finder.
066       */
067      public static ComponentFinder finderWithCurrentAwtHierarchy() {
068        return new BasicComponentFinder(new ExistingHierarchy());
069      }
070    
071      /**
072       * Creates a new <code>{@link BasicComponentFinder}</code>. The created finder does not use any
073       * <code>{@link Settings}</code>.
074       * @param hierarchy the component hierarchy to use.
075       */
076      protected BasicComponentFinder(ComponentHierarchy hierarchy) {
077        this(hierarchy, null);
078      }
079    
080      /**
081       * Creates a new <code>{@link BasicComponentFinder}</code>.
082       * @param hierarchy the component hierarchy to use.
083       * @param settings the configuration settings to use. It can be <code>null</code>.
084       */
085      protected BasicComponentFinder(ComponentHierarchy hierarchy, Settings settings) {
086        this.hierarchy = hierarchy;
087        this.settings = settings;
088        printer = new BasicComponentPrinter(hierarchy);
089        includeHierarchyIfComponentNotFound(true);
090      }
091    
092      /** {@inheritDoc} */
093      public ComponentPrinter printer() { return printer; }
094    
095      /** {@inheritDoc} */
096      public <T extends Component> T findByType(Class<T> type) {
097        return findByType(type, requireShowing());
098      }
099    
100      /** {@inheritDoc} */
101      @RunsInEDT
102      public <T extends Component> T findByType(Class<T> type, boolean showing) {
103        return type.cast(find(new TypeMatcher(type, showing)));
104      }
105    
106      /** {@inheritDoc} */
107      @RunsInEDT
108      public <T extends Component> T findByType(Container root, Class<T> type) {
109        return findByType(root, type, requireShowing());
110      }
111    
112      /** {@inheritDoc} */
113      @RunsInEDT
114      public <T extends Component> T findByType(Container root, Class<T> type, boolean showing) {
115        return type.cast(find(root, new TypeMatcher(type, showing)));
116      }
117    
118      /** {@inheritDoc} */
119      @RunsInEDT
120      public <T extends Component> T findByName(String name, Class<T> type) {
121        return findByName(name, type, requireShowing());
122      }
123    
124      /** {@inheritDoc} */
125      @RunsInEDT
126      public <T extends Component> T findByName(String name, Class<T> type, boolean showing) {
127        Component found = find(new NameMatcher(name, type, showing));
128        return type.cast(found);
129      }
130    
131      /** {@inheritDoc} */
132      @RunsInEDT
133      public Component findByName(String name) {
134        return findByName(name, requireShowing());
135      }
136    
137      /** {@inheritDoc} */
138      @RunsInEDT
139      public Component findByName(String name, boolean showing) {
140        return find(new NameMatcher(name, showing));
141      }
142    
143      /** {@inheritDoc} */
144      @RunsInEDT
145      public <T extends Component> T findByLabel(String label, Class<T> type) {
146        return findByLabel(label, type, requireShowing());
147      }
148    
149      /** {@inheritDoc} */
150      @RunsInEDT
151      public <T extends Component> T findByLabel(String label, Class<T> type, boolean showing) {
152        Component found = find(new LabelMatcher(label, type, showing));
153        return labelFor(found, type);
154      }
155    
156      /** {@inheritDoc} */
157      @RunsInEDT
158      public Component findByLabel(String label) {
159        return findByLabel(label, requireShowing());
160      }
161    
162      /** {@inheritDoc} */
163      @RunsInEDT
164      public Component findByLabel(String label, boolean showing) {
165        Component found = find(new LabelMatcher(label, showing));
166        return labelFor(found, Component.class);
167      }
168    
169      /** {@inheritDoc} */
170      @RunsInEDT
171      public <T extends Component> T find(GenericTypeMatcher<T> m) {
172        Component found = find((ComponentMatcher)m);
173        return m.supportedType().cast(found);
174      }
175    
176      /** {@inheritDoc} */
177      @RunsInEDT
178      public Component find(ComponentMatcher m) {
179        return find(hierarchy, m);
180      }
181    
182      /** {@inheritDoc} */
183      @RunsInEDT
184      public <T extends Component> T findByName(Container root, String name, Class<T> type) {
185        return findByName(root, name, type, requireShowing());
186      }
187    
188      /** {@inheritDoc} */
189      @RunsInEDT
190      public <T extends Component> T findByName(Container root, String name, Class<T> type, boolean showing) {
191        Component found = find(root, new NameMatcher(name, type, showing));
192        return type.cast(found);
193      }
194    
195      /** {@inheritDoc} */
196      @RunsInEDT
197      public Component findByName(Container root, String name) {
198        return findByName(root, name, requireShowing());
199      }
200    
201      /** {@inheritDoc} */
202      @RunsInEDT
203      public Component findByName(Container root, String name, boolean showing) {
204        return find(root, new NameMatcher(name, showing));
205      }
206    
207      /** {@inheritDoc} */
208      @RunsInEDT
209      public <T extends Component> T findByLabel(Container root, String label, Class<T> type) {
210        return findByLabel(root, label, type, requireShowing());
211      }
212    
213      /** {@inheritDoc} */
214      @RunsInEDT
215      public <T extends Component> T findByLabel(Container root, String label, Class<T> type, boolean showing) {
216        Component found = find(root, new LabelMatcher(label, type, showing));
217        return labelFor(found, type);
218      }
219    
220      /** {@inheritDoc} */
221      @RunsInEDT
222      public Component findByLabel(Container root, String label) {
223        return findByLabel(root, label, requireShowing());
224      }
225    
226      private boolean requireShowing() {
227        return requireShowingFromSettingsOr(false);
228      }
229    
230      /** {@inheritDoc} */
231      @RunsInEDT
232      public Component findByLabel(Container root, String label, boolean showing) {
233        Component found = find(root, new LabelMatcher(label, showing));
234        return labelFor(found, Component.class);
235      }
236    
237      private <T> T labelFor(Component label, Class<T> type) {
238        assertThat(label).isInstanceOf(JLabel.class);
239        Component target = ((JLabel)label).getLabelFor();
240        assertThat(target).isInstanceOf(type);
241        return type.cast(target);
242      }
243    
244      /** {@inheritDoc} */
245      @RunsInEDT
246      public <T extends Component> T find(Container root, GenericTypeMatcher<T> m) {
247        Component found = find(root, (ComponentMatcher)m);
248        return m.supportedType().cast(found);
249      }
250    
251      /** {@inheritDoc} */
252      @RunsInEDT
253      public Component find(Container root, ComponentMatcher m) {
254        return find(hierarchy(root), m);
255      }
256    
257      @RunsInEDT
258      private Component find(ComponentHierarchy h, ComponentMatcher m)  {
259        Collection<Component> found = finderDelegate.find(h, m);
260        if (found.isEmpty()) throw componentNotFound(h, m);
261        if (found.size() > 1) throw multipleComponentsFound(found, m);
262        return found.iterator().next();
263      }
264    
265      @RunsInEDT
266      private ComponentLookupException componentNotFound(ComponentHierarchy h, ComponentMatcher m) {
267        String message = concat("Unable to find component using matcher ", m, ".");
268        if (includeHierarchyIfComponentNotFound())
269          message = concat(message,
270              LINE_SEPARATOR, LINE_SEPARATOR, "Component hierarchy:", LINE_SEPARATOR, formattedHierarchy(root(h)));
271        throw new ComponentLookupException(message);
272      }
273    
274      private static Container root(ComponentHierarchy h) {
275        if (h instanceof SingleComponentHierarchy) return ((SingleComponentHierarchy)h).root();
276        return null;
277      }
278    
279      @RunsInEDT
280      private String formattedHierarchy(Container root) {
281        ByteArrayOutputStream out = new ByteArrayOutputStream();
282        PrintStream printStream = new PrintStream(out, true);
283        printer.printComponents(printStream, root);
284        printStream.flush();
285        return new String(out.toByteArray());
286      }
287    
288      @RunsInEDT
289      private static ComponentLookupException multipleComponentsFound(Collection<Component> found, ComponentMatcher m) {
290        StringBuilder message = new StringBuilder();
291        message.append("Found more than one component using matcher ").append(m).append(".").append(LINE_SEPARATOR)
292               .append(LINE_SEPARATOR)
293               .append("Found:");
294        appendComponents(message, found);
295        if (!found.isEmpty()) message.append(LINE_SEPARATOR);
296        throw new ComponentLookupException(message.toString(), found);
297      }
298    
299      @RunsInEDT
300      private static void appendComponents(final StringBuilder message, final Collection<Component> found) {
301        execute(new GuiTask() {
302          protected void executeInEDT() {
303            for (Component c : found) message.append(LINE_SEPARATOR).append(format(c));
304          }
305        });
306      }
307    
308      /** {@inheritDoc} */
309      public boolean includeHierarchyIfComponentNotFound() {
310        return includeHierarchyInComponentLookupException;
311      }
312    
313      /** {@inheritDoc} */
314      public void includeHierarchyIfComponentNotFound(boolean newValue) {
315        includeHierarchyInComponentLookupException = newValue;
316      }
317    
318      /** {@inheritDoc} */
319      public Collection<Component> findAll(ComponentMatcher m) {
320        return finderDelegate.find(hierarchy, m);
321      }
322    
323      /** {@inheritDoc} */
324      public Collection<Component> findAll(Container root, ComponentMatcher m) {
325        return finderDelegate.find(hierarchy(root), m);
326      }
327    
328      /** {@inheritDoc} */
329      public <T extends Component> Collection<T> findAll(GenericTypeMatcher<T> m) {
330        return finderDelegate.find(hierarchy, m);
331      }
332    
333      /** {@inheritDoc} */
334      public <T extends Component> Collection<T> findAll(Container root, GenericTypeMatcher<T> m) {
335        return finderDelegate.find(hierarchy(root), m);
336      }
337    
338      /**
339       * Returns the value of the flag "requireShowing" in the <code>{@link ComponentLookupScope}</code> this finder's
340       * <code>{@link Settings}</code>. If the settings object is <code>null</code>, this method will return the provided
341       * default value.
342       * @param defaultValue the value to return if this matcher does not have any configuration settings.
343       * @return the value of the flag "requireShowing" in this finder's settings, or the provided default value if this
344       * finder does not have configuration settings.
345       */
346      protected final boolean requireShowingFromSettingsOr(boolean defaultValue) {
347        if (settings == null) return defaultValue;
348        return settings.componentLookupScope().requireShowing();
349      }
350    
351      private ComponentHierarchy hierarchy(Container root) {
352        if (root == null) return hierarchy;
353        return new SingleComponentHierarchy(root, hierarchy);
354      }
355    }