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 4476 : CPLString OGRGMLASTruncateIdentifier(const CPLString &osName, 25 : int nIdentMaxLength) 26 : { 27 4476 : int nExtra = static_cast<int>(osName.size()) - nIdentMaxLength; 28 4476 : CPLAssert(nExtra > 0); 29 : 30 : // Decompose in tokens 31 4476 : char **papszTokens = CSLTokenizeString2(osName, "_", CSLT_ALLOWEMPTYTOKENS); 32 8952 : std::vector<char> achDelimiters; 33 8952 : std::vector<CPLString> aosTokens; 34 18250 : for (int j = 0; papszTokens[j] != nullptr; ++j) 35 : { 36 13774 : const char *pszToken = papszTokens[j]; 37 13774 : bool bIsCamelCase = false; 38 : // Split parts like camelCase or CamelCase into several tokens 39 13774 : if (pszToken[0] != '\0' && pszToken[1] >= 'a' && pszToken[1] <= 'z') 40 : { 41 11952 : bIsCamelCase = true; 42 11952 : bool bLastIsLower = true; 43 23904 : std::vector<CPLString> aoParts; 44 23904 : CPLString osCurrentPart; 45 11952 : osCurrentPart += pszToken[0]; 46 11952 : osCurrentPart += pszToken[1]; 47 143704 : for (int k = 2; pszToken[k]; ++k) 48 : { 49 131943 : if (pszToken[k] >= 'A' && pszToken[k] <= 'Z') 50 : { 51 12552 : if (!bLastIsLower) 52 : { 53 191 : bIsCamelCase = false; 54 191 : break; 55 : } 56 12361 : aoParts.push_back(osCurrentPart); 57 12361 : osCurrentPart.clear(); 58 12361 : bLastIsLower = false; 59 : } 60 : else 61 : { 62 119391 : bLastIsLower = true; 63 : } 64 131752 : osCurrentPart += pszToken[k]; 65 : } 66 11952 : if (bIsCamelCase) 67 : { 68 11761 : if (!osCurrentPart.empty()) 69 11761 : aoParts.push_back(std::move(osCurrentPart)); 70 35685 : for (size_t k = 0; k < aoParts.size(); ++k) 71 : { 72 23924 : achDelimiters.push_back((j > 0 && k == 0) ? '_' : '\0'); 73 23924 : aosTokens.push_back(aoParts[k]); 74 : } 75 : } 76 : } 77 13774 : if (!bIsCamelCase) 78 : { 79 2013 : achDelimiters.push_back((j > 0) ? '_' : '\0'); 80 2013 : aosTokens.push_back(pszToken); 81 : } 82 : } 83 4476 : CSLDestroy(papszTokens); 84 : 85 : // Truncate identifier by removing last character of longest part 86 8952 : std::map<int, std::set<size_t>> oMapLengthToIdx; 87 : // Ignore last token in map creation 88 25937 : for (size_t j = 0; j + 1 < aosTokens.size(); ++j) 89 : { 90 21461 : const int nTokenLen = static_cast<int>(aosTokens[j].size()); 91 21461 : oMapLengthToIdx[nTokenLen].insert(j); 92 : } 93 4476 : int nLastTokenSize = static_cast<int>(aosTokens.back().size()); 94 4476 : 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 4446 : bool bHasDoneSomething = true; 105 87712 : while (nExtra > 0 && bHasDoneSomething) 106 : { 107 83266 : bHasDoneSomething = false; 108 83266 : auto iter = oMapLengthToIdx.end(); 109 83266 : --iter; 110 : // Avoid truncating last token unless it is excessively longer 111 : // than previous ones. 112 83266 : if (nLastTokenSize > 2 * iter->first) 113 : { 114 6054 : aosTokens.back().resize(nLastTokenSize - 1); 115 6054 : nLastTokenSize--; 116 6054 : bHasDoneSomething = true; 117 6054 : nExtra--; 118 : } 119 77212 : else if (iter->first > 1) 120 : { 121 : // Reduce one token by one character 122 76455 : const size_t j = *iter->second.begin(); 123 76455 : aosTokens[j].resize(iter->first - 1); 124 : 125 : // Move it to a new bucket 126 76455 : iter->second.erase(iter->second.begin()); 127 76455 : oMapLengthToIdx[iter->first - 1].insert(j); 128 : 129 : // Remove this bucket if is empty 130 76455 : if (iter->second.empty()) 131 : { 132 29697 : oMapLengthToIdx.erase(iter); 133 : } 134 : 135 76455 : nExtra--; 136 76455 : bHasDoneSomething = true; 137 : } 138 : } 139 : } 140 : 141 : // Reassemble truncated parts 142 4476 : CPLString osNewName; 143 30413 : for (size_t j = 0; j < aosTokens.size(); ++j) 144 : { 145 25937 : if (achDelimiters[j]) 146 9298 : osNewName += achDelimiters[j]; 147 25937 : osNewName += aosTokens[j]; 148 : } 149 : 150 : // If we are still longer than max allowed, truncate beginning of name 151 4476 : if (nExtra > 0) 152 : { 153 757 : osNewName = osNewName.substr(nExtra); 154 : } 155 4476 : CPLAssert(static_cast<int>(osNewName.size()) == nIdentMaxLength); 156 8952 : return osNewName; 157 : } 158 : 159 : /************************************************************************/ 160 : /* OGRGMLASAddSerialNumber() */ 161 : /************************************************************************/ 162 : 163 90 : CPLString OGRGMLASAddSerialNumber(const CPLString &osNameIn, int iOccurrence, 164 : size_t nOccurrences, int nIdentMaxLength) 165 : { 166 90 : CPLString osName(osNameIn); 167 102 : const int nDigitsSize = (nOccurrences < 10) ? 1 168 12 : : (nOccurrences < 100) ? 2 169 : : 3; 170 : char szDigits[4]; 171 90 : snprintf(szDigits, sizeof(szDigits), "%0*d", nDigitsSize, iOccurrence); 172 90 : if (nIdentMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH) 173 : { 174 86 : if (static_cast<int>(osName.size()) < nIdentMaxLength) 175 : { 176 8 : if (static_cast<int>(osName.size()) + nDigitsSize < nIdentMaxLength) 177 : { 178 8 : 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 180 : return osName; 200 : }