LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/xlsx - ogrxlsxdatasource.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 886 804 90.7 %
Date: 2012-04-28 Functions: 69 61 88.4 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrxlsxdatasource.cpp 24173 2012-03-29 21:09:52Z rouault $
       3                 :  *
       4                 :  * Project:  XLSX Translator
       5                 :  * Purpose:  Implements OGRXLSXDataSource class
       6                 :  * Author:   Even Rouault, even dot rouault at mines dash paris dot org
       7                 :  *
       8                 :  ******************************************************************************
       9                 :  * Copyright (c) 2012, Even Rouault <even dot rouault at mines dash paris dot org>
      10                 :  *
      11                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      12                 :  * copy of this software and associated documentation files (the "Software"),
      13                 :  * to deal in the Software without restriction, including without limitation
      14                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15                 :  * and/or sell copies of the Software, and to permit persons to whom the
      16                 :  * Software is furnished to do so, subject to the following conditions:
      17                 :  *
      18                 :  * The above copyright notice and this permission notice shall be included
      19                 :  * in all copies or substantial portions of the Software.
      20                 :  *
      21                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27                 :  * DEALINGS IN THE SOFTWARE.
      28                 :  ****************************************************************************/
      29                 : 
      30                 : #include "ogr_xlsx.h"
      31                 : #include "ogr_p.h"
      32                 : #include "cpl_conv.h"
      33                 : #include "cpl_time.h"
      34                 : 
      35                 : CPL_CVSID("$Id: ogrxlsxdatasource.cpp 24173 2012-03-29 21:09:52Z rouault $");
      36                 : 
      37                 : /************************************************************************/
      38                 : /*                            OGRXLSXLayer()                            */
      39                 : /************************************************************************/
      40                 : 
      41             212 : OGRXLSXLayer::OGRXLSXLayer( OGRXLSXDataSource* poDSIn,
      42                 :                             int nSheetIdIn,
      43                 :                             const char * pszName,
      44                 :                             int bUpdatedIn) :
      45             212 :                                 OGRMemLayer(pszName, NULL, wkbNone)
      46                 : {
      47             212 :     bInit = FALSE;
      48             212 :     nSheetId = nSheetIdIn;
      49             212 :     poDS = poDSIn;
      50             212 :     bUpdated = bUpdatedIn;
      51             212 :     bHasHeaderLine = FALSE;
      52             212 : }
      53                 : 
      54                 : /************************************************************************/
      55                 : /*                              Init()                                  */
      56                 : /************************************************************************/
      57                 : 
      58            5528 : void OGRXLSXLayer::Init()
      59                 : {
      60            5528 :     if (!bInit)
      61                 :     {
      62             126 :         bInit = TRUE;
      63             126 :         CPLDebug("XLSX", "Init(%s)", GetName());
      64             126 :         poDS->BuildLayer(this, nSheetId);
      65                 :     }
      66            5528 : }
      67                 : 
      68                 : /************************************************************************/
      69                 : /*                             Updated()                                */
      70                 : /************************************************************************/
      71                 : 
      72            1930 : void OGRXLSXLayer::SetUpdated(int bUpdatedIn)
      73                 : {
      74            1930 :     if (bUpdatedIn && !bUpdated && poDS->GetUpdatable())
      75                 :     {
      76               6 :         bUpdated = TRUE;
      77               6 :         poDS->SetUpdated();
      78                 :     }
      79            1924 :     else if (bUpdated && !bUpdatedIn)
      80                 :     {
      81              22 :         bUpdated = FALSE;
      82                 :     }
      83            1930 : }
      84                 : 
      85                 : /************************************************************************/
      86                 : /*                           SyncToDisk()                               */
      87                 : /************************************************************************/
      88                 : 
      89               0 : OGRErr OGRXLSXLayer::SyncToDisk()
      90                 : {
      91               0 :     return poDS->SyncToDisk();
      92                 : }
      93                 : 
      94                 : /************************************************************************/
      95                 : /*                          GetNextFeature()                            */
      96                 : /************************************************************************/
      97                 : 
      98             714 : OGRFeature* OGRXLSXLayer::GetNextFeature()
      99                 : {
     100             714 :     Init();
     101             714 :     OGRFeature* poFeature = OGRMemLayer::GetNextFeature();
     102             714 :     if (poFeature)
     103             598 :         poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine);
     104             714 :     return poFeature;
     105                 : }
     106                 : 
     107                 : /************************************************************************/
     108                 : /*                           GetFeature()                               */
     109                 : /************************************************************************/
     110                 : 
     111               4 : OGRFeature* OGRXLSXLayer::GetFeature( long nFeatureId )
     112                 : {
     113               4 :     Init();
     114               4 :     OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine));
     115               4 :     if (poFeature)
     116               4 :         poFeature->SetFID(nFeatureId);
     117               4 :     return poFeature;
     118                 : }
     119                 : 
     120                 : /************************************************************************/
     121                 : /*                           SetFeature()                               */
     122                 : /************************************************************************/
     123                 : 
     124             632 : OGRErr OGRXLSXLayer::SetFeature( OGRFeature *poFeature )
     125                 : {
     126             632 :     Init();
     127             632 :     if (poFeature == NULL)
     128               0 :         return OGRMemLayer::SetFeature(poFeature);
     129                 : 
     130             632 :     long nFID = poFeature->GetFID();
     131             632 :     if (nFID != OGRNullFID)
     132               2 :         poFeature->SetFID(nFID - (1 + bHasHeaderLine));
     133             632 :     SetUpdated();
     134             632 :     OGRErr eErr = OGRMemLayer::SetFeature(poFeature);
     135             632 :     poFeature->SetFID(nFID);
     136             632 :     return eErr;
     137                 : }
     138                 : 
     139                 : /************************************************************************/
     140                 : /*                          DeleteFeature()                             */
     141                 : /************************************************************************/
     142                 : 
     143               0 : OGRErr OGRXLSXLayer::DeleteFeature( long nFID )
     144                 : {
     145               0 :     Init();
     146               0 :     SetUpdated();
     147               0 :     return OGRMemLayer::DeleteFeature(nFID - (1 + bHasHeaderLine));
     148                 : }
     149                 : 
     150                 : /************************************************************************/
     151                 : /*                          OGRXLSXDataSource()                         */
     152                 : /************************************************************************/
     153                 : 
     154              26 : OGRXLSXDataSource::OGRXLSXDataSource()
     155                 : 
     156                 : {
     157              26 :     pszName = NULL;
     158              26 :     bUpdatable = FALSE;
     159              26 :     bUpdated = FALSE;
     160                 : 
     161              26 :     nLayers = 0;
     162              26 :     papoLayers = NULL;
     163                 : 
     164              26 :     bFirstLineIsHeaders = FALSE;
     165                 : 
     166              26 :     oParser = NULL;
     167              26 :     bStopParsing = FALSE;
     168              26 :     nWithoutEventCounter = 0;
     169              26 :     nDataHandlerCounter = 0;
     170              26 :     nStackDepth = 0;
     171              26 :     nDepth = 0;
     172              26 :     nCurLine = 0;
     173              26 :     nCurCol = 0;
     174              26 :     stateStack[0].eVal = STATE_DEFAULT;
     175              26 :     stateStack[0].nBeginDepth = 0;
     176              26 :     bInCellXFS = FALSE;
     177                 : 
     178              26 :     poCurLayer = NULL;
     179                 : 
     180                 :     const char* pszXLSXFieldTypes =
     181              26 :                 CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", "");
     182              26 :     bAutodetectTypes = !EQUAL(pszXLSXFieldTypes, "STRING");
     183              26 : }
     184                 : 
     185                 : /************************************************************************/
     186                 : /*                         ~OGRXLSXDataSource()                          */
     187                 : /************************************************************************/
     188                 : 
     189              26 : OGRXLSXDataSource::~OGRXLSXDataSource()
     190                 : 
     191                 : {
     192              26 :     SyncToDisk();
     193                 : 
     194              26 :     CPLFree( pszName );
     195                 : 
     196             220 :     for(int i=0;i<nLayers;i++)
     197             194 :         delete papoLayers[i];
     198              26 :     CPLFree( papoLayers );
     199              26 : }
     200                 : 
     201                 : /************************************************************************/
     202                 : /*                           TestCapability()                           */
     203                 : /************************************************************************/
     204                 : 
     205              20 : int OGRXLSXDataSource::TestCapability( const char * pszCap )
     206                 : 
     207                 : {
     208              20 :     if( EQUAL(pszCap,ODsCCreateLayer) )
     209              16 :         return bUpdatable;
     210               4 :     else if( EQUAL(pszCap,ODsCDeleteLayer) )
     211               0 :         return bUpdatable;
     212                 :     else
     213               4 :         return FALSE;
     214                 : }
     215                 : 
     216                 : /************************************************************************/
     217                 : /*                              GetLayer()                              */
     218                 : /************************************************************************/
     219                 : 
     220             502 : OGRLayer *OGRXLSXDataSource::GetLayer( int iLayer )
     221                 : 
     222                 : {
     223             502 :     if (iLayer < 0 || iLayer >= nLayers)
     224               0 :         return NULL;
     225                 : 
     226             502 :     return papoLayers[iLayer];
     227                 : }
     228                 : 
     229                 : /************************************************************************/
     230                 : /*                            GetLayerCount()                           */
     231                 : /************************************************************************/
     232                 : 
     233             480 : int OGRXLSXDataSource::GetLayerCount()
     234                 : {
     235             480 :     return nLayers;
     236                 : }
     237                 : 
     238                 : /************************************************************************/
     239                 : /*                                Open()                                */
     240                 : /************************************************************************/
     241                 : 
     242              24 : int OGRXLSXDataSource::Open( const char * pszFilename,
     243                 :                              VSILFILE* fpWorkbook,
     244                 :                              VSILFILE* fpSharedStrings,
     245                 :                              VSILFILE* fpStyles,
     246                 :                              int bUpdateIn )
     247                 : 
     248                 : {
     249              24 :     bUpdatable = bUpdateIn;
     250                 : 
     251              24 :     pszName = CPLStrdup( pszFilename );
     252                 : 
     253              24 :     AnalyseWorkbook(fpWorkbook);
     254              24 :     AnalyseSharedStrings(fpSharedStrings);
     255              24 :     AnalyseStyles(fpStyles);
     256                 : 
     257                 :     /* Remove empty layers at the end, which tend to be there */
     258              24 :     while(nLayers > 1)
     259                 :     {
     260              40 :         if (papoLayers[nLayers-1]->GetFeatureCount() == 0)
     261                 :         {
     262              18 :             delete papoLayers[nLayers-1];
     263              18 :             nLayers --;
     264                 :         }
     265                 :         else
     266              22 :             break;
     267                 :     }
     268                 : 
     269              24 :     return TRUE;
     270                 : }
     271                 : 
     272                 : /************************************************************************/
     273                 : /*                             Create()                                 */
     274                 : /************************************************************************/
     275                 : 
     276               2 : int OGRXLSXDataSource::Create( const char * pszFilename, char **papszOptions )
     277                 : {
     278               2 :     bUpdated = TRUE;
     279               2 :     bUpdatable = TRUE;
     280                 : 
     281               2 :     pszName = CPLStrdup( pszFilename );
     282                 : 
     283               2 :     return TRUE;
     284                 : }
     285                 : 
     286                 : /************************************************************************/
     287                 : /*                           startElementCbk()                          */
     288                 : /************************************************************************/
     289                 : 
     290            4358 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
     291                 :                                     const char **ppszAttr)
     292                 : {
     293            4358 :     ((OGRXLSXDataSource*)pUserData)->startElementCbk(pszName, ppszAttr);
     294            4358 : }
     295                 : 
     296            4358 : void OGRXLSXDataSource::startElementCbk(const char *pszName,
     297                 :                                        const char **ppszAttr)
     298                 : {
     299            4358 :     if (bStopParsing) return;
     300                 : 
     301            4358 :     nWithoutEventCounter = 0;
     302            4358 :     switch(stateStack[nStackDepth].eVal)
     303                 :     {
     304            1670 :         case STATE_DEFAULT: startElementDefault(pszName, ppszAttr); break;
     305             436 :         case STATE_SHEETDATA:   startElementTable(pszName, ppszAttr); break;
     306            1118 :         case STATE_ROW:     startElementRow(pszName, ppszAttr); break;
     307            1134 :         case STATE_CELL:    startElementCell(pszName, ppszAttr); break;
     308                 :         case STATE_TEXTV:   break;
     309                 :         default:            break;
     310                 :     }
     311            4358 :     nDepth++;
     312                 : }
     313                 : 
     314                 : /************************************************************************/
     315                 : /*                            endElementCbk()                           */
     316                 : /************************************************************************/
     317                 : 
     318            4358 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
     319                 : {
     320            4358 :     ((OGRXLSXDataSource*)pUserData)->endElementCbk(pszName);
     321            4358 : }
     322                 : 
     323            4358 : void OGRXLSXDataSource::endElementCbk(const char *pszName)
     324                 : {
     325            4358 :     if (bStopParsing) return;
     326                 : 
     327            4358 :     nWithoutEventCounter = 0;
     328                 : 
     329            4358 :     nDepth--;
     330            4358 :     switch(stateStack[nStackDepth].eVal)
     331                 :     {
     332            1572 :         case STATE_DEFAULT: break;
     333              98 :         case STATE_SHEETDATA:   endElementTable(pszName); break;
     334             436 :         case STATE_ROW:     endElementRow(pszName); break;
     335            1206 :         case STATE_CELL:    endElementCell(pszName); break;
     336                 :         case STATE_TEXTV:   break;
     337                 :         default:            break;
     338                 :     }
     339                 : 
     340            4358 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
     341            2796 :         nStackDepth --;
     342                 : }
     343                 : 
     344                 : /************************************************************************/
     345                 : /*                            dataHandlerCbk()                          */
     346                 : /************************************************************************/
     347                 : 
     348            3206 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
     349                 : {
     350            3206 :     ((OGRXLSXDataSource*)pUserData)->dataHandlerCbk(data, nLen);
     351            3206 : }
     352                 : 
     353            3206 : void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen)
     354                 : {
     355            3206 :     if (bStopParsing) return;
     356                 : 
     357            3206 :     nDataHandlerCounter ++;
     358            3206 :     if (nDataHandlerCounter >= BUFSIZ)
     359                 :     {
     360                 :         CPLError(CE_Failure, CPLE_AppDefined,
     361               0 :                  "File probably corrupted (million laugh pattern)");
     362               0 :         XML_StopParser(oParser, XML_FALSE);
     363               0 :         bStopParsing = TRUE;
     364               0 :         return;
     365                 :     }
     366                 : 
     367            3206 :     nWithoutEventCounter = 0;
     368                 : 
     369            3206 :     switch(stateStack[nStackDepth].eVal)
     370                 :     {
     371             838 :         case STATE_DEFAULT: break;
     372             102 :         case STATE_SHEETDATA:   break;
     373             384 :         case STATE_ROW:     break;
     374             836 :         case STATE_CELL:    break;
     375            1046 :         case STATE_TEXTV:   dataHandlerTextV(data, nLen);
     376                 :         default:            break;
     377                 :     }
     378                 : }
     379                 : 
     380                 : /************************************************************************/
     381                 : /*                                PushState()                           */
     382                 : /************************************************************************/
     383                 : 
     384            3292 : void OGRXLSXDataSource::PushState(HandlerStateEnum eVal)
     385                 : {
     386            3292 :     if (nStackDepth + 1 == STACK_SIZE)
     387                 :     {
     388               0 :         bStopParsing = TRUE;
     389               0 :         return;
     390                 :     }
     391            3292 :     nStackDepth ++;
     392            3292 :     stateStack[nStackDepth].eVal = eVal;
     393            3292 :     stateStack[nStackDepth].nBeginDepth = nDepth;
     394                 : }
     395                 : 
     396                 : /************************************************************************/
     397                 : /*                          GetAttributeValue()                         */
     398                 : /************************************************************************/
     399                 : 
     400            4718 : static const char* GetAttributeValue(const char **ppszAttr,
     401                 :                                      const char* pszKey,
     402                 :                                      const char* pszDefaultVal)
     403                 : {
     404           16602 :     while(*ppszAttr)
     405                 :     {
     406           11572 :         if (strcmp(ppszAttr[0], pszKey) == 0)
     407            4406 :             return ppszAttr[1];
     408            7166 :         ppszAttr += 2;
     409                 :     }
     410             312 :     return pszDefaultVal;
     411                 : }
     412                 : 
     413                 : /************************************************************************/
     414                 : /*                            GetOGRFieldType()                         */
     415                 : /************************************************************************/
     416                 : 
     417            1128 : OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char* pszValue,
     418                 :                                                 const char* pszValueType)
     419                 : {
     420            1128 :     if (!bAutodetectTypes || pszValueType == NULL)
     421              32 :         return OFTString;
     422            1096 :     else if (strcmp(pszValueType, "string") == 0)
     423             410 :         return OFTString;
     424             686 :     else if (strcmp(pszValueType, "float") == 0)
     425                 :     {
     426             504 :         CPLValueType eValueType = CPLGetValueType(pszValue);
     427             504 :         if (eValueType == CPL_VALUE_STRING)
     428               0 :             return OFTString;
     429             504 :         else if (eValueType == CPL_VALUE_INTEGER)
     430             334 :             return OFTInteger;
     431                 :         else
     432             170 :             return OFTReal;
     433                 :     }
     434             182 :     else if (strcmp(pszValueType, "datetime") == 0)
     435                 :     {
     436              76 :         return OFTDateTime;
     437                 :     }
     438             106 :     else if (strcmp(pszValueType, "date") == 0)
     439                 :     {
     440              68 :         return OFTDate;
     441                 :     }
     442              38 :     else if (strcmp(pszValueType, "time") == 0)
     443                 :     {
     444              26 :         return OFTTime;
     445                 :     }
     446                 :     else
     447              12 :         return OFTString;
     448                 : }
     449                 : 
     450                 : /************************************************************************/
     451                 : /*                              SetField()                              */
     452                 : /************************************************************************/
     453                 : 
     454            1156 : static void SetField(OGRFeature* poFeature,
     455                 :                      int i,
     456                 :                      const char* pszValue,
     457                 :                      const char* pszCellType)
     458                 : {
     459            1156 :     if (pszValue[0] == '\0')
     460             304 :         return;
     461                 : 
     462             852 :     OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType();
     463                 : 
     464             958 :     if (strcmp(pszCellType, "time") == 0 ||
     465                 :         strcmp(pszCellType, "date") == 0 ||
     466                 :         strcmp(pszCellType, "datetime") == 0)
     467                 :     {
     468                 :         struct tm sTm;
     469             106 :         double dfNumberOfDaysSince1900 = atof(pszValue);
     470                 : #define NUMBER_OF_DAYS_BETWEEN_1900_AND_1970        25569
     471                 : #define NUMBER_OF_SECONDS_PER_DAY                   86400
     472                 :         GIntBig nUnixTime = (GIntBig)((dfNumberOfDaysSince1900 -
     473                 :                                        NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 )*
     474             106 :                                                 NUMBER_OF_SECONDS_PER_DAY);
     475             106 :         CPLUnixTimeToYMDHMS(nUnixTime, &sTm);
     476                 : 
     477             192 :         if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime)
     478                 :         {
     479                 :             poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
     480              86 :                                 sTm.tm_hour, sTm.tm_min, sTm.tm_sec, 0);
     481                 :         }
     482              20 :         else if (strcmp(pszCellType, "time") == 0)
     483                 :         {
     484                 :             poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d",
     485               4 :                                 sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
     486                 :         }
     487              16 :         else if (strcmp(pszCellType, "date") == 0)
     488                 :         {
     489                 :             poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d",
     490               8 :                                 sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday));
     491                 :         }
     492                 :         else /* if (strcmp(pszCellType, "datetime") == 0) */
     493                 :         {
     494                 :             poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d",
     495                 :                                 sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday,
     496               8 :                                 sTm.tm_hour, sTm.tm_min, sTm.tm_sec));
     497                 :         }
     498                 :     }
     499                 :     else
     500             746 :         poFeature->SetField(i, pszValue);
     501                 : }
     502                 : 
     503                 : /************************************************************************/
     504                 : /*                          DetectHeaderLine()                          */
     505                 : /************************************************************************/
     506                 : 
     507              76 : void OGRXLSXDataSource::DetectHeaderLine()
     508                 : 
     509                 : {
     510              76 :     int bHeaderLineCandidate = TRUE;
     511                 :     size_t i;
     512             344 :     for(i = 0; i < apoFirstLineTypes.size(); i++)
     513                 :     {
     514             304 :         if (apoFirstLineTypes[i] != "string")
     515                 :         {
     516                 :             /* If the values in the first line are not text, then it is */
     517                 :             /* not a header line */
     518              36 :             bHeaderLineCandidate = FALSE;
     519              36 :             break;
     520                 :         }
     521                 :     }
     522                 : 
     523              76 :     size_t nCountTextOnCurLine = 0;
     524              76 :     size_t nCountNonEmptyOnCurLine = 0;
     525             322 :     for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++)
     526                 :     {
     527             246 :         if (apoCurLineTypes[i] == "string")
     528                 :         {
     529                 :             /* If there are only text values on the second line, then we cannot */
     530                 :             /* know if it is a header line or just a regular line */
     531              56 :             nCountTextOnCurLine ++;
     532                 :         }
     533             190 :         else if (apoCurLineTypes[i] != "")
     534                 :         {
     535             176 :             nCountNonEmptyOnCurLine ++;
     536                 :         }
     537                 :     }
     538                 : 
     539              76 :     const char* pszXLSXHeaders = CPLGetConfigOption("OGR_XLSX_HEADERS", "");
     540              76 :     bFirstLineIsHeaders = FALSE;
     541              76 :     if (EQUAL(pszXLSXHeaders, "FORCE"))
     542               2 :         bFirstLineIsHeaders = TRUE;
     543              74 :     else if (EQUAL(pszXLSXHeaders, "DISABLE"))
     544               4 :         bFirstLineIsHeaders = FALSE;
     545              70 :     else if (bHeaderLineCandidate &&
     546                 :              apoFirstLineTypes.size() != 0 &&
     547                 :              apoFirstLineTypes.size() == apoCurLineTypes.size() &&
     548                 :              nCountTextOnCurLine != apoFirstLineTypes.size() &&
     549                 :              nCountNonEmptyOnCurLine != 0)
     550                 :     {
     551              14 :         bFirstLineIsHeaders = TRUE;
     552                 :     }
     553                 :     CPLDebug("XLSX", "%s %s",
     554              76 :              poCurLayer->GetName(),
     555             152 :              bFirstLineIsHeaders ? "has header line" : "has no header line");
     556              76 : }
     557                 : 
     558                 : /************************************************************************/
     559                 : /*                          startElementDefault()                       */
     560                 : /************************************************************************/
     561                 : 
     562            1670 : void OGRXLSXDataSource::startElementDefault(const char *pszName,
     563                 :                                            const char **ppszAttr)
     564                 : {
     565            1670 :     if (strcmp(pszName, "sheetData") == 0)
     566                 :     {
     567              98 :         apoFirstLineValues.resize(0);
     568             196 :         apoFirstLineTypes.resize(0);
     569              98 :         nCurLine = 0;
     570              98 :         PushState(STATE_SHEETDATA);
     571                 :     }
     572            1670 : }
     573                 : 
     574                 : /************************************************************************/
     575                 : /*                          startElementTable()                        */
     576                 : /************************************************************************/
     577                 : 
     578             436 : void OGRXLSXDataSource::startElementTable(const char *pszName,
     579                 :                                          const char **ppszAttr)
     580                 : {
     581             436 :     if (strcmp(pszName, "row") == 0)
     582                 :     {
     583             436 :         PushState(STATE_ROW);
     584                 : 
     585                 :         int nNewCurLine = atoi(
     586             436 :             GetAttributeValue(ppszAttr, "r", "0")) - 1;
     587             996 :         for(;nCurLine<nNewCurLine;)
     588                 :         {
     589             124 :             nCurCol = 0;
     590             124 :             apoCurLineValues.resize(0);
     591             248 :             apoCurLineTypes.resize(0);
     592             124 :             endElementRow("row");
     593                 :         }
     594             436 :         nCurCol = 0;
     595             436 :         apoCurLineValues.resize(0);
     596             872 :         apoCurLineTypes.resize(0);
     597                 :     }
     598             436 : }
     599                 : 
     600                 : /************************************************************************/
     601                 : /*                           endElementTable()                          */
     602                 : /************************************************************************/
     603                 : 
     604              98 : void OGRXLSXDataSource::endElementTable(const char *pszName)
     605                 : {
     606              98 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
     607                 :     {
     608              98 :         CPLAssert(strcmp(pszName, "sheetData") == 0);
     609                 : 
     610              98 :         if (nCurLine == 0 ||
     611                 :             (nCurLine == 1 && apoFirstLineValues.size() == 0))
     612                 :         {
     613                 :             /* We could remove empty sheet, but too late now */
     614                 :         }
     615              80 :         else if (nCurLine == 1)
     616                 :         {
     617                 :             /* If we have only one single line in the sheet */
     618                 :             size_t i;
     619              12 :             for(i = 0; i < apoFirstLineValues.size(); i++)
     620                 :             {
     621               8 :                 const char* pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
     622                 :                 OGRFieldType eType = GetOGRFieldType(apoFirstLineValues[i].c_str(),
     623               8 :                                                      apoFirstLineTypes[i].c_str());
     624               8 :                 OGRFieldDefn oFieldDefn(pszFieldName, eType);
     625               8 :                 poCurLayer->CreateField(&oFieldDefn);
     626                 :             }
     627                 : 
     628               4 :             OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
     629              12 :             for(i = 0; i < apoFirstLineValues.size(); i++)
     630                 :             {
     631                 :                 SetField(poFeature, i, apoFirstLineValues[i].c_str(),
     632               8 :                          apoFirstLineTypes[i].c_str());
     633                 :             }
     634               4 :             poCurLayer->CreateFeature(poFeature);
     635               4 :             delete poFeature;
     636                 :         }
     637                 : 
     638              98 :         if (poCurLayer)
     639                 :         {
     640              98 :             ((OGRMemLayer*)poCurLayer)->SetUpdatable(bUpdatable);
     641              98 :             ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE);
     642              98 :             ((OGRXLSXLayer*)poCurLayer)->SetUpdated(FALSE);
     643                 :         }
     644                 : 
     645              98 :         poCurLayer = NULL;
     646                 :     }
     647              98 : }
     648                 : 
     649                 : /************************************************************************/
     650                 : /*                            startElementRow()                         */
     651                 : /************************************************************************/
     652                 : 
     653            1118 : void OGRXLSXDataSource::startElementRow(const char *pszName,
     654                 :                                        const char **ppszAttr)
     655                 : {
     656            1118 :     if (strcmp(pszName, "c") == 0)
     657                 :     {
     658            1118 :         PushState(STATE_CELL);
     659                 : 
     660            1118 :         const char* pszR = GetAttributeValue(ppszAttr, "r", NULL);
     661            1118 :         if (pszR)
     662                 :         {
     663                 :             /* Convert col number from base 26 */
     664            1118 :             int nNewCurCol = (pszR[0] - 'A');
     665            1118 :             int i = 1;
     666            2236 :             while(pszR[i] >= 'A' && pszR[i] <= 'Z')
     667                 :             {
     668               0 :                 nNewCurCol = nNewCurCol * 26 + (pszR[i] - 'A');
     669               0 :                 i ++;
     670                 :             }
     671            2700 :             for(;nCurCol<nNewCurCol;nCurCol++)
     672                 :             {
     673             232 :                 apoCurLineValues.push_back("");
     674             464 :                 apoCurLineTypes.push_back("");
     675                 :             }
     676                 :         }
     677                 : 
     678            1118 :         osValueType = "float";
     679                 : 
     680            1118 :         const char* pszS = GetAttributeValue(ppszAttr, "s", "-1");
     681            1118 :         int nS = atoi(pszS);
     682            2066 :         if (nS >= 0 && nS < (int)apoStyles.size())
     683                 :         {
     684             948 :             OGRFieldType eType = apoStyles[nS];
     685             948 :             if (eType == OFTDateTime)
     686              50 :                 osValueType = "datetime";
     687             898 :             else if (eType == OFTDate)
     688              40 :                 osValueType = "date";
     689             858 :             else if (eType == OFTTime)
     690              16 :                 osValueType = "time";
     691                 :         }
     692             170 :         else if (nS != -1)
     693               0 :             CPLDebug("XLSX", "Cannot find style %d", nS);
     694                 : 
     695            1118 :         const char* pszT = GetAttributeValue(ppszAttr, "t", "");
     696            1118 :         if ( EQUAL(pszT,"s"))
     697             492 :             osValueType = "stringLookup";
     698             626 :         else if( EQUAL(pszT,"inlineStr") )
     699              52 :             osValueType = "string";
     700                 : 
     701            1118 :         osValue = "";
     702                 :     }
     703            1118 : }
     704                 : 
     705                 : /************************************************************************/
     706                 : /*                            endElementRow()                           */
     707                 : /************************************************************************/
     708                 : 
     709             560 : void OGRXLSXDataSource::endElementRow(const char *pszName)
     710                 : {
     711             560 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
     712                 :     {
     713             560 :         CPLAssert(strcmp(pszName, "row") == 0);
     714                 : 
     715                 :         OGRFeature* poFeature;
     716                 :         size_t i;
     717                 : 
     718                 :         /* Backup first line values and types in special arrays */
     719             560 :         if (nCurLine == 0)
     720                 :         {
     721              80 :             apoFirstLineTypes = apoCurLineTypes;
     722              80 :             apoFirstLineValues = apoCurLineValues;
     723                 : 
     724                 :     #if skip_leading_empty_rows
     725                 :             if (apoFirstLineTypes.size() == 0)
     726                 :             {
     727                 :                 /* Skip leading empty rows */
     728                 :                 apoFirstLineTypes.resize(0);
     729                 :                 apoFirstLineValues.resize(0);
     730                 :                 return;
     731                 :             }
     732                 :     #endif
     733                 :         }
     734                 : 
     735             560 :         if (nCurLine == 1)
     736                 :         {
     737              76 :             DetectHeaderLine();
     738                 : 
     739              76 :             poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders);
     740                 : 
     741              76 :             if (bFirstLineIsHeaders)
     742                 :             {
     743             210 :                 for(i = 0; i < apoFirstLineValues.size(); i++)
     744                 :                 {
     745             194 :                     const char* pszFieldName = apoFirstLineValues[i].c_str();
     746             194 :                     if (pszFieldName[0] == '\0')
     747               0 :                         pszFieldName = CPLSPrintf("Field%d", (int)i + 1);
     748             194 :                     OGRFieldType eType = OFTString;
     749             194 :                     if (i < apoCurLineValues.size())
     750                 :                     {
     751                 :                         eType = GetOGRFieldType(apoCurLineValues[i].c_str(),
     752             194 :                                                 apoCurLineTypes[i].c_str());
     753                 :                     }
     754             194 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
     755             194 :                     poCurLayer->CreateField(&oFieldDefn);
     756                 :                 }
     757                 :             }
     758                 :             else
     759                 :             {
     760             192 :                 for(i = 0; i < apoFirstLineValues.size(); i++)
     761                 :                 {
     762                 :                     const char* pszFieldName =
     763             132 :                         CPLSPrintf("Field%d", (int)i + 1);
     764                 :                     OGRFieldType eType = GetOGRFieldType(
     765                 :                                             apoFirstLineValues[i].c_str(),
     766             132 :                                             apoFirstLineTypes[i].c_str());
     767             132 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
     768             132 :                     poCurLayer->CreateField(&oFieldDefn);
     769                 :                 }
     770                 : 
     771              60 :                 poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
     772             192 :                 for(i = 0; i < apoFirstLineValues.size(); i++)
     773                 :                 {
     774                 :                     SetField(poFeature, i, apoFirstLineValues[i].c_str(),
     775             132 :                              apoFirstLineTypes[i].c_str());
     776                 :                 }
     777              60 :                 poCurLayer->CreateFeature(poFeature);
     778              60 :                 delete poFeature;
     779                 :             }
     780                 :         }
     781                 : 
     782             560 :         if (nCurLine >= 1)
     783                 :         {
     784                 :             /* Add new fields found on following lines. */
     785             960 :             if (apoCurLineValues.size() >
     786             480 :                 (size_t)poCurLayer->GetLayerDefn()->GetFieldCount())
     787                 :             {
     788             154 :                 for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount();
     789                 :                     i < apoCurLineValues.size();
     790                 :                     i++)
     791                 :                 {
     792                 :                     const char* pszFieldName =
     793             100 :                         CPLSPrintf("Field%d", (int)i + 1);
     794                 :                     OGRFieldType eType = GetOGRFieldType(
     795                 :                                                 apoCurLineValues[i].c_str(),
     796             100 :                                                 apoCurLineTypes[i].c_str());
     797             100 :                     OGRFieldDefn oFieldDefn(pszFieldName, eType);
     798             100 :                     poCurLayer->CreateField(&oFieldDefn);
     799                 :                 }
     800                 :             }
     801                 : 
     802                 :             /* Update field type if necessary */
     803             480 :             if (bAutodetectTypes)
     804                 :             {
     805            1418 :                 for(i = 0; i < apoCurLineValues.size(); i++)
     806                 :                 {
     807             948 :                     if (apoCurLineValues[i].size())
     808                 :                     {
     809                 :                         OGRFieldType eValType = GetOGRFieldType(
     810                 :                                                 apoCurLineValues[i].c_str(),
     811             694 :                                                 apoCurLineTypes[i].c_str());
     812                 :                         OGRFieldType eFieldType =
     813             694 :                             poCurLayer->GetLayerDefn()->GetFieldDefn(i)->GetType();
     814             694 :                         if (eFieldType == OFTDateTime &&
     815                 :                             (eValType == OFTDate || eValType == OFTTime) )
     816                 :                         {
     817                 :                             /* ok */
     818                 :                         }
     819             694 :                         else if (eFieldType == OFTReal && eValType == OFTInteger)
     820                 :                         {
     821                 :                            /* ok */;
     822                 :                         }
     823             686 :                         else if (eFieldType != OFTString && eValType != eFieldType)
     824                 :                         {
     825                 :                             OGRFieldDefn oNewFieldDefn(
     826              44 :                                 poCurLayer->GetLayerDefn()->GetFieldDefn(i));
     827              52 :                             if ((eFieldType == OFTDate || eFieldType == OFTTime) &&
     828                 :                                    eValType == OFTDateTime)
     829               8 :                                 oNewFieldDefn.SetType(OFTDateTime);
     830              44 :                             else if (eFieldType == OFTInteger &&
     831                 :                                      eValType == OFTReal)
     832               8 :                                 oNewFieldDefn.SetType(OFTReal);
     833                 :                             else
     834              28 :                                 oNewFieldDefn.SetType(OFTString);
     835                 :                             poCurLayer->AlterFieldDefn(i, &oNewFieldDefn,
     836              44 :                                                        ALTER_TYPE_FLAG);
     837                 :                         }
     838                 :                     }
     839                 :                 }
     840                 :             }
     841                 : 
     842                 :             /* Add feature for current line */
     843             480 :             poFeature = new OGRFeature(poCurLayer->GetLayerDefn());
     844            1496 :             for(i = 0; i < apoCurLineValues.size(); i++)
     845                 :             {
     846                 :                 SetField(poFeature, i, apoCurLineValues[i].c_str(),
     847            1016 :                          apoCurLineTypes[i].c_str());
     848                 :             }
     849             480 :             poCurLayer->CreateFeature(poFeature);
     850             480 :             delete poFeature;
     851                 :        }
     852                 : 
     853             560 :         nCurLine++;
     854                 :     }
     855             560 : }
     856                 : 
     857                 : /************************************************************************/
     858                 : /*                           startElementCell()                         */
     859                 : /************************************************************************/
     860                 : 
     861            1134 : void OGRXLSXDataSource::startElementCell(const char *pszName,
     862                 :                                         const char **ppszAttr)
     863                 : {
     864            1134 :     if (osValue.size() == 0 && strcmp(pszName, "v") == 0)
     865                 :     {
     866             994 :         PushState(STATE_TEXTV);
     867                 :     }
     868             140 :     else if (osValue.size() == 0 && strcmp(pszName, "t") == 0)
     869                 :     {
     870              52 :         PushState(STATE_TEXTV);
     871                 :     }
     872            1134 : }
     873                 : 
     874                 : /************************************************************************/
     875                 : /*                            endElementCell()                          */
     876                 : /************************************************************************/
     877                 : 
     878            1206 : void OGRXLSXDataSource::endElementCell(const char *pszName)
     879                 : {
     880            1206 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
     881                 :     {
     882            1118 :         CPLAssert(strcmp(pszName, "c") == 0);
     883                 : 
     884            1118 :         if (osValueType == "stringLookup")
     885                 :         {
     886             492 :             int nIndex = atoi(osValue);
     887             492 :             if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size()))
     888             492 :                 osValue = apoSharedStrings[nIndex];
     889                 :             else
     890               0 :                 CPLDebug("XLSX", "Cannot find string %d", nIndex);
     891             492 :             osValueType = "string";
     892                 :         }
     893                 : 
     894            1118 :         apoCurLineValues.push_back(osValue);
     895            1118 :         apoCurLineTypes.push_back(osValueType);
     896                 : 
     897            1118 :         nCurCol += 1;
     898                 :     }
     899            1206 : }
     900                 : 
     901                 : /************************************************************************/
     902                 : /*                           dataHandlerTextV()                         */
     903                 : /************************************************************************/
     904                 : 
     905            1046 : void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen)
     906                 : {
     907            1046 :     osValue.append(data, nLen);
     908            1046 : }
     909                 : 
     910                 : /************************************************************************/
     911                 : /*                              BuildLayer()                            */
     912                 : /************************************************************************/
     913                 : 
     914             126 : void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer* poLayer, int nSheetId)
     915                 : {
     916             126 :     poCurLayer = poLayer;
     917                 : 
     918                 :     CPLString osSheetFilename(
     919             126 :         CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, nSheetId));
     920             126 :     const char* pszSheetFilename = osSheetFilename.c_str();
     921             126 :     VSILFILE* fp = VSIFOpenL(pszSheetFilename, "rb");
     922             126 :     if (fp == NULL)
     923                 :         return;
     924                 : 
     925              98 :     int bUpdatedBackup = bUpdated;
     926                 : 
     927              98 :     oParser = OGRCreateExpatXMLParser();
     928              98 :     XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
     929              98 :     XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
     930              98 :     XML_SetUserData(oParser, this);
     931                 : 
     932              98 :     VSIFSeekL( fp, 0, SEEK_SET );
     933                 : 
     934              98 :     bStopParsing = FALSE;
     935              98 :     nWithoutEventCounter = 0;
     936              98 :     nDataHandlerCounter = 0;
     937              98 :     nStackDepth = 0;
     938              98 :     nDepth = 0;
     939              98 :     stateStack[0].eVal = STATE_DEFAULT;
     940              98 :     stateStack[0].nBeginDepth = 0;
     941                 : 
     942                 :     char aBuf[BUFSIZ];
     943                 :     int nDone;
     944              98 :     do
     945                 :     {
     946              98 :         nDataHandlerCounter = 0;
     947                 :         unsigned int nLen =
     948              98 :             (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
     949              98 :         nDone = VSIFEofL(fp);
     950              98 :         if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
     951                 :         {
     952                 :             CPLError(CE_Failure, CPLE_AppDefined,
     953                 :                      "XML parsing of %s file failed : %s at line %d, column %d",
     954                 :                      pszSheetFilename,
     955                 :                      XML_ErrorString(XML_GetErrorCode(oParser)),
     956                 :                      (int)XML_GetCurrentLineNumber(oParser),
     957               0 :                      (int)XML_GetCurrentColumnNumber(oParser));
     958               0 :             bStopParsing = TRUE;
     959                 :         }
     960              98 :         nWithoutEventCounter ++;
     961                 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
     962                 : 
     963              98 :     XML_ParserFree(oParser);
     964              98 :     oParser = NULL;
     965                 : 
     966              98 :     if (nWithoutEventCounter == 10)
     967                 :     {
     968                 :         CPLError(CE_Failure, CPLE_AppDefined,
     969               0 :                  "Too much data inside one element. File probably corrupted");
     970               0 :         bStopParsing = TRUE;
     971                 :     }
     972                 : 
     973              98 :     VSIFCloseL(fp);
     974                 : 
     975              98 :     bUpdated = bUpdatedBackup;
     976                 : }
     977                 : 
     978                 : /************************************************************************/
     979                 : /*                          startElementSSCbk()                         */
     980                 : /************************************************************************/
     981                 : 
     982            1210 : static void XMLCALL startElementSSCbk(void *pUserData, const char *pszName,
     983                 :                                     const char **ppszAttr)
     984                 : {
     985            1210 :     ((OGRXLSXDataSource*)pUserData)->startElementSSCbk(pszName, ppszAttr);
     986            1210 : }
     987                 : 
     988            1210 : void OGRXLSXDataSource::startElementSSCbk(const char *pszName,
     989                 :                                        const char **ppszAttr)
     990                 : {
     991            1210 :     if (bStopParsing) return;
     992                 : 
     993            1210 :     nWithoutEventCounter = 0;
     994            1210 :     switch(stateStack[nStackDepth].eVal)
     995                 :     {
     996                 :         case STATE_DEFAULT:
     997                 :         {
     998            1210 :             if (strcmp(pszName,"t") == 0)
     999                 :             {
    1000             594 :                 PushState(STATE_T);
    1001             594 :                 osCurrentString = "";
    1002                 :             }
    1003                 :             break;
    1004                 :         }
    1005                 :         default:
    1006                 :             break;
    1007                 :     }
    1008            1210 :     nDepth++;
    1009                 : }
    1010                 : 
    1011                 : /************************************************************************/
    1012                 : /*                           endElementSSCbk()                          */
    1013                 : /************************************************************************/
    1014                 : 
    1015            1210 : static void XMLCALL endElementSSCbk(void *pUserData, const char *pszName)
    1016                 : {
    1017            1210 :     ((OGRXLSXDataSource*)pUserData)->endElementSSCbk(pszName);
    1018            1210 : }
    1019                 : 
    1020            1210 : void OGRXLSXDataSource::endElementSSCbk(const char *pszName)
    1021                 : {
    1022            1210 :     if (bStopParsing) return;
    1023                 : 
    1024            1210 :     nWithoutEventCounter = 0;
    1025                 : 
    1026            1210 :     nDepth--;
    1027            1210 :     switch(stateStack[nStackDepth].eVal)
    1028                 :     {
    1029             616 :         case STATE_DEFAULT: break;
    1030                 :         case STATE_T:
    1031                 :         {
    1032             594 :             if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1033                 :             {
    1034             594 :                 apoSharedStrings.push_back(osCurrentString);
    1035                 :             }
    1036                 :             break;
    1037                 :         }
    1038                 :         default:            break;
    1039                 :     }
    1040                 : 
    1041            1210 :     if (stateStack[nStackDepth].nBeginDepth == nDepth)
    1042             616 :         nStackDepth --;
    1043                 : }
    1044                 : 
    1045                 : /************************************************************************/
    1046                 : /*                           dataHandlerSSCbk()                         */
    1047                 : /************************************************************************/
    1048                 : 
    1049             982 : static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data, int nLen)
    1050                 : {
    1051             982 :     ((OGRXLSXDataSource*)pUserData)->dataHandlerSSCbk(data, nLen);
    1052             982 : }
    1053                 : 
    1054             982 : void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen)
    1055                 : {
    1056             982 :     if (bStopParsing) return;
    1057                 : 
    1058             982 :     nDataHandlerCounter ++;
    1059             982 :     if (nDataHandlerCounter >= BUFSIZ)
    1060                 :     {
    1061                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1062               0 :                  "File probably corrupted (million laugh pattern)");
    1063               0 :         XML_StopParser(oParser, XML_FALSE);
    1064               0 :         bStopParsing = TRUE;
    1065               0 :         return;
    1066                 :     }
    1067                 : 
    1068             982 :     nWithoutEventCounter = 0;
    1069                 : 
    1070             982 :     switch(stateStack[nStackDepth].eVal)
    1071                 :     {
    1072             328 :         case STATE_DEFAULT: break;
    1073             654 :         case STATE_T:       osCurrentString.append(data, nLen); break;
    1074                 :         default:            break;
    1075                 :     }
    1076                 : }
    1077                 : 
    1078                 : /************************************************************************/
    1079                 : /*                          AnalyseSharedStrings()                      */
    1080                 : /************************************************************************/
    1081                 : 
    1082              24 : void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE* fpSharedStrings)
    1083                 : {
    1084              24 :     if (fpSharedStrings == NULL)
    1085               2 :         return;
    1086                 : 
    1087              22 :     oParser = OGRCreateExpatXMLParser();
    1088              22 :     XML_SetElementHandler(oParser, ::startElementSSCbk, ::endElementSSCbk);
    1089              22 :     XML_SetCharacterDataHandler(oParser, ::dataHandlerSSCbk);
    1090              22 :     XML_SetUserData(oParser, this);
    1091                 : 
    1092              22 :     VSIFSeekL( fpSharedStrings, 0, SEEK_SET );
    1093                 : 
    1094              22 :     bStopParsing = FALSE;
    1095              22 :     nWithoutEventCounter = 0;
    1096              22 :     nDataHandlerCounter = 0;
    1097              22 :     nStackDepth = 0;
    1098              22 :     nDepth = 0;
    1099              22 :     stateStack[0].eVal = STATE_DEFAULT;
    1100              22 :     stateStack[0].nBeginDepth = 0;
    1101                 : 
    1102                 :     char aBuf[BUFSIZ];
    1103                 :     int nDone;
    1104              22 :     do
    1105                 :     {
    1106              22 :         nDataHandlerCounter = 0;
    1107                 :         unsigned int nLen =
    1108              22 :             (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSharedStrings );
    1109              22 :         nDone = VSIFEofL(fpSharedStrings);
    1110              22 :         if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
    1111                 :         {
    1112                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1113                 :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1114                 :                      "sharedStrings.xml",
    1115                 :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1116                 :                      (int)XML_GetCurrentLineNumber(oParser),
    1117               0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1118               0 :             bStopParsing = TRUE;
    1119                 :         }
    1120              22 :         nWithoutEventCounter ++;
    1121                 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1122                 : 
    1123              22 :     XML_ParserFree(oParser);
    1124              22 :     oParser = NULL;
    1125                 : 
    1126              22 :     if (nWithoutEventCounter == 10)
    1127                 :     {
    1128                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1129               0 :                  "Too much data inside one element. File probably corrupted");
    1130               0 :         bStopParsing = TRUE;
    1131                 :     }
    1132                 : 
    1133              22 :     VSIFCloseL(fpSharedStrings);
    1134              22 :     fpSharedStrings = NULL;
    1135                 : }
    1136                 : 
    1137                 : 
    1138                 : /************************************************************************/
    1139                 : /*                          startElementWBCbk()                         */
    1140                 : /************************************************************************/
    1141                 : 
    1142             392 : static void XMLCALL startElementWBCbk(void *pUserData, const char *pszName,
    1143                 :                                     const char **ppszAttr)
    1144                 : {
    1145             392 :     ((OGRXLSXDataSource*)pUserData)->startElementWBCbk(pszName, ppszAttr);
    1146             392 : }
    1147                 : 
    1148             392 : void OGRXLSXDataSource::startElementWBCbk(const char *pszName,
    1149                 :                                        const char **ppszAttr)
    1150                 : {
    1151             392 :     if (bStopParsing) return;
    1152                 : 
    1153             392 :     nWithoutEventCounter = 0;
    1154             392 :     if (strcmp(pszName,"sheet") == 0)
    1155                 :     {
    1156             196 :         const char* pszSheetName = GetAttributeValue(ppszAttr, "name", NULL);
    1157             196 :         const char* pszSheetId = GetAttributeValue(ppszAttr, "sheetId", NULL);
    1158             196 :         if (pszSheetName && pszSheetId)
    1159                 :         {
    1160             196 :             int nSheetId = atoi(pszSheetId);
    1161             196 :             papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
    1162             196 :             papoLayers[nLayers++] = new OGRXLSXLayer(this, nSheetId, pszSheetName);
    1163                 :         }
    1164                 :     }
    1165                 : }
    1166                 : 
    1167                 : /************************************************************************/
    1168                 : /*                             AnalyseWorkbook()                        */
    1169                 : /************************************************************************/
    1170                 : 
    1171              24 : void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE* fpWorkbook)
    1172                 : {
    1173              24 :     oParser = OGRCreateExpatXMLParser();
    1174              24 :     XML_SetElementHandler(oParser, ::startElementWBCbk, NULL);
    1175              24 :     XML_SetUserData(oParser, this);
    1176                 : 
    1177              24 :     VSIFSeekL( fpWorkbook, 0, SEEK_SET );
    1178                 : 
    1179              24 :     bStopParsing = FALSE;
    1180              24 :     nWithoutEventCounter = 0;
    1181              24 :     nDataHandlerCounter = 0;
    1182                 : 
    1183                 :     char aBuf[BUFSIZ];
    1184                 :     int nDone;
    1185              24 :     do
    1186                 :     {
    1187              24 :         nDataHandlerCounter = 0;
    1188                 :         unsigned int nLen =
    1189              24 :             (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpWorkbook );
    1190              24 :         nDone = VSIFEofL(fpWorkbook);
    1191              24 :         if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
    1192                 :         {
    1193                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1194                 :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1195                 :                      "workbook.xml",
    1196                 :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1197                 :                      (int)XML_GetCurrentLineNumber(oParser),
    1198               0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1199               0 :             bStopParsing = TRUE;
    1200                 :         }
    1201              24 :         nWithoutEventCounter ++;
    1202                 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1203                 : 
    1204              24 :     XML_ParserFree(oParser);
    1205              24 :     oParser = NULL;
    1206                 : 
    1207              24 :     if (nWithoutEventCounter == 10)
    1208                 :     {
    1209                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1210               0 :                  "Too much data inside one element. File probably corrupted");
    1211               0 :         bStopParsing = TRUE;
    1212                 :     }
    1213                 : 
    1214              24 :     VSIFCloseL(fpWorkbook);
    1215              24 : }
    1216                 : 
    1217                 : 
    1218                 : /************************************************************************/
    1219                 : /*                       startElementStylesCbk()                        */
    1220                 : /************************************************************************/
    1221                 : 
    1222            1652 : static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszName,
    1223                 :                                     const char **ppszAttr)
    1224                 : {
    1225            1652 :     ((OGRXLSXDataSource*)pUserData)->startElementStylesCbk(pszName, ppszAttr);
    1226            1652 : }
    1227                 : 
    1228            1652 : void OGRXLSXDataSource::startElementStylesCbk(const char *pszName,
    1229                 :                                        const char **ppszAttr)
    1230                 : {
    1231            1652 :     if (bStopParsing) return;
    1232                 : 
    1233            1652 :     nWithoutEventCounter = 0;
    1234            1652 :     if (strcmp(pszName,"numFmt") == 0)
    1235                 :     {
    1236             160 :         const char* pszFormatCode = GetAttributeValue(ppszAttr, "formatCode", NULL);
    1237             160 :         const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1238             160 :         int nNumFmtId = atoi(pszNumFmtId);
    1239             160 :         if (pszFormatCode && nNumFmtId >= 164)
    1240                 :         {
    1241                 :             int bHasDate = strstr(pszFormatCode, "DD") != NULL ||
    1242             160 :                            strstr(pszFormatCode, "YY") != NULL;
    1243             160 :             int bHasTime = strstr(pszFormatCode, "HH") != NULL;
    1244             182 :             if (bHasDate && bHasTime)
    1245              22 :                 apoMapStyleFormats[nNumFmtId] = OFTDateTime;
    1246             138 :             else if (bHasDate)
    1247              22 :                 apoMapStyleFormats[nNumFmtId] = OFTDate;
    1248             116 :             else if (bHasTime)
    1249              22 :                 apoMapStyleFormats[nNumFmtId] = OFTTime;
    1250                 :             else
    1251              94 :                 apoMapStyleFormats[nNumFmtId] = OFTReal;
    1252                 :         }
    1253                 :     }
    1254            1492 :     else if (strcmp(pszName,"cellXfs") == 0)
    1255                 :     {
    1256              24 :         bInCellXFS = TRUE;
    1257                 :     }
    1258            1468 :     else if (bInCellXFS && strcmp(pszName,"xf") == 0)
    1259                 :     {
    1260             216 :         const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1");
    1261             216 :         int nNumFmtId = atoi(pszNumFmtId);
    1262             216 :         OGRFieldType eType = OFTReal;
    1263             216 :         if (nNumFmtId >= 0)
    1264                 :         {
    1265             216 :             if (nNumFmtId < 164)
    1266                 :             {
    1267                 :                 // From http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/
    1268               2 :                 if (nNumFmtId >= 14 && nNumFmtId <= 17)
    1269               0 :                     eType = OFTDate;
    1270               2 :                 else if (nNumFmtId >= 18 && nNumFmtId <= 21)
    1271               0 :                     eType = OFTTime;
    1272               2 :                 else if (nNumFmtId == 22)
    1273               0 :                     eType = OFTDateTime;
    1274                 :             }
    1275                 :             else
    1276                 :             {
    1277             214 :                 std::map<int, OGRFieldType>::iterator oIter = apoMapStyleFormats.find(nNumFmtId);
    1278             214 :                 if (oIter != apoMapStyleFormats.end())
    1279             214 :                     eType = oIter->second;
    1280                 :                 else
    1281               0 :                     CPLDebug("XLSX", "Cannot find entry in <numFmts> with numFmtId=%d", nNumFmtId);
    1282                 :             }
    1283                 :         }
    1284                 :         //printf("style[%d] = %d\n", apoStyles.size(), eType);
    1285                 : 
    1286             216 :         apoStyles.push_back(eType);
    1287                 :     }
    1288                 : }
    1289                 : 
    1290                 : /************************************************************************/
    1291                 : /*                       endElementStylesCbk()                          */
    1292                 : /************************************************************************/
    1293                 : 
    1294            1652 : static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszName)
    1295                 : {
    1296            1652 :     ((OGRXLSXDataSource*)pUserData)->endElementStylesCbk(pszName);
    1297            1652 : }
    1298                 : 
    1299            1652 : void OGRXLSXDataSource::endElementStylesCbk(const char *pszName)
    1300                 : {
    1301            1652 :     if (bStopParsing) return;
    1302                 : 
    1303            1652 :     nWithoutEventCounter = 0;
    1304            1652 :     if (strcmp(pszName,"cellXfs") == 0)
    1305                 :     {
    1306              24 :         bInCellXFS = FALSE;
    1307                 :     }
    1308                 : }
    1309                 : 
    1310                 : /************************************************************************/
    1311                 : /*                             AnalyseStyles()                          */
    1312                 : /************************************************************************/
    1313                 : 
    1314              24 : void OGRXLSXDataSource::AnalyseStyles(VSILFILE* fpStyles)
    1315                 : {
    1316              24 :     if (fpStyles == NULL)
    1317               0 :         return;
    1318                 : 
    1319              24 :     oParser = OGRCreateExpatXMLParser();
    1320              24 :     XML_SetElementHandler(oParser, ::startElementStylesCbk, ::endElementStylesCbk);
    1321              24 :     XML_SetUserData(oParser, this);
    1322                 : 
    1323              24 :     VSIFSeekL( fpStyles, 0, SEEK_SET );
    1324                 : 
    1325              24 :     bStopParsing = FALSE;
    1326              24 :     nWithoutEventCounter = 0;
    1327              24 :     nDataHandlerCounter = 0;
    1328              24 :     bInCellXFS = FALSE;
    1329                 : 
    1330                 :     char aBuf[BUFSIZ];
    1331                 :     int nDone;
    1332              24 :     do
    1333                 :     {
    1334              24 :         nDataHandlerCounter = 0;
    1335                 :         unsigned int nLen =
    1336              24 :             (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpStyles );
    1337              24 :         nDone = VSIFEofL(fpStyles);
    1338              24 :         if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
    1339                 :         {
    1340                 :             CPLError(CE_Failure, CPLE_AppDefined,
    1341                 :                      "XML parsing of %s file failed : %s at line %d, column %d",
    1342                 :                      "styles.xml",
    1343                 :                      XML_ErrorString(XML_GetErrorCode(oParser)),
    1344                 :                      (int)XML_GetCurrentLineNumber(oParser),
    1345               0 :                      (int)XML_GetCurrentColumnNumber(oParser));
    1346               0 :             bStopParsing = TRUE;
    1347                 :         }
    1348              24 :         nWithoutEventCounter ++;
    1349                 :     } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
    1350                 : 
    1351              24 :     XML_ParserFree(oParser);
    1352              24 :     oParser = NULL;
    1353                 : 
    1354              24 :     if (nWithoutEventCounter == 10)
    1355                 :     {
    1356                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1357               0 :                  "Too much data inside one element. File probably corrupted");
    1358               0 :         bStopParsing = TRUE;
    1359                 :     }
    1360                 : 
    1361              24 :     VSIFCloseL(fpStyles);
    1362                 : }
    1363                 : 
    1364                 : /************************************************************************/
    1365                 : /*                            CreateLayer()                             */
    1366                 : /************************************************************************/
    1367                 : 
    1368                 : OGRLayer *
    1369              16 : OGRXLSXDataSource::CreateLayer( const char * pszLayerName,
    1370                 :                                 OGRSpatialReference *poSRS,
    1371                 :                                 OGRwkbGeometryType eType,
    1372                 :                                 char ** papszOptions )
    1373                 : 
    1374                 : {
    1375                 : /* -------------------------------------------------------------------- */
    1376                 : /*      Verify we are in update mode.                                   */
    1377                 : /* -------------------------------------------------------------------- */
    1378              16 :     if( !bUpdatable )
    1379                 :     {
    1380                 :         CPLError( CE_Failure, CPLE_NoWriteAccess,
    1381                 :                   "Data source %s opened read-only.\n"
    1382                 :                   "New layer %s cannot be created.\n",
    1383               0 :                   pszName, pszLayerName );
    1384                 : 
    1385               0 :         return NULL;
    1386                 :     }
    1387                 : 
    1388                 : /* -------------------------------------------------------------------- */
    1389                 : /*      Do we already have this layer?  If so, should we blow it        */
    1390                 : /*      away?                                                           */
    1391                 : /* -------------------------------------------------------------------- */
    1392                 :     int iLayer;
    1393                 : 
    1394              72 :     for( iLayer = 0; iLayer < nLayers; iLayer++ )
    1395                 :     {
    1396              56 :         if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
    1397                 :         {
    1398               0 :             if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
    1399                 :                 && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
    1400                 :             {
    1401               0 :                 DeleteLayer( pszLayerName );
    1402                 :             }
    1403                 :             else
    1404                 :             {
    1405                 :                 CPLError( CE_Failure, CPLE_AppDefined,
    1406                 :                           "Layer %s already exists, CreateLayer failed.\n"
    1407                 :                           "Use the layer creation option OVERWRITE=YES to "
    1408                 :                           "replace it.",
    1409               0 :                           pszLayerName );
    1410               0 :                 return NULL;
    1411                 :             }
    1412                 :         }
    1413                 :     }
    1414                 : 
    1415                 : /* -------------------------------------------------------------------- */
    1416                 : /*      Create the layer object.                                        */
    1417                 : /* -------------------------------------------------------------------- */
    1418              16 :     OGRLayer* poLayer = new OGRXLSXLayer(this, nLayers + 1, pszLayerName, TRUE);
    1419                 : 
    1420              16 :     papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
    1421              16 :     papoLayers[nLayers] = poLayer;
    1422              16 :     nLayers ++;
    1423                 : 
    1424              16 :     bUpdated = TRUE;
    1425                 : 
    1426              16 :     return poLayer;
    1427                 : }
    1428                 : 
    1429                 : /************************************************************************/
    1430                 : /*                            DeleteLayer()                             */
    1431                 : /************************************************************************/
    1432                 : 
    1433               0 : void OGRXLSXDataSource::DeleteLayer( const char *pszLayerName )
    1434                 : 
    1435                 : {
    1436                 :     int iLayer;
    1437                 : 
    1438                 : /* -------------------------------------------------------------------- */
    1439                 : /*      Verify we are in update mode.                                   */
    1440                 : /* -------------------------------------------------------------------- */
    1441               0 :     if( !bUpdatable )
    1442                 :     {
    1443                 :         CPLError( CE_Failure, CPLE_NoWriteAccess,
    1444                 :                   "Data source %s opened read-only.\n"
    1445                 :                   "Layer %s cannot be deleted.\n",
    1446               0 :                   pszName, pszLayerName );
    1447                 : 
    1448               0 :         return;
    1449                 :     }
    1450                 : 
    1451                 : /* -------------------------------------------------------------------- */
    1452                 : /*      Try to find layer.                                              */
    1453                 : /* -------------------------------------------------------------------- */
    1454               0 :     for( iLayer = 0; iLayer < nLayers; iLayer++ )
    1455                 :     {
    1456               0 :         if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
    1457               0 :             break;
    1458                 :     }
    1459                 : 
    1460               0 :     if( iLayer == nLayers )
    1461                 :     {
    1462                 :         CPLError( CE_Failure, CPLE_AppDefined,
    1463                 :                   "Attempt to delete layer '%s', but this layer is not known to OGR.",
    1464               0 :                   pszLayerName );
    1465               0 :         return;
    1466                 :     }
    1467                 : 
    1468               0 :     DeleteLayer(iLayer);
    1469                 : }
    1470                 : 
    1471                 : /************************************************************************/
    1472                 : /*                            DeleteLayer()                             */
    1473                 : /************************************************************************/
    1474                 : 
    1475               0 : OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer)
    1476                 : {
    1477               0 :     if( iLayer < 0 || iLayer >= nLayers )
    1478                 :     {
    1479                 :         CPLError( CE_Failure, CPLE_AppDefined,
    1480                 :                   "Layer %d not in legal range of 0 to %d.",
    1481               0 :                   iLayer, nLayers-1 );
    1482               0 :         return OGRERR_FAILURE;
    1483                 :     }
    1484                 : 
    1485                 : /* -------------------------------------------------------------------- */
    1486                 : /*      Blow away our OGR structures related to the layer.  This is     */
    1487                 : /*      pretty dangerous if anything has a reference to this layer!     */
    1488                 : /* -------------------------------------------------------------------- */
    1489                 : 
    1490               0 :     delete papoLayers[iLayer];
    1491                 :     memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
    1492               0 :              sizeof(void *) * (nLayers - iLayer - 1) );
    1493               0 :     nLayers--;
    1494                 : 
    1495               0 :     bUpdated = TRUE;
    1496                 : 
    1497               0 :     return OGRERR_NONE;
    1498                 : }
    1499                 : 
    1500                 : /************************************************************************/
    1501                 : /*                            WriteOverride()                           */
    1502                 : /************************************************************************/
    1503                 : 
    1504              60 : static void WriteOverride(VSILFILE* fp, const char* pszPartName, const char* pszContentType)
    1505                 : {
    1506                 :     VSIFPrintfL(fp, "<Override PartName=\"%s\" ContentType=\"%s\"/>\n",
    1507              60 :                 pszPartName, pszContentType);
    1508              60 : }
    1509                 : 
    1510                 : #define XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    1511                 : #define MAIN_NS "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\""
    1512                 : #define SCHEMA_OD "http://schemas.openxmlformats.org/officeDocument/2006"
    1513                 : #define SCHEMA_OD_RS "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    1514                 : #define SCHEMA_PACKAGE "http://schemas.openxmlformats.org/package/2006"
    1515                 : #define SCHEMA_PACKAGE_RS "http://schemas.openxmlformats.org/package/2006/relationships"
    1516                 : 
    1517                 : /************************************************************************/
    1518                 : /*                           WriteContentTypes()                        */
    1519                 : /************************************************************************/
    1520                 : 
    1521               4 : static void WriteContentTypes(const char* pszName, int nLayers)
    1522                 : {
    1523                 :     VSILFILE* fp;
    1524                 : 
    1525               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName), "wb");
    1526               4 :     VSIFPrintfL(fp, XML_HEADER);
    1527               4 :     VSIFPrintfL(fp, "<Types xmlns=\"%s/content-types\">\n", SCHEMA_PACKAGE);
    1528               4 :     WriteOverride(fp, "/_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml");
    1529               4 :     WriteOverride(fp, "/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml");
    1530               4 :     WriteOverride(fp, "/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml");
    1531               4 :     WriteOverride(fp, "/xl/_rels/workbook.xml.rels", "application/vnd.openxmlformats-package.relationships+xml");
    1532              36 :     for(int i=0;i<nLayers;i++)
    1533                 :     {
    1534                 :         WriteOverride(fp, CPLSPrintf("/xl/worksheets/sheet%d.xml", i+1),
    1535              32 :                       "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml");
    1536                 :     }
    1537               4 :     WriteOverride(fp, "/xl/styles.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml");
    1538               4 :     WriteOverride(fp, "/xl/workbook.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
    1539               4 :     WriteOverride(fp, "/xl/sharedStrings.xml","application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml");
    1540               4 :     VSIFPrintfL(fp, "</Types>\n");
    1541               4 :     VSIFCloseL(fp);
    1542               4 : }
    1543                 : 
    1544                 : /************************************************************************/
    1545                 : /*                             WriteApp()                               */
    1546                 : /************************************************************************/
    1547                 : 
    1548               4 : static void WriteApp(const char* pszName)
    1549                 : {
    1550                 :     VSILFILE* fp;
    1551                 : 
    1552               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName), "wb");
    1553               4 :     VSIFPrintfL(fp, XML_HEADER);
    1554                 :     VSIFPrintfL(fp, "<Properties xmlns=\"%s/extended-properties\" "
    1555               4 :                     "xmlns:vt=\"%s/docPropsVTypes\">\n", SCHEMA_OD, SCHEMA_OD);
    1556               4 :     VSIFPrintfL(fp, "<TotalTime>0</TotalTime>\n");
    1557               4 :     VSIFPrintfL(fp, "</Properties>\n");
    1558               4 :     VSIFCloseL(fp);
    1559               4 : }
    1560                 : 
    1561                 : /************************************************************************/
    1562                 : /*                             WriteCore()                              */
    1563                 : /************************************************************************/
    1564                 : 
    1565               4 : static void WriteCore(const char* pszName)
    1566                 : {
    1567                 :     VSILFILE* fp;
    1568                 : 
    1569               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName), "wb");
    1570               4 :     VSIFPrintfL(fp, XML_HEADER);
    1571                 :     VSIFPrintfL(fp, "<cp:coreProperties xmlns:cp=\"%s/metadata/core-properties\" "
    1572                 :                     "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
    1573                 :                     "xmlns:dcmitype=\"http://purl.org/dc/dcmitype/\" "
    1574                 :                     "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
    1575               4 :                     "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n", SCHEMA_PACKAGE);
    1576               4 :     VSIFPrintfL(fp, "<cp:revision>0</cp:revision>\n");
    1577               4 :     VSIFPrintfL(fp, "</cp:coreProperties>\n");
    1578               4 :     VSIFCloseL(fp);
    1579               4 : }
    1580                 : 
    1581                 : /************************************************************************/
    1582                 : /*                            WriteWorkbook()                           */
    1583                 : /************************************************************************/
    1584                 : 
    1585               4 : static void WriteWorkbook(const char* pszName, OGRDataSource* poDS)
    1586                 : {
    1587                 :     VSILFILE* fp;
    1588                 : 
    1589               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName), "wb");
    1590               4 :     VSIFPrintfL(fp, XML_HEADER);
    1591               4 :     VSIFPrintfL(fp, "<workbook %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    1592               4 :     VSIFPrintfL(fp, "<fileVersion appName=\"Calc\"/>\n");
    1593                 :     /*
    1594                 :     VSIFPrintfL(fp, "<workbookPr backupFile=\"false\" showObjects=\"all\" date1904=\"false\"/>\n");
    1595                 :     VSIFPrintfL(fp, "<workbookProtection/>\n");
    1596                 :     VSIFPrintfL(fp, "<bookViews>\n");
    1597                 :     VSIFPrintfL(fp, "<workbookView activeTab=\"0\" firstSheet=\"0\" showHorizontalScroll=\"true\" "
    1598                 :                     "showSheetTabs=\"true\" showVerticalScroll=\"true\" tabRatio=\"600\" windowHeight=\"8192\" "
    1599                 :                     "windowWidth=\"16384\" xWindow=\"0\" yWindow=\"0\"/>\n");
    1600                 :     VSIFPrintfL(fp, "</bookViews>\n");
    1601                 :     */
    1602               4 :     VSIFPrintfL(fp, "<sheets>\n");
    1603              36 :     for(int i=0;i<poDS->GetLayerCount();i++)
    1604                 :     {
    1605              32 :         OGRXLSXLayer* poLayer = (OGRXLSXLayer*) poDS->GetLayer(i);
    1606              32 :         const char* pszLayerName = poLayer->GetName();
    1607              32 :         char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName);
    1608              32 :         VSIFPrintfL(fp, "<sheet name=\"%s\" sheetId=\"%d\" state=\"visible\" r:id=\"rId%d\"/>\n", pszXML, i+1, i+2);
    1609              32 :         CPLFree(pszXML);
    1610                 :     }
    1611               4 :     VSIFPrintfL(fp, "</sheets>\n");
    1612               4 :     VSIFPrintfL(fp, "<calcPr iterateCount=\"100\" refMode=\"A1\" iterate=\"false\" iterateDelta=\"0.001\"/>\n");
    1613               4 :     VSIFPrintfL(fp, "</workbook>\n");
    1614               4 :     VSIFCloseL(fp);
    1615               4 : }
    1616                 : 
    1617                 : /************************************************************************/
    1618                 : /*                            BuildColString()                          */
    1619                 : /************************************************************************/
    1620                 : 
    1621             206 : static void BuildColString(char szCol[5], int nCol)
    1622                 : {
    1623             206 :     int k = 0;
    1624             206 :     szCol[k++] = (nCol % 26) + 'A';
    1625             412 :     while(nCol >= 26)
    1626                 :     {
    1627               0 :         nCol /= 26;
    1628               0 :         szCol[k++] = (nCol % 26) + 'A';
    1629                 :     }
    1630             206 :     szCol[k] = 0;
    1631             206 :     for(int l=0;l<k/2;l++)
    1632                 :     {
    1633               0 :         char chTmp = szCol[k-1-l];
    1634               0 :         szCol[k-1-l] = szCol[l];
    1635               0 :         szCol[l] = chTmp;
    1636                 :     }
    1637             206 : }
    1638                 : 
    1639                 : /************************************************************************/
    1640                 : /*                             WriteLayer()                             */
    1641                 : /************************************************************************/
    1642                 : 
    1643              32 : static void WriteLayer(const char* pszName, OGRLayer* poLayer, int iLayer,
    1644                 :                        std::map<std::string,int>& oStringMap,
    1645                 :                        std::vector<std::string>& oStringList)
    1646                 : {
    1647                 :     VSILFILE* fp;
    1648                 :     int j;
    1649                 : 
    1650              32 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, iLayer + 1), "wb");
    1651              32 :     VSIFPrintfL(fp, XML_HEADER);
    1652              32 :     VSIFPrintfL(fp, "<worksheet %s xmlns:r=\"%s\">\n", MAIN_NS, SCHEMA_OD_RS);
    1653                 :     /*
    1654                 :     VSIFPrintfL(fp, "<sheetViews>\n");
    1655                 :     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",
    1656                 :                 (i == 0) ? "true" : "false");
    1657                 :     VSIFPrintfL(fp, "<selection activeCell=\"A1\" activeCellId=\"0\" pane=\"topLeft\" sqref=\"A1\"/>\n");
    1658                 :     VSIFPrintfL(fp, "</sheetView>\n");
    1659                 :     VSIFPrintfL(fp, "</sheetViews>\n");*/
    1660                 : 
    1661              32 :     poLayer->ResetReading();
    1662                 : 
    1663              32 :     OGRFeature* poFeature = poLayer->GetNextFeature();
    1664                 : 
    1665              32 :     OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
    1666              32 :     int bHasHeaders = FALSE;
    1667              32 :     int iRow = 1;
    1668                 : 
    1669              32 :     VSIFPrintfL(fp, "<cols>\n");
    1670             124 :     for(j=0;j<poFDefn->GetFieldCount();j++)
    1671                 :     {
    1672              92 :         int nWidth = 15;
    1673              92 :         if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime)
    1674              10 :             nWidth = 25;
    1675                 :         VSIFPrintfL(fp, "<col min=\"%d\" max=\"%d\" width=\"%d\"/>\n",
    1676              92 :                     j+1, 1024, nWidth);
    1677                 : 
    1678              92 :         if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(),
    1679                 :                     CPLSPrintf("Field%d", j+1)) != 0)
    1680              48 :             bHasHeaders = TRUE;
    1681                 :     }
    1682              32 :     VSIFPrintfL(fp, "</cols>\n");
    1683                 : 
    1684              32 :     VSIFPrintfL(fp, "<sheetData>\n");
    1685                 : 
    1686              32 :     if (bHasHeaders && poFeature != NULL)
    1687                 :     {
    1688               4 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    1689              52 :         for(j=0;j<poFDefn->GetFieldCount();j++)
    1690                 :         {
    1691              48 :             const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef();
    1692              48 :             std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
    1693                 :             int nStringIndex;
    1694              48 :             if (oIter != oStringMap.end())
    1695               0 :                 nStringIndex = oIter->second;
    1696                 :             else
    1697                 :             {
    1698              48 :                 nStringIndex = (int)oStringList.size();
    1699              48 :                 oStringMap[pszVal] = nStringIndex;
    1700              96 :                 oStringList.push_back(pszVal);
    1701                 :             }
    1702                 : 
    1703                 :             char szCol[5];
    1704              48 :             BuildColString(szCol, j);
    1705                 : 
    1706              48 :             VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
    1707              48 :             VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    1708              48 :             VSIFPrintfL(fp, "</c>\n");
    1709                 :         }
    1710               4 :         VSIFPrintfL(fp, "</row>\n");
    1711                 : 
    1712               4 :         iRow ++;
    1713                 :     }
    1714                 : 
    1715             162 :     while(poFeature != NULL)
    1716                 :     {
    1717              98 :         VSIFPrintfL(fp, "<row r=\"%d\">\n", iRow);
    1718             602 :         for(j=0;j<poFeature->GetFieldCount();j++)
    1719                 :         {
    1720             504 :             if (poFeature->IsFieldSet(j))
    1721                 :             {
    1722                 :                 char szCol[5];
    1723             158 :                 BuildColString(szCol, j);
    1724                 : 
    1725             158 :                 OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType();
    1726                 : 
    1727             158 :                 if (eType == OFTReal)
    1728                 :                 {
    1729              30 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
    1730              30 :                     VSIFPrintfL(fp, "<v>%.16f</v>\n", poFeature->GetFieldAsDouble(j));
    1731              30 :                     VSIFPrintfL(fp, "</c>\n");
    1732                 :                 }
    1733             128 :                 else if (eType == OFTInteger)
    1734                 :                 {
    1735              18 :                     VSIFPrintfL(fp, "<c r=\"%s%d\">\n", szCol, iRow);
    1736              18 :                     VSIFPrintfL(fp, "<v>%d</v>\n", poFeature->GetFieldAsInteger(j));
    1737              18 :                     VSIFPrintfL(fp, "</c>\n");
    1738                 :                 }
    1739             132 :                 else if (eType == OFTDate || eType == OFTDateTime || eType == OFTTime)
    1740                 :                 {
    1741                 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" s=\"%d\">\n", szCol, iRow,
    1742              22 :                                 (eType == OFTDate) ? 1 : (eType == OFTDateTime) ? 2 : 3);
    1743                 :                     int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
    1744                 :                     poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay,
    1745              22 :                                                     &nHour, &nMinute, &nSecond, &nTZFlag);
    1746                 :                     struct tm brokendowntime;
    1747              22 :                     memset(&brokendowntime, 0, sizeof(brokendowntime));
    1748              22 :                     brokendowntime.tm_year = (eType == OFTTime) ? 70 : nYear - 1900;
    1749              22 :                     brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1;
    1750              22 :                     brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay;
    1751              22 :                     brokendowntime.tm_hour = nHour;
    1752              22 :                     brokendowntime.tm_min = nMinute;
    1753              22 :                     brokendowntime.tm_sec = nSecond;
    1754              22 :                     GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
    1755              22 :                     double dfNumberOfDaysSince1900 = (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY);
    1756              22 :                     if (eType != OFTTime)
    1757              18 :                         dfNumberOfDaysSince1900 += NUMBER_OF_DAYS_BETWEEN_1900_AND_1970;
    1758              22 :                     if (eType == OFTDate)
    1759               4 :                         VSIFPrintfL(fp, "<v>%d</v>\n", (int)(dfNumberOfDaysSince1900 + 0.1));
    1760                 :                     else
    1761              18 :                         VSIFPrintfL(fp, "<v>%.16f</v>\n", dfNumberOfDaysSince1900);
    1762              22 :                     VSIFPrintfL(fp, "</c>\n");
    1763                 :                 }
    1764                 :                 else
    1765                 :                 {
    1766              88 :                     const char* pszVal = poFeature->GetFieldAsString(j);
    1767              88 :                     std::map<std::string,int>::iterator oIter = oStringMap.find(pszVal);
    1768                 :                     int nStringIndex;
    1769              88 :                     if (oIter != oStringMap.end())
    1770              28 :                         nStringIndex = oIter->second;
    1771                 :                     else
    1772                 :                     {
    1773              60 :                         nStringIndex = (int)oStringList.size();
    1774              60 :                         oStringMap[pszVal] = nStringIndex;
    1775             120 :                         oStringList.push_back(pszVal);
    1776                 :                     }
    1777              88 :                     VSIFPrintfL(fp, "<c r=\"%s%d\" t=\"s\">\n", szCol, iRow);
    1778              88 :                     VSIFPrintfL(fp, "<v>%d</v>\n", nStringIndex);
    1779              88 :                     VSIFPrintfL(fp, "</c>\n");
    1780                 :                 }
    1781                 :             }
    1782                 :         }
    1783              98 :         VSIFPrintfL(fp, "</row>\n");
    1784                 : 
    1785              98 :         iRow ++;
    1786              98 :         delete poFeature;
    1787              98 :         poFeature = poLayer->GetNextFeature();
    1788                 :     }
    1789              32 :     VSIFPrintfL(fp, "</sheetData>\n");
    1790              32 :     VSIFPrintfL(fp, "</worksheet>\n");
    1791              32 :     VSIFCloseL(fp);
    1792              32 : }
    1793                 : 
    1794                 : /************************************************************************/
    1795                 : /*                        WriteSharedStrings()                          */
    1796                 : /************************************************************************/
    1797                 : 
    1798               4 : static void WriteSharedStrings(const char* pszName,
    1799                 :                        std::map<std::string,int>& oStringMap,
    1800                 :                        std::vector<std::string>& oStringList)
    1801                 : {
    1802                 :     VSILFILE* fp;
    1803                 : 
    1804               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName), "wb");
    1805               4 :     VSIFPrintfL(fp, XML_HEADER);
    1806                 :     VSIFPrintfL(fp, "<sst %s uniqueCount=\"%d\">\n",
    1807                 :                 MAIN_NS,
    1808               4 :                 (int)oStringList.size());
    1809             112 :     for(int i = 0; i < (int)oStringList.size(); i++)
    1810                 :     {
    1811             108 :         VSIFPrintfL(fp, "<si>\n");
    1812             108 :         char* pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str());
    1813             108 :         VSIFPrintfL(fp, "<t>%s</t>\n", pszXML);
    1814             108 :         CPLFree(pszXML);
    1815             108 :         VSIFPrintfL(fp, "</si>\n");
    1816                 :     }
    1817               4 :     VSIFPrintfL(fp, "</sst>\n");
    1818               4 :     VSIFCloseL(fp);
    1819               4 : }
    1820                 : 
    1821                 : /************************************************************************/
    1822                 : /*                           WriteStyles()                              */
    1823                 : /************************************************************************/
    1824                 : 
    1825               4 : static void WriteStyles(const char* pszName)
    1826                 : {
    1827                 :     VSILFILE* fp;
    1828                 : 
    1829               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName), "wb");
    1830               4 :     VSIFPrintfL(fp, XML_HEADER);
    1831               4 :     VSIFPrintfL(fp, "<styleSheet %s>\n", MAIN_NS);
    1832               4 :     VSIFPrintfL(fp, "<numFmts count=\"4\">\n");
    1833               4 :     VSIFPrintfL(fp, "<numFmt formatCode=\"GENERAL\" numFmtId=\"164\"/>\n");
    1834               4 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YY\" numFmtId=\"165\"/>\n");
    1835               4 :     VSIFPrintfL(fp, "<numFmt formatCode=\"DD/MM/YYYY\\ HH:MM:SS\" numFmtId=\"166\"/>\n");
    1836               4 :     VSIFPrintfL(fp, "<numFmt formatCode=\"HH:MM:SS\" numFmtId=\"167\"/>\n");
    1837               4 :     VSIFPrintfL(fp, "</numFmts>\n");
    1838               4 :     VSIFPrintfL(fp, "<fonts count=\"1\">\n");
    1839               4 :     VSIFPrintfL(fp, "<font>\n");
    1840               4 :     VSIFPrintfL(fp, "<name val=\"Arial\"/>\n");
    1841               4 :     VSIFPrintfL(fp, "<family val=\"2\"/>\n");
    1842               4 :     VSIFPrintfL(fp, "<sz val=\"10\"/>\n");
    1843               4 :     VSIFPrintfL(fp, "</font>\n");
    1844               4 :     VSIFPrintfL(fp, "</fonts>\n");
    1845               4 :     VSIFPrintfL(fp, "<fills count=\"1\">\n");
    1846               4 :     VSIFPrintfL(fp, "<fill>\n");
    1847               4 :     VSIFPrintfL(fp, "<patternFill patternType=\"none\"/>\n");
    1848               4 :     VSIFPrintfL(fp, "</fill>\n");
    1849               4 :     VSIFPrintfL(fp, "</fills>\n");
    1850               4 :     VSIFPrintfL(fp, "<borders count=\"1\">\n");
    1851               4 :     VSIFPrintfL(fp, "<border diagonalDown=\"false\" diagonalUp=\"false\">\n");
    1852               4 :     VSIFPrintfL(fp, "<left/>\n");
    1853               4 :     VSIFPrintfL(fp, "<right/>\n");
    1854               4 :     VSIFPrintfL(fp, "<top/>\n");
    1855               4 :     VSIFPrintfL(fp, "<bottom/>\n");
    1856               4 :     VSIFPrintfL(fp, "<diagonal/>\n");
    1857               4 :     VSIFPrintfL(fp, "</border>\n");
    1858               4 :     VSIFPrintfL(fp, "</borders>\n");
    1859               4 :     VSIFPrintfL(fp, "<cellStyleXfs count=\"1\">\n");
    1860               4 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\">\n");
    1861               4 :     VSIFPrintfL(fp, "</xf>\n");
    1862               4 :     VSIFPrintfL(fp, "</cellStyleXfs>\n");
    1863               4 :     VSIFPrintfL(fp, "<cellXfs count=\"4\">\n");
    1864               4 :     VSIFPrintfL(fp, "<xf numFmtId=\"164\" xfId=\"0\"/>\n");
    1865               4 :     VSIFPrintfL(fp, "<xf numFmtId=\"165\" xfId=\"0\"/>\n");
    1866               4 :     VSIFPrintfL(fp, "<xf numFmtId=\"166\" xfId=\"0\"/>\n");
    1867               4 :     VSIFPrintfL(fp, "<xf numFmtId=\"167\" xfId=\"0\"/>\n");
    1868               4 :     VSIFPrintfL(fp, "</cellXfs>\n");
    1869               4 :     VSIFPrintfL(fp, "<cellStyles count=\"1\">\n");
    1870               4 :     VSIFPrintfL(fp, "<cellStyle builtinId=\"0\" customBuiltin=\"false\" name=\"Normal\" xfId=\"0\"/>\n");
    1871               4 :     VSIFPrintfL(fp, "</cellStyles>\n");
    1872               4 :     VSIFPrintfL(fp, "</styleSheet>\n");
    1873               4 :     VSIFCloseL(fp);
    1874               4 : }
    1875                 : 
    1876                 : /************************************************************************/
    1877                 : /*                           WriteWorkbookRels()                        */
    1878                 : /************************************************************************/
    1879                 : 
    1880               4 : static void WriteWorkbookRels(const char* pszName, int nLayers)
    1881                 : {
    1882                 :     VSILFILE* fp;
    1883                 : 
    1884               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName), "wb");
    1885               4 :     VSIFPrintfL(fp, XML_HEADER);
    1886               4 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    1887               4 :     VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/styles\" Target=\"styles.xml\"/>\n", SCHEMA_OD_RS);
    1888              36 :     for(int i=0;i<nLayers;i++)
    1889                 :     {
    1890                 :         VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/worksheet\" Target=\"worksheets/sheet%d.xml\"/>\n",
    1891              32 :                     2 + i, SCHEMA_OD_RS, 1 + i);
    1892                 :     }
    1893                 :     VSIFPrintfL(fp, "<Relationship Id=\"rId%d\" Type=\"%s/sharedStrings\" Target=\"sharedStrings.xml\"/>\n",
    1894               4 :                 2 + nLayers, SCHEMA_OD_RS);
    1895               4 :     VSIFPrintfL(fp, "</Relationships>\n");
    1896               4 :     VSIFCloseL(fp);
    1897               4 : }
    1898                 : 
    1899                 : /************************************************************************/
    1900                 : /*                             WriteDotRels()                           */
    1901                 : /************************************************************************/
    1902                 : 
    1903               4 : static void WriteDotRels(const char* pszName)
    1904                 : {
    1905                 :     VSILFILE* fp;
    1906                 : 
    1907               4 :     fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName), "wb");
    1908               4 :     VSIFPrintfL(fp, XML_HEADER);
    1909               4 :     VSIFPrintfL(fp, "<Relationships xmlns=\"%s\">\n", SCHEMA_PACKAGE_RS);
    1910               4 :     VSIFPrintfL(fp, "<Relationship Id=\"rId1\" Type=\"%s/officeDocument\" Target=\"xl/workbook.xml\"/>\n", SCHEMA_OD_RS);
    1911               4 :     VSIFPrintfL(fp, "<Relationship Id=\"rId2\" Type=\"%s/metadata/core-properties\" Target=\"docProps/core.xml\"/>\n", SCHEMA_PACKAGE_RS);
    1912               4 :     VSIFPrintfL(fp, "<Relationship Id=\"rId3\" Type=\"%s/extended-properties\" Target=\"docProps/app.xml\"/>\n", SCHEMA_OD_RS);
    1913               4 :     VSIFPrintfL(fp, "</Relationships>\n");
    1914               4 :     VSIFCloseL(fp);
    1915               4 : }
    1916                 : 
    1917                 : /************************************************************************/
    1918                 : /*                            SyncToDisk()                              */
    1919                 : /************************************************************************/
    1920                 : 
    1921              26 : OGRErr OGRXLSXDataSource::SyncToDisk()
    1922                 : {
    1923                 :     int i;
    1924                 : 
    1925              26 :     if (!bUpdated)
    1926              22 :         return OGRERR_NONE;
    1927                 : 
    1928                 :     VSIStatBufL sStat;
    1929               4 :     if (VSIStatL(pszName, &sStat) == 0)
    1930                 :     {
    1931               2 :         if (VSIUnlink( pszName ) != 0)
    1932                 :         {
    1933                 :             CPLError(CE_Failure, CPLE_FileIO,
    1934               0 :                     "Cannot delete %s", pszName);
    1935               0 :             return OGRERR_FAILURE;
    1936                 :         }
    1937                 :     }
    1938                 : 
    1939                 :     /* Cause all layers to be initialized */
    1940              36 :     for(int i = 0; i<nLayers; i++)
    1941                 :     {
    1942              32 :         ((OGRXLSXLayer*)papoLayers[i])->GetLayerDefn();
    1943                 :     }
    1944                 : 
    1945                 :     /* Maintain new ZIP files opened */
    1946               4 :     VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "wb");
    1947               4 :     if (fpZIP == NULL)
    1948                 :     {
    1949                 :         CPLError(CE_Failure, CPLE_FileIO,
    1950               0 :                  "Cannot create %s", pszName);
    1951               0 :         return OGRERR_FAILURE;
    1952                 :     }
    1953                 : 
    1954               4 :     WriteContentTypes(pszName, nLayers);
    1955                 : 
    1956                 :     //VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755);
    1957               4 :     WriteApp(pszName);
    1958               4 :     WriteCore(pszName);
    1959                 : 
    1960                 :     //VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755);
    1961               4 :     WriteWorkbook(pszName, this);
    1962                 : 
    1963               4 :     std::map<std::string,int> oStringMap;
    1964               4 :     std::vector<std::string> oStringList;
    1965                 : 
    1966                 :     //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755);
    1967              36 :     for(i=0;i<nLayers;i++)
    1968                 :     {
    1969              32 :         WriteLayer(pszName, GetLayer(i), i, oStringMap, oStringList);
    1970                 :     }
    1971                 : 
    1972               4 :     WriteSharedStrings(pszName, oStringMap, oStringList);
    1973               4 :     WriteStyles(pszName);
    1974                 : 
    1975                 :     //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/_rels", pszName),0755);
    1976               4 :     WriteWorkbookRels(pszName, nLayers);
    1977                 : 
    1978                 :     //VSIMkdir(CPLSPrintf("/vsizip/%s/_rels", pszName),0755);
    1979               4 :     WriteDotRels(pszName);
    1980                 : 
    1981                 :     /* Now close ZIP file */
    1982               4 :     VSIFCloseL(fpZIP);
    1983                 : 
    1984                 :     /* Reset updated flag at datasource and layer level */
    1985               4 :     bUpdated = FALSE;
    1986              36 :     for(int i = 0; i<nLayers; i++)
    1987                 :     {
    1988              32 :         ((OGRXLSXLayer*)papoLayers[i])->SetUpdated(FALSE);
    1989                 :     }
    1990                 : 
    1991               4 :     return OGRERR_NONE;
    1992                 : }

Generated by: LCOV version 1.7