1 //**************************************************************************************************
2 //                                          StrUtils.cpp                                           *
3 //                                         --------------                                          *
4 // Started     : 2008-05-15                                                                        *
5 // Last Update : 2015-01-15                                                                        *
6 // Copyright   : (C) 2008 by MSWaters                                                              *
7 //**************************************************************************************************
8 
9 //**************************************************************************************************
10 //                                                                                                 *
11 //      This program is free software; you can redistribute it and/or modify it under the          *
12 //      terms of the GNU General Public License as published by the Free Software Foundation;      *
13 //      either version 3 of the License, or (at your option) any later version.                    *
14 //                                                                                                 *
15 //**************************************************************************************************
16 
17 #include "StrUtils.hpp"
18 
19 //**************************************************************************************************
20 // Compare algorithm for sorting node labels.
21 //
22 // As a general rule node labels are automatically generated and are purely numeric. Where a label
23 // diverges from this form it's often because the user has identified it as having some special
24 // significance. When displayed in a list control on a GUI it's more convenient to have these
25 // labels positioned towards the top.
26 //
27 // Argument List :
28 //   rosArg1 - a reference to the first  string to be compared
29 //   rosArg2 - a reference to the second string to be compared
30 //
31 // Return Values :
32 //   If ros1 > ros2 then -1
33 //   If ros1 = ros2 then  0
34 //   If ros1 < ros2 then  1
35 
iStrCmpNode(const wxString & rosArg1,const wxString & rosArg2)36 int  iStrCmpNode( const wxString & rosArg1, const wxString & rosArg2 )
37 {
38   wxChar  oc1, oc2;
39   size_t  sz1, sz2;
40   long    li1, li2;
41 
42   // First look for empty strings
43   sz1 = rosArg1.Len( );
44   sz2 = rosArg2.Len( );
45   if( sz1==0 && sz2!=0 ) return(  1 );
46   if( sz1==0 && sz2==0 ) return(  0 );
47   if( sz1!=0 && sz2==0 ) return( -1 );
48 
49   // Now do some simple tests
50   oc1 = rosArg1.at( 0 );
51   oc2 = rosArg2.at( 0 );
52   if( wxIsalpha( oc1 ) && wxIsdigit( oc2 ) ) return( -1 );
53   if( wxIsdigit( oc1 ) && wxIsalpha( oc2 ) ) return(  1 );
54 
55   // If the node names are purely numeric do the compare
56   if( rosArg1.ToLong( &li1 ) && rosArg2.ToLong( &li2 ) )
57   {
58     if( li1 >  li2 ) return(  1 );
59     if( li1 == li2 ) return(  0 );
60     if( li1 <  li2 ) return( -1 );
61   }
62 
63   // It's all got too hard, just compare the labels as component labels
64   return( iStrCmpCpnt( rosArg1, rosArg2 ) );
65 }
66 
67 //**************************************************************************************************
68 // Compare algorithm for sorting component labels.
69 //
70 // As a rule component labels are comprised of a single letter (signifying the component type)
71 // followed by a number. Where a label diverges from this form it's often because the user has
72 // identified it as having some significance. When displayed in a list control on a GUI it's more
73 // convenient to have these special labels positioned towards the top. Any remaining labels are
74 // simply sorted alphabetically.
75 //
76 // Argument List :
77 //   rosArg1 - a reference to the first  string to be compared
78 //   rosArg2 - a reference to the second string to be compared
79 //
80 // Return Values :
81 //   If ros1 > ros2 then -1
82 //   If ros1 = ros2 then  0
83 //   If ros1 < ros2 then  1
84 
iStrCmpCpnt(const wxString & rosArg1,const wxString & rosArg2)85 int  iStrCmpCpnt( const wxString & rosArg1, const wxString & rosArg2 )
86 {
87   wxString  osStr1, osStr2;
88   ulong     ulNum1, ulNum2;
89   wxString  osEnd1, osEnd2;
90   wxString  os1;
91   wxChar    oc1;
92   size_t    sz1, sz2;
93 
94   // First look for empty strings
95   sz1 = rosArg1.Len( );
96   sz2 = rosArg2.Len( );
97   if( sz1==0 && sz2!=0 ) return(  1 );
98   if( sz1==0 && sz2==0 ) return(  0 );
99   if( sz1!=0 && sz2==0 ) return( -1 );
100 
101   // Parse the first alpha and numeric components of the first argument
102   for( sz1=0, osStr1=wxT(""); sz1<rosArg1.Len( ); sz1++ )
103   {
104     oc1 = rosArg1.at( sz1 );
105     if( ! wxIsalpha( oc1 ) ) break;
106     osStr1 += oc1;
107   }
108   for( os1=wxT(""); sz1<rosArg1.Len( ); sz1++ )
109   {
110     oc1 = rosArg1.at( sz1 );
111     if( ! wxIsdigit( oc1 ) ) break;
112     os1 += oc1;
113   }
114   if( ! os1.IsEmpty( ) ) os1.ToULong( &ulNum1 );
115   else                   ulNum1 = 0;
116   osEnd1 = rosArg1.Mid( sz1 );
117 
118   // Parse the first alpha and numeric components of the second argument
119   for( sz1=0, osStr2=wxT(""); sz1<rosArg2.Len( ); sz1++ )
120   {
121     oc1 = rosArg2.at( sz1 );
122     if( ! wxIsalpha( oc1 ) ) break;
123     osStr2 += oc1;
124   }
125   for( os1=wxT(""); sz1<rosArg2.Len( ); sz1++ )
126   {
127     oc1 = rosArg2.at( sz1 );
128     if( ! wxIsdigit( oc1 ) ) break;
129     os1 += oc1;
130   }
131   if( ! os1.IsEmpty( ) ) os1.ToULong( &ulNum2 );
132   else                   ulNum2 = 0;
133   osEnd2 = rosArg2.Mid( sz1 );
134 
135   // Compare the first string component of the two arguments
136   sz1 = osStr1.Len( );
137   sz2 = osStr2.Len( );
138   if( sz1 == sz2 )
139     if( osStr1 != osStr2 )
140       return( osStr1.Cmp( osStr2 ) );
141   if( sz1 > sz2 ) return( -1 );
142   if( sz1 < sz2 ) return(  1 );
143 
144   // Compare the first numeric component of the two arguments
145   sz1 = osEnd1.Len( );
146   sz2 = osEnd2.Len( );
147   if( sz1==0 && sz2==0 )
148   {
149     if( ulNum1 >  ulNum2 ) return(  1 );
150     if( ulNum1 == ulNum2 ) return(  0 );
151     if( ulNum2 >  ulNum1 ) return( -1 );
152   }
153   if( sz1 > sz2 ) return( -1 );
154   else            return(  1 );
155 
156   // If all else fails do it all again
157 //std::cout << "\niStrCmpCpnt( ) : Look out! Re-entrance\n";
158   return( iStrCmpCpnt( osEnd1, osEnd2 ) );
159 }
160 
161 //**************************************************************************************************
162 // Compare algorithm for sorting signal source labels.
163 //
164 // Signal source labels form a sub-set of all component labels which, as a general rule, are
165 // comprised of a single letter (signifying the component type) followed by a number. Where a label
166 // diverges from this form it's often because the user has identified it as having some special
167 // significance. When displayed in a list control on a GUI it's more convenient to have these
168 // significant labels positioned towards the top.
169 //
170 // Argument List :
171 //   rosArg1 - a reference to the first  string to be compared
172 //   rosArg2 - a reference to the second string to be compared
173 //
174 // Return Values :
175 //   If ros1 > ros2 then -1
176 //   If ros1 = ros2 then  0
177 //   If ros1 < ros2 then  1
178 
iStrCmpSrc(const wxString & rosArg1,const wxString & rosArg2)179 int  iStrCmpSrc( const wxString & rosArg1, const wxString & rosArg2 )
180 {
181   eTypeCpnt  eCpnt1, eCpnt2;
182   size_t     sz1, sz2;
183 
184   // First look for empty strings
185   sz1 = rosArg1.Len( );
186   sz2 = rosArg2.Len( );
187   if( sz1==0 && sz2!=0 ) return(  1 );
188   if( sz1==0 && sz2==0 ) return(  0 );
189   if( sz1!=0 && sz2==0 ) return( -1 );
190 
191   // Compare component types
192   eCpnt1 = Component::eGetType( rosArg1 );
193   eCpnt2 = Component::eGetType( rosArg2 );
194   switch( eCpnt1 )
195   {
196     case eCPNT_IVS : sz1 = 5; break;
197     case eCPNT_ICS : sz1 = 4; break;
198     case eCPNT_IND : sz1 = 3; break;
199     case eCPNT_CAP : sz1 = 2; break;
200     case eCPNT_RES : sz1 = 1; break;
201     default        : sz1 = 0; break;
202   }
203   switch( eCpnt2 )
204   {
205     case eCPNT_IVS : sz2 = 5; break;
206     case eCPNT_ICS : sz2 = 4; break;
207     case eCPNT_IND : sz2 = 3; break;
208     case eCPNT_CAP : sz2 = 2; break;
209     case eCPNT_RES : sz2 = 1; break;
210     default        : sz2 = 0; break;
211   }
212   if( sz1>0 && sz2>0 )
213   {
214     if( sz1 > sz2 ) return( -1 );
215     if( sz1 < sz2 ) return(  1 );
216   }
217 
218   // It's all got too hard, just compare the labels as component labels
219   return( iStrCmpCpnt( rosArg1, rosArg2 ) );
220 }
221 
222 //**************************************************************************************************
223 // Reduce a string down so that it can be displayed in a single line.
224 //
225 // (This function is intended for debugging purposes. Multi-line and formated messages can be sent
226 //  to std::cout or std::cerr.)
227 //
228 // Argument List :
229 //   ros - The string to be reformatted
230 //
231 // Return Values :
232 //   A reference to string containing a single line
233 
rosStrToLine(const wxString & ros)234 const wxString & rosStrToLine( const wxString & ros )
235 {
236   static  wxString  os1;
237   size_t  sz1;
238   char    c1;
239 
240   // Copy the input string
241   os1 = ros;
242 
243   // Remove white-space (space, tab, '\n' & '\r') from both ends of the string
244   os1.Trim( false );
245   os1.Trim( true );
246 
247   // Replace '\n' & '\r' within the string with space characters
248   for( sz1=0; sz1<os1.Length( ); sz1++ )
249   {
250     c1 = os1.GetChar( sz1 );
251 
252     if( c1=='\n' || c1=='\r' )
253       os1.SetChar( sz1, ' ' );
254   }
255 
256   // Replace sequences of space characters with one space character
257   while( (sz1=os1.find(wxT("  "))) != wxString::npos )
258     os1.erase( sz1, 1 );
259 
260   return( os1 );
261 }
262 
263 //**************************************************************************************************
264 //                                          Test Utility                                           *
265 //**************************************************************************************************
266 
267 #ifdef TEST_STRUTILS
268 
269 using  namespace  std;
270 
271 // Function prototypes
272 
273 void  Usage( char * psAppName );
274 void  PrintArray( wxArrayString & roas );
275 
276 //**************************************************************************************************
277 
main(int argc,char * argv[])278 int  main( int argc, char * argv[ ] )
279 {
280   wxArrayString  oas1;
281   wxString       os1;
282 
283   // Validate the argument count passed to the application
284   if( argc > 2 )                    { Usage( argv[0] ); exit( EXIT_FAILURE ); }
285 
286   // Process the command line arguments
287   os1 = wxConvLibc.cMB2WC( argv[ 1 ] );
288   if( argc > 1 )
289   {
290     if( os1.at( 0 ) == wxT('-') )
291     {
292       if( os1.at( 1 ) == wxT('h') ) { Usage( argv[0] ); exit( EXIT_SUCCESS ); }
293       else                          { Usage( argv[0] ); exit( EXIT_FAILURE ); }
294     }
295   }
296 
297   // Display the utility banner
298   cout << "\n    StrUtils Test Utility"
299        << "\n  Version 1.02 (04/04/2014)\n";
300 
301   // Exercise the function : iStrCmpCpnt( )
302   oas1.Empty( );
303   oas1.Add( wxT("3,1") );
304   oas1.Add( wxT("2,0") );
305   std::cout << "\nTest iStrCmpCpnt( ) :\n";
306   std::cout << "  Before : "; PrintArray( oas1 );
307   oas1.Sort( &iStrCmpCpnt );
308   std::cout << "  After  : "; PrintArray( oas1 ); // */
309 
310   // Exercise the function : iStrCmpNode( )
311   oas1.Empty( );
312   oas1.Add( wxT("Vcc") );
313   oas1.Add( wxT("1") );
314   oas1.Add( wxT("2") );
315   oas1.Add( wxT("2SK1058_Drive") );
316   oas1.Add( wxT("3") );
317   oas1.Add( wxT("V1") );
318   std::cout << "\nTest iStrCmpNode( ) :\n";
319   std::cout << "  Before : "; PrintArray( oas1 );
320   oas1.Sort( &iStrCmpNode );
321   std::cout << "  After  : "; PrintArray( oas1 ); // */
322 
323   // Exercise the function : iStrCmpSrc( )
324   oas1.Empty( );
325   oas1.Add( wxT("Iin") );
326   oas1.Add( wxT("Rin") );
327   oas1.Add( wxT("Vin") );
328   oas1.Add( wxT("V2") );
329   oas1.Add( wxT("I1") );
330   std::cout << "\nTest iStrCmpSrc( ) :\n";
331   std::cout << "  Before : "; PrintArray( oas1 );
332   oas1.Sort( &iStrCmpNode );
333   std::cout << "  After  : "; PrintArray( oas1 ); // */
334 
335   std::cout << '\n';
336 
337   exit( EXIT_SUCCESS );
338 }
339 
340 //**************************************************************************************************
341 
Usage(char * psAppName)342 void  Usage( char * psAppName )
343 {
344   cout << "\nUsage   : " << psAppName << " [-OPTIONS]"
345        << "\nOptions :"
346        << "\n  -h : Print usage (this message)\n";
347 }
348 
349 //**************************************************************************************************
350 
PrintArray(wxArrayString & roas)351 void  PrintArray( wxArrayString & roas )
352 {
353   size_t  sz1;
354 
355   for( sz1=0; sz1<roas.GetCount( ); sz1++ )
356   {
357     if( sz1 > 0 ) std::cout << "  ";
358     std::cout << roas.Item( sz1 ).mb_str( );
359   }
360 
361   std::cout << '\n';
362 }
363 
364 //**************************************************************************************************
365 
366 #endif // TEST_STRUTILS
367