Line data Source code
1 : /****************************************************************************** 2 : * Project: OGR 3 : * Purpose: OGRGMLASDriver implementation 4 : * Author: Even Rouault, <even dot rouault at spatialys dot com> 5 : * 6 : * Initial development funded by the European Earth observation programme 7 : * Copernicus 8 : * 9 : ****************************************************************************** 10 : * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com> 11 : * 12 : * SPDX-License-Identifier: MIT 13 : ****************************************************************************/ 14 : 15 : #include "ogr_gmlas.h" 16 : 17 : #include <map> 18 : #include <set> 19 : 20 : /************************************************************************/ 21 : /* OGRGMLASTruncateIdentifier() */ 22 : /************************************************************************/ 23 : 24 3709 : CPLString OGRGMLASTruncateIdentifier(const CPLString &osName, 25 : int nIdentMaxLength) 26 : { 27 3709 : int nExtra = static_cast<int>(osName.size()) - nIdentMaxLength; 28 3709 : CPLAssert(nExtra > 0); 29 : 30 : // Decompose in tokens 31 3709 : char **papszTokens = CSLTokenizeString2(osName, "_", CSLT_ALLOWEMPTYTOKENS); 32 7418 : std::vector<char> achDelimiters; 33 7418 : std::vector<CPLString> aosTokens; 34 14065 : for (int j = 0; papszTokens[j] != nullptr; ++j) 35 : { 36 10356 : const char *pszToken = papszTokens[j]; 37 10356 : bool bIsCamelCase = false; 38 : // Split parts like camelCase or CamelCase into several tokens 39 10356 : if (pszToken[0] != '\0' && pszToken[1] >= 'a' && pszToken[1] <= 'z') 40 : { 41 9143 : bIsCamelCase = true; 42 9143 : bool bLastIsLower = true; 43 18286 : std::vector<CPLString> aoParts; 44 18286 : CPLString osCurrentPart; 45 9143 : osCurrentPart += pszToken[0]; 46 9143 : osCurrentPart += pszToken[1]; 47 93742 : for (int k = 2; pszToken[k]; ++k) 48 : { 49 84790 : if (pszToken[k] >= 'A' && pszToken[k] <= 'Z') 50 : { 51 7107 : if (!bLastIsLower) 52 : { 53 191 : bIsCamelCase = false; 54 191 : break; 55 : } 56 6916 : aoParts.push_back(osCurrentPart); 57 6916 : osCurrentPart.clear(); 58 6916 : bLastIsLower = false; 59 : } 60 : else 61 : { 62 77683 : bLastIsLower = true; 63 : } 64 84599 : osCurrentPart += pszToken[k]; 65 : } 66 9143 : if (bIsCamelCase) 67 : { 68 8952 : if (!osCurrentPart.empty()) 69 8952 : aoParts.push_back(osCurrentPart); 70 24622 : for (size_t k = 0; k < aoParts.size(); ++k) 71 : { 72 15670 : achDelimiters.push_back((j > 0 && k == 0) ? '_' : '\0'); 73 15670 : aosTokens.push_back(aoParts[k]); 74 : } 75 : } 76 : } 77 10356 : if (!bIsCamelCase) 78 : { 79 1404 : achDelimiters.push_back((j > 0) ? '_' : '\0'); 80 1404 : aosTokens.push_back(pszToken); 81 : } 82 : } 83 3709 : CSLDestroy(papszTokens); 84 : 85 : // Truncate identifier by removing last character of longest part 86 7418 : std::map<int, std::set<size_t>> oMapLengthToIdx; 87 : // Ignore last token in map creation 88 17074 : for (size_t j = 0; j + 1 < aosTokens.size(); ++j) 89 : { 90 13365 : const int nTokenLen = static_cast<int>(aosTokens[j].size()); 91 13365 : oMapLengthToIdx[nTokenLen].insert(j); 92 : } 93 3709 : int nLastTokenSize = static_cast<int>(aosTokens.back().size()); 94 3709 : if (oMapLengthToIdx.empty()) 95 : { 96 30 : if (nLastTokenSize > nExtra) 97 : { 98 30 : aosTokens.back().resize(nLastTokenSize - nExtra); 99 30 : nExtra = 0; 100 : } 101 : } 102 : else 103 : { 104 3679 : bool bHasDoneSomething = true; 105 77480 : while (nExtra > 0 && bHasDoneSomething) 106 : { 107 73801 : bHasDoneSomething = false; 108 73801 : auto iter = oMapLengthToIdx.end(); 109 73801 : --iter; 110 : // Avoid truncating last token unless it is excessively longer 111 : // than previous ones. 112 73801 : if (nLastTokenSize > 2 * iter->first) 113 : { 114 6028 : aosTokens.back().resize(nLastTokenSize - 1); 115 6028 : nLastTokenSize--; 116 6028 : bHasDoneSomething = true; 117 6028 : nExtra--; 118 : } 119 67773 : else if (iter->first > 1) 120 : { 121 : // Reduce one token by one character 122 67016 : const size_t j = *iter->second.begin(); 123 67016 : aosTokens[j].resize(iter->first - 1); 124 : 125 : // Move it to a new bucket 126 67016 : iter->second.erase(iter->second.begin()); 127 67016 : oMapLengthToIdx[iter->first - 1].insert(j); 128 : 129 : // Remove this bucket if is empty 130 67016 : if (iter->second.empty()) 131 : { 132 26392 : oMapLengthToIdx.erase(iter); 133 : } 134 : 135 67016 : nExtra--; 136 67016 : bHasDoneSomething = true; 137 : } 138 : } 139 : } 140 : 141 : // Reassemble truncated parts 142 3709 : CPLString osNewName; 143 20783 : for (size_t j = 0; j < aosTokens.size(); ++j) 144 : { 145 17074 : if (achDelimiters[j]) 146 6647 : osNewName += achDelimiters[j]; 147 17074 : osNewName += aosTokens[j]; 148 : } 149 : 150 : // If we are still longer than max allowed, truncate beginning of name 151 3709 : if (nExtra > 0) 152 : { 153 757 : osNewName = osNewName.substr(nExtra); 154 : } 155 3709 : CPLAssert(static_cast<int>(osNewName.size()) == nIdentMaxLength); 156 7418 : return osNewName; 157 : } 158 : 159 : /************************************************************************/ 160 : /* OGRGMLASAddSerialNumber() */ 161 : /************************************************************************/ 162 : 163 82 : CPLString OGRGMLASAddSerialNumber(const CPLString &osNameIn, int iOccurrence, 164 : size_t nOccurrences, int nIdentMaxLength) 165 : { 166 82 : CPLString osName(osNameIn); 167 94 : const int nDigitsSize = (nOccurrences < 10) ? 1 168 12 : : (nOccurrences < 100) ? 2 169 : : 3; 170 : char szDigits[4]; 171 82 : snprintf(szDigits, sizeof(szDigits), "%0*d", nDigitsSize, iOccurrence); 172 82 : if (nIdentMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH) 173 : { 174 78 : if (static_cast<int>(osName.size()) < nIdentMaxLength) 175 : { 176 0 : if (static_cast<int>(osName.size()) + nDigitsSize < nIdentMaxLength) 177 : { 178 0 : osName += szDigits; 179 : } 180 : else 181 : { 182 0 : osName.resize(nIdentMaxLength - nDigitsSize); 183 0 : osName += szDigits; 184 : } 185 : } 186 : else 187 : { 188 : const int nTruncatedSize = 189 78 : static_cast<int>(osName.size()) - nDigitsSize; 190 78 : if (nTruncatedSize >= 0) 191 78 : osName.resize(nTruncatedSize); 192 78 : osName += szDigits; 193 : } 194 : } 195 : else 196 : { 197 4 : osName += szDigits; 198 : } 199 164 : return osName; 200 : }