/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_i18npool.hxx"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <set>

#include <rtl/ustrbuf.hxx>

#include "LocaleNode.hxx"
#include <com/sun/star/i18n/NumberFormatIndex.hpp>

// NOTE: MUST match the Locale versionDTD attribute defined in data/locale.dtd
#define LOCALE_VERSION_DTD "2.0.3"

typedef ::std::set< ::rtl::OUString > NameSet;
typedef ::std::set< sal_Int16 > ValueSet;

namespace cssi = ::com::sun::star::i18n;

LocaleNode::LocaleNode (const OUString& name, const Reference< XAttributeList > & attr)
    : aName(name)
    , xAttribs(new Attr(attr))
    , parent(0)
    , children(0)
    , nChildren(0)
    , childArrSize(0)
    , nError(0)
{
}

int LocaleNode::getError() const
{
    int err = nError;
    for (sal_Int32 i=0;i<nChildren;i++)
        err += children[i]->getError();
    return err;
}

void LocaleNode::print () const {
	printf ("<");
	::rtl::OUString str (aName);
	for(sal_Int32 i = 0; i < str.getLength(); i++)
		printf( "%c", str[i]);
	printf (">\n");
}

void LocaleNode::printR () const {
	print();
	for (sal_Int32 i=0;i<nChildren;i++)
		children[i]->printR();
	printf ("\t");
	print();
}

void LocaleNode::addChild ( LocaleNode * node) {
	if (childArrSize <= nChildren) {
		LocaleNode ** arrN = (LocaleNode **)malloc( sizeof (LocaleNode *)*(childArrSize+10) ) ;
		for (sal_Int32 i = 0; i<childArrSize ; i++)
			arrN[i] = children[i];
		if ( childArrSize > 0 )
			free(children);
		childArrSize += 10;
		children = arrN;
	}
	children[nChildren++] = node;
	node->setParent (this);
}

void LocaleNode::setParent ( LocaleNode * node) {
	parent = node;
}

const LocaleNode* LocaleNode::getRoot() const
{
    const LocaleNode* pRoot = 0;
    const LocaleNode* pParent = this;
    while ( (pParent = pParent->getParent()) != 0 )
        pRoot = pParent;
    return pRoot;
}

const LocaleNode * LocaleNode::findNode ( const sal_Char *name) const {
	if (aName.equalsAscii(name))
		return this;
	for (sal_Int32 i = 0; i< nChildren; i++)  {
		const LocaleNode *n=children[i]->findNode(name);
		if (n)
			return n;
		}
	return 0;
}

 LocaleNode::~LocaleNode() {
	for (sal_Int32 i=0; i<nChildren;i++)
		delete (children[i]);
}

LocaleNode* LocaleNode::createNode (const OUString& name, const Reference< XAttributeList > & attr)
{
	if (name.equalsAscii("LC_INFO"))
		return new LCInfoNode (name,attr);
	if (name.equalsAscii("LC_CTYPE"))
		return new LCCTYPENode (name,attr);
	if (name.equalsAscii("LC_FORMAT"))
		return new LCFormatNode (name,attr);
	if (name.equalsAscii("LC_FORMAT_1"))
		return new LCFormatNode (name,attr);
	if (name.equalsAscii("LC_CALENDAR"))
		return new LCCalendarNode (name,attr);
	if (name.equalsAscii("LC_CURRENCY"))
		return new LCCurrencyNode (name,attr);
	if (name.equalsAscii("LC_TRANSLITERATION"))
		return new LCTransliterationNode (name,attr);
	if (name.equalsAscii("LC_COLLATION"))
		return new LCCollationNode (name,attr);
	if (name.equalsAscii("LC_INDEX"))
		return new LCIndexNode (name,attr);
	if (name.equalsAscii("LC_SEARCH"))
		return new LCSearchNode (name,attr);
	if (name.equalsAscii("LC_MISC"))
		return new LCMiscNode (name,attr);
  	if (name.equalsAscii("LC_NumberingLevel"))
                return new LCNumberingLevelNode (name, attr);
  	if (name.equalsAscii("LC_OutLineNumberingLevel"))
                return new LCOutlineNumberingLevelNode (name, attr);

	return new LocaleNode(name,attr);
}


//   printf(" name: '%s'\n", p->getName().pData->buffer );
//   printf("value: '%s'\n", p->getValue().pData->buffer );

#define OSTR(s) (OUStringToOString( (s), RTL_TEXTENCODING_UTF8).getStr())

void print_OUString( const OUString& s )
{
    printf( "%s", OSTR(s));
}

bool is_empty( const OUString& s )
{
     return (s.getLength()==0) || (s.getLength()==1 && s[0]=='\n');
}

void print_indent( int depth )
{
     for( int i=0; i<depth; i++ ) printf("    ");
}

void print_color( int color )
{
     printf("\033[%dm", color);
}

void print_node( const LocaleNode* p, int depth=0 )
{
     if( !p ) return;

     print_indent( depth );
     printf("<");
     print_color(36);
     print_OUString( p->getName()  );
     print_color(0);
     const Attr* q = p->getAttr();
     if( q )
     {
          for( sal_Int32 j=0; j<q->getLength(); j++ )
          {
               printf(" ");
               print_color(33);
               print_OUString( q->getTypeByIndex(j) );
               print_color(0);
               printf("=");
               print_color(31);
               printf("'");
               print_OUString( q->getValueByIndex(j) );
               printf("'");
               print_color(0);
          }
     }
     printf(">");
     printf("\n");
     if( !is_empty( p->getValue() ) )
     {
          print_indent( depth+1 );
          printf("value: ");
          print_color(31);
          printf("'");
          print_OUString( p->getValue() );
          printf("'");
          print_color(0);
          printf("\n");
     }
     for( sal_Int32 i=0; i<p->getNumberOfChildren(); i++ )
     {
          print_node( p->getChildAt(i), depth+1 );
     }
     print_indent( depth );
     printf("</");
     print_OUString( p->getName()  );
     printf(">");
     printf("\n");
}

void LocaleNode :: generateCode (const OFileWriter &of) const
{
    ::rtl::OUString aDTD = getAttr()->getValueByName("versionDTD");
    if (!aDTD.equalsAscii( LOCALE_VERSION_DTD))
    {
        ++nError;
        fprintf( stderr, "Error: Locale versionDTD is not %s, see comment in locale.dtd\n", LOCALE_VERSION_DTD);
    }
	for (sal_Int32 i=0; i<nChildren;i++)
		children[i]->generateCode (of);
//      print_node( this );
}


::rtl::OUString LocaleNode::writeParameterCheckLen( const OFileWriter &of,
        const char* pParameterName, const LocaleNode* pNode,
        sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
{
    OUString aVal;
    if (pNode)
        aVal = pNode->getValue();
    else
    {
        ++nError;
        fprintf( stderr, "Error: node NULL pointer for parameter %s.\n",
                pParameterName);
    }
    // write empty data if error
    of.writeParameter( pParameterName, aVal); 
    sal_Int32 nLen = aVal.getLength();
    if (nLen < nMinLen)
    {
        ++nError;
        fprintf( stderr, "Error: less than %ld character%s (%ld) in %s '%s'.\n",
                sal::static_int_cast< long >(nMinLen), (nMinLen > 1 ? "s" : ""),
                sal::static_int_cast< long >(nLen),
                (pNode ? OSTR( pNode->getName()) : ""),
                OSTR( aVal));
    }
    else if (nLen > nMaxLen && nMaxLen >= 0)
        fprintf( stderr,
                "Warning: more than %ld character%s (%ld) in %s %s not supported by application.\n",
                sal::static_int_cast< long >(nMaxLen), (nMaxLen > 1 ? "s" : ""),
                sal::static_int_cast< long >(nLen),
                (pNode ? OSTR( pNode->getName()) : ""),
                OSTR( aVal));
    return aVal;
}


::rtl::OUString LocaleNode::writeParameterCheckLen( const OFileWriter &of,
        const char* pNodeName, const char* pParameterName,
        sal_Int32 nMinLen, sal_Int32 nMaxLen ) const
{
    OUString aVal;
	const LocaleNode * pNode = findNode( pNodeName);
    if (pNode)
        aVal = writeParameterCheckLen( of, pParameterName, pNode, nMinLen, nMaxLen);
    else
    {
        ++nError;
        fprintf( stderr, "Error: node %s not found.\n", pNodeName);
        // write empty data if error
        of.writeParameter( pParameterName, aVal); 
    }
    return aVal;
}

void LocaleNode::incError( const char* pStr ) const
{
    ++nError;
    fprintf( stderr, "Error: %s\n", pStr);
}

void LocaleNode::incError( const ::rtl::OUString& rStr ) const
{
    incError( OSTR( rStr));
}

char* LocaleNode::prepareErrorFormat( const char* pFormat, const char* pDefaultConversion ) const
{
    static char buf[2048];
    strcpy( buf, "Error: ");
    strncat( buf, pFormat, 2000);
    char* p = buf;
    while (((p = strchr( p, '%')) != 0) && p[1] == '%')
        p += 2;
    if (!p)
        strcat( buf, pDefaultConversion);
    strcat( buf, "\n");
    return buf;
}

void LocaleNode::incErrorInt( const char* pStr, int nVal ) const
{
    ++nError;
    fprintf( stderr, prepareErrorFormat( pStr, ": %d"), nVal);
}

void LocaleNode::incErrorStr( const char* pStr, const ::rtl::OUString& rVal ) const
{
    ++nError;
    fprintf( stderr, prepareErrorFormat( pStr, ": %s"), OSTR( rVal));
}

void LCInfoNode::generateCode (const OFileWriter &of) const
{

	const LocaleNode * languageNode = findNode("Language");
	const LocaleNode * countryNode = findNode("Country");
	const LocaleNode * variantNode = findNode("Variant");

	if (languageNode)
    {
		writeParameterCheckLen( of, "langID", languageNode->getChildAt(0), 2, -1);
		of.writeParameter("langDefaultName", languageNode->getChildAt(1)->getValue());
	}
    else
        incError( "No Language node.");
	if (countryNode)
    {
		of.writeParameter("countryID", countryNode->getChildAt(0)->getValue());
		of.writeParameter("countryDefaultName", countryNode->getChildAt(1)->getValue());
	}
    else
        incError( "No Country node.");
    if (variantNode)
    {
        of.writeParameter("Variant", variantNode->getValue());
        fprintf( stderr, "Warning: %s\n",
                "Variants are not supported by application.");
    }
    else
        of.writeParameter("Variant", ::rtl::OUString());
	of.writeAsciiString("\nstatic const sal_Unicode* LCInfoArray[] = {\n");
	of.writeAsciiString("\tlangID,\n");
	of.writeAsciiString("\tlangDefaultName,\n");
	of.writeAsciiString("\tcountryID,\n");
	of.writeAsciiString("\tcountryDefaultName,\n");
	of.writeAsciiString("\tVariant\n");
	of.writeAsciiString("};\n\n");
	of.writeFunction("getLCInfo_", "0", "LCInfoArray");
}

void LCCTYPENode::generateCode (const OFileWriter &of) const
{
	const LocaleNode * sepNode = 0;
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getLocaleItem_", useLocale);
	    return;
	}
	::rtl::OUString str =   getAttr() -> getValueByName("unoid");
	of.writeAsciiString("\n\n");
	of.writeParameter("LC_CTYPE_Unoid", str);;

    OUString aDateSep =
        writeParameterCheckLen( of, "DateSeparator", "dateSeparator", 1, 1);
    OUString aThoSep =
        writeParameterCheckLen( of, "ThousandSeparator", "thousandSeparator", 1, 1);
    OUString aDecSep =
        writeParameterCheckLen( of, "DecimalSeparator", "decimalSeparator", 1, 1);
    OUString aTimeSep =
        writeParameterCheckLen( of, "TimeSeparator", "timeSeparator", 1, 1);
    OUString aTime100Sep =
        writeParameterCheckLen( of, "Time100SecSeparator", "time100SecSeparator", 1, 1);
    OUString aListSep =
        writeParameterCheckLen( of, "ListSeparator", "listSeparator", 1, 1);

    OUString aLDS;

	sepNode = findNode("LongDateDayOfWeekSeparator");
    aLDS = sepNode->getValue();
	of.writeParameter("LongDateDayOfWeekSeparator", aLDS);
    if (aLDS.getLength() == 1 && aLDS.getStr()[0] == ',')
        fprintf( stderr, "Warning: %s\n",
                "LongDateDayOfWeekSeparator is only a comma not followed by a space. Usually this is not the case and may lead to concatenated display names like \"Wednesday,May 9, 2007\".");

	sepNode = findNode("LongDateDaySeparator");
    aLDS = sepNode->getValue();
	of.writeParameter("LongDateDaySeparator", aLDS);
    if (aLDS.getLength() == 1 && (aLDS.getStr()[0] == ',' || aLDS.getStr()[0] == '.'))
        fprintf( stderr, "Warning: %s\n",
                "LongDateDaySeparator is only a comma or dot not followed by a space. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May 9,2007\".");

	sepNode = findNode("LongDateMonthSeparator");
    aLDS = sepNode->getValue();
	of.writeParameter("LongDateMonthSeparator", aLDS);
    if (aLDS.getLength() == 0)
        fprintf( stderr, "Warning: %s\n",
                "LongDateMonthSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, May9, 2007\".");

	sepNode = findNode("LongDateYearSeparator");
    aLDS = sepNode->getValue();
	of.writeParameter("LongDateYearSeparator", aLDS);
    if (aLDS.getLength() == 0)
        fprintf( stderr, "Warning: %s\n",
                "LongDateYearSeparator is empty. Usually this is not the case and may lead to concatenated display names like \"Wednesday, 2007May 9\".");


    int nSavErr = nError;
    int nWarn = 0;
    if (aDateSep == aTimeSep)
        incError( "DateSeparator equals TimeSeparator.");
    if (aDecSep == aThoSep)
        incError( "DecimalSeparator equals ThousandSeparator.");
    if (aThoSep.equalsAscii( " "))
        incError( "ThousandSeparator is an ' ' ordinary space, this should be a non-breaking space U+00A0 instead.");
    if (aListSep == aDecSep)
        fprintf( stderr, "Warning: %s\n",
                "ListSeparator equals DecimalSeparator.");
    if (aListSep == aThoSep)
        fprintf( stderr, "Warning: %s\n",
                "ListSeparator equals ThousandSeparator.");
    if (aListSep.getLength() != 1 || aListSep.getStr()[0] != ';')
    {
        incError( "ListSeparator not ';' semicolon. Strongly recommended. Currently required.");
        ++nSavErr;  // format codes not affected
    }
    if (aTimeSep == aTime100Sep)
        ++nWarn, fprintf( stderr, "Warning: %s\n",
                "Time100SecSeparator equals TimeSeparator, this is probably an error.");
    if (aDecSep != aTime100Sep)
        ++nWarn, fprintf( stderr, "Warning: %s\n",
                "Time100SecSeparator is different from DecimalSeparator, this may be correct or not. Intended?");
    if (nSavErr != nError || nWarn)
        fprintf( stderr, "Warning: %s\n",
                "Don't forget to adapt corresponding FormatCode elements when changing separators.");

	OUString aQuoteStart =
        writeParameterCheckLen( of, "QuotationStart", "quotationStart", 1, 1);
	OUString aQuoteEnd =
        writeParameterCheckLen( of, "QuotationEnd", "quotationEnd", 1, 1);
	OUString aDoubleQuoteStart =
        writeParameterCheckLen( of, "DoubleQuotationStart", "doubleQuotationStart", 1, 1);
	OUString aDoubleQuoteEnd =
        writeParameterCheckLen( of, "DoubleQuotationEnd", "doubleQuotationEnd", 1, 1);

    if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() > 127)
        fprintf( stderr, "Warning: %s\n",
                "QuotationStart is an ASCII character but QuotationEnd is not.");
    if (aQuoteEnd.toChar() <= 127 && aQuoteStart.toChar() > 127)
        fprintf( stderr, "Warning: %s\n",
                "QuotationEnd is an ASCII character but QuotationStart is not.");
    if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() > 127)
        fprintf( stderr, "Warning: %s\n",
                "DoubleQuotationStart is an ASCII character but DoubleQuotationEnd is not.");
    if (aDoubleQuoteEnd.toChar() <= 127 && aDoubleQuoteStart.toChar() > 127)
        fprintf( stderr, "Warning: %s\n",
                "DoubleQuotationEnd is an ASCII character but DoubleQuotationStart is not.");
    if (aQuoteStart.toChar() <= 127 && aQuoteEnd.toChar() <= 127)
        fprintf( stderr, "Warning: %s\n",
                "QuotationStart and QuotationEnd are both ASCII characters. Not necessarily an error, but unusual.");
    if (aDoubleQuoteStart.toChar() <= 127 && aDoubleQuoteEnd.toChar() <= 127)
        fprintf( stderr, "Warning: %s\n",
                "DoubleQuotationStart and DoubleQuotationEnd are both ASCII characters. Not necessarily an error, but unusual.");
    if (aQuoteStart == aQuoteEnd)
        fprintf( stderr, "Warning: %s\n",
                "QuotationStart equals QuotationEnd. Not necessarily an error, but unusual.");
    if (aDoubleQuoteStart == aDoubleQuoteEnd)
        fprintf( stderr, "Warning: %s\n",
                "DoubleQuotationStart equals DoubleQuotationEnd. Not necessarily an error, but unusual.");
    /* TODO: should equalness of single and double quotes be an error? Would 
     * need to adapt quite some locales' data. */
    if (aQuoteStart == aDoubleQuoteStart)
        fprintf( stderr, "Warning: %s\n",
                "QuotationStart equals DoubleQuotationStart. Not necessarily an error, but unusual.");
    if (aQuoteEnd == aDoubleQuoteEnd)
        fprintf( stderr, "Warning: %s\n",
                "QuotationEnd equals DoubleQuotationEnd. Not necessarily an error, but unusual.");
    // Known good values, exclude ASCII single (U+0027, ') and double (U+0022, ") quotes.
    int ic;
    switch (ic = aQuoteStart.toChar())
    {
        case 0x2018:    // LEFT SINGLE QUOTATION MARK
        case 0x201a:    // SINGLE LOW-9 QUOTATION MARK
        case 0x201b:    // SINGLE HIGH-REVERSED-9 QUOTATION MARK
        case 0x2039:    // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
        case 0x203a:    // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
        case 0x300c:    // LEFT CORNER BRACKET (Chinese)
            ;
            break;
        default:
            fprintf( stderr, "Warning: %s U+%04X %s\n",
                    "QuotationStart may be wrong:", ic, OSTR( aQuoteStart));
    }
    switch (ic = aQuoteEnd.toChar())
    {
        case 0x2019:    // RIGHT SINGLE QUOTATION MARK
        case 0x201a:    // SINGLE LOW-9 QUOTATION MARK
        case 0x201b:    // SINGLE HIGH-REVERSED-9 QUOTATION MARK
        case 0x2039:    // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
        case 0x203a:    // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
        case 0x300d:    // RIGHT CORNER BRACKET (Chinese)
            ;
            break;
        default:
            fprintf( stderr, "Warning: %s U+%04X %s\n",
                    "QuotationEnd may be wrong:", ic, OSTR( aQuoteEnd));
    }
    switch (ic = aDoubleQuoteStart.toChar())
    {
        case 0x00ab:    // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
        case 0x00bb:    // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
        case 0x201c:    // LEFT DOUBLE QUOTATION MARK
        case 0x201e:    // DOUBLE LOW-9 QUOTATION MARK
        case 0x201f:    // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
        case 0x300e:    // LEFT WHITE CORNER BRACKET (Chinese)
            ;
            break;
        default:
            fprintf( stderr, "Warning: %s U+%04X %s\n",
                    "DoubleQuotationStart may be wrong:", ic, OSTR( aDoubleQuoteStart));
    }
    switch (ic = aDoubleQuoteEnd.toChar())
    {
        case 0x00ab:    // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
        case 0x00bb:    // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
        case 0x201d:    // RIGHT DOUBLE QUOTATION MARK
        case 0x201e:    // DOUBLE LOW-9 QUOTATION MARK
        case 0x201f:    // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
        case 0x300f:    // RIGHT WHITE CORNER BRACKET (Chinese)
            ;
            break;
        default:
            fprintf( stderr, "Warning: %s U+%04X %s\n",
                    "DoubleQuotationEnd may be wrong:", ic, OSTR( aDoubleQuoteEnd));
    }

	writeParameterCheckLen( of, "TimeAM", "timeAM", 1, -1);
	writeParameterCheckLen( of, "TimePM", "timePM", 1, -1);
	sepNode = findNode("MeasurementSystem");
	of.writeParameter("measurementSystem", sepNode->getValue());

	of.writeAsciiString("\nstatic const sal_Unicode* LCType[] = {\n");
	of.writeAsciiString("\tLC_CTYPE_Unoid,\n");
	of.writeAsciiString("\tdateSeparator,\n");
	of.writeAsciiString("\tthousandSeparator,\n");
	of.writeAsciiString("\tdecimalSeparator,\n");
	of.writeAsciiString("\ttimeSeparator,\n");
	of.writeAsciiString("\ttime100SecSeparator,\n");
	of.writeAsciiString("\tlistSeparator,\n");
	of.writeAsciiString("\tquotationStart,\n");
	of.writeAsciiString("\tquotationEnd,\n");
	of.writeAsciiString("\tdoubleQuotationStart,\n");
	of.writeAsciiString("\tdoubleQuotationEnd,\n");
	of.writeAsciiString("\ttimeAM,\n");
	of.writeAsciiString("\ttimePM,\n");
	of.writeAsciiString("\tmeasurementSystem,\n");
	of.writeAsciiString("\tLongDateDayOfWeekSeparator,\n");
	of.writeAsciiString("\tLongDateDaySeparator,\n");
	of.writeAsciiString("\tLongDateMonthSeparator,\n");
	of.writeAsciiString("\tLongDateYearSeparator\n");
	of.writeAsciiString("};\n\n");
	of.writeFunction("getLocaleItem_", "0", "LCType");
}


sal_Int16 LCFormatNode::mnSection = 0;
sal_Int16 LCFormatNode::mnFormats = 0;

void LCFormatNode::generateCode (const OFileWriter &of) const
{
    OUString str;
    if (mnSection >= 2)
        incError("more than 2 LC_FORMAT sections");
    of.writeParameter("replaceFrom", getAttr() -> getValueByName("replaceFrom"), mnSection);
    str = getAttr() -> getValueByName("replaceTo");
    // Locale data generator inserts FFFF for LangID, we need to adapt that.
    if (str.endsWithIgnoreAsciiCaseAsciiL( "-FFFF]", 6))
        incErrorStr("replaceTo=\"%s\" needs FFFF to be adapted to the real LangID value.", str);
    of.writeParameter("replaceTo", str, mnSection);
    ::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
    if (useLocale.getLength() > 0) {
        switch (mnSection)
        {
            case 0:
                of.writeRefFunction("getAllFormats0_", useLocale, "replaceTo0");
                break;
            case 1:
                of.writeRefFunction("getAllFormats1_", useLocale, "replaceTo1");
                break;
        }
        return;
    }
    sal_Int16 formatCount = mnFormats;
    NameSet  aMsgIdSet;
    ValueSet aFormatIndexSet;
    NameSet  aDefaultsSet;
    bool bCtypeIsRef = false;

    for (sal_Int16 i = 0; i< getNumberOfChildren() ; i++,formatCount++) {
        LocaleNode * currNode = getChildAt (i);
        OUString aUsage;
        OUString aType;
        OUString aFormatIndex;
        //		currNode -> print();
        const Attr *  currNodeAttr = currNode->getAttr();
        //printf ("getLen() = %d\n", currNode->getAttr()->getLength());

        str = currNodeAttr -> getValueByName("msgid");
        if (!aMsgIdSet.insert( str).second)
            incErrorStr( "Duplicated msgid=\"%s\" in FormatElement.", str);
        of.writeParameter("FormatKey", str, formatCount);

        str = currNodeAttr -> getValueByName("default");
        bool bDefault = str.equalsAscii( "true");
        of.writeDefaultParameter("FormatElement", str, formatCount);

        aType = currNodeAttr -> getValueByName("type");
        of.writeParameter("FormatType", aType, formatCount);

        aUsage = currNodeAttr -> getValueByName("usage");
        of.writeParameter("FormatUsage", aUsage, formatCount);

        aFormatIndex = currNodeAttr -> getValueByName("formatindex");
        sal_Int16 formatindex = (sal_Int16)aFormatIndex.toInt32();
        if (!aFormatIndexSet.insert( formatindex).second)
            incErrorInt( "Duplicated formatindex=\"%d\" in FormatElement.", formatindex);
        of.writeIntParameter("Formatindex", formatCount, formatindex);

        // Ensure only one default per usage and type.
        if (bDefault)
        {
            OUString aKey( aUsage + OUString( sal_Unicode(',')) + aType);
            if (!aDefaultsSet.insert( aKey).second)
            {
                OUString aStr( RTL_CONSTASCII_USTRINGPARAM( "Duplicated default for usage=\""));
                aStr += aUsage;
                aStr += OUString( RTL_CONSTASCII_USTRINGPARAM( "\" type=\""));
                aStr += aType;
                aStr += OUString( RTL_CONSTASCII_USTRINGPARAM( "\": formatindex=\""));
                aStr += aFormatIndex;
                aStr += OUString( RTL_CONSTASCII_USTRINGPARAM( "\"."));
                incError( aStr);
            }
        }

        const LocaleNode * n = currNode -> findNode("FormatCode");
        if (n)
        {
            of.writeParameter("FormatCode", n->getValue(), formatCount);
            // Check separator usage for some FormatCode elements.
            const LocaleNode* pCtype = 0;
            switch (formatindex)
            {
                case cssi::NumberFormatIndex::NUMBER_1000DEC2 : // #,##0.00
                case cssi::NumberFormatIndex::TIME_MMSS00 :     // MM:SS.00
                case cssi::NumberFormatIndex::TIME_HH_MMSS00 :  // [HH]:MM:SS.00
                    {
                        const LocaleNode* pRoot = getRoot();
                        if (!pRoot)
                            incError( "No root for FormatCode.");
                        else
                        {
                            pCtype = pRoot->findNode( "LC_CTYPE");
                            if (!pCtype)
                                incError( "No LC_CTYPE found for FormatCode.");
                            else
                            {
                                OUString aRef( pCtype->getAttr()->getValueByName("ref"));
                                if (aRef.getLength() > 0)
                                {
                                    if (!bCtypeIsRef)
                                        fprintf( stderr,
                                                "Warning: Can't check separators used in FormatCode due to LC_CTYPE ref=\"%s\".\n"
                                                "If these two locales use identical format codes, you should consider to use the ref= mechanism also for the LC_FORMAT element, together with replaceFrom= and replaceTo= for the currency.\n",
                                                OSTR( aRef));
                                    bCtypeIsRef = true;
                                    pCtype = 0;
                                }
                            }
                        }
                    }
                    break;
                // Currency formats should be something like [C]###0;-[C]###0
                // and not parenthesized [C]###0;([C]###0) if not en_US.
                case cssi::NumberFormatIndex::CURRENCY_1000INT :
                case cssi::NumberFormatIndex::CURRENCY_1000INT_RED :  
                case cssi::NumberFormatIndex::CURRENCY_1000DEC2 :  
                case cssi::NumberFormatIndex::CURRENCY_1000DEC2_RED :  
                case cssi::NumberFormatIndex::CURRENCY_1000DEC2_CCC :  
                case cssi::NumberFormatIndex::CURRENCY_1000DEC2_DASHED :  
                    if (strcmp( of.getLocale(), "en_US") != 0)
                    {
                        OUString aCode( n->getValue());
                        OUString aPar1( RTL_CONSTASCII_USTRINGPARAM( "0)" ));
                        OUString aPar2( RTL_CONSTASCII_USTRINGPARAM( "-)" ));
                        OUString aPar3( RTL_CONSTASCII_USTRINGPARAM( " )" ));
                        OUString aPar4( RTL_CONSTASCII_USTRINGPARAM( "])" ));
                        if (aCode.indexOf( aPar1 ) > 0 || aCode.indexOf( aPar2 ) > 0 ||
                                aCode.indexOf( aPar3 ) > 0 || aCode.indexOf( aPar4 ) > 0)
                            fprintf( stderr, "Warning: FormatCode formatindex=\"%d\" for currency uses parentheses for negative amounts, which probably is not correct for locales not based on en_US.\n", formatindex);
                    }
                    break;
            }
            if (pCtype)
            {
                int nSavErr = nError;
                OUString aCode( n->getValue());
                if (formatindex == cssi::NumberFormatIndex::NUMBER_1000DEC2)
                {
                    sal_Int32 nDec = -1;
                    sal_Int32 nGrp = -1;
                    const LocaleNode* pSep = pCtype->findNode( "DecimalSeparator");
                    if (!pSep)
                        incError( "No DecimalSeparator found for FormatCode.");
                    else
                    {
                        nDec = aCode.indexOf( pSep->getValue());
                        if (nDec < 0)
                            incErrorInt( "DecimalSeparator not present in FormatCode formatindex=\"%d\".",
                                    formatindex);
                    }
                    pSep = pCtype->findNode( "ThousandSeparator");
                    if (!pSep)
                        incError( "No ThousandSeparator found for FormatCode.");
                    else
                    {
                        nGrp = aCode.indexOf( pSep->getValue());
                        if (nGrp < 0)
                            incErrorInt( "ThousandSeparator not present in FormatCode formatindex=\"%d\".",
                                    formatindex);
                    }
                    if (nDec >= 0 && nGrp >= 0 && nDec <= nGrp)
                        incErrorInt( "Ordering of ThousandSeparator and DecimalSeparator not correct in formatindex=\"%d\".",
                                formatindex);
                }
                if (formatindex == cssi::NumberFormatIndex::TIME_MMSS00 ||
                        formatindex == cssi::NumberFormatIndex::TIME_HH_MMSS00)
                {
                    sal_Int32 nTime = -1;
                    sal_Int32 n100s = -1;
                    const LocaleNode* pSep = pCtype->findNode( "TimeSeparator");
                    if (!pSep)
                        incError( "No TimeSeparator found for FormatCode.");
                    else
                    {
                        nTime = aCode.indexOf( pSep->getValue());
                        if (nTime < 0)
                            incErrorInt( "TimeSeparator not present in FormatCode formatindex=\"%d\".",
                                    formatindex);
                    }
                    pSep = pCtype->findNode( "Time100SecSeparator");
                    if (!pSep)
                        incError( "No Time100SecSeparator found for FormatCode.");
                    else
                    {
                        n100s = aCode.indexOf( pSep->getValue());
                        if (n100s < 0)
                            incErrorInt( "Time100SecSeparator not present in FormatCode formatindex=\"%d\".",
                                    formatindex);
                        OUStringBuffer a100s( pSep->getValue());
                        a100s.appendAscii( "00");
                        n100s = aCode.indexOf( a100s.makeStringAndClear());
                        if (n100s < 0)
                            incErrorInt( "Time100SecSeparator+00 not present in FormatCode formatindex=\"%d\".",
                                    formatindex);
                    }
                    if (n100s >= 0 && nTime >= 0 && n100s <= nTime)
                        incErrorInt( "Ordering of Time100SecSeparator and TimeSeparator not correct in formatindex=\"%d\".",
                                formatindex);
                }
                if (nSavErr != nError)
                    fprintf( stderr,
                            "Warning: formatindex=\"%d\",\"%d\",\"%d\" are the only FormatCode elements checked for separator usage, there may be others that have errors.\n",
                            int(cssi::NumberFormatIndex::NUMBER_1000DEC2),
                            int(cssi::NumberFormatIndex::TIME_MMSS00),
                            int(cssi::NumberFormatIndex::TIME_HH_MMSS00));

            }
        }
        else
            incError( "No FormatCode in FormatElement.");
        n = currNode -> findNode("DefaultName");
        if (n)
            of.writeParameter("FormatDefaultName", n->getValue(), formatCount);
        else
            of.writeParameter("FormatDefaultName", ::rtl::OUString(), formatCount);

    }

    // Check presence of all required format codes only in first section
    // LC_FORMAT, not in optional LC_FORMAT_1
    if (mnSection == 0)
    {
        // 0..47 MUST be present, 48,49 MUST NOT be present
        ValueSet::const_iterator aIter( aFormatIndexSet.begin());
        for (sal_Int16 nNext = cssi::NumberFormatIndex::NUMBER_START;
                nNext < cssi::NumberFormatIndex::INDEX_TABLE_ENTRIES; ++nNext)
        {
            sal_Int16 nHere = ::std::min( ((aIter != aFormatIndexSet.end() ? *aIter :
                    cssi::NumberFormatIndex::INDEX_TABLE_ENTRIES)),
                    cssi::NumberFormatIndex::INDEX_TABLE_ENTRIES);
            if (aIter != aFormatIndexSet.end()) ++aIter;
            for ( ; nNext < nHere; ++nNext)
            {
                switch (nNext)
                {
                    case cssi::NumberFormatIndex::FRACTION_1 :
                    case cssi::NumberFormatIndex::FRACTION_2 :
                    case cssi::NumberFormatIndex::BOOLEAN :
                    case cssi::NumberFormatIndex::TEXT :
                        // generated internally
                        break;
                    default:
                        incErrorInt( "FormatElement formatindex=\"%d\" not present.", nNext);
                }
            }
            switch (nHere)
            {
                case cssi::NumberFormatIndex::BOOLEAN :
                    incErrorInt( "FormatElement formatindex=\"%d\" reserved for internal ``BOOLEAN''.", nNext);
                    break;
                case cssi::NumberFormatIndex::TEXT :
                    incErrorInt( "FormatElement formatindex=\"%d\" reserved for internal ``@'' (TEXT).", nNext);
                    break;
                default:
                    ;   // nothing
            }
        }
    }

    of.writeAsciiString("\nstatic const sal_Int16 ");
    of.writeAsciiString("FormatElementsCount");
    of.writeInt(mnSection);
    of.writeAsciiString(" = ");
    of.writeInt( formatCount - mnFormats);
    of.writeAsciiString(";\n");
    of.writeAsciiString("static const sal_Unicode* ");
    of.writeAsciiString("FormatElementsArray");
    of.writeInt(mnSection);
    of.writeAsciiString("[] = {\n");
    for(sal_Int16 i = mnFormats; i < formatCount; i++) {

        of.writeAsciiString("\t");
        of.writeAsciiString("FormatCode");
        of.writeInt(i);
        of.writeAsciiString(",\n");

        of.writeAsciiString("\t");
        of.writeAsciiString("FormatDefaultName");
        of.writeInt(i);
        of.writeAsciiString(",\n");

        of.writeAsciiString("\t");
        of.writeAsciiString("FormatKey");
        of.writeInt(i);
        of.writeAsciiString(",\n");

        of.writeAsciiString("\t");
        of.writeAsciiString("FormatType");
        of.writeInt(i);
        of.writeAsciiString(",\n");

        of.writeAsciiString("\t");
        of.writeAsciiString("FormatUsage");
        of.writeInt(i);
        of.writeAsciiString(",\n");

        of.writeAsciiString("\t");
        of.writeAsciiString("Formatindex");
        of.writeInt(i);
        of.writeAsciiString(",\n");


        of.writeAsciiString("\tdefaultFormatElement");
        of.writeInt(i);
        of.writeAsciiString(",\n");
    }
    of.writeAsciiString("};\n\n");

    switch (mnSection)
    {
        case 0:
            of.writeFunction("getAllFormats0_", "FormatElementsCount0", "FormatElementsArray0", "replaceFrom0", "replaceTo0");
            break;
        case 1:
            of.writeFunction("getAllFormats1_", "FormatElementsCount1", "FormatElementsArray1", "replaceFrom1", "replaceTo1");
            break;
    }

    mnFormats = mnFormats + formatCount;
    ++mnSection;
}

void LCCollationNode::generateCode (const OFileWriter &of) const
{
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getCollatorImplementation_", useLocale);
	    of.writeRefFunction("getCollationOptions_", useLocale);
	    return;
	}
	sal_Int16 nbOfCollations = 0;
	sal_Int16 nbOfCollationOptions = 0;
	sal_Int16 j;

	for ( j = 0; j < getNumberOfChildren(); j++ ) {
		LocaleNode * currNode = getChildAt (j);
		if( currNode->getName().compareToAscii("Collator") == 0 )
		{
			::rtl::OUString str;
			str = currNode->getAttr() -> getValueByName("unoid");
			of.writeParameter("CollatorID", str, j);
			str = currNode->getValue();
			of.writeParameter("CollatorRule", str, j);
			str = currNode -> getAttr() -> getValueByName("default");
			of.writeDefaultParameter("Collator", str, j);
			of.writeAsciiString("\n");

			nbOfCollations++;
		}
		if( currNode->getName().compareToAscii("CollationOptions") == 0 )
		{
			LocaleNode* pCollationOptions = currNode;
			nbOfCollationOptions = sal::static_int_cast<sal_Int16>( pCollationOptions->getNumberOfChildren() );
			for( sal_Int16 i=0; i<nbOfCollationOptions; i++ )
			{
				of.writeParameter("collationOption", pCollationOptions->getChildAt( i )->getValue(), i );
			}

			of.writeAsciiString("static const sal_Int16 nbOfCollationOptions = ");
			of.writeInt( nbOfCollationOptions );
			of.writeAsciiString(";\n\n");
		}
	}
	of.writeAsciiString("static const sal_Int16 nbOfCollations = ");
	of.writeInt(nbOfCollations);
	of.writeAsciiString(";\n\n");

	of.writeAsciiString("\nstatic const sal_Unicode* LCCollatorArray[] = {\n");
	for(j = 0; j < nbOfCollations; j++) {
		of.writeAsciiString("\tCollatorID");
		of.writeInt(j);
		of.writeAsciiString(",\n");

		of.writeAsciiString("\tdefaultCollator");
		of.writeInt(j);
		of.writeAsciiString(",\n");

        of.writeAsciiString("\tCollatorRule");
        of.writeInt(j);
        of.writeAsciiString(",\n");
	}
	of.writeAsciiString("};\n\n");

	of.writeAsciiString("static const sal_Unicode* collationOptions[] = {");
	for( j=0; j<nbOfCollationOptions; j++ )
	{
		of.writeAsciiString( "collationOption" );
		of.writeInt( j );
		of.writeAsciiString( ", " );
	}
	of.writeAsciiString("NULL };\n");
	of.writeFunction("getCollatorImplementation_", "nbOfCollations", "LCCollatorArray");
	of.writeFunction("getCollationOptions_", "nbOfCollationOptions", "collationOptions");
}

void LCSearchNode::generateCode (const OFileWriter &of) const
{
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getSearchOptions_", useLocale);
	    return;
	}

	if( getNumberOfChildren() != 1 )
	{
        ++nError;
        fprintf(
            stderr, "Error: LC_SEARCH: more than 1 child: %ld\n",
            sal::static_int_cast< long >(getNumberOfChildren()));
	}
	sal_Int32 i;
	LocaleNode* pSearchOptions = getChildAt( 0 );
	sal_Int32   nSearchOptions = pSearchOptions->getNumberOfChildren();
	for( i=0; i<nSearchOptions; i++ )
	{
		of.writeParameter("searchOption", pSearchOptions->getChildAt( i )->getValue(), sal::static_int_cast<sal_Int16>(i) );
	}

	of.writeAsciiString("static const sal_Int16 nbOfSearchOptions = ");
	of.writeInt( sal::static_int_cast<sal_Int16>( nSearchOptions ) );
	of.writeAsciiString(";\n\n");

	of.writeAsciiString("static const sal_Unicode* searchOptions[] = {");
	for( i=0; i<nSearchOptions; i++ )
	{
		of.writeAsciiString( "searchOption" );
		of.writeInt( sal::static_int_cast<sal_Int16>(i) );
		of.writeAsciiString( ", " );
	}
	of.writeAsciiString("NULL };\n");
	of.writeFunction("getSearchOptions_", "nbOfSearchOptions", "searchOptions");
}

void LCIndexNode::generateCode (const OFileWriter &of) const
{
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getIndexAlgorithm_", useLocale);
	    of.writeRefFunction("getUnicodeScripts_", useLocale);
	    of.writeRefFunction("getFollowPageWords_", useLocale);
	    return;
	}
	sal_Int16 nbOfIndexs = 0;
    sal_Int16 nbOfUnicodeScripts = 0;
	sal_Int16 nbOfPageWords = 0;
    sal_Int16 i;
	for (i = 0; i< getNumberOfChildren();i++) {
		LocaleNode * currNode = getChildAt (i);
		if( currNode->getName().compareToAscii("IndexKey") == 0 )
		{
			::rtl::OUString str;
			str = currNode->getAttr() -> getValueByName("unoid");
			of.writeParameter("IndexID", str, nbOfIndexs);
			str = currNode->getAttr() -> getValueByName("module");
			of.writeParameter("IndexModule", str, nbOfIndexs);
			str = currNode->getValue();
			of.writeParameter("IndexKey", str, nbOfIndexs);
			str = currNode -> getAttr() -> getValueByName("default");
			of.writeDefaultParameter("Index", str, nbOfIndexs);
			str = currNode -> getAttr() -> getValueByName("phonetic");
			of.writeDefaultParameter("Phonetic", str, nbOfIndexs);
			of.writeAsciiString("\n");

			nbOfIndexs++;
		}
		if( currNode->getName().compareToAscii("UnicodeScript") == 0 )
		{
            of.writeParameter("unicodeScript", currNode->getValue(), nbOfUnicodeScripts );
            nbOfUnicodeScripts++;

		}
		if( currNode->getName().compareToAscii("FollowPageWord") == 0 )
        {
			of.writeParameter("followPageWord", currNode->getValue(), nbOfPageWords);
            nbOfPageWords++;
        }
	}
	of.writeAsciiString("static const sal_Int16 nbOfIndexs = ");
	of.writeInt(nbOfIndexs);
	of.writeAsciiString(";\n\n");

	of.writeAsciiString("\nstatic const sal_Unicode* IndexArray[] = {\n");
	for(i = 0; i < nbOfIndexs; i++) {
		of.writeAsciiString("\tIndexID");
		of.writeInt(i);
		of.writeAsciiString(",\n");

		of.writeAsciiString("\tIndexModule");
		of.writeInt(i);
		of.writeAsciiString(",\n");

		of.writeAsciiString("\tIndexKey");
		of.writeInt(i);
		of.writeAsciiString(",\n");

		of.writeAsciiString("\tdefaultIndex");
		of.writeInt(i);
		of.writeAsciiString(",\n");

		of.writeAsciiString("\tdefaultPhonetic");
		of.writeInt(i);
		of.writeAsciiString(",\n");
	}
	of.writeAsciiString("};\n\n");

    of.writeAsciiString("static const sal_Int16 nbOfUnicodeScripts = ");
    of.writeInt( nbOfUnicodeScripts );
    of.writeAsciiString(";\n\n");

	of.writeAsciiString("static const sal_Unicode* UnicodeScriptArray[] = {");
	for( i=0; i<nbOfUnicodeScripts; i++ )
	{
		of.writeAsciiString( "unicodeScript" );
		of.writeInt( i );
		of.writeAsciiString( ", " );
	}
	of.writeAsciiString("NULL };\n\n");

	of.writeAsciiString("static const sal_Int16 nbOfPageWords = ");
	of.writeInt(nbOfPageWords);
	of.writeAsciiString(";\n\n");

	of.writeAsciiString("static const sal_Unicode* FollowPageWordArray[] = {\n");
	for(i = 0; i < nbOfPageWords; i++) {
		of.writeAsciiString("\tfollowPageWord");
		of.writeInt(i);
		of.writeAsciiString(",\n");
	}
	of.writeAsciiString("\tNULL\n};\n\n");

	of.writeFunction("getIndexAlgorithm_", "nbOfIndexs", "IndexArray");
	of.writeFunction("getUnicodeScripts_", "nbOfUnicodeScripts", "UnicodeScriptArray");
	of.writeFunction("getFollowPageWords_", "nbOfPageWords", "FollowPageWordArray");
}

void LCCalendarNode::generateCode (const OFileWriter &of) const
{
    ::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
    if (useLocale.getLength() > 0) {
        of.writeRefFunction("getAllCalendars_", useLocale);
        return;
    }
    sal_Int16 nbOfCalendars = sal::static_int_cast<sal_Int16>( getNumberOfChildren() );
    ::rtl::OUString str;
    sal_Int16 * nbOfDays = new sal_Int16[nbOfCalendars];
    sal_Int16 * nbOfMonths = new sal_Int16[nbOfCalendars];
    sal_Int16 * nbOfEras = new sal_Int16[nbOfCalendars];
    sal_Int16 j;
    sal_Int16 i;
    bool bHasGregorian = false;


    for ( i = 0; i < nbOfCalendars; i++) {
        LocaleNode * calNode = getChildAt (i);
        OUString calendarID = calNode -> getAttr() -> getValueByName("unoid");
        of.writeParameter( "calendarID", calendarID, i);
        bool bGregorian = calendarID.equalsAscii( "gregorian");
        if (!bHasGregorian)
            bHasGregorian = bGregorian;
        str = calNode -> getAttr() -> getValueByName("default");
        of.writeDefaultParameter("Calendar", str, i);

        // Generate Days of Week
        const sal_Char *elementTag;
        LocaleNode * daysNode = NULL;
        ::rtl::OUString ref_name = calNode->getChildAt(0)->getAttr()->getValueByName("ref");
        if (ref_name.getLength() > 0 && i > 0) {
            for (j = 0; j < i; j++) {
                str = getChildAt(j)->getAttr()->getValueByName("unoid");
                if (str.equals(ref_name))
                    daysNode = getChildAt(j)->getChildAt(0);
            }
        }
        if (ref_name.getLength() > 0 && daysNode == NULL) {
            of.writeParameter("dayRef", OUString::createFromAscii("ref"), i);
            of.writeParameter("dayRefName", ref_name, i);
            nbOfDays[i] = 0;
        } else {
            if (daysNode == NULL)
                daysNode = calNode -> getChildAt(0);
            nbOfDays[i] = sal::static_int_cast<sal_Int16>( daysNode->getNumberOfChildren() );
            if (bGregorian && nbOfDays[i] != 7)
                incErrorInt( "A Gregorian calendar must have 7 days per week, this one has %d", nbOfDays[i]);
            elementTag = "day";
            for (j = 0; j < nbOfDays[i]; j++) {
                LocaleNode *currNode = daysNode -> getChildAt(j);
                OUString dayID( currNode->getChildAt(0)->getValue());
                of.writeParameter("dayID", dayID, i, j);
                if (j == 0 && bGregorian && !dayID.equalsAscii( "sun"))
                    incError( "First day of a week of a Gregorian calendar must be <DayID>sun</DayID>");
                of.writeParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
                of.writeParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
            }
        }

        // Generate Months of Year
        LocaleNode * monthsNode = NULL;
        ref_name = calNode->getChildAt(1)->getAttr()->getValueByName("ref");
        if (ref_name.getLength() > 0 && i > 0) {
            for (j = 0; j < i; j++) {
                str = getChildAt(j)->getAttr()->getValueByName("unoid");
                if (str.equals(ref_name))
                    monthsNode = getChildAt(j)->getChildAt(1);
            }
        }
        if (ref_name.getLength() > 0 && monthsNode == NULL) {
            of.writeParameter("monthRef", OUString::createFromAscii("ref"), i);
            of.writeParameter("monthRefName", ref_name, i);
            nbOfMonths[i] = 0;
        } else {
            if (monthsNode == NULL)
                monthsNode = calNode -> getChildAt(1);
            nbOfMonths[i] = sal::static_int_cast<sal_Int16>( monthsNode->getNumberOfChildren() );
            if (bGregorian && nbOfMonths[i] != 12)
                incErrorInt( "A Gregorian calendar must have 12 months, this one has %d", nbOfMonths[i]);
            elementTag = "month";
            for (j = 0; j < nbOfMonths[i]; j++) {
                LocaleNode *currNode = monthsNode -> getChildAt(j);
                OUString monthID( currNode->getChildAt(0)->getValue());
                of.writeParameter("monthID", monthID, i, j);
                if (j == 0 && bGregorian && !monthID.equalsAscii( "jan"))
                    incError( "First month of a year of a Gregorian calendar must be <MonthID>jan</MonthID>");
                of.writeParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
                of.writeParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
            }
        }

        // Generate Era name
        LocaleNode * erasNode = NULL;
        ref_name =   calNode -> getChildAt(2) ->getAttr()->getValueByName("ref");
        if (ref_name.getLength() > 0 && i > 0) {
            for (j = 0; j < i; j++) {
                str = getChildAt(j)->getAttr()->getValueByName("unoid");
                if (str.equals(ref_name))
                    erasNode = getChildAt(j)->getChildAt(2);
            }
        }
        if (ref_name.getLength() > 0 && erasNode == NULL) {
            of.writeParameter("eraRef", OUString::createFromAscii("ref"), i);
            of.writeParameter("eraRefName", ref_name, i);
            nbOfEras[i] = 0;
        } else {
            if (erasNode == NULL)
                erasNode = calNode -> getChildAt(2);
            nbOfEras[i] = sal::static_int_cast<sal_Int16>( erasNode->getNumberOfChildren() );
            if (bGregorian && nbOfEras[i] != 2)
                incErrorInt( "A Gregorian calendar must have 2 eras, this one has %d", nbOfEras[i]);
            elementTag = "era";
            for (j = 0; j < nbOfEras[i]; j++) {
                LocaleNode *currNode = erasNode -> getChildAt(j);
                OUString eraID( currNode->getChildAt(0)->getValue());
                of.writeParameter("eraID", eraID, i, j);
                if (j == 0 && bGregorian && !eraID.equalsAscii( "bc"))
                    incError( "First era of a Gregorian calendar must be <EraID>bc</EraID>");
                if (j == 1 && bGregorian && !eraID.equalsAscii( "ad"))
                    incError( "Second era of a Gregorian calendar must be <EraID>ad</EraID>");
                of.writeAsciiString("\n");
                of.writeParameter(elementTag, "DefaultAbbrvName",currNode->getChildAt(1)->getValue() ,i, j);
                of.writeParameter(elementTag, "DefaultFullName",currNode->getChildAt(2)->getValue() , i, j);
            }
        }
        str = calNode->getChildAt(3)->getChildAt(0)->getValue();
        if (nbOfDays[i])
        {
            for (j = 0; j < nbOfDays[i]; j++)
            {
                LocaleNode *currNode = daysNode->getChildAt(j);
                OUString dayID( currNode->getChildAt(0)->getValue());
                if (str == dayID)
                    break;  // for
            }
            if (j >= nbOfDays[i])
                incErrorStr( "<StartDayOfWeek> <DayID> must be one of the <DaysOfWeek>, but is", str);
        }
        of.writeParameter("startDayOfWeek", str, i);
        str = calNode ->getChildAt(4)-> getValue();
        sal_Int16 nDays = sal::static_int_cast<sal_Int16>( str.toInt32() );
        if (nDays < 1 || (0 < nbOfDays[i] && nbOfDays[i] < nDays))
            incErrorInt( "Bad value of MinimalDaysInFirstWeek: %d, must be 1 <= value <= days_in_week",  nDays);
        of.writeIntParameter("minimalDaysInFirstWeek", i, nDays);
    }
    if (!bHasGregorian)
        fprintf( stderr, "Warning: %s\n", "No Gregorian calendar defined, are you sure?");

    of.writeAsciiString("static const sal_Int16 calendarsCount = ");
    of.writeInt(nbOfCalendars);
    of.writeAsciiString(";\n\n");

    of.writeAsciiString("static const sal_Unicode nbOfDays[] = {");
    for(i = 0; i < nbOfCalendars - 1; i++) {
        of.writeInt(nbOfDays[i]);
        of.writeAsciiString(", ");
    };
    of.writeInt(nbOfDays[i]);
    of.writeAsciiString("};\n");

    of.writeAsciiString("static const sal_Unicode nbOfMonths[] = {");
    for(i = 0; i < nbOfCalendars - 1; i++) {
        of.writeInt(nbOfMonths[i]);
        of.writeAsciiString(", ");
    };
    of.writeInt(nbOfMonths[i]);
    of.writeAsciiString("};\n");

    of.writeAsciiString("static const sal_Unicode nbOfEras[] = {");
    for(i = 0; i < nbOfCalendars - 1; i++) {
        of.writeInt(nbOfEras[i]);
        of.writeAsciiString(", ");
    };
    of.writeInt(nbOfEras[i]);
    of.writeAsciiString("};\n");


    of.writeAsciiString("static const sal_Unicode* calendars[] = {\n");
    of.writeAsciiString("\tnbOfDays,\n");
    of.writeAsciiString("\tnbOfMonths,\n");
    of.writeAsciiString("\tnbOfEras,\n");
    for(i = 0; i < nbOfCalendars; i++) {
        of.writeAsciiString("\tcalendarID");
        of.writeInt(i);
        of.writeAsciiString(",\n");
        of.writeAsciiString("\tdefaultCalendar");
        of.writeInt(i);
        of.writeAsciiString(",\n");
        if (nbOfDays[i] == 0) {
            of.writeAsciiString("\tdayRef");
            of.writeInt(i); of.writeAsciiString(",\n");
            of.writeAsciiString("\tdayRefName");
            of.writeInt(i); of.writeAsciiString(",\n");
        } else {
            for(j = 0; j < nbOfDays[i]; j++) {
                of.writeAsciiString("\tdayID");
                of.writeInt(i);	of.writeInt(j);	of.writeAsciiString(",\n");
                of.writeAsciiString("\tdayDefaultAbbrvName");
                of.writeInt(i);	of.writeInt(j);	of.writeAsciiString(",\n");
                of.writeAsciiString("\tdayDefaultFullName");of.writeInt(i);	of.writeInt(j);	of.writeAsciiString(",\n");
            }
        }
        if (nbOfMonths[i] == 0) {
            of.writeAsciiString("\tmonthRef");
            of.writeInt(i); of.writeAsciiString(",\n");
            of.writeAsciiString("\tmonthRefName");
            of.writeInt(i); of.writeAsciiString(",\n");
        } else {
            for(j = 0; j < nbOfMonths[i]; j++) {
                of.writeAsciiString("\tmonthID");of.writeInt(i);of.writeInt(j);of.writeAsciiString(",\n");
                of.writeAsciiString("\tmonthDefaultAbbrvName");of.writeInt(i);of.writeInt(j);of.writeAsciiString(",\n");
                of.writeAsciiString("\tmonthDefaultFullName");of.writeInt(i);of.writeInt(j);of.writeAsciiString(",\n");
            }
        }
        if (nbOfEras[i] == 0) {
            of.writeAsciiString("\teraRef");
            of.writeInt(i); of.writeAsciiString(",\n");
            of.writeAsciiString("\teraRefName");
            of.writeInt(i); of.writeAsciiString(",\n");
        } else {
            for(j = 0; j < nbOfEras[i]; j++) {
                of.writeAsciiString("\teraID");	of.writeInt(i);	of.writeInt(j);	of.writeAsciiString(",\n");
                of.writeAsciiString("\teraDefaultAbbrvName");of.writeInt(i);of.writeInt(j);of.writeAsciiString(",\n");
                of.writeAsciiString("\teraDefaultFullName");of.writeInt(i);of.writeInt(j);of.writeAsciiString(",\n");
            }
        }
        of.writeAsciiString("\tstartDayOfWeek");of.writeInt(i);	of.writeAsciiString(",\n");
        of.writeAsciiString("\tminimalDaysInFirstWeek");of.writeInt(i);	of.writeAsciiString(",\n");
    }

    of.writeAsciiString("};\n\n");
    of.writeFunction("getAllCalendars_", "calendarsCount", "calendars");

    delete []nbOfDays;
    delete []nbOfMonths;
    delete []nbOfEras;
}

bool isIso4217( const OUString& rStr )
{
    const sal_Unicode* p = rStr.getStr();
    return rStr.getLength() == 3
        && 'A' <= p[0] && p[0] <= 'Z'
        && 'A' <= p[1] && p[1] <= 'Z'
        && 'A' <= p[2] && p[2] <= 'Z'
        ;
}

void LCCurrencyNode :: generateCode (const OFileWriter &of) const
{
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getAllCurrencies_", useLocale);
	    return;
	}
	sal_Int16 nbOfCurrencies = 0;
	::rtl::OUString str;
	sal_Int16 i;

    bool bTheDefault= false;
    bool bTheCompatible = false;
	for ( i = 0; i < getNumberOfChildren(); i++,nbOfCurrencies++) {
		LocaleNode * calNode = getChildAt (i);
        str = calNode->getAttr() -> getValueByName("default");
		bool bDefault = of.writeDefaultParameter("Currency", str, nbOfCurrencies);
        str = calNode->getAttr() -> getValueByName("usedInCompatibleFormatCodes");
        bool bCompatible = of.writeDefaultParameter("CurrencyUsedInCompatibleFormatCodes", str, nbOfCurrencies);
        str = calNode->getAttr() -> getValueByName("legacyOnly");
        bool bLegacy = of.writeDefaultParameter("CurrencyLegacyOnly", str, nbOfCurrencies);
        if (bLegacy && (bDefault || bCompatible))
            incError( "Currency: if legacyOnly==true, both 'default' and 'usedInCompatibleFormatCodes' must be false.");
        if (bDefault)
        {
            if (bTheDefault)
                incError( "Currency: more than one default currency.");
            bTheDefault = true;
        }
        if (bCompatible)
        {
            if (bTheCompatible)
                incError( "Currency: more than one currency flagged as usedInCompatibleFormatCodes.");
            bTheCompatible = true;
        }
		str = calNode -> findNode ("CurrencyID") -> getValue();
		of.writeParameter("currencyID", str, nbOfCurrencies);
        // CurrencyID MUST be ISO 4217.
        if (!bLegacy && !isIso4217(str))
            incError( "CurrencyID is not ISO 4217");
		str = calNode -> findNode ("CurrencySymbol") -> getValue();
		of.writeParameter("currencySymbol", str, nbOfCurrencies);
		str = calNode -> findNode ("BankSymbol") -> getValue();
		of.writeParameter("bankSymbol", str, nbOfCurrencies);
        // BankSymbol currently must be ISO 4217. May change later if 
        // application always uses CurrencyID instead of BankSymbol.
        if (!bLegacy && !isIso4217(str))
            incError( "BankSymbol is not ISO 4217");
		str = calNode -> findNode ("CurrencyName") -> getValue();
		of.writeParameter("currencyName", str, nbOfCurrencies);
        str = calNode -> findNode ("DecimalPlaces") -> getValue();
        sal_Int16 nDecimalPlaces = (sal_Int16)str.toInt32();
        of.writeIntParameter("currencyDecimalPlaces", nbOfCurrencies, nDecimalPlaces);
		of.writeAsciiString("\n");
	};

    if (!bTheDefault)
        incError( "Currency: no default currency.");
    if (!bTheCompatible)
        incError( "Currency: no currency flagged as usedInCompatibleFormatCodes.");

	of.writeAsciiString("static const sal_Int16 currencyCount = ");
	of.writeInt(nbOfCurrencies);
	of.writeAsciiString(";\n\n");
	of.writeAsciiString("static const sal_Unicode* currencies[] = {\n");
	for(i = 0; i < nbOfCurrencies; i++) {
		of.writeAsciiString("\tcurrencyID");
		of.writeInt(i);
		of.writeAsciiString(",\n");
		of.writeAsciiString("\tcurrencySymbol");
		of.writeInt(i);
		of.writeAsciiString(",\n");
		of.writeAsciiString("\tbankSymbol");
		of.writeInt(i);
		of.writeAsciiString(",\n");
		of.writeAsciiString("\tcurrencyName");
		of.writeInt(i);
		of.writeAsciiString(",\n");
		of.writeAsciiString("\tdefaultCurrency");
		of.writeInt(i);
		of.writeAsciiString(",\n");
        of.writeAsciiString("\tdefaultCurrencyUsedInCompatibleFormatCodes");
		of.writeInt(i);
		of.writeAsciiString(",\n");
        of.writeAsciiString("\tcurrencyDecimalPlaces");
		of.writeInt(i);
		of.writeAsciiString(",\n");
		of.writeAsciiString("\tdefaultCurrencyLegacyOnly");
		of.writeInt(i);
		of.writeAsciiString(",\n");
	}
	of.writeAsciiString("};\n\n");
	of.writeFunction("getAllCurrencies_", "currencyCount", "currencies");
}

void LCTransliterationNode::generateCode (const OFileWriter &of) const
{
	::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
	if (useLocale.getLength() > 0) {
	    of.writeRefFunction("getTransliterations_", useLocale);
	    return;
	}
	sal_Int16 nbOfModules = 0;
	::rtl::OUString str;
	sal_Int16 i;

	for ( i = 0; i < getNumberOfChildren(); i++,nbOfModules++) {
		LocaleNode * calNode = getChildAt (i);
		str = calNode->getAttr() -> getValueByIndex(0);
		of.writeParameter("Transliteration", str, nbOfModules);
	}
	of.writeAsciiString("static const sal_Int16 nbOfTransliterations = ");
	of.writeInt(nbOfModules);
	of.writeAsciiString(";\n\n");

	of.writeAsciiString("\nstatic const sal_Unicode* LCTransliterationsArray[] = {\n");
	for( i = 0; i < nbOfModules; i++) {
		of.writeAsciiString("\tTransliteration");
		of.writeInt(i);
		of.writeAsciiString(",\n");
	}
	of.writeAsciiString("};\n\n");
	of.writeFunction("getTransliterations_", "nbOfTransliterations", "LCTransliterationsArray");
}

struct NameValuePair {
	const sal_Char *name;
	const sal_Char *value;
};
static NameValuePair ReserveWord[] = {
	{ "trueWord", "true" },
	{ "falseWord", "false" },
	{ "quarter1Word", "1st quarter" },
	{ "quarter2Word", "2nd quarter" },
	{ "quarter3Word", "3rd quarter" },
	{ "quarter4Word", "4th quarter" },
	{ "aboveWord", "above" },
	{ "belowWord", "below" },
	{ "quarter1Abbreviation", "Q1" },
	{ "quarter2Abbreviation", "Q2" },
	{ "quarter3Abbreviation", "Q3" },
	{ "quarter4Abbreviation", "Q4" }
};

void LCMiscNode::generateCode (const OFileWriter &of) const
{
    ::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
    if (useLocale.getLength() > 0) {
	of.writeRefFunction("getForbiddenCharacters_", useLocale);
	of.writeRefFunction("getBreakIteratorRules_", useLocale);
	of.writeRefFunction("getReservedWords_", useLocale);
	return;
    }
    const LocaleNode * reserveNode = findNode("ReservedWords");
    if (!reserveNode)
        incError( "No ReservedWords element."); // should not happen if validated..
    const LocaleNode * forbidNode = findNode("ForbiddenCharacters");
    const LocaleNode * breakNode = findNode("BreakIteratorRules");

    bool bEnglishLocale = (strncmp( of.getLocale(), "en_", 3) == 0);

    sal_Int16 nbOfWords = 0;
	::rtl::OUString str;
	sal_Int16 i;

	for ( i = 0; i < sal_Int16(sizeof(ReserveWord)/sizeof(ReserveWord[0])); i++,nbOfWords++) {
        const LocaleNode * curNode = (reserveNode ? reserveNode->findNode(
                    ReserveWord[i].name) : 0);
        if (!curNode)
            fprintf( stderr,
                    "Warning: No %s in ReservedWords, using en_US default: \"%s\".\n",
                    ReserveWord[i].name, ReserveWord[i].value);
        str = curNode ? curNode -> getValue() : OUString::createFromAscii(ReserveWord[i].value);
        if (!str.getLength())
        {
            ++nError;
            fprintf( stderr, "Error: No content for ReservedWords %s.\n", ReserveWord[i].name);
        }
        of.writeParameter("ReservedWord", str, nbOfWords);
        // "true", ..., "below" trigger untranslated warning.
        if (!bEnglishLocale && curNode && (0 <= i && i <= 7) &&
                str.equalsIgnoreAsciiCaseAscii( ReserveWord[i].value))
        {
            fprintf( stderr,
                    "Warning: ReservedWord %s seems to be untranslated \"%s\".\n",
                    ReserveWord[i].name, ReserveWord[i].value);
        }
    }
    of.writeAsciiString("static const sal_Int16 nbOfReservedWords = ");
	of.writeInt(nbOfWords);
	of.writeAsciiString(";\n\n");
	of.writeAsciiString("\nstatic const sal_Unicode* LCReservedWordsArray[] = {\n");
	for( i = 0; i < nbOfWords; i++) {
		of.writeAsciiString("\tReservedWord");
		of.writeInt(i);
		of.writeAsciiString(",\n");
	}
	of.writeAsciiString("};\n\n");
	of.writeFunction("getReservedWords_", "nbOfReservedWords", "LCReservedWordsArray");

    if (forbidNode)    {
     	of.writeParameter( "forbiddenBegin", forbidNode -> getChildAt(0)->getValue());
     	of.writeParameter( "forbiddenEnd", forbidNode -> getChildAt(1)->getValue());
     	of.writeParameter( "hangingChars", forbidNode -> getChildAt(2)->getValue());
    } else {
     	of.writeParameter( "forbiddenBegin", ::rtl::OUString());
     	of.writeParameter( "forbiddenEnd", ::rtl::OUString());
     	of.writeParameter( "hangingChars", ::rtl::OUString());
    }
	of.writeAsciiString("\nstatic const sal_Unicode* LCForbiddenCharactersArray[] = {\n");
	of.writeAsciiString("\tforbiddenBegin,\n");
	of.writeAsciiString("\tforbiddenEnd,\n");
	of.writeAsciiString("\thangingChars\n");
	of.writeAsciiString("};\n\n");
	of.writeFunction("getForbiddenCharacters_", "3", "LCForbiddenCharactersArray");

    if (breakNode) {
     	of.writeParameter( "EditMode", breakNode -> getChildAt(0)->getValue());
     	of.writeParameter( "DictionaryMode", breakNode -> getChildAt(1)->getValue());
     	of.writeParameter( "WordCountMode", breakNode -> getChildAt(2)->getValue());
     	of.writeParameter( "CharacterMode", breakNode -> getChildAt(3)->getValue());
     	of.writeParameter( "LineMode", breakNode -> getChildAt(4)->getValue());
    } else {
     	of.writeParameter( "EditMode", ::rtl::OUString());
     	of.writeParameter( "DictionaryMode", ::rtl::OUString());
     	of.writeParameter( "WordCountMode", ::rtl::OUString());
     	of.writeParameter( "CharacterMode", ::rtl::OUString());
     	of.writeParameter( "LineMode", ::rtl::OUString());
    }
	of.writeAsciiString("\nstatic const sal_Unicode* LCBreakIteratorRulesArray[] = {\n");
	of.writeAsciiString("\tEditMode,\n");
	of.writeAsciiString("\tDictionaryMode,\n");
	of.writeAsciiString("\tWordCountMode,\n");
	of.writeAsciiString("\tCharacterMode,\n");
	of.writeAsciiString("\tLineMode\n");
	of.writeAsciiString("};\n\n");
	of.writeFunction("getBreakIteratorRules_", "5", "LCBreakIteratorRulesArray");

}

void LCNumberingLevelNode::generateCode (const OFileWriter &of) const
{
     of.writeAsciiString("// ---> ContinuousNumbering\n");
    ::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
    if (useLocale.getLength() > 0) {
	of.writeRefFunction2("getContinuousNumberingLevels_", useLocale);
	return;
    }

     // hard code number of attributes per style.
     const int   nAttributes = 5;
     const char* attr[ nAttributes ] = { "Prefix", "NumType", "Suffix", "Transliteration", "NatNum" };

     // record each attribute of each style in a static C++ variable.
     // determine number of styles on the fly.
     sal_Int32 nStyles = getNumberOfChildren();
     sal_Int32 i;

     for( i = 0; i < nStyles; i++ )
     {
          const Attr* q = getChildAt( i )->getAttr();
          for( sal_Int32 j=0; j<nAttributes; j++ )
          {
               const char* name = attr[j];
               OUString   value = q->getValueByName( name );
               of.writeParameter("continuous", name, value, sal::static_int_cast<sal_Int16>(i) );
          }
     }

     // record number of styles and attributes.
     of.writeAsciiString("static const sal_Int16 continuousNbOfStyles = ");
     of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
     of.writeAsciiString(";\n\n");
     of.writeAsciiString("static const sal_Int16 continuousNbOfAttributesPerStyle = ");
     of.writeInt( nAttributes );
     of.writeAsciiString(";\n\n");

     // generate code. (intermediate arrays)
     for( i=0; i<nStyles; i++ )
     {
          of.writeAsciiString("\nstatic const sal_Unicode* continuousStyle" );
          of.writeInt( sal::static_int_cast<sal_Int16>(i) );
          of.writeAsciiString("[] = {\n");
          for( sal_Int32 j=0; j<nAttributes; j++)
          {
               of.writeAsciiString("\t");
               of.writeAsciiString( "continuous" );
               of.writeAsciiString( attr[j] );
               of.writeInt(sal::static_int_cast<sal_Int16>(i));
               of.writeAsciiString(",\n");
          }
          of.writeAsciiString("\t0\n};\n\n");
     }

     // generate code. (top-level array)
     of.writeAsciiString("\n");
     of.writeAsciiString("static const sal_Unicode** LCContinuousNumberingLevelsArray[] = {\n" );
     for( i=0; i<nStyles; i++ )
     {
          of.writeAsciiString( "\t" );
          of.writeAsciiString( "continuousStyle" );
          of.writeInt( sal::static_int_cast<sal_Int16>(i) );
          of.writeAsciiString( ",\n");
     }
     of.writeAsciiString("\t0\n};\n\n");
     of.writeFunction2("getContinuousNumberingLevels_", "continuousNbOfStyles",
			"continuousNbOfAttributesPerStyle", "LCContinuousNumberingLevelsArray");
}


void LCOutlineNumberingLevelNode::generateCode (const OFileWriter &of) const
{
     of.writeAsciiString("// ---> OutlineNumbering\n");
    ::rtl::OUString useLocale =   getAttr() -> getValueByName("ref");
    if (useLocale.getLength() > 0) {
	of.writeRefFunction3("getOutlineNumberingLevels_", useLocale);
	return;
    }

     // hardcode number of attributes per level
     const int   nAttributes = 11;
     const char* attr[ nAttributes ] =
     {
          "Prefix",
          "NumType",
          "Suffix",
          "BulletChar",
          "BulletFontName",
          "ParentNumbering",
          "LeftMargin",
          "SymbolTextDistance",
          "FirstLineOffset",
          "Transliteration",
          "NatNum",
     };

     // record each attribute of each level of each style in a static C++ variable.
     // determine number of styles and number of levels per style on the fly.
     sal_Int32 nStyles = getNumberOfChildren();
     vector<sal_Int32> nLevels; // may be different for each style?
     for( sal_Int32 i = 0; i < nStyles; i++ )
     {
          LocaleNode* p = getChildAt( i );
          nLevels.push_back( p->getNumberOfChildren() );
          for( sal_Int32 j=0; j<nLevels.back(); j++ )
          {
               const Attr* q = p->getChildAt( j )->getAttr();
               for( sal_Int32 k=0; k<nAttributes; k++ )
               {
                    const char* name = attr[k];
                    OUString   value = q->getValueByName( name );
                    of.writeParameter("outline", name, value,
                                        sal::static_int_cast<sal_Int16>(i),
                                        sal::static_int_cast<sal_Int16>(j) );
               }
          }
     }

     // verify that each style has the same number of levels.
     for( size_t i=0; i<nLevels.size(); i++ )
     {
          if( nLevels[0] != nLevels[i] )
          {
               incError( "Numbering levels don't match.");
          }
     }

     // record number of attributes, levels, and styles.
     of.writeAsciiString("static const sal_Int16 outlineNbOfStyles = ");
     of.writeInt( sal::static_int_cast<sal_Int16>( nStyles ) );
     of.writeAsciiString(";\n\n");
     of.writeAsciiString("static const sal_Int16 outlineNbOfLevelsPerStyle = ");
     of.writeInt( sal::static_int_cast<sal_Int16>( nLevels.back() ) );
     of.writeAsciiString(";\n\n");
     of.writeAsciiString("static const sal_Int16 outlineNbOfAttributesPerLevel = ");
     of.writeInt( nAttributes );
     of.writeAsciiString(";\n\n");

     // too complicated for now...
//     of.writeAsciiString("static const sal_Int16 nbOfOutlineNumberingLevels[] = { ");
//     for( sal_Int32 j=0; j<nStyles; j++ )
//     {
//          of.writeInt( nLevels[j] );
//          of.writeAsciiString(", ");
//     }
//     of.writeAsciiString("};\n\n");


     for( sal_Int32 i=0; i<nStyles; i++ )
     {
          for( sal_Int32 j=0; j<nLevels.back(); j++ )
          {
               of.writeAsciiString("static const sal_Unicode* outline");
               of.writeAsciiString("Style");
               of.writeInt( sal::static_int_cast<sal_Int16>(i) );
               of.writeAsciiString("Level");
               of.writeInt( sal::static_int_cast<sal_Int16>(j) );
               of.writeAsciiString("[] = { ");

               for( sal_Int32 k=0; k<nAttributes; k++ )
               {
                    of.writeAsciiString( "outline" );
                    of.writeAsciiString( attr[k] );
                    of.writeInt( sal::static_int_cast<sal_Int16>(i) );
                    of.writeInt( sal::static_int_cast<sal_Int16>(j) );
                    of.writeAsciiString(", ");
               }
               of.writeAsciiString("NULL };\n");
          }
     }

     of.writeAsciiString("\n");


     for( sal_Int32 i=0; i<nStyles; i++ )
     {
          of.writeAsciiString("static const sal_Unicode** outline");
          of.writeAsciiString( "Style" );
          of.writeInt( sal::static_int_cast<sal_Int16>(i) );
          of.writeAsciiString("[] = { ");

          for( sal_Int32 j=0; j<nLevels.back(); j++ )
          {
               of.writeAsciiString("outlineStyle");
               of.writeInt( sal::static_int_cast<sal_Int16>(i) );
               of.writeAsciiString("Level");
               of.writeInt( sal::static_int_cast<sal_Int16>(j) );
               of.writeAsciiString(", ");
          }
          of.writeAsciiString("NULL };\n");
     }
     of.writeAsciiString("\n");

     of.writeAsciiString("static const sal_Unicode*** LCOutlineNumberingLevelsArray[] = {\n" );
     for( sal_Int32 i=0; i<nStyles; i++ )
     {
          of.writeAsciiString( "\t" );
          of.writeAsciiString( "outlineStyle" );
          of.writeInt( sal::static_int_cast<sal_Int16>(i) );
          of.writeAsciiString(",\n");
     }
     of.writeAsciiString("\tNULL\n};\n\n");
     of.writeFunction3("getOutlineNumberingLevels_", "outlineNbOfStyles", "outlineNbOfLevelsPerStyle",
			"outlineNbOfAttributesPerLevel", "LCOutlineNumberingLevelsArray");
}

Attr::Attr (const Reference< XAttributeList > & attr) {
	sal_Int16 len = attr->getLength();
	name.realloc (len);
	value.realloc (len);
	for (sal_Int16 i =0; i< len;i++) {
		name[i] = attr->getNameByIndex(i);
		value[i] = attr -> getValueByIndex(i);
	}
}

const OUString& Attr::getValueByName (const sal_Char *str) const {
    static OUString empty;
	sal_Int32 len = name.getLength();
	for (sal_Int32 i = 0;i<len;i++)
		if (name[i].equalsAscii(str))
			return value[i];
	return empty;
}

sal_Int32 Attr::getLength() const{
	return name.getLength();
}

const OUString& Attr::getTypeByIndex (sal_Int32 idx) const {
	return name[idx];
}

const OUString& Attr::getValueByIndex (sal_Int32 idx) const
{
	return value[idx];
}
