Package net.aufdemrand.denizen.flags

Source Code of net.aufdemrand.denizen.flags.FlagManager$Value

package net.aufdemrand.denizen.flags;

import net.aufdemrand.denizen.Denizen;
import net.aufdemrand.denizen.events.EventManager;
import net.aufdemrand.denizen.events.core.FlagSmartEvent;
import net.aufdemrand.denizen.objects.*;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import org.bukkit.configuration.ConfigurationSection;

import java.util.*;

// <--[language]
// @name flags
// @group flag system
// @description
// Flags are a feature that is implemented by Denizen to allow the dynamic and persistent storage of information
// to NPC and player objects as well as the server on a global scale. Whether you need to store a quest variable
// on a player, or you are keeping track of the number of people logging into your server, flags can help.
//
// First, a couple facts about flags:
// 1) They are persistent, that is - they survive a server restart, as long as '/denizen save' or a proper shutdown
// has happened since the flag was made.
// 2) They are stored in flags.yml whenever '/denizen save' is used. Otherwise, since writing to the disk is
// 'expensive' as far as performance is concerned, they are stored in memory.
// 3) Flags can be attached to players, NPCs, or the server. This means both 'aufdemrand' and 'davidcernat' can have
// a flag by the same name, but contain separate values if stored to their player-objects.
// 4) Flags can be booleans, single-item values, or multiple-item arrays, and storing information inside them is
// smart and reliable.
// 5) You can set expirations on any flag, to set the maximum life of a flag. This is great for cooldowns and
// temporary variables.
// 6) Flags have world script events, so you can tell when a flag is changing, and react to those changes.
//
// Here's the basics:
//
// Since the range of information needing to be stored can vary, flags offer a flexible way to handle many situations.
// Flags in their simplest form are a true/false method of storage, with tags available to check if a flag exists. But
// they can also store a piece of information, in a 'key/value' relationship. Name the flag, set the value. This can
// serve a dual purpose, since 1) you can prove it exists, using it as a boolean, and 2) another piece of information
// can be stored. It's like saying, 'this flag exists, and stored with it is a chunk of information'.
//
// The elements stored in flags also have the ability to do perform functions if they are numbers. Easily create
// a counter with flags, simply by using the flag command to use the '+' feature. Also available is '-' to
// decrease, '*' to multiply, and '/' to divide the values.
//
// Flags can act as arrays of information as well, with the ability to add additional elements to a single flag. Flags
// with multiple values use a 'smart-index' with the ability to get/remove information via a number, or by its value.
// Included with the flag command is an easy way to add and remove elements from the array, see the flag command for
// more information.
//
// Flags are modified by using the flag command, and easily read by using a replaceable tag. Here are some examples
// of some snippets of actual code:
//
// Setting a boolean flag to a player, and retrieving it:
// We'll use the player linked to the script by specifying <player> in the flag command. The next argument
// is 'stoneskin', shown here without quotes since it's just one word. This will be the name of the flag.
// Second command, 'narrate', as used below will show the player "p@player_name has flag 'stoneskin'? true". The 'true'
// is retrieved by using the player attribute 'has_flag[flag_name]' to check. If this flag did not exist, it would
// return false. To see a list of flag tags, see 'A list of flag-specific tags'.
// <code>
// - flag <player> stoneskin
// - narrate "<player.name> has flag 'stoneskin'? <player.has_flag[stoneskin]>"
// </code>
//
//
// Using flags as counters:
// Definitions are nice and light, but sometimes they just don't do the job. For example, using flags in foreach loops
// is a great idea, and really easy to do.
// <code>
// # initiate a loop through each player that has logged onto the server
// # Inside the loop, check if the player's flag 'completed' contains in it an element named 'beginners tutorial'.
// # If it does, increment the server flag 'completes_counter' by one, and give it 10 seconds to live.
// - foreach <server.list_players> {
//     - if <%value%.flag[completed].as_list> contains 'beginners tutorial'
//       flag server completes_counter:++ duration:10s
//   }
// # Now show the number of players who had the element in their 'completed' flag.
// - narrate "The number of players who have completed the beginner's tutorial is<&co> <server.flag[completes_counter]>"
// </code>
//
//
// Using flags as object storage:
// Flags can hold fetchable objects as well. Let's say players can be friends with NPCs. Why not store the friends
// on the NPC with a list of player objects?
// <code>
// - flag <npc> friends:->:<player>
// - foreach <npc.flag[friends].as_list> {
//     - chat t:%value% 'You are my friend!'
//   }
// </code>
//
// Need to store a location? Store it in a flag!
// <code>
// - flag <player> home_location:<player.location.simple>
// - narrate "Your home location is now '<player.flag[home_location].as_location.simple>'!"
// </code>
//
//
// Flags are good for lots of other things too! Check out the flag command and 'fl@flag' tags
// for more specific information.
// -->


public class FlagManager {

    // Valid flag actions
    public static enum Action { SET_VALUE, SET_BOOLEAN, INCREASE, DECREASE, MULTIPLY,
        DIVIDE, INSERT, REMOVE, SPLIT, DELETE }


    // Constructor
    private Denizen denizen;

    public FlagManager(Denizen denizen) {
        this.denizen = denizen;
    }

    // Static methods
    public static boolean playerHasFlag(dPlayer player, String flagName) {
        if (player == null || flagName == null) return false;
        return DenizenAPI.getCurrentInstance().flagManager()
                .getPlayerFlag(player, flagName).size() > 0;
    }

    public static boolean npcHasFlag(dNPC npc, String flagName) {
        if (npc == null || flagName == null) return false;
        return DenizenAPI.getCurrentInstance().flagManager()
                .getNPCFlag(npc.getId(), flagName).size() > 0;
    }

    public static boolean serverHasFlag(String flagName) {
        if (flagName == null) return false;
        return DenizenAPI.getCurrentInstance().flagManager()
                .getGlobalFlag(flagName).size() > 0;
    }

    public static void clearNPCFlags(int npcid) {
        DenizenAPI.getCurrentInstance().getSaves().set("NPCs." + npcid, null);
    }

    /**
     * Returns a NPC Flag object. If this flag currently exists
     * it will be populated with the current values. If the flag does NOT exist,
     * it will be created with blank values.
     *
     */
    public Flag getNPCFlag(int npcid, String flagName) {
        return new Flag("NPCs." + npcid + ".Flags." + flagName.toUpperCase(), flagName, "n@" + npcid);
    }

    /**
     * Returns a Global Flag object. If this flag currently exists
     * it will be populated with the current values. If the flag does NOT exist,
     * it will be created with blank values.
     *
     */
    public Flag getGlobalFlag(String flagName) {
        return new Flag("Global.Flags." + flagName.toUpperCase(), flagName, "SERVER");
    }

    /**
     * Returns a Flag Object tied to a Player. If this flag currently exists
     * it will be populated with the current values. If the flag does NOT exist,
     * it will be created with blank values.
     *
     */
    public Flag getPlayerFlag(dPlayer player, String flagName) {
        if (player == null)
            return new Flag("players.00.UNKNOWN.Flags." + flagName.toUpperCase(), flagName, "p@null");
        return new Flag("Players." + player.getSaveName() + ".Flags." + flagName.toUpperCase(), flagName, player.identify());
    }

    public Flag getPlayerFlag(UUID player, String flagName) {
        if (player == null)
            return new Flag("players.00.UNKNOWN.Flags." + flagName.toUpperCase(), flagName, "p@null");
        String baseID = player.toString().toUpperCase().replace("-", "");
        return new Flag("Players." + baseID.substring(0, 2) + "." + baseID + ".Flags." + flagName.toUpperCase(), flagName, "p@" + player.toString());
    }

    /**
     * Returns a list of flag names currently attached to an NPC.
     */
    public Set<String> listNPCFlags(int npcid) {
        ConfigurationSection section = denizen.getSaves().getConfigurationSection("NPCs." + npcid + ".Flags");
        return section != null ? section.getValues(true).keySet() : null;
    }

    /**
     * Returns a list of flag names currently attached to the server.
     */
    public Set<String> listGlobalFlags() {
        ConfigurationSection section = denizen.getSaves().getConfigurationSection("Global.Flags");
        return section != null ? section.getValues(true).keySet() : null;
    }

    /**
     * Returns a list of flag names currently attached to a player.
     */
    public Set<String> listPlayerFlags(dPlayer player) {
        ConfigurationSection section = denizen.getSaves().getConfigurationSection("Players." + player.getSaveName() + ".Flags");
        return section!= null ? section.getValues(true).keySet() : null;
    }


    /**
     * Flag object contains methods for working with Flags and contain a list
     * of the values associated with said flag (if existing) and (optionally) an
     * expiration (if existing).
     *
     * Storage example in Denizen saves.yml:
     *
     * 'FLAG_NAME':
     * - First Value
     * - Second Value
     * - Third Value
     * - ...
     * 'FLAG_NAME-expiration': 123456789
     *
     * To work with multiple values in a flag, an index must be provided. Indexes
     * start at 1 and get higher as more items are added. Specifying an index of -1, or,
     * when possible, supplying NO index will result in retrieving/setting/etc the
     * item with the highest index. Also, note that when using a FLAG TAG in DSCRIPT,
     * ie. <FLAG.P:FLAG_NAME>, specifying no index will follow suit, that is, the
     * value with the highest index will be referenced.
     *
     */
    public class Flag {

        private Value value;
        private String flagPath;
        private String flagName;
        private String flagOwner;
        private long expiration = (long) -1;
        private boolean valid = true;

        Flag(String flagPath, String flagName, String flagOwner) {
            this.flagPath = flagPath;
            this.flagName = flagName;
            this.flagOwner = flagOwner;
            rebuild();
        }

        /**
         * Returns whether or not a value currently exists in the Flag. The
         * provided value will check if it matches an existing value by means
         * of a String.equalsIgnoreCase as well as a Double match if the
         * provided value is a number.
         *
         */
        public boolean contains(String stringValue) {
            if (checkExpired())
                return false;
            for (String val : value.values) {
                if (val.equalsIgnoreCase(stringValue)) return true;
                try {
                    if (Double.valueOf(val).equals(Double.valueOf(stringValue))) return true;
                } catch (NumberFormatException e) { /* Not a valid number, continue. */ }
            }

            return false;
        }

        /**
         * Gets whether the flag is still valid.
         *
         */
        public boolean StillValid() {
            return valid;
        }

        /**
         * Gets all values currently stored in the flag.
         *
         */
        public List<String> values() {
            checkExpired();
            return value.values;
        }

        /**
         * Gets a specific value stored in a flag when given an index.
         *
         */
        public Value get(int index) {
            checkExpired();
            return value.get(index);
        }

        // <--[event]
        // @Events
        // flag cleared
        // player flag cleared
        // player flag <flagname> cleared
        // npc flag cleared
        // npc flag <flagname> cleared
        // server flag cleared
        // server flag <flagname> cleared
        //
        // @Warning This event will fire rapidly and not exactly when you might expect it to fire.
        //
        // @Triggers when a flag is cleared
        // @Context
        // <context.owner> returns an Element of the flag owner's object.
        // <context.name> returns an Element of the flag name.
        // <context.type> returns an Element of the flag type.
        // <context.old_value> returns an Element of the flag's previous value.
        //
        // -->
        /**
         * Clears all values from a flag, essentially making it null.
         *
         */
        public void clear() {
            String OldOwner = flagOwner;
            String OldName = flagName;
            dObject OldValue = value.size() > 1
                    ? new dList(denizen.getSaves().getStringList(flagPath))
                    : value.size() == 1 ? new Element(value.get(0).asString()): Element.valueOf("null");

            denizen.getSaves().set(flagPath, null);
            denizen.getSaves().set(flagPath + "-expiration", null);
            valid = false;
            rebuild();

            if (FlagSmartEvent.IsActive()) {
                List<String> world_script_events = new ArrayList<String>();

                Map<String, dObject> context = new HashMap<String, dObject>();
                dPlayer player = null;
                if (dPlayer.matches(OldOwner))
                    player = dPlayer.valueOf(OldOwner);
                dNPC npc = null;
                if (dNPC.matches(OldOwner))
                    npc = dNPC.valueOf(OldOwner);

                String type;

                if (player != null) {
                    type = "player";
                }
                else if (npc != null) {
                    type = "npc";
                }
                else {
                    type = "server";
                }
                world_script_events.add(type + " flag cleared");
                world_script_events.add(type + " flag " + OldName + " cleared");

                context.put("owner", Element.valueOf(OldOwner));
                context.put("name", Element.valueOf(OldName));
                context.put("type", Element.valueOf(type));
                context.put("old_value", OldValue);

                world_script_events.add("flag cleared");

                EventManager.doEvents(world_script_events,
                        npc, player, context);
            }
        }

        /**
         * Gets the first value stored in the Flag.
         *
         */
        public Value getFirst() {
            checkExpired();
            return value.get(1);
        }

        /**
         * Gets the last value stored in the Flag.
         *
         */
        public Value getLast() {
            checkExpired();
            return value.get(value.size());
        }

        /**
         * Sets the value of the most recent value added to the flag. This does
         * not create a new value unless the flag is currently empty of values.
         *
         */
        public void set(Object obj) {
            set(obj, -1);
        }

        /**
         * Sets a specific value in the flag. Adds the value to the flag if
         * the index doesn't exist. If the index is less than 0, it instead
         * works with the most recent value added to the flag. If the flag is
         * currently empty, the value is added.
         *
         */
        public void set(Object obj, int index) {
            checkExpired();

            // No index? Work with last item in the Flag.
            if (index < 0) index = size();

            if (size() == 0) value.values.add((String) obj);
            else if (index > 0) {
                if (value.values.size() > index - 1) {
                    value.values.remove(index - 1);
                    value.values.add(index - 1, (String) obj);

                    // Index higher than currently exists? Add the item to the end of the list.
                } else value.values.add((String) obj);
            }
            valid = true;
            save();
            rebuild();
        }

        /**
         * Adds a value to the end of the Flag's Values. This value will have an index
         * of size() + 1. Returns the index of the value added. This could change if
         * values are removed.
         *
         */
        public int add(Object obj) {
            checkExpired();
            value.values.add((String) obj);
            valid = true;
            save();
            rebuild();
            return size();
        }

        /**
         * Splits a dScript list into values that are then added to the flag.
         * Returns the index of the last value added to the flag.
         *
         */
        public int split(Object obj) {
            checkExpired();
            String[] split = ((String) obj).replace("li@", "").split("\\|"); // the pipe character | needs to be escaped

            if (split.length > 0) {
                for (String val : split)
                    if (val.length() > 0)
                        value.values.add(val);

                save();
                rebuild();
            }

            return size();
        }

        /**
         * Removes a value from the Flag's current values. The first value that matches
         * (values are checked as Double and String.equalsIgnoreCase) is removed. If
         * no match, no removal is done.
         *
         */
        public void remove(Object obj) {
            remove(obj, -1);
        }

        /**
         * Removes a value from the Flag's current values. If an index is specified,
         * that specific value is removed. If no index is specified (or -1 is
         * specified as the index), the first value that matches (values are
         * checked as Double and String.equalsIgnoreCase) is removed. If a positive
         * index is specified that does not exist, no removal is done.
         *
         */
        public void remove(Object obj, int index) {
            checkExpired();

            // No index? Match object and remove it.
            if (index <= 0 && obj != null) {
                int x = 0;
                for (String val : value.values) {

                    // Evaluate as String
                    if (val.equalsIgnoreCase(String.valueOf(obj))) {

                        value.values.remove(x);
                        break;
                    }

                    // Evaluate as number
                    try {
                        if (Double.valueOf(val).equals(Double.valueOf((String) obj))) {
                            value.values.remove(x);
                            break;
                        }
                    } catch (Exception e) { /* Not a valid number, continue. */ }

                    x++;
                }

                // Else, remove specified index
            } else if (index <= size()) value.values.remove(index - 1);

            valid = true;
            save();
            rebuild();
        }

        /**
         * Used to give an expiration time for a flag. This is the same format
         * as System.getCurrentTimeMillis(), which is the number of milliseconds
         * since Jan 1, 1960. As an example, to get a valid expiration for a
         * specific amount of seconds from the current time, use the code snippet
         * Flag.setExpiration(System.getCurrentTimeMillis() + (delay * 1000))
         * where 'delay' is the amount of seconds.
         *
         */
        public void setExpiration(Long expiration) {
            valid = true;
            this.expiration = expiration;
            save();
        }

        /**
         * Returns the number of items in the Flag. This directly corresponds
         * with the indexes, since Flag Indexes start with 1, unlike Java Lists
         * which start at 0.
         *
         */
        public int size() {
            checkExpired();
            return value.size();
        }

        // <--[event]
        // @Events
        // flag changed
        // player flag changed
        // player flag <flagname> changed
        // npc flag changed
        // npc flag <flagname> changed
        // server flag changed
        // server flag <flagname> changed
        //
        // @Warning This event will fire rapidly and not exactly when you might expect it to fire.
        //
        // @Triggers when a flag is changed
        // @Context
        // <context.owner> returns an Element of the flag owner's object.
        // <context.name> returns an Element of the flag name.
        // <context.type> returns an Element of the flag type.
        // <context.old_value> returns an Element of the flag's previous value.
        //
        // -->
        /**
         * Saves the current values in this object to the Denizen saves.yml.
         * This is called internally when needed, but might be useful to call
         * if you are extending the usage of Flags yourself.
         *
         */
        public void save() {
            String OldOwner = flagOwner;
            String OldName = flagName;
            List<String> oldValueList = denizen.getSaves().getStringList(flagPath);
            dObject OldValue = oldValueList.size() > 1 ? new dList(oldValueList)
                    : oldValueList.size() == 1 ? new Element(oldValueList.get(0)): Element.valueOf("null");

            denizen.getSaves().set(flagPath, value.values);
            denizen.getSaves().set(flagPath + "-expiration", (expiration > 0 ? expiration : null));
            rebuild();

            if (FlagSmartEvent.IsActive()) {
                List<String> world_script_events = new ArrayList<String>();

                Map<String, dObject> context = new HashMap<String, dObject>();
                dPlayer player = null;
                if (dPlayer.matches(OldOwner)) player = dPlayer.valueOf(OldOwner);
                dNPC npc = null;
                if (dNPC.matches(OldOwner)) npc = dNPC.valueOf(OldOwner);

                String type;

                if (player != null) {
                    type = "player";
                }
                else if (npc != null) {
                    type = "npc";
                }
                else {
                    type = "server";
                }
                world_script_events.add(type + " flag changed");
                world_script_events.add(type + " flag " + OldName + " changed");

                context.put("owner", Element.valueOf(OldOwner));
                context.put("name", Element.valueOf(OldName));
                context.put("type", Element.valueOf(type));
                context.put("old_value", OldValue);

                world_script_events.add("flag changed");

                EventManager.doEvents(world_script_events,
                        npc, player, context);
            }

        }

        /**
         * Returns a String value of the last item in a Flag Value. If there is only
         * a single item in the flag, it returns it. To return the value of another
         * item in the Flag, use 'flag.get(index).asString()'.
         *
         */
        @Override
        public String toString() {
            checkExpired();
            // Possibly use reflection to check whether dList or dElement is calling this?
            // If dList, return fl@..., if dElement, return f@...
            return (flagOwner.equalsIgnoreCase("SERVER") ? "fl@" + flagName : "fl[" + flagOwner + "]@" + flagName);
        }

        // <--[event]
        // @Events
        // flag expires
        // player flag expires
        // player flag <flagname> expires
        // npc flag expires
        // npc flag <flagname> expires
        // server flag expires
        // server flag <flagname> expires
        //
        // @Warning This event will fire rapidly and not exactly when you might expect it to fire.
        //
        // @Triggers when a flag expires
        // @Context
        // <context.owner> returns an Element of the flag owner's object.
        // <context.name> returns an Element of the flag name.
        // <context.type> returns an Element of the flag type.
        // <context.old_value> returns an Element of the flag's previous value.
        //
        // -->
        /**
         * Removes flag if expiration is found to be up. This is called when an action
         * is done on the flag, such as get() or put(). If expired, the flag will be
         * erased before moving on.
         *
         */
        public boolean checkExpired() {
            rebuild();
            if (denizen.getSaves().contains(flagPath + "-expiration"))
                if (expiration > 1 && expiration < System.currentTimeMillis()) {
                    String OldOwner = flagOwner;
                    String OldName = flagName;
                    dObject OldValue = value.size() > 1
                            ? new dList(denizen.getSaves().getStringList(flagPath))
                            : value.size() == 1 ? new Element(value.get(0).asString()): Element.valueOf("null");
                    denizen.getSaves().set(flagPath + "-expiration", null);
                    denizen.getSaves().set(flagPath, null);
                    valid = false;
                    rebuild();
                    //dB.log('\'' + flagName + "' has expired! " + flagPath);
                    if (FlagSmartEvent.IsActive()) {
                        List<String> world_script_events = new ArrayList<String>();

                        Map<String, dObject> context = new HashMap<String, dObject>();
                        dPlayer player = null;
                        if (dPlayer.matches(OldOwner))
                            player = dPlayer.valueOf(OldOwner);
                        dNPC npc = null;
                        if (dNPC.matches(OldOwner))
                            npc = dNPC.valueOf(OldOwner);

                        String type;

                        if (player != null) {
                            type = "player";
                        }
                        else if (npc != null) {
                            type = "npc";
                        }
                        else {
                            type = "server";
                        }
                        world_script_events.add(type + " flag expires");
                        world_script_events.add(type + " flag " + OldName + " expires");

                        context.put("owner", Element.valueOf(OldOwner));
                        context.put("name", Element.valueOf(OldName));
                        context.put("type", Element.valueOf(type));
                        context.put("old_value", OldValue);

                        world_script_events.add("flag expires");

                        EventManager.doEvents(world_script_events,
                                npc, player, context);
                    }
                    return true;
                }
            return false;
        }

        public Duration expiration() {
            return new Duration((double) (expiration - System.currentTimeMillis()) / 1000);
        }

        /**
         * Returns the time left before the flag will expire. Minutes are only shown
         * if there is less than a day left, and seconds are only shown if there are
         * less than 10 minutes left.
         *
         */
        @Deprecated
        public String expirationTime() {
            rebuild();

            long seconds = (expiration - System.currentTimeMillis()) / 1000;

            long days = seconds / 86400;
            long hours = (seconds - days * 86400) / 3600;
            long minutes = (seconds - days * 86400 - hours * 3600) / 60;
            seconds = seconds - days * 86400 - hours * 3600 - minutes * 60;

            String timeString = "";

            if (days > 0)
                timeString = String.valueOf(days) + "d ";
            if (hours > 0)
                timeString = timeString + String.valueOf(hours) + "h ";
            if (minutes > 0 && days == 0)
                timeString = timeString + String.valueOf(minutes) + "m ";
            if (seconds > 0 && minutes < 10 && hours == 0 && days == 0)
                timeString = timeString + String.valueOf(seconds) + "s";

            return timeString.trim();
        }

        /**
         * Rebuilds the flag object with data from the saves.yml (in Memory)
         * to ensure that data is current if updated outside of the scope
         * of the plugin.
         *
         */
        public Flag rebuild() {
            if (denizen.getSaves().contains(flagPath + "-expiration"))
                this.expiration = (denizen.getSaves().getLong(flagPath + "-expiration"));
            List<String> cval = denizen.getSaves().getStringList(flagPath);
            if (cval == null) {
                cval = new ArrayList<String>();
                cval.add(denizen.getSaves().getString(flagPath, ""));
            }
            value = new Value(cval);
            return this;
        }

        /**
         * Determines if the flag is empty.
         *
         */
        public boolean isEmpty() {
            return value.isEmpty();
        }

        /**
         * Performs an action on the flag.
         *
         * @param action  a valid Action enum
         * @param value   the value specified for the action
         * @param index   the flag index, null if none
         */
        public void doAction(Action action, Element value, Integer index) {

            String val = (value != null ? value.asString() : null);

            if (index == null) index = -1;

            if (action == null) return;

            // Do flagAction
            switch (action) {
                case INCREASE: case DECREASE: case MULTIPLY: case DIVIDE:
                    double currentValue = get(index).asDouble();
                    set(Double.toString(math(currentValue, Double.valueOf(value.asString()), action)), index);
                    break;

                case SET_BOOLEAN:
                    set("true", index);
                    break;

                case SET_VALUE:
                    set(val, index);
                    break;

                case INSERT:
                    add(val);
                    break;

                case REMOVE:
                    remove(val, index);
                    break;

                case SPLIT:
                    split(val);
                    break;

                case DELETE:
                    clear();
            }
        }

        private double math(double currentValue, double value, Action flagAction) {
            switch (flagAction) {
                case INCREASE:
                    return currentValue + value;

                case DECREASE:
                    return currentValue - value;

                case MULTIPLY:
                    return currentValue * value;

                case DIVIDE:
                    return currentValue / value;

                default:
                    break;
            }

            return 0;
        }

    }


    /**
     *  Value object that is in charge of holding values that belong to a flag.
     *  Also contains some methods for retrieving stored values as specific
     *  data types. Otherwise, this object is used internally and created/destroyed
     *  automatically when working with Flag objects.
     *
     */
    public class Value {

        private List<String> values;
        private int index = -1;

        private Value(List<String> values) {
            this.values = values;
            if (this.values == null) {
                this.values = new ArrayList<String>();
            }
        }

        /**
         * Used internally to specify which value to work with, if multiple values
         * exist. If value is less than 0, value is set to the last value added.
         *
         */
        private void adjustIndex() {
            // -1 = last object.
            if (index < 0)
                index = size() - 1;
        }

        /**
         * Retrieves a boolean of the value. If the value is set to ANYTHING except
         * 'FALSE' (equalsIgnoreCase), it will return true. Useful for determining
         * whether a value exists, as FALSE is also returned if the value is not set.
         *
         */
        public boolean asBoolean() {
            adjustIndex();
            try {
                return !values.get(index).equalsIgnoreCase("FALSE");
            } catch (Exception e) { return false; }
        }

        /**
         * Retrieves a double value of the specified index. If value is not set,
         * or the value is not convertible to a Double, 0 is returned.
         *
         */
        public double asDouble() {
            adjustIndex();
            try {
                return Double.valueOf(values.get(index));
            } catch (Exception e) { return 0; }
        }

        /**
         * Returns an Integer value of the specified index. If the value has
         * decimal point information, it is rounded. If value is not set,
         * or the value is not convertible to a Double, 0 is returned.
         *
         */
        public int asInteger() {
            adjustIndex();
            try {
                return Double.valueOf(values.get(index)).intValue();
            } catch (Exception e) { return 0; }
        }

        /**
         * Returns a String value of the entirety of the values
         * contained as a comma-separated list. If the value doesn't
         * exist, "" is returned.
         *
         */
        public String asCommaSeparatedList() {
            adjustIndex();
            String returnList = "";

            for (String string : values)
                returnList = returnList + string + ", ";

            return returnList.substring(0, returnList.length() - 2);
        }

        /**
         * Returns a String value of the entirety of the values
         * contained as a dScript list. If the value doesn't
         * exist, "" is returned.
         *
         */
        public dList asList() {
            adjustIndex();
            return new dList(values);
        }

        /**
         * Returns a String value of the entirety of the values
         * contained as a dScript list, with a prefix added to
         * the start of each value. If the value doesn't
         * exist, "" is returned.
         *
         */
        public dList asList(String prefix) {
            adjustIndex();
            return new dList(values, prefix);
        }

        /**
         * Returns a String value of the value in the specified index. If
         * the value doesn't exist, "" is returned.
         *
         */
        public String asString() {
            adjustIndex();
            try {
                return values.get(index);
            } catch (Exception e) { return ""; }
        }

        /**
         * Returns an Integer value of the number of values
         * contained in a dScript list.
         *
         */
        public int asSize() {
            adjustIndex();
            return values.size();
        }

        /**
         * Returns an instance of the appropriate Object, as detected by this method.
         * Should check if instanceof Integer, Double, Boolean, List, or String.
         *
         */
        public Object asAutoDetectedObject() {
            adjustIndex();
            String arg = values.get(index);

            try {
                // If an Integer
                if (aH.matchesInteger(arg))
                    return aH.getIntegerFrom(arg);

                    // If a Double
                else if (aH.matchesDouble(arg))
                    return aH.getDoubleFrom(arg);

                    // If a Boolean
                else if (arg.equalsIgnoreCase("true")) return true;
                else if (arg.equalsIgnoreCase("false")) return false;

                    // If a List<Object>
                else if (arg.contains("|")) {
                    List<String> toList = new ArrayList<String>();
                    return new dList(toList);
                }

                // Must be a String
                else return arg;
            } catch (Exception e) { return ""; }

        }

        /**
         * Used internally to specify the index. When using as API, you should
         * instead use the get(index) method in the Flag object.
         *
         */
        private Value get(int i) {
            index = i - 1;
            adjustIndex();
            return this;
        }

        /**
         * Determines if the flag is empty.
         *
         */
        public boolean isEmpty() {
            if (values.isEmpty()) return true;
            adjustIndex();
            if (this.size() < index + 1) return true;
            return values.get(index).equals("");
        }

        /**
         * Used internally. Returns the size of the current values list.
         *
         */
        private int size() {
            return values.size();
        }
    }
}
TOP

Related Classes of net.aufdemrand.denizen.flags.FlagManager$Value

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.