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 }