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 (C) 2024 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "increment.h"
25
26#include <wx/wxcrt.h>
27
28#include <cmath>
29#include <iostream>
30#include <regex>
31
32
33KICOMMON_API bool IncrementString( wxString& name, int aIncrement )
34{
35 if( name.IsEmpty() )
36 return true;
37
38 wxString suffix;
39 wxString digits;
40 wxString outputFormat;
41 wxString outputNumber;
42 int ii = name.Len() - 1;
43 int dCount = 0;
44
45 while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) )
46 {
47 suffix = name.GetChar( ii ) + suffix;
48 ii--;
49 }
50
51 while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) )
52 {
53 digits = name.GetChar( ii ) + digits;
54 ii--;
55 dCount++;
56 }
57
58 if( digits.IsEmpty() )
59 return true;
60
61 long number = 0;
62
63 if( digits.ToLong( &number ) )
64 {
65 number += aIncrement;
66
67 // Don't let result go below zero
68
69 if( number > -1 )
70 {
71 name.Remove( ii + 1 );
72 //write out a format string with correct number of leading zeroes
73 outputFormat.Printf( wxS( "%%0%dld" ), dCount );
74 //write out the number using the format string
75 outputNumber.Printf( outputFormat, number );
76 name << outputNumber << suffix;
77 return true;
78 }
79 }
80
81 return false;
82}
83
84
85std::optional<wxString> STRING_INCREMENTER::Increment( const wxString& aStr, int aDelta,
86 size_t aRightIndex ) const
87{
88 if( aStr.IsEmpty() )
89 return std::nullopt;
90
91 wxString remaining = aStr;
92 std::vector<std::pair<wxString, STRING_PART_TYPE>> parts;
93 size_t goodParts = 0;
94
95 // Keep popping chunks off the string until we have what we need
96 while( goodParts < ( aRightIndex + 1 ) && !remaining.IsEmpty() )
97 {
98 static const std::regex integerRegex( R"(\d+$)" );
99 // ABC or abc but not Abc
100 static const std::regex sameCaseAlphabetRegex( R"(([a-z]+|[A-Z]+)$)" );
101 // Skippables - for now anything that isn't a letter or number
102 static const std::regex skipRegex( R"([^a-zA-Z0-9]+$)" );
103
104 std::string remainingStr = remaining.ToStdString();
105 std::smatch match;
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 for( auto it = parts.rbegin(); it != parts.rend(); ++it )
143 {
144 result << it->first;
145 }
146 return result;
147}
148
149
150static bool containsIOSQXZ( const wxString& aStr )
151{
152 static const wxString iosqxz = "IOSQXZ";
153 for( const wxUniChar& c : aStr )
154 {
155 if( iosqxz.Contains( c ) )
156 return true;
157 }
158 return false;
159}
160
161
162bool STRING_INCREMENTER::incrementPart( wxString& aPart, STRING_PART_TYPE aType, int aDelta ) const
163{
164 switch( aType )
165 {
167 {
168 long number = 0;
169 bool zeroPadded = aPart.StartsWith( '0' );
170 size_t oldLen = aPart.Len();
171
172 if( aPart.ToLong( &number ) )
173 {
174 number += aDelta;
175
176 // Going below zero makes things awkward
177 // and is not usually that useful.
178 if( number < 0 )
179 return false;
180
181 aPart.Printf( "%ld", number );
182
183 // If the number was zero-padded, we need to re-pad it
184 if( zeroPadded )
185 {
186 aPart = wxString( "0", oldLen - aPart.Len() ) + aPart;
187 }
188 return true;
189 }
190
191 break;
192 }
194 {
195 // Covert to uppercase
196 wxString upper = aPart.Upper();
197 bool wasUpper = aPart == upper;
198
199 static const wxString alphabetFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
200 static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
201
202 const wxString& alpha =
203 ( m_SkipIOSQXZ & !containsIOSQXZ( aPart ) ) ? alphaNoIOSQXZ : alphabetFull;
204
205 int index = IndexFromAlphabetic( upper, alpha );
206
207 // Something was not in the alphabet
208 if( index == -1 )
209 return false;
210
211 // It's such a big number that we don't want to increment it
212 if( index > m_AlphabeticMaxIndex && m_AlphabeticMaxIndex >= 0 )
213 return false;
214
215 index += aDelta;
216
217 if( index < 0 )
218 return false;
219
220 wxString newStr = AlphabeticFromIndex( index, alpha, true );
221
222 if( !wasUpper )
223 newStr = newStr.Lower();
224
225 aPart = newStr;
226
227 return true;
228 }
229 case STRING_PART_TYPE::SKIP: break;
230 }
231
232 return false;
233}
234
235
236KICOMMON_API int IndexFromAlphabetic( const wxString& aStr, const wxString& aAlphabet )
237{
238 int index = 0;
239 const int radix = aAlphabet.Length();
240
241 for( size_t i = 0; i < aStr.Len(); i++ )
242 {
243 int alphaIndex = aAlphabet.Find( aStr[i] );
244
245 if( alphaIndex == wxNOT_FOUND )
246 return -1;
247
248 if( i != aStr.Len() - 1 )
249 alphaIndex++;
250
251 index += alphaIndex * pow( radix, aStr.Len() - 1 - i );
252 }
253
254 return index;
255}
256
257
258wxString KICOMMON_API AlphabeticFromIndex( size_t aN, const wxString& aAlphabet,
259 bool aZeroBasedNonUnitCols )
260{
261 wxString itemNum;
262 bool firstRound = true;
263 const int radix = aAlphabet.Length();
264
265 do
266 {
267 int modN = aN % radix;
268
269 if( aZeroBasedNonUnitCols && !firstRound )
270 modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
271
272 itemNum.insert( 0, 1, aAlphabet[modN] );
273
274 aN /= radix;
275 firstRound = false;
276 } while( aN );
277
278 return itemNum;
279}
const char * name
Definition: DXF_plotter.cpp:57
int m_AlphabeticMaxIndex
Definition: increment.h:84
bool incrementPart(wxString &aPart, STRING_PART_TYPE aType, int aDelta) const
Definition: increment.cpp:162
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:85
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",...
Definition: increment.cpp:236
wxString KICOMMON_API AlphabeticFromIndex(size_t aN, const wxString &aAlphabet, bool aZeroBasedNonUnitCols)
Get an alphabetic string like A, B, ... Z, AA, AB, ... ZZ, AAA, ...
Definition: increment.cpp:258
static bool containsIOSQXZ(const wxString &aStr)
Definition: increment.cpp:150
KICOMMON_API bool IncrementString(wxString &name, int aIncrement)
Generic string incrementer.
Definition: increment.cpp:33
KICOMMON_API wxString AlphabeticFromIndex(size_t aN, const wxString &aAlphabet, bool aZeroBasedNonUnitCols)
Get an alphabetic string like A, B, ... Z, AA, AB, ... ZZ, AAA, ...
Definition: increment.cpp:258
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",...
Definition: increment.cpp:236
#define KICOMMON_API
Definition: kicommon.h:28