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 }