ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterjavastyle.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14// ====================================== Global Fragment ======================================
15#include "alib/alib.inl"
16// =========================================== Module ==========================================
17#if ALIB_C20_MODULES
19 import ALib.Lang;
21 import ALib.EnumOps;
22 import ALib.Exceptions;
23# if ALIB_CAMP
24 import ALib.Camp.Base;
25# endif
26#else
28# include "ALib.Lang.H"
29# include "ALib.Exceptions.H"
31# include "ALib.Camp.Base.H"
32#endif
33// ====================================== Implementation =======================================
34using namespace alib::strings;
35namespace alib::format {
36
37
39: FormatterStdImpl( A_CHAR( "FormatterJavaStyle" ) )
40{
41 // arguments are counted starting with 1.
43
44 // set number format to Java defaults
45 DefaultNumberFormat.Flags -= NumberFormatFlags::ForceDecimalPoint;
46 DefaultNumberFormat.Flags += NumberFormatFlags::WriteExponentPlusSign;
47
48 // set number format to Java defaults
49 DefaultNumberFormat .HexLiteralPrefix = A_CHAR( "0X" );
50
51 DefaultNumberFormat .OctLiteralPrefix =
52 AlternativeNumberFormat.OctLiteralPrefix = A_CHAR( "0" );
53
54 DefaultNumberFormat .NANLiteral =
55 AlternativeNumberFormat.NANLiteral = A_CHAR( "NaN" );
56 DefaultNumberFormat .INFLiteral = A_CHAR( "INFINITY" );
57 AlternativeNumberFormat.INFLiteral = A_CHAR( "Infinity" );
58}
59
60
62{
63 SPFormatter clone;
65
66 // create a clone of #Next, in the case that next is derived from std base class
67 if( Next )
68 clone->Next= Next->Clone();
69
70 // copy my settings, that's it
71 clone->CloneSettings( *this );
72
73 return clone;
74}
75
76
77
79{
80 // first invoke parent's setting...
82
83 // ...then make some "Java like" adjustments
84 placeholderJS.ConversionUpper = false;
85 placeholder.ValueAlignment = lang::Alignment::Right;
86
87 placeholder.NF.Flags -= NumberFormatFlags::ForceDecimalPoint;
88 placeholder.NF.Flags -= NumberFormatFlags::OmitTrailingFractionalZeros;
89
90 placeholderJS.AlternateForm = false;
91
92 placeholderJS.Precision = -1;
93 placeholderJS.DefaultPrecision = 6;
94}
95
96
98{
99 integer idx= 0;
100 while( (idx= parser.IndexOf('%', idx )) >= 0
101 && ( parser.CharAt( idx + 1 ) == '%'
102 || parser.CharAt( idx + 1 ) == 'n' ) )
103 {
104 // double ESC found (escaped %% or %n)
105 idx+= + 2;
106 }
107 return idx;
108}
109
110
112{
113 if( length == 0)
114 return;
115
116 targetString->EnsureRemainingCapacity( length );
117 auto* src = parser.Buffer();
118 auto* dest= targetString->VBuffer() + targetString->Length();
119 parser.ConsumeChars( length );
120
121 character c1;
122 character c2= *src;
123 while( length > 1 )
124 {
125 c1= c2;
126 c2= *++src;
127
128 if( ( c1 == '%' && c2 =='%')
129 || c1 == '\\' )
130 {
131 if( c1 == '\\' )
132 switch(c2)
133 {
134 case 'r': c1= '\r' ; break;
135 case 'n': c1= '\n' ; break;
136 case 't': c1= '\t' ; break;
137 case 'a': c1= '\a' ; break;
138 case 'b': c1= '\b' ; break;
139 case 'v': c1= '\v' ; break;
140 case 'f': c1= '\f' ; break;
141 case '"': c1= '"' ; break;
142 default: c1= '?' ; break;
143 }
144
145 c2= *++src;
146 --length;
147 }
148 else if( c1 == '%' && c2 =='n' )
149 {
150 c1= '\n';
151 ++src;
152 --length;
153 }
154
155 *dest++= c1;
156 --length;
157 }
158
159 // copy last character and adjust target string length:
160 // Note: length usually is 1. Only if last character is an escape sequence, it is 0.
161 if( length == 1)
162 *dest= *src;
163 targetString->SetLength( dest - targetString->VBuffer() + length);
164}
165
166
167
169{
170 enum states
171 {
172 POSITION = 1,
173 FLAGS = 2,
174 WIDTH = 3,
175 PRECISION = 4,
176 TYPE = 5,
177 TYPE_SUFFIX = 6,
178 END = 10,
179 };
180
181 states state= POSITION;
182 #define NEXTSTATE(s) { state= s; continue; }
183
184 while( true )
185 {
186 // switch over state. With 'break' we consume one character (kind of success) while
187 // with 'continue' we keep the current character (and go to another state)
188 switch ( state )
189 {
190 case POSITION:
191 {
192 int argNo= -1;
193 if ( parser.ConsumeChar('<') )
194 {
195 argNo= placeholder.PreviousArgIdx + 1;
196 }
197 else
198 {
199 integer i= 0;
200 while( i < parser.Length()
201 && isdigit( parser.CharAt<NC>(i) ) )
202 ++i;
203
204 if( i > 0 && parser.CharAt<NC>(i) == '$')
205 {
206 parser.ConsumeDecDigits( argNo ); ALIB_ASSERT_RESULT_EQUALS(
207 parser.ConsumeChar('$') , true )
208 }
209 }
210 if( argNo >= 0 )
211 setArgument( argNo );
212
213 NEXTSTATE(FLAGS)
214 }
215
216 case FLAGS:
217 {
218 bool flagsDone= false;
219 while(!flagsDone)
220 {
221 switch ( parser.CharAtStart() )
222 {
223 case '-':
224 placeholder.ValueAlignment= lang::Alignment::Left;
225 placeholder.AlignmentSpecified= true;
226 break;
227
228 case '^':
229 placeholder.ValueAlignment= lang::Alignment::Center;
230 placeholder.AlignmentSpecified= true;
231 break;
232
233 case '#':
234 {
235 placeholderJS.AlternateForm= true;
236 placeholder.WriteBinOctHexPrefix= true;
237 placeholder.NF.Flags += NumberFormatFlags::ForceDecimalPoint;
238 placeholder.NF.Flags -= NumberFormatFlags::OmitTrailingFractionalZeros;
239 break;
240 }
241
242 case '+':
243 case ' ':
244 placeholder.NF.PlusSign= parser.CharAtStart();
245 break;
246
247 case '0':
248 placeholder.SignPaddingMode= true;
249 break;
250
251 case ',':
252 placeholder.NF.Flags += NumberFormatFlags::WriteGroupChars;
253 break;
254
255 case '(':
257 formatString, formatString.Length() - parser.Length() );
258
259 default:
260 flagsDone= true;
261 break;
262 }
263
264 if( !flagsDone )
265 parser.ConsumeChars( 1 );
266 }
267 NEXTSTATE(WIDTH)
268 }
269
270
271 case WIDTH:
272 {
273 if( isdigit( parser.CharAtStart() ) )
274 parser.ConsumeDecDigits( placeholder.Width );
275
276 NEXTSTATE(PRECISION)
277 }
278
279 case PRECISION:
280 {
281 if( parser.ConsumeChar( '.' ) && !parser.ConsumeDecDigits( placeholderJS.Precision ) )
283 formatString, formatString.Length() - parser.Length() );
284
285 NEXTSTATE(TYPE)
286 }
287
288 case TYPE:
289 {
290 placeholder.TypeCode= parser.CharAtStart();
291 parser.ConsumeChars( 1 );
292
293 character typeCharLower= characters::ToLower( placeholder.TypeCode );
294
295 if ( typeCharLower == 'a' )
296 {
298 formatString, formatString.Length() - parser.Length() - 1 );
299 }
300
301 if( placeholderJS.AlternateForm && String( A_CHAR( "sSbBhHgGcCtT" ) ).IndexOf( placeholder.TypeCode ) >= 0 )
303 placeholder.TypeCode,
304 formatString, formatString.Length() - parser.Length() - 1 );
305
306 if( String(A_CHAR( "seg" )).IndexOf( placeholder.TypeCode ) >= 0 )
307 {
308 if( placeholder.TypeCode != 's' )
309 placeholder.NF.ExponentSeparator= AlternativeNumberFormat.ExponentSeparator;
310 placeholder.NF.INFLiteral= AlternativeNumberFormat.INFLiteral;
311 placeholder.NF.NANLiteral= AlternativeNumberFormat.NANLiteral;
312 }
313
314 if( String(A_CHAR( "SBCT" )).IndexOf( placeholder.TypeCode ) >= 0 )
315 placeholderJS.ConversionUpper= true;
316
317 if( NCString("egf").IndexOf( static_cast<nchar>(typeCharLower) ) < 0 )
318 placeholder.CutContent= placeholderJS.Precision;
319
320 if( placeholderJS.Precision >=0 && String(A_CHAR( "cCtTd" )).IndexOf( placeholder.TypeCode ) >= 0 )
322 placeholderJS.Precision, placeholder.TypeCode,
323 formatString, formatString.Length() - parser.Length() - 1 );
324
325 if( placeholder.TypeCode == 'X' || placeholder.TypeCode == 'H' ) placeholder.NF.Flags -= NumberFormatFlags::HexLowerCase;
326 else if( placeholder.TypeCode == 'x' || placeholder.TypeCode == 'h' ) placeholder.NF.HexLiteralPrefix= AlternativeNumberFormat.HexLiteralPrefix;
327
328
329 switch ( typeCharLower )
330 {
331 case 's': placeholderJS.Precision= -1;
332 break;
333
334 case 'b': placeholder.Type= PHTypes::Bool; break;
335 case 'c': placeholder.Type= PHTypes::Character; break;
336 case 'd': placeholder.Type= PHTypes::IntBase10; break;
337 case 'o': placeholder.Type= PHTypes::IntOctal; break;
338 case 'x': placeholder.Type= PHTypes::IntHex; break;
339 case 'h': placeholder.Type= PHTypes::IntHex; break;
340 case 'e': placeholder.Type= PHTypes::Float;
341 placeholder.NF.Flags+= NumberFormatFlags::ForceScientific; break;
342 case 'g': placeholder.Type= PHTypes::Float; break;
343 case 'f': placeholder.Type= PHTypes::Float;
344 placeholder.NF.IntegralPartMinimumWidth= 1;
345 placeholderJS.DefaultPrecision = -1; break;
346
347 case 't': placeholderJS.DateTime= parser.CharAtStart();
348 parser.ConsumeChars( 1 );
349 switch( placeholderJS.DateTime )
350 {
351 case 'H': placeholder.FormatSpec= A_CHAR( "HH" ) ; break;
352 case 'k': placeholder.FormatSpec= A_CHAR( "H" ) ; break;
353 case 'I': placeholder.FormatSpec= A_CHAR( "KK" ) ; break;
354 case 'l': placeholder.FormatSpec= A_CHAR( "K" ) ; break;
355 case 'M': placeholder.FormatSpec= A_CHAR( "mm" ) ; break;
356 case 'S': placeholder.FormatSpec= A_CHAR( "ss" ) ; break;
357 // not supported: case 'L': ;
358 // not supported: case 'N': ;
359 // not supported: case 'p': ;
360 // not supported: case 'z': ;
361 // not supported: case 'Z': ;
362 // not supported: case 's': ;
363 // not supported: case 'Q': ;
364 case 'B': placeholder.FormatSpec= A_CHAR( "MMMM" ); break;
365 case 'b': placeholder.FormatSpec= A_CHAR( "MMM" ); break;
366 case 'h':
367 case 'A': placeholder.FormatSpec= A_CHAR( "dddd" ); break;
368 case 'a': placeholder.FormatSpec= A_CHAR( "ddd" ); break;
369 // not supported: case 'C': ;
370 case 'Y': placeholder.FormatSpec= A_CHAR( "yyyy" ); break;
371 case 'y': placeholder.FormatSpec= A_CHAR( "yy" ); break;
372 // not supported: case 'j': ;
373 case 'm': placeholder.FormatSpec= A_CHAR( "MM" ); break;
374 case 'd': placeholder.FormatSpec= A_CHAR( "dd" ); break;
375 case 'e': placeholder.FormatSpec= A_CHAR( "d" ); break;
376
377 case 'R': placeholder.FormatSpec= A_CHAR( "HH:mm" ); break;
378 case 'T': placeholder.FormatSpec= A_CHAR( "HH:mm:ss" ); break;
379 // not supported: case 'r': ;
380
381 case 'D': placeholder.FormatSpec= A_CHAR( "MM/dd/yy" ); break;
382 case 'F': placeholder.FormatSpec= A_CHAR( "yyyy-MM-dd" ); break;
383 // not supported: case 'c': ;
384
385 default:
387 placeholderJS.DateTime,
388 formatString, formatString.Length() - parser.Length() - 1 );
389 }
390 break;
391
392 default:
394 placeholder.TypeCode,
395 formatString, formatString.Length() - parser.Length() - 1 );
396 }
397
398 NEXTSTATE(TYPE_SUFFIX)
399 }
400
401 case TYPE_SUFFIX:
402 {
403 NEXTSTATE(END)
404 }
405
406 case END:
407 //parser.ConsumeChars(1);
408 return true;
409
410 default: ALIB_ERROR( "FORMAT", "Illegal switch state." ) break;
411 } // state switch
412
413 } // read loop
414
415}
416
417
419{
420 if( startIdx >= 0 && placeholderJS.ConversionUpper && target == nullptr )
421 targetString->ToUpper( startIdx );
422 return true;
423}
424
425
427{
428 bool wasFloat= placeholder.Type == PHTypes::Float;
429 if( wasFloat )
430 {
431 if ( placeholderJS.Precision >= 0 )
432 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
433 else if( placeholder.NF.FractionalPartWidth < 0 )
434 placeholder.NF.FractionalPartWidth= placeholderJS.DefaultPrecision;
435 }
436
438
439 if( !wasFloat && placeholder.Type == PHTypes::Float )
440 {
441 if ( placeholderJS.Precision >= 0 )
442 placeholder.NF.FractionalPartWidth= placeholderJS.Precision;
443 }
444
445 return result;
446}
447
448} // namespace [alib::format]
449
void InsertDerived(TArgs &&... args)
virtual ALIB_DLL void writeStringPortion(integer length) override
virtual ALIB_DLL void resetPlaceholder() override
virtual ALIB_DLL SPFormatter Clone() override
PlaceholderAttributesJS placeholderJS
The extended placeholder attributes.
virtual ALIB_DLL bool checkStdFieldAgainstArgument() override
virtual ALIB_DLL integer findPlaceholder() override
virtual ALIB_DLL bool parsePlaceholder() override
virtual ALIB_DLL bool preAndPostProcess(integer startIdx, AString *target) override
AString * targetString
The target string as provided with method Format.
FormatterStdImpl(const String &formatterClassName)
Substring parser
The current (remaining) format string.
@ Float
Outputs a number in floating point format.
@ IntBase10
Outputs a given number in base 10. The default.
@ IntHex
Outputs a given number in base 16.
@ IntOctal
Outputs a given number in base 8.
String formatString
The format string as provided with method Format.
NumberFormat DefaultNumberFormat
Definition formatter.inl:96
virtual ALIB_DLL void CloneSettings(Formatter &reference)
NumberFormat AlternativeNumberFormat
SharedPtr< Formatter > Next
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.inl:1066
#define ALIB_CALLER_NULLED
Definition alib.inl:1010
#define A_CHAR(STR)
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
TChar ToLower(TChar c)
@ Center
Chooses centered alignment.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
strings::TCString< nchar > NCString
Type alias in namespace alib.
Definition cstring.inl:512
characters::nchar nchar
Type alias in namespace alib.
exceptions::Exception Exception
Type alias in namespace alib.
containers::SharedPtr< format::Formatter > SPFormatter
Definition formatter.inl:42
characters::character character
Type alias in namespace alib.