pcgen.core.Globals.java Source code

Java tutorial

Introduction

Here is the source code for pcgen.core.Globals.java

Source

/*
 * Globals.java
 * Copyright 2001 (C) Bryan McRoberts <merton_monk@yahoo.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Created on April 21, 2001, 2:15 PM
 *
 * Current Ver: $Revision$
 * Last Editor: $Author$
 * Last Edited: $Date$
 *
 */
package pcgen.core;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.logging.Level;

import javax.swing.JFrame;

import org.apache.commons.lang.SystemUtils;

import pcgen.base.util.RandomUtil;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.Constants;
import pcgen.cdom.base.MasterListInterface;
import pcgen.cdom.content.BaseDice;
import pcgen.cdom.content.CNAbilityFactory;
import pcgen.cdom.enumeration.IntegerKey;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.RaceType;
import pcgen.cdom.enumeration.SourceFormat;
import pcgen.cdom.enumeration.StringKey;
import pcgen.cdom.enumeration.Type;
import pcgen.core.character.EquipSlot;
import pcgen.core.chooser.CDOMChooserFacadeImpl;
import pcgen.core.spell.Spell;
import pcgen.core.utils.CoreUtility;
import pcgen.facade.core.ChooserFacade.ChooserTreeViewType;
import pcgen.gui2.facade.Gui2InfoFactory;
import pcgen.rules.context.AbstractReferenceContext;
import pcgen.rules.context.ConsolidatedListCommitStrategy;
import pcgen.rules.context.LoadContext;
import pcgen.rules.context.RuntimeLoadContext;
import pcgen.rules.context.RuntimeReferenceContext;
import pcgen.system.ConfigurationSettings;
import pcgen.system.PCGenSettings;
import pcgen.util.Logging;
import pcgen.util.chooser.ChooserFactory;
import pcgen.util.enumeration.Load;
import pcgen.util.enumeration.VisionType;

/**
 * This is like the top level model container. However,
 * it is build from static methods rather than instantiated.
 *
 * @author Bryan McRoberts <merton_monk@users.sourceforge.net>
 * @author boomer70 <boomer70@yahoo.com>
 * @version $Revision$
 */
public final class Globals {
    /** These are changed during normal operation */
    private static List<PlayerCharacter> pcList = new ArrayList<PlayerCharacter>();
    /** Race, a s_EMPTYRACE */
    public static Race s_EMPTYRACE;

    /** These are system constants */
    public static final String javaVersion = System.getProperty("java.version"); //$NON-NLS-1$
    /** Java Version Major */
    public static final int javaVersionMajor = Integer.valueOf(javaVersion.substring(0, javaVersion.indexOf('.')))
            .intValue();
    /** Java Version Minor */
    public static final int javaVersionMinor = Integer
            .valueOf(javaVersion.substring(javaVersion.indexOf('.') + 1, javaVersion.lastIndexOf('.'))).intValue();

    /** NOTE: The defaultPath is duplicated in LstSystemLoader. */
    private static final String defaultPcgPath = Globals.getUserFilesPath() + File.separator + "characters"; //$NON-NLS-1$

    private static final List<String> custColumnWidth = new ArrayList<String>();
    private static SourceFormat sourceDisplay = SourceFormat.LONG;
    private static int selectedPaper = -1;

    /** we need maps for efficient lookups */
    private static Map<URI, Campaign> campaignMap = new HashMap<URI, Campaign>();
    private static Map<String, Campaign> campaignNameMap = new HashMap<String, Campaign>();
    private static Map<String, Spell> spellMap = new TreeMap<String, Spell>(String.CASE_INSENSITIVE_ORDER);
    private static Map<String, String> eqSlotMap = new HashMap<String, String>();

    /** We use lists for efficient iteration */
    private static List<Campaign> campaignList = new ArrayList<Campaign>(85);

    // end of filter creation sets
    private static JFrame rootFrame;
    private static final StringBuilder section15 = new StringBuilder(30000);

    /** whether or not the GUI is used (false for command line) */
    private static boolean useGUI = true;

    /** whether or not we are running on a Mac */
    public static final boolean isMacPlatform = SystemUtils.IS_OS_MAC;
    /** default location for options.ini on a Mac */
    public static final String defaultMacOptionsPath = System.getProperty("user.home")
            + "/Library/Preferences/pcgen";

    private static final Comparator<CDOMObject> pObjectComp = new Comparator<CDOMObject>() {
        @Override
        public int compare(final CDOMObject o1, final CDOMObject o2) {
            return o1.getKeyName().compareToIgnoreCase(o2.getKeyName());
        }
    };

    public static final Comparator<CDOMObject> pObjectNameComp = new Comparator<CDOMObject>() {
        @Override
        public int compare(final CDOMObject o1, final CDOMObject o2) {
            final Collator collator = Collator.getInstance();

            // Check sort keys first
            String key1 = o1.get(StringKey.SORT_KEY);
            if (key1 == null) {
                key1 = o1.getDisplayName();
            }
            String key2 = o2.get(StringKey.SORT_KEY);
            if (key2 == null) {
                key2 = o2.getDisplayName();
            }
            if (!key1.equals(key2)) {
                return collator.compare(key1, key2);
            }
            if (!o1.getDisplayName().equals(o2.getDisplayName())) {
                return collator.compare(o1.getDisplayName(), o2.getDisplayName());
            }
            // Fall back to keyname if the displayname is the same
            return collator.compare(o1.getKeyName(), o2.getKeyName());
        }
    };

    // Optimizations used by any code needing empty arrays.  All empty arrays
    // of the same type are idempotent.
    /** EMPTY_STRING_ARRAY*/
    public static final String[] EMPTY_STRING_ARRAY = new String[0];

    /**
     * Get a list of the allowed game modes
     * @return list of the allowed game modes
     */
    public static List<String> getAllowedGameModes() {
        if (SettingsHandler.getGame() != null) {
            return SettingsHandler.getGame().getAllowedModes();
        }

        return new ArrayList<String>();
    }

    /**
     * Get the Bio Set
     * @return the Bio Set
     */
    public static BioSet getBioSet() {
        return SettingsHandler.getGame().getBioSet();
    }

    /**
     * Get the campaign by file name
     * @param aName
     * @return Campaign
     */
    public static Campaign getCampaignByURI(final URI aName) {
        return getCampaignByURI(aName, true);
    }

    /**
     * This method is used to locate a Campaign object based on its file
     * name (in URL syntax).
     *
     * @param aName           String name of file, in URL.toString() format
     * @param complainOnError boolean true to log an error if the campaign
     *                        cannot be found
     * @return Campaign loaded from the given filename, or null if it
     *         cannot be found
     */
    public static Campaign getCampaignByURI(final URI aName, final boolean complainOnError) {
        final Campaign campaign = campaignMap.get(aName);

        if ((campaign == null) && complainOnError) {
            Logging.errorPrint("Could not find campaign by filename: " + aName);
        }

        return campaign;
    }

    /**
     * Get campaign list
     * @return campaign list
     */
    public static List<Campaign> getCampaignList() {
        return campaignList;
    }

    /**
     * Get campaign by key
     * @param aKey
     * @return Campaign
     */
    public static Campaign getCampaignKeyed(final String aKey) {

        Campaign campaign = getCampaignKeyedSilently(aKey);
        if (campaign == null) {
            Logging.errorPrint("Could not find campaign: " + aKey);
        }

        return campaign;
    }

    /**
     * Get campaign by key
     * @param aKey
     * @return Campaign
     */
    public static Campaign getCampaignKeyedSilently(final String aKey) {
        for (Campaign campaign : getCampaignList()) {
            if (campaign.getKeyName().equalsIgnoreCase(aKey)) {
                return campaign;
            }
        }

        return null;
    }

    /**
     * Finds all PObjects that match the passed in type.  All the types listed
     * in aType must match for the object to be returned.
     * @param aPObjectList List of PObjects to search
     * @param aType A "." separated list of TYPEs to match
     * @return List of PObjects matching all TYPEs
     */
    public static <T extends CDOMObject> List<T> getPObjectsOfType(final Collection<T> aPObjectList,
            final String aType) {
        final ArrayList<T> ret = new ArrayList<T>(aPObjectList.size());

        List<String> typeList = new ArrayList<String>();
        StringTokenizer tok = new StringTokenizer(aType, ".");
        while (tok.hasMoreTokens()) {
            typeList.add(tok.nextToken());
        }

        for (T anObject : aPObjectList) {
            boolean match = false;
            for (String type : typeList) {
                final boolean sense = !(type.charAt(0) == '!');
                if (anObject.isType(type) == sense) {
                    match = true;
                } else {
                    match = false;
                    break;
                }
            }
            if (match) {
                ret.add(anObject);
            }
        }
        ret.trimToSize();
        return ret;
    }

    /**
     * @param fromTab
     * @param col
     * @param value
     */
    public static void setCustColumnWidth(final String fromTab, final int col, final int value) {
        boolean found = false;
        final String cName = fromTab.concat(Integer.toString(col));
        final String addMe = cName.concat(Constants.PIPE).concat(Integer.toString(value));

        if (getCustColumnWidth().isEmpty()) {
            getCustColumnWidth().add(addMe);
        }

        final int loopMax = getCustColumnWidth().size();

        for (int i = 0; i < loopMax; ++i) {
            final String colWidth = getCustColumnWidth().get(i);
            if (colWidth == null || colWidth.length() == 0) {
                continue;
            }
            final StringTokenizer tTok = new StringTokenizer(colWidth, Constants.PIPE, false);
            final String tabName = tTok.nextToken();

            if (cName.equals(tabName)) {
                getCustColumnWidth().set(i, addMe);
                found = true;
            }
        }

        if (!found) {
            getCustColumnWidth().add(addMe);
        }
    }

    /**
     * Get custom column width
     * @param fromTab
     * @param col
     * @return custom column width
     */
    public static int getCustColumnWidth(final String fromTab, final int col) {
        int colSize = 0;
        final String cName = fromTab.concat(Integer.toString(col));

        final int loopMax = getCustColumnWidth().size();

        for (int i = 0; i < loopMax; ++i) {
            final StringTokenizer tTok = new StringTokenizer(getCustColumnWidth().get(i), Constants.PIPE, false);
            if (tTok.hasMoreTokens()) {
                final String tabName = tTok.nextToken();

                if (tabName.equals(cName)) {
                    colSize = Integer.parseInt(tTok.nextToken());
                }
            }
        }

        return colSize;
    }
    // END Game Modes Section.

    /**
     * Get the default path
     * @return default path
     */
    public static String getDefaultPath() {
        return ConfigurationSettings.getUserDir();
    }

    /**
     * Get the default path
     * @return default path
     */
    public static String getUserFilesPath() {
        return expandRelativePath(System.getProperty("user.home") + File.separator + ".pcgen");
    }

    /**
     * Returns the name of the Default Spell Book, or null if there is no default spell book.
     * @return default spellbook
     */
    public static String getDefaultSpellBook() {
        String book = null;

        if (SettingsHandler.getGame() != null) {
            book = SettingsHandler.getGame().getDefaultSpellBook();
        }

        return book;
    }

    /**
     * Get equipment slot by name
     * @param aName
     * @return equipment slot
     */
    public static EquipSlot getEquipSlotByName(final String aName) {
        for (EquipSlot es : SystemCollections.getUnmodifiableEquipSlotList()) {
            if (es.getSlotName().equals(aName)) {
                return es;
            }
        }

        return null;
    }

    /**
     * Get equipment slot map
     * @return equipment slot map
     */
    private static Map<String, String> getEquipSlotMap() {
        return eqSlotMap;
    }

    /**
     * Set equipment slot type count
     * @param aString
     * @param aNum
     */
    public static void setEquipSlotTypeCount(final String aString, final String aNum) {
        getEquipSlotMap().put(aString, aNum);
    }

    /**
     * returns the # of slots for an equipmentslots Type
     * The number of slots is define by the NUMSLOTS: line
     * in the game mode file equipmentslots.lst
     * @param aType
     * @return equipment slot type count
     */
    public static int getEquipSlotTypeCount(final String aType) {
        final String aNum = getEquipSlotMap().get(aType);

        if (aNum != null) {
            return Integer.parseInt(aNum);
        }
        return 0;
    }

    /**
     * Get game mode align text
     * @return game mode align text
     */
    public static String getGameModeAlignmentText() {
        return SettingsHandler.getGame().getAlignmentText();
    }

    /**
     * Get the game mode point pool name
     * @return game mode point pool name
     */
    public static String getGameModePointPoolName() {
        return SettingsHandler.getGame().getPointPoolName();
    }

    /**
     * TRUE if the game mode has a point pool
     * @return TRUE if the game mode has a point pool
     */
    public static boolean getGameModeHasPointPool() {
        return getGameModePointPoolName().length() != 0;
    }

    /**
     * Get game mode spell range formula
     * @param aRange
     * @return game mode spell range formula
     */
    public static String getGameModeSpellRangeFormula(final String aRange) {
        return SettingsHandler.getGame().getSpellRangeFormula(aRange);
    }

    /**
     * Get game mode unit set
     * @return game mode unit set
     */
    public static UnitSet getGameModeUnitSet() {
        return SettingsHandler.getGame().getUnitSet();
    }

    /**
     * Get global deity list
     * @return global deity lis
     */
    public static List<String> getGlobalDeityList() {
        if (SettingsHandler.getGame() != null) {
            return SettingsHandler.getGame().getDeityList();
        }

        return new ArrayList<String>();
    }

    /**
     * Return TRUE if in a particular game mode
     * @param gameMode
     * @return TRUE if in a particular game mode
     */
    public static boolean isInGameMode(final String gameMode) {
        if ((gameMode.length() == 0) || ((SettingsHandler.getGame() != null)
                && gameMode.equalsIgnoreCase(SettingsHandler.getGame().getName()))) {
            return true;
        }

        return false;
    }

    /**
     * Set PC List
     * @param argPcList
     */
    public static void setPCList(final List<PlayerCharacter> argPcList) {
        pcList = argPcList;
    }

    /**
     * Get PC List
     * @return List of Pcs
     */
    public static List<PlayerCharacter> getPCList() {
        return pcList;
    }

    /**
     * Get paper count
     * @return paper count
     */
    public static int getPaperCount() {
        return SettingsHandler.getGame().getModeContext().getReferenceContext()
                .getConstructedObjectCount(PaperInfo.class);
    }

    /**
     * Get paper info
     * @param infoType
     * @return paper info
     */
    public static String getPaperInfo(final int infoType) {
        return getPaperInfo(getSelectedPaper(), infoType);
    }

    /**
     * Get paper info
     * @param idx
     * @param infoType
     * @return paper info
     */
    public static String getPaperInfo(final int idx, final int infoType) {
        if ((idx < 0) || (idx >= SettingsHandler.getGame().getModeContext().getReferenceContext()
                .getConstructedObjectCount(PaperInfo.class))) {
            return null;
        }

        final PaperInfo pi = SettingsHandler.getGame().getModeContext().getReferenceContext()
                .getItemInOrder(PaperInfo.class, idx);

        return pi.getPaperInfo(infoType);
    }

    /**
     * Sets the root frame
     * The root frame is the container in which all
     * other panels, frame etc are placed.
     *
     * @param frame the <code>PCGen_Frame1</code> which is to be root
     */
    public static void setRootFrame(final JFrame frame) {
        rootFrame = frame;
    }

    /**
     * Returns the current root frame.
     *
     * @return the <code>rootFrame</code> property
     */
    public static JFrame getRootFrame() {
        return rootFrame;
    }

    /**
     * Get the section 15
     * @return section 15
     */
    public static StringBuilder getSection15() {
        return section15;
    }

    /**
     * Get selected paper
     * @return selected paper
     */
    public static int getSelectedPaper() {
        return selectedPaper;
    }

    /**
     * Set source display
     * @param sourceType
     */
    public static void setSourceDisplay(final SourceFormat sourceType) {
        sourceDisplay = sourceType;
    }

    /**
     * Get source display
     * @return source display
     */
    public static SourceFormat getSourceDisplay() {
        return sourceDisplay;
    }

    /**
     * Get spell by key
     * @param aKey
     * @return Spell
     */
    public static Spell getSpellKeyed(final String aKey) {
        return getSpellMap().get(aKey);
    }

    /**
     * Get spell map
     * @return spell map
     */
    public static Map<String, Spell> getSpellMap() {
        return Collections.unmodifiableMap(spellMap);
    }

    /**
     * Add spell to the spell map.
     * 
     * @param key The key the object is associated with.
     * @param anObject The object to be added to the map.
     */
    public static void addToSpellMap(final String key, final Spell spell) {
        spellMap.put(key, spell);
    }

    /**
     * Remove the item with the listed key from the global spell map.
     * 
     * @param key The key of the item to be removed.
     * @return Previous value associated with specified key, or null 
     * if there was no mapping for key.
     */
    public static Object removeFromSpellMap(final String key) {
        return spellMap.remove(key);
    }

    /**
     * Sets whether to use the GUI or not
     * @param aBool
     */
    public static void setUseGUI(final boolean aBool) {
        useGUI = aBool;
    }

    /**
     * TRUE if using UI
     * @return TRUE if using UI
     */
    public static boolean getUseGUI() {
        return isUseGUI();
    }

    /**
     * This method is called by the persistence layer to
     * add a campaign that it has located to the globl campaign list.
     *
     * @param campaign Campaign loaded from persistence to add to the
     *                 Global campaign list
     */
    public static void addCampaign(final Campaign campaign) {
        campaignMap.put(campaign.getSourceURI(), campaign);
        campaignList.add(campaign);
        Campaign oldCampaign = campaignNameMap.put(campaign.getName(), campaign);
        if (oldCampaign != null) {
            if (oldCampaign.getSourceURI().toString().equalsIgnoreCase(campaign.getSourceURI().toString())) {
                Logging.errorPrint(
                        "The campaign (" + campaign.getName() + ") was referenced with the incorrect case: "
                                + oldCampaign.getSourceURI() + " vs " + campaign.getSourceURI());
            } else {
                Logging.errorPrint(
                        "Loaded Campaigns with matching names (" + campaign.getName() + ") at different Locations: "
                                + oldCampaign.getSourceURI() + " " + campaign.getSourceURI());
            }
        }
    }

    /**
     * Adjust damage
     * @param aDamage
     * @param baseSize
     * @param finalSize
     * @return adjusted damage
     */
    public static String adjustDamage(final String aDamage, int baseSize, final int finalSize) {
        AbstractReferenceContext ref = Globals.getContext().getReferenceContext();
        BaseDice bd = ref.silentlyGetConstructedCDOMObject(BaseDice.class, aDamage);
        int multiplier = 0;
        List<RollInfo> steps = null;
        if (bd == null) {
            //Need to test for higher dice
            final RollInfo aRollInfo = new RollInfo(aDamage);
            final String baseDice = "1d" + Integer.toString(aRollInfo.sides);
            bd = ref.silentlyGetConstructedCDOMObject(BaseDice.class, baseDice);
            if (bd != null) {
                multiplier = aRollInfo.times;
            }
        } else {
            multiplier = 1;
        }
        RollInfo bi;
        if (bd == null) {
            bi = new RollInfo(aDamage);
        } else {
            if (baseSize < finalSize) {
                steps = bd.getUpSteps();
            } else if (baseSize > finalSize) {
                steps = bd.getDownSteps();
            } else {
                // Not a warning?
                return aDamage;
            }
            int difference = Math.abs(baseSize - finalSize);

            int index;
            if (steps.size() > difference) {
                index = difference - 1;
            } else {
                index = steps.size() - 1;
            }
            bi = steps.get(index);
        }
        if (multiplier > 1) {
            // Ugh, have to do this for "cloning" to avoid polluting the master
            // RollInfo
            bi = new RollInfo(bi.toString());
            bi.times *= multiplier;
        }
        return bi.toString();
    }

    /**
     * Return true if resizing the equipment will have any "noticable" effect
     * checks for cost modification, armor bonus, weight, capacity
     * @param aPC
     *
     * @param aEq
     * @param typeList
     * @return TRUE or FALSE
     */
    public static boolean canResizeHaveEffect(final PlayerCharacter aPC, final Equipment aEq,
            List<String> typeList) {
        // cycle through typeList and see if it matches one in the BONUS:ITEMCOST|TYPE=etc on sizeadjustment
        if (typeList == null) {
            typeList = aEq.typeList();
        }
        List<String> upperTypeList = new ArrayList<String>(typeList.size());
        for (String type : typeList) {
            upperTypeList.add(type.toUpperCase());
        }

        List<String> resizeTypeList = SettingsHandler.getGame().getResizableTypeList();
        for (String rType : resizeTypeList) {
            if (upperTypeList.contains(rType.toUpperCase())) {
                return true;
            }
        }

        return false;
    }

    /**
     * Find out the state of a PRERULE check
     * @param aKey
     * @return true or false
     */
    public static boolean checkRule(final String aKey) {
        RuleCheck rule = SettingsHandler.getGame().getModeContext().getReferenceContext()
                .silentlyGetConstructedCDOMObject(RuleCheck.class, aKey);
        if (rule == null) {
            return false;
        }
        if (SettingsHandler.hasRuleCheck(aKey)) {
            return SettingsHandler.getRuleCheck(aKey);
        } else {
            return rule.getDefault();
        }
    }

    /**
     * This method is called by the persistence layer to clear the global
     * campaigns for a refresh.
     */
    public static void clearCampaignsForRefresh() {
        emptyLists();
        campaignMap.clear();
        campaignList.clear();
    }

    /**
     * Check if enough data has been loaded to support character creation.
     * Will also report to the log the number of items of each of the 
     * necessary types that are currently loaded.  
     * @return true or false
     */
    public static boolean displayListsHappy() {
        // NOTE: If you add something here be sure to update the log output below
        boolean listsHappy = checkListsHappy();

        Level logLevel = listsHappy ? Logging.DEBUG : Logging.WARNING;
        if (Logging.isLoggable(logLevel)) {
            Logging.log(logLevel, "Number of objects loaded. The following should " + "all be greater than 0:");
            Logging.log(logLevel, "Races="
                    + Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Race.class).size());
            Logging.log(logLevel, "Classes="
                    + getContext().getReferenceContext().getConstructedCDOMObjects(PCClass.class).size());
            Logging.log(logLevel, "Skills="
                    + Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Skill.class).size());
            Logging.log(logLevel, "Feats=" + Globals.getContext().getReferenceContext()
                    .getManufacturer(Ability.class, AbilityCategory.FEAT).getConstructedObjectCount());
            Logging.log(logLevel, "Equipment="
                    + Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Equipment.class).size());
            Logging.log(logLevel, "ArmorProfs="
                    + Globals.getContext().getReferenceContext().getConstructedCDOMObjects(ArmorProf.class).size());
            Logging.log(logLevel, "ShieldProfs=" + Globals.getContext().getReferenceContext()
                    .getConstructedCDOMObjects(ShieldProf.class).size());
            Logging.log(logLevel, "WeaponProfs=" + Globals.getContext().getReferenceContext()
                    .getConstructedCDOMObjects(WeaponProf.class).size());
            Logging.log(logLevel, "Kits="
                    + Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Kit.class).size());
            Logging.log(logLevel, "Templates=" + Globals.getContext().getReferenceContext()
                    .getConstructedCDOMObjects(PCTemplate.class).size());
        }
        return listsHappy;
    }

    /**
     * Check if enough data has been loaded to support character creation.
     * @return true or false
     */
    public static boolean checkListsHappy() {
        // NOTE: If you add something here be sure to update the log output in displayListsHappy above
        boolean listsHappy = !((Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Race.class)
                .size() == 0)
                || (getContext().getReferenceContext().getConstructedCDOMObjects(PCClass.class).size() == 0)
                //            || (Globals.getContext().ref.getConstructedCDOMObjects(Skill.class).size() == 0)
                //            || (Globals.getContext().ref.getManufacturer(
                //                  Ability.class, AbilityCategory.FEAT).getConstructedObjectCount() == 0)
                || (Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Equipment.class)
                        .size() == 0)
                || (Globals.getContext().getReferenceContext().getConstructedCDOMObjects(WeaponProf.class)
                        .size() == 0));
        return listsHappy;
    }

    /**
     * Clears all lists of game data.
     */
    public static void emptyLists() {
        // These lists do not need cleared; they are tied to game mode
        // alignmentList
        // checkList
        // gameModeList
        // campaignList
        // statList
        // All other lists should be cleared!!!
        //////////////////////////////////////
        // DO NOT CLEAR THESE HERE!!!
        // They only get loaded once.
        //
        //birthplaceList.clear();
        //cityList.clear();
        //hairStyleList.clear();
        //helpContextFileList.clear();
        //interestsList.clear();
        //locationList.clear();
        //paperInfo.clear();
        //phobiaList.clear();
        //phraseList.clear();
        //schoolsList.clear();
        //sizeAdjustmentList.clear();
        //specialsList.clear();
        //speechList.clear();
        //traitList.clear();
        //unitSet.clear();
        //////////////////////////////////////

        // Clear Maps (not strictly necessary, but done for consistency)
        spellMap = new TreeMap<String, Spell>(String.CASE_INSENSITIVE_ORDER);
        VisionType.clearConstants();

        // Perform other special cleanup
        Equipment.clearEquipmentTypes();
        SettingsHandler.getGame().clearLoadContext();

        RaceType.clearConstants();
        createEmptyRace();
        CNAbilityFactory.reset();
    }

    /**
     * Execute post export commands for standard files
     * @param fileName
     */
    public static void executePostExportCommandStandard(final String fileName) {
        String postExportCommand = SettingsHandler.getPostExportCommandStandard();
        executePostExportCommand(fileName, postExportCommand);
    }

    /**
     * Execute post export commands for PDF
     * @param fileName
     */
    public static void executePostExportCommandPDF(final String fileName) {
        String postExportCommand = SettingsHandler.getPostExportCommandPDF();
        executePostExportCommand(fileName, postExportCommand);
    }

    /**
     * Execute any post export commands
     * @param fileName
     * @param postExportCommand
     */
    private static void executePostExportCommand(final String fileName, String postExportCommand) {
        ArrayList<String> aList = new ArrayList<String>();
        StringTokenizer aTok = new StringTokenizer(postExportCommand, " ");
        while (aTok.hasMoreTokens()) {
            aList.add(aTok.nextToken());
        }
        String[] cmdArray = new String[aList.size()];
        for (int idx = 0; idx < aList.size(); idx++) {
            final String s = aList.get(idx);
            if (s.indexOf("%") > -1) {
                final String beforeString = s.substring(0, s.indexOf("%"));
                final String afterString = s.substring(s.indexOf("%") + 1);
                cmdArray[idx] = beforeString + fileName + afterString;
            } else {
                cmdArray[idx] = s;
            }
        }

        if (cmdArray.length > 0) {
            try {
                Runtime.getRuntime().exec(cmdArray);
            } catch (IOException ex) {
                Logging.errorPrint("Could not execute " + postExportCommand + " after exporting " + fileName, ex);
            }
        }
    }

    /**
     * Roll the hitpoints for a single level.
     *
     * @param min the minimum number on the die
     * @param max the maximum number on the die
     * @param name the PC's name (used for a message to the user)
     * @param level the level the hit points are being rolled for (used for a message to the user)
     * @param totalLevel the level the hitpoints are being rolled for (used in maths)
     * @return the hitpoints for the given level.
     */
    public static int rollHP(final int min, final int max, final String name, final int level,
            final int totalLevel) {
        int roll;

        switch (SettingsHandler.getHPRollMethod()) {
        case Constants.HP_USER_ROLLED:
            roll = -1;

            break;

        case Constants.HP_AVERAGE:

            roll = max - min;

            // (n+1)/2
            // average roll on a die with an  odd # of sides works out exactly
            // average roll on a die with an even # of sides will have an extra 0.5

            if (((totalLevel & 0x01) == 0) && ((roll & 0x01) != 0)) {
                ++roll;
            }

            roll = min + (roll / 2);

            break;

        case Constants.HP_AUTO_MAX:
            roll = max;

            break;

        case Constants.HP_PERCENTAGE:
            roll = min - 1 + (int) ((SettingsHandler.getHPPercent() * (max - min + 1)) / 100.0);

            break;

        case Constants.HP_AVERAGE_ROUNDED_UP:
            roll = (int) Math.ceil((min + max) / 2.0);

            break;

        case Constants.HP_STANDARD:
        default:
            roll = Math.abs(RandomUtil.getRandomInt(max - min + 1)) + min;

            break;
        }

        //      if (SettingsHandler.getShowHPDialogAtLevelUp())
        //      {
        //         final Object[] rollChoices = new Object[max - min + 2];
        //         rollChoices[0] = Constants.NONESELECTED;
        //
        //         for (int i = min; i <= max; ++i)
        //         {
        //            rollChoices[i - min + 1] = i;
        //         }
        //
        //         while (min <= max)
        //         {
        //            //TODO: This must be refactored away. Core shouldn't know about gui.
        //            final InputInterface ii = InputFactory.getInputInstance();
        //            final Object selectedValue = ii.showInputDialog(Globals.getRootFrame(),
        //               "Randomly generate a number between " + min + " and " + max
        //                  + "." + Constants.LINE_SEPARATOR
        //                  + "Select it from the box below.",
        //               SettingsHandler.getGame().getHPText() + " for "
        //                  + CoreUtility.ordinal(level) + " level of " + name,
        //               MessageType.INFORMATION,
        //               rollChoices, roll);
        //
        //            if ((selectedValue != null) && (selectedValue instanceof Integer))
        //            {
        //               roll = (Integer) selectedValue;
        //
        //               break;
        //            }
        //         }
        //      }

        return roll;
    }

    /**
     * Select the paper
     * @param paperName
     * @return TRUE if OK
     */
    public static boolean selectPaper(final String paperName) {
        for (int i = 0; i < SettingsHandler.getGame().getModeContext().getReferenceContext()
                .getConstructedObjectCount(PaperInfo.class); ++i) {
            final PaperInfo pi = SettingsHandler.getGame().getModeContext().getReferenceContext()
                    .getItemInOrder(PaperInfo.class, i);

            if (pi.getName().equals(paperName)) {
                setSelectedPaper(i);
                PCGenSettings.getInstance().setProperty(PCGenSettings.PAPERSIZE, paperName);

                return true;
            }
        }

        setSelectedPaper(-1);

        return false;
    }

    /**
     * Apply the user's preferences to the initial state of the Globals.  
     */
    public static void initPreferences() {
        if (selectedPaper != -1) {
            // Already initialized
            return;
        }
        String papersize = PCGenSettings.getInstance().initProperty(PCGenSettings.PAPERSIZE, "A4");
        selectPaper(papersize);
    }

    /**
     * Sorts chooser lists using the appropriate method, based on the type of the first item in either list.
     * Not pretty, but it works.
     *
     * @param availableList
     * @param selectedList
     */
    public static void sortChooserLists(final List availableList, final List selectedList) {
        final boolean nonPObjectInList;

        if (availableList.size() > 0) {
            nonPObjectInList = !(availableList.get(0) instanceof CDOMObject);
        } else if (selectedList.size() > 0) {
            nonPObjectInList = !(selectedList.get(0) instanceof CDOMObject);
        } else {
            nonPObjectInList = false;
        }

        if (nonPObjectInList) {
            Collections.sort(availableList);
            // NOCHOICE feats add nulls to the selectedList
            if (selectedList.size() > 0 && selectedList.get(0) != null) {
                Collections.sort(selectedList);
            }
        } else {
            Globals.sortPObjectListByName(availableList);
            Globals.sortPObjectListByName(selectedList);
        }
    }

    /**
     * Sort Pcgen Object list
     * @param aList
     * @return Sorted list of Pcgen Objects
     */
    public static List<? extends CDOMObject> sortPObjectList(final List<? extends CDOMObject> aList) {
        Collections.sort(aList, pObjectComp);

        return aList;
    }

    /**
     * Sort Pcgen Object list by name
     * @param <T> 
     * 
     * @param aList
     * @return Sorted list of Pcgen Objects
     */
    public static <T extends CDOMObject> List<T> sortPObjectListByName(final List<T> aList) {
        Collections.sort(aList, pObjectNameComp);

        return aList;
    }

    static String getBonusFeatString() {
        List<String> bonusFeatLevels = SettingsHandler.getGame().getBonusFeatLevels();
        if (bonusFeatLevels == null || bonusFeatLevels.isEmpty()) {
            // Default to no bonus feats.
            return "9999|0";
        }
        return bonusFeatLevels.get(0);
    }

    static int getBonusStatsForLevel(final int level, final PlayerCharacter aPC) {
        int num = 0;

        for (String s : SettingsHandler.getGame().getBonusStatLevels()) {
            num = bonusParsing(s, level, num, aPC);
        }

        return num;
    }

    /**
     * Get a choice from a list
     * @param title The title of the chooser dialog.
     * @param choiceList The list of possible choices.
     * @param selectedList The values already selected (none of which should be in the available list).
     * @param pool The number of choices the user can make.
     * @param pc The character the choice is being made for.
     * @return a choice
     */
    public static <T> List<T> getChoiceFromList(final String title, final List<T> choiceList,
            final List<T> selectedList, final int pool, PlayerCharacter pc) {
        return getChoiceFromList(title, choiceList, selectedList, pool, false, false, pc);
    }

    /**
     * Ask the user for a choice from a list.
     * @param title The title of the chooser dialog.
     * @param choiceList The list of possible choices.
     * @param selectedList The values already selected (none of which should be in the available list).
     * @param pool The number of choices the user can make.
     * @param forceChoice true if the user will be forced to make all choices.
     * @param preferRadioSelection true if this would be better presented as a radio button list
     * @param pc The character the choice is being made for.
     * @return The list of choices made by the user.
     */
    public static <T> List<T> getChoiceFromList(final String title, final List<T> choiceList,
            final List<T> selectedList, final int pool, final boolean forceChoice,
            final boolean preferRadioSelection, PlayerCharacter pc) {
        List<T> startingSelectedList = new ArrayList<T>();
        if (selectedList != null) {
            startingSelectedList = selectedList;
        }

        CDOMChooserFacadeImpl<T> chooserFacade = new CDOMChooserFacadeImpl<T>(title, choiceList,
                startingSelectedList, pool);
        chooserFacade.setAllowsDups(false);
        chooserFacade.setRequireCompleteSelection(forceChoice);
        chooserFacade.setInfoFactory(new Gui2InfoFactory(pc));
        chooserFacade.setDefaultView(ChooserTreeViewType.NAME);
        chooserFacade.setPreferRadioSelection(preferRadioSelection);
        ChooserFactory.getDelegate().showGeneralChooser(chooserFacade);

        return chooserFacade.getFinalSelected();
    }

    static List<String> getCustColumnWidth() {
        return custColumnWidth;
    }

    static String getDefaultPcgPath() {
        return expandRelativePath(defaultPcgPath);
    }

    /**
     * returns the location of the "filepaths.ini" file
     * which could be one of several locations
     * depending on the OS and user preferences
     * @return option path
     */
    static String getFilepathsPath() {
        String aPath;

        // first see if it was specified on the command line
        aPath = System.getProperty("pcgen.filepaths"); //$NON-NLS-1$

        if (aPath == null) {
            aPath = System.getProperty("user.dir") + File.separator + "filepaths.ini"; //$NON-NLS-1$ //$NON-NLS-2$;
        } else {
            File testPath = new File(expandRelativePath(aPath));
            if (testPath.exists() && testPath.isDirectory()) {
                aPath = testPath.getAbsolutePath() + File.separator + "filepaths.ini"; //$NON-NLS-1$
                testPath = new File(aPath);
            }
            if (testPath.exists() && !testPath.canWrite()) {
                Logging.errorPrint("WARNING: The filepaths file you specified is not updatable. "
                        + "Filepath changes will not be saved. File is " + testPath.getAbsolutePath());
            }
        }

        return expandRelativePath(aPath);
    }

    /**
     * returns the location of the "filter.ini" file
     * which could be one of several locations
     * depending on the OS and user preferences
     * @return filter path
     */
    static String getFilterPath() {
        String aPath;

        // first see if it was specified on the command line
        aPath = System.getProperty("pcgen.filter");

        if (aPath == null) {
            aPath = getFilePath("filter.ini");
        } else {
            File testPath = new File(expandRelativePath(aPath));
            if (testPath.exists() && testPath.isDirectory()) {
                aPath = testPath.getAbsolutePath() + File.separator + "filter.ini";
                testPath = new File(aPath);
            }
            if (testPath.exists() && !testPath.canWrite()) {
                Logging.errorPrint("WARNING: The filter file you specified is not updatable. "
                        + "Filter changes will not be saved. File is " + testPath.getAbsolutePath());
            }
        }

        return expandRelativePath(aPath);
    }

    /**
     * returns the location of the "options.ini" file
     * which could be one of several locations
     * depending on the OS and user preferences
     * @return option path
     */
    static String getOptionsPath() {
        String aPath;

        // first see if it was specified on the command line
        aPath = System.getProperty("pcgen.options");

        if (aPath == null) {
            aPath = getFilePath("options.ini");
        } else {
            File testPath = new File(expandRelativePath(aPath));
            if (testPath.exists() && testPath.isDirectory()) {
                aPath = testPath.getAbsolutePath() + File.separator + "options.ini";
                testPath = new File(aPath);
            }
            if (testPath.exists() && !testPath.canWrite()) {
                Logging.errorPrint("WARNING: The options file you specified is not updatable. "
                        + "Settings changes will not be saved. File is " + testPath.getAbsolutePath());
            }
        }

        return expandRelativePath(aPath);
    }

    public static int getSkillMultiplierForLevel(final int level) {
        final List<String> sml = SettingsHandler.getGame().getSkillMultiplierLevels();

        if ((level > sml.size()) || (level <= 0)) {
            return 1;
        }

        return Integer.parseInt(sml.get(level - 1));
    }

    /**
     * Reduce/increase damage for modified size as per DMG p.162
     * @param aDamage
     * @param baseSize
     * @param newSize
     * @return String
     */
    public static String adjustDamage(String aDamage, SizeAdjustment baseSize, SizeAdjustment newSize) {
        if (aDamage.length() == 0) {
            return aDamage;
        }
        int baseIndex = baseSize.get(IntegerKey.SIZEORDER);
        int newIndex = newSize.get(IntegerKey.SIZEORDER);
        return adjustDamage(aDamage, baseIndex, newIndex);
    }

    public static double calcEncumberedMove(final Load load, final double unencumberedMove) {
        double encumberedMove;

        switch (load) {
        case LIGHT:
            encumberedMove = unencumberedMove;

            break;

        case MEDIUM:
        case HEAVY:

            if (CoreUtility.doublesEqual(unencumberedMove, 5)) {
                encumberedMove = 5;
            } else if (CoreUtility.doublesEqual(unencumberedMove, 10)) {
                encumberedMove = 5;
            } else {
                encumberedMove = (Math.floor(unencumberedMove / 15) * 10) + (((int) unencumberedMove) % 15);
            }

            break;

        case OVERLOAD:
            encumberedMove = 0;

            break;

        default:
            Logging.errorPrint("The load " + load + " is not possible.");
            encumberedMove = 0;

            break;
        }

        return encumberedMove;
    }

    // Methods

    static void initCustColumnWidth(final List<String> l) {
        getCustColumnWidth().clear();
        getCustColumnWidth().addAll(l);
    }

    /**
     * Get a writable path for storing files
     * First check to see if it's been set in-program
     * Then check user home directory
     * Else use directory pcgen started from
     * @param aString
     * @return file path
     */
    private static String getFilePath(final String aString) {
        final String fType = SettingsHandler.getFilePaths();

        if ((fType == null) || fType.equals("pcgen")) {
            // we are either running PCGen for the first
            // time or user wants default file locations
            return System.getProperty("user.dir") + File.separator + aString;
        } else if (fType.equals("user")) {
            // use the users "home" directory + .pcgen
            return System.getProperty("user.home") + File.separator + ".pcgen" + File.separator + aString;
        } else if (fType.equals("mac_user")) {
            // use the users "home" directory + standard Mac settings
            return System.getProperty("user.home") + "/Library/Preferences/pcgen" + File.separator + aString;
        } else {
            // use the specified directory
            return fType + File.separator + aString;
        }
    }

    private static void setSelectedPaper(final int argSelectedPaper) {
        Globals.selectedPaper = argSelectedPaper;
    }

    private static boolean isUseGUI() {
        return useGUI;
    }

    private static int bonusParsing(final String l, final int level, int num, final PlayerCharacter aPC) {
        // should be in format levelnum,rangenum[,numchoices] 
        final StringTokenizer aTok = new StringTokenizer(l, "|", false);
        final int startLevel = Integer.parseInt(aTok.nextToken());
        final String rangeLevelFormula = aTok.nextToken();
        final int rangeLevel = aPC.getVariableValue(rangeLevelFormula, "").intValue();
        int numChoices = 1;
        if (aTok.hasMoreTokens()) {
            numChoices = Integer.parseInt(aTok.nextToken());
        }

        if ((level == startLevel)
                || ((level > startLevel) && (rangeLevel > 0) && (((level - startLevel) % rangeLevel) == 0))) {
            num += numChoices;
        }

        return num;
    }

    public static void createEmptyRace() {
        if (s_EMPTYRACE == null) {
            s_EMPTYRACE = new Race();
            s_EMPTYRACE.setName(Constants.NONESELECTED);
            s_EMPTYRACE.addToListFor(ListKey.TYPE, Type.HUMANOID);
        }

        getContext().getReferenceContext().importObject(s_EMPTYRACE);
    }

    private static String expandRelativePath(String path) {
        if (path.startsWith("@")) {
            path = System.getProperty("user.dir") + File.separator + path.substring(1);
        }

        return path;
    }

    public static LoadContext getContext() {
        return SettingsHandler.getGame().getContext();
    }

    private static LoadContext globalContext = new RuntimeLoadContext(new RuntimeReferenceContext(),
            new ConsolidatedListCommitStrategy());

    public static LoadContext getGlobalContext() {
        return globalContext;
    }

    /**
     * @return The class instance controlling the association lists for the current game mode.
     */
    public static MasterListInterface getMasterLists() {
        return SettingsHandler.getGame().getMasterLists();
    }
}