KiCad PCB EDA Suite
Loading...
Searching...
No Matches
increment.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "increment.h"
21
22#include <wx/wxcrt.h>
23
24#include <cmath>
25#include <iostream>
26#include <regex>
27
28
29KICOMMON_API bool IncrementString( wxString& name, int aIncrement )
30{
31 if( name.IsEmpty() )
32 return true;
33
34 wxString suffix;
35 wxString digits;
36 wxString outputFormat;
37 wxString outputNumber;
38 int ii = name.Len() - 1;
39 int dCount = 0;
40
41 while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) )
42 {
43 suffix = name.GetChar( ii ) + suffix;
44 ii--;
45 }
46
47 while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) )
48 {
49 digits = name.GetChar( ii ) + digits;
50 ii--;
51 dCount++;
52 }
53
54 if( digits.IsEmpty() )
55 return true;
56
57 long number = 0;
58
59 if( digits.ToLong( &number ) )
60 {
61 number += aIncrement;
62
63 // Don't let result go below zero
64 if( number > -1 )
65 {
66 name.Remove( ii + 1 );
67
68 //write out a format string with correct number of leading zeroes
69 outputFormat.Printf( wxS( "%%0%dld" ), dCount );
70
71 //write out the number using the format string
72 outputNumber.Printf( outputFormat, number );
73 name << outputNumber << suffix;
74 return true;
75 }
76 }
77
78 return false;
79}
80
81
82std::optional<wxString> STRING_INCREMENTER::Increment( const wxString& aStr, int aDelta,
83 size_t aRightIndex ) const
84{
85 if( aStr.IsEmpty() )
86 return std::nullopt;
87
88 wxString remaining = aStr;
89 std::vector<std::pair<wxString, STRING_PART_TYPE>> parts;
90 size_t goodParts = 0;
91
92 // Keep popping chunks off the string until we have what we need
93 while( goodParts < ( aRightIndex + 1 ) && !remaining.IsEmpty() )
94 {
95 static const std::regex integerRegex( R"(\d+$)" );
96
97 // ABC or abc but not Abc
98 static const std::regex sameCaseAlphabetRegex( R"(([a-z]+|[A-Z]+)$)" );
99
100 // Skippables - for now anything that isn't a letter or number
101 static const std::regex skipRegex( R"([^a-zA-Z0-9]+$)" );
102
103 std::string remainingStr = remaining.ToStdString();
104 std::smatch match;
105
106 if( std::regex_search( remainingStr, match, integerRegex ) )
107 {
108 parts.push_back( { match.str(), STRING_PART_TYPE::INTEGER } );
109 remaining = remaining.Left( remaining.Len() - match.str().size() );
110 goodParts++;
111 }
112 else if( std::regex_search( remainingStr, match, sameCaseAlphabetRegex ) )
113 {
114 parts.push_back( { match.str(), STRING_PART_TYPE::ALPHABETIC } );
115 remaining = remaining.Left( remaining.Len() - match.str().size() );
116 goodParts++;
117 }
118 else if( std::regex_search( remainingStr, match, skipRegex ) )
119 {
120 parts.push_back( { match.str(), STRING_PART_TYPE::SKIP } );
121 remaining = remaining.Left( remaining.Len() - match.str().size() );
122 }
123 else
124 {
125 // Out of ideas
126 break;
127 }
128 }
129
130 // Couldn't find the part we wanted
131 if( goodParts < aRightIndex + 1 )
132 return std::nullopt;
133
134 // Increment the part we wanted
135 bool didIncrement = incrementPart( parts.back().first, parts.back().second, aDelta );
136
137 if( !didIncrement )
138 return std::nullopt;
139
140 // Reassemble the string - the left-over part, then parts in reverse
141 wxString result = remaining;
142
143 for( auto it = parts.rbegin(); it != parts.rend(); ++it )
144 {
145 result << it->first;
146 }
147
148 return result;
149}
150
151
152static bool containsIOSQXZ( const wxString& aStr )
153{
154 static const wxString iosqxz = "IOSQXZ";
155
156 for( const wxUniChar& c : aStr )
157 {
158 if( iosqxz.Contains( c ) )
159 return true;
160 }
161
162 return false;
163}
164
165
166bool STRING_INCREMENTER::incrementPart( wxString& aPart, STRING_PART_TYPE aType, int aDelta ) const
167{
168 switch( aType )
169 {
171 {
172 long number = 0;
173 bool zeroPadded = aPart.StartsWith( '0' );
174 size_t oldLen = aPart.Len();
175
176 if( aPart.ToLong( &number ) )
177 {
178 number += aDelta;
179
180 // Going below zero makes things awkward
181 // and is not usually that useful.
182 if( number < 0 )
183 return false;
184
185 aPart.Printf( "%ld", number );
186
187 // If the number was zero-padded, we need to re-pad it
188 if( zeroPadded )
189 {
190 aPart = wxString( "0", oldLen - aPart.Len() ) + aPart;
191 }
192
193 return true;
194 }
195
196 break;
197 }
199 {
200 // Covert to uppercase
201 wxString upper = aPart.Upper();
202 bool wasUpper = aPart == upper;
203
204 static const wxString alphabetFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
205 static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
206
207 const wxString& alpha =
208 ( m_SkipIOSQXZ & !containsIOSQXZ( aPart ) ) ? alphaNoIOSQXZ : alphabetFull;
209
210 int index = IndexFromAlphabetic( upper, alpha );
211
212 // Something was not in the alphabet
213 if( index == -1 )
214 return false;
215
216 // It's such a big number that we don't want to increment it
218 return false;
219
220 index += aDelta;
221
222 if( index < 0 )
223 return false;
224
225 wxString newStr = AlphabeticFromIndex( index, alpha, true );
226
227 if( !wasUpper )
228 newStr = newStr.Lower();
229
230 aPart = newStr;
231
232 return true;
233 }
234 case STRING_PART_TYPE::SKIP: break;
235 }
236
237 return false;
238}
239
240
241KICOMMON_API int IndexFromAlphabetic( const wxString& aStr, const wxString& aAlphabet )
242{
243 int index = 0;
244 const int radix = aAlphabet.Length();
245
246 for( size_t i = 0; i < aStr.Len(); i++ )
247 {
248 int alphaIndex = aAlphabet.Find( aStr[i] );
249
250 if( alphaIndex == wxNOT_FOUND )
251 return -1;
252
253 if( i != aStr.Len() - 1 )
254 alphaIndex++;
255
256 index += alphaIndex * pow( radix, aStr.Len() - 1 - i );
257 }
258
259 return index;
260}
261
262
263wxString KICOMMON_API AlphabeticFromIndex( size_t aN, const wxString& aAlphabet,
264 bool aZeroBasedNonUnitCols )
265{
266 wxString itemNum;
267 bool firstRound = true;
268 const int radix = aAlphabet.Length();
269
270 do
271 {
272 int modN = aN % radix;
273
274 if( aZeroBasedNonUnitCols && !firstRound )
275 modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
276
277 itemNum.insert( 0, 1, aAlphabet[modN] );
278
279 aN /= radix;
280 firstRound = false;
281 } while( aN );
282
283 return itemNum;
284}
int index
const char * name
bool incrementPart(wxString &aPart, STRING_PART_TYPE aType, int aDelta) const
std::optional< wxString > Increment(const wxString &aStr, int aDelta, size_t aRightIndex) const
Increment the n-th part from the right of the given string.
Definition increment.cpp:82
KICOMMON_API int IndexFromAlphabetic(const wxString &aStr, const wxString &aAlphabet)
Attempt to convert a string to an integer, assuming it is an alphabetic string like "A",...
wxString KICOMMON_API AlphabeticFromIndex(size_t aN, const wxString &aAlphabet, bool aZeroBasedNonUnitCols)
Get an alphabetic string like A, B, ... Z, AA, AB, ... ZZ, AAA, ...
static bool containsIOSQXZ(const wxString &aStr)
KICOMMON_API bool IncrementString(wxString &name, int aIncrement)
Generic string incrementer.
Definition increment.cpp:29
KICOMMON_API wxString AlphabeticFromIndex(size_t aN, const wxString &aAlphabet, bool aZeroBasedNonUnitCols)
Get an alphabetic string like A, B, ... Z, AA, AB, ... ZZ, AAA, ...
KICOMMON_API int IndexFromAlphabetic(const wxString &aStr, const wxString &aAlphabet)
Attempt to convert a string to an integer, assuming it is an alphabetic string like "A",...
#define KICOMMON_API
Definition kicommon.h:27
wxString result
Test unit parsing edge cases and error handling.