1 /* 2 * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.jemmy.lookup; 24 25 import java.io.PrintStream; 26 import java.util.List; 27 import org.jemmy.interfaces.*; 28 import java.util.ArrayList; 29 import java.util.HashMap; 30 import java.util.Map; 31 import org.jemmy.control.Wrap; 32 import org.jemmy.control.Wrapper; 33 import org.jemmy.env.Environment; 34 import org.jemmy.env.TestOut; 35 import org.jemmy.timing.State; 36 37 /** 38 * Default implementation of Lookup. Regularly, it is enough just override this 39 * implementation and allow it to handle sub-lookups creation. 40 * @see AbstractLookup#lookup(org.jemmy.lookup.LookupCriteria) 41 * @see AbstractLookup#lookup(java.lang.Class, org.jemmy.lookup.LookupCriteria) 42 * @param <CONTROL> 43 * @author shura 44 */ 45 public abstract class AbstractLookup<CONTROL> extends AbstractParent<CONTROL> implements Lookup<CONTROL> { 46 47 static final String PREFIX_DELTA = "| "; 48 private ArrayList<CONTROL> found; 49 Environment env; 50 private Class<CONTROL> clss; 51 private LookupCriteria<CONTROL> criteria = null; 52 private Wrapper wrapper; 53 54 /** 55 * Identifies output where lookup progress is printed. 56 * @see Environment#getOutput(java.lang.String) 57 * @see AbstractLookup#wait(int) 58 * @see AbstractLookup#size() 59 */ 60 public static final String OUTPUT = AbstractLookup.class.getName() + ".OUTPUT"; 61 62 63 static { 64 Environment.getEnvironment().initTimeout(WAIT_CONTROL_TIMEOUT); 65 Environment.getEnvironment().initOutput(OUTPUT, TestOut.getNullOutput()); 66 } 67 68 /** 69 * This actual lookup logic is delegated to <code>getCildren(java.lang.Object)</code> 70 * method 71 * @see AbstractLookup#getChildren(java.lang.Object) 72 * @param env 73 * @param controlClass 74 * @param criteria 75 * @param wrapper 76 */ 77 public AbstractLookup(Environment env, Class<CONTROL> controlClass, LookupCriteria<CONTROL> criteria, Wrapper wrapper) { 78 this.env = env; 79 found = new ArrayList<CONTROL>(); 80 this.clss = controlClass; 81 this.criteria = criteria; 82 this.wrapper = wrapper; 83 } 84 85 Wrapper getWrapper() { 86 return wrapper; 87 } 88 89 /** 90 * 91 * @return 92 */ 93 LookupCriteria<CONTROL> getCriteria() { 94 return criteria; 95 } 96 97 /** 98 * 99 * @return 100 */ 101 Environment getEnvironment() { 102 return env; 103 } 104 105 /** 106 * 107 * @return The class of the sount controls. 108 */ 109 Class<CONTROL> getControlClass() { 110 return clss; 111 } 112 113 public <T extends CONTROL> Lookup<T> lookup(Class<T> controlClass, LookupCriteria<T> criteria) { 114 return new ClassLookupImpl<CONTROL, T>(env, this, controlClass, criteria, wrapper); 115 } 116 117 public Lookup<CONTROL> lookup(LookupCriteria<CONTROL> criteria) { 118 return new ClassLookupImpl<CONTROL, CONTROL>(env, this, clss, criteria, wrapper); 119 } 120 121 /** 122 * Waits for certain number of controls to fit criteria. 123 * Depending on how outputs set, prints out info about the lookup. 124 * @param count 125 * @return this, after the count of found number of found controls 126 * exceeds the required. 127 * @see AbstractLookup#OUTPUT 128 */ 129 public Lookup<? extends CONTROL> wait(final int count) { 130 getEnvironment().getOutput(OUTPUT).println("Waiting for " + count + " controls of " + clss.getName() + " class fitting criteria " + criteria); 131 env.getWaiter(Lookup.WAIT_CONTROL_TIMEOUT.getName()).ensureState(new State<Integer>() { 132 133 public Integer reached() { 134 if(found.size() < count) 135 refresh(); 136 return (found.size() >= count) ? found.size() : null; 137 } 138 139 @Override 140 public String toString() { 141 return "Waiting for " + count + " " + clss.getName() + " controls to be found adhering to " 142 + criteria; 143 } 144 145 }); 146 return this; 147 } 148 149 /** 150 * Gets the number of controls which fit criteria. 151 * Depending on how outputs set, prints out info about the lookup. 152 * @see AbstractLookup#OUTPUT 153 * @return 154 */ 155 public int size() { 156 getEnvironment().getOutput(OUTPUT).println("Getting number of controls of " + clss.getName() + " class fitting criteria " + criteria); 157 refresh(); 158 return found.size(); 159 } 160 161 /** 162 * 163 */ 164 void refresh() { 165 found.clear(); 166 List childen = getChildren(null); 167 if (childen != null) { 168 for (Object c : childen) { 169 add(c); 170 } 171 } 172 } 173 174 @SuppressWarnings("element-type-mismatch") 175 private void add(Object subparent) { 176 if (subparent != null/* && !found.contains(subparent)*/) { 177 if (clss.isInstance(subparent) && check(clss.cast(subparent))) { 178 found.add(clss.cast(subparent)); 179 } 180 List childen = getChildren(subparent); 181 if (childen != null) { 182 for (Object child : childen) { 183 add(child); 184 } 185 } 186 } 187 } 188 189 /** 190 * 191 * @param control 192 * @return 193 */ 194 protected boolean check(CONTROL control) { 195 return control != null && criteria.check(control); 196 } 197 198 /** 199 * Returns Wrap of the control with specified index 200 * @param index 201 * @return Wrap 202 */ 203 public Wrap<? extends CONTROL> wrap(int index) { 204 return instantiate(get(index)); 205 } 206 207 /** 208 * 209 * @return 210 */ 211 public Wrap<? extends CONTROL> wrap() { 212 return wrap(0); 213 } 214 215 /** 216 * @{inheritDoc} 217 */ 218 public <INTERFACE extends ControlInterface> INTERFACE as(int index, Class<INTERFACE> interfaceClass) { 219 return wrap(index).as(interfaceClass); 220 } 221 222 /** 223 * @{inheritDoc} 224 */ 225 public <INTERFACE extends ControlInterface> INTERFACE as(Class<INTERFACE> interfaceClass) { 226 return as(0, interfaceClass); 227 } 228 229 /** 230 * @{inheritDoc} 231 */ 232 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> INTERFACE as(int index, Class<INTERFACE> interfaceClass, Class<TYPE> type) { 233 return wrap(index).as(interfaceClass, type); 234 } 235 236 /** 237 * @{inheritDoc} 238 */ 239 public <TYPE, INTERFACE extends TypeControlInterface<TYPE>> INTERFACE as(Class<INTERFACE> interfaceClass, Class<TYPE> type) { 240 return as(0, interfaceClass, type); 241 } 242 243 public CONTROL get(int index) { 244 wait(index + 1); 245 return found.get(index); 246 } 247 248 /** 249 * @{inheritDoc} 250 */ 251 public CONTROL get() { 252 return get(0); 253 } 254 255 /** 256 * 257 * @return 258 */ 259 List<CONTROL> getFound() { 260 return found; 261 } 262 263 /** 264 * 265 * @return 266 */ 267 public Class<CONTROL> getType() { 268 return getControlClass(); 269 } 270 271 /** 272 * Override this to get subchildren of the controls in the hierarchy. 273 * List could contain any type of objects - not just <code>CONTROL</code>. 274 * @param subParent - one of the elements in the hierarchy reflected by this lookup. 275 * If null passed - first level children are expected. 276 * @return 277 */ 278 abstract List getChildren(Object subParent); 279 280 private String buildClassChain(Class cls) { 281 StringBuilder sb = new StringBuilder(cls.getName()); 282 if (getType().isInterface()) { 283 sb.append(" implements ").append(getType().getName()); 284 } else { 285 do { 286 cls = cls.getSuperclass(); 287 sb.append(" <- ").append(cls.getName()); 288 } while (!cls.equals(getType()) && !cls.equals(Object.class)); 289 } 290 return sb.toString(); 291 } 292 293 /** 294 * Wraps the control with a <code>Wrap</code> class. 295 * @see Wrap 296 * @param wrap 297 * @return Wrap 298 */ 299 private Wrap<? extends CONTROL> instantiate(CONTROL control) { 300 return wrapper.wrap(clss, control); 301 } 302 303 /** 304 * 305 * @param out 306 * @param obj 307 * @param prefix 308 */ 309 protected void dumpOne(PrintStream out, CONTROL obj, String prefix) { 310 Map<String, Object> data = getWrapper().wrap(getControlClass(), getControlClass().cast(obj)).getPropertiesQiuet(); 311 out.println(prefix + "+-" + buildClassChain(obj.getClass())); 312 for (String key : data.keySet()) { 313 out.print(prefix + PREFIX_DELTA + " " + key + "="); 314 if (data.get(key) == null) { 315 out.println("null"); 316 } else { 317 out.println(data.get(key)); 318 } 319 } 320 } 321 322 /** 323 * 324 * @param out 325 * @param lookup 326 */ 327 protected abstract void dump(PrintStream out, Lookup<? extends CONTROL> lookup); 328 329 public void dump(PrintStream out) { 330 dump(out, this); 331 } 332 /* 333 static class LookupImpl<T> extends AbstractLookup<T> { 334 335 AbstractLookup<T> parent; 336 337 public LookupImpl(Environment env, AbstractLookup<T> parent, Class<T> clss, LookupCriteria<T> criteria) { 338 super(env, clss, criteria); 339 this.parent = parent; 340 } 341 342 @Override 343 public List<T> getChildren(T subParent) { 344 return getFound(); 345 } 346 347 @Override 348 public Wrap<? extends T> instantiate(T control) { 349 return parent.instantiate(control); 350 } 351 352 @Override 353 protected void refresh() { 354 parent.refresh(); 355 super.refresh(); 356 } 357 358 } 359 */ 360 361 static class ClassLookupImpl<T, ST extends T> extends AbstractLookup<ST> { 362 363 AbstractLookup<T> parent; 364 Class<ST> cls; 365 366 public ClassLookupImpl(Environment env, AbstractLookup<T> parent, Class<ST> cls, LookupCriteria<ST> criteria, Wrapper wrapper) { 367 super(env, cls, criteria, wrapper); 368 this.cls = cls; 369 this.parent = parent; 370 } 371 372 @Override 373 protected boolean check(ST control) { 374 return getControlClass().isInstance(control) && super.check(control); 375 } 376 377 @Override 378 public List getChildren(Object subParent) { 379 return getFound(); 380 } 381 382 @Override 383 protected void refresh() { 384 parent.refresh(); 385 for (T next : parent.found) { 386 if (cls.isInstance(next) && check(cls.cast(next))) { 387 super.found.add(cls.cast(next)); 388 } 389 } 390 } 391 392 @Override 393 protected void dump(PrintStream out, Lookup<? extends ST> lookup) { 394 parent.dump(out, lookup); 395 } 396 } 397 /* 398 static class ClassLookupImpl<ST> extends AbstractLookup<ST> { 399 400 AbstractLookup parent; 401 402 public ClassLookupImpl(Environment env, AbstractLookup parent, Class<ST> cls, LookupCriteria<ST> criteria) { 403 super(env, cls, criteria); 404 this.parent = parent; 405 } 406 407 @Override 408 protected boolean check(ST wrap) { 409 return getControlClass().isInstance(wrap) && super.check(wrap); 410 } 411 412 @Override 413 public List getChildren(Object subParent) { 414 return getFound(); 415 } 416 417 @Override 418 public Wrap<? extends ST> instantiate(ST wrap) { 419 return parent.instantiate(wrap); 420 } 421 422 @Override 423 protected void refresh() { 424 parent.refresh(); 425 super.refresh(); 426 } 427 428 } 429 */ 430 /* 431 class ClassParent<T extends CONTROL> implements Parent<T> { 432 433 Class clss; 434 435 ClassParent(Class clss) { 436 this.clss = clss; 437 } 438 439 public Wrap<? extends T> instantiate(T wrap) { 440 return (Wrap<? extends T>) AbstractLookup.this.instantiate((CONTROL) wrap); 441 } 442 443 public Lookup<T> lookup(final LookupCriteria<T> criteria) { 444 return new LookupImpl<T>(env, this, new TypedLookup<T>(clss) { 445 446 @Override 447 public boolean checkControl(T wrap) { 448 return criteria.check(wrap); 449 } 450 }); 451 } 452 } 453 */ 454 }