/*
 ******************************************************************************
 * Copyright (C) 2004-2008 International Business Machines Corporation and    *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */
package org.unicode.cldr.icu;

//CLDR imports
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.MissingResourceException;
import java.util.StringTokenizer;

import org.unicode.cldr.ant.CLDRConverterTool;
import org.unicode.cldr.util.CLDRFile;
import org.unicode.cldr.util.SimpleFactory;
import org.unicode.cldr.util.XPathParts.Comments;

import com.ibm.icu.dev.tool.UOption;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceTypeMismatchException;

/**
 * This class is a user runnable class intended to create LDML documents by
 * calling the appropriate ICU API functions and retrieving the data.
 * 
 * @author Brian Rower - IBM - August 2009
 * 
 */
@SuppressWarnings("deprecation")
public class ICU2LDMLWriter extends CLDRConverterTool
{
    private static final UOption[] options = new UOption[] {
        UOption.HELP_H(),
        UOption.HELP_QUESTION_MARK(),
        UOption.DESTDIR(),
        UOption.VERBOSE(),
        UOption.create("main", 'm', UOption.NO_ARG),
        UOption.create("trans", 't', UOption.NO_ARG),
        UOption.create("coll", 'c', UOption.NO_ARG)
    };

    // ******************** ENUMS *************************

    // For command line options
    private static final int OP_HELP1 = 0;
    private static final int OP_HELP2 = 1;
    private static final int OP_DESTDIR = 2;
    private static final int OP_VERBOSE = 3;
    private static final int OP_MAIN = 4;
    private static final int OP_TRANS = 5;
    private static final int OP_COLL = 6;

    private static final String FILE_SEPERATOR = "\\";
    private static final String LDML_FILE_EXTENSION = ".xml";

    private static final String MAIN_FOLDER = "main";
    private static final String COLLATION_FOLDER = "collation";
    private static final String TRANSFORMS_FOLDER = "transforms";

    private static final char SEP = '/';

    private boolean verbose = false;
    private boolean doMain = false;
    private boolean doTrans = false;
    private boolean doColl = false;

    private String outDir = "./";

    private String currentLocaleName = null;

    private Hashtable<String, String> theHash = null;

    private static final String COMMENT = "This ldml file was generated by the ICU2LDMLWriter"
        + " from ICU resource bundle data.\nIf you are looking for the"
        + " full CLDR version of this document please look in the CLDR repository.";

    public static void main(String[] args)
    {
        ICU2LDMLWriter w = new ICU2LDMLWriter();
        w.processArgs(args);
    }

    public void processArgs(String[] args)
    {
        int remainingArgc = 0;
        // for some reason when
        // Class classDefinition = Class.forName(className);
        // object = classDefinition.newInstance();
        // is done then the options are not reset!!
        for (int i = 0; i < options.length; i++)
        {
            options[i].doesOccur = false;
        }
        try
        {
            remainingArgc = UOption.parseArgs(args, options);
        } catch (Exception e)
        {
            printError("(parsing args): " + e.toString());
            e.printStackTrace();
            usage();
        }

        if (options[OP_HELP1].doesOccur || options[OP_HELP2].doesOccur || args.length == 0)
        {
            usage();
        }

        if (options[OP_VERBOSE].doesOccur)
        {
            verbose = true;
        }

        if (options[OP_DESTDIR].doesOccur)
        {
            outDir = options[OP_DESTDIR].value;
        }

        if (options[OP_MAIN].doesOccur)
        {
            doMain = true;
        }
        if (options[OP_TRANS].doesOccur)
        {
            doTrans = true;
        }
        if (options[OP_COLL].doesOccur)
        {
            doColl = true;
        }

        // if they specified files, we'll generate those, otherwise we'll generate all possible locale files
        if (remainingArgc > 0)
        {
            for (int i = 0; i < remainingArgc; i++)
            {
                String localeName = args[i];
                if (localeName.indexOf(".") > 0)
                {
                    localeName = localeName.substring(0, localeName.indexOf("."));

                }
                currentLocaleName = localeName;
                processLocale(args[i]);
            }
        }
        else
        {
            ULocale[] locales = ULocale.getAvailableLocales();
            for (int i = 0; i < locales.length; i++)
            {
                currentLocaleName = locales[i].getName();
                processLocale(locales[i]);
            }
            ULocale r = new ULocale("root");
            currentLocaleName = r.getName();
            processLocale(r);
        }

    }

    private void processLocale(String localeID)
    {
        ULocale temp = new ULocale(localeID);
        if (temp.getName() == null)
        {
            // the localeID given was not valid, exit
            printError("Invalid locale ID: " + localeID);
            System.exit(-1);
        }
        processLocale(temp);
    }

    private void processLocale(ULocale locale)
    {
        printInfo("Processing " + locale.getName());

        // process each type of possible file...
        // ****************************** Main ********************************
        if (doMain || (!(doMain || doTrans || doColl)))
        {
            try
            {
                processMain(locale);
            } catch (Exception e)
            {
                printError("Could not produce main locale file: " + getMainPath(locale));
            }
        }

        // ****************************** Collation *****************************
        if (doColl || (!(doMain || doTrans || doColl)))
        {
            try
            {
                processCollation(locale);
            } catch (Exception e)
            {
                printError("Could not produce collation file: " + getCollationPath(locale));
            }
        }
        // **************************** Transforms *******************************
        if (doTrans || (!(doMain || doTrans || doColl)))
        {
            try
            {
                processTransforms(locale);
            } catch (Exception e)
            {
                printError("Could not produce transforms file: " + getTransformsPath(locale));
            }
        }
    }

    private void processMain(ULocale locale) throws Exception
    {
        CLDRFile xfile = SimpleFactory.makeFile(locale.getName());

        final String baseName = "com/ibm/icu/impl/data/icudt41b";
        ClassLoader loader = ICUData.class.getClassLoader();

        if (loader == null) { // boot class loader
            loader = ClassLoader.getSystemClassLoader();
        }

        ICUResourceBundle.resetBundleCache();
        ICUResourceBundle bund = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(baseName, locale.getName(),
            loader, true);
        String curXPath = "//";
        curXPath += LDMLConstants.LDML;
        xfile.addComment(curXPath, COMMENT, Comments.CommentType.PREBLOCK);

        addLocaleDisplayNames(xfile, curXPath, bund);
        addCharacters(xfile, curXPath, bund);
        addDelimiters(xfile, curXPath, bund);
        addDates(xfile, curXPath, bund);
        addNumbers(xfile, curXPath, bund);
        addUnits(xfile, curXPath, bund);
        addLayout(xfile, curXPath, bund);

        PrintWriter out = createPrinter(getMainPath(locale));
        xfile.write(out);
    }

    private void processCollation(ULocale locale) throws Exception
    {
        // TODO create a "collation" LDML Locale data file
        // PrintWriter out = createPrinter(getCollationPath(locale));
        // printError("processCollation is not yet implemented.");
        printError("Collation is not currently implemented.");

    }

    private void processTransforms(ULocale locale) throws Exception
    {
        // TODO create a "transforms" LDML Locale data file
        // PrintWriter out = createPrinter(getTransformsPath(locale));
        // printError("processTransforms is not yet implemented.");
        printError("Transliteration is not currently implemented.");
    }

    // ******************** Methods to write different types of tags ****************************

    // ***************************Methods for the localeDisplayNames tag and tags inside it **********
    private void addLocaleDisplayNames(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.LDN;

        addLocaleDisplayPattern(xfile, curXPath, bund);
        addLanguages(xfile, curXPath, bund);
        addScripts(xfile, curXPath, bund);
        addTerritories(xfile, curXPath, bund);
        addVariants(xfile, curXPath, bund);
        addTypes(xfile, curXPath, bund);
        addMeasurementSystemNames(xfile, curXPath, bund);
        addCodePatterns(xfile, curXPath, bund);
    }

    private void addLocaleDisplayPattern(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagPreName = LDMLConstants.LOCALE;
        final String resourceName = LDMLConstants.LOCALEDISPLAYPATTERN;
        curXPath += SEP + LDMLConstants.LOCALEDISPLAYPATTERN;

        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + translateICUtoLDMLName(resourceName), bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle varTable = (ICUResourceBundle) bund.get(resourceName);
                Enumeration<String> keys = varTable.getKeysSafe();
                Object temp;
                String curKey;
                String curValue;
                while (keys.hasMoreElements())
                {
                    temp = keys.nextElement();
                    if (temp instanceof String)
                    {
                        curKey = (String) temp;
                        if (varTable.isAlias(curKey))
                        {
                            addAlias(xfile, curXPath + SEP + translateICUtoLDMLName(resourceName),
                                varTable.getAliasPath(curKey), varTable.getResPath());
                        }
                        else
                        {
                            curValue = varTable.getString(curKey);

                            String myXPath = curXPath + SEP + innerTagPreName + firstToUpper(curKey);
                            xfile.add(myXPath, curValue);
                        }
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + resourceName + " in the ICU bundle.");
        }
    }

    /*
     * Has to do an inner loop to read the strings in, cannot use generic printer
     */
    private void addTypes(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String resourceName = "Types";
        final String outerTagName = translateICUtoLDMLName(resourceName);

        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + outerTagName, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                ICUResourceBundle table = (ICUResourceBundle) bund.get(resourceName);
                Enumeration<String> keys = table.getKeysSafe();
                Object temp;
                String curKey;
                String curType;
                String curValue = "";

                curXPath += SEP + outerTagName;
                while (keys.hasMoreElements())
                {
                    temp = keys.nextElement();
                    if (temp instanceof String)
                    {
                        curKey = (String) temp;
                        if (table.isAlias(curKey))
                        {
                            addAlias(xfile, curXPath + SEP + translateICUtoLDMLName(curKey),
                                table.getAliasPath(curKey), table.getResPath());
                        }
                        else
                        {
                            ICUResourceBundle curTable = (ICUResourceBundle) table.get(curKey);
                            Enumeration<String> types = curTable.getKeysSafe();

                            while (types.hasMoreElements())
                            {
                                temp = types.nextElement();
                                if (temp instanceof String)
                                {
                                    curType = (String) temp;
                                    if (curTable.isAlias(curType))
                                    {
                                        addAlias(xfile, curXPath + SEP + translateICUtoLDMLName(curType),
                                            curTable.getAliasPath(curType), curTable.getResPath());
                                    }
                                    else
                                    {
                                        ICUResourceBundle meep = (ICUResourceBundle) curTable.get(curType);
                                        curValue = meep.getString();

                                        String myXPath = curXPath + SEP
                                            + LDMLConstants.TYPE + addXType(curType) + addXKey(curKey);

                                        xfile.add(myXPath, curValue);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + resourceName + " in the ICU bundle.");
        }
    }

    private void addScripts(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagName = LDMLConstants.SCRIPT;
        final String resourceName = "Scripts";
        final String outerTagName = translateICUtoLDMLName(resourceName);
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    private void addVariants(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagName = LDMLConstants.VARIANT;
        final String resourceName = "Variants";
        final String outerTagName = translateICUtoLDMLName(resourceName);
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    private void addLanguages(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {

        final String innerTagName = LDMLConstants.LANGUAGE;
        final String resourceName = "Languages";
        final String outerTagName = translateICUtoLDMLName(resourceName);
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    private void addTerritories(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagName = LDMLConstants.TERRITORY;
        final String resourceName = "Countries";
        final String outerTagName = translateICUtoLDMLName(resourceName);
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    private void addMeasurementSystemNames(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagName = LDMLConstants.MSN;
        final String resourceName = LDMLConstants.MSNS;
        final String outerTagName = translateICUtoLDMLName(resourceName);
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    private void addCodePatterns(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String innerTagName = LDMLConstants.CODE_PATTERN;
        final String resourceName = LDMLConstants.CODE_PATTERNS;
        final String outerTagName = LDMLConstants.CODE_PATTERNS;
        addGenericTagSet(xfile, curXPath, bund, resourceName, outerTagName, innerTagName);
    }

    // *************************** Method for writing characters **********************

    private void addCharacters(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {

        final String outerTagName = LDMLConstants.CHARACTERS;
        final String auxType = LDMLConstants.AUXILIARY;
        final String exempCharResName = "ExemplarCharacters";
        final String innerTagName = translateICUtoLDMLName(exempCharResName);
        final String auxExempCharResName = "AuxExemplarCharacters";

        curXPath += SEP + outerTagName;

        try
        {
            if (bund.isAlias(exempCharResName))
            {
                addAlias(xfile, curXPath + SEP + innerTagName, bund.getAliasPath(exempCharResName), bund.getResPath());
            }
            else
            {
                ICUResourceBundle b = (ICUResourceBundle) bund.get(exempCharResName);
                xfile.add(curXPath + SEP + innerTagName, b.getString());
            }
        } catch (MissingResourceException e) {
        } // No problem, just doesn't have it
        try
        {
            if (bund.isAlias(auxExempCharResName))
            {
                addAlias(xfile, curXPath + SEP + innerTagName, bund.getAliasPath(auxExempCharResName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle b = (ICUResourceBundle) bund.get(auxExempCharResName);
                xfile.add(curXPath + SEP + innerTagName + addXType(auxType), b.getString());
            }
        } catch (MissingResourceException e) {
        } // No problem, just doesn't have it

    }

    /*
     * Cannot use generic printer method on write delimiters
     */
    private void addDelimiters(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String resourceName = LDMLConstants.DELIMITERS;
        final String outerTagName = LDMLConstants.DELIMITERS;
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + outerTagName, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                ICUResourceBundle table = (ICUResourceBundle) bund.get(resourceName);
                Enumeration<String> keys = table.getKeysSafe();
                Object temp;
                String curKey;
                String curValue = "";
                curXPath += SEP + outerTagName;
                while (keys.hasMoreElements())
                {
                    temp = keys.nextElement();

                    if (temp instanceof String)
                    {
                        curKey = (String) temp;
                        curValue = table.getString(curKey);
                        String myXPath = curXPath + SEP + curKey;
                        xfile.add(myXPath, curValue);
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + resourceName + " in the ICU bundle.");
        }
    }

    // ************************* Methods for Date *********************************

    private void addDates(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.DATES;
        addCalendars(xfile, curXPath, bund);
        addTimeZones(xfile, curXPath, bund);
    }

    private void addCalendars(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        // <!ELEMENT calendars (alias | (default*, calendar*, special*)) >
        final String resourceName = LDMLConstants.CALENDAR;
        Object temp = null;
        String curKey = null;
        ICUResourceBundle curBund = null;
        Enumeration<String> keys = null;

        curXPath += SEP + LDMLConstants.CALENDARS;

        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.CALENDARS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(resourceName);

                // list of all types of calendars
                keys = bund.getKeysSafe();

                // loop through all types of calendars
                while (keys.hasMoreElements())
                {
                    temp = keys.nextElement();

                    if (temp instanceof String)
                    {
                        // the name of this type of calendar
                        curKey = (String) temp;

                        if (bund.isAlias(curKey))
                        {
                            addAlias(xfile, curXPath + SEP + LDMLConstants.CALENDAR + addXType(curKey),
                                bund.getAliasPath(curKey), bund.getResPath());
                        }
                        else if (curKey.equals(LDMLConstants.DEFAULT))
                        {
                            addDefault(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                        }
                        else
                        {
                            curBund = (ICUResourceBundle) bund.get(curKey);
                            String myXPath = curXPath + SEP + LDMLConstants.CALENDAR + addXType(curKey);
                            addCalendar(xfile, myXPath, curBund);
                        }
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + resourceName + " in the ICU bundle.");
        }
    }

    private static final int ID_MONTHS = 0;
    private static final int ID_DAYS = 1;
    private static final int ID_QUARTERS = 2;

    private void addCalendar(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        // <!ELEMENT calendar (alias | (months?, monthNames?, monthAbbr?, days?,
        // dayNames?, dayAbbr?, quarters?, week?, am*, pm*, eras?, dateFormats?,
        // timeFormats?, dateTimeFormats?, fields*, special*))>

        addMDorQs(xfile, curXPath, bund, ID_MONTHS);
        addMDorQs(xfile, curXPath, bund, ID_DAYS);
        addMDorQs(xfile, curXPath, bund, ID_QUARTERS);
        addAMPM(xfile, curXPath, bund);
        addEras(xfile, curXPath, bund);
        addDateFormats(xfile, curXPath, bund);
        addTimeFormats(xfile, curXPath, bund);
        addDateTimeFormats(xfile, curXPath, bund);
        addFields(xfile, curXPath, bund);
        addSpecial(xfile, curXPath, bund);
    }

    // for adding months dates and quarters
    private void addMDorQs(CLDRFile xfile, String curXPath, ICUResourceBundle bund, int id)
    {
        String resourceName = "";
        String tagName = "";
        switch (id)
        {
        case ID_MONTHS:
            resourceName = "monthNames";
            tagName = LDMLConstants.MONTHS;
            break;
        case ID_DAYS:
            resourceName = "dayNames";
            tagName = LDMLConstants.DAYS;
            break;
        case ID_QUARTERS:
            resourceName = LDMLConstants.QUARTERS;
            tagName = LDMLConstants.QUARTERS;
            break;
        default:
            printError("Unknown ID code: " + id);
            System.exit(-1);
            break;
        }
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + tagName, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(resourceName);
                curXPath += SEP + tagName;

                Enumeration<String> keys = bund.getKeysSafe();
                String curKey = null;
                while (keys.hasMoreElements())
                {
                    curKey = (String) keys.nextElement();
                    if (curKey.equals(LDMLConstants.FORMAT) || curKey.equals(LDMLConstants.STAND_ALONE))
                    {
                        addMDorQContext(xfile, curXPath, (ICUResourceBundle) bund.get(curKey), id);
                    }
                    else if (curKey.equals(LDMLConstants.DEFAULT))
                    {
                        addDefault(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                    }
                    else
                    {
                        printError("Did not know how to handle " + resourceName + " resource: " + curKey);
                    }

                    // addSpecial(xfile, curXPath, bund);
                }
            }
        } catch (MissingResourceException e) {
        } // doesn't have data for this one
    }

    private void addMDorQContext(CLDRFile xfile, String curXPath, ICUResourceBundle bund, int id)
    {
        String tagName = "";
        String subTagName = "";
        switch (id)
        {
        case ID_MONTHS:
            tagName = LDMLConstants.MONTH_CONTEXT;
            subTagName = LDMLConstants.MONTH_WIDTH;
            break;
        case ID_DAYS:
            tagName = LDMLConstants.DAY_CONTEXT;
            subTagName = LDMLConstants.DAY_WIDTH;
            break;
        case ID_QUARTERS:
            tagName = LDMLConstants.QUARTER_CONTEXT;
            subTagName = LDMLConstants.QUARTER_WIDTH;
            break;
        default:
            printError("Unknown ID code: " + id);
            System.exit(-1);
            break;
        }
        String type = bund.getKey();
        curXPath += SEP + tagName + addXType(type);
        String curKey = null;
        Enumeration<String> keys = bund.getKeysSafe();
        while (keys.hasMoreElements())
        {
            curKey = (String) keys.nextElement();
            if (bund.isAlias(curKey))
            {
                addAlias(xfile, curXPath + SEP + subTagName + addXType(curKey), bund.getAliasPath(curKey),
                    bund.getResPath());
            }
            else if (curKey.equals(LDMLConstants.DEFAULT))
            {
                addDefault(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
            }
            else
            {
                addMDorQWidth(xfile, curXPath, (ICUResourceBundle) bund.get(curKey), id);
            }
        }
    }

    private void addMDorQWidth(CLDRFile xfile, String curXPath, ICUResourceBundle bund, int id)
    {
        String tagName = "";
        String subTagName = "";
        switch (id)
        {
        case ID_MONTHS:
            tagName = LDMLConstants.MONTH_WIDTH;
            subTagName = LDMLConstants.MONTH;
            break;
        case ID_DAYS:
            tagName = LDMLConstants.DAY_WIDTH;
            subTagName = LDMLConstants.DAY;
            break;
        case ID_QUARTERS:
            tagName = LDMLConstants.QUARTER_WIDTH;
            subTagName = LDMLConstants.QUARTER;
            break;
        default:
            printError("Unknown ID code: " + id);
            System.exit(-1);
            break;
        }

        curXPath += SEP + tagName + addXType(bund.getKey());

        String curKey;
        Enumeration<String> keys = bund.getKeysSafe();
        while (keys.hasMoreElements())
        {
            curKey = (String) keys.nextElement();
            if (bund.isAlias(curKey))
            {
                addAlias(xfile, curXPath + SEP + subTagName + addXType(plusOne(curKey)), bund.getAliasPath(curKey),
                    bund.getResPath());
            }
            else
            {
                addMDorQ(xfile, curXPath, (ICUResourceBundle) bund.get(curKey), id);
            }
        }
    }

    private void addMDorQ(CLDRFile xfile, String curXPath, ICUResourceBundle bund, int id)
    {
        String tagName = "";
        String type = "";
        switch (id)
        {
        case ID_MONTHS:
            tagName = LDMLConstants.MONTH;
            type = plusOne(bund.getKey());
            break;
        case ID_DAYS:
            tagName = LDMLConstants.DAY;
            type = getDayOfWeek(bund.getKey());
            break;
        case ID_QUARTERS:
            tagName = LDMLConstants.QUARTER;
            type = plusOne(bund.getKey());
            break;
        default:
            printError("Unknown ID code: " + id);
            System.exit(-1);
            break;
        }
        curXPath += SEP + tagName + addXType(type);
        xfile.add(curXPath, bund.getString());
    }

    private void addAMPM(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String ampmResourceName = "AmPmMarkers";
        String amXPath = curXPath + SEP + LDMLConstants.AM;
        String pmXPath = curXPath + SEP + LDMLConstants.PM;
        try
        {
            if (bund.isAlias(ampmResourceName))
            {
                addAlias(xfile, curXPath + SEP + ampmResourceName, bund.getAliasPath(ampmResourceName),
                    bund.getResPath());
                // TODO AM/PM is a single resource in ICU, alias's will not occur? Most likely duplicates the data from
                // the pointed to location
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(ampmResourceName);
                xfile.add(amXPath, bund.getString(0));
                xfile.add(pmXPath, bund.getString(1));
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addEras(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String erasResourceName = LDMLConstants.ERAS;
        try
        {
            if (bund.isAlias(erasResourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.ERAS, bund.getAliasPath(erasResourceName),
                    bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(erasResourceName);
                curXPath += SEP + LDMLConstants.ERAS;

                addErasAbbr(xfile, curXPath, bund);
                addErasNames(xfile, curXPath, bund);
                addErasNarrow(xfile, curXPath, bund);
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addErasAbbr(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        if (bund.isAlias(LDMLConstants.ABBREVIATED))
        {
            addAlias(xfile, curXPath + SEP + LDMLConstants.ERAABBR, bund.getAliasPath(LDMLConstants.ABBREVIATED),
                bund.getResPath());
        }
        else
        {
            bund = (ICUResourceBundle) bund.get(LDMLConstants.ABBREVIATED);
            curXPath += SEP + LDMLConstants.ERAABBR;
            Enumeration<String> keys = bund.getKeys();
            String curKey;
            while (keys.hasMoreElements())
            {
                curKey = (String) keys.nextElement();
                if (bund.isAlias(curKey))
                {
                    // addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                    printError("Found alias in Era Abbr.");
                }
                else
                {
                    addEra(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                }
            }
        }
    }

    private void addErasNames(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        if (bund.isAlias(LDMLConstants.WIDE))
        {
            addAlias(xfile, curXPath + SEP + LDMLConstants.ERANAMES, bund.getAliasPath(LDMLConstants.WIDE),
                bund.getResPath());
        }
        else
        {
            bund = (ICUResourceBundle) bund.get(LDMLConstants.WIDE);
            curXPath += SEP + LDMLConstants.ERANAMES;
            Enumeration<String> keys = bund.getKeys();
            String curKey;
            while (keys.hasMoreElements())
            {
                curKey = (String) keys.nextElement();
                if (bund.isAlias(curKey))
                {
                    // addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                    printError("Found alias in Era Names.");
                }
                else
                {
                    addEra(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                }
            }
        }
    }

    private void addErasNarrow(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        if (bund.isAlias(LDMLConstants.NARROW))
        {
            addAlias(xfile, curXPath + SEP + LDMLConstants.ERANARROW, bund.getAliasPath(LDMLConstants.NARROW),
                bund.getResPath());
        }
        else
        {
            bund = (ICUResourceBundle) bund.get(LDMLConstants.NARROW);
            curXPath += SEP + LDMLConstants.ERANARROW;
            Enumeration<String> keys = bund.getKeys();
            String curKey;
            while (keys.hasMoreElements())
            {
                curKey = (String) keys.nextElement();
                if (bund.isAlias(curKey))
                {
                    // addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                    printError("Found alias in narrow eras.");
                }
                else
                {
                    addEra(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                }
            }
        }
    }

    private void addEra(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.ERA + addXType(bund.getKey());
        xfile.add(curXPath, bund.getString());
    }

    private void addDateFormats(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String resourceName = "DateTimePatterns";
        String fullType, longType, mediumType, shortType;

        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.DATE_FORMATS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                curXPath += SEP + LDMLConstants.DATE_FORMATS;
                bund = (ICUResourceBundle) bund.get(resourceName);

                fullType = bund.getString(4);
                longType = bund.getString(5);
                mediumType = bund.getString(6);
                shortType = bund.getString(7);

                String fullPath = curXPath + SEP + LDMLConstants.DFL + addXType("full") + SEP
                    + LDMLConstants.DATE_FORMAT + SEP + LDMLConstants.PATTERN;
                String longPath = curXPath + SEP + LDMLConstants.DFL + addXType("long") + SEP
                    + LDMLConstants.DATE_FORMAT + SEP + LDMLConstants.PATTERN;
                String mediumPath = curXPath + SEP + LDMLConstants.DFL + addXType("medium") + SEP
                    + LDMLConstants.DATE_FORMAT + SEP + LDMLConstants.PATTERN;
                String shortPath = curXPath + SEP + LDMLConstants.DFL + addXType("short") + SEP
                    + LDMLConstants.DATE_FORMAT + SEP + LDMLConstants.PATTERN;

                xfile.add(fullPath, fullType);
                xfile.add(longPath, longType);
                xfile.add(mediumPath, mediumType);
                xfile.add(shortPath, shortType);
                // TODO Cannot find the 'default' resource in the ICUResourceBundle...doesn't exist?
                // TODO There are no alias's in Date Formats, the data is duplicated into the string array when building
                // ICU data
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addTimeFormats(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String resourceName = "DateTimePatterns";
        String fullType, longType, mediumType, shortType;
        try
        {
            bund = (ICUResourceBundle) bund.get(resourceName);

            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.TIME_FORMATS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                curXPath += SEP + LDMLConstants.TIME_FORMATS;
                fullType = bund.getString(0);
                longType = bund.getString(1);
                mediumType = bund.getString(2);
                shortType = bund.getString(3);

                String fullPath = curXPath + SEP + LDMLConstants.TFL + addXType("full") + SEP
                    + LDMLConstants.TIME_FORMAT + SEP + LDMLConstants.PATTERN;
                String longPath = curXPath + SEP + LDMLConstants.TFL + addXType("long") + SEP
                    + LDMLConstants.TIME_FORMAT + SEP + LDMLConstants.PATTERN;
                String mediumPath = curXPath + SEP + LDMLConstants.TFL + addXType("medium") + SEP
                    + LDMLConstants.TIME_FORMAT + SEP + LDMLConstants.PATTERN;
                String shortPath = curXPath + SEP + LDMLConstants.TFL + addXType("short") + SEP
                    + LDMLConstants.TIME_FORMAT + SEP + LDMLConstants.PATTERN;

                xfile.add(fullPath, fullType);
                xfile.add(longPath, longType);
                xfile.add(mediumPath, mediumType);
                xfile.add(shortPath, shortType);
                // TODO Cannot find the 'default' resource in the ICUResourceBundle...doesn't exist?
                // TODO There are no alias's in Time Formats, the data is duplicated into the string array when building
                // ICU data
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addDateTimeFormats(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        String resourceName = "DateTimePatterns";
        curXPath += SEP + LDMLConstants.DATE_TIME_FORMATS;
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                ICUResourceBundle bund1 = (ICUResourceBundle) bund.get(resourceName);
                String pattern = bund1.getString(8);
                String fullPath = curXPath + SEP + LDMLConstants.DTFL + SEP + LDMLConstants.DATE_TIME_FORMAT + SEP
                    + LDMLConstants.PATTERN;
                xfile.add(fullPath, pattern);
                // TODO Cannot find the 'default' resource in the ICUResourceBundle...doesn't exist?
                // TODO There are no alias's in Date Time Formats, the data is duplicated into the string array when
                // building ICU data
            }
        } catch (MissingResourceException e) {
        }

        // deal with Available formats
        try
        {
            resourceName = LDMLConstants.AVAIL_FMTS;

            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.AVAIL_FMTS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle bund1 = (ICUResourceBundle) bund.get(resourceName);
                String myXPath = curXPath + SEP + LDMLConstants.AVAIL_FMTS;
                Enumeration<String> keys = bund1.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    String curKey = keys.nextElement();
                    if (bund1.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + LDMLConstants.DATE_FMT_ITEM + addXID(curKey),
                            bund1.getAliasPath(curKey), bund.getResPath());
                    }
                    else
                    {
                        addDateFormatItem(xfile, myXPath, (ICUResourceBundle) bund1.get(curKey));
                    }
                }
            }
        } catch (MissingResourceException e) {
        }

        // deal with intervalFormats
        try
        {
            resourceName = LDMLConstants.INTVL_FMTS;
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.INTVL_FMTS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle bund1 = (ICUResourceBundle) bund.get(resourceName);
                String myXPath = curXPath + SEP + LDMLConstants.INTVL_FMTS;
                Enumeration<String> keys = bund1.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    String curKey = keys.nextElement();
                    if (bund1.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + LDMLConstants.INTVL_FMT_ITEM + addXID(curKey),
                            bund1.getAliasPath(curKey), bund1.getResPath());
                    }
                    else
                    {
                        addIntervalFormatItem(xfile, myXPath, (ICUResourceBundle) bund1.get(curKey));
                    }
                }
            }
        } catch (MissingResourceException e) {
        }

        // deal with appendItems
        try
        {
            resourceName = LDMLConstants.APPEND_ITEMS;
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.APPEND_ITEMS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle bund1 = (ICUResourceBundle) bund.get(resourceName);
                String myXPath = curXPath + SEP + LDMLConstants.APPEND_ITEMS;
                Enumeration<String> keys = bund1.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    String curKey = keys.nextElement();
                    if (bund1.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + LDMLConstants.APPEND_ITEM, bund1.getAliasPath(curKey),
                            bund1.getResPath());
                    }
                    else
                    {
                        String myXPath1 = myXPath + SEP + LDMLConstants.APPEND_ITEM + addXRequest(curKey);
                        xfile.add(myXPath1, bund1.getString(curKey));
                    }
                }
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addDateFormatItem(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.DATE_FMT_ITEM + addXID(bund.getKey());
        xfile.add(curXPath, bund.getString());
    }

    private void addIntervalFormatItem(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        String key = bund.getKey();
        if (key.equals(LDMLConstants.FALLBACK))
        {
            curXPath += SEP + LDMLConstants.INTVL_FMT_FALL;
            xfile.add(curXPath, bund.getString(key));
        }
        else
        {
            curXPath += SEP + LDMLConstants.INTVL_FMT_ITEM + addXID(key);
            Enumeration<String> keys = bund.getKeysSafe();
            while (keys.hasMoreElements())
            {
                String curKey = keys.nextElement();
                if (bund.isAlias(curKey))
                {
                    addAlias(xfile, curXPath + SEP + LDMLConstants.GREATEST_DIFF + addXID(curKey),
                        bund.getAliasPath(curKey), bund.getResPath());
                }
                else
                {
                    addGreatestDiff(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                }
            }
        }
    }

    private void addGreatestDiff(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.GREATEST_DIFF + addXID(bund.getKey());
        xfile.add(curXPath, bund.getString());
    }

    private void addFields(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        // <!ELEMENT fields ( alias | (field*, special*)) >
        // <!ELEMENT field ( alias | (displayName?, relative*, special*)) >
        String resourceName = LDMLConstants.FIELDS;
        curXPath += SEP + LDMLConstants.FIELDS;
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(resourceName);

                String curKey;
                Enumeration<String> keys = bund.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    curKey = keys.nextElement();
                    if (bund.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + LDMLConstants.FIELD + addXType(curKey),
                            bund.getAliasPath(curKey), bund.getResPath());
                    }
                    else
                    {
                        addField(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                    }
                }
            }
        } catch (MissingResourceException e) {
        }

    }

    private void addField(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String displayNameResourceName = "dn";
        final String relativeResourceName = LDMLConstants.RELATIVE;

        try
        {
            if (bund.isAlias(displayNameResourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.FIELD + addXType(bund.getKey()),
                    bund.getAliasPath(displayNameResourceName), bund.getResPath());
            }
            else
            {
                curXPath += SEP + LDMLConstants.FIELD + addXType(bund.getKey());
                addDisplayName(xfile, curXPath, (ICUResourceBundle) bund.get(displayNameResourceName));
            }
        } catch (MissingResourceException e) {
        }

        try
        {
            if (bund.isAlias(relativeResourceName))
            {
                addAlias(xfile, curXPath + SEP + relativeResourceName, bund.getAliasPath(relativeResourceName),
                    bund.getResPath());
            }
            else
            {
                addRelatives(xfile, curXPath, (ICUResourceBundle) bund.get(relativeResourceName));
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addDisplayName(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.DISPLAY_NAME;
        xfile.add(curXPath, bund.getString());
    }

    private void addRelatives(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        String curKey;
        Enumeration<String> keys = bund.getKeysSafe();
        while (keys.hasMoreElements())
        {
            curKey = keys.nextElement();
            if (bund.isAlias(curKey))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.RELATIVE + addXType(curKey), bund.getAliasPath(curKey),
                    bund.getResPath());
            }
            else
            {
                addRelative(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
            }
        }
    }

    private void addRelative(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.RELATIVE + addXType(bund.getKey());
        xfile.add(curXPath, bund.getString());
    }

    private void addSpecial(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        // TODO Special - This is part of the LDML spec, but does not seem to be relevant for any ICU data
    }

    private void addTimeZones(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String timeZoneResourceName = "zoneStrings";
        try
        {
            if (bund.isAlias(timeZoneResourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.TZN, bund.getAliasPath(timeZoneResourceName),
                    bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(timeZoneResourceName);
                curXPath += SEP + LDMLConstants.TZN;
                // get all keys (each one is the name of a time zone or one of a few special cases
                Enumeration<String> keys = bund.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    String curKey = keys.nextElement();

                    if (bund.isAlias(curKey))
                    {
                        // TODO this will not have the proper tag around it
                        // addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                        printError("Found a Time Zone Name alias.");
                    }
                    else if (isMeta(curKey))
                    {
                        addZone(xfile, curXPath, (ICUResourceBundle) bund.get(curKey), META_ID);
                    }
                    else if (isZoneSpecial(curKey))
                    {
                        addZoneSpecial(xfile, curXPath, (ICUResourceBundle) bund.get(curKey));
                    }
                    else
                    {
                        // it's just a regular time zone
                        addZone(xfile, curXPath, (ICUResourceBundle) bund.get(curKey), ZONE_ID);
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + timeZoneResourceName + " in the ICU bundle.");
        }
    }

    private static final int ZONE_ID = 0;
    private static final int META_ID = 1;

    private void addZone(CLDRFile xfile, String curXPath, ICUResourceBundle bund, int id)
    {
        String exemplarCityResourceName = "ec";

        String longGenericResourceName = "lg";
        String longDaylightResourceName = "ld";
        String longStandardResourceName = "ls";

        String shortDaylightResourceName = "sd";
        String shortGenericResourceName = "sg";
        String shortStandardResourceName = "ss";

        String commonlyUsedResourceName = "cu";

        switch (id)
        {
        case ZONE_ID:
            curXPath += SEP + LDMLConstants.ZONE + addXType(bund.getKey().replace(':', '/'));
            break;
        case META_ID:
            curXPath += SEP + LDMLConstants.METAZONE + addXType(bund.getKey().substring(5).replace(':', '/'));
            break;
        default:
            printError("Invalid ID: " + id);
            break;
        }
        Enumeration<String> keys = bund.getKeysSafe();

        while (keys.hasMoreElements())
        {
            String curKey = keys.nextElement();

            if (bund.isAlias(curKey))
            {
                // addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                // TODO will not produce proper outter tags
                printError("Found alias in Time Zone.");
            }
            else if (curKey.equals(exemplarCityResourceName))
            {
                String val = bund.getString(curKey);
                String myXPath = curXPath + SEP + LDMLConstants.EXEMPLAR_CITY;
                xfile.add(myXPath, val);
            }
            else if (curKey.equals(longGenericResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.LONG + SEP + LDMLConstants.GENERIC;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(longStandardResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.LONG + SEP + LDMLConstants.STANDARD;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(longDaylightResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.LONG + SEP + LDMLConstants.DAYLIGHT;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(shortGenericResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.SHORT + SEP + LDMLConstants.GENERIC;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(shortStandardResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.SHORT + SEP + LDMLConstants.STANDARD;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(shortDaylightResourceName))
            {
                String myXPath = curXPath + SEP + LDMLConstants.SHORT + SEP + LDMLConstants.DAYLIGHT;
                xfile.add(myXPath, bund.getString(curKey));
            }
            else if (curKey.equals(commonlyUsedResourceName))
            {
                String val = (bund.get(curKey).getInt() == 1) ? "true" : "false";
                String myXPath = curXPath + SEP + LDMLConstants.COMMONLY_USED;
                xfile.add(myXPath, val);
            }
            else
            {
                printError("Unrecognized element in time zone: " + curKey);
            }
        }
    }

    private void addZoneSpecial(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String hourFormatResourceName = LDMLConstants.HOUR_FORMAT;
        final String hoursFormatResourceName = LDMLConstants.HOURS_FORMAT;
        final String gmtFormatResourceName = LDMLConstants.GMT_FORMAT;
        final String regionFormatResourceName = LDMLConstants.REGION_FORMAT;
        final String fallbackFormatResourceName = LDMLConstants.FALLBACK_FORMAT;

        String key = bund.getKey();

        if (key.equals(LDMLConstants.PREFERENCE_ORDERING))
        {
            /*
             * preferenceOrdering{
             * "",
             * }
             * <preferenceOrdering choice=""/>
             */
            String[] val = bund.getStringArray();
            String list = "";
            for (int x = 0; x < val.length; x++)
            {
                list += val[x] + " ";
            }
            list = list.substring(0, list.length() - 1);
            curXPath += SEP + LDMLConstants.PREFERENCE_ORDERING + addXChoice(list);
            xfile.add(curXPath, "");
        }
        else if (key.equals(LDMLConstants.SINGLE_COUNTRIES))
        {
            /*
             * <singleCountries list=
             * "Europe/Kiev Pacific/Majuro Africa/Bamako America/Godthab America/Santiago America/Guayaquil Asia/Shanghai Asia/Tashkent Asia/Kuala_Lumpur Europe/Madrid Europe/Lisbon Europe/London Pacific/Auckland Pacific/Tahiti"
             * />
             * singleCountries{
             * "Europe/Kiev",
             * "Pacific/Majuro",
             * "Africa/Bamako",
             * "America/Godthab",
             * "America/Santiago",
             * "America/Guayaquil",
             * "Asia/Shanghai",
             * "Asia/Tashkent",
             * "Asia/Kuala_Lumpur",
             * "Europe/Madrid",
             * "Europe/Lisbon",
             * "Europe/London",
             * "Pacific/Auckland",
             * "Pacific/Tahiti",
             * }
             */
            String[] val = bund.getStringArray();
            String list = "";
            for (int x = 0; x < val.length; x++)
            {
                list += val[x] + " ";
            }
            list = list.substring(0, list.length() - 1);
            curXPath += SEP + LDMLConstants.SINGLE_COUNTRIES + addXList(list);
            xfile.add(curXPath, "");
        }
        else if (key.equals(hourFormatResourceName))
        {
            String val = bund.getString(key);
            curXPath += SEP + LDMLConstants.HOUR_FORMAT;
            xfile.add(curXPath, val);
        }
        else if (key.equals(hoursFormatResourceName))
        {
            String val = bund.getString(key);
            curXPath += SEP + LDMLConstants.HOURS_FORMAT;
            xfile.add(curXPath, val);
        }
        else if (key.equals(gmtFormatResourceName))
        {
            String val = bund.getString(key);
            curXPath += SEP + LDMLConstants.GMT_FORMAT;
            xfile.add(curXPath, val);
        }
        else if (key.equals(regionFormatResourceName))
        {
            String val = bund.getString(key);
            curXPath += SEP + LDMLConstants.REGION_FORMAT;
            xfile.add(curXPath, val);
        }
        else if (key.equals(fallbackFormatResourceName))
        {
            String val = bund.getString(key);
            curXPath += SEP + LDMLConstants.FALLBACK_FORMAT;
            xfile.add(curXPath, val);
        }
        else
        {
            printError("Did not recognize Time Zone special tag: " + key);
        }
    }

    private void addNumbers(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.NUMBERS;
        addSymbols(xfile, curXPath, bund);
        addNumberFormats(xfile, curXPath, bund);
        addCurrencies(xfile, curXPath, bund);
    }

    private void addSymbols(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String resourceName = "NumberElements";

        final int decimalIndex = 0;
        final int groupIndex = 1;
        final int listIndex = 2;
        final int percentSignIndex = 3;
        final int nativeZeroDigitIndex = 4;
        final int patternDigitIndex = 5;
        final int minusSignIndex = 6;
        final int exponentialIndex = 7;
        final int perMileIndex = 8;
        final int infinityIndex = 9;
        final int nanIndex = 10;
        final int plusSignIndex = 11;
        final int lengthIndex = 12;

        String[] vals;
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.SYMBOLS, bund.getAliasPath(resourceName),
                    bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(resourceName);
                curXPath += SEP + LDMLConstants.SYMBOLS;
                vals = bund.getStringArray();
                if (vals.length != lengthIndex)
                {
                    printError("Length of Symbols array was not correct. Length was: " + vals.length
                        + ", should have been: " + lengthIndex);
                }

                xfile.add(curXPath + SEP + LDMLConstants.DECIMAL, vals[decimalIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.GROUP, vals[groupIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.LIST, vals[listIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.PERCENT_SIGN, vals[percentSignIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.NATIVE_ZERO_SIGN, vals[nativeZeroDigitIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.PATTERN_DIGIT, vals[patternDigitIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.MINUS_SIGN, vals[minusSignIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.EXPONENTIAL, vals[exponentialIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.PER_MILLE, vals[perMileIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.INFINITY, vals[infinityIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.NAN, vals[nanIndex]);
                xfile.add(curXPath + SEP + LDMLConstants.PLUS_SIGN, vals[plusSignIndex]);
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addNumberFormats(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String patternResourceName = "NumberPatterns";
        final String currencyUnitPatternResourceName = "CurrencyUnitPatterns";

        final int decimalIndex = 0;
        final int currencyIndex = 1;
        final int percentIndex = 2;
        final int scientificIndex = 3;
        final int lengthIndex = 4;

        try
        {
            if (bund.isAlias(patternResourceName))
            {
                addAlias(xfile, curXPath + SEP + patternResourceName, bund.getAliasPath(patternResourceName),
                    bund.getResPath());
            }
            else
            {
                ICUResourceBundle patternRes = (ICUResourceBundle) bund.get(patternResourceName);
                ICUResourceBundle curUnitPatternRes = null;
                try
                {
                    if (bund.isAlias(currencyUnitPatternResourceName))
                    {
                        printWarning(currencyUnitPatternResourceName + " was an Alias, unsure how to handle.");
                    }
                    else
                    {
                        curUnitPatternRes = (ICUResourceBundle) bund.get(currencyUnitPatternResourceName);
                    }
                } catch (MissingResourceException e)
                {
                    printInfo("Did not find resource " + currencyUnitPatternResourceName + " in the ICU Bundle.");
                }
                String[] patterns = patternRes.getStringArray();

                if (patterns.length != lengthIndex)
                {
                    printError(patternResourceName + " did not contain enough patterns. Expected: " + lengthIndex
                        + ". Got: " + patterns.length);
                }
                else
                {
                    addDecimalFormats(xfile, curXPath, patterns[decimalIndex]);
                    addScientificFormats(xfile, curXPath, patterns[scientificIndex]);
                    addPercentFormats(xfile, curXPath, patterns[percentIndex]);
                    addCurrencyFormats(xfile, curXPath, patterns[currencyIndex], curUnitPatternRes);
                }
            }
        } catch (Exception e) {
        }
    }

    private void addDecimalFormats(CLDRFile xfile, String curXPath, String val)
    {
        curXPath += SEP + LDMLConstants.DECIMAL_FORMATS;
        curXPath += SEP + LDMLConstants.DECFL;
        curXPath += SEP + LDMLConstants.DECIMAL_FORMAT;
        curXPath += SEP + LDMLConstants.PATTERN;
        xfile.add(curXPath, val);
    }

    private void addScientificFormats(CLDRFile xfile, String curXPath, String val)
    {
        curXPath += SEP + LDMLConstants.SCIENTIFIC_FORMATS;
        curXPath += SEP + LDMLConstants.SCIFL;
        curXPath += SEP + LDMLConstants.SCIENTIFIC_FORMAT;
        curXPath += SEP + LDMLConstants.PATTERN;
        xfile.add(curXPath, val);
    }

    private void addPercentFormats(CLDRFile xfile, String curXPath, String val)
    {
        curXPath += SEP + LDMLConstants.PERCENT_FORMATS;
        curXPath += SEP + LDMLConstants.PERFL;
        curXPath += SEP + LDMLConstants.PERCENT_FORMAT;
        curXPath += SEP + LDMLConstants.PATTERN;
        xfile.add(curXPath, val);
    }

    private void addCurrencyFormats(CLDRFile xfile, String curXPath, String val, ICUResourceBundle bund)
    {
        Enumeration<String> keys;

        curXPath += SEP + LDMLConstants.CURRENCY_FORMATS;

        String myXPath = curXPath + SEP + LDMLConstants.CURFL;
        myXPath += SEP + LDMLConstants.CURRENCY_FORMAT;
        myXPath += SEP + LDMLConstants.PATTERN;
        xfile.add(myXPath, val);

        if (bund != null)
        {
            keys = bund.getKeysSafe();
            while (keys.hasMoreElements())
            {
                String curKey = keys.nextElement();
                if (bund.isAlias(curKey))
                {
                    addAlias(xfile, curXPath + SEP + LDMLConstants.UNIT_PATTERN + addXCount(curKey),
                        bund.getAliasPath(curKey), bund.getResPath());
                }
                else
                {
                    String curVal = (String) bund.getString(curKey);
                    myXPath = curXPath + SEP + LDMLConstants.UNIT_PATTERN + addXCount(curKey);
                    xfile.add(myXPath, curVal);
                }
            }
        }
    }

    private void addCurrencies(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String currenciesResourceName = "Currencies";
        final String currenciesPluralResourceName = "CurrencyPlurals";

        curXPath += SEP + LDMLConstants.CURRENCIES;

        try
        {
            ICUResourceBundle pluralBund = null;
            try
            {
                if (bund.isAlias(currenciesPluralResourceName))
                {
                    // addAlias(xfile, curXPath + SEP + currenciesPluralResourceName,
                    // bund.getAliasPath(currenciesPluralResourceName), bund.getResPath());
                    // TODO ??? do nothing? ICU seperates our plurals and singles, LDML does not
                }
                else
                {
                    pluralBund = (ICUResourceBundle) bund.get(currenciesPluralResourceName);
                }
            } catch (MissingResourceException e)
            {
                printInfo("No plural currency resource found.");
            }
            if (bund.isAlias(currenciesResourceName))
            {
                addAlias(xfile, curXPath, bund.getAliasPath(currenciesResourceName), bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(currenciesResourceName);

                Enumeration<String> keys = bund.getKeysSafe();

                while (keys.hasMoreElements())
                {
                    String curKey = keys.nextElement();
                    if (bund.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + curKey, bund.getAliasPath(curKey), bund.getResPath());
                    }
                    else
                    {
                        ICUResourceBundle curSingle = (ICUResourceBundle) bund.get(curKey);
                        ICUResourceBundle curPlural = null;
                        if (pluralBund != null)
                        {
                            try
                            {
                                curPlural = (ICUResourceBundle) pluralBund.get(curKey);
                            } catch (Exception e) {
                            }
                        }
                        addCurrency(xfile, curXPath, curKey, curSingle, curPlural);
                    }
                }
            }
        } catch (MissingResourceException e) {
        }
    }

    private void addCurrency(CLDRFile xfile, String curXPath, String key, ICUResourceBundle symbBund,
        ICUResourceBundle pluralBund)
    {
        String[] symbArray = null;

        try
        {
            symbArray = symbBund.getStringArray();
        } catch (UResourceTypeMismatchException e)
        {
            List<String> strings = new ArrayList<String>();
            Enumeration<String> keys = symbBund.getKeysSafe();
            while (keys.hasMoreElements())
            {
                String curKey = keys.nextElement();
                try
                {
                    String val = symbBund.getString(curKey);
                    strings.add(val);
                } catch (Exception z) {
                }
            }
            symbArray = new String[strings.size()];
            for (int x = 0; x < strings.size(); x++)
            {
                symbArray[x] = (String) strings.get(x);
            }
        }

        if (symbArray.length != 2)
        {
            printError("Currency array for " + key + " is of the wrong length: " + symbArray.length);
        }

        String[] keys = null;
        String[] values = null;

        try
        {
            if (pluralBund != null)
            {
                Enumeration<String> ekeys = pluralBund.getKeysSafe();
                List<String> keysArray = new ArrayList<String>();
                List<String> valuesArray = new ArrayList<String>();

                while (ekeys.hasMoreElements())
                {
                    String curKey = ekeys.nextElement();
                    if (pluralBund.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath, pluralBund.getAliasPath(curKey), pluralBund.getResPath());
                    }
                    else
                    {
                        String curVal = pluralBund.getString(curKey);
                        keysArray.add(curKey);
                        valuesArray.add(curVal);
                    }
                }
                Object[] temp = keysArray.toArray();
                keys = new String[temp.length];
                for (int x = 0; x < temp.length; x++)
                {
                    keys[x] = (String) temp[x];
                }

                temp = valuesArray.toArray();
                values = new String[temp.length];
                for (int x = 0; x < temp.length; x++)
                {
                    values[x] = (String) temp[x];
                }
            }
        } catch (Exception e) {
        }

        String myXPath = curXPath + SEP + LDMLConstants.CURRENCY + addXType(key);
        String dnXPath = myXPath + SEP + LDMLConstants.DISPLAY_NAME;
        String symbXPath = myXPath + SEP + LDMLConstants.SYMBOL;

        xfile.add(dnXPath, symbArray[1]);

        if (keys != null)
        {
            for (int x = 0; x < keys.length; x++)
            {
                xfile.add(dnXPath + addXCount(keys[x]), values[x]);
            }
        }

        if (!symbArray[0].equals(key))
        {
            xfile.add(symbXPath, symbArray[0]);
        }
    }

    private void addUnits(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        final String unitResourceName = LDMLConstants.UNITS;

        curXPath += SEP + LDMLConstants.UNITS;

        try
        {
            if (bund.isAlias(unitResourceName))
            {
                addAlias(xfile, curXPath, bund.getAliasPath(unitResourceName), bund.getResPath());
            }
            else
            {
                bund = (ICUResourceBundle) bund.get(unitResourceName);
                Enumeration<String> keys = bund.getKeysSafe();
                while (keys.hasMoreElements())
                {
                    String curKey = (String) keys.nextElement();

                    if (bund.isAlias(curKey))
                    {
                        addAlias(xfile, curXPath + SEP + LDMLConstants.UNIT + addXType(curKey),
                            bund.getAliasPath(curKey), bund.getResPath());
                    }
                    else
                    {
                        ICUResourceBundle t = (ICUResourceBundle) bund.get(curKey);
                        addUnit(xfile, curXPath, t);
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    private void addUnit(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.UNIT + addXType(bund.getKey());

        Enumeration<String> keys = bund.getKeysSafe();
        while (keys.hasMoreElements())
        {
            String curKey = keys.nextElement();

            if (bund.isAlias(curKey))
            {
                addAlias(xfile, curXPath + SEP + LDMLConstants.UNIT_PATTERN + addXCount(curKey),
                    bund.getAliasPath(curKey), bund.getResPath());
            }
            else
            {
                String curVal = bund.getString(curKey);
                String myXPath = curXPath + SEP + LDMLConstants.UNIT_PATTERN + addXCount(curKey);
                xfile.add(myXPath, curVal);
            }
        }
    }

    private void addLayout(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        // TODO cannot find data in resource bundle...
    }

    private void addDefault(CLDRFile xfile, String curXPath, ICUResourceBundle bund)
    {
        curXPath += SEP + LDMLConstants.DEFAULT + addXChoice(bund.getString());
        xfile.add(curXPath, "");
    }

    private void addAlias(CLDRFile xfile, String curXPath, String path, String curPath)
    {
        // <alias source="locale" path="../../calendar[@type='islamic']/eras"/>
        // eras:alias{"/LOCALE/calendar/islamic/eras"}

        int start = path.indexOf(SEP) + 1;
        if (start > 0)
        {
            int end = path.indexOf(SEP, start);

            String source = path.substring(start, end).toLowerCase();
            path = path.substring(end + 1);

            char[] cArray = new char[1];
            cArray[0] = SEP;
            String pathSep = new String(cArray);

            StringTokenizer stk = new StringTokenizer(curPath, pathSep);
            String relativePath = "";

            while (stk.hasMoreTokens())
            {
                stk.nextToken();
                relativePath += ".." + SEP;
            }

            boolean isType = false;
            stk = new StringTokenizer(path, pathSep);

            while (stk.hasMoreTokens())
            {
                String temp = stk.nextToken();

                if (isType)
                {
                    relativePath += addXType(translateICUtoLDMLName(temp)) + SEP;
                    isType = false;
                }
                else if (usesType(temp))
                {
                    relativePath += translateICUtoLDMLName(temp);
                    isType = true;
                }
                else
                {
                    relativePath += translateICUtoLDMLName(temp) + SEP;
                }
            }

            relativePath = cleanRelativePath(relativePath);

            if (relativePath.charAt(relativePath.length() - 1) == SEP)
            {
                relativePath = relativePath.substring(0, relativePath.length() - 1);
            }
            relativePath = reducePath(relativePath, curXPath);
            relativePath = relativePath.replace('"', '\'');

            curXPath += SEP + LDMLConstants.ALIAS + addXSource(source) + addXPath(relativePath);
        }
        else
        {
            curXPath += SEP + LDMLConstants.ALIAS + addXSource(path);
        }

        xfile.add(curXPath, "");

    }

    private String reducePath(String relative, String cur)
    {
        String temp = "";
        String[] r = relative.split("" + SEP);
        String[] c = cur.split("" + SEP);
        int dotCnt = 0;

        while (r[0].equals(".."))
        {
            // remove the first element
            String[] t = new String[r.length - 1];
            for (int x = 0; x < t.length; x++)
            {
                t[x] = r[x + 1];
            }
            r = t;
            // count the dots
            dotCnt++;
        }

        while (arrayContains(c, r[0]))
        {
            // remove the first element
            String[] t = new String[r.length - 1];
            for (int x = 0; x < t.length; x++)
            {
                t[x] = r[x + 1];
            }
            r = t;

            // remove a dot
            dotCnt--;
        }
        for (int x = 0; x < dotCnt; x++)
        {
            temp += ".." + SEP;
        }
        for (int x = 0; x < r.length; x++)
        {
            temp += r[x] + SEP;
        }
        temp = temp.substring(0, temp.length() - 1);
        return temp;
    }

    private boolean arrayContains(String[] a, String s)
    {
        for (int x = 0; x < a.length; x++)
        {
            if (a[x].equals(s))
            {
                return true;
            }
        }
        return false;
    }

    // *********************** Generic tag writer ***************************

    /*
     * This method is a generic way of looking up a resource and printing it.
     * This method can only be used if the resourceName given corresponds to a resource Table,
     * and that table only contains string elements.
     */
    private void addGenericTagSet(CLDRFile xfile, String curXPath, ICUResourceBundle bund, String resourceName,
        String outerTagName, String innerTagName)
    {
        try
        {
            if (bund.isAlias(resourceName))
            {
                addAlias(xfile, curXPath + SEP + outerTagName, bund.getAliasPath(resourceName), bund.getResPath());
            }
            else
            {
                ICUResourceBundle table = (ICUResourceBundle) bund.get(resourceName);
                Enumeration<String> keys = table.getKeysSafe();
                Object temp;
                String curKey;
                String curValue = "";

                curXPath += SEP + outerTagName + SEP + innerTagName;

                while (keys.hasMoreElements())
                {
                    temp = keys.nextElement();

                    if (temp instanceof String)
                    {
                        curKey = (String) temp;

                        if (table.isAlias(curKey))
                        {
                            addAlias(xfile, curXPath, table.getAliasPath(curKey), table.getResPath());
                        }
                        else
                        {
                            curValue = table.getString(curKey);
                            String myXPath = curXPath + addXType(curKey);
                            xfile.add(myXPath, curValue);
                        }
                    }
                }
            }
        } catch (MissingResourceException e)
        {
            printInfo("Did not find " + resourceName + " in the ICU bundle.");
        }
    }

    // ***************************** Random helper methods **********************

    private String getDayOfWeek(String num)
    {
        switch ((new Integer(num)))
        {
        case 0:
            return "sun";
        case 1:
            return "mon";
        case 2:
            return "tue";
        case 3:
            return "wed";
        case 4:
            return "thu";
        case 5:
            return "fri";
        case 6:
            return "sat";
        default:
            printError("Day resource not properly formatted.");
            System.exit(-1);
        }
        return "";
    }

    private String plusOne(String num)
    {
        return new Integer((new Integer(num) + 1)).toString();
    }

    private String addXKey(String key)
    {
        return "[@" + LDMLConstants.KEY + "=\"" + key + "\"]";
    }

    private String addXType(String type)
    {
        return "[@" + LDMLConstants.TYPE + "=\"" + type + "\"]";
    }

    private String addXID(String id)
    {
        return "[@" + LDMLConstants.ID + "=\"" + id + "\"]";
    }

    private String addXCount(String cnt)
    {
        return "[@" + LDMLConstants.COUNT + "=\"" + cnt + "\"]";
    }

    private String addXSource(String src)
    {
        return "[@" + LDMLConstants.SOURCE + "=\"" + src + "\"]";
    }

    private String addXPath(String path)
    {
        return "[@" + LDMLConstants.PATH + "=\"" + path + "\"]";
    }

    private String addXChoice(String c)
    {
        return "[@" + LDMLConstants.CHOICE + "=\"" + c + "\"]";
    }

    private String addXRequest(String req)
    {
        return "[@" + LDMLConstants.REQUEST + "=\"" + req + "\"]";
    }

    private String addXList(String list)
    {
        return "[@" + LDMLConstants.LIST + "=\"" + list + "\"]";
    }

    private String firstToUpper(String str)
    {
        return str.substring(0, 1).toUpperCase() + str.substring(1, str.length()).toLowerCase();
    }

    private boolean isMeta(String key)
    {
        try
        {
            String meta = key.substring(0, 5);
            if (meta.equals("meta:"))
            {
                return true;
            }
            return false;
        } catch (IndexOutOfBoundsException e)
        {
            return false;
        }
    }

    private boolean isZoneSpecial(String key)
    {
        if (key.equals(LDMLConstants.FALLBACK_FORMAT)
            || key.equals(LDMLConstants.GMT_FORMAT)
            || key.equals(LDMLConstants.HOUR_FORMAT)
            || key.equals(LDMLConstants.REGION_FORMAT)
            || key.equals(LDMLConstants.HOURS_FORMAT)
            || key.equals(LDMLConstants.PREFERENCE_ORDERING)
            || key.equals(LDMLConstants.SINGLE_COUNTRIES))
        {
            return true;
        }
        return false;
    }

    /**
     * This method will take in the ICU resource name and if there is an equivalent LDML tag name for it,
     * that name is returned.
     * 
     * @param icuResourceName
     * @return
     */
    private String translateICUtoLDMLName(String icuResourceName)
    {
        if (theHash == null)
        {
            populateTranslationHash();
        }

        String temp = theHash.get(icuResourceName);
        if (temp == null)
        {
            return icuResourceName;
        }
        return temp;
    }

    private void populateTranslationHash()
    {
        theHash = new Hashtable<String, String>();

        theHash.put("dayNames", LDMLConstants.DAYS);
        theHash.put("monthNames", LDMLConstants.MONTHS);
        theHash.put("Countries", LDMLConstants.TERRITORIES);
        theHash.put("Languages", LDMLConstants.LANGUAGES);
        theHash.put("Currencies", LDMLConstants.CURRENCIES);
        theHash.put("Variants", LDMLConstants.VARIANTS);
        theHash.put("Scripts", LDMLConstants.SCRIPTS);
        theHash.put("Keys", LDMLConstants.KEYS);
        theHash.put("Types", LDMLConstants.TYPES);
        theHash.put("Version", LDMLConstants.VERSION);
        theHash.put("ExemplarCharacters", LDMLConstants.EXEMPLAR_CHARACTERS);
        theHash.put("zoneStrings", LDMLConstants.TZN);
        theHash.put("localPatternChars", LDMLConstants.LPC);
        theHash.put("PaperSize", LDMLConstants.PAPER_SIZE);
        theHash.put("dn", LDMLConstants.DISPLAY_NAME);
        theHash.put("fallback", LDMLConstants.INTVL_FMT_FALL);
        theHash.put("ec", LDMLConstants.EXEMPLAR_CITY);
        theHash.put("cu", LDMLConstants.COMMONLY_USED);
        theHash.put("unitPattern", LDMLConstants.UNIT);
        theHash.put("eras/abbreviated", LDMLConstants.ERAS + SEP + LDMLConstants.ERAABBR);
        theHash.put("eras/wide", LDMLConstants.ERAS + SEP + LDMLConstants.ERANAMES);
        theHash.put("eras/narrow", LDMLConstants.ERAS + SEP + LDMLConstants.ERANARROW);

        // ***************MONTHS***************
        // months/format => months/monthContext[@type="format"]
        theHash.put(LDMLConstants.MONTHS + SEP + LDMLConstants.FORMAT, LDMLConstants.MONTHS + SEP
            + LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT));

        // months/stand-alone => months/monthContext[@type="stand-alone"]
        theHash.put(LDMLConstants.MONTHS + SEP + LDMLConstants.STAND_ALONE, LDMLConstants.MONTHS + SEP
            + LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE));

        // monthContext[@type="format"]/wide => monthContext[@type="format"]/monthWidth[@type="wide"]
        theHash.put(LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.WIDE,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.WIDE));

        // monthContext[@type="format"]/narrow => monthContext[@type="format"]/monthWidth[@type="narrow"]
        theHash.put(LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.NARROW,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.NARROW));

        // monthContext[@type="format"]/abbreviated => monthContext[@type="format"]/monthWidth[@type="abbreviated"]
        theHash.put(LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.ABBREVIATED,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

        // monthContext[@type="stand-alone"]/wide => monthContext[@type="stand-alone"]/monthWidth[@type="wide"]
        theHash.put(LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.WIDE,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.WIDE));

        // monthContext[@type="stand-alone"]/narrow => monthContext[@type="stand-alone"]/monthWidth[@type="narrow"]
        theHash.put(LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.NARROW,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.NARROW));

        // monthContext[@type="stand-alone"]/abbreviated =>
        // monthContext[@type="stand-alone"]/monthWidth[@type="abbreviated"]
        theHash.put(
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.ABBREVIATED,
            LDMLConstants.MONTH_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.MONTH_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

        // ***************DAYS*************

        // days/format => days/dayContext[@type="format"]
        theHash.put(LDMLConstants.DAYS + SEP + LDMLConstants.FORMAT, LDMLConstants.DAYS + SEP
            + LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT));

        // days/stand-alone => days/dayContext[@type="stand-alone"]
        theHash.put(LDMLConstants.DAYS + SEP + LDMLConstants.STAND_ALONE, LDMLConstants.DAYS + SEP
            + LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE));

        // dayContext[@type="format"]/wide => dayContext[@type="format"]/dayWidth[@type="wide"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.WIDE,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.WIDE));

        // dayContext[@type="format"]/narrow => dayContext[@type="format"]/dayWidth[@type="narrow"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.NARROW,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.NARROW));

        // dayContext[@type="format"]/abbreviated => dayContext[@type="format"]/dayWidth[@type="abbreviated"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.ABBREVIATED,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

        // dayContext[@type="stand-alone"]/wide => dayContext[@type="stand-alone"]/dayWidth[@type="wide"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.WIDE,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.WIDE));

        // dayContext[@type="stand-alone"]/narrow => dayContext[@type="stand-alone"]/dayWidth[@type="narrow"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.NARROW,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.NARROW));

        // dayContext[@type="stand-alone"]/abbreviated => dayContext[@type="stand-alone"]/dayWidth[@type="abbreviated"]
        theHash.put(LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.ABBREVIATED,
            LDMLConstants.DAY_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.DAY_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

        // ***************QUARTERS*************
        // quarters/format => quarters/quarterContext[@type="format"]
        theHash.put(LDMLConstants.QUARTERS + SEP + LDMLConstants.FORMAT, LDMLConstants.QUARTERS + SEP
            + LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT));

        // quarters/stand-alone => quarters/quarterContext[@type="stand-alone"]
        theHash.put(LDMLConstants.QUARTERS + SEP + LDMLConstants.STAND_ALONE, LDMLConstants.QUARTERS + SEP
            + LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE));

        // quarterContext[@type="format"]/wide => quarterContext[@type="format"]/quarterWidth[@type="wide"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.WIDE,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.WIDE));

        // quarterContext[@type="format"]/narrow => quarterContext[@type="format"]/quarterWidth[@type="narrow"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.NARROW,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.NARROW));

        // quarterContext[@type="format"]/abbreviated =>
        // quarterContext[@type="format"]/quarterWidth[@type="abbreviated"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.ABBREVIATED,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.FORMAT) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

        // quarterContext[@type="stand-alone"]/wide => quarterContext[@type="stand-alone"]/quarterWidth[@type="wide"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.WIDE,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.WIDE));

        // quarterContext[@type="stand-alone"]/narrow =>
        // quarterContext[@type="stand-alone"]/quarterWidth[@type="narrow"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.NARROW,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.NARROW));

        // quarterContext[@type="stand-alone"]/abbreviated =>
        // quarterContext[@type="stand-alone"]/quarterWidth[@type="abbreviated"]
        theHash.put(LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP
            + LDMLConstants.ABBREVIATED,
            LDMLConstants.QUARTER_CONTEXT + addXType(LDMLConstants.STAND_ALONE) + SEP + LDMLConstants.QUARTER_WIDTH
                + addXType(LDMLConstants.ABBREVIATED));

    }

    private boolean usesType(String tag)
    {
        return (tag.equals(LDMLConstants.CALENDAR)
            || tag.equals(LDMLConstants.MONTH_CONTEXT)
            || tag.equals(LDMLConstants.MONTH_WIDTH)
            || tag.equals(LDMLConstants.CALENDAR)
            || tag.equals(LDMLConstants.CALENDAR)
            || tag.equals(LDMLConstants.CALENDAR)
            || tag.equals(LDMLConstants.CALENDAR));
    }

    private String cleanRelativePath(String p)
    {
        char[] cArray = new char[1];
        cArray[0] = SEP;
        String pathSep = new String(cArray);

        StringTokenizer tok = new StringTokenizer(p, pathSep);
        String returnString = p;
        if (tok.countTokens() > 1)
        {
            List<String> l = new ArrayList<String>();

            while (tok.hasMoreTokens())
            {
                l.add(tok.nextToken());
            }

            int x = 0;
            int z = 1;
            while (z < l.size())
            {
                StringTokenizer t = new StringTokenizer(translateICUtoLDMLName(l.get(x) + SEP
                    + l.get(z)), pathSep);
                l.set(x, t.nextToken());
                l.set(z, t.nextToken());
                z++;
                x++;
            }
            Object[] o = l.toArray();
            returnString = (String) o[0];
            for (x = 1; x < o.length; x++)
            {
                returnString += SEP + (String) o[x];
            }
        }

        return returnString;
    }

    // ******************** Methods for File path constructions **************/

    private String getMainPath(ULocale locale)
    {
        return createFullPath(MAIN_FOLDER, locale.getName() + LDML_FILE_EXTENSION);
    }

    private String getCollationPath(ULocale locale)
    {
        return createFullPath(COLLATION_FOLDER, locale.getName() + LDML_FILE_EXTENSION);
    }

    private String getTransformsPath(ULocale locale)
    {
        return createFullPath(TRANSFORMS_FOLDER, locale.getName() + LDML_FILE_EXTENSION);
    }

    private String createFullPath(String folderName, String fileName)
    {
        String fullName = outDir;

        // if the last character is not a slash in the output directory
        if (!fullName.substring(fullName.length() - 1).equals(FILE_SEPERATOR))
        {
            fullName += "/";
        }
        fullName += folderName;

        File f = new File(fullName);
        if (!f.exists())
        {
            f.mkdir();
        }

        if (!fullName.substring(fullName.length() - 1).equals(FILE_SEPERATOR))
        {
            fullName += "/";
        }
        fullName += fileName;

        return fullName;
    }

    private void usage()
    {
        System.out
            .println("\nUsage: ICU2LDMLWriter [OPTIONS] -d OUTPUTFOLDER [FILES]\n"
                +
                "This program generates LDML documents which represent the data currently inside of your ICU installation\n"
                +
                "If no files are given, a file for each supported locale will be produced.\n"
                +
                "If no destination folder is given, the file structure will be created in the current working directory.\n"
                +
                "Options:\n" +
                "-m or --main			Generate the main locale files.\n" +
                "-t or --trans			Generate the transliteration locale files.\n" +
                "-c or --coll			Generate the collation files.\n" +
                "If none of the above options are given, all three types of files will be generated.\n");
        System.exit(-1);
    }

    private PrintWriter createPrinter(String fullPath) throws Exception
    {
        FileOutputStream fos = new FileOutputStream(fullPath);
        BufferedOutputStream bow = new BufferedOutputStream(fos);
        OutputStreamWriter osw = new OutputStreamWriter(bow, "UTF-8");
        PrintWriter pw = new PrintWriter(osw, true);
        return pw;
    }

    // **********************Printing Methods *********************************

    private void printInfo(String message)
    {
        if (isVerbose())
        {
            System.out.println("INFO : " + currentLocaleName + ": " + message);
        }
    }

    private void printWarning(String message)
    {
        System.err.println("WARNING : " + currentLocaleName + ": " + message);
    }

    private void printError(String message)
    {
        System.err.println("ERROR : " + currentLocaleName + ": " + message);
    }

    private boolean isVerbose()
    {
        return verbose;
    }
}
