/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright The KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "increment.h" #include #include #include #include KICOMMON_API bool IncrementString( wxString& name, int aIncrement ) { if( name.IsEmpty() ) return true; wxString suffix; wxString digits; wxString outputFormat; wxString outputNumber; int ii = name.Len() - 1; int dCount = 0; while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) ) { suffix = name.GetChar( ii ) + suffix; ii--; } while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) ) { digits = name.GetChar( ii ) + digits; ii--; dCount++; } if( digits.IsEmpty() ) return true; long number = 0; if( digits.ToLong( &number ) ) { number += aIncrement; // Don't let result go below zero if( number > -1 ) { name.Remove( ii + 1 ); //write out a format string with correct number of leading zeroes outputFormat.Printf( wxS( "%%0%dld" ), dCount ); //write out the number using the format string outputNumber.Printf( outputFormat, number ); name << outputNumber << suffix; return true; } } return false; } std::optional STRING_INCREMENTER::Increment( const wxString& aStr, int aDelta, size_t aRightIndex ) const { if( aStr.IsEmpty() ) return std::nullopt; wxString remaining = aStr; std::vector> parts; size_t goodParts = 0; // Keep popping chunks off the string until we have what we need while( goodParts < ( aRightIndex + 1 ) && !remaining.IsEmpty() ) { static const std::regex integerRegex( R"(\d+$)" ); // ABC or abc but not Abc static const std::regex sameCaseAlphabetRegex( R"(([a-z]+|[A-Z]+)$)" ); // Skippables - for now anything that isn't a letter or number static const std::regex skipRegex( R"([^a-zA-Z0-9]+$)" ); std::string remainingStr = remaining.ToStdString(); std::smatch match; if( std::regex_search( remainingStr, match, integerRegex ) ) { parts.push_back( { match.str(), STRING_PART_TYPE::INTEGER } ); remaining = remaining.Left( remaining.Len() - match.str().size() ); goodParts++; } else if( std::regex_search( remainingStr, match, sameCaseAlphabetRegex ) ) { parts.push_back( { match.str(), STRING_PART_TYPE::ALPHABETIC } ); remaining = remaining.Left( remaining.Len() - match.str().size() ); goodParts++; } else if( std::regex_search( remainingStr, match, skipRegex ) ) { parts.push_back( { match.str(), STRING_PART_TYPE::SKIP } ); remaining = remaining.Left( remaining.Len() - match.str().size() ); } else { // Out of ideas break; } } // Couldn't find the part we wanted if( goodParts < aRightIndex + 1 ) return std::nullopt; // Increment the part we wanted bool didIncrement = incrementPart( parts.back().first, parts.back().second, aDelta ); if( !didIncrement ) return std::nullopt; // Reassemble the string - the left-over part, then parts in reverse wxString result = remaining; for( auto it = parts.rbegin(); it != parts.rend(); ++it ) { result << it->first; } return result; } static bool containsIOSQXZ( const wxString& aStr ) { static const wxString iosqxz = "IOSQXZ"; for( const wxUniChar& c : aStr ) { if( iosqxz.Contains( c ) ) return true; } return false; } bool STRING_INCREMENTER::incrementPart( wxString& aPart, STRING_PART_TYPE aType, int aDelta ) const { switch( aType ) { case STRING_PART_TYPE::INTEGER: { long number = 0; bool zeroPadded = aPart.StartsWith( '0' ); size_t oldLen = aPart.Len(); if( aPart.ToLong( &number ) ) { number += aDelta; // Going below zero makes things awkward // and is not usually that useful. if( number < 0 ) return false; aPart.Printf( "%ld", number ); // If the number was zero-padded, we need to re-pad it if( zeroPadded ) { aPart = wxString( "0", oldLen - aPart.Len() ) + aPart; } return true; } break; } case STRING_PART_TYPE::ALPHABETIC: { // Covert to uppercase wxString upper = aPart.Upper(); bool wasUpper = aPart == upper; static const wxString alphabetFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY"; const wxString& alpha = ( m_SkipIOSQXZ & !containsIOSQXZ( aPart ) ) ? alphaNoIOSQXZ : alphabetFull; int index = IndexFromAlphabetic( upper, alpha ); // Something was not in the alphabet if( index == -1 ) return false; // It's such a big number that we don't want to increment it if( index > m_AlphabeticMaxIndex && m_AlphabeticMaxIndex >= 0 ) return false; index += aDelta; if( index < 0 ) return false; wxString newStr = AlphabeticFromIndex( index, alpha, true ); if( !wasUpper ) newStr = newStr.Lower(); aPart = newStr; return true; } case STRING_PART_TYPE::SKIP: break; } return false; } KICOMMON_API int IndexFromAlphabetic( const wxString& aStr, const wxString& aAlphabet ) { int index = 0; const int radix = aAlphabet.Length(); for( size_t i = 0; i < aStr.Len(); i++ ) { int alphaIndex = aAlphabet.Find( aStr[i] ); if( alphaIndex == wxNOT_FOUND ) return -1; if( i != aStr.Len() - 1 ) alphaIndex++; index += alphaIndex * pow( radix, aStr.Len() - 1 - i ); } return index; } wxString KICOMMON_API AlphabeticFromIndex( size_t aN, const wxString& aAlphabet, bool aZeroBasedNonUnitCols ) { wxString itemNum; bool firstRound = true; const int radix = aAlphabet.Length(); do { int modN = aN % radix; if( aZeroBasedNonUnitCols && !firstRound ) modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx" itemNum.insert( 0, 1, aAlphabet[modN] ); aN /= radix; firstRound = false; } while( aN ); return itemNum; }