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