text:
#include "ogr_xlsx.h"
#include "ogr_p.h"
#include "cpl_conv.h"
#include "cpl_time.h"

CPL_CVSID("$Id: ogrxlsxdatasource.cpp 33437 2016-02-12 11:02:55Z rouault $");

namespace OGRXLSX {

/************************************************************************/
/*                            OGRXLSXLayer()                            */
/************************************************************************/

OGRXLSXLayer::OGRXLSXLayer( OGRXLSXDataSource* poDSIn,
                            int nSheetIdIn,
                            const char * pszName,
                            int bUpdatedIn) :
                                OGRMemLayer(pszName, NULL, wkbNone)
{
    bInit = FALSE;
    nSheetId = nSheetIdIn;
    poDS = poDSIn;
    bUpdated = bUpdatedIn;
    bHasHeaderLine = FALSE;
}

/************************************************************************/
/*                              Init()                                  */
/************************************************************************/

void OGRXLSXLayer::Init()
{
    if (!bInit)
    {
        bInit = TRUE;
        CPLDebug("XLSX", "Init(%s)", GetName());
        poDS->BuildLayer(this, nSheetId);
    }
}

/************************************************************************/
/*                             Updated()                                */
/************************************************************************/

void OGRXLSXLayer::SetUpdated(int bUpdatedIn)
{
    if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
    {
        bUpdated = TRUE;
        poDS->SetUpdated();
    }
    else if (bUpdated && !bUpdatedIn)
    {
        bUpdated = FALSE;
    }
}

/************************************************************************/
/*                           SyncToDisk()                               */
/************************************************************************/

OGRErr OGRXLSXLayer::SyncToDisk()
{
    poDS->FlushCache();
    return OGRERR_NONE;
}

/************************************************************************/
/*                          GetNextFeature()                            */
/************************************************************************/

OGRFeature* OGRXLSXLayer::GetNextFeature()
{
    Init();
    OGRFeature* poFeature = OGRMemLayer::GetNextFeature();
    if (poFeature)
        poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine);
    return poFeature;
}

/************************************************************************/
/*                           GetFeature()                               */
/************************************************************************/

OGRFeature* OGRXLSXLayer::GetFeature( GIntBig nFeatureId )
{
    Init();
    OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine));
    if (poFeature)
        poFeature->SetFID(nFeatureId);
    return poFeature;
}

/************************************************************************/
/*                           ISetFeature()                               */
/************************************************************************/

OGRErr OGRXLSXLayer::ISetFeature( OGRFeature *poFeature )
{
    Init();
    if (poFeature == NULL)
        return OGRMemLayer::ISetFeature(poFeature);

    GIntBig nFID = poFeature->GetFID();
    if (nFID != OGRNullFID)
        poFeature->SetFID(nFID - (1 + bHasHeaderLine));
    SetUpdated();
    OGRErr eErr = OGRMemLayer::ISetFeature(poFeature);
    poFeature->SetFID(nFID);
    return eErr;
}

/************************************************************************/
/*                          DeleteFeature()                             */
/************************************************************************/

OGRErr OGRXLSXLayer::DeleteFeature( GIntBig nFID )
{
    Init();
    SetUpdated();
    return OGRMemLayer::DeleteFeature(nFID - (1 + bHasHeaderLine));
}

/************************************************************************/
/*                          OGRXLSXDataSource()                         */
/************************************************************************/

OGRXLSXDataSource::OGRXLSXDataSource()

{
    pszName = NULL;
    bUpdatable = FALSE;
    bUpdated = FALSE;

    nLayers = 0;
    papoLayers = NULL;

    bFirstLineIsHeaders = FALSE;

    oParser = NULL;
    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nDataHandlerCounter = 0;
    nStackDepth = 0;
    nDepth = 0;
    nCurLine = 0;
    nCurCol = 0;
    stateStack[0].eVal = STATE_DEFAULT;
    stateStack[0].nBeginDepth = 0;
    bInCellXFS = FALSE;

    poCurLayer = NULL;

    const char* pszXLSXFieldTypes =
                CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "");
    bAutodetectTypes = !EQUAL(pszXLSXFieldTypes, "STRING");
}

/************************************************************************/
/*                         ~OGRXLSXDataSource()                          */
/************************************************************************/

OGRXLSXDataSource::~OGRXLSXDataSource()

{
    FlushCache();

    CPLFree( pszName );

    for(int i=0;i<nLayers;i++)
        delete papoLayers[i];
    CPLFree( papoLayers );
}

/************************************************************************/
/*                           TestCapability()                           */
/************************************************************************/

int OGRXLSXDataSource::TestCapability( const char * pszCap )

{
    if( EQUAL(pszCap,ODsCCreateLayer) )
        return bUpdatable;
    else if( EQUAL(pszCap,ODsCDeleteLayer) )
        return bUpdatable;
    else
        return FALSE;
}

/************************************************************************/
/*                              GetLayer()                              */
/************************************************************************/

OGRLayer *OGRXLSXDataSource::GetLayer( int iLayer )

{
    if (iLayer < 0 || iLayer >= nLayers)
        return NULL;

    return papoLayers[iLayer];
}

/************************************************************************/
/*                            GetLayerCount()                           */
/************************************************************************/

int OGRXLSXDataSource::GetLayerCount()
{
    return nLayers;
}

/************************************************************************/
/*                                Open()                                */
/************************************************************************/

int OGRXLSXDataSource::Open( const char * pszFilename,
                             VSILFILE* fpWorkbook,
                             VSILFILE* fpSharedStrings,
                             VSILFILE* fpStyles,
                             int bUpdateIn )

{
    bUpdatable = bUpdateIn;

    pszName = CPLStrdup( pszFilename );

    AnalyseWorkbook(fpWorkbook);
    AnalyseSharedStrings(fpSharedStrings);
    AnalyseStyles(fpStyles);

    /* Remove empty layers at the end, which tend to be there */
    while(nLayers > 1)
    {
        if (papoLayers[nLayers-1]->GetFeatureCount() == 0)
        {
            delete papoLayers[nLayers-1];
            nLayers --;
        }
        else
            break;
    }

    return TRUE;
}

/************************************************************************/
/*                             Create()                                 */
/************************************************************************/

int OGRXLSXDataSource::Create( const char * pszFilename,
                               CPL_UNUSED char **papszOptions )
{
    bUpdated = TRUE;
    bUpdatable = TRUE;

    pszName = CPLStrdup( pszFilename );

    return TRUE;
}

/************************************************************************/
/*                           startElementCbk()                          */
/************************************************************************/

static void XMLCALL startElementCbk(void *pUserData, const char *pszNameIn,
                                    const char **ppszAttr)
{
    ((OGRXLSXDataSource*)pUserData)->startElementCbk(pszNameIn, ppszAttr);
}

void OGRXLSXDataSource::startElementCbk(const char *pszNameIn,
                                       const char **ppszAttr)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;
    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT: startElementDefault(pszNameIn, ppszAttr); break;
        case STATE_SHEETDATA:   startElementTable(pszNameIn, ppszAttr); break;
        case STATE_ROW:     startElementRow(pszNameIn, ppszAttr); break;
        case STATE_CELL:    startElementCell(pszNameIn, ppszAttr); break;
        case STATE_TEXTV:   break;
        default:            break;
    }
    nDepth++;
}

/************************************************************************/
/*                            endElementCbk()                           */
/************************************************************************/

static void XMLCALL endElementCbk(void *pUserData, const char *pszNameIn)
{
    ((OGRXLSXDataSource*)pUserData)->endElementCbk(pszNameIn);
}

void OGRXLSXDataSource::endElementCbk(const char *pszNameIn)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;

    nDepth--;
    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT: break;
        case STATE_SHEETDATA:   endElementTable(pszNameIn); break;
        case STATE_ROW:     endElementRow(pszNameIn); break;
        case STATE_CELL:    endElementCell(pszNameIn); break;
        case STATE_TEXTV:   break;
        default:            break;
    }

    if (stateStack[nStackDepth].nBeginDepth == nDepth)
        nStackDepth --;
}

/************************************************************************/
/*                            dataHandlerCbk()                          */
/************************************************************************/

static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
{
    ((OGRXLSXDataSource*)pUserData)->dataHandlerCbk(data, nLen);
}

void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
{
    if (bStopParsing) return;

    nDataHandlerCounter ++;
    if (nDataHandlerCounter >= BUFSIZ)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "File probably corrupted (million laugh pattern)");
        XML_StopParser(oParser, XML_FALSE);
        bStopParsing = TRUE;
        return;
    }

    nWithoutEventCounter = 0;

    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT: break;
        case STATE_SHEETDATA:   break;
        case STATE_ROW:     break;
        case STATE_CELL:    break;
        case STATE_TEXTV:   dataHandlerTextV(data, nLen);
        default:            break;
    }
}

/************************************************************************/
/*                                PushState()                           */
/************************************************************************/

void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
{
    if (nStackDepth + 1 == STACK_SIZE)
    {
        bStopParsing = TRUE;
        return;
    }
    nStackDepth ++;
    stateStack[nStackDepth].eVal = eVal;
    stateStack[nStackDepth].nBeginDepth = nDepth;
}

/************************************************************************/
/*                          GetAttributeValue()                         */
/************************************************************************/

static const char* GetAttributeValue(const char **ppszAttr,
                                     const char* pszKey,
                                     const char* pszDefaultVal)
{
    while(*ppszAttr)
    {
        if (strcmp(ppszAttr[0], pszKey) == 0)
            return ppszAttr[1];
        ppszAttr += 2;
    }
    return pszDefaultVal;
}

/************************************************************************/
/*                            GetOGRFieldType()                         */
/************************************************************************/

OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char* pszValue,
                                                const char* pszValueType)
{
    if (!bAutodetectTypes || pszValueType == NULL)
        return OFTString;
    else if (strcmp(pszValueType, "string") == 0)
        return OFTString;
    else if (strcmp(pszValueType, "float") == 0)
    {
        CPLValueType eValueType = CPLGetValueType(pszValue);
        if (eValueType == CPL_VALUE_STRING)
            return OFTString;
        else if (eValueType == CPL_VALUE_INTEGER)
        {
            GIntBig nVal = CPLAtoGIntBig(pszValue);
            if( !CPL_INT64_FITS_ON_INT32(nVal) )
                return OFTInteger64;
            else
                return OFTInteger;
        }
        else
            return OFTReal;
    }
    else if (strcmp(pszValueType, "datetime") == 0 ||
             strcmp(pszValueType, "datetime_ms") == 0)
    {
        return OFTDateTime;
    }
    else if (strcmp(pszValueType, "date") == 0)
    {
        return OFTDate;
    }
    else if (strcmp(pszValueType, "time") == 0)
    {
        return OFTTime;
    }
    else
        return OFTString;
}

/************************************************************************/
/*                              SetField()                              */
/************************************************************************/

static void SetField(OGRFeature* poFeature,
                     int i,
                     const char* pszValue,
                     const char* pszCellType)
{
    if (pszValue[0] == '\0')
        return;

    OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();

    if (strcmp(pszCellType, "time") == 0 ||
        strcmp(pszCellType, "date") == 0 ||
        strcmp(pszCellType, "datetime") == 0 ||
        strcmp(pszCellType, "datetime_ms") == 0)
    {
        struct tm sTm;
        double dfNumberOfDaysSince1900 = CPLAtof(pszValue);
#define NUMBER_OF_DAYS_BETWEEN_1900_AND_1970        25569
#define NUMBER_OF_SECONDS_PER_DAY                   86400
        GIntBig nUnixTime = (GIntBig)((dfNumberOfDaysSince1900 -
                                       NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 )*
                                                NUMBER_OF_SECONDS_PER_DAY);
        CPLUnixTimeToYMDHMS(nUnixTime, &sTm);

        if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
        {
            double fFracSec = fmod(fmod(dfNumberOfDaysSince1900,1) * 3600 * 24, 1);
            poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
                                sTm.tm_hour, sTm.tm_min,
                                static_cast<float>(sTm.tm_sec + fFracSec), 0 );
        }
        else if (strcmp(pszCellType, "time") == 0)
        {
            poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d",
                                sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
        }
        else if (strcmp(pszCellType, "date") == 0)
        {
            poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d",
                                sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday));
        }
        else /* if (strcmp(pszCellType, "datetime") == 0) */
        {
            double fFracSec = fmod(fmod(dfNumberOfDaysSince1900,1) * 3600 * 24, 1);
            poFeature->SetField(i,
                                sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
                                sTm.tm_hour, sTm.tm_min,
                                static_cast<float>(sTm.tm_sec + fFracSec), 0);
        }
    }
    else
        poFeature->SetField(i, pszValue);
}

/************************************************************************/
/*                          DetectHeaderLine()                          */
/************************************************************************/

void OGRXLSXDataSource::DetectHeaderLine()

{
    int bHeaderLineCandidate = TRUE;
    size_t i;
    for(i = 0; i < apoFirstLineTypes.size(); i++)
    {
        if (apoFirstLineTypes[i] != "string")
        {
            /* If the values in the first line are not text, then it is */
            /* not a header line */
            bHeaderLineCandidate = FALSE;
            break;
        }
    }

    size_t nCountTextOnCurLine = 0;
    size_t nCountNonEmptyOnCurLine = 0;
    for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
    {
        if (apoCurLineTypes[i] == "string")
        {
            /* If there are only text values on the second line, then we cannot */
            /* know if it is a header line or just a regular line */
            nCountTextOnCurLine ++;
        }
        else if (apoCurLineTypes[i] != "")
        {
            nCountNonEmptyOnCurLine ++;
        }
    }

    const char* pszXLSXHeaders = CPLGetConfigOption("OGR_XLSX_HEADERS", "");
    bFirstLineIsHeaders = FALSE;
    if (EQUAL(pszXLSXHeaders, "FORCE"))
        bFirstLineIsHeaders = TRUE;
    else if (EQUAL(pszXLSXHeaders, "DISABLE"))
        bFirstLineIsHeaders = FALSE;
    else if (bHeaderLineCandidate &&
             apoFirstLineTypes.size() != 0 &&
             apoFirstLineTypes.size() == apoCurLineTypes.size() &&
             nCountTextOnCurLine != apoFirstLineTypes.size() &&
             nCountNonEmptyOnCurLine != 0)
    {
        bFirstLineIsHeaders = TRUE;
    }
    CPLDebug("XLSX", "%s %s",
             poCurLayer->GetName(),
             bFirstLineIsHeaders ? "has header line" : "has no header line");
}

/************************************************************************/
/*                          startElementDefault()                       */
/************************************************************************/

void OGRXLSXDataSource::startElementDefault(const char *pszNameIn,
                                            CPL_UNUSED const char **ppszAttr)
{
    if (strcmp(pszNameIn, "sheetData") == 0)
    {
        apoFirstLineValues.resize(0);
        apoFirstLineTypes.resize(0);
        nCurLine = 0;
        PushState(STATE_SHEETDATA);
    }
}

/************************************************************************/
/*                          startElementTable()                        */
/************************************************************************/

void OGRXLSXDataSource::startElementTable(const char *pszNameIn,
                                         const char **ppszAttr)
{
    if (strcmp(pszNameIn, "row") == 0)
    {
        PushState(STATE_ROW);

        int nNewCurLine = atoi(
            GetAttributeValue(ppszAttr, "r", "0")) - 1;
        for(;nCurLine<nNewCurLine;)
        {
            nCurCol = 0;
            apoCurLineValues.resize(0);
            apoCurLineTypes.resize(0);
            endElementRow("row");
        }
        nCurCol = 0;
        apoCurLineValues.resize(0);
        apoCurLineTypes.resize(0);
    }
}

/************************************************************************/
/*                           endElementTable()                          */
/************************************************************************/

void OGRXLSXDataSource::endElementTable(CPL_UNUSED const char *pszNameIn)
{
    if (stateStack[nStackDepth].nBeginDepth == nDepth)
    {
        CPLAssert(strcmp(pszNameIn, "sheetData") == 0);

        if (nCurLine == 0 ||
            (nCurLine == 1 && apoFirstLineValues.size() == 0))
        {
            /* We could remove empty sheet, but too late now */
        }
        else if (nCurLine == 1)
        {
            /* If we have only one single line in the sheet */
            size_t i;
            for(i = 0; i < apoFirstLineValues.size(); i++)
            {
                const char* pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
                OGRFieldType eType = GetOGRFieldType(apoFirstLineValues[i].c_str(),
                                                     apoFirstLineTypes[i].c_str());
                OGRFieldDefn oFieldDefn(pszFieldName, eType);
                poCurLayer->CreateField(&oFieldDefn);
            }

            OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
            for(i = 0; i < apoFirstLineValues.size(); i++)
            {
                SetField(poFeature, static_cast<int>(i), apoFirstLineValues[i].c_str(),
                         apoFirstLineTypes[i].c_str());
            }
            CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
            delete poFeature;
        }

        if (poCurLayer)
        {
            ((OGRMemLayer*)poCurLayer)->SetUpdatable(CPL_TO_BOOL(bUpdatable));
            ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE);
            ((OGRXLSXLayer*)poCurLayer)->SetUpdated(FALSE);
        }

        poCurLayer = NULL;
    }
}

/************************************************************************/
/*                            startElementRow()                         */
/************************************************************************/

void OGRXLSXDataSource::startElementRow(const char *pszNameIn,
                                       const char **ppszAttr)
{
    if (strcmp(pszNameIn, "c") == 0)
    {
        PushState(STATE_CELL);

        const char* pszR = GetAttributeValue(ppszAttr, "r", NULL);
        if (pszR)
        {
            /* Convert col number from base 26 */
            /*
            A Z   AA AZ   BA BZ   ZA   ZZ   AAA    ZZZ      AAAA
            0 25  26 51   52 77   676  701  702    18277    18278
            */
            int nNewCurCol = (pszR[0] - 'A');
            int i = 1;
            while(pszR[i] >= 'A' && pszR[i] <= 'Z')
            {
                // We wouldn't need the +1 if this was a proper base 26
                nNewCurCol = (nNewCurCol + 1) * 26 + (pszR[i] - 'A');
                i ++;
            }
            for(;nCurCol<nNewCurCol;nCurCol++)
            {
                apoCurLineValues.push_back("");
                apoCurLineTypes.push_back("");
            }
        }

        osValueType = "float";

        const char* pszS = GetAttributeValue(ppszAttr, "s", "-1");
        int nS = atoi(pszS);
        if (nS >= 0 && nS < (int)apoStyles.size())
        {
            XLSXFieldTypeExtended eType = apoStyles[nS];
            if (eType.eType == OFTDateTime)
            {
                if( eType.bHasMS )
                    osValueType = "datetime_ms";
                else
                    osValueType = "datetime";
            }
            else if (eType.eType == OFTDate)
                osValueType = "date";
            else if (eType.eType == OFTTime)
                osValueType = "time";
        }
        else if (nS != -1)
            CPLDebug("XLSX", "Cannot find style %d", nS);

        const char* pszT = GetAttributeValue(ppszAttr, "t", "");
        if ( EQUAL(pszT,"s"))
            osValueType = "stringLookup";
        else if( EQUAL(pszT,"inlineStr") )
            osValueType = "string";

        osValue = "";
    }
}

/************************************************************************/
/*                            endElementRow()                           */
/************************************************************************/

void OGRXLSXDataSource::endElementRow(CPL_UNUSED const char *pszNameIn)
{
    if (stateStack[nStackDepth].nBeginDepth == nDepth)
    {
        CPLAssert(strcmp(pszNameIn, "row") == 0);

        OGRFeature* poFeature;
        size_t i;

        /* Backup first line values and types in special arrays */
        if (nCurLine == 0)
        {
            apoFirstLineTypes = apoCurLineTypes;
            apoFirstLineValues = apoCurLineValues;

    #if skip_leading_empty_rows
            if (apoFirstLineTypes.size() == 0)
            {
                /* Skip leading empty rows */
                apoFirstLineTypes.resize(0);
                apoFirstLineValues.resize(0);
                return;
            }
    #endif
        }

        if (nCurLine == 1)
        {
            DetectHeaderLine();

            poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);

            if (bFirstLineIsHeaders)
            {
                for(i = 0; i < apoFirstLineValues.size(); i++)
                {
                    const char* pszFieldName = apoFirstLineValues[i].c_str();
                    if (pszFieldName[0] == '\0')
                        pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
                    OGRFieldType eType = OFTString;
                    if (i < apoCurLineValues.size())
                    {
                        eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
                                                apoCurLineTypes[i].c_str());
                    }
                    OGRFieldDefn oFieldDefn(pszFieldName, eType);
                    poCurLayer->CreateField(&oFieldDefn);
                }
            }
            else
            {
                for(i = 0; i < apoFirstLineValues.size(); i++)
                {
                    const char* pszFieldName =
                        CPLSPrintf("Field%d", (int)i + 1);
                    OGRFieldType eType = GetOGRFieldType(
                                            apoFirstLineValues[i].c_str(),
                                            apoFirstLineTypes[i].c_str());
                    OGRFieldDefn oFieldDefn(pszFieldName, eType);
                    poCurLayer->CreateField(&oFieldDefn);
                }

                poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
                for(i = 0; i < apoFirstLineValues.size(); i++)
                {
                    SetField(poFeature, static_cast<int>(i), apoFirstLineValues[i].c_str(),
                             apoFirstLineTypes[i].c_str());
                }
                CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
                delete poFeature;
            }
        }

        if (nCurLine >= 1)
        {
            /* Add new fields found on following lines. */
            if (apoCurLineValues.size() >
                (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
            {
                for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
                    i < apoCurLineValues.size();
                    i++)
                {
                    const char* pszFieldName =
                        CPLSPrintf("Field%d", (int)i + 1);
                    OGRFieldType eType = GetOGRFieldType(
                                                apoCurLineValues[i].c_str(),
                                                apoCurLineTypes[i].c_str());
                    OGRFieldDefn oFieldDefn(pszFieldName, eType);
                    poCurLayer->CreateField(&oFieldDefn);
                }
            }

            /* Update field type if necessary */
            if (bAutodetectTypes)
            {
                for(i = 0; i < apoCurLineValues.size(); i++)
                {
                    if (apoCurLineValues[i].size())
                    {
                        OGRFieldType eValType = GetOGRFieldType(
                                                apoCurLineValues[i].c_str(),
                                                apoCurLineTypes[i].c_str());
                        OGRFieldType eFieldType =
                            poCurLayer->GetLayerDefn()->GetFieldDefn(static_cast<int>(i))->GetType();
                        if (eFieldType == OFTDateTime &&
                            (eValType == OFTDate || eValType == OFTTime) )
                        {
                            /* ok */
                        }
                        else if (eFieldType == OFTReal && (eValType == OFTInteger || eValType == OFTInteger64))
                        {
                           /* ok */;
                        }
                        else if (eFieldType == OFTInteger64 && eValType == OFTInteger )
                        {
                            /* ok */;
                        }
                        else if (eFieldType != OFTString && eValType != eFieldType)
                        {
                            OGRFieldDefn oNewFieldDefn(
                                poCurLayer->GetLayerDefn()->GetFieldDefn(static_cast<int>(i)));
                            if ((eFieldType == OFTDate || eFieldType == OFTTime) &&
                                   eValType == OFTDateTime)
                                oNewFieldDefn.SetType(OFTDateTime);
                            else if ((eFieldType == OFTInteger || eFieldType == OFTInteger64) &&
                                     eValType == OFTReal)
                                oNewFieldDefn.SetType(OFTReal);
                            else if( eFieldType == OFTInteger && eValType == OFTInteger64 )
                                oNewFieldDefn.SetType(OFTInteger64);
                            else
                                oNewFieldDefn.SetType(OFTString);
                            poCurLayer->AlterFieldDefn(static_cast<int>(i), &oNewFieldDefn,
                                                       ALTER_TYPE_FLAG);
                        }
                    }
                }
            }

            /* Add feature for current line */
            poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
            for(i = 0; i < apoCurLineValues.size(); i++)
            {
                SetField(poFeature, static_cast<int>(i), apoCurLineValues[i].c_str(),
                         apoCurLineTypes[i].c_str());
            }
            CPL_IGNORE_RET_VAL(poCurLayer->CreateFeature(poFeature));
            delete poFeature;
       }

        nCurLine++;
    }
}

/************************************************************************/
/*                           startElementCell()                         */
/************************************************************************/

void OGRXLSXDataSource::startElementCell(const char *pszNameIn,
                                         CPL_UNUSED const char **ppszAttr)
{
    if (osValue.size() == 0 && strcmp(pszNameIn, "v") == 0)
    {
        PushState(STATE_TEXTV);
    }
    else if (osValue.size() == 0 && strcmp(pszNameIn, "t") == 0)
    {
        PushState(STATE_TEXTV);
    }
}

/************************************************************************/
/*                            endElementCell()                          */
/************************************************************************/

void OGRXLSXDataSource::endElementCell(CPL_UNUSED const char *pszNameIn)
{
    if (stateStack[nStackDepth].nBeginDepth == nDepth)
    {
        CPLAssert(strcmp(pszNameIn, "c") == 0);

        if (osValueType == "stringLookup")
        {
            int nIndex = atoi(osValue);
            if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
                osValue = apoSharedStrings[nIndex];
            else
                CPLDebug("XLSX", "Cannot find string %d", nIndex);
            osValueType = "string";
        }

        apoCurLineValues.push_back(osValue);
        apoCurLineTypes.push_back(osValueType);

        nCurCol += 1;
    }
}

/************************************************************************/
/*                           dataHandlerTextV()                         */
/************************************************************************/

void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
{
    osValue.append(data, nLen);
}

/************************************************************************/
/*                              BuildLayer()                            */
/************************************************************************/

void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer* poLayer, int nSheetId)
{
    poCurLayer = poLayer;

    CPLString osSheetFilename(
        CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, nSheetId));
    const char* pszSheetFilename = osSheetFilename.c_str();
    VSILFILE* fp = VSIFOpenL(pszSheetFilename, "rb");
    if (fp == NULL)
        return;

    int bUpdatedBackup = bUpdated;

    oParser = OGRCreateExpatXMLParser();
    XML_SetElementHandler(oParser, OGRXLSX::startElementCbk, OGRXLSX::endElementCbk);
    XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerCbk);
    XML_SetUserData(oParser, this);

    VSIFSeekL( fp, 0, SEEK_SET );

    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nDataHandlerCounter = 0;
    nStackDepth = 0;
    nDepth = 0;
    stateStack[0].eVal = STATE_DEFAULT;
    stateStack[0].nBeginDepth = 0;

    char aBuf[BUFSIZ];
    int nDone;
    do
    {
        nDataHandlerCounter = 0;
        unsigned int nLen =
            (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
        nDone = VSIFEofL(fp);
        if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of %s file failed : %s at line %d, column %d",
                     pszSheetFilename,
                     XML_ErrorString(XML_GetErrorCode(oParser)),
                     (int)XML_GetCurrentLineNumber(oParser),
                     (int)XML_GetCurrentColumnNumber(oParser));
            bStopParsing = TRUE;
        }
        nWithoutEventCounter ++;
    } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);

    XML_ParserFree(oParser);
    oParser = NULL;

    if (nWithoutEventCounter == 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Too much data inside one element. File probably corrupted");
        bStopParsing = TRUE;
    }

    VSIFCloseL(fp);

    bUpdated = bUpdatedBackup;
}

/************************************************************************/
/*                          startElementSSCbk()                         */
/************************************************************************/

static void XMLCALL startElementSSCbk(void *pUserData, const char *pszNameIn,
                                    const char **ppszAttr)
{
    ((OGRXLSXDataSource*)pUserData)->startElementSSCbk(pszNameIn, ppszAttr);
}

void OGRXLSXDataSource::startElementSSCbk(const char *pszNameIn,
                                          CPL_UNUSED const char **ppszAttr)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;
    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT:
        {
            if (strcmp(pszNameIn,"t") == 0)
            {
                PushState(STATE_T);
                osCurrentString = "";
            }
            break;
        }
        default:
            break;
    }
    nDepth++;
}

/************************************************************************/
/*                           endElementSSCbk()                          */
/************************************************************************/

static void XMLCALL endElementSSCbk(void *pUserData, const char *pszNameIn)
{
    ((OGRXLSXDataSource*)pUserData)->endElementSSCbk(pszNameIn);
}

void OGRXLSXDataSource::endElementSSCbk(CPL_UNUSED const char *pszNameIn)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;

    nDepth--;
    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT: break;
        case STATE_T:
        {
            if (stateStack[nStackDepth].nBeginDepth == nDepth)
            {
                apoSharedStrings.push_back(osCurrentString);
            }
            break;
        }
        default:            break;
    }

    if (stateStack[nStackDepth].nBeginDepth == nDepth)
        nStackDepth --;
}

/************************************************************************/
/*                           dataHandlerSSCbk()                         */
/************************************************************************/

static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data, int nLen)
{
    ((OGRXLSXDataSource*)pUserData)->dataHandlerSSCbk(data, nLen);
}

void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
{
    if (bStopParsing) return;

    nDataHandlerCounter ++;
    if (nDataHandlerCounter >= BUFSIZ)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "File probably corrupted (million laugh pattern)");
        XML_StopParser(oParser, XML_FALSE);
        bStopParsing = TRUE;
        return;
    }

    nWithoutEventCounter = 0;

    switch(stateStack[nStackDepth].eVal)
    {
        case STATE_DEFAULT: break;
        case STATE_T:       osCurrentString.append(data, nLen); break;
        default:            break;
    }
}

/************************************************************************/
/*                          AnalyseSharedStrings()                      */
/************************************************************************/

void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE* fpSharedStrings)
{
    if (fpSharedStrings == NULL)
        return;

    oParser = OGRCreateExpatXMLParser();
    XML_SetElementHandler(oParser, OGRXLSX::startElementSSCbk, OGRXLSX::endElementSSCbk);
    XML_SetCharacterDataHandler(oParser, OGRXLSX::dataHandlerSSCbk);
    XML_SetUserData(oParser, this);

    VSIFSeekL( fpSharedStrings, 0, SEEK_SET );

    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nDataHandlerCounter = 0;
    nStackDepth = 0;
    nDepth = 0;
    stateStack[0].eVal = STATE_DEFAULT;
    stateStack[0].nBeginDepth = 0;

    char aBuf[BUFSIZ];
    int nDone;
    do
    {
        nDataHandlerCounter = 0;
        unsigned int nLen =
            (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSharedStrings );
        nDone = VSIFEofL(fpSharedStrings);
        if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of %s file failed : %s at line %d, column %d",
                     "sharedStrings.xml",
                     XML_ErrorString(XML_GetErrorCode(oParser)),
                     (int)XML_GetCurrentLineNumber(oParser),
                     (int)XML_GetCurrentColumnNumber(oParser));
            bStopParsing = TRUE;
        }
        nWithoutEventCounter ++;
    } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);

    XML_ParserFree(oParser);
    oParser = NULL;

    if (nWithoutEventCounter == 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Too much data inside one element. File probably corrupted");
        bStopParsing = TRUE;
    }

    VSIFCloseL(fpSharedStrings);
    fpSharedStrings = NULL;
}

/************************************************************************/
/*                          startElementWBCbk()                         */
/************************************************************************/

static void XMLCALL startElementWBCbk(void *pUserData, const char *pszNameIn,
                                    const char **ppszAttr)
{
    ((OGRXLSXDataSource*)pUserData)->startElementWBCbk(pszNameIn, ppszAttr);
}

void OGRXLSXDataSource::startElementWBCbk(const char *pszNameIn,
                                       const char **ppszAttr)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;
    if (strcmp(pszNameIn,"sheet") == 0)
    {
        const char* pszSheetName = GetAttributeValue(ppszAttr, "name", NULL);
        /*const char* pszSheetId = GetAttributeValue(ppszAttr, "sheetId", NULL);*/
        if (pszSheetName /*&& pszSheetId*/)
        {
            /*int nSheetId = atoi(pszSheetId);*/
            int nSheetId = nLayers + 1;
            papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
            papoLayers[nLayers++] = new OGRXLSXLayer(this, nSheetId, pszSheetName);
        }
    }
}

/************************************************************************/
/*                             AnalyseWorkbook()                        */
/************************************************************************/

void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE* fpWorkbook)
{
    oParser = OGRCreateExpatXMLParser();
    XML_SetElementHandler(oParser, OGRXLSX::startElementWBCbk, NULL);
    XML_SetUserData(oParser, this);

    VSIFSeekL( fpWorkbook, 0, SEEK_SET );

    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nDataHandlerCounter = 0;

    char aBuf[BUFSIZ];
    int nDone;
    do
    {
        nDataHandlerCounter = 0;
        unsigned int nLen =
            (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpWorkbook );
        nDone = VSIFEofL(fpWorkbook);
        if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of %s file failed : %s at line %d, column %d",
                     "workbook.xml",
                     XML_ErrorString(XML_GetErrorCode(oParser)),
                     (int)XML_GetCurrentLineNumber(oParser),
                     (int)XML_GetCurrentColumnNumber(oParser));
            bStopParsing = TRUE;
        }
        nWithoutEventCounter ++;
    } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);

    XML_ParserFree(oParser);
    oParser = NULL;

    if (nWithoutEventCounter == 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Too much data inside one element. File probably corrupted");
        bStopParsing = TRUE;
    }

    VSIFCloseL(fpWorkbook);
}

/************************************************************************/
/*                       startElementStylesCbk()                        */
/************************************************************************/

static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszNameIn,
                                    const char **ppszAttr)
{
    ((OGRXLSXDataSource*)pUserData)->startElementStylesCbk(pszNameIn, ppszAttr);
}

void OGRXLSXDataSource::startElementStylesCbk(const char *pszNameIn,
                                       const char **ppszAttr)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;
    if (strcmp(pszNameIn,"numFmt") == 0)
    {
        const char* pszFormatCode = GetAttributeValue(ppszAttr, "formatCode", NULL);
        const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
        int nNumFmtId = atoi(pszNumFmtId);
        if (pszFormatCode && nNumFmtId >= 164)
        {
            int bHasDate = strstr(pszFormatCode, "DD") != NULL ||
                           strstr(pszFormatCode, "YY") != NULL;
            int bHasTime = strstr(pszFormatCode, "HH") != NULL;
            if (bHasDate && bHasTime)
                apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDateTime,
                                        strstr(pszFormatCode, "SS.000") != NULL );
            else if (bHasDate)
                apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDate);
            else if (bHasTime)
                apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTTime);
            else
                apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTReal);
        }
    }
    else if (strcmp(pszNameIn,"cellXfs") == 0)
    {
        bInCellXFS = TRUE;
    }
    else if (bInCellXFS && strcmp(pszNameIn,"xf") == 0)
    {
        const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
        int nNumFmtId = atoi(pszNumFmtId);
        XLSXFieldTypeExtended eType(OFTReal);
        if (nNumFmtId >= 0)
        {
            if (nNumFmtId < 164)
            {
                // From http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
                if (nNumFmtId >= 14 && nNumFmtId <= 17)
                    eType = XLSXFieldTypeExtended(OFTDate);
                else if (nNumFmtId >= 18 && nNumFmtId <= 21)
                    eType = XLSXFieldTypeExtended(OFTTime);
                else if (nNumFmtId == 22)
                    eType = XLSXFieldTypeExtended(OFTDateTime);
            }
            else
            {
                std::map<int, XLSXFieldTypeExtended>::iterator oIter = apoMapStyleFormats.find(nNumFmtId);
                if (oIter != apoMapStyleFormats.end())
                    eType = oIter->second;
                else
                    CPLDebug("XLSX", "Cannot find entry in <numFmts> with numFmtId=%d", nNumFmtId);
            }
        }
        //printf("style[%d] = %d\n", apoStyles.size(), eType);

        apoStyles.push_back(eType);
    }
}

/************************************************************************/
/*                       endElementStylesCbk()                          */
/************************************************************************/

static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszNameIn)
{
    ((OGRXLSXDataSource*)pUserData)->endElementStylesCbk(pszNameIn);
}

void OGRXLSXDataSource::endElementStylesCbk(const char *pszNameIn)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;
    if (strcmp(pszNameIn,"cellXfs") == 0)
    {
        bInCellXFS = FALSE;
    }
}

/************************************************************************/
/*                             AnalyseStyles()                          */
/************************************************************************/

void OGRXLSXDataSource::AnalyseStyles(VSILFILE* fpStyles)
{
    if (fpStyles == NULL)
        return;

    oParser = OGRCreateExpatXMLParser();
    XML_SetElementHandler(oParser, OGRXLSX::startElementStylesCbk, OGRXLSX::endElementStylesCbk);
    XML_SetUserData(oParser, this);

    VSIFSeekL( fpStyles, 0, SEEK_SET );

    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nDataHandlerCounter = 0;
    bInCellXFS = FALSE;

    char aBuf[BUFSIZ];
    int nDone;
    do
    {
        nDataHandlerCounter = 0;
        unsigned int nLen =
            (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpStyles );
        nDone = VSIFEofL(fpStyles);
        if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of %s file failed : %s at line %d, column %d",
                     "styles.xml",
                     XML_ErrorString(XML_GetErrorCode(oParser)),
                     (int)XML_GetCurrentLineNumber(oParser),
                     (int)XML_GetCurrentColumnNumber(oParser));
            bStopParsing = TRUE;
        }
        nWithoutEventCounter ++;
    } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);

    XML_ParserFree(oParser);
    oParser = NULL;

    if (nWithoutEventCounter == 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Too much data inside one element. File probably corrupted");
        bStopParsing = TRUE;
    }

    VSIFCloseL(fpStyles);
}

/************************************************************************/
/*                           ICreateLayer()                             */
/************************************************************************/

OGRLayer *
OGRXLSXDataSource::ICreateLayer( const char * pszLayerName,
                                 CPL_UNUSED OGRSpatialReference *poSRS,
                                 CPL_UNUSED OGRwkbGeometryType eType,
                                 char ** papszOptions )

{
/* -------------------------------------------------------------------- */
/*      Verify we are in update mode.                                   */
/* -------------------------------------------------------------------- */
    if( !bUpdatable )
    {
        CPLError( CE_Failure, CPLE_NoWriteAccess,
                  "Data source %s opened read-only.\n"
                  "New layer %s cannot be created.\n",
                  pszName, pszLayerName );

        return NULL;
    }

/* -------------------------------------------------------------------- */
/*      Do we already have this layer?  If so, should we blow it        */
/*      away?                                                           */
/* -------------------------------------------------------------------- */
    int iLayer;

    for( iLayer = 0; iLayer < nLayers; iLayer++ )
    {
        if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
        {
            if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
                && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
            {
                DeleteLayer( pszLayerName );
            }
            else
            {
                CPLError( CE_Failure, CPLE_AppDefined,
                          "Layer %s already exists, CreateLayer failed.\n"
                          "Use the layer creation option OVERWRITE=YES to "
                          "replace it.",
                          pszLayerName );
                return NULL;
            }
        }
    }

/* -------------------------------------------------------------------- */
/*      Create the layer object.                                        */
/* -------------------------------------------------------------------- */
    OGRLayer* poLayer = new OGRXLSXLayer(this, nLayers + 1, pszLayerName, TRUE);

    papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
    papoLayers[nLayers] = poLayer;
    nLayers ++;

    bUpdated = TRUE;

    return poLayer;
}

/************************************************************************/
/*                            DeleteLayer()                             */
/************************************************************************/

void OGRXLSXDataSource::DeleteLayer( const char *pszLayerName )

{
    int iLayer;

/* -------------------------------------------------------------------- */
/*      Verify we are in update mode.                                   */
/* -------------------------------------------------------------------- */
    if( !bUpdatable )
    {
        CPLError( CE_Failure, CPLE_NoWriteAccess,
                  "Data source %s opened read-only.\n"
                  "Layer %s cannot be deleted.\n",
                  pszName, pszLayerName );

        return;
    }

/* -------------------------------------------------------------------- */
/*      Try to find layer.                                              */
/* -------------------------------------------------------------------- */
    for( iLayer = 0; iLayer < nLayers; iLayer++ )
    {
        if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
            break;
    }

    if( iLayer == nLayers )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Attempt to delete layer '%s', but this layer is not known to OGR.",
                  pszLayerName );
        return;
    }

    DeleteLayer(iLayer);
}

/************************************************************************/
/*                            DeleteLayer()                             */
/************************************************************************/

OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
{
    if( iLayer < 0 || iLayer >= nLayers )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Layer %d not in legal range of 0 to %d.",
                  iLayer, nLayers-1 );
        return OGRERR_FAILURE;
    }

/* -------------------------------------------------------------------- */
/*      Blow away our OGR structures related to the layer.  This is     */
/*      pretty dangerous if anything has a reference to this layer!     */
/* -------------------------------------------------------------------- */

    delete papoLayers[iLayer];
    memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
             sizeof(void *) * (nLayers - iLayer - 1) );
    nLayers--;

    bUpdated = TRUE;

    return OGRERR_NONE;
}

/************************************************************************/
/*                            WriteOverride()                           */
/************************************************************************/

static void WriteOverride(VSILFILE* fp, const char* pszPartName, const char* pszContentType)
{
    VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
                pszPartName, pszContentType);
}

#define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
#define MAIN_NS "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\""
#define SCHEMA_OD "http://schemas.openxmlformats.org/officeDocument/2006"
#define SCHEMA_OD_RS "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
#define SCHEMA_PACKAGE "http://schemas.openxmlformats.org/package/2006"
#define SCHEMA_PACKAGE_RS "http://schemas.openxmlformats.org/package/2006/relationships"

/************************************************************************/
/*                           WriteContentTypes()                        */
/************************************************************************/

static void WriteContentTypes(const char* pszName, int nLayers)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
    WriteOverride(fp, "/_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml");
    WriteOverride(fp, "/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml");
    WriteOverride(fp, "/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml");
    WriteOverride(fp, "/xl/_rels/workbook.xml.rels", "application/vnd.openxmlformats-package.relationships+xml");
    for(int i=0;i<nLayers;i++)
    {
        WriteOverride(fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i+1),
                      "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
    }
    WriteOverride(fp, "/xl/styles.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
    WriteOverride(fp, "/xl/workbook.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
    WriteOverride(fp, "/xl/sharedStrings.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
    VSIFPrintfL(fp, "</Types>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                             WriteApp()                               */
/************************************************************************/

static void WriteApp(const char* pszName)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<Properties xmlns=\"%s/extended-properties\" "
                    "xmlns:vt=\"%s/docPropsVTypes\">\n", SCHEMA_OD, SCHEMA_OD);
    VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
    VSIFPrintfL(fp, "</Properties>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                             WriteCore()                              */
/************************************************************************/

static void WriteCore(const char* pszName)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
                    "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
                    "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
                    "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
                    "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n", SCHEMA_PACKAGE);
    VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
    VSIFPrintfL(fp, "</cp:coreProperties>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                            WriteWorkbook()                           */
/************************************************************************/

static void WriteWorkbook(const char* pszName, OGRDataSource* poDS)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
    /*
    VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\" date1904=\"false\"/>\n");
    VSIFPrintfL(fp, "<workbookProtection/>\n");
    VSIFPrintfL(fp, "<bookViews>\n");
    VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\" showHorizontalScroll=\"true\" "
                    "showSheetTabs=\"true\" showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
                    "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
    VSIFPrintfL(fp, "</bookViews>\n");
    */
    VSIFPrintfL(fp, "<sheets>\n");
    for(int i=0;i<poDS->GetLayerCount();i++)
    {
        OGRXLSXLayer* poLayer = (OGRXLSXLayer*) poDS->GetLayer(i);
        const char* pszLayerName = poLayer->GetName();
        char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
        VSIFPrintfL(fp, "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" r:id=\"rId%d\"/>\n", pszXML, i+1, i+2);
        CPLFree(pszXML);
    }
    VSIFPrintfL(fp, "</sheets>\n");
    VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" iterate=\"false\" iterateDelta=\"0.001\"/>\n");
    VSIFPrintfL(fp, "</workbook>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                            BuildColString()                          */
/************************************************************************/

static void BuildColString(char szCol[5], int nCol)
{
    /*
    A Z   AA AZ   BA BZ   ZA   ZZ   AAA    ZZZ      AAAA
    0 25  26 51   52 77   676  701  702    18277    18278
    */
    int k = 0;
    szCol[k++] = (nCol % 26) + 'A';
    while(nCol >= 26)
    {
        nCol /= 26;
        // We would not need a decrement if this was a proper base 26
        // numeration scheme.
        nCol --;
        szCol[k++] = (nCol % 26) + 'A';
    }
    szCol[k] = 0;
    for(int l=0;l<k/2;l++)
    {
        char chTmp = szCol[k-1-l];
        szCol[k-1-l] = szCol[l];
        szCol[l] = chTmp;
    }
}

/************************************************************************/
/*                             WriteLayer()                             */
/************************************************************************/

static void WriteLayer(const char* pszName, OGRLayer* poLayer, int iLayer,
                       std::map<std::string,int>& oStringMap,
                       std::vector<std::string>& oStringList)
{
    VSILFILE* fp;
    int j;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, iLayer + 1), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    /*
    VSIFPrintfL(fp, "<sheetViews>\n");
    VSIFPrintfL(fp, "<sheetView colorId=\"64\" defaultGridColor=\"true\" rightToLeft=\"false\" showFormulas=\"false\" showGridLines=\"true\" showOutlineSymbols=\"true\" showRowColHeaders=\"true\" showZeros=\"true\" tabSelected=\"%s\" topLeftCell=\"A1\" view=\"normal\" windowProtection=\"false\" workbookViewId=\"0\" zoomScale=\"100\" zoomScaleNormal=\"100\" zoomScalePageLayoutView=\"60\">\n",
                (i == 0) ? "true" : "false");
    VSIFPrintfL(fp, "<selection activeCell=\"A1\" activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n");
    VSIFPrintfL(fp, "</sheetView>\n");
    VSIFPrintfL(fp, "</sheetViews>\n");*/

    poLayer->ResetReading();

    OGRFeature* poFeature = poLayer->GetNextFeature();

    OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
    int bHasHeaders = FALSE;
    int iRow = 1;

    VSIFPrintfL(fp, "<cols>\n");
    for(j=0;j<poFDefn->GetFieldCount();j++)
    {
        int nWidth = 15;
        if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
            nWidth = 29;
        VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
                    j+1, 1024, nWidth);

        if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
                    CPLSPrintf("Field%d", j+1)) != 0)
            bHasHeaders = TRUE;
    }
    VSIFPrintfL(fp, "</cols>\n");

    VSIFPrintfL(fp, "<sheetData>\n");

    if (bHasHeaders && poFeature != NULL)
    {
        VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
        for(j=0;j<poFDefn->GetFieldCount();j++)
        {
            const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
            std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
            int nStringIndex;
            if (oIter != oStringMap.end())
                nStringIndex = oIter->second;
            else
            {
                nStringIndex = (int)oStringList.size();
                oStringMap[pszVal] = nStringIndex;
                oStringList.push_back(pszVal);
            }

            char szCol[5];
            BuildColString(szCol, j);

            VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
            VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
            VSIFPrintfL(fp, "</c>\n");
        }
        VSIFPrintfL(fp, "</row>\n");

        iRow ++;
    }

    while(poFeature != NULL)
    {
        VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
        for(j=0;j<poFeature->GetFieldCount();j++)
        {
            if (poFeature->IsFieldSet(j))
            {
                char szCol[5];
                BuildColString(szCol, j);

                OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType();

                if (eType == OFTReal)
                {
                    VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
                    VSIFPrintfL(fp, "<v>%.16f</v>\n", poFeature->GetFieldAsDouble(j));
                    VSIFPrintfL(fp, "</c>\n");
                }
                else if (eType == OFTInteger)
                {
                    VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
                    VSIFPrintfL(fp, "<v>%d</v>\n", poFeature->GetFieldAsInteger(j));
                    VSIFPrintfL(fp, "</c>\n");
                }
                else if (eType == OFTInteger64)
                {
                    VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
                    VSIFPrintfL(fp, "<v>" CPL_FRMT_GIB "</v>\n", poFeature->GetFieldAsInteger64(j));
                    VSIFPrintfL(fp, "</c>\n");
                }
                else if (eType == OFTDate || eType == OFTDateTime || eType == OFTTime)
                {
                    int nYear, nMonth, nDay, nHour, nMinute, nTZFlag;
                    float fSecond;
                    poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
                                                    &nHour, &nMinute, &fSecond, &nTZFlag );
                    struct tm brokendowntime;
                    memset(&brokendowntime, 0, sizeof(brokendowntime));
                    brokendowntime.tm_year = (eType == OFTTime) ? 70 : nYear - 1900;
                    brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
                    brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
                    brokendowntime.tm_hour = nHour;
                    brokendowntime.tm_min = nMinute;
                    brokendowntime.tm_sec = (int)fSecond;
                    GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
                    double dfNumberOfDaysSince1900 = (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
                    dfNumberOfDaysSince1900 += fmod(fSecond,1) / NUMBER_OF_SECONDS_PER_DAY;
                    int s = (eType == OFTDate) ? 1 : (eType == OFTDateTime) ? 2 : 3;
                    if( eType == OFTDateTime && OGR_GET_MS(fSecond) )
                        s = 4;
                    VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", szCol, iRow, s);
                    if (eType != OFTTime)
                        dfNumberOfDaysSince1900 += NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
                    if (eType == OFTDate)
                        VSIFPrintfL(fp, "<v>%d</v>\n", (int)(dfNumberOfDaysSince1900 + 0.1));
                    else
                        VSIFPrintfL(fp, "<v>%.16f</v>\n", dfNumberOfDaysSince1900);
                    VSIFPrintfL(fp, "</c>\n");
                }
                else
                {
                    const char* pszVal = poFeature->GetFieldAsString(j);
                    std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
                    int nStringIndex;
                    if (oIter != oStringMap.end())
                        nStringIndex = oIter->second;
                    else
                    {
                        nStringIndex = (int)oStringList.size();
                        oStringMap[pszVal] = nStringIndex;
                        oStringList.push_back(pszVal);
                    }
                    VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
                    VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
                    VSIFPrintfL(fp, "</c>\n");
                }
            }
        }
        VSIFPrintfL(fp, "</row>\n");

        iRow ++;
        delete poFeature;
        poFeature = poLayer->GetNextFeature();
    }
    VSIFPrintfL(fp, "</sheetData>\n");
    VSIFPrintfL(fp, "</worksheet>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                        WriteSharedStrings()                          */
/************************************************************************/

static void WriteSharedStrings(const char* pszName,
                               CPL_UNUSED std::map<std::string,int>& oStringMap,
                               std::vector<std::string>& oStringList)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n",
                MAIN_NS,
                (int)oStringList.size());
    for(int i = 0; i < (int)oStringList.size(); i++)
    {
        VSIFPrintfL(fp, "<si>\n");
        char* pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
        VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
        CPLFree(pszXML);
        VSIFPrintfL(fp, "</si>\n");
    }
    VSIFPrintfL(fp, "</sst>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                           WriteStyles()                              */
/************************************************************************/

static void WriteStyles(const char* pszName)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
    VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
    VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
    VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
    VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
    VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
    VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS.000\" numFmtId=\"168\"/>\n");
    VSIFPrintfL(fp, "</numFmts>\n");
    VSIFPrintfL(fp, "<fonts count=\"1\">\n");
    VSIFPrintfL(fp, "<font>\n");
    VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
    VSIFPrintfL(fp, "<family val=\"2\"/>\n");
    VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
    VSIFPrintfL(fp, "</font>\n");
    VSIFPrintfL(fp, "</fonts>\n");
    VSIFPrintfL(fp, "<fills count=\"1\">\n");
    VSIFPrintfL(fp, "<fill>\n");
    VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
    VSIFPrintfL(fp, "</fill>\n");
    VSIFPrintfL(fp, "</fills>\n");
    VSIFPrintfL(fp, "<borders count=\"1\">\n");
    VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
    VSIFPrintfL(fp, "<left/>\n");
    VSIFPrintfL(fp, "<right/>\n");
    VSIFPrintfL(fp, "<top/>\n");
    VSIFPrintfL(fp, "<bottom/>\n");
    VSIFPrintfL(fp, "<diagonal/>\n");
    VSIFPrintfL(fp, "</border>\n");
    VSIFPrintfL(fp, "</borders>\n");
    VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
    VSIFPrintfL(fp, "</xf>\n");
    VSIFPrintfL(fp, "</cellStyleXfs>\n");
    VSIFPrintfL(fp, "<cellXfs count=\"5\">\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "<xf numFmtId=\"168\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "</cellXfs>\n");
    VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
    VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" name=\"Normal\" xfId=\"0\"/>\n");
    VSIFPrintfL(fp, "</cellStyles>\n");
    VSIFPrintfL(fp, "</styleSheet>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                           WriteWorkbookRels()                        */
/************************************************************************/

static void WriteWorkbookRels(const char* pszName, int nLayers)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/styles\" Target=\"styles.xml\"/>\n", SCHEMA_OD_RS);
    for(int i=0;i<nLayers;i++)
    {
        VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" Target=\"worksheets/sheet%d.xml\"/>\n",
                    2 + i, SCHEMA_OD_RS, 1 + i);
    }
    VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" Target=\"sharedStrings.xml\"/>\n",
                2 + nLayers, SCHEMA_OD_RS);
    VSIFPrintfL(fp, "</Relationships>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                             WriteDotRels()                           */
/************************************************************************/

static void WriteDotRels(const char* pszName)
{
    VSILFILE* fp;

    fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName), "wb");
    VSIFPrintfL(fp, XML_HEADER);
    VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" Target=\"xl/workbook.xml\"/>\n", SCHEMA_OD_RS);
    VSIFPrintfL(fp, "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" Target=\"docProps/core.xml\"/>\n", SCHEMA_PACKAGE_RS);
    VSIFPrintfL(fp, "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" Target=\"docProps/app.xml\"/>\n", SCHEMA_OD_RS);
    VSIFPrintfL(fp, "</Relationships>\n");
    VSIFCloseL(fp);
}

/************************************************************************/
/*                            FlushCache()                              */
/************************************************************************/

void OGRXLSXDataSource::FlushCache()
{
    if (!bUpdated)
        return;

    VSIStatBufL sStat;
    if (VSIStatL(pszName, &sStat) == 0)
    {
        if (VSIUnlink( pszName ) != 0)
        {
            CPLError(CE_Failure, CPLE_FileIO,
                    "Cannot delete %s", pszName);
            return;
        }
    }

    /* Cause all layers to be initialized */
    for(int i = 0; i<nLayers; i++)
    {
        ((OGRXLSXLayer*)papoLayers[i])->GetLayerDefn();
    }

    /* Maintain new ZIP files opened */
    VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "wb");
    if (fpZIP == NULL)
    {
        CPLError(CE_Failure, CPLE_FileIO,
                 "Cannot create %s", pszName);
        return;
    }

    WriteContentTypes(pszName, nLayers);

    //VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
    WriteApp(pszName);
    WriteCore(pszName);

    //VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
    WriteWorkbook(pszName, this);

    std::map<std::string,int> oStringMap;
    std::vector<std::string> oStringList;

    //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
    for(int i=0;i<nLayers;i++)
    {
        WriteLayer(pszName, GetLayer(i), i, oStringMap, oStringList);
    }

    WriteSharedStrings(pszName, oStringMap, oStringList);
    WriteStyles(pszName);

    //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
    WriteWorkbookRels(pszName, nLayers);

    //VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
    WriteDotRels(pszName);

    /* Now close ZIP file */
    VSIFCloseL(fpZIP);

    /* Reset updated flag at datasource and layer level */
    bUpdated = FALSE;
    for(int i = 0; i<nLayers; i++)
    {
        ((OGRXLSXLayer*)papoLayers[i])->SetUpdated(FALSE);
    }

    return;
}

} /* end of OGRXLSX namespace */


text:
#include "OneWayBlock.h"
#include "EmptySpaceBlock.h"
#include "Projectile.h"
#include "GameModel.h"
#include "GameEventManager.h"

const float OneWayBlock::ACCEPTIBLE_MAX_ANGLE_TO_ONE_WAY_IN_RADS = M_PI / 180.0f * 89.0f; // (89 degrees)

OneWayBlock::OneWayBlock(const OneWayDir& dir, unsigned int wLoc, unsigned int hLoc) :
LevelPiece(wLoc, hLoc), dirType(dir) {

    switch (dir) {
        case OneWayBlock::OneWayUp:
            this->oneWayDir = Vector2D(0, 1);
            break;
        case OneWayBlock::OneWayDown:
            this->oneWayDir = Vector2D(0, -1);
            break;
        case OneWayBlock::OneWayLeft:
            this->oneWayDir = Vector2D(-1, 0);
            break;
        case OneWayBlock::OneWayRight:
            this->oneWayDir = Vector2D(1, 0);
            break;
        default:
            assert(false);
            break;
    }
}

OneWayBlock::~OneWayBlock() {
}

bool OneWayBlock::ConvertCharToOneWayDir(const char& oneWayChar, OneWayBlock::OneWayDir& oneWayDirEnum) {
    switch (oneWayChar) {
        case 'u':
            oneWayDirEnum = OneWayBlock::OneWayUp;
            break;
        case 'd':
            oneWayDirEnum = OneWayBlock::OneWayDown;
            break;
        case 'l':
            oneWayDirEnum = OneWayBlock::OneWayLeft;
            break;
        case 'r':
            oneWayDirEnum = OneWayBlock::OneWayRight;
            break;
        default:
            assert(false);
            return false;
    }
    return true;
}

// Determine whether the given projectile will pass through this block...
bool OneWayBlock::ProjectilePassesThrough(const Projectile* projectile) const {
    if (this->IsGoingTheOneWay(projectile->GetVelocityDirection())) {
        return true;
    }

	switch (projectile->GetType()) {

        case Projectile::BossOrbBulletProjectile:
        case Projectile::BossLaserBulletProjectile:
		case Projectile::PaddleLaserBulletProjectile:
        case Projectile::BallLaserBulletProjectile:
        case Projectile::LaserTurretBulletProjectile:
			// When frozen, projectiles can pass through
			if (this->HasStatus(LevelPiece::IceCubeStatus)) {
				return true;
			}
			break;

		case Projectile::CollateralBlockProjectile:
			return true;

		case Projectile::FireGlobProjectile:
        case Projectile::PaddleFlameBlastProjectile:
        case Projectile::PaddleIceBlastProjectile:
            return false;

		default:
			break;
	}

	return false;
}

/**
 * Get the number of points when this piece changes to the given piece.
 */
int OneWayBlock::GetPointsOnChange(const LevelPiece& changeToPiece) const {
    if (changeToPiece.GetType() == LevelPiece::Empty) {
        return OneWayBlock::POINTS_ON_BLOCK_DESTROYED;
    }
    return 0;
}

LevelPiece* OneWayBlock::Destroy(GameModel* gameModel, const LevelPiece::DestructionMethod& method) {

	if (this->HasStatus(LevelPiece::IceCubeStatus)) {
	    // EVENT: Ice was shattered
	    GameEventManager::Instance()->ActionBlockIceShattered(*this);
	    bool success = gameModel->RemoveStatusForLevelPiece(this, LevelPiece::IceCubeStatus);
        UNUSED_VARIABLE(success);
	    assert(success);
	}

    // Only collateral blocks, Tesla lightning and disintegration can destroy a one-way block
    if (method != LevelPiece::CollateralDestruction && method != LevelPiece::TeslaDestruction &&
        method != LevelPiece::DisintegrationDestruction) {
        return this;
    }

	// EVENT: Block is being destroyed
	GameEventManager::Instance()->ActionBlockDestroyed(*this, method);

    // Only drop an item if the block wasn't disintegrated
    if (method != LevelPiece::DisintegrationDestruction) {
	    gameModel->AddPossibleItemDrop(*this);
    }

	// Tell the level that this piece has changed to empty...
	GameLevel* level = gameModel->GetCurrentLevel();
	LevelPiece* emptyPiece = new EmptySpaceBlock(this->wIndex, this->hIndex);
	level->PieceChanged(gameModel, this, emptyPiece, method);

	// Obliterate all that is left of this block...
	LevelPiece* tempThis = this;
	delete tempThis;
	tempThis = NULL;

	return emptyPiece;
}

/**
 * Update the collision boundries of this block.
 */
void OneWayBlock::UpdateBounds(const LevelPiece* leftNeighbor, const LevelPiece* bottomNeighbor,
                               const LevelPiece* rightNeighbor, const LevelPiece* topNeighbor,
                               const LevelPiece* topRightNeighbor, const LevelPiece* topLeftNeighbor,
                               const LevelPiece* bottomRightNeighbor, const LevelPiece* bottomLeftNeighbor) {

    static const int MAX_NUM_LINES = 4;
    Collision::LineSeg2D boundingLines[MAX_NUM_LINES];
    Vector2D  boundingNorms[MAX_NUM_LINES];
    bool onInside[MAX_NUM_LINES];

    int lineCount = 0;
    bool hasBound, isOnInside;

	// Left boundary of the piece
	if (leftNeighbor != NULL) {
        if (leftNeighbor->HasStatus(LevelPiece::IceCubeStatus) ||
            leftNeighbor->GetType() != LevelPiece::Solid &&
            leftNeighbor->GetType() != LevelPiece::LaserTurret && leftNeighbor->GetType() != LevelPiece::RocketTurret &&
            leftNeighbor->GetType() != LevelPiece::MineTurret && leftNeighbor->GetType() != LevelPiece::Breakable &&
            leftNeighbor->GetType() != LevelPiece::AlwaysDrop && leftNeighbor->GetType() != LevelPiece::Regen &&
            leftNeighbor->GetType() != LevelPiece::ItemDrop) {

            hasBound = true;
            isOnInside = (leftNeighbor == NULL || leftNeighbor->HasStatus(LevelPiece::IceCubeStatus) ||
                leftNeighbor->GetType() == LevelPiece::NoEntry || leftNeighbor->GetType() == LevelPiece::Switch ||
                leftNeighbor->GetType() == LevelPiece::Solid);

            if (leftNeighbor->GetType() == LevelPiece::OneWay && !leftNeighbor->HasStatus(LevelPiece::IceCubeStatus)) {
                if (static_cast<const OneWayBlock*>(leftNeighbor)->GetDirType() != this->GetDirType()) {
                    isOnInside = true;
                }
                else {
                    hasBound = false;
                }
            }

            if (hasBound) {
                boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT),
                    this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT));
			    boundingNorms[lineCount] = Vector2D(-1, 0);
                onInside[lineCount] = isOnInside;
                lineCount++;
            }
		}
	}
    else {
        // leftNeighbor is NULL
        if (this->dirType == OneWayLeft) {
            boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT),
                this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT));
            boundingNorms[lineCount] = Vector2D(-1, 0);
            onInside[lineCount] = true;
            lineCount++;
        }
    }

	// Bottom boundary of the piece
	if (bottomNeighbor != NULL) {
		if (bottomNeighbor->HasStatus(LevelPiece::IceCubeStatus | LevelPiece::OnFireStatus) ||
            bottomNeighbor->GetType() != LevelPiece::Solid &&
            bottomNeighbor->GetType() != LevelPiece::LaserTurret && bottomNeighbor->GetType() != LevelPiece::RocketTurret &&
            bottomNeighbor->GetType() != LevelPiece::MineTurret && bottomNeighbor->GetType() != LevelPiece::Breakable &&
            bottomNeighbor->GetType() != LevelPiece::AlwaysDrop && bottomNeighbor->GetType() != LevelPiece::Regen &&
            bottomNeighbor->GetType() != LevelPiece::ItemDrop) {

            hasBound = true;
            isOnInside = (bottomNeighbor == NULL || bottomNeighbor->HasStatus(LevelPiece::IceCubeStatus | LevelPiece::OnFireStatus) ||
                bottomNeighbor->GetType() == LevelPiece::NoEntry || bottomNeighbor->GetType() == LevelPiece::Switch ||
                bottomNeighbor->GetType() == LevelPiece::Solid);

            if (bottomNeighbor->GetType() == LevelPiece::OneWay && !bottomNeighbor->HasStatus(LevelPiece::IceCubeStatus)) {
                if (static_cast<const OneWayBlock*>(bottomNeighbor)->GetDirType() != this->GetDirType()) {
                    isOnInside = true;
                }
                else {
                    hasBound = false;
                }
            }

            if (hasBound) {
                boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT),
                    this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT));
			    boundingNorms[lineCount] = Vector2D(0, -1);
                onInside[lineCount] = isOnInside;
                lineCount++;
            }
		}
	}
    else {
        // bottomNeighbor is NULL
        if (this->dirType != OneWayUp) {
            boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT),
                this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT));
            boundingNorms[lineCount] = Vector2D(0, -1);
            onInside[lineCount] = true;
            lineCount++;
        }
    }

	// Right boundary of the piece
	if (rightNeighbor != NULL) {
		if (rightNeighbor->HasStatus(LevelPiece::IceCubeStatus) ||
            rightNeighbor->GetType() != LevelPiece::Solid &&
            rightNeighbor->GetType() != LevelPiece::LaserTurret && rightNeighbor->GetType() != LevelPiece::RocketTurret &&
            rightNeighbor->GetType() != LevelPiece::MineTurret && rightNeighbor->GetType() != LevelPiece::Breakable &&
            rightNeighbor->GetType() != LevelPiece::AlwaysDrop && rightNeighbor->GetType() != LevelPiece::Regen &&
            rightNeighbor->GetType() != LevelPiece::ItemDrop) {

            hasBound = true;
            isOnInside = (rightNeighbor == NULL || rightNeighbor->HasStatus(LevelPiece::IceCubeStatus) ||
                rightNeighbor->GetType() == LevelPiece::NoEntry || rightNeighbor->GetType() == LevelPiece::Switch ||
                rightNeighbor->GetType() == LevelPiece::Solid);

            if (rightNeighbor->GetType() == LevelPiece::OneWay && !rightNeighbor->HasStatus(LevelPiece::IceCubeStatus)) {
                if (static_cast<const OneWayBlock*>(rightNeighbor)->GetDirType() != this->GetDirType()) {
                    isOnInside = true;
                }
                else {
                    hasBound = false;
                }
            }

            if (hasBound) {
                boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT),
                    this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT));
			    boundingNorms[lineCount] = Vector2D(1, 0);
                onInside[lineCount] = isOnInside;
                lineCount++;
            }
		}
	}
    else {
        // rightNeighbor is NULL
        if (this->dirType == OneWayRight) {
            boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, -LevelPiece::HALF_PIECE_HEIGHT),
                this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT));
            boundingNorms[lineCount] = Vector2D(1, 0);
            onInside[lineCount] = true;
            lineCount++;
        }
    }

	// Top boundary of the piece
	if (topNeighbor != NULL) {
		if (topNeighbor->HasStatus(LevelPiece::IceCubeStatus | LevelPiece::OnFireStatus) ||
            topNeighbor->GetType() != LevelPiece::Solid &&
            topNeighbor->GetType() != LevelPiece::LaserTurret && topNeighbor->GetType() != LevelPiece::RocketTurret &&
            topNeighbor->GetType() != LevelPiece::MineTurret && topNeighbor->GetType() != LevelPiece::Breakable &&
            topNeighbor->GetType() != LevelPiece::AlwaysDrop && topNeighbor->GetType() != LevelPiece::Regen &&
            topNeighbor->GetType() != LevelPiece::ItemDrop) {

            hasBound = true;
            isOnInside = (topNeighbor == NULL || topNeighbor->HasStatus(LevelPiece::IceCubeStatus | LevelPiece::OnFireStatus) ||
                topNeighbor->GetType() == LevelPiece::NoEntry || topNeighbor->GetType() == LevelPiece::Switch ||
                topNeighbor->GetType() == LevelPiece::Solid);

            if (topNeighbor->GetType() == LevelPiece::OneWay && !topNeighbor->HasStatus(LevelPiece::IceCubeStatus)) {
                if (static_cast<const OneWayBlock*>(topNeighbor)->GetDirType() != this->GetDirType()) {
                    isOnInside = true;
                }
                else {
                    hasBound = false;
                }
            }

            if (hasBound) {
                boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT),
                    this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT));
			    boundingNorms[lineCount] = Vector2D(0, 1);
                onInside[lineCount] = isOnInside;
                lineCount++;
            }
		}
	}
    else {
        // topNeighbor is NULL
        if (this->dirType != OneWayDown) {
            boundingLines[lineCount] = Collision::LineSeg2D(this->center + Vector2D(LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT),
                this->center + Vector2D(-LevelPiece::HALF_PIECE_WIDTH, LevelPiece::HALF_PIECE_HEIGHT));
            boundingNorms[lineCount] = Vector2D(0, 1);
            onInside[lineCount] = true;
            lineCount++;
        }
    }

	this->SetBounds(BoundingLines(lineCount, boundingLines, boundingNorms, onInside),
        leftNeighbor, bottomNeighbor, rightNeighbor, topNeighbor,
        topRightNeighbor, topLeftNeighbor, bottomRightNeighbor, bottomLeftNeighbor);
}

LevelPiece* OneWayBlock::CollisionOccurred(GameModel* gameModel, GameBall& ball) {
	ball.SetLastPieceCollidedWith(NULL);

	// One-way blocks can be frozen, but they still can't be destroyed (in most cases)
	bool isIceBall  = ((ball.GetBallType() & GameBall::IceBall) == GameBall::IceBall);
	if (isIceBall) {
		this->FreezePieceInIce(gameModel);
	}
	else {
		bool isFireBall = ((ball.GetBallType() & GameBall::FireBall) == GameBall::FireBall);
		if (!isFireBall) {
			if (this->HasStatus(LevelPiece::IceCubeStatus)) {
				// EVENT: Ice was shattered
				GameEventManager::Instance()->ActionBlockIceShattered(*this);
			}
		}

		// Unfreeze a frozen block
		if (this->HasStatus(LevelPiece::IceCubeStatus)) {
			bool success = gameModel->RemoveStatusForLevelPiece(this, LevelPiece::IceCubeStatus);
            UNUSED_VARIABLE(success);
			assert(success);

            if (isFireBall) {
                // EVENT: Frozen block canceled-out by fire
                GameEventManager::Instance()->ActionBlockIceCancelledWithFire(*this);
            }
		}
	}

	return this;
}

LevelPiece* OneWayBlock::CollisionOccurred(GameModel* gameModel, PlayerPaddle& paddle) {

    if (paddle.IsLastPieceCollidedWith(this)) {
        return this;
    }

    if (this->HasStatus(LevelPiece::IceCubeStatus)) {
        // EVENT: Ice was shattered
        GameEventManager::Instance()->ActionBlockIceShattered(*this);

        bool success = gameModel->RemoveStatusForLevelPiece(this, LevelPiece::IceCubeStatus);
        UNUSED_VARIABLE(success);
        assert(success);

        paddle.SetLastPieceCollidedWith(this);
    }

    return this;
}

/**
 * Called when the block is hit by a projectile. Tends to cause the projectile to
 * extinguish, however for the collateral block projectile, it will completely destroy this.
 */
LevelPiece* OneWayBlock::CollisionOccurred(GameModel* gameModel, Projectile* projectile) {
	LevelPiece* resultingPiece = this;

	switch (projectile->GetType()) {

        case Projectile::BossOrbBulletProjectile:
        case Projectile::BossLaserBulletProjectile:
		case Projectile::PaddleLaserBulletProjectile:
        case Projectile::BallLaserBulletProjectile:
        case Projectile::LaserTurretBulletProjectile:
			if (this->HasStatus(LevelPiece::IceCubeStatus)) {
				this->DoIceCubeReflectRefractLaserBullets(projectile, gameModel);
			}
			break;

        case Projectile::BossLightningBoltBulletProjectile:
        case Projectile::BossShockOrbBulletProjectile:
            break;

		case Projectile::CollateralBlockProjectile:
            resultingPiece = this->Destroy(gameModel, LevelPiece::CollateralDestruction);
			break;

		case Projectile::PaddleRocketBulletProjectile:
        case Projectile::PaddleRemoteCtrlRocketBulletProjectile:
        case Projectile::RocketTurretBulletProjectile:
        case Projectile::BossRocketBulletProjectile:

            assert(dynamic_cast<RocketProjectile*>(projectile) != NULL);
			resultingPiece = gameModel->GetCurrentLevel()->RocketExplosion(gameModel, static_cast<RocketProjectile*>(projectile), this);

            if (this->HasStatus(LevelPiece::IceCubeStatus)) {
                GameEventManager::Instance()->ActionBlockIceShattered(*this);
                bool success = gameModel->RemoveStatusForLevelPiece(this, LevelPiece::IceCubeStatus);
                UNUSED_VARIABLE(success);
                assert(success);
            }

			break;

        case Projectile::PaddleMineBulletProjectile:
        case Projectile::MineTurretBulletProjectile:
            // A mine will just come to rest on the block.
            break;

		case Projectile::FireGlobProjectile:
			// Fire glob just extinguishes on a one-way block, unless it's frozen in an ice cube;
			// in that case, unfreeze it
            this->LightPieceOnFire(gameModel, false);
			break;

        case Projectile::PaddleFlameBlastProjectile:
            this->LightPieceOnFire(gameModel, false);
            break;

        case Projectile::PaddleIceBlastProjectile:
            this->FreezePieceInIce(gameModel);
            break;

        case Projectile::PortalBlobProjectile:
            break;

		default:
			assert(false);
			break;
	}

	return resultingPiece;
}

//int OneWayBlock::GetBlockingBoundIndex() const {
//    switch (this->dirType) {
//        case OneWayBlock::OneWayUp:
//            return OneWayBlock::TOP_BOUNDRY_IDX;
//        case OneWayBlock::OneWayDown:
//            return OneWayBlock::BOTTOM_BOUNDRY_IDX;
//        case OneWayBlock::OneWayLeft:
//            return OneWayBlock::LEFT_BOUNDRY_IDX;
//        case OneWayBlock::OneWayRight:
//            return OneWayBlock::RIGHT_BOUNDRY_IDX;
//        default:
//            assert(false);
//            break;
//    }
//
//    assert(false);
//    return -1;
//}

redpajama_set_name: RedPajamaGithub


text:
function validateTicket(ticket) {
    for (let keys in ticket) {
        if (keys !== "id" && keys !== "comments") {
            if (ticket[keys].match(/([<>&])/gm)) {
                toastr.error("You can't use symbols <> and & in " + keys);
                return false;
            } else if ((ticket[keys] === "" || ticket[keys] === undefined) && keys !== "comment") {
                toastr.error("You can't have empty filed  " + keys);
                return false;
            }
        }
    }

    if (ticket.engineer === 'select') {
        toastr.error("Please select Enigneer!");
        return false;
    }

    if (ticket.urgency === 'Select Urgency') {
        toastr.error("Please select Urgency status!");
        return false;
    }

    return true;
}

export {
    validateTicket
}


text:
package scsp

//Licensed 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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

// TagValuesItem is a nested struct in scsp response
type TagValuesItem struct {
	Status               string `json:"Status" xml:"Status"`
	Description          string `json:"Description" xml:"Description"`
	TagName              string `json:"TagName" xml:"TagName"`
	TagGroupCode         string `json:"TagGroupCode" xml:"TagGroupCode"`
	TagCode              string `json:"TagCode" xml:"TagCode"`
	TagGroupName         string `json:"TagGroupName" xml:"TagGroupName"`
	EntityRelationNumber string `json:"EntityRelationNumber" xml:"EntityRelationNumber"`
}


text: Final project written in pt-br.


text:
namespace java {
  namespace lang {
    class Class;
  }
}
template<class T> class JArray;

namespace org {
  namespace apache {
    namespace lucene {
      namespace analysis {
        namespace core {

          class KeywordAnalyzer : public ::org::apache::lucene::analysis::Analyzer {
          public:
            enum {
              mid_init$_54c6a166,
              mid_createComponents_1be39cae,
              max_mid
            };

            static ::java::lang::Class *class$;
            static jmethodID *mids$;
            static bool live$;
            static jclass initializeClass(bool);

            explicit KeywordAnalyzer(jobject obj) : ::org::apache::lucene::analysis::Analyzer(obj) {
              if (obj != NULL)
                env->getClass(initializeClass);
            }
            KeywordAnalyzer(const KeywordAnalyzer& obj) : ::org::apache::lucene::analysis::Analyzer(obj) {}

            KeywordAnalyzer();
          };
        }
      }
    }
  }
}

#include <Python.h>

namespace org {
  namespace apache {
    namespace lucene {
      namespace analysis {
        namespace core {
          extern PyTypeObject PY_TYPE(KeywordAnalyzer);

          class t_KeywordAnalyzer {
          public:
            PyObject_HEAD
            KeywordAnalyzer object;
            static PyObject *wrap_Object(const KeywordAnalyzer&);
            static PyObject *wrap_jobject(const jobject&);
            static void install(PyObject *module);
            static void initialize(PyObject *module);
          };
        }
      }
    }
  }
}

#endif


text:
`rustup` is a *toolchain multiplexer*. It installs and manages many Rust
toolchains and presents them all through a single set of tools installed to
`~/.cargo/bin`. The [`rustc`] and [`cargo`] executables installed in
`~/.cargo/bin` are *[proxies]* that delegate to the real toolchain. `rustup`
then provides mechanisms to easily change the active toolchain by
reconfiguring the behavior of the proxies.

So when `rustup` is first installed, running `rustc` will run the proxy in
`$HOME/.cargo/bin/rustc`, which in turn will run the stable compiler. If you
later *change the default toolchain* to nightly with `rustup default nightly`,
then that same proxy will run the `nightly` compiler instead.

This is similar to Ruby's [rbenv], Python's [pyenv], or Node's [nvm].

[rbenv]: https://github.com/rbenv/rbenv
[pyenv]: https://github.com/yyuu/pyenv
[nvm]: https://github.com/creationix/nvm
[`rustc`]: https://doc.rust-lang.org/rustc/
[`cargo`]: https://doc.rust-lang.org/cargo/
[proxies]: proxies.md

## Terminology

* **channel** — Rust is released to three different "channels": stable, beta,
  and nightly. See the [Channels] chapter for more details.

* **toolchain** — A "toolchain" is a complete installation of the Rust
  compiler (`rustc`) and related tools (like `cargo`). A [toolchain
  specification] includes the release channel or version, and the host
  platform that the toolchain runs on.

* **target** — `rustc` is capable of generating code for many platforms. The
  "target" specifies the platform that the code will be generated for. By
  default, `cargo` and `rustc` use the host toolchain's platform as the
  target. To build for a different target, usually the target's standard
  library needs to be installed first via the `rustup target` command. See the
  [Cross-compilation] chapter for more details.

* **component** — Each release of Rust includes several "components", some of
  which are required (like `rustc`) and some that are optional (like
  [`clippy`]). See the [Components] chapter for more detail.

* **profile** — In order to make it easier to work with components, a
  "profile" defines a grouping of components. See the [Profiles] chapter for
  more details.

* **proxy** —  A wrapper for a common Rust component (like `rustc`), built to forward
  CLI invocations to the active Rust toolchain. See the [Proxies] chapter for more details.

[`clippy`]: https://github.com/rust-lang/rust-clippy
[components]: components.md
[cross-compilation]: ../cross-compilation.md
[profiles]: profiles.md
[toolchain specification]: toolchains.md
[channels]: channels.md
[proxies]: proxies.md