<commit_before>package com.addonovan.ftcext.control

import android.widget.Spinner
import com.addonovan.ftcext.*
import com.addonovan.ftcext.config.*
import com.addonovan.ftcext.reflection.FieldFinder
import com.qualcomm.robotcore.hardware.*
import com.qualcomm.robotcore.robocol.Telemetry
import java.util.*

/**
 * The OpMode class built upon Qualcomm's. This class provides more utility
 * than the standard class does, and *must* be used in order to be able
 * to access the other functionality of this library.
 *
 * @see OpMode
 * @see LinearOpMode
 * @see Register
 *
 * @author addonovan
 * @since 6/12/16
 */
abstract class AbstractOpMode()
{

    //
    // Values
    //

    /** The name that this OpMode is registered as in the Registrar. */
    val RegisteredName by lazy()
    {
        getRegisterName( javaClass );
    }

    /** The first controller. */
    val gamepad1: Gamepad
            get() = Hardware.gamepad1;

    /** The second controller. */
    val gamepad2: Gamepad
            get() = Hardware.gamepad2;

    /** Telemetry for sending back current states and what not. */
    val telemetry: Telemetry
            get() = Hardware.telemetry;

    /** Direct access to the hardware map. Directly referencing this is discouraged. */
    val hardwareMap: HardwareMap
            get() = Hardware.hardwareMap;

    //
    // Constructors
    //

    init
    {
        loadConfigs( CONFIG_FILE ); // load the config whenever the OpMode is about to execute

        val configs = getOpModeConfigs( RegisteredName );

        // prompt the user which variant to use if there's more than one
        if ( configs.size > 1 )
        {
            val variantList = ArrayList< String >();
            configs.forEach { config -> variantList += config.Variant };

            spinnerDialog(
                    "Choose a variant",
                    "Choose a configuration variant to use",
                    variantList.toArray( arrayOf< String >() ),
                    { string ->
                        config = getOpModeConfig( RegisteredName, string ) // sets the config to the correct variant
                    }
            );
        }
    }

    //
    // Things from Original OpMode
    //

    /**
     * Initialize the robot. This occurs directly after the OpMode
     * is selected.
     */
    abstract fun init();

    /**
     * Called the moment that the start button is pressed on the
     * Driver Station remote. This is the first code that is executed
     * when an OpMode is running.
     */
    open fun start() {};

    /**
     * Called the moment that the stop button is pressed (or the
     * timer runs our) on the Driver Station remote. This is the
     * last code that is executed when an OpMode is running.
     */
    open fun stop() {};

    //
    // Config Fetching
    //

    /** The selected configuration variant (defaults to &#91;default&$93;) */
    private var config = getOpModeConfig( getRegisterName( javaClass ), "[default]" );

    //
    // Device Fetching
    //

    /** All the different types of DeviceMappings in the hardware map */
    private val hardwareMaps by lazy()
    {
        FieldFinder( hardwareMap ).inheritsFrom( HardwareMap.DeviceMapping::class.java ).get();
    }

    /**
     * The map of cached device mappings. Awful name for it, I know.
     * The key is the class in the generics of the value, i.e.
     * `deviceMappingMap[ Int.javaClass ] = HardwareMap.DeviceMapping< Int >`
     */
    private val deviceMappingMap = HashMap< Class< * >, HardwareMap.DeviceMapping< * > >();

    /**
     * Finds the device with the given name in the correct device mapping based off of
     * the type parameters. For example:
     *
     * `private final DcMotor left_motor = getDevice< DcMotor >( "left_motor" );`
     *
     * instead of
     *
     * `private final DcMotor left_motor = getHardwareMap().dcMotor.get( "left_motor" );`
     *
     * On top of being (ever-so-slightly) shorter, this replaces multiple different
     * method and field calls (i.e. [hardwareMap.dcMotor], [hardwareMap.irSeekerSensor], etc),
     * and ensures that the device will never be `null`, because this method will error
     * immediately on class initialization if it is, instead of whenever the first
     * methods are invoked on it.
     *
     * @param[name]
     *          The name of the hardware device.
     * @return The device with the given name and the specified type (via type parameters).
     *
     * @throws NullPointerException
     *          If no hardware device with the given name could be found.
     * @throws IllegalArgumentException
     *          If the generic type wasn't a supported type in the hardware map.
     */
    final fun < T : HardwareDevice > getDevice( name: String ): T
    {
        val type = getGenericType( ArrayList< T >() ); // oh god, this is such a hack
        val map = getDeviceMapping< T >( type ); // the corresponding map for this type

        val value = map[ name ] ?: throw NullPointerException( "No hardware of type ${type.simpleName} with the name $name" );

        return value;
    }

    /**
     * Gets the correct [HardwareMap.DeviceMapping] for the given type.
     *
     * @param[type]
     *          The class type (in reality, it's the class of `T`, but that's not checked).
     * @return The correct [HardwareMap.DeviceMapping] for type `T`.
     *
     * @throws IllegalArgumentException
     *          If the generic type wasn't a supported type in the hardware map.
     */
    @Suppress( "unchecked_cast" ) // the cast is checked via reflections
    private fun < T > getDeviceMapping( type: Class< * > ): HardwareMap.DeviceMapping< T >
    {
        // if we've already found the device mapping before, just look it up
        if ( deviceMappingMap.containsKey( type ) )
        {
            return deviceMappingMap[ type ] as HardwareMap.DeviceMapping< T >; // this should be ensured by how the data is entered into the map
        }

        // search for the correct hardware map manually
        for ( field in hardwareMaps )
        {
            val deviceMapping = field.get( hardwareMap ) as HardwareMap.DeviceMapping< * >; // find the specific map for this OpMode

            // if you didn't want to see hacks, you shouldn't've come here :/

            // find the data-backing hashmap's field
            val mapField = deviceMapping.javaClass.getDeclaredField( "a" );
            mapField.isAccessible = true; // force it to be accessible

            // get the value of the field
            val map = mapField.get( deviceMapping ) as HashMap< String, * >;

            // if the type of the map is the same one as the one we're given
            if ( getGenericType( map.values ).isAssignableFrom( type ) )
            {
                val value = deviceMapping as HardwareMap.DeviceMapping< T >;
                deviceMappingMap[ type ] = value; // cache it in the map so we don't have to do this loop again
                return value;
            }
        }

        // doing this ensures that we'll never have to check for null types anywhere!
        // also, if it somehow gets to this, the user is an idiot anyways, and needs
        // to immediately see that it fails.
        throw IllegalArgumentException( "No hardware map available for type ${type.simpleName}" );
    }

}
<commit_msg>Create new variants on the fly<commit_after>package com.addonovan.ftcext.control

import android.widget.Spinner
import com.addonovan.ftcext.*
import com.addonovan.ftcext.config.*
import com.addonovan.ftcext.reflection.FieldFinder
import com.qualcomm.robotcore.hardware.*
import com.qualcomm.robotcore.robocol.Telemetry
import java.util.*

/**
 * The OpMode class built upon Qualcomm's. This class provides more utility
 * than the standard class does, and *must* be used in order to be able
 * to access the other functionality of this library.
 *
 * @see OpMode
 * @see LinearOpMode
 * @see Register
 *
 * @author addonovan
 * @since 6/12/16
 */
abstract class AbstractOpMode()
{

    //
    // Values
    //

    /** The name that this OpMode is registered as in the Registrar. */
    val RegisteredName by lazy()
    {
        getRegisterName( javaClass );
    }

    /** The first controller. */
    val gamepad1: Gamepad
            get() = Hardware.gamepad1;

    /** The second controller. */
    val gamepad2: Gamepad
            get() = Hardware.gamepad2;

    /** Telemetry for sending back current states and what not. */
    val telemetry: Telemetry
            get() = Hardware.telemetry;

    /** Direct access to the hardware map. Directly referencing this is discouraged. */
    val hardwareMap: HardwareMap
            get() = Hardware.hardwareMap;

    //
    // Constructors
    //

    init
    {
        loadConfigs( CONFIG_FILE ); // load the config whenever the OpMode is about to execute

        val configs = getOpModeConfigs( RegisteredName );

        // prompt the user which variant to use if there's more than one
        if ( configs.size > 1 )
        {
            val variantList = ArrayList< String >();
            configs.forEach { config -> variantList += config.Variant };
            val newVariant = "Create a new variant";

            variantList += newVariant;

            spinnerDialog(
                    "Choose a variant",
                    "Choose a configuration variant to use",
                    variantList.toArray( arrayOf< String >() ),
                    { string ->

                        // create a new, blank OpMode configuration
                        if ( string == newVariant )
                        {
                            prompt(
                                    "New variant",
                                    "Choose a name for the new variant",
                                    "Create Variant",
                                    { string ->
                                        config = getOpModeConfig( RegisteredName, string ); // create the new variant
                                    }
                            );
                        }
                        // `string` is the name of the variant
                        else
                        {
                            config = getOpModeConfig( RegisteredName, string ) // sets the config to the correct variant
                        }
                    }
            );
        }
    }

    //
    // Things from Original OpMode
    //

    /**
     * Initialize the robot. This occurs directly after the OpMode
     * is selected.
     */
    abstract fun init();

    /**
     * Called the moment that the start button is pressed on the
     * Driver Station remote. This is the first code that is executed
     * when an OpMode is running.
     */
    open fun start() {};

    /**
     * Called the moment that the stop button is pressed (or the
     * timer runs our) on the Driver Station remote. This is the
     * last code that is executed when an OpMode is running.
     */
    open fun stop() {};

    //
    // Config Fetching
    //

    /** The selected configuration variant (defaults to &#91;default&$93;) */
    private var config = getOpModeConfig( getRegisterName( javaClass ), "[default]" );

    //
    // Device Fetching
    //

    /** All the different types of DeviceMappings in the hardware map */
    private val hardwareMaps by lazy()
    {
        FieldFinder( hardwareMap ).inheritsFrom( HardwareMap.DeviceMapping::class.java ).get();
    }

    /**
     * The map of cached device mappings. Awful name for it, I know.
     * The key is the class in the generics of the value, i.e.
     * `deviceMappingMap[ Int.javaClass ] = HardwareMap.DeviceMapping< Int >`
     */
    private val deviceMappingMap = HashMap< Class< * >, HardwareMap.DeviceMapping< * > >();

    /**
     * Finds the device with the given name in the correct device mapping based off of
     * the type parameters. For example:
     *
     * `private final DcMotor left_motor = getDevice< DcMotor >( "left_motor" );`
     *
     * instead of
     *
     * `private final DcMotor left_motor = getHardwareMap().dcMotor.get( "left_motor" );`
     *
     * On top of being (ever-so-slightly) shorter, this replaces multiple different
     * method and field calls (i.e. [hardwareMap.dcMotor], [hardwareMap.irSeekerSensor], etc),
     * and ensures that the device will never be `null`, because this method will error
     * immediately on class initialization if it is, instead of whenever the first
     * methods are invoked on it.
     *
     * @param[name]
     *          The name of the hardware device.
     * @return The device with the given name and the specified type (via type parameters).
     *
     * @throws NullPointerException
     *          If no hardware device with the given name could be found.
     * @throws IllegalArgumentException
     *          If the generic type wasn't a supported type in the hardware map.
     */
    final fun < T : HardwareDevice > getDevice( name: String ): T
    {
        val type = getGenericType( ArrayList< T >() ); // oh god, this is such a hack
        val map = getDeviceMapping< T >( type ); // the corresponding map for this type

        val value = map[ name ] ?: throw NullPointerException( "No hardware of type ${type.simpleName} with the name $name" );

        return value;
    }

    /**
     * Gets the correct [HardwareMap.DeviceMapping] for the given type.
     *
     * @param[type]
     *          The class type (in reality, it's the class of `T`, but that's not checked).
     * @return The correct [HardwareMap.DeviceMapping] for type `T`.
     *
     * @throws IllegalArgumentException
     *          If the generic type wasn't a supported type in the hardware map.
     */
    @Suppress( "unchecked_cast" ) // the cast is checked via reflections
    private fun < T > getDeviceMapping( type: Class< * > ): HardwareMap.DeviceMapping< T >
    {
        // if we've already found the device mapping before, just look it up
        if ( deviceMappingMap.containsKey( type ) )
        {
            return deviceMappingMap[ type ] as HardwareMap.DeviceMapping< T >; // this should be ensured by how the data is entered into the map
        }

        // search for the correct hardware map manually
        for ( field in hardwareMaps )
        {
            val deviceMapping = field.get( hardwareMap ) as HardwareMap.DeviceMapping< * >; // find the specific map for this OpMode

            // if you didn't want to see hacks, you shouldn't've come here :/

            // find the data-backing hashmap's field
            val mapField = deviceMapping.javaClass.getDeclaredField( "a" );
            mapField.isAccessible = true; // force it to be accessible

            // get the value of the field
            val map = mapField.get( deviceMapping ) as HashMap< String, * >;

            // if the type of the map is the same one as the one we're given
            if ( getGenericType( map.values ).isAssignableFrom( type ) )
            {
                val value = deviceMapping as HardwareMap.DeviceMapping< T >;
                deviceMappingMap[ type ] = value; // cache it in the map so we don't have to do this loop again
                return value;
            }
        }

        // doing this ensures that we'll never have to check for null types anywhere!
        // also, if it somehow gets to this, the user is an idiot anyways, and needs
        // to immediately see that it fails.
        throw IllegalArgumentException( "No hardware map available for type ${type.simpleName}" );
    }

}

{'fraction_non_alphanumeric': 0.08677052739113779, 'fraction_numerical': 0.0017877665687651642, 'mean_word_length': 1.9805899143672692, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4940717', 'n_tokens_mistral': 4191, 'n_tokens_neox': 4014, 'n_words': 1597}