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