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 }