1   package com.atlassian.pageobjects.elements.query.webdriver;
2   
3   import com.atlassian.pageobjects.elements.WebDriverLocatable;
4   import com.atlassian.webdriver.AtlassianWebDriver;
5   import com.google.common.base.Function;
6   import com.google.common.base.Supplier;
7   import org.openqa.selenium.NoSuchElementException;
8   import org.openqa.selenium.StaleElementReferenceException;
9   import org.openqa.selenium.WebElement;
10  
11  import static com.atlassian.pageobjects.elements.util.StringConcat.asString;
12  import static com.google.common.base.Preconditions.checkNotNull;
13  
14  /**
15   * <p>
16   * {@link com.atlassian.pageobjects.elements.WebDriverLocatable} based timed query that retrieves
17   * {@link org.openqa.selenium.WebElement} using provided {@link com.atlassian.pageobjects.elements.WebDriverLocatable}
18   * and applies provided function from that element to the target value.
19   *
20   * <p>
21   * If given element is not found, the 'invalid value' semantics of the timed query are applied. 
22   *
23   */
24  public class WebDriverLocatableBasedTimedQuery<T> extends GenericWebDriverTimedQuery<T>
25  {
26      private final WebDriverLocatable locatable;
27  
28      public WebDriverLocatableBasedTimedQuery(WebDriverLocatable locatable, AtlassianWebDriver driver,
29                                               Function<WebElement, T> valueProvider, long timeout)
30      {
31          super(new LocatableBasedSupplier<T>(driver, locatable, valueProvider), timeout);
32          this.locatable = locatable;
33      }
34  
35      public WebDriverLocatableBasedTimedQuery(WebDriverLocatable locatable, AtlassianWebDriver driver,
36                                               Function<WebElement, T> valueProvider, long timeout, long interval)
37      {
38          super(new LocatableBasedSupplier<T>(driver, locatable, valueProvider), timeout, interval);
39          this.locatable = locatable;
40      }
41  
42      public WebDriverLocatableBasedTimedQuery(WebDriverLocatable locatable, AtlassianWebDriver driver,
43                                               Function<WebElement, T> valueProvider, long timeout, long interval,
44                                               T invalidValue)
45      {
46          super(new LocatableBasedSupplier<T>(driver, locatable, valueProvider, invalidValue), timeout, interval);
47          this.locatable = locatable;
48      }
49  
50      public WebDriverLocatableBasedTimedQuery(WebDriverLocatableBasedTimedQuery<T> origin, long timeout)
51      {
52          super(origin.webElementSupplier(), timeout, origin.interval);
53          this.locatable = origin.locatable;
54      }
55  
56      LocatableBasedSupplier<T> webElementSupplier()
57      {
58          return (LocatableBasedSupplier<T>) valueSupplier;
59      }
60  
61      @Override
62      public String toString()
63      {
64          return asString(super.toString(), "[locatable=", locatable, "]");
65      }
66  
67      private static class LocatableBasedSupplier<S> implements Supplier<S>
68      {
69          private final AtlassianWebDriver webDriver;
70          private final WebDriverLocatable locatable;
71          private final Function<WebElement, S> valueProvider;
72          private final S invalidValue;
73  
74           public LocatableBasedSupplier(AtlassianWebDriver webDriver ,WebDriverLocatable locatable,
75                                         Function<WebElement, S> valueProvider, S invalid)
76          {
77              this.valueProvider = valueProvider;
78              this.webDriver = webDriver;
79              this.locatable = checkNotNull(locatable);
80              this.invalidValue = invalid;
81          }
82  
83          public LocatableBasedSupplier(AtlassianWebDriver webDriver, WebDriverLocatable locatable, Function<WebElement, S> valueProvider)
84          {
85              this(webDriver, locatable, valueProvider, null);
86          }
87  
88          public S get()
89          {
90              try
91              {
92                  return valueProvider.apply((WebElement) locatable.waitUntilLocated(webDriver, 0));
93              }
94              catch(StaleElementReferenceException e)
95              {
96                  // element became stale between the time we got it from the locatable and we called the
97                  //  value provider. Error out here and try on next poll.
98                  throw new InvalidValue(invalidValue);
99              }
100             catch (NoSuchElementException e1)
101             {
102                 throw new InvalidValue(invalidValue);
103             }
104         }
105     }
106 }