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, 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 if( number > -1 )
69 {
70 name.Remove( ii + 1 );
71
72 //write out a format string with correct number of leading zeroes
73 outputFormat.Printf( wxS( "%%0%dld" ), dCount );
74
75 //write out the number using the format string
76 outputNumber.Printf( outputFormat, number );
77 name << outputNumber << suffix;
78 return true;
79 }
80 }
81
82 return false;
83}
84
85
86std::optional<wxString> STRING_INCREMENTER::Increment( const wxString& aStr, int aDelta,
87 size_t aRightIndex ) const
88{
89 if( aStr.IsEmpty() )
90 return std::nullopt;
91
92 wxString remaining = aStr;
93 std::vector<std::pair<wxString, STRING_PART_TYPE>> parts;
94 size_t goodParts = 0;
95
96 // Keep popping chunks off the string until we have what we need
97 while( goodParts < ( aRightIndex + 1 ) && !remaining.IsEmpty() )
98 {
99 static const std::regex integerRegex( R"(\d+$)" );
100
101 // ABC or abc but not Abc
102 static const std::regex sameCaseAlphabetRegex( R"(([a-z]+|[A-Z]+)$)" );
103
104 // Skippables - for now anything that isn't a letter or number
105 static const std::regex skipRegex( R"([^a-zA-Z0-9]+$)" );
106
107 std::string remainingStr = remaining.ToStdString();
108 std::smatch match;
109
110 if( std::regex_search( remainingStr, match, integerRegex ) )
111 {
112 parts.push_back( { match.str(), STRING_PART_TYPE::INTEGER } );
113 remaining = remaining.Left( remaining.Len() - match.str().size() );
114 goodParts++;
115 }
116 else if( std::regex_search( remainingStr, match, sameCaseAlphabetRegex ) )
117 {
118 parts.push_back( { match.str(), STRING_PART_TYPE::ALPHABETIC } );
119 remaining = remaining.Left( remaining.Len() - match.str().size() );
120 goodParts++;
121 }
122 else if( std::regex_search( remainingStr, match, skipRegex ) )
123 {
124 parts.push_back( { match.str(), STRING_PART_TYPE::SKIP } );
125 remaining = remaining.Left( remaining.Len() - match.str().size() );
126 }
127 else
128 {
129 // Out of ideas
130 break;
131 }
132 }
133
134 // Couldn't find the part we wanted
135 if( goodParts < aRightIndex + 1 )
136 return std::nullopt;
137
138 // Increment the part we wanted
139 bool didIncrement = incrementPart( parts.back().first, parts.back().second, aDelta );
140
141 if( !didIncrement )
142 return std::nullopt;
143
144 // Reassemble the string - the left-over part, then parts in reverse
145 wxString result = remaining;
146
147 for( auto it = parts.rbegin(); it != parts.rend(); ++it )
148 {
149 result << it->first;
150 }
151
152 return result;
153}
154
155
156static bool containsIOSQXZ( const wxString& aStr )
157{
158 static const wxString iosqxz = "IOSQXZ";
159
160 for( const wxUniChar& c : aStr )
161 {
162 if( iosqxz.Contains( c ) )
163 return true;
164 }
165
166 return false;
167}
168
169
170bool STRING_INCREMENTER::incrementPart( wxString& aPart, STRING_PART_TYPE aType, int aDelta ) const
171{
172 switch( aType )
173 {
175 {
176 long number = 0;
177 bool zeroPadded = aPart.StartsWith( '0' );
178 size_t oldLen = aPart.Len();
179
180 if( aPart.ToLong( &number ) )
181 {
182 number += aDelta;
183
184 // Going below zero makes things awkward
185 // and is not usually that useful.
186 if( number < 0 )
187 return false;
188
189 aPart.Printf( "%ld", number );
190
191 // If the number was zero-padded, we need to re-pad it
192 if( zeroPadded )
193 {
194 aPart = wxString( "0", oldLen - aPart.Len() ) + aPart;
195 }
196
197 return true;
198 }
199
200 break;
201 }
203 {
204 // Covert to uppercase
205 wxString upper = aPart.Upper();
206 bool wasUpper = aPart == upper;
207
208 static const wxString alphabetFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
209 static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
210
211 const wxString& alpha =
212 ( m_SkipIOSQXZ & !containsIOSQXZ( aPart ) ) ? alphaNoIOSQXZ : alphabetFull;
213
214 int index = IndexFromAlphabetic( upper, alpha );
215
216 // Something was not in the alphabet
217 if( index == -1 )
218 return false;
219
220 // It's such a big number that we don't want to increment it
221 if( index > m_AlphabeticMaxIndex && m_AlphabeticMaxIndex >= 0 )
222 return false;
223
224 index += aDelta;
225
226 if( index < 0 )
227 return false;
228
229 wxString newStr = AlphabeticFromIndex( index, alpha, true );
230
231 if( !wasUpper )
232 newStr = newStr.Lower();
233
234 aPart = newStr;
235
236 return true;
237 }
238 case STRING_PART_TYPE::SKIP: break;
239 }
240
241 return false;
242}
243
244
245KICOMMON_API int IndexFromAlphabetic( const wxString& aStr, const wxString& aAlphabet )
246{
247 int index = 0;
248 const int radix = aAlphabet.Length();
249
250 for( size_t i = 0; i < aStr.Len(); i++ )
251 {
252 int alphaIndex = aAlphabet.Find( aStr[i] );
253
254 if( alphaIndex == wxNOT_FOUND )
255 return -1;
256
257 if( i != aStr.Len() - 1 )
258 alphaIndex++;
259
260 index += alphaIndex * pow( radix, aStr.Len() - 1 - i );
261 }
262
263 return index;
264}
265
266
267wxString KICOMMON_API AlphabeticFromIndex( size_t aN, const wxString& aAlphabet,
268 bool aZeroBasedNonUnitCols )
269{
270 wxString itemNum;
271 bool firstRound = true;
272 const int radix = aAlphabet.Length();
273
274 do
275 {
276 int modN = aN % radix;
277
278 if( aZeroBasedNonUnitCols && !firstRound )
279 modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
280
281 itemNum.insert( 0, 1, aAlphabet[modN] );
282
283 aN /= radix;
284 firstRound = false;
285 } while( aN );
286
287 return itemNum;
288}
const char * name
Definition: DXF_plotter.cpp:59
int m_AlphabeticMaxIndex
Definition: increment.h:84
bool incrementPart(wxString &aPart, STRING_PART_TYPE aType, int aDelta) const
Definition: increment.cpp:170
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:86
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:245
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:267
static bool containsIOSQXZ(const wxString &aStr)
Definition: increment.cpp:156
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:267
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:245
#define KICOMMON_API
Definition: kicommon.h:28