Repository: appium/java-client
Issue: 5.0.0-BETA2: ClassCastException: WebElement -> IOSElement

## Description
Failed to cast WebElement into IOSElement. Worked with 5.0.0-BETA1.

## Environment

* java client build version or git revision if you use some shapshot: 5.0.0-BETA2
* Appium server version or git revision if you use some shapshot: 1.6.4-beta
* Mobile platform/version under test: iOS
* Real device or emulator/simulator: Simulator

## Code To Reproduce Issue [ Good To Have ]
`((IOSElement) driver.findElement(MobileBy.className("XCUIElementTypePickerWheel"))).setValue(countryName);`

## Ecxeption stacktraces
java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebElement cannot be cast to io.appium.java_client.ios.IOSElement



Changed files:
src/main/java/io/appium/java_client/internal/ElementMap.java
src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java
src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java
src/main/java/io/appium/java_client/AppiumDriver.java
src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java
src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java
src/main/java/io/appium/java_client/HasSessionDetails.java

diff --git a/src/main/java/io/appium/java_client/AppiumDriver.java b/src/main/java/io/appium/java_client/AppiumDriver.java
index a3adf2e9..fa9696cf 100644
--- a/src/main/java/io/appium/java_client/AppiumDriver.java
+++ b/src/main/java/io/appium/java_client/AppiumDriver.java
@@ -76,7 +76,6 @@ public class AppiumDriver<T extends WebElement>
     private ExecuteMethod executeMethod;
     private final String platformName;
     private final String automationName;
-    private String currentContext;
 
 
     /**
@@ -92,19 +91,6 @@ public class AppiumDriver<T extends WebElement>
         locationContext = new RemoteLocationContext(executeMethod);
         super.setErrorHandler(errorHandler);
         this.remoteAddress = executor.getAddressOfRemoteServer();
-        final AppiumDriver<?> driver = this;
-
-        HasSessionDetails hasSessionDetails = new HasSessionDetails() {
-            @Override
-            public Response execute(String driverCommand, Map<String, ?> parameters) {
-                return driver.execute(driverCommand, parameters);
-            }
-
-            @Override
-            public Response execute(String driverCommand) {
-                return driver.execute(driverCommand);
-            }
-        };
 
         Object capabilityPlatform1 = getCapabilities().getCapability(PLATFORM_NAME);
         Object capabilityAutomation1 = getCapabilities().getCapability(AUTOMATION_NAME);
@@ -112,15 +98,14 @@ public class AppiumDriver<T extends WebElement>
         Object capabilityPlatform2 = capabilities.getCapability(PLATFORM_NAME);
         Object capabilityAutomation2 = capabilities.getCapability(AUTOMATION_NAME);
 
-        platformName = ofNullable(ofNullable(hasSessionDetails.getPlatformName())
+        platformName = ofNullable(ofNullable(super.getPlatformName())
                 .orElse(capabilityPlatform1 != null ? String.valueOf(capabilityPlatform1) : null))
                 .orElse(capabilityPlatform2 != null ? String.valueOf(capabilityPlatform2) : null);
-        automationName = ofNullable(ofNullable(hasSessionDetails.getAutomationName())
+        automationName = ofNullable(ofNullable(super.getAutomationName())
                 .orElse(capabilityAutomation1 != null ? String.valueOf(capabilityAutomation1) : null))
                 .orElse(capabilityAutomation2 != null ? String.valueOf(capabilityAutomation2) : null);
 
         this.setElementConverter(new JsonToMobileElementConverter(this, this));
-        currentContext = getContext();
     }
 
     public AppiumDriver(URL remoteAddress, Capabilities desiredCapabilities) {
@@ -352,7 +337,6 @@ public class AppiumDriver<T extends WebElement>
     @Override public WebDriver context(String name) {
         checkNotNull(name, "Must supply a context name");
         execute(DriverCommand.SWITCH_TO_CONTEXT, ImmutableMap.of("name", name));
-        currentContext = name;
         return this;
     }
 
@@ -430,9 +414,6 @@ public class AppiumDriver<T extends WebElement>
     }
 
     @Override public boolean isBrowser() {
-        if  (super.isBrowser()) {
-            return true;
-        }
-        return !currentContext.toLowerCase().contains("NATIVE_APP".toLowerCase());
+        return !getContext().toLowerCase().contains("NATIVE_APP".toLowerCase());
     }
 }
diff --git a/src/main/java/io/appium/java_client/HasSessionDetails.java b/src/main/java/io/appium/java_client/HasSessionDetails.java
index 2e91b7b8..3106b290 100644
--- a/src/main/java/io/appium/java_client/HasSessionDetails.java
+++ b/src/main/java/io/appium/java_client/HasSessionDetails.java
@@ -55,8 +55,5 @@ public interface HasSessionDetails extends ExecutesMethod {
     /**
      * @return is focus on browser or on native content.
      */
-    default boolean isBrowser() {
-        Object browserName = getSessionDetail("browserName");
-        return browserName != null && !isBlank(String.valueOf(browserName));
-    }
+    boolean isBrowser();
 }
diff --git a/src/main/java/io/appium/java_client/internal/ElementMap.java b/src/main/java/io/appium/java_client/internal/ElementMap.java
index 3dacc2ce..9435a7e7 100644
--- a/src/main/java/io/appium/java_client/internal/ElementMap.java
+++ b/src/main/java/io/appium/java_client/internal/ElementMap.java
@@ -18,6 +18,7 @@ package io.appium.java_client.internal;
 
 import com.google.common.collect.ImmutableMap;
 
+import io.appium.java_client.HasSessionDetails;
 import io.appium.java_client.MobileElement;
 import io.appium.java_client.android.AndroidElement;
 import io.appium.java_client.ios.IOSElement;
@@ -69,14 +70,17 @@ public enum ElementMap {
     }
 
     /**
-     * @param platform platform name.
-     * @param automation automation name.
+     * @param hasSessionDetails something that implements {@link io.appium.java_client.HasSessionDetails}.
      * @return subclass of {@link io.appium.java_client.MobileElement} that convenient to current session details.
      */
-    public static Class<? extends RemoteWebElement> getElementClass(String platform, String automation) {
+    public static Class<? extends RemoteWebElement> getElementClass(HasSessionDetails hasSessionDetails) {
+        if (hasSessionDetails == null) {
+            return RemoteWebElement.class;
+        }
         ElementMap element = Optional.ofNullable(mobileElementMap.get(String
-                .valueOf(automation).toLowerCase().trim()))
-                .orElse(mobileElementMap.get(String.valueOf(platform).toLowerCase().trim()));
+                .valueOf(hasSessionDetails.getAutomationName()).toLowerCase().trim()))
+                .orElse(mobileElementMap
+                        .get(String.valueOf(hasSessionDetails.getPlatformName()).toLowerCase().trim()));
         if (element == null) {
             return RemoteWebElement.class;
         }
diff --git a/src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java b/src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java
index a2bb0143..103963d1 100644
--- a/src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java
+++ b/src/main/java/io/appium/java_client/internal/JsonToMobileElementConverter.java
@@ -87,13 +87,7 @@ public class JsonToMobileElementConverter extends JsonToWebElementConverter {
 
     protected RemoteWebElement newMobileElement() {
         Class<? extends RemoteWebElement> target;
-        if (hasSessionDetails.isBrowser()) {
-            target = getElementClass(null, null);
-        } else {
-            target = getElementClass(hasSessionDetails.getPlatformName(),
-                    hasSessionDetails.getAutomationName());
-        }
-
+        target = getElementClass(hasSessionDetails);
         try {
             Constructor<? extends RemoteWebElement> constructor = target.getDeclaredConstructor();
             constructor.setAccessible(true);
diff --git a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java
index 7ac6d1f0..0f7f3990 100644
--- a/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java
+++ b/src/main/java/io/appium/java_client/pagefactory/AppiumFieldDecorator.java
@@ -72,18 +72,8 @@ public class AppiumFieldDecorator implements FieldDecorator {
     private final String platform;
     private final String automation;
     private final TimeOutDuration timeOutDuration;
+    private final HasSessionDetails hasSessionDetails;
 
-    private static String extractSessionData(WebDriver driver, Supplier<String> dataSupplier) {
-        if (driver == null) {
-            return null;
-        }
-
-        if (!(driver instanceof HasSessionDetails)) {
-            return null;
-        }
-
-        return String.valueOf(dataSupplier.get());
-    }
 
     public AppiumFieldDecorator(SearchContext context, long implicitlyWaitTimeOut,
         TimeUnit timeUnit) {
@@ -100,10 +90,17 @@ public class AppiumFieldDecorator implements FieldDecorator {
      */
     public AppiumFieldDecorator(SearchContext context, TimeOutDuration timeOutDuration) {
         this.originalDriver = unpackWebDriverFromSearchContext(context);
-        platform = extractSessionData(originalDriver, () ->
-                HasSessionDetails.class.cast(originalDriver).getPlatformName());
-        automation = extractSessionData(originalDriver, () ->
-                HasSessionDetails.class.cast(originalDriver).getAutomationName());
+        if (originalDriver == null
+                || !HasSessionDetails.class.isAssignableFrom(originalDriver.getClass())) {
+            hasSessionDetails = null;
+            platform = null;
+            automation = null;
+        } else {
+            hasSessionDetails = HasSessionDetails.class.cast(originalDriver);
+            platform = hasSessionDetails.getPlatformName();
+            automation = hasSessionDetails.getAutomationName();
+        }
+
         this.timeOutDuration = timeOutDuration;
 
         defaultElementFieldDecoracor = new DefaultFieldDecorator(
@@ -221,6 +218,6 @@ public class AppiumFieldDecorator implements FieldDecorator {
 
     private WebElement proxyForAnElement(ElementLocator locator) {
         ElementInterceptor elementInterceptor = new ElementInterceptor(locator, originalDriver);
-        return getEnhancedProxy(getElementClass(platform, automation), elementInterceptor);
+        return getEnhancedProxy(getElementClass(hasSessionDetails), elementInterceptor);
     }
 }
diff --git a/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java b/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java
index ae2d1112..dc1d8947 100644
--- a/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java
+++ b/src/test/java/io/appium/java_client/appium/element/generation/android/AndroidElementGeneratingTest.java
@@ -13,7 +13,6 @@ import io.appium.java_client.remote.MobileCapabilityType;
 import io.appium.java_client.remote.MobilePlatform;
 import org.junit.Test;
 import org.openqa.selenium.remote.DesiredCapabilities;
-import org.openqa.selenium.remote.RemoteWebElement;
 
 import java.io.File;
 import java.util.function.Supplier;
@@ -49,7 +48,7 @@ public class AndroidElementGeneratingTest extends BaseElementGenerationTest {
         }, (by, aClass) -> {
                 driver.context("WEBVIEW_io.appium.android.apis");
                 return commonPredicate.test(by, aClass);
-            }, tagName("a"), RemoteWebElement.class));
+            }, tagName("a"), AndroidElement.class));
     }
 
     @Test public void whenAndroidBrowserIsLaunched() {
@@ -65,6 +64,6 @@ public class AndroidElementGeneratingTest extends BaseElementGenerationTest {
             }, (by, aClass) -> {
                 driver.get("https://www.google.com");
                 return commonPredicate.test(by, aClass);
-            }, className("gsfi"), RemoteWebElement.class));
+            }, className("gsfi"), AndroidElement.class));
     }
 }
diff --git a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java
index 3c4cd9e7..3d0d995e 100644
--- a/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java
+++ b/src/test/java/io/appium/java_client/appium/element/generation/ios/IOSElementGenerationTest.java
@@ -14,7 +14,6 @@ import io.appium.java_client.remote.MobilePlatform;
 import org.junit.Test;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.remote.DesiredCapabilities;
-import org.openqa.selenium.remote.RemoteWebElement;
 
 import java.io.File;
 import java.util.function.Function;
@@ -89,7 +88,7 @@ public class IOSElementGenerationTest extends BaseElementGenerationTest {
                     }
                 });
                 return commonPredicate.test(by, aClass);
-            }, className("gsfi"), RemoteWebElement.class));
+            }, className("gsfi"), IOSElement.class));
     }
 
     @Test public void whenIOSBrowserIsLaunched() {
@@ -97,7 +96,7 @@ public class IOSElementGenerationTest extends BaseElementGenerationTest {
                 clientBrowserCapabilitiesSupplier, (by, aClass) -> {
                 driver.get("https://www.google.com");
                 return commonPredicate.test(by, aClass);
-            }, className("gsfi"), RemoteWebElement.class));
+            }, className("gsfi"), IOSElement.class));
     }
 
     @Test
@@ -117,6 +116,6 @@ public class IOSElementGenerationTest extends BaseElementGenerationTest {
         }, clientBrowserCapabilitiesSupplier, (by, aClass) -> {
                 driver.get("https://www.google.com");
                 return commonPredicate.test(by, aClass);
-            }, className("gsfi"), RemoteWebElement.class));
+            }, className("gsfi"), IOSElement.class));
     }
 }