OptionsFile is used to store values in a file and manage the conversions of those values to certain types. You can create an OptionsFile in three ways. In all three, a Converters object is required.

OptionsFile opts = new OptionsFile(UtilConverters.getInstance(), File file); //from an existing file
OptionsFile opts = new OptionsFile(UtilConverters.getInstance(), Map<String, String> map); //from a map of String to String
OptionsFile opts = new OptionsFile(UtilConverters.getInstance()); //from thin air!

Then you can add items to it:

String name = "Mr. Prof. Dr. Adm. Rev. Sir Gerald Archibald Edgar Shelby Bartholomew Reginald Eggbert Mortimer Alphonso Smith XLII, Jr.";
int age = 83; //shoutout to my octogenarians
double height = 98.8; //inches
TeamColor teamColor = TeamColor.BLUE;

opts.set("name", name);
opts.set("age", age);
opts.set("height", height);
opts.set("teamColor", teamColor);

Or get items from it:

String name = opts.get("name", String.class); //get the name
Integer age = opts.get("age", 0); //get the age. If not found, use 0.
Double height = opts.get("height", 0.0); //get the height. If not found, use 0.0.
TeamColor teamColor = opts.get("teamColor", null); //get the teamColor. If not found, use null.

Or save it to a file:

if (opts.writeToFile(new File("person.txt"))) {
    System.out.println("Save success!");
} else {
    System.out.println("File not found!");
}

You can also set and get arrays of values of any type supported by the given Converters.

Integer[] array1 = { 1, 2, 3 };
opts.setArray("array", array1);
Integer[] array2 = opts.getArray("array", Integer.class);

TeamColor[] array3 = { TeamColor.RED, TeamColor.RED, TeamColor.BLUE };
opts.setArray("array", array3);
TeamColor[] array4 = opts.getArray("array", TeamColor.class);

ftc/electronvolts/util/OptionsFile.java

package ftc.electronvolts.util.files;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IllegalFormatConversionException;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.regex.Pattern;

/**
 * This file was made by the electronVolts, FTC team 7393
 *
 * This class stores and retrieves values from a file. It should probably be
 * replaced by an XML or JSON interpreter.
 */
public class OptionsFile {
    private static final String DEFAULT_ARRAY_SEPARATOR = ",";
    private static final String DEFAULT_SEPARATOR = "=";

    /**
     * converts objects to and from strings
     */
    private final Converters converters;

    /**
     * stores the strings that are read to and written from files
     */
    private Map<String, String> values;

    /**
     * @param converters the utilities that convert strings to and from objects
     */
    public OptionsFile(Converters converters) {
        this.converters = converters;
        values = new HashMap<>();
    }

    /**
     * @param converters the utilities that convert strings to and from objects
     * @param values the map of values to be loaded
     */
    public OptionsFile(Converters converters, Map<String, String> values) {
        this.converters = converters;
        this.values = values;
    }

    /**
     * retrieve an OptionsFile from a file
     *
     * @param converters the utilities that convert strings to and from objects
     * @param file the file to read from
     * @return the OptionsFile
     */
    public OptionsFile(Converters converters, File file) {
        this.converters = converters;
        values = new HashMap<>();
        BufferedReader br = null;
        try {
            //read each line of the file
            br = new BufferedReader(new FileReader(file));
            String separator = br.readLine();
            if (separator == null) return;
            String line;
            while ((line = br.readLine()) != null) {
                try {
                    //split the line at the "="
                    String[] elements = line.split(separator);

                    //extract the key and value from the split line
                    String key = elements[0].trim();
                    String value = elements[1].trim();

                    //put the key and value into the map
                    values.put(key, value);
                } catch (IndexOutOfBoundsException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e1) {
            e1.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * store the values to a file
     *
     * @param file the file
     * @return whether or not it worked
     */
    public boolean writeToFile(File file) {
        try {
            FileWriter fw = new FileWriter(file);

            String separator = DEFAULT_SEPARATOR;
            boolean done = false;
            while (!done) {
                done = true;
                for (Map.Entry<String, String> entry : values.entrySet()) {
                    if (entry.getKey().contains(separator) || entry.getValue().contains(separator)) {
                        done = false;
                        separator += DEFAULT_SEPARATOR;
                        break;
                    }
                }
            }

            fw.write(separator + "\n");
            for (Map.Entry<String, String> entry : values.entrySet()) {
                fw.write(entry.getKey() + separator + entry.getValue() + "\n");
            }
            fw.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @return A map of all the values from the file
     */
    public Map<String, String> getValues() {
        return values;
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a boolean
     */
    public void set(String tag, boolean value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Boolean.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a byte
     */
    public void set(String tag, byte value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Byte.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a char
     */
    public void set(String tag, char value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Character.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a short
     */
    public void set(String tag, short value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Short.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value an int
     */
    public void set(String tag, int value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Integer.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a long
     */
    public void set(String tag, long value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Long.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a float
     */
    public void set(String tag, float value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Float.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param value a double
     */
    public void set(String tag, double value) {
        //convert the value to a string and add the key-value pair to the values map
        values.put(tag, Double.toString(value));
    }

    /**
     * set a value in the map
     *
     * @param tag the name of the value
     * @param object the Object to put into the map
     */
    public <T> void set(String tag, T object) {

        //if the object is null, add a null value to the map
        if (object == null) {
            values.put(tag, null);
            return;
        }

        //get the class to convert to
        Class<T> clazz = (Class<T>) object.getClass();

        //get the converter for the specified class
        Converter<T> converter = converters.getConverter(clazz);

        //throw an error if there is no converter for the class
        if (converter == null) {
            throw new MissingResourceException("No converter given.", converters.getClass().getName(), clazz.getName());
        }

        //convert the value to a string
        String string = converter.toString(object);

        //if the result is null, throw an exception
        if (string == null) throw new IllegalFormatConversionException((char) 0, clazz);

        //add the key-value pair to the values map
        values.put(tag, string);
    }

    /**
     * set an array of values in a map
     * 
     * @param tag the name of the value
     * @param objects the array of objects to put in the map
     */
    public <T> void setArray(String tag, T[] objects) {
        setArray(tag, objects, DEFAULT_ARRAY_SEPARATOR);
    }

    /**
     * set an array of values in a map
     * 
     * @param tag the name of the value
     * @param objects the array of objects to put in the map
     * @param separator the string to join the array elements with. Cannot be in
     *            the output of the conversion for any item of the array
     */
    public <T> void setArray(String tag, T[] objects, String separator) {
        //if the object is null, add a null value to the map
        if (objects == null) {
            values.put(tag, null);
            return;
        }

        //get the class to convert to
        Class<T> clazz = (Class<T>) objects.getClass().getComponentType();

        //get the converter for the specified class
        Converter<T> converter = converters.getConverter(clazz);

        //throw an error if there is no converter for the class
        if (converter == null) {
            throw new MissingResourceException("No converter given for \"" + clazz.getName() + "\".", converters.getClass().getName(), clazz.getName());
        }

        StringBuilder stringBuilder = new StringBuilder();

        boolean first = true;
        for (T object : objects) {
            if (!first) stringBuilder.append(separator);
            first = false;

            //convert the value to a string
            String string = converter.toString(object);

            //if the result is null, throw an exception
            if (string == null) throw new IllegalFormatConversionException((char) 0, clazz);
            if (string.contains(separator)) {
                throw new IllegalArgumentException("Converted input \"" + string + "\" cannot contain separator \"" + separator + "\".");
            }

            //append it to the string builder
            stringBuilder.append(string);
        }

        //build the string
        String string = stringBuilder.toString();

        //add the key-value pair to the values map
        values.put(tag, string);
    }

    /**
     * 
     * @param tag the name of the value
     * @param clazz the class to convert to
     * @return an array of the specified type
     * @throws IllegalArgumentException if there is no converter for the given
     *             type
     */
    public <T> T[] getArray(String tag, Class<T> clazz) {
        return getArray(tag, clazz, DEFAULT_ARRAY_SEPARATOR);
    }

    /**
     * 
     * @param tag the name of the value
     * @param clazz the class to convert to
     * @param separator the string to separate the array elements with (not a
     *            regex)
     * @return an array of the specified type
     * @throws IllegalArgumentException if there is no converter for the given
     *             type
     */
    public <T> T[] getArray(String tag, Class<T> clazz, String separator) {
        //get the converter for the specified class
        Converter<T> converter = converters.getConverter(clazz);

        //throw an error if there is no converter for the class
        if (converter == null) {
            throw new MissingResourceException("No converter given.", converters.getClass().getName(), clazz.getName());
        }

        if (!values.containsKey(tag)) {
            throw new IllegalArgumentException();
        }

        //get the value from the map
        String string = values.get(tag);

        //if the input is null, return null
        if (string == null) return null;

        //separate the string into parts. use the separator as a literal string, not a regex
        String[] parts = string.split(Pattern.quote(separator));

        T[] results = (T[]) Array.newInstance(clazz, parts.length);
        for (int i=0; i<parts.length; i++) {
            String part = parts[i];

            //convert the string to the object
            T result = converter.fromString(part);

            //if the result is null, throw an exception
            if (result == null) throw new IllegalFormatConversionException((char) 0, clazz);

            results[i] = result;
        }

        return results;
    }

    /**
     * @param tag the name of the value
     * @param clazz the class to convert to
     * @return the value converted to the specified type
     * @throws MissingResourceException if there is no converter for the given
     *             type
     * @throws IllegalArgumentException if there is no value with the given tag
     * @throws IllegalFormatConversionException if the string could not be
     *             converted to the specified object
     */
    public <T> T get(String tag, Class<T> clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("clazz cannot be null.");
        }

        //get the converter for the specified class
        Converter<T> converter = converters.getConverter(clazz);

        //throw an error if there is no converter for the class
        if (converter == null) {
            throw new MissingResourceException("No converter given.", converters.getClass().getName(), clazz.getName());
        }

        if (!values.containsKey(tag)) {
            throw new IllegalArgumentException();
        }

        //get the value from the map
        String string = values.get(tag);

        //if the input is null, return null
        if (string == null) return null;

        //convert the string to the object
        T result = converter.fromString(string);

        //if the result is null, throw an exception
        if (result == null) throw new IllegalFormatConversionException((char) 0, clazz);

        return result;
    }

    /**
     * @param tag the name of the value
     * @param clazz the class to convert to
     * @param fallback the value to use if the conversion fails
     * @return the value converted to the specified type
     */
    public <T> T get(String tag, Class<T> clazz, T fallback) {
        //try to convert, otherwise return the fallback
        try {
            return get(tag, clazz);
        } catch (IllegalArgumentException e) {
            return fallback;
        }
    }

    /**
     * @param tag the name of the value
     * @param fallback the value to use if none is found
     * @return the value converted to the specified type
     * @throws IllegalArgumentException if there is no converter for the given
     *             type
     */
    public <T> T get(String tag, T fallback) {
        //get the class to convert to
        Class<T> clazz = (Class<T>) fallback.getClass();

        return get(tag, clazz, fallback);
    }

}