All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.tsystems.mms.tic.testframework.mobile.pageobject.AppiumClassFinder Maven / Gradle / Ivy

There is a newer version: 2.5
Show newest version
/*
 * Testerra
 *
 * (C) 2023, Martin Großmann, Deutsche Telekom MMS GmbH, Deutsche Telekom AG
 *
 * Deutsche Telekom AG and all other contributors /
 * copyright owners license this file to you under the Apache
 * License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package eu.tsystems.mms.tic.testframework.mobile.pageobject;

import eu.tsystems.mms.tic.testframework.logging.Loggable;
import eu.tsystems.mms.tic.testframework.mobile.driver.MobileOsChecker;
import eu.tsystems.mms.tic.testframework.pageobjects.Page;
import eu.tsystems.mms.tic.testframework.pageobjects.PageObject;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created on 2023-03-13
 *
 * @author mgn
 */
public class AppiumClassFinder implements Loggable {

    private static AppiumClassFinder INSTANCE;

    /**
     * This call takes some time. It has an impact to the duration of the first page check (takes ca 2-3 seconds longer).
     */
    private final Reflections reflections = new Reflections(filter(configure()));

    private AppiumClassFinder() {
    }

    public static AppiumClassFinder getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new AppiumClassFinder();
        }
        return INSTANCE;
    }

    public  Class getBestMatchingClass(Class baseClass, WebDriver webDriver) {
        MobileOsChecker osChecker = new MobileOsChecker();
        Platform platform = osChecker.getPlatform(webDriver);

        Class matchedClass = AppiumClassFinder.Caches.getCache(baseClass, platform);

        if (matchedClass == null) {
            // scan and fill cache
            this.findSubPagesOf(baseClass);
            matchedClass = AppiumClassFinder.Caches.getCache(baseClass, platform);

            if (matchedClass == null) {
                throw new RuntimeException("Something went wrong scanning this class for sub types: " + baseClass.getName());
            }
        }

        return (Class) matchedClass;
    }

    private  void findSubPagesOf(final Class pageClass) {
        log().debug("Searching for subtypes of class {}", pageClass);
        Class baseClass = null;
        if (!Modifier.isAbstract(pageClass.getModifiers())) {
            baseClass = pageClass;
            AppiumClassFinder.Caches.setCache(baseClass, this.getMatchingPlatform(baseClass.getSimpleName()), baseClass);
        }

        Set> subClasses = reflections.getSubTypesOf((Class) baseClass);
        for (Class subClass : subClasses) {
            String subClassName = subClass.getSimpleName();

            if (Modifier.isAbstract(subClass.getModifiers())) {
                log().debug("Not taking {} into consideration, because it is abstract", subClassName);
            } else {
                if (this.matchingPattern(subClassName, baseClass.getSimpleName())) {
                    AppiumClassFinder.Caches.setCache(baseClass, this.getMatchingPlatform(subClassName), subClass);
                }
            }
        }
    }

    public void clearCache() {
        AppiumClassFinder.Caches.IMPLEMENTATIONS_CACHE.clear();
    }

    private boolean matchingPattern(String className, String baseClassName) {
        Pattern pattern = Pattern.compile("(?i)^(ios|android)" + baseClassName);
        Matcher matcher = pattern.matcher(className);
        return matcher.find();
    }

    private Platform getMatchingPlatform(String className) {
        if (className.toLowerCase().contains(Platform.IOS.toString().toLowerCase())) {
            return Platform.IOS;
        }
        if (className.toLowerCase().contains(Platform.ANDROID.toString().toLowerCase())) {
            return Platform.ANDROID;
        }
        return Platform.ANY;
    }

    private ConfigurationBuilder configure() {
        return new ConfigurationBuilder().setUrls(ClasspathHelper.forJavaClassPath());
    }

    /**
     * This method should prune resources we are not interested in, but not change the interesting results.
     */
    private ConfigurationBuilder filter(final ConfigurationBuilder configuration) {
        configuration.setScanners(new SubTypesScanner()); // drops TypeAnnotationScanner
        configuration.useParallelExecutor();
        return configuration;
    }

    /**
     * Store already found classes in a map like:
     * MainClass.class: [
     * Platform.ANY:       MainClass.class
     * Platform.IOS:       IOSMainClass.class
     * Platform.ANDROID:   AndroidMainClass.class
     * ]
     */
    private static class Caches {

        private static final Platform DEFAULT_PLATFORM = Platform.ANY;

        private static final Map, Map>> IMPLEMENTATIONS_CACHE = new ConcurrentHashMap<>();

        private static Class getCache(Class pageClass, Platform platform) {
            if (platform != Platform.ANDROID && platform != Platform.IOS) {
                platform = DEFAULT_PLATFORM;
            }

            synchronized (IMPLEMENTATIONS_CACHE) {
                if (!IMPLEMENTATIONS_CACHE.containsKey(pageClass)) {
                    return null;
                }

                Map> map = IMPLEMENTATIONS_CACHE.get(pageClass);
                Class matchedClass = map.get(platform);
                if (matchedClass == null) {
                    matchedClass = map.get(DEFAULT_PLATFORM);
                }
                return matchedClass;
            }
        }

        /**
         * Returns the best matching class from map. If no platform specific map exists, the baseclass will return.
         */
        private static void setCache(Class pageClass, Platform platform, Class prioritizedClassInfos) {
            if (platform != Platform.ANDROID && platform != Platform.IOS) {
                platform = DEFAULT_PLATFORM;
            }

            synchronized (IMPLEMENTATIONS_CACHE) {
                if (!IMPLEMENTATIONS_CACHE.containsKey(pageClass)) {
                    IMPLEMENTATIONS_CACHE.put(pageClass, new HashMap<>());
                }
                Map> map = IMPLEMENTATIONS_CACHE.get(pageClass);

                map.put(platform, prioritizedClassInfos);
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy